프롬프트에 267단어 명세를 앞세우면 LLM 생성 코드 결함이 줄어든다
Specification Before Generation: A Pre-Registered, Five-Model Paired Evaluation of a Specification Frame for LLM-Generated Code in Money, Time, Idempotency, and Access Tasks
무엇인가
대규모 언어모델이 쓴 코드의 보안성은 4년째 나아지지 않았다. Veracode의 2026 GenAI 코드 보안 보고서는 4년간 100개가 넘는 모델을 측정해 생성 코드가 보안 테스트를 통과한 비율이 56퍼센트, 실패가 44퍼센트였고 그 실패율이 첫 측정 이후 사실상 움직이지 않았다고 밝힌다. 학계 측정도 같은 방향이다. Pearce 등의 연구는 89개 시나리오에서 Copilot 완성 코드의 약 40퍼센트가 취약했고, Tihanyi 등은 9개 모델이 만든 C 프로그램 33만 1천 개 중 최소 62퍼센트가 형식 검증에서 취약했다고 보고했다. 규제 산업 백엔드에서 특히 문제가 되는 결함 부류는 금액 연산, 시간 처리, 재시도 안전성, 접근 제어다. 팀들은 AGENTS.md나 CLAUDE.md, cursor rules 같은 지시 파일로 대응해 왔지만, 저자가 아는 한 가장 큰 통제 실험인 ETH 취리히 그룹의 438개 과제 평가는 사람이 쓴 지시 파일도 모델이 생성한 지시 파일도 과제 성공률을 높이지 못했고 오히려 낮출 수 있으며 비용을 20퍼센트 이상 늘렸다고 결론지었다. 2025년 스택오버플로 설문에서도 응답자의 46퍼센트가 AI 도구의 정확성을 불신했고 66퍼센트가 '거의 맞지만 아주 조금 틀린' 답을 가장 큰 불만으로 꼽았다.
어떻게 동작하나
이 논문이 검증하는 것은 지시 파일이 아니라 명세(specification)다. 스타일 가이드가 어떻게 쓸지를 알려준다면, 명세는 결과가 무엇을 만족해야 하는지를 알려준다. 저자는 자신의 저서에서 정리한 Specification Frame을 267단어짜리 고정 프롬프트 서문으로 구체화했다. Role, Context, Task, Constraints 네 개의 표제 구획으로 되어 있고, 생성 전에 한 번 인스턴스화되어 모든 과제와 모든 모델에 바이트 단위로 동일한 텍스트가 앞에 붙는다. 플레이스홀더도 링크도 없고, 프레임의 어떤 문장도 특정 과제를 가리키지 않는다. 제약은 절차가 아니라 조건으로 진술된다. 금액은 decimal.Decimal이나 정수 센트로 다루고 반올림은 마지막 단계에서만 half up으로 하며 금액을 부동소수점 동등 비교로 비교하지 않는다. 재시도될 수 있는 핸들러는 호출자가 준 멱등성 키를 요구하고 확인한 뒤에만 효과를 적용한다. 비밀번호는 솔트를 친 느린 해시(scrypt, 높은 반복 수의 pbkdf2_hmac)로만 저장한다. 그 밖에 리다이렉트 대상 허용 목록 검증, 사용자 입력으로 만든 경로를 기준 디렉터리 안으로 해석·확인, 경계에서 fail-closed 입력 검증, 모호한 요구사항은 범위를 지어내지 않고 가장 엄격하게 읽기 등 일곱 개 제약이 붙는다. 파일 구조나 변수 이름, 테스트 실행 방법은 프레임이 다루지 않는다. 짧기 때문에 지시 파일에서 제기된 비용 문제가 같은 규모로 생기지 않고, 과제 독립적이므로 팀이 저장소 수준 산출물로 채택할 수 있다.
무엇과 다른가
실험은 사전등록됐다. 2026년 8월 18일 등록되어 첫 커밋에서 동결됐고, 생성 전에 네 개의 날짜 표기 부록이 붙었다. 핵심 규칙은 원샷(one-shot)이다. 생성이 시작된 뒤에는 어떤 이유로도 출력을 재생성하지 않는다. 제공자 오류나 빈 응답은 파일을 쓰지 않고 문서화된 재개 프로토콜로 재시도해 모든 과제가 팔마다 정확히 하나의 출력을 갖게 한다. 금융·의료·보험 실무에서 뽑은 50개 백엔드 과제를 네 도메인으로 나눴다. 금액 처리 12개(MO01~MO12), 멱등성·재시도 안전성 12개(ID01~ID12), 날짜·시간 12개(DA01~DA10, DT11~DT12), 인증·접근 14개(AU01~AU14)다. 각 과제를 다섯 개 벤더 계보의 최전선 모델 다섯 개에 두 번씩, 맨 프롬프트와 프레임을 앞에 붙인 프롬프트로 보내 총 500개 출력을 얻었다. 온도 0을 요청했지만 두 벤더 엔드포인트는 해당 모델군에서 이 파라미터를 받지 않아 생략됐고, 그 두 모델은 제공자 기본값으로 돌았다. 채점은 파이썬 AST 위에서 동작하는 결정적 검사기 아홉 개가 맡았다. 각 과제는 미리 등록된 검사기로만 채점되고(총 106개 등록, 검사기당 11~13개), 한 검사기는 한 출력에 최대 한 개의 발견만 기여하므로 과제 점수는 0에서 5 사이가 된다. 검사기 타당성은 생성 전 11개 known-bad·11개 known-good 픽스처 단위 시험, 50개 과제 픽스처 증명(동결 시점과 9월 19일 재실행 모두 50/50 통과), 실행 후 시드 20260905로 뽑은 10퍼센트 표본 수동 판정의 세 층으로 확립됐다. 프레임을 전혀 모르는 Bandit 1.9.4도 500개 출력 전체에 독립적으로 돌렸고 중간 이상 심각도만 셌다.
어떻게 쓰나
결과는 다섯 모델 모두에서 같은 방향이었다. 프레임 팔의 과제당 결함 감소는 평균 0.16에서 0.70건이었고, 모든 Holm 보정 부호 검정이 유의했으며 모든 부트스트랩 신뢰구간이 0을 배제했다. 250개 과제 쌍 중 150쌍은 동점, 100쌍은 차이가 났고 차이가 난 100쌍에서 프레임 팔이 95번 이겼다. 어떤 모델, 어떤 도메인에서도 프레임 팔이 더 나빠지지 않았다. 프레임을 모르는 Bandit은 맨 팔에서 중간 이상 이슈 53건, 프레임 팔에서 11건을 찾았고 이 역시 모든 모델에서 같은 방향이었다. 효과는 모델의 무프롬프트 기본값이 가장 약한 곳에서 가장 컸다. 저자는 이를 프레임이 모델에 없는 규율을 공급한다는 '바닥(floor)' 효과로 해석한다. 세 번째 가설(효과가 금액·날짜시간 도메인에 집중된다)은 서술적으로만 평가됐다. 눈에 띄는 반례도 있다. 시간대 제약을 과제 텍스트 자체에 담고 있던 DT 계열 두 과제는 어떤 모델, 어떤 팔에서도 발견을 내지 않았다. 저자는 이를 과제 텍스트가 이미 담고 있는 제약의 양이 프레임 효과를 좌우한다는 명세 설명의 예측과 맞는다고 본다.
전제와 한계
실무 함의는 세 가지로 정리된다. 첫째, 규제 산업 팀이 쓸 수 있는 가장 싼 개입은 시니어 엔지니어가 이미 아는 것을 제약으로 적어 모든 생성 앞에 놓는 것이다. 프레임은 267단어이고 호출당 비용은 결제·청구 시스템의 프로덕션 장애 한 건 비용에 비하면 반올림 오차다. 둘째, 효과가 가장 큰 결함 부류인 금액 연산과 멱등성은 보안 스캐너의 규칙 집합 밖에 있다. 스캐너 발견만으로 생성 코드 품질을 재는 팀은 이 결함을 보지 못하고 프레임의 가장 큰 이득도 보지 못한다. 이 논문이 쓴 것과 같은 도메인 특화 검사기는 작성 비용이 싸고 스캐너 옆에 놓아야 한다. 셋째, 효과 크기가 모델 기본값의 약함을 따라간다는 관찰은 이 산출물을 프롬프트가 아니라 팀이 통제하는 저장소에 두라고 말한다. 모델 라우팅이 점점 개발자 통제 밖으로 나가는 상황에서 코드와 함께 이동하는 명세가 팀이 소유하는 유일한 생성 컨텍스트다.
저자가 밝힌 한계는 분명하다. 50개 과제, 실무자 한 명이 쓴 과제, 파이썬, 도구 없는 단일 턴 생성이라는 조건은 모든 백엔드 작업을 대표하지 않는다. 검사기를 프레임을 설계한 같은 저자가 만들었기 때문에 검사기가 결함이 아니라 프레임의 문구를 탐지할 가능성이 원리상 남는다. 저자는 검사기가 AST만 보고 산문은 보지 않는다는 점, 생성 전 픽스처로 타당성을 세웠다는 점, 프레임과 무관한 Bandit이 모든 모델에서 방향을 뒷받침한다는 점을 완화책으로 든다. 더 중요한 미해결 교란도 인정한다. 프레임 팔은 267단어를 더하므로 그 효과 일부가 명세 내용이 아니라 추가된 숙고(reasoning)에서 올 수 있는데, 길이를 맞춘 중립 서문 팔도 reason-first 팔도 돌리지 않아 이 교란은 배제되지 않았다. 기능적 정확성은 측정하지 않았다. 사후 탐색적 컴파일 점검에서 맨 팔 250개 중 247개, 프레임 팔 250개 중 236개가 바이트 컴파일에 성공했고 프레임 팔 실패 14개 전부와 맨 팔 실패 3개 중 2개가 전송 중 잘린 파일이었다. 잘린 출력은 16개(Claude 14개 중 프레임 팔 12개, DeepSeek 1개, Gemini 1개)로 프레임 팔 쪽에 치우쳐 있어 결과를 프레임 팔에 유리하게 편향시킬 수 있다. 저자는 잘린 파일이 있는 쌍을 모두 제외한 민감도 분석을 제시한다. Claude 38쌍 평균 차이 0.84(부트스트랩 95퍼센트 CI 0.47~1.29, 차이 난 20쌍 중 19쌍 개선, Holm 보정 부호 검정 p=4.0×10⁻⁵), DeepSeek 49쌍 0.41(0.24~0.57, 17쌍 중 17쌍), Gemini 49쌍 0.71(0.41~1.10, 26쌍 중 24쌍), Qwen과 GPT는 변화 없음. 모든 모델별 결론이 유지되고 방향은 5/5다. 팔당 한 번만 실행했으므로 모델 내 분산은 측정하지 않았고, 온도 0에서도 추론 엔드포인트가 완전 결정적이지 않아 생성의 정확한 재현은 원리상 불가능하다. 분석의 재현은 공개된 출력 바이트로부터 정확하다.
논문은 사전등록에서 벗어난 항목도 모두 문서화한다. 생성 전 모델 명단 교체, 실행 당일 플래그십 세대 교체, 두 엔드포인트의 온도 파라미터 미수용, 실행 중 출력 상한 조정, 일시적 실패 후 재개, Bandit의 2차 채점 패스, '동일 시스템 메시지' 조항이 양 팔 모두 시스템 메시지 없음으로 실현된 점, 데이터셋 tasks.json 헤더에 n:40이 남아 있는 메타데이터 화석 등이다. 저자는 어떤 이탈도 가설·분석·성공한 출력을 바꾸지 않았다고 밝힌다. 50개 과제와 100개 프롬프트의 체크섬, 채워진 프레임, 500개 모델 출력, 검사기 아홉 개와 픽스처, 시드가 담긴 채점·통계 코드, Bandit 결과, 판정 기록, 검증 로그, 실행 로그, 네 개 부록이 붙은 사전등록이 모두 Zenodo DOI 10.5281/zenodo.22850887(버전 1.1)로 공개되어 있어 저자를 믿지 않고도 논문의 모든 수치를 원시 바이트에서 다시 유도할 수 있다. 총 API 비용은 20달러 미만이었다.