seojuny.dev

AI가 짠 코드, 얼마나 믿어도 될까

8분
  • #AI
  • #Coding Agent
  • #AGENTS.md
  • #LLM

요즘은 바이브코딩(vibe coding)으로 아무나 AI를 이용해 간단한 웹 앱 프로젝트 하나쯤은 만들 수 있는 시대다. 나 역시 코드를 직접 짜는 일이 줄었다. 예전엔 함수 하나도 손으로 다 쳤는데, 요즘은 기능 하나를 통째로 AI가 짜고 나는 검사만 한다. 편해진 만큼 이렇게 다 맡겨도 되나 싶은 걱정도 커졌다.

프로젝트가 작을 땐 AI가 뭘 만들었는지 금방 파악할 수 있다. 문제는 커질 때다. 코드가 많아질수록 다 확인하기 힘들어진다. 코드 짜는 시간을 줄이려고 AI를 쓰는데, 정작 확인하는 시간이 더 늘어나는 셈이다. 그래서 새 프로젝트를 시작할 때마다 챙기면 좋을 만한 것들을 정리해본다.

AGENTS.md

프로젝트를 시작하면 가장 먼저 AGENTS.md라는 파일을 만든다. 이 프로젝트가 뭘 하는지, 절대 깨지 말아야 할 원칙이 뭔지, 어떤 명령으로 서버를 켜고 테스트를 돌리는지, 폴더 구조는 어떻게 생겼는지, 커밋 메시지는 어떻게 쓰는지 적어둔다. 대략 이런 식이다.

# AGENTS.md
 
## 프로젝트 소개
개인 취미 프로젝트 트래커. FastAPI 백엔드 + React 프론트엔드.
 
## 절대 깨지 말아야 할 원칙
- 사용자 데이터는 항상 트랜잭션 안에서 다룬다.
- 외부 API 키는 코드에 직접 쓰지 않는다. 환경변수만 쓴다.
 
## 개발 명령
- 백엔드: `cd backend && uv run uvicorn app.main:app --reload`
- 프론트엔드: `cd frontend && pnpm dev`
- 테스트: `uv run pytest` / `pnpm test`
 
## 폴더 구조
- `backend/app/` — API 라우터, 서비스, 모델
- `frontend/src/` — 컴포넌트, 페이지, 훅
 
## 커밋 규칙
Conventional Commits, 영어. 예: `feat(auth): add refresh token rotation`

이 파일이 없으면 새 세션을 열 때마다 같은 말을 반복해야 한다. 매번 하다 보면 어느 순간 안 하고 넘어가고, 거기서 실수가 나온다. 파일 하나에 적어두면 그럴 일이 없다.

Cursor, Copilot, Codex 같은 도구는 대부분 이 AGENTS.md를 공통으로 읽는다. 다만 Claude Code는 AGENTS.md 대신 CLAUDE.md를 읽는다. 여러 AI 툴을 같이 쓰면서 컨텍스트를 한곳에서 관리하고 싶으면, AGENTS.md를 본체로 두고 CLAUDE.md는 심볼릭 링크로 연결해두는 식으로 쓸 수 있다.

다만 프로젝트가 커지면 이 파일 하나로는 부족해진다. 거대한 AGENTS.md 하나는 뻔하게 망가진다. 파일이 길어질수록 정작 중요한 규칙을 놓치고, 모든 걸 다 중요하다고 적으면 결국 아무것도 중요하지 않은 셈이 된다. 그래서 이 파일은 100줄 안팎으로 짧게 유지하고, 실제 내용은 docs/ 폴더에 나눠 정리하는 편이 낫다. 대략 이런 식이다.

docs/
├── architecture.md        # 전체 구조, 도메인 나눈 방식
├── decisions/              # 왜 이렇게 정했는지 기록
│   ├── 2026-03-auth-strategy.md
│   └── 2026-05-db-choice.md
└── tech-debt.md            # 알고 있지만 아직 못 고친 것들

AGENTS.md에는 "인증 방식은 docs/decisions/2026-03-auth-strategy.md 참고" 정도로만 적어두고, 실제 맥락은 해당 문서에서 찾아 읽게 한다.

이 파일도 결국 사람 말로 쓴 부탁이라는 한계는 있다. "절대 깨지 말 것"이라고 적어도, AI가 매번 정확히 지킨다는 보장은 없다.

정적 규칙과 테스트

말로 부탁하는 것만으로 부족하면 기계로 확인하면 된다. 커밋 전에 자동으로 도는 pre-commit 훅이 그 역할을 한다.

repos:
  - repo: local
    hooks:
      - id: backend-checks
        entry: bash -c 'cd backend && uv run ruff check . && uv run ruff format --check .'
        files: ^backend/.*\.py$
      - id: frontend-checks
        entry: bash -c 'cd frontend && pnpm exec prettier --check . && pnpm exec eslint . && pnpm exec tsc --noEmit'
        files: ^frontend/.*\.(ts|tsx|js|mjs|json|css|md)$

프론트엔드 프로젝트라면 세 가지가 들어간다. prettier는 들여쓰기나 줄바꿈 같은 포맷을 맞추고, eslint는 안 쓰는 변수나 잘못된 훅 사용 같은 코드 스타일·문법 규칙을 잡는다. tsc는 타입이 안 맞는 코드를 찾아낸다. 셋 다 통과해야 커밋이 된다.

AGENTS.md가 부탁이라면, 이건 안 지키면 커밋 자체가 막히는 검사다. 스타일뿐 아니라 더 깊은 것도 강제할 수 있다. 코드 구조까지 자체 린트 규칙으로 정해두고, 어긴 코드는 커밋 단계에서 막을 수도 있다. 에러 메시지도 AI가 읽고 바로 고칠 수 있게 구체적으로 써두면 좋다. 리뷰할 때마다 반복해서 지적하던 걸 규칙 하나로 만들어두면, 그다음부턴 같은 말을 또 할 필요가 없다.

포맷과 스타일만으로는 코드가 제대로 동작하는지까지는 못 잡는다. 그래서 테스트가 필요하다. 보통은 기능부터 짜고 테스트를 나중에 붙이는데, 순서를 바꿔서 테스트부터 짜달라고 하는 편이 낫다. 뭘 검증해야 하는지 먼저 정해두면 AI가 그 기준에 맞춰 기능을 짜게 되고, 나중에 다른 부분을 고치다 이 기능이 망가져도 테스트가 바로 알려준다. 사람은 뭘 검증할지만 정하고, 테스트를 짜고 그걸 통과하는 코드를 만드는 일은 AI에게 맡기면 된다.

권한 설정

코드가 좀 별로인 것보다 훨씬 무서운 건, AI가 파일을 통째로 지우거나 원격 저장소에 강제로 밀어넣는 명령을 실행해버리는 사고다. 코드는 다시 고치면 되지만 이런 사고는 되돌리기 어렵다.

그래서 명령마다 위험한 정도를 나눠 허용 목록을 만든다. Claude Code라면 프로젝트 루트의 .claude/settings.json 파일에 이렇게 적어둔다. Cursor나 Codex 같은 다른 도구도 이름과 형식만 다를 뿐 비슷한 설정 파일을 따로 둔다.

{
  "permissions": {
    "allow": ["Bash(pnpm test:*)", "Bash(git commit:*)", "Bash(git push:*)"],
    "ask": ["Bash(git push --force:*)", "Bash(rm -rf:*)"]
  }
}

테스트를 돌리거나 커밋하는 것처럼 되돌릴 수 있는 명령은 바로 허용해두고, 강제 푸시나 시스템 설정을 바꾸는 것처럼 되돌리기 어려운 명령만 실행 전에 확인받는다. 확인을 너무 자주 받으면 오히려 안 좋다. 내용을 제대로 안 읽고 그냥 승인하는 버릇이 생기기 때문이다. 정말 위험한 명령만 골라서 확인받아야, 확인 창이 떴을 때 진짜로 멈춰서 들여다보게 된다.

세션 간 메모리

세션이 끝나면 그 안에서 나눈 이야기는 대부분 사라진다. 다음 날 다시 열면 어제 왜 그렇게 짰는지, 뭘 시도했다가 포기했는지 AI는 기억하지 못한다. 그러면 같은 시행착오를 또 반복하게 된다.

그래서 세션이 끝나도 남아야 할 정보는 마크다운 파일로 따로 적어둔다. memory/ 폴더 하나에 주제별로 파일을 나누고, 파일마다 무슨 내용인지 짧게 적는다. 예를 들어 memory/no-explanatory-comments.md 파일은 이런 식이다.

---
name: no-explanatory-comments
description: 코드에 동작을 설명하는 주석은 기본적으로 달지 않는다
---
 
동작을 설명하는 주석은 기본적으로 달지 않는다. 함수·변수 이름으로 의도가 드러나면
그걸로 충분하고, 무엇을 하는지 설명하는 주석은 코드가 바뀌어도 같이 안 바뀌어
금방 거짓말이 된다.

파일이 늘어나면 다 훑어보기 힘드니, 파일 이름과 한 줄 설명만 모은 MEMORY.md 같은 인덱스 파일을 따로 둔다. 다음 세션이 시작할 때 이 인덱스부터 불러오면, 몇 주 전에 준 피드백을 또 설명할 필요가 없다.

이 아이디어를 더 키우면, 원본 자료·AI가 관리하는 위키·둘의 관계를 정해주는 파일로 나눠서 AI가 새 자료를 읽을 때마다 위키 페이지를 직접 고쳐 쓰게 하는 방식도 가능하다. 매번 처음부터 답을 찾는 대신, 정리된 지식을 계속 최신 상태로 쌓아가는 셈이다.

토큰과 모델

여기까지 갖추면 신뢰는 높아지지만 그만큼 AI를 돌리는 횟수도 늘어난다. 코드 리뷰를 시키고, 테스트를 짜게 하고, 규칙을 지켰는지 확인시키는 일을 전부 AI에게 맡기기 때문이다. 이때 습관적으로 제일 좋은 모델만 쓰다 보면 토큰 사용량이 눈에 띄게 늘어난다.

처음엔 좋은 모델이 무조건 좋다고 생각해서 어떤 작업이든 같은 모델로 돌리기 쉬운데, 사실 작업마다 필요한 성능이 다르다. 코드 구조를 설계하거나 애매한 요구사항을 판단하는 일은 좋은 모델을 쓸 만하다. 반면 파일을 뒤져 함수를 찾거나 정해진 규칙을 지켰는지 확인하는 단순 작업은 가벼운 모델로도 충분하다.

그래서 서브에이전트를 역할별로 나누고, 역할에 맞는 모델을 붙인다. 중요한 판단을 내리는 메인 에이전트에는 가장 좋은 모델을 쓰고, 반복적인 조사나 확인을 맡는 서브에이전트에는 가벼운 모델을 붙이는 식이다. 이렇게 나누면 같은 예산으로 더 많은 검증을 돌릴 수 있다.

메인 에이전트는 강한 모델로 핵심 판단을 맡고, 코드 탐색·규칙 확인·테스트 검증 같은 반복 작업은 가벼운 모델을 붙인 서브에이전트에게 나눠 맡긴다
역할별로 모델 나눠 쓰기

마치며

여기까지 갖춰도 AI가 짠 코드를 완벽하게 믿을 수는 없다. 규칙 파일도 결국 사람이 말로 쓴 부탁이고, 그걸 읽는 AI도 완벽하지 않다. 처음부터 완벽한 세팅을 만들려고 하기보다, 일단 쓰면서 이것저것 시도해보고 안 맞으면 고치는 쪽이 현실적이다.

요즘은 유명한 개발자들의 GitHub 저장소에 들어가 그들이 어떤 AGENTS.md를 쓰는지 자주 들여다본다. 같은 문제를 어떻게 풀어 규칙에 담았는지 보면, 내 프로젝트에 빠진 부분이 눈에 들어올 때가 많다.


이 글은 사실관계나 해석에 오류가 있을 수 있습니다. 잘못된 내용이나 질문이 있으면 댓글로 편하게 남겨 주세요.

Reference