본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 05. 27. 18:53

AI 생성 코드 보안: 당신이 배포했다면, 당신이 책임져야 합니다

요약

AI 생성 코드를 사용할 때 발생하는 보안 책임과 위험성을 경고합니다. AI가 생성한 코드는 겉보기에 완벽해 보일 수 있으나, SQL 인젝션이나 취약한 인증 흐름 같은 보안 결함을 포함할 수 있으며 개발자의 검토 없는 머지는 심각한 사고로 이어질 수 있습니다.

핵심 포인트

  • AI 생성 코드에 대한 최종 책임은 개발자에게 있음
  • LLM은 보안 위협 모델이나 시스템 경계를 이해하지 못함
  • AI 생성 테스트는 예외 케이스보다 해피 패스에 치우치는 경향이 있음
  • 검토 없는 코드 머지는 기술 부채와 아키텍처 드리프트를 유발함

한 개발자가 금요일 오후에 풀 리퀘스트 (Pull Request)를 머지 (Merge)합니다. 월요일 아침, 보안 검토 과정에서 새로운 엔드포인트 (Endpoint)의 SQL 인젝션 (SQL injection) 결함이 발견됩니다. 무슨 일이 일어났는지 묻자, 돌아온 대답은 이렇습니다. “Copilot이 그 부분을 생성했어요. 저는 제대로 읽어보지 않았습니다.”

그 대답은 버그보다 더 심각합니다.

AI 생성 코드의 보안 문제는 단순히 모델이 안전하지 않은 패턴을 생성할 수 있다는 점만이 아닙니다. 문제는 팀들이 코드를 작성하는 행위와 함께 소유권 (Ownership) 또한 외주를 줄 수 있는 것처럼 배포된 코드를 취급하기 시작한다는 점입니다. 스캐너 (Scanner), 정적 분석 보안 테스트 (SAST), 그리고 의존성 감사 (Dependency audit)는 여전히 중요하지만, 이들은 더 근본적인 실패의 하류 단계에 위치합니다. 즉, 누군가가 자신이 이해하지 못한 코드를 머지했다는 사실입니다. AI 생성 코드를 보안하려면, 먼저 그 문제부터 해결해야 합니다.

"완료"라는 환상 (그리고 당신이 물려받게 될 취약점들)

AI 코드 생성은 빠릅니다. 그 속도는 개발자의 머릿속에 위험한 지름길을 만듭니다. 코드가 몇 초 만에 나타났으니, 작업이 거의 끝났을 것이라고 생각하게 되는 것입니다.

하지만 그렇지 않습니다.

LLM (Large Language Model)에서 나온 코드는 익숙한 위험을 내포하고 있으며, 종종 신뢰할 수 있을 만큼 세련된 방식으로 이를 포장하여 제공합니다. 학습 데이터에는 다음과 같은 안전하지 않은 패턴들이 포함되어 있습니다: 문자열로 구축된 SQL 쿼리 (SQL queries), 오래된 인증 흐름 (Authentication flows), 하드코딩된 비밀 값 (Hard-coded secrets), 허용 범위가 넓은 CORS 정책 (CORS policies), 취약한 검증 (Validation), 그리고 프로덕션 (Production) 환경에 절대 포함되어서는 안 될 오래된 블로그 포스트에서 복사된 예시들입니다. 모델은 당신의 위협 모델 (Threat model), 신뢰 경계 (Trust boundaries), 또는 어떤 엔드포인트가 공용 인터넷에 노출되어 있는지 알지 못합니다. 모델은 자신이 제안한 DbContext가 당신의 테넌트 경계 검사 (Tenant boundary checks)를 우회한다는 사실도, 자신이 도입한 헬퍼 (Helper) 함수가 팀이 다른 모든 곳에서 사용하는 권한 부여 정책 (Authorization policy)을 건너뛴다는 사실도 알지 못합니다.

테스트 측면의 상황도 다르지 않습니다. AI가 생성한 테스트는 해피 패스 (Happy paths)에 치우쳐 있습니다. 이들은 요청이 잘 형성되어 있고 호출자가 권한을 가지고 있을 때 엔드포인트가 200 OK를 반환하는지 확인합니다. 하지만 잘못된 형식의 입력 (Malformed input), 권한 상승 (Privilege escalation), 동시성 실패 (Concurrency failures), 레이스 컨디션 (Race conditions), 또는 실제 사고를 유발하는 권한 부여 (Authorization)의 예외 케이스들을 조사하는 경우는 거의 없습니다. 이는 팀에게는 통과된 빌드 (Green build)를 제공하고, 잘못된 안전감을 심어줍니다. 커버리지 (Coverage)는 올라가고, 자신감도 올라갑니다. 하지만 시스템이 더 안전해진 것은 아닙니다.

이것이 바로 여러분이 의존성 지옥과 아키텍처 드리프트 (Dependency hell and architectural drift)에 빠지게 되는 방식입니다. 각각의 풀 리퀘스트 (Pull request)는 검토되지 않은 가정을 하나씩 추가합니다: 오래된 패키지, 불필요한 추상화, 숨겨진 권한 확인, 아무도 설명할 수 없는 헬퍼 메서드(Helper method) 같은 것들 말입니다. 개별적인 변경 사항은 무해해 보입니다. 하지만 이들이 모이면 팀이 더 이상 이해할 수 없는 공격 표면 (Attack surface)을 만들어냅니다.

이것이 바로 "완료(Done)"라는 환상 뒤에 숨겨진 진짜 위험입니다. AI는 미완성된 작업을 완성된 것처럼 보이게 만들며, 팀은 생성된 결과물을 완성된 엔지니어링으로 혼동하는 순간 취약점을 물려받게 됩니다.

재투자된 시간 가설 (The Reinvested-Time Thesis)

AI 지원 개발에 대한 흔한 주장은 표면적으로는 합리적으로 들립니다: "AI가 하루에 두 시간을 아껴줍니다."

더 나은 질문은 그 두 시간이 어떻게 쓰이느냐 하는 것입니다.

만약 그 답이 "더 많은 기능을 출시한다"라면, 그 거래는 잘못 이해된 것입니다. AI는 업무의 어려운 부분을 제거하지 않았습니다. 단지 타이핑(Typing)의 일부를 제거했을 뿐입니다. 보일러플레이트 (Boilerplate) 생성, DTO 매핑, CRUD 스캐폴딩 (Scaffolding), 반복적인 핸들러 (Handlers), 그리고 테스트 스켈레톤 (Test skeletons)은 더 빠르게 만들어질 수 있습니다. 하지만 아키텍처적 판단 (Architectural judgment)이 저렴해진 것은 아닙니다. 보안 검토 (Security review)가 쉬워진 것도 아닙니다. 통합 테스트 (Integration testing)는 여전히 시스템이 데모에서 어떻게 작동하는지가 아니라, 부하 상황에서 어떻게 실패하는지를 이해하는 사람을 필요로 합니다.

저는 이것을 **재투자된 시간 가설 (Reinvested-Time Thesis)**이라고 생각합니다. AI가 코드 생성에서 아껴주는 모든 시간은 모델이 책임질 수 없는 작업에 재투자되어야 합니다.

이는 생성된 코드를 덜 주의 깊게 보는 것이 아니라, 더 세심하게 읽어야 함을 의미합니다. 이는 npm install이나 dotnet restore가 제안된 내용을 의존성 (dependency)으로 바꾸기 전에 패키지 추가 사항을 추적하는 것을 의미합니다. 모델이 건너뛴 부정 테스트 (negative tests)를 작성하는 것을 의미합니다. 생성된 추상화 (abstraction)가 의도한 아키텍처 (architecture)와 일치하는지, 아니면 팀이 향후 2년 동안 지원해야 할 두 번째 패턴을 조용히 도입했는지 확인하는 것을 의미합니다.

AI를 순수한 가속 (acceleration) 도구로 취급하는 팀은 종종 이해도가 낮은 상태에서 더 빠르게 제품을 출시합니다. AI를 시간 재할당 (time reallocation) 도구로 취급하는 팀은 인간의 판단력을 마땅히 있어야 할 곳에 유지하며, 출시하는 결과물에 대한 확신을 높입니다. 결과물은 여전히 더 빠르게 나올 수 있습니다. 차이점은 절약된 시간이 더 큰 스프린트 (sprint) 약속 속으로 사라지는 대신, 리뷰 (review), 검증 (verification), 그리고 시스템 사고 (system thinking)에 사용된다는 점입니다.

이러한 거래는 대시보드상에서는 덜 흥미로워 보일 수 있습니다. 하지만 프로덕션 (production) 환경에는 훨씬 더 좋습니다.

툴링 공급망 (Tooling Supply Chain): 이제 더 이상 npm만의 문제가 아닙니다

대부분의 팀은 이미 런타임 의존성 (runtime dependencies)에 대한 공급망 리스크 (supply chain risk)를 이해하고 있습니다. package.json을 검사하고, packages.lock.json 또는 package-lock.json을 감사하며, 새로운 NuGet 패키지를 검토하고, 알려진 CVE를 주시합니다. 좋습니다. 계속 그렇게 하십시오.

이제 공급망은 더 커졌습니다.

VS Code나 JetBrains Rider에 AI 코딩 확장 프로그램 (extension)을 설치할 때, 여러분은 단순히 무해한 편의 기능을 추가하는 것이 아닙니다. 여러분은 소프트웨어에 소스 코드, 프롬프트 (prompts), 파일 내용, 그리고 비밀 정보 (secrets)나 내부 계약 (internal contracts)이 포함될 수 있는 주변 컨텍스트 (context)에 대한 광범위한 가시성을 부여하는 것입니다. 에이전트 (agent)를 MCP 서버에 연결할 때, 여러분은 로컬 파일, 셸 명령 (shell commands), Git 상태, 이슈 트래커 (issue trackers), 데이터베이스 스키마 (database schemas), 또는 내부 문서를 노출할 수도 있습니다. 이러한 권한은 새로운 프로덕션 의존성만큼이나 중요하며, 어떤 경우에는 여러분이 배포하는 코드의 상류 (upstream)에 위치하기 때문에 그보다 더 중요할 수도 있습니다.

그것은 관점을 바꿉니다. AI가 생성한 코드와 제3자 패키지(third-party packages)는 모두 당신이 직접 작성하지 않은 코드의 형태입니다. 이들은 동일한 수준의 회의론적 시각을 가져야 합니다. 환각(Hallucinated)된 임포트(import)는 타이포스쿼팅(typosquatted)된 패키지를 불러올 수 있습니다. 제안된 라이브러리는 방치되었거나, 오래되었거나, 혹은 당신의 표준과 호환되지 않을 수 있습니다. 도구 자체는 데이터를 유출하거나, 컨텍스트를 과도하게 수집하거나, 개발 환경 내에서 안전하지 않은 자동화를 위한 경로를 만들 수도 있습니다.

.NET 팀의 경우, 이는 .csproj 파일의 모든 새로운 패키지 참조를 검토하고, 생성된 예제가 매개변수화된 쿼리(parameterized queries)나 EF Core API를 사용하는 것이 더 안전한 상황에서 생(raw) SQL을 사용하는지 확인하는 것을 의미할 수 있습니다. TypeScript 팀의 경우, 생성된 import 문을 해롭지 않은 접착 코드(glue code)가 아니라 공급망 검토(supply chain review)의 일부로 취급해야 함을 의미합니다. 두 경우 모두, 코드를 생성한 근본적인 원인인 에디터 확장 프로그램(editor extensions), 에이전트 권한(agent permissions), 그리고 MCP 통합(MCP integrations)을 검토해야 함을 의미하기도 합니다.

만약 당신이 검토 없이 미지의 런타임 의존성(runtime dependency)을 추가하지 않을 것이라면, 미지의 AI 도구에 당신의 저장소(repo), 환경, 또는 머지 경로(merge path)를 신뢰해서도 안 됩니다.

1인칭 머지 규칙 (The First-Person Merge Rule)

팀이 책임감을 잃어가는 것을 발견하는 가장 빠른 방법은 사고 발생 후 팀이 어떻게 말하는지를 듣는 것입니다.

“Claude가 그 엔드포인트를 추가했어요.”
“Copilot이 null 체크를 놓쳤어요.”
“Codex가 권한 부여 속성(authorization attribute)을 잊어버렸어요.”

이러한 언어는 무해하게 들립니다. 하지만 그렇지 않습니다. 개발자가 머지된 코드를 3인칭으로 설명하는 순간, 책임은 그것을 배포한 사람들로부터 멀어지기 시작합니다. 작성자가 도구가 실제 출처임을 암시할 수 있기 때문에 리뷰는 느슨해집니다. “AI가 그랬어요”라는 말은 “왜 사람이 그것을 승인했는가?”라는 더 어려운 질문을 차단하기 때문에 회고(Retros)는 더 모호해집니다. 주니어 개발자들은 이러한 표현을 듣고 AI 시대의 최악의 교훈을 배우게 됩니다: 결과물이 모델로부터 나왔다면, 이해하지 못한 채 코드를 배포하는 것은 용인될 수 있다는 교훈 말입니다.

제가 팀들이 채택하기를 바라는 규칙은 간단합니다: 당신이 머지했다면, 당신이 작성한 것입니다.

저는 이것을 **1인칭 머지 규칙 (First-Person Merge Rule)**이라고 부릅니다.

이 규칙에 따르면, 올바른 문장은 “Copilot이 검증을 누락했다”가 아닙니다. 올바른 문장은 “내가 검증 없이 코드를 머지했다”입니다. “Claude가 안전하지 않은 쿼리를 생성했다”가 아니라, “내가 안전하지 않은 쿼리를 승인했다”가 되어야 합니다. 도구가 텍스트를 생성했을지는 몰라도, 머지를 클릭하거나, PR (Pull Request)을 승인하거나, 팀을 대신하여 리스크를 감수하지는 않았기 때문입니다.

이것은 비난을 위한 연극 (blame theatre)이 아닙니다. 소유권 (ownership)의 명확한 경계를 유지하기 위함입니다. 일단 그 경계가 모호해지면 — 이곳의 다른 게시물에서 책임의 안개 (the accountability fog)라고 부르는 현상이 발생하면 — 보안은 빠르게 무너집니다. 리뷰어들은 검토를 멈추게 되는데, 이는 인간 작성자가 소유자라기보다 단순히 전달자 (courier)처럼 느껴지기 때문입니다. 사고 분석 (incident analysis)은 프로세스 교정 대신 도구에 대한 비판으로 변질됩니다. 팀들은 나쁜 코드가 운영 환경 (production)에 유입되도록 방치한 무너진 규율은 무시한 채, 모델의 품질에만 집중하게 됩니다.

언어는 대부분의 엔지니어링 팀이 인정하는 것보다 더 강력하게 문화를 형성합니다. 만약 당신의 팀이 “AI가 그랬어요”라는 변명을 금지한다면, 작성자들이 결과물에 대한 책임을 진다는 것을 알게 되므로 리뷰 품질이 향상됩니다. 사후 분석 (postmortems)은 의사결정에 집중할 수 있게 되어 개선됩니다. 책임이 더 이상 전가될 수 없게 되므로 보안 관련 논의도 개선됩니다.

풀 리퀘스트 (pull requests)에서 이 규칙을 사용하십시오. 회고 (retrospectives)에서 사용하십시오. 주니어 개발자를 코칭할 때도 사용하십시오. 머지 버튼에는 단 하나의 이름이 연결되어 있습니다. 그 이름이 중요합니다.

직접 작성하지 않은 코드를 실제로 소유하는 방법

소유권은 그것이 행동을 변화시킬 때에만 의미가 있습니다.

실질적인 모델은 AI의 출력물을 코드베이스에 합류한 지 이틀째 된 새로운 계약업체의 풀 리퀘스트 (Pull Request)를 다루는 것과 동일한 방식으로 취급하는 것입니다. 당신은 모든 줄을 읽을 것입니다. 가정을 의심할 것입니다. 그들이 당신의 권한 모델 (Authorization Model), 테넌시 규칙 (Tenancy Rules), 에러 핸들링 (Error Handling), 그리고 배포 제약 사항 (Deployment Constraints)을 이해했는지 확인할 것입니다. 여기에도 동일한 기준이 적용됩니다. 유용한 약식 표현은 AI를 무한한 인턴처럼 취급하는 것입니다. 즉, 빠르고 의욕적이며, 당신의 시스템에서 어떤 부분이 안전하지 않은지 모른 채 방대한 양의 코드를 생성할 수 있는 존재로 보는 것입니다.

이러한 사고방식은 의도 아키텍처 (Intent Architecture)와 결합될 때 가장 효과적입니다. 즉, 제한된 작업으로 업무를 지시하고, 모델이 건드릴 수 있는 범위를 제한하며, 모델의 낙관적인 추측 대신 시스템의 실제 실패 모드 (Failure Modes)를 기준으로 출력을 검증하는 것입니다. 그 차이는 구체적입니다. "이 정책을 강제하는 주문 취소를 위한 ASP.NET Core 엔드포인트를 생성하라"는 검토가 가능하지만, "취소 기능을 만들어라"는 나중에 당신이 책임져야 할 아키텍처 드리프트 (Architectural Drift)를 초래하는 방식입니다.

실행 가능한 체크리스트는 다음과 같습니다:

  • 생성된 코드를 실제 리뷰 게이트 (Review Gate)를 통해 실행하십시오. 이 기둥의 제로 트러스트 감사 (Zero Trust Audit)는 해피 패스 (Happy-path)의 공백, 환각된 의존성 (Hallucinated Dependencies), 하드코딩된 비밀값 (Hard-coded Secrets)과 같은 메커니즘을 다룹니다. 소유권 규칙은 그 위에 자리 잡습니다. 만약 당신이 코드 한 줄을 설명할 수 없다면, 그것을 머지 (Merge)하지 마십시오.
  • AI가 절약해 준 시간을 다음 티켓(Ticket)을 처리하는 데 쓰지 말고, 그 리뷰와 모델이 건너뛴 네거티브 테스트 (Negative Tests)에 재투자하십시오.
  • 풀 리퀘스트 (Pull Request)와 회고 (Retros)에서 '1인칭 머지 규칙 (First-Person Merge Rule)'을 채택하십시오. "AI가 했습니다"라는 문장은 허용되지 않습니다.
  • 당신의 AI 툴링을 공급망 (Supply Chain)으로 취급하십시오. 모델이 제안하는 모든 패키지의 출처 (Provenance)를 확인하고, 당신의 확장 프로그램 (Extensions), 에이전트 (Agents), 그리고 MCP 서버가 무엇을 읽고, 쓰고, 전송할 수 있는지 감사하십시오.

이 중 그 어느 것도 화려하지 않습니다. 이것은 절제된 엔지니어링 (Disciplined Engineering)입니다. AI를 사용하면서 안전을 유지하는 팀은 가장 화려한 도구를 가진 팀이 아닙니다. 그들은 코드를 배포한 인간에게 소유권 (Ownership)을 계속 귀속시킨 팀입니다.

그것이 바로 표준입니다: 당신이 배포했다면, 당신이 책임집니다.

자주 묻는 질문 (FAQ)

AI 생성 코드는 안전한가요?

기본적으로는 그렇지 않습니다. 모델은 학습 데이터로부터 보안에 취약한 패턴—문자열로 구축된 SQL (String-built SQL), 누락된 권한 확인 (Authorization checks), 하드코딩된 비밀 정보 (Hard-coded secrets)—을 재현하며, 당신의 위협 모델 (Threat model)에 대한 지식이 없습니다. AI 생성 코드는 보안을 확보할 수 있지만, 이는 당신이 다른 모든 것에 적용하는 것과 동일한 검토 (Review), 테스트 (Testing), 그리고 의존성 조사 (Dependency scrutiny)를 통해서만 가능합니다. 당신이 머지 (Merge)하는 코드의 보안은 모델의 책임이 아니라 당신의 책임입니다.

AI 생성 코드에 대한 책임은 누구에게 있나요?

그것을 머지하는 사람에게 있습니다. 도구는 텍스트를 생성할 수 있지만, 당신의 팀을 대신하여 풀 리퀘스트 (Pull Request)를 승인하거나 리스크를 수용할 수는 없습니다. 이것이 바로 '1인칭 머지 규칙 (First-Person Merge Rule)'입니다: 당신이 머지했다면, 당신이 작성한 것입니다.

AI 생성 코드의 가장 큰 리스크는 무엇인가요?

네 가지 반복되는 리스크가 있습니다: 학습 데이터로부터 상속된 보안에 취약한 패턴, 실제 실패 모드 (Failure modes)를 숨기는 해피 패스 테스트 (Happy-path tests), 환각 (Hallucinated) 또는 오래된 제3자 의존성 (Third-party dependencies), 그리고 AI 툴링 (Tooling) 자체—즉, 당신의 환경에 대해 광범위한 접근 권한을 가진 확장 프로그램 (Extensions), 에이전트 (Agents), 그리고 MCP 서버입니다.

AI 생성 코드를 어떻게 안전하게 검토하나요?

입사 이틀 차인 계약직 직원이 보낸 풀 리퀘스트 (Pull Request)처럼 취급하십시오: 모든 줄을 읽고, 모든 가정을 의심하며, 실제 실패 모드 (Failure modes)에 비추어 검증하십시오. 작동 모델은 직접적입니다. 제약하고, 검증하십시오(Constrain, Verify)—모델에게는 제한된 작업 (Bounded tasks)을 부여하고, 모델이 건드릴 수 있는 범위를 제한하며, 모델이 건너뛴 엣지 케이스 (Edge cases)를 테스트하십시오.

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0