AI가 작성한 코드 리뷰에서 발견하는 5가지 허점: 권한 누락, 무효화된 예외 처리, 존재하지 않는 API 등 (Python 및
요약
AI가 작성한 코드는 외관상 완벽해 보이지만, 권한 누락, 무효화된 예외 처리, 존재하지 않는 API 사용 등 여러 허점을 가질 수 있습니다. 본 글은 이러한 잠재적 오류들을 발견하고 수정하는 5가지 방법을 제시하며, 코드 리뷰 시 개발자가 주의해야 할 핵심 포인트를 다룹니다.
핵심 포인트
- AI 코드는 완벽해 보이지만 권한 체크 누락 등의 논리적 허점이 있을 수 있습니다.
- 무효화된 예외 처리(catch-all)는 호출하는 쪽이 결과를 구별할 수 있도록 수정해야 합니다.
- 존재하지 않는 API 사용은 타입 체크를 통과해도 런타임에 실패할 수 있으므로 공식 문서를 확인해야 합니다.
- 로그에 개인 정보가 노출되거나, 요청 범위를 벗어난 변경이 발생할 수 있습니다.
AI가 작성한 코드를 검토할 때, 모든 테스트를 통과하더라도 놓치기 쉬운 5가지 허점을 작은 코드로 재현했습니다. 바로 권한 체크 누락, 무효화된 예외 처리(catch-all), 존재하지 않거나 의미가 다른 API 사용, 로그에 기록되는 개인 정보 노출, 그리고 요청 범위를 벗어난 변경입니다.
테스트의 품질이나 AI가 제안하는 패키지 이름 확인은 이전에 작성한 다른 글("AI로 테스트를 작성하기: QA팀을 위한 4단계 검증 루프", "AI가 제안한 패키지 이름을 설치 전에 레지스트리에서 확인하는 방법")에서 다루었으므로, 여기서는 생략합니다. 궁금한 부분만 골라 읽으셔도 충분합니다.
이러한 허점들은 사람이 작성한 코드에서도 발생할 수 있습니다. 다만 AI가 작성한 코드는 외관상 깔끔하고 주석이나 타입 정의까지 갖추고 있어, 보는 쪽에서 '완벽하다'라고 착각하기 쉽습니다. 각 섹션은 '자주 발생하는 형태', '실행 시 어떤 일이 벌어지는지', '리뷰에서 물어볼 것', '수정 방법' 순서로 구성했습니다.
작동 확인은 2026년 10월 17일, macOS 환경에서 진행되었습니다.
- Python 3.14.6, pytest 9.1.1
- Node.js v22.22.3 (TypeScript를 직접 실행하기 위해
--experimental-strip-types사용) - TypeScript 5.9.3 (타입 체크만 진행)
어떤 경우에 호출하는 쪽이 그것을 알 수 있는 수단이 있는지입니다. catch
내용이 return [],
return null,
return 0,
로그만 기록하는 등 어느 하나라면, 먼저 의심합니다. 수정 방법은 아무것도 감추지 않고 올리거나, 올리지 않을 거라면 '실패했다'는 값(결과 타입이나 에러가 포함된 반환값)으로 하여 호출하는 쪽이 구별할 수 있게 합니다.
Array.prototype.groupBy는 표준에 없습니다. 표준에는 Object.groupBy와 Map.groupBy가 있습니다. 이름이 더 그럴싸해서 눈으로 읽을 때는 통과해 버립니다.
// invented.ts(발췌)
type Order = { id: number; status: string };
// 존재하지 않는 메서드(Array.prototype.groupBy는 표준에 없음)
...
실행하면, 실행 시에만 실패합니다.
invented: TypeError: list.groupBy is not a function
fixed : { paid: 2, shipped: 1 }
타입 체크를 통과했다면, 실행 전에 멈춥니다. @ts-expect-error를 제거한 코드를 tsc --noEmit --strict --target es2024 --lib es2024로 돌린 결과입니다.
t.ts(3,15): error TS2339: Property 'groupBy' does not exist on type 'Order[]'.
즉, AI가 any나 // @ts-ignore를 추가해서 타입 에러를 없애지 않았는지 보는 것이 관건입니다. 리뷰에서는 '이것을 실제로 동작시켜 봤는지, 타입 체크는 통과했는지'와 '공식 문서의 어디에 적혀 있는지'를 묻습니다.
이쪽이 더 알아채기 어렵습니다. 에러가 나지 않고, 동작하고, 결과만 다릅니다.
# resub.py
import re
# '대소문자를 무시하고 치환'의 의도
...
unsafe: Secret and SECRET and ***
fixed : *** and *** and ***
re.IGNORECASE as int = 2
re.sub의 네 번째 위치 인수는 flags가 아니라 count입니다. re.IGNORECASE는 정수 2이므로, '최대 2회 치환'으로 해석됩니다. 게다가 대소문자는 구분된 채로 남아있어, 소문자의 secret만 치환되었습니다. 예외는 발생하지 않습니다. Python 3.14.6에서는 DeprecationWarning: 'count' is passed as positional argument가 나왔지만, 경고를 보지 않으면 알아차리지 못합니다.
또 하나, [10, 2, 1].sort()는 [1, 10, 2]가 됩니다(TypeScript/JavaScript의 기본값은 문자열 순). 숫자는 .sort((a, b) => a - b)로 작성합니다.
리뷰에서 묻는 것은 '인자의 순서와 의미를 공식 시그니처로 확인했는지'입니다. 위치 인수가 3개 이상 있는 호출은 키워드 인수로 작성되어 있는지 봅니다. 경고를 놓치지 않도록 CI에서 경고를 에러로 만드는(Python의 경우 -W error) 설정도 유효합니다.
'디버깅하기 쉽도록' 입력을 통째로 로그에 출력하는 형태입니다.
# pii_log.py
import logging
logger = logging.getLogger(
함수 인수에 요청 전체나 사용자 객체가 그대로 전달되지 않았는지 확인합니다. 해결 방법은 출력 항목을 명시하는 것(허용 목록 방식)입니다. '개인 정보를 삭제해라'보다는 '출력해도 되는 것을 선택하라'가 항목이 늘어났을 때 누락될 위험이 적습니다.
'청구서의 세금 계산을 고쳐달라'고 요청했는데, 로그인 처리나 락 파일까지 변경된 경우가 있습니다. 차이가 크면 읽는 사람이 대충 훑어보게 됩니다.
사람의 눈으로 추적하기 전에 기계로 확인할 수 있습니다. 변경 파일이 허용된 범위 내에 있는지 조사하는 작은 스크립트입니다.
scope_check.py
"""변경 파일이, 요청으로 허가한 범위 내에 있는지를 확인한다."""
import fnmatch
...
임시 git 리포지토리(`src/invoice/`,
`src/auth/`,
`package-lock.json`
을 변경한 브랜치)에서 `src/invoice/*`만 허용하고 실행했습니다.
python scope_check.py main "src/invoice/*"
범위 외: package-lock.json
범위 외: src/auth/login.py
종료 코드는 1이었습니다. `fnmatch`의 `*`는 `/`도 포함하여 일치하므로, `src/invoice/*`는 `src/invoice/` 아래 깊은 계층에도 일치합니다. 엄격하게 계층을 구분하고 싶다면, `pathlib.PurePath.match`나 `pathspec` 등으로 대체하세요.
범위 외가 나오면 리뷰에서 "이것은 다른 PR로 할 수 없을까요?"라고 묻습니다. 락 파일의 변경은 의존성을 추가하지 않았는데도 바뀌었다면, 반드시 이유를 확인합니다. 의존 패키지가 늘어난 경우에는 앞서 언급한 다른 기사의 방법으로 이름을 확인합니다.
- 위의 코드는 구멍 모양을 재현하기 위한 최소 예시입니다. 실제 버그는 프레임워크의 권한 메커니즘, ORM, 미들웨어 안에 나타납니다. 형태가 달라도 묻는 질문은 같습니다.
- 5가지 구멍은 전체 일부입니다. 업무 규칙의 오류(자릿수 처리, 마감일, 고객별 예외 등)는 코드를 동작시켜도 알 수 없습니다. 요구사항을 읽은 사람만이 판단할 수 있습니다.
- 정적 분석 도구로 잡힐 수도 있는 것들(빈 `catch`, 사용하지 않는 변수 등)도 있습니다. 여기서는 시도하지 않았습니다. 도구 설정은 어디까지를 기계에 맡길지 결정한 후에 하는 것이 성공적입니다. - 동작 확인은 Python 3.14.6, Node.js v22.22.3, TypeScript 5.9.3입니다.
`re.sub`의 경고나 `Object.groupBy` 유무는 버전에 따라 달라집니다.
하나만 시도해 본다면, 1번 '타인의 ID로 호출하기'입니다. 자신의 프로젝트의 조회(get) 엔드포인트를 3개 선택하여 다른 사용자 ID로 실제로 호출해 보세요. 화면을 통해 확인하는 것에서는 보이지 않지만, 직접 호출하면 보이는 경우가 있습니다.
- OWASP "A01:2021 Broken Access Control" https://owasp.org/Top10/A01_2021-Broken_Access_Control/
- Python Documentation "re.sub" https://docs.python.org/3/library/re.html#re.sub
- MDN Web Docs "Object.groupBy()" https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/groupBy
- MDN Web Docs "Array.prototype.sort()" https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
- TypeScript Documentation "tsc CLI Options" https://www.typescriptlang.org/docs/handbook/compiler-options.html
AI 자동 생성 콘텐츠
본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기