Solidity 리뷰를 위한 Qwen2.5-Coder vs DeepSeek-Coder 비교: 로컬 환경에서의 실제 경험
요약
로컬 환경에서 Solidity 컨트랙트 보안 리뷰를 위해 Qwen2.5-Coder와 DeepSeek-Coder 7B 모델을 비교 분석했습니다. 실험 결과, Qwen2.5-Coder가 지시 이행 능력과 출력 형식의 규율 측면에서 더 우수한 성능을 보였습니다.
핵심 포인트
- Qwen2.5-Coder가 DeepSeek-Coder보다 지시 이행(Instruction following) 능력이 뛰어남
- 자동화 파이프라인 구축 시 모델의 출력 형식 준수 여부가 매우 중요함
- 7B 이하 소형 모델은 복잡한 감사보다는 보조 도구로서의 역할에 적합함
- 실험은 전형적인 보안 패턴을 가진 소규모 컨트랙트를 대상으로 진행됨
저는 제가 직접 심어놓은 버그가 포함된 10개의 작은 Solidity 컨트랙트 폴더를 보관하고 있습니다. 출금 함수에서의 전형적인 재진입(reentrancy) 문제, 초기화 함수(initializer)의 접근 제어자(access modifier) 누락, 예치자에게 유리하게 반올림되는 ERC4626 금고(vault), 서명자를 절대 검증하지 않는 서명 확인, 그리고 몇 가지 더 미묘한 로직 버그들이 포함되어 있습니다. 어떤 로컬 모델을 내 디스크에 저장할지 결정할 때마다, 저는 모든 후보 모델을 동일한 프롬프트로 이 10개의 컨트랙트에 실행해보고 결과를 비교합니다. 이것은 벤치마크(benchmark)는 아니며 마지막에 퍼센트 수치가 나오는 것도 아니지만, 몇 달 동안 이를 반복한 결과 패턴이 기록할 가치가 있을 만큼 일관되게 나타났습니다.
이번 라운드: WSL2 상에서 Ollama를 통해 실행되는 qwen2.5-coder와 deepseek-coder의 대결입니다. 두 모델 모두 제 하드웨어에 여유롭게 들어가는 크기, 즉 7B 이하의 크기로 테스트했습니다. 만약 33B 모델을 로컬에서 실행할 수 있다면 결론은 달라질 것이며, 어차피 소형 모델의 벤치마크는 읽는 것을 멈추는 것이 좋습니다.
방법론 (Methodology)
동일한 10개의 컨트랙트를 사용하며, 모두 150줄을 넘지 않습니다. 컨트랙트당 동일한 세 가지 프롬프트를 사용했습니다: 보안 문제를 위해 이 컨트랙트를 리뷰하라는 개방형 질문, "특히 재진입(reentrancy)과 접근 제어(access control) 문제를 확인하라"는 타겟형 질문, 그리고 발견 사항을 심각도 태그가 붙은 리스트로 요구하는 구조화된 질문입니다. 소형 모델은 노이즈(noise)가 많아 운 좋게 얻은 샘플 하나로는 아무것도 증명할 수 없기에, 각 조합을 여러 번 실행했습니다. 온도는 낮게 설정하되 0은 아니게 했습니다. 점수는 수동으로 매겼습니다: 심어놓은 버그를 찾아냈는가, 이를 정확하게 설명했는가, 그리고 얼마나 많은 노이즈가 함께 따라왔는가를 기준으로 삼았습니다.
두 가지 솔직한 주의사항이 있습니다. 첫째, 이 컨트랙트들은 작고 독립적이어서 모든 모델에게 유리합니다. 실제 리뷰는 컨트랙트 간 호출 그래프(call graphs)와 2000줄에 달하는 차이점(diffs)을 다루는 것을 의미하며, 어떤 계열의 7B 모델이라도 그런 환경에서는 살아남지 못합니다. 둘째, 제가 심어놓은 버그들은 훈련 데이터(training data)에 끊임없이 등장하는 전형적인 패턴에 치우쳐 있습니다. 이 두 가지 사실은 제 설정이 "소화 가능한 코드에 대한 유용한 어시스턴트(assistant)"를 측정하는 것이지, "감사관(auditor)의 대체재"를 측정하는 것이 아님을 의미합니다. 하지만 괜찮습니다. 어차피 제가 이 모델들에게 맡길 수 있는 유일한 작업은 그것뿐이니까요.
qwen2.5-coder가 승리하는 지점
지시 이행 (Instruction following). 이것은 실질적으로 가장 큰 격차이며, 보안 지식과는 무관한 문제입니다. 제가 다음과 같은 출력을 요구할 때를 예로 들어보겠습니다:
결과를 리스트 형식으로 보고하세요. 각 항목에 대해:
SEVERITY: HIGH | MEDIUM | LOW
LINE: <번호 또는 범위>
...
7B 규모의 Qwen은 거의 매번 이를 준수합니다. 비슷한 크기의 DeepSeek는 대부분의 경우 준수하지만, 서문을 추가하거나, 제한 사항을 무시하거나, 제가 직접 제거해야 하는 해설로 리스트를 감싸는 등 더 자주 벗어나는 경향을 보입니다. 만약 모델의 출력을 파이프라인 (pipeline)에 연결하여 사용한다면(저 역시 그렇게 하고 있으며, 이 비교는 저의 오픈 소스 감사 도구인 spectr-ai를 위한 모델 선정 과정에서 시작되었습니다), 형식의 규율 (format discipline)은 버그를 아주 미세하게 더 잘 찾아내는 능력보다 더 가치가 있습니다. 왜냐하면 파싱 (parse)할 수 없는 결과는 결국 얻지 못한 결과와 같기 때문입니다.
타겟팅된 점검 (Targeted checks). Qwen에게 재진입성 (reentrancy)에 대해 구체적으로 물으면 재진입성에 집중된 답변을 유지합니다. 반면 DeepSeek는 답변 도중 가스 (gas) 관련 언급이나 스타일 지적 (style nits)으로 더 자주 빠지는데, 이는 좁은 범위의 질문을 던졌을 때 신호 (signal)를 오염시킵니다.
설명 품질 (Explanation quality). 두 모델 모두 동일한 버그를 찾아냈을 때, Qwen의 설명은 실제 공격 시퀀스(잔액 업데이트 전 fallback을 통한 재진입, withdraw 호출 등)를 명시하는 경우가 더 많습니다. 반면 작은 규모의 DeepSeek는 외부 호출 (external calls)에 대한 일반적인 교과서적 문장에 치우치는 경향이 있습니다. 둘 다 "정답"이지만, 보고서 작성 시에는 하나가 더 유용합니다.
DeepSeek-Coder가 대등하거나 승리하는 지점
고전적인 버그에 대한 원시 패턴 회상 (Raw pattern recall). 보호되지 않은 selfdestruct 스타일의 권한 부여 함수, 누락된 수정자 (modifiers), 조잡한 재진입성 (reentrancy)과 같은 가장 단순한 버그들에 대해서는 DeepSeek도 똑같이 신뢰할 수 있으며, 때로는 위험성을 더 구체적으로 표현하기도 합니다. DeepSeek의 학습 데이터에는 분명히 방대한 양의 Solidity가 포함되어 있습니다.
회의론 (Skepticism). DeepSeek가 약간 더 많은 부분을 지적하는데, 이는 양날의 검과 같습니다. 보상 계산이 오래된 잔액 스냅샷 (stale balance snapshot)을 사용하는 논리적 버그가 포함된 저의 미묘한 컨트랙트 중 하나에서, DeepSeek는 정확한 버그를 설명하는 경우는 드물었지만 Qwen보다 더 자주 적절한 함수를 지목하며 의구심을 나타냈습니다. "사람아, 여기를 봐"라고 가리키는 포인터로서, 이는 실질적인 가치가 있습니다. 그 대가는 깨끗한 코드에서 발생하는 더 많은 노이즈(noise)입니다. 제가 의도적으로 깨끗하게 작성한 컨트랙트에서 DeepSeek는 Qwen보다 더 자주 그럴듯하게 들리는 문제들을 만들어냈습니다.
환각 (Hallucination)의 양상. 두 모델 모두 서로 다른 방식으로 환각을 일으킵니다. Qwen의 실패 모드 (failure mode) 는 확신에 찬 누락 (confident omission)으로, 버그가 있는 코드에 대해 깨끗하다는 판정을 내리는 것입니다. DeepSeek의 실패 모드는 확신에 찬 발명 (confident invention)으로, 외부 호출 (external call)이 없어 구조적으로 발생할 수 없는 재진입 (reentrancy) 문제를 인용하는 식입니다. 보안 분류 (security triage) 측면에서는 DeepSeek의 실패 모드를 약간 더 선호합니다 (오보(false alarm)는 몇 분의 시간을 소모할 뿐이지만, 놓치는 것은 모든 것을 잃는 것이기 때문입니다). 하지만 노이즈가 모든 경고에 대한 신뢰를 떨어뜨리는 자동화 측면에서는 Qwen의 방식을 선호합니다.
모델 크기가 제품군보다 중요하며, 그 차이는 압도적이다
이번 실험의 모든 라운드에서 가장 일관되게 발견된 사실은, 동일한 제품군 내에서의 1.5B와 7B 사이의 격차가 동일한 크기에서의 Qwen와 DeepSeek 사이의 격차보다 훨씬 크다는 점입니다.
제가 매일 사용하며 빠른 분류 및 분류 (triage) 용도로 매우 좋아하는 qwen2.5-coder:1.5b는 단순히 코드 리뷰어로서의 역할을 수행하지 못합니다. 테스트한 10개의 컨트랙트에서 이 모델은 가장 교과서적인 패턴만을 포착할 뿐, 두 단계의 추론 (reasoning)이 필요한 것은 모두 놓치며, 구조화된 출력 형식 (structured output format)을 빈번하게 망가뜨립니다. 미묘하게 심어놓은 버그들, 즉 오래된 스냅샷, 반올림 방향 등은 이 모델에게 보이지 않는 것이나 다름없습니다. DeepSeek의 가장 작은 변체들도 마찬가지입니다. 대략 7B 미만에서는 "어떤 제품군인가"라는 질문 자체가 잘못되었습니다. 두 모델 모두 상태 (state)와 제어 흐름 (control flow)에 대한 다단계 추론을 수행하기에는 너무 작습니다.
7B 규모에서 두 모델 제품군 모두 작은 계약(contract) 내의 전형적인 상위 10가지 유형의 버그를 안정적으로 잡아내며, 때로는 더 어려운 문제에서 놀라운 성능을 보여주는 임계점을 넘습니다. 하지만 그다음 단계인 비즈니스 로직 버그(business logic bugs)의 일관된 탐지 단계는 넘어서지 못합니다. 이 버그들은 알려진 위험한 형태(shape)를 매칭하는 것이 아니라, 코드가 무엇을 수행해야 하는지를 이해해야 하는 버그들입니다. 실제 감사(audit) 결과는 바로 이 지점에서 발생하며, 이것이 제가 이 모델들을 감사자(auditor)가 아닌 분류 보조 도구(triage assistants)라고 계속해서 말하는 이유입니다.
실질적인 결론은 다음과 같습니다: 만약 Solidity 작업을 위해 로컬 모델을 선택한다면, 자신의 하드웨어가 여유롭게 실행할 수 있는 가장 큰 모델을 확보하는 데 집중하십시오. 그 후에 모델 제품군을 비교하십시오. 제품군 선택은 결정적인 차이를 만드는 요소(tiebreaker)일 뿐입니다.
내가 실제로 사용하는 것들
qwen2.5-coder:7b는 구조화된 모든 작업, 즉 리뷰 단계, 분류, 그리고 이전에 언급했던 pre-commit 및 repo-triage 파이프라인을 위한 나의 기본 모델입니다. 지시 이행(instruction following) 능력이 이 모델을 기본 자리로 만들었습니다. qwen2.5-coder:1.5b는 오답의 비용이 거의 없는 빠르고 중요도가 낮은 필터링 작업을 위해 설치된 상태로 유지됩니다. deepseek-coder는 두 번째 의견을 듣기 위해 디스크에 보관합니다. 계약이 이상하게 느껴지는데 qwen이 무시할 때, 저는 deepseek을 실행하여 그것이 지적하는 부분을 읽으며, 이를 진실의 원천(truth source)이라기보다 의심 생성기(suspicion generator)로 취급합니다. 서로 다르게 틀린 두 모델이 의견이 일치하지 않는 지점이 종종 제가 더 면밀히 살펴봐야 할 바로 그 지점이며, 두 모델을 모두 사용해도 비용은 0달러이고 내 기기에서 나가는 데이터도 전혀 없습니다.
틀렸지만 의심스러운 모델과 조용하지만 정확한 모델 중 어떤 것이 당신의 워크플로우에 더 적합합니까?
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기