🤖 GPT-5.4 vs Claude Sonnet 4.6 vs Gemini 3.1 Pro — 4가지 실제 시나리오에서의 에이전트 코딩 능력 비교
요약
GPT-5.4, Claude Sonnet 4.6, Gemini 3.1 Pro를 대상으로 4가지 프로그래밍 스택에서의 에이전트 코딩 능력을 비교 분석한 연구입니다. 동일한 프롬프트 하에서 모델이 코드의 정확성, 에러 처리, 유지보수성 등을 어떻게 구현하는지 실질적인 시나리오를 통해 검증했습니다.
핵심 포인트
- Go, Python, Node.js, React 등 4가지 스택 기반 비교
- 정확성, 에러 처리, 관용적 스타일 등 시니어 리뷰어 기준 평가
- Claude Sonnet 4.6이 생성 속도 면에서 압도적인 성능 기록
- 모델별 사전 지식(Priors)에 따른 코드 구성 차이 확인
세 가지 최첨단 코딩 모델이 Go, Python, Node.js (vanilla http), 그리고 React + TypeScript라는 네 가지 스택을 사용하여 동일한 작은 제품(TODO REST API 및 TODO UI)을 처음부터 작성하는 정면 비교입니다.
이것은 합성 벤치마크(Synthetic benchmark)가 아닙니다. 각 모델에는 동일한 평이한 영어 프롬프트(Prompt)가 주어졌으며, 하나의 파일을 생성했습니다. 그 결과물은 시니어 리뷰어가 PR(Pull Request)에서 사용하는 것과 동일한 기준인 정확성(Correctness), HTTP 의미론(HTTP semantics), 에러 처리(Error handling), 유효성 검사(Validation), 관용적 스타일(Idiomatic style), 그리고 유지보수성(Maintainability)을 바탕으로 평가되었습니다.
📋 목차
- 🗣️ 프롬프트 (The Prompt)
- ⚙️ 설정 (Setup)
- 🐹 시나리오 1 — Go REST API
- 🐍 시나리오 2 — Python REST API
- 🟨 시나리오 3 — Node.js REST API
- ⚛️ 시나리오 4 — React + TypeScript UI
- 🏆 종합 스코어보드 (Aggregate Scoreboard)
- 🔍 나타난 패턴들 (Patterns That Emerged)
- 🎯 모델 선택에 주는 의미 (What This Means for Picking a Model)
🗣️ 프롬프트 (The Prompt)
모든 시나리오의 모든 모델은 언어 토큰만 교체된 정확히 동일한 한 줄의 지침을 받았습니다:
"100줄의 코드 이내로 todo 기능을 제공하는 [golang / python / nodejs / reactjs] 파일을 작성해줘"
그게 전부입니다. 명세(Spec)도, 엔드포인트(Endpoints) 목록도, 유효성 검사(Validation), CORS, REST 의미론(REST semantics), 또는 접근성(Accessibility)에 대한 힌트도 없었습니다. 100줄 제한은 의도적인 것이었습니다. 이는 모델이 _무엇을 포함하고 무엇을 생략할지_에 대해 스스로 판단하도록 강제하며, 바로 이 지점에서 모델의 사전 지식(Priors)이 드러납니다. 모든 것을 추가할 여유는 없습니다. 선택해야만 합니다.
⚙️ 설정 (Setup)
소스 저장소: truongpx396/gpt-5.4_claude-sonnet-4.6_gemini-3.1-pro-coding-capability — 생성된 모든 파일은
gencode_golang/,gencode_python/,gencode_node/, 그리고gencode_reactjs/아래에 정리되어 있습니다.
세 가지 경쟁 모델 모두 GitHub Copilot을 통해 접속되었으며, 각 모델은 기본 추론(Reasoning) 설정으로 실행되었습니다.
| 모델 | 추론 모드 (Reasoning mode) | 컨텍스트 윈도우 (Context window) | 생성 속도 (Generation speed)* | 접속 방식 (Access) |
|---|---|---|---|---|
| GPT-5.4 | 중간 (기본값) | 400k | ~24 tok/s | GitHub Copilot |
| ... |
- 본 테스트 중에 측정됨 — 각 작업은 약 100행 / 약 700개의 출력 토큰을 생성했습니다. Claude Sonnet 4.6이 압도적인 차이로 가장 빨랐으며, GPT-5.4보다 약 42% 빠르고 Gemini 3.1 Pro보다 약 13% 빨랐습니다. 실제로 이는 20초 대기와 29초 대기의 차이를 의미하며, 단일 생성 (one-shot generation) 시에는 눈에 띄지만 결정적인 차이는 아닙니다. 하지만 많은 순차적 호출이 발생하는 에이전트 루프 (agentic loops)에서는 이 차이가 크게 누적될 것입니다.
각 출력물에 대해 시니어 리뷰어(senior-reviewer)가 검토하는 방식의 판결 자체는 Claude Code 내부에서 실행되는 1M-토큰 컨텍스트 윈도우를 가진 Claude Sonnet 4.7에 의해 생성되었습니다. 해당 모델은 평가 대상이 된 코드를 직접 작성하지 않았으며, 오직 읽고 채점만 수행했습니다.
리뷰 모델에 제공된 프롬프트는 모든 시나리오에서 동일했으며, 폴더 이름만 교체되었습니다:
"
gencode_golang/gencode_python/gencode_node/gencode_reactjs폴더에 있는 3개의 파일을 확인하고, 어떤 코드가 더 나은지 그리고 그 이유가 무엇인지 알려주세요."
이 실험에서 "컨텍스트 윈도우 (context window)" 열은 생각보다 중요도가 낮습니다. 각 작업은 수백 개의 토큰 내에 들어오기 때문입니다. 대신 이 지표는 각 벤더가 Copilot 내에서 자신의 모델을 어떻게 포지셔닝하는지를 보여주는 데 더 중요합니다. GPT-5.4는 헤비급 (heavyweight), Sonnet 4.6은 워크호스 (workhorse, 실무 중심형), Gemini 3.1 Pro는 프리뷰 티어 (preview tier)로 자리 잡고 있습니다.
격리 및 편향 방지 (Isolation & Bias Prevention)
각 파일은 전용의 깨끗하고 새로운 컨텍스트 (dedicated, clean, fresh context) — 즉, 이전 대화 기록이 없고, 공유된 채팅 세션이 없으며, 모델 간의 상호 참조가 없는 별도의 리포지토리(repo)에서 생성되었습니다. 생성된 후 각 출력물은 검토를 위해 별도의 목적지 리포지토리로 이동되었습니다. 결정적으로, 생성 과정 중에는 사전 설정된 규칙, 사용자 정의 지침 (custom instructions), 시스템 프롬프트 (system prompts), 또는 .github/copilot-instructions.md 파일이 전혀 존재하지 않았습니다 — 모든 모델은 순수 기본 설정 (bare defaults) 상태로 실행되었습니다. 이는 다음을 의미합니다:
- 어떤 모델도 생성 전이나 생성 중에 다른 모델의 출력을 보지 못했습니다.
- 공유된 컨텍스트 윈도우 (Context Window)를 통해 경쟁 모델 간의 스타일, 구조 또는 결정 사항이 유출될 수 없었습니다.
- 어떤 커스텀 시스템 프롬프트 (Custom System Prompt)도 특정 패턴을 따르도록 모델을 유도하거나 멀리하게 하지 않았습니다.
- 검토자 (Sonnet 4.7)는 오직 원본 파일만을 전달받았으며, 어떤 모델이 어떤 파일을 작성했는지에 대한 힌트는 전혀 받지 못했습니다.
- 파일 이름 접미사 (
_gpt-5.4,_claude-sonet-4.6,_gemini-3.1-pro)는 모든 판정이 완료된 후에만 적용되었습니다. 생성 및 검토 단계 동안 파일은 오직 번호로만 식별되었습니다 (todo_1_,todo_2_,todo_3_). 가독성을 위해 사후적으로 귀속 정보가 추가되었습니다.
목표는 앵커링 편향 (Anchoring Bias, 하나의 솔루션을 본 후 다른 솔루션을 작성하는 것), 컨텍스트 유출 (Context Bleed), 그리고 모델의 자기 편향 (Model Self-favoritism)과 같은 편향의 원인을 가능한 한 제거하는 것이었습니다.
🐹 시나리오 1 — Go REST API
순위: Sonnet 4.6 > GPT-5.4 > Gemini 3.1 Pro
우승자: Claude Sonnet 4.6
Sonnet 4.6은 Go 1.22+의 메서드 인식 라우팅 (Method-aware Routing)을 나머지 기본 사항들과 결합한 유일한 모델이었습니다. 이 모델은 r.PathValue("id")를 사용하는 mux.HandleFunc("/todos/{id}", ...)를 사용하였고, 일반적인 Content-Type / WriteHeader / Encode 3단계 과정을 제거하는 jsonResponse() 헬퍼 함수, 구조화된 JSON 에러 바디, 디스패치(Dispatch)를 위한 switch r.Method, 그리고 — 가장 중요한 점으로서 — 누락된 필드가 조용히 제로 값(Zero value)으로 초기화되지 않도록 부분 업데이트를 위한 포인터 필드 (Pointer Fields)를 사용했습니다:
gencode_golang/todo_2_claude-sonet-4.6.go:83-97
case http.MethodPut:
var body struct {
Title *string `json:"title"`
...
또한 주목할 점은 body.Title == ""를 검증하고, 기본 mux 대신 명시적인 http.NewServeMux()를 사용하며, 실제 GET /todos/{id} 경로를 노출한다는 것입니다.
2위: GPT-5.4 — 올바른 의미론, 구식 라우팅
GPT-5.4는 _의미(meaning)_는 정확하게 파악했습니다. 부분 업데이트를 위한 포인터 필드를 포함한 PATCH 방식, strings.TrimSpace 검증 등을 사용했습니다. 하지만 Go 1.22 이전의 패턴을 사용했습니다. 경로 파싱을 위해 수동으로 strings.TrimPrefix(r.URL.Path, "/todos/")를 사용하고, 일반 텍스트 에러 본문을 가진 http.Error를 사용하며, 조회와 메서드 디스패치(method dispatch)가 뒤섞인 하나의 거대한 핸들러를 작성했습니다. 마치 2020년의 Go 코드를 보는 듯합니다.
최하위: Gemini 3.1 Pro — 현대적인 겉모습, 무너진 기본기
Gemini 3.1 Pro의 코드는 가장 현대적으로 보였지만 ("GET /todos" 스타일의 라우팅), 기초적인 부분에서 실패했습니다:
strconv.Atoi(r.PathValue("id"))및json.NewDecoder(r.Body).Decode(&t)에서 발생하는 에러를 무시했습니다. → 잘못된 입력이 들어오면 400 에러 대신id=0이 됩니다.map[int]Todo를 저장소로 사용합니다. →GET /todos를 호출할 때마다 아이템이 무작위 순서로 반환됩니다. 이것은 API가 아니라 슬롯머신입니다.- 입력 검증(input validation)이 없으며, 빈 제목에 대한 방어 로직(empty-title guard)도 없습니다.
- PUT 요청 시 전체 레코드를 덮어씁니다.
completed필드를 생략하면false로 바뀝니다.
고전적인 실수 유발 요소(foot-guns)를 현대적인 문법으로 감싸놓은 형태입니다.
🐍 시나리오 2 — Python REST API (표준 라이브러리 http.server)
순위: GPT-5.4 > Sonnet 4.6 > Gemini 3.1 Pro
이 시나리오는 GPT-5.4가 압도적으로 1위를 차지한 유일한 사례입니다.
우승: GPT-5.4 — 가장 안전한 입력 처리
GPT-5.4는 지루하지만 중요한 세부 사항들을 완벽하게 처리했습니다. 항상 CORS 헤더를 송출하는 send() 헬퍼, Content-Length 누락에 대해 방어 로직을 갖춘 read_json() (다른 모델들은 int(None)에서 충돌이 발생합니다), UUID ID, createdAt 타임스탬프, OPTIONS 요청에 대한 204 No Content, 기본 요청 로그 억제, 그리고 **진정한 PATCH 의미론(semantics)**을 구현했습니다:
gencode_python/todo_1_gpt-5.4.py:21-24
def read_json(handler):
size = int(handler.headers.get("Content-Length", "0"))
raw = handler.rfile.read(size) if size else b"{}"
...
gencode_python/todo_1_gpt-5.4.py:52-61
def do_PATCH(self):
todo = self.find_todo()
if not todo:
...
오직 놓친 부분만: GET /todos/{id}가 없고, 저장소가 딕셔너리 대신 리스트입니다 (O(n) 조회).
차상위권: Sonnet 4.6 — 더 나은 데이터 모델, 약한 의미론
Sonnet 4.6은 올바른 데이터 구조인 dict 저장을 선택했습니다. 이는 O(1) 조회를 제공하며 삭제 시 깔끔한 dict.pop()을 구현했고 유용한 시작 배너까지 추가했습니다. 하지만 부분 업데이트를 **PUT**으로 레이블링했는데, 이는 RFC 7231에 따르면 의미론적으로 틀립니다 (PUT은 전체 교체 의미). 또한 title이 문자열이 아닐 경우 body["title"].strip()에서 잠재적인 AttributeError가 기다리고 있습니다.
최하위권: Gemini 3.1 Pro — 좋은 아이디어는 하나, 회귀(regressions)가 많음
Gemini 3.1 Pro는 단 하나의 진정으로 좋은 아이디어를 제공했습니다. 바로 end_headers 오버라이드를 통한 CORS 구현인데, 이는 세 모델 중 가장 DRY한 접근 방식입니다. 그 외 모든 것은 회귀적입니다: 전역 카운터에서 예측 가능한 정수 ID, 유효성 검사 부재 (빈 문자열 "" 제목이 조용히 저장됨), Content-Length 누락 시 충돌 발생 (int(None) → TypeError), DELETE가 인플레이스 수정 대신 전역 리스트를 재바인딩하는 문제 (다른 모든 참조를 깨뜨림), OPTIONS에 잘못된 상태 코드 (204 대신 200), 기본 stderr 로그 스팸.
🟨 시나리오 3 — Node.js REST API (순수 node:http)
순위: GPT-5.4 > Sonnet 4.6 > Gemini 3.1 Pro
우승자: GPT-5.4 — 가장 깔끔한 추상화
GPT-5.4의 Node 버전이 실제로 배포할 만한 코드입니다. 이 코드는 ESM import(최신 Node와 일치), 충돌 방지 ID를 위한 randomUUID() 사용, 상태 + CORS + content-type을 한 번의 호출로 전송하는 단일 send() 헬퍼, PATCH에 대한 엄격한 필드별 타입 검증, 그리고 잘못된 JSON에 대해 400 (500이 아님)을 반환하는 최상위 try/catch를 사용합니다:
gencode_node/todo_1_gpt-5.4.js:42-48
if (url.pathname.startsWith('/todos/') && req.method === 'PATCH') {
if (!todo) return send(res, 404, { error: 'todo not found' })
const { text, completed } = await readBody(req)
...
typeof completed === 'boolean' 검사는 장난감 코드를 실제 제품 코드와 구분하는 종류의 디테일입니다. Gemini가 사용하는 전개 및 임시방편적 접근 방식({ ...todos[index], ...data, id })은 클라이언트가 completed: "yes"를 작성하여 모든 사람을 위한 스키마를 깨뜨리게 만듭니다.
차순위: Sonnet 4.6 — 브라우저에서 사용하기는 깔끔하지만 불가능함
Sonnet 4.6의 Node 코드는 라우트별 JSON 파싱 오류 처리가 가장 좋고, DELETE 시에 정확히 204 No Content를 반환합니다. 하지만 CORS 헤더를 전혀 전송하지 않아 프록시 없이 브라우저 프론트엔드에서 사용하기는 불가능합니다. TODO 앱의 관점에서 이는 치명적인 제품 결함입니다.
최하위: Gemini 3.1 Pro — 장황하고 의미적으로 틀림
Go와 같은 패턴을 보입니다: PATCH가 의도된 곳에 PUT이 사용되었고, 잘못된 JSON은 400 대신 500을 반환하며, 입력 유효성 검사가 없고, 제목에 trim()이 없어 (" "도 유효한 TODO임), 그리고 ESM import 대신 require를 사용했습니다. 이는 2025년 버전의 Node 예제로는 어색합니다. 좋은 점은 하나 있습니다: for await (const chunk of req)는 세 가지 중 가장 관용적인 바디 리더입니다. 작은 승리, 많은 패배입니다.
⚛️ 시나리오 4 — React + TypeScript UI
순위: Sonnet 4.6 > Gemini 3.1 Pro > GPT-5.4
이 시나리오는 모든 차원에서 단 하나의 승자가 존재하지 않기 때문에 가장 흥미로운 시나리오입니다. 각 모델은 다른 모델이 부족했던 무언가를 보여주었습니다.
종합 우승자: Sonnet 4.6 — 최고의 아키텍처 및 기능 세트
Sonnet 4.6은 가장 완전한 TODO 기능을 생성했습니다: 추가(add), 토글(toggle), 삭제(delete), 필터(전체/활성/완료)(filter (all/active/done)), 남은 항목 카운터(items-left counter), 빈 상태(empty state), 그리고 완료 항목 지우기(clear-completed). 또한 핸들러(handlers)를 작은 이름이 지정된 함수(named functions)로 분리하였고, 모든 스타일링을 단일 s 객체로 모아 JSX가 스타일링 노이즈가 아닌 구조처럼 읽히도록 했습니다. 필터 로직은 상태(state)가 아닌 파생된 값(derived value)으로 처리되었는데, 이는 React의 관용적인(idiomatic) 방식입니다:
gencode_reactjs/todo_2_claude-sonet-4.6.tsx:10-26
const add = () => {
const text = input.trim();
if (!text) return;
...
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기