dev.to

I Gave ChatGPT My Full Codebase. The Results Scared Me — But Not for the Reason You Think.

ChatGPT에 코드베이스 전체를 맡겼습니다. 결과가 무서웠던 진짜 이유

작성자는 저장소 전체를 분석시킨 결과 유용한 결함을 찾았지만, 존재하지 않는 취약점도 그럴듯하게 지어내고 실제 결제 데이터 유실 버그는 놓쳤습니다. AI를 검토자가 아니라 문제 후보를 찾는 도구로 쓰고, 실제 코드 확인과 독립적인 테스트로 판단해야 한다고 설명합니다.

AI 요약

저자는 라우트, 마이그레이션, 설정 파일을 포함한 코드베이스 전체를 ChatGPT에 넣고 “무엇이 잘못됐나”라고 물었습니다. 보안 문제도 중요하지만 더 크게 놀란 건 답변의 확신과 정확성이 별개라는 점이었습니다. 실제 구조를 파악하고 유용한 결함도 찾았지만, 틀린 항목과 놓친 버그도 같은 어조와 형식으로 제시했습니다.

실제로 찾아낸 문제와 만들어낸 문제

모델은 인증이 미들웨어 하나를 거친다는 점, 두 서비스가 부적절하게 같은 테이블을 공유한다는 점, 결제 웹훅과 가입 경로가 서로 다른 코드에서 사용자 데이터를 갱신한다는 점을 짚었습니다. 작성자가 2022년에 저장소에 올렸다가 지운 비밀 정보가 Git 이력에 남아 있다는 사실도 발견했습니다. 웹훅과 가입 경로가 같은 행을 잠금 없이 갱신해 재시도 상황에서 한쪽 데이터가 덮일 수 있다는 경쟁 조건, 새 권한 검사를 건너뛰는 채로 남아 있던 관리자 엔드포인트도 찾아냈습니다.

하지만 항목 7은 존재하지 않는 함수에 SQL 인젝션이 있다고 주장했습니다. 항목 9는 이미 적용된 인증 검사를 놓쳤습니다. 두 오류 모두 실제 문제와 똑같이 자신감 있는 설명과 수정 제안을 붙였습니다. 작성자가 코드를 직접 열어 확인하자 두 주장은 약 30초 만에 틀린 것으로 드러났습니다. 구체적인 파일명과 변수명을 섞은 환각은 일반적인 오류보다 더 믿음직하게 들릴 수 있습니다.

코드 바깥에 있는 웹훅의 위험

가장 위험한 버그는 모델이 놓쳤습니다. 결제 웹훅 처리 코드가 먼저 결제 제공자에게 수신 확인을 보내고, 그 뒤 데이터베이스에 저장했습니다. 확인 응답 뒤 저장이 실패하거나 프로세스가 종료되면 제공자는 처리가 끝났다고 판단해 재시도하지 않습니다. 결제 데이터가 조용히 사라지지만 로그에는 오류가 남지 않을 수 있습니다.

작성자가 웹훅 처리기 자체에 문제가 없는지 다시 물었을 때 모델은 코드가 괜찮다고 답했고 주석을 추가하라고 제안했습니다. 이 결함은 한 함수 안의 코드만 읽어서는 놓치기 쉽습니다. 확인 응답과 영구 저장 사이의 순서, 그리고 제공자의 재시도 동작이 함께 얽힌 ‘경계(seam)’ 문제이기 때문입니다. 코드베이스 전체를 넣어도 실행 환경과 외부 서비스의 동작까지 분석에 포함되는 건 아닙니다.

AI는 탐색자로, 판정자로는 쓰지 않기

작성자는 결과를 실제로 맞은 세 건, 자신 있게 틀린 두 건, 치명적인 결함 하나를 놓친 경우로 정리합니다. 모델이 문제 후보를 넓게 찾아내는 ‘탐색자(finder)’라면 유용합니다. 사람이 각 항목을 확인하고 분류하면 됩니다. 반면 “이 코드는 맞으니 배포해도 된다”고 보증하는 ‘증인(witness)’ 역할은 맡기면 안 됩니다. 모델은 문제를 찾을 때와 놓쳤을 때 모두 같은 확신으로 말하기 때문입니다.

따라서 모델이 제시한 문제는 판정이 아니라 단서로 취급하고 실제 코드를 확인해야 합니다. 변경을 승인하는 검증 절차는 작성 모델과 독립적으로 구성해야 합니다. 다른 모델 계열이나 반대 관점의 프롬프트, 실제로 실행하는 테스트, 사람의 병합 승인을 조합할 수 있습니다. 같은 모델이 코드를 작성하고 테스트까지 만들면 같은 맹점이 테스트에도 들어갈 수 있습니다.

경계 문제에는 경계 테스트가 필요합니다. 웹훅의 확인 응답과 데이터 저장 사이에서 프로세스를 강제로 종료하고 제공자가 어떻게 동작하는지 확인하라고 제안합니다. 모델은 확인 응답이나 커밋이 영구 저장보다 먼저 일어나는 지점을 찾아 후보를 열거하는 데 쓸 수 있지만, 결함 여부는 장애 주입과 실제 동작으로 검증해야 합니다.

코드 전송 전 보안 점검

소비자용 채팅 제품에 독점 코드베이스를 붙여 넣는 일은 편의에 따라 자동으로 할 선택이 아니라고 경고합니다. 해당 요금제의 약관을 확인하고, 학습이나 보관을 하지 않는다는 계약이 없는 한 입력 내용이 보존되거나 학습에 쓰일 가능성을 고려해야 합니다. 자격 증명, 토큰, 고객 데이터, 내부 호스트 이름을 먼저 제거해야 합니다. 실제 업무라면 학습 금지 보장이 있는 엔터프라이즈 제품을 쓰거나 코드가 있는 환경에서 모델을 실행하라고 권합니다. 저자는 비밀 정보를 교체한 임시 복제본으로 작업했다고 덧붙입니다.

dev.to 반응

  • @danielchinasz — 말씀하신 내용은 실제로 일어나는 일입니다. AI가 코드베이스 전체를 읽고 미처 보지 못한 문제, 꽤 심각한 문제까지 찾아낼 수 있습니다. 그렇다고 엄밀하거나 그 자체로 신뢰할 수 있다는 뜻은 아닙니다. 심각한 버그를 새로 지어내기도 합니다. 테스트는 이전보다 더 엄격해야 합니다.
    • @infoinlet1 — 네, 정확합니다. 아직도 걸리는 점은 같은 도구가 전혀 눈치채지 못했을 버그를 찾고, 두 줄 뒤에는 자신감 넘치고 형식까지 완벽한 버그를 내놓는다는 겁니다. 더 엄격한 테스트가 필요하다는 데 동의합니다. 덧붙이자면 AI가 테스트까지 작성하면 AI의 맹점도 테스트에 들어갑니다. 이미 옳다고 믿는 동작을 그대로 검사할 테니까요. 그러니 ‘더 엄격하게’에는 독립적인 관점의 테스트와 한 파일 안에 갇히지 않는 경계 동작 테스트도 포함돼야 합니다. 웹훅에서 확인 응답을 저장보다 먼저 보내는 문제는 일반적인 단위 테스트에 드러나지 않습니다. 그렇지 않으면 서로 조용히 동의하는 테스트만 더 초록색이 됩니다.
  • @xiangc_92294 — “모델은 건네받은 범위 안에서 읽고, 위험은 그림이 아니라 그 범위에 있었습니다”라는 문장이 정확히 짚었습니다. LLM은 정적인 텍스트를 분석하지만, 프로덕션 장애는 분산된 상태 전이에서 일어납니다. 코드베이스 전체를 넘겨도 네트워크 분할, 서드파티 웹훅의 재시도 간격, 새벽 3시에 연결이 고갈된 데이터베이스 풀까지 넘길 수는 없습니다. 정적 코드 검토는 사람이나 AI나 문법과 뻔한 로직을 확인합니다. 장애 주입과 통합 테스트는 실제 상황을 검증합니다. 경계 버그를 잡으려면 모델에게 코드를 읽히는 대신 비동기 코드 두 줄 사이에서 프로세스를 종료해 보세요.
    • @infoinlet1 — 제가 어설프게 말하려던 내용을 더 정확하게 표현해 주셔서 감사합니다. “모델에게 코드를 읽히지 말고 비동기 코드 두 줄 사이에서 프로세스를 종료해 보라”는 말이 정확합니다. 다만 모델은 장애를 주입할 지점을 찾는 데 계속 쓸 수 있습니다. 저장소를 보여주고 “영구 저장 전에 확인 응답이나 응답을 보내거나 커밋하는 지점, 또는 await 동안 DB 연결을 잡고 있는 지점을 모두 찾아 달라”고 하면 후보 지점을 나열하는 데 꽤 능숙합니다. 그런 다음 각각에 장애를 주입하고 실제 동작으로 판단하면 됩니다. 모델은 용의자를 찾고 카오스 테스트가 유죄를 가립니다. 판사 역할만 맡기지 않으면 됩니다.
  • @morgan_todd99 — 같은 프로젝트를 오래 다루다 보면 더는 눈에 띄지 않는 문제를 AI가 코드베이스 전체를 받아 찾아낼 수 있다는 점이 특히 좋았습니다.

원문: dev.to / 번역·요약: Trawling