AI 에이전트가 읽을 수 없는 코드를 작성하는 것을 멈추세요
요약
AI 에이전트가 코드를 정확히 이해하고 작업할 수 있도록 'AI 친화적인 코드'를 작성하는 방법의 중요성을 다룹니다. 좋은 코드는 인간과 AI 모두에게 명확하며, 모호한 명명 규칙이나 구조를 피하는 것이 AI의 환각을 줄이는 핵심입니다.
핵심 포인트
- AI 에이전트는 제한된 컨텍스트 윈도우 내에서 코드를 파싱하고 추론함
- 모호한 코드는 AI에게 보이지 않는 정보와 같아 환각을 유발함
- AI를 위한 코드는 결국 인간이 읽기 좋은 좋은 코드와 일치함
- 명확한 명명 규칙, 구조, 주석이 AI의 의도 파악에 결정적 역할을 함
자, 이 말이 좀 아이러니하게 들릴 수도 있습니다. 어쩌면 약간 당혹스러울지도 모르겠네요.
우리 대부분은 — 네, 저 자신도 포함해서 — 매일 코드를 작성하기(write) 위해 AI 도구를 사용합니다. GitHub Copilot, Cursor, Claude, 무엇이든 말이죠. 우리는 AI가 우리를 대신해 전체 함수를 생성하도록 합니다. 컴포넌트를 리팩터링(refactor)하고, API를 디버깅(debug)하며, 테스트를 작성하도록 프롬프트(prompt)를 입력합니다.
그런데 어째서인지, 우리가 바로 그 AI 에이전트들에게 다시 입력하는 코드는 그들을 완전히 혼란스럽게 만드는 엉망진창인 상태입니다.
몇 달 전 제 프로젝트 중 하나에서 이 점을 발견했습니다. 저는 백엔드에는 Node/Express, 프론트엔드에는 React를 사용하는 풀스택(full-stack) 앱을 구축하고 있었는데, Cursor에게 세 개의 파일을 가로질러 버그를 추적하는 것을 도와달라고 요청했습니다. AI는 그냥... 중간에 포기해 버렸습니다. 존재하지 않는 변수를 계속 참조하고, 한 함수의 출력을 다른 함수의 출력과 혼동하며, 상황을 훨씬 더 악화시키는 코드를 자신만만하게 작성했습니다.
처음에는 모델(model) 탓을 했습니다. 그러다 제 코드를 살펴보게 되었습니다.
아무도 이야기하지 않는 문제
우리는 수년 동안 우리 자신을 위해 코드를 작성해 왔습니다. 혹은 무언가 불분명할 때 전화를 걸어줄 팀 동료를 위해, 또는 우리가 의도했던 것을 결국 기억해 낼 미래의 우리 자신을 위해 작성해 왔습니다.
하지만 AI 에이전트에게는 그런 사치가 없습니다. 그들은 읽는 도중에 질문을 던지지 않습니다 (물론 성능이 좋은 것들은 시도하지만, 한계가 있습니다). 그들은 컨텍스트 윈도우 (context window) — 한 번에 '볼' 수 있는 고정된 토큰(token) 양 — 를 통해 코드를 파싱(parse)하며, 그 스냅샷으로부터 모든 것을 추론하려고 시도합니다.
만약 당신의 코드가 아무런 사전 정보 없이 읽는 인간에게도 모호하다면, 그것을 추론하려는 AI 에이전트에게는 보이지 않는(invisible) 것이나 다름없습니다.
2026년 초 기준으로, Claude Sonnet 및 GPT-4o와 같은 모델들은 200k-450k 토큰의 컨텍스트 윈도우를 가지고 있습니다. 매우 많아 보일 수 있습니다. 하지만 실제 운영 환경의 코드베이스(codebase)는 어떤가요? 수백 개의 파일, 수천 개의 의존성(dependencies), 그리고 추상화(abstraction) 계층들이 존재합니다. 어떤 AI 에이전트도 이 모든 것을 한 번에 볼 수는 없습니다.
따라서 여러분이 Cursor에게 4개의 파일에 걸쳐 있는 버그를 수정해달라고 요청할 때, 에이전트는 불완전한 정보 하에서 추론(reasoning)을 수행하는 것입니다. 여러분의 명명 규칙(naming), 구조(structure), 주석(comments) — 이 모든 것들이 에이전트가 여러분의 의도를 이해하느냐, 아니면 코드베이스를 헤매며 환각(hallucination)을 일으키느냐를 결정짓는 차이가 됩니다.
"AI가 읽을 수 있는 코드"의 실제 의미
세부 사항으로 들어가기 전에 명확히 해두겠습니다. 저는 여러분이 AI를 위해 코드를 작성해야 한다고 말하는 것이 아닙니다. 그것은 본말전도입니다. 좋은 코드는 언제나 좋은 코드입니다. 제가 말하고자 하는 바는, 새벽 2시에 지친 인간이 읽기 좋은 코드를 만드는 요소들이, 제한된 컨텍스트(context)로 작업하는 AI 에이전트가 읽기 좋게 만드는 요소와 _정확히 일치한다_는 것입니다.
AI가 읽을 수 있는 코드란 그저... 좋은 코드입니다. 우리가 그것을 쓰는 법을 잊어버렸을 뿐입니다.
우리 모두가 저지르는 죄악들
1. 아무 의미도 없는 변수 이름
이 부분은 우리 모두가 더 나은 방법을 알고 있기에 참 부끄러운 부분입니다.
// 작년에 제 코드가 실제로 이랬던 모습입니다
const d = await fetchData(u);
const r = process(d);
...
d가 무엇인가요? u는요? f는요? v * m은 도대체 무엇인가요?
저는 그게 무엇을 의미하는지 압니다. 제가 썼으니까요. 하지만 이것을 AI 에이전트에 붙여넣고 에러 핸들링(error handling)을 추가해달라고 요청하면, 에이전트는 모든 것이 무엇을 나타내는지 _추측(guess)_해야만 합니다. 그리고 잘못 추측할 것입니다. 정확히 말하면, 자신 있게 추측한 뒤 망가진 코드를 작성할 것입니다.
다음과 비교해 보세요:
const userData = await fetchUserProfile(userId);
const processedUser = normalizeUserData(userData);
return processedUser.friends.map(friend => friend.views * friend.multiplier);
이제 AI 에이전트는 이에 대해 추론할 수 있습니다. normalizeUserData가 아마도 예측 가능한 무언가를 반환할 것이라는 점을 압니다. friends가 배열이라는 것도 압니다. .views와 .multiplier가 무엇에 관한 것인지도 추론할 수 있습니다. 에러 핸들링은 거의 저절로 작성될 수준입니다.
2. 너무 많은 일을 하는 함수
React 컴포넌트에 handleSubmit이라는 함수가 있었습니다. 길이는 180줄에 달했습니다. 그 함수는 폼 데이터(form data)를 검증하고, API 호출을 수행하며, 세 개의 서로 다른 상태 변수(state variables)를 업데이트하고, Redux 액션(action)을 디스패치(dispatch)하며, 분석(analytics) 로그를 남기고, 조건에 따라 사용자를 리다이렉트(redirect)까지 했습니다.
제가 Claude에게 여기에 로딩 상태(loading state)를 추가하는 것을 도와달라고 요청했을 때, 그 응답은 거의 웃음이 나올 정도로 틀렸습니다. Claude는 말 그대로 흐름을 추적할 수 없었기 때문에 네 군데의 서로 다른 위치에 setLoading(true)를 추가했습니다.
해결책:
// 수정 전: 180줄짜리 괴물 함수
async function handleSubmit(e) { ... }
...
이제 모든 하위 함수(sub-function)는 단 하나의 작업만을 수행합니다. AI 에이전트는 180줄짜리 난장판 전체를 이해할 필요 없이, 그중 어떤 것이든 이해하고, 수정하거나, 확장할 수 있습니다.
3. 도처에 널린 매직 넘버 (Magic Numbers)
# 여기서 86400은 무엇을 의미하나요?
if time_diff > 86400:
send_reminder_email(user)
저는 수많은 코드베이스에서 이런 모습을 보았습니다. 제가 작성한 코드도 포함해서 말이죠. AI는 86400이 하루의 초(seconds) 단위라는 것을 전혀 알지 못합니다. 그것이 타임아웃(timeout) 값인지, 데이터베이스 ID 제한인지, 파일 크기인지 — 무엇이든 될 수 있습니다.
SECONDS_IN_A_DAY = 86400
if time_since_last_login > SECONDS_IN_A_DAY:
...
이제 명확해졌습니다. 인간에게도, AI에게도, 그리고 당신이 3개월 후에 다시 돌아왔을 때도 말이죠.
4. 누락되었거나 쓸모없는 주석
나쁜 주석에는 두 가지 종류가 있습니다:
// 주석이 전혀 없음 (나쁨)
async function sync(items, flag) {
if (flag) return items.filter(i => i.s === 'a');
...
// 완전히 쓸모없는 주석 (어떻게든 더 나쁨)
// 이 함수는 아이템을 동기화합니다
async function sync(items, flag) {
...
이 두 가지 모두 AI 에이전트가 이 로직이 왜 존재하는지, 어떤 예외 케이스(edge cases)를 처리하는지, 또는 flag가 무엇을 나타내는지 이해하는 데 도움이 되지 않습니다.
/**
* 데이터베이스와 인벤토리 아이템을 동기화합니다.
*
...
이것이 바로 AI 에이전트가 구문(syntax)뿐만 아니라 의도(intent)를 이해할 수 있게 해주는 주석의 종류입니다. 큰 차이가 있습니다.
5. 파일 간의 일관성 없는 패턴
이것은 미묘한 문제이지만 AI 에이전트의 성능을 확실히 망가뜨립니다.
한 파일에서는 다음과 같이 작성합니다:
const { data, error } = await supabase.from('users').select('*');
다른 파일에서는 다음과 같이 작성합니다:
const response = await api.get('/users');
const users = response.data.users;
또 다른 파일에서는 다음과 같이 작성합니다:
fetchUsers().then(setUsers).catch(console.error);
세 가지 서로 다른 비동기 패턴 (async patterns). 세 가지 서로 다른 에러 처리 (error handling) 방식. 세 가지 서로 다른 결과 저장 방식. AI 에이전트 (AI agent)가 당신의 앱 데이터 흐름을 이해하려고 할 때, 이러한 불일치는 에이전트가 이전의 가정을 전혀 활용하지 못한 채 모든 파일을 매번 새로운 퍼즐처럼 다루도록 강요합니다.
하나의 패턴을 선택하세요. 그리고 모든 곳에 사용하세요. 당신의 팀이 고마워할 것입니다. AI가 고마워할 것입니다. 미래의 당신이 고마워할 것입니다.
실제 시나리오: Claude를 이용한 디버깅 (Debugging)
제 동기(저희 둘 다 소프트웨어 공학을 공부하고 있고, 그는 학기 프로젝트를 진행 중이었습니다)에게 일어났던 일을 말씀드리겠습니다.
그는 꽤 표준적인 REST API를 가진 Node.js 백엔드를 가지고 있었습니다. 인증 미들웨어 (authentication middleware)에 문제가 생겼습니다. 그는 인증 파일을 Claude에 붙여넣고 무엇이 잘못되었는지 물었습니다.
Claude는 다음과 같이 답변했습니다:
"당신의
verifyToken함수는req.headers.authorization을 사용하고 있는 것으로 보이지만, 라우트 (routes) 파일의 미들웨어는 토큰을 다른 형식으로 기대하고 있을 수 있습니다..."
문제는 라우트 파일이 붙여넣어지지 않았다는 점이었습니다. Claude는 인증 파일을 통해 토큰을 처리하는 라우트 파일이 아마도 존재할 것이라고 '추론 (inferring)'하고 있었습니다. Claude는 교육적인 추측 (educated guesses)을 하고 있었으며, 때로는 그것이 맞았고, 때로는 완전히 틀렸습니다.
제 동기가 인증 파일을 정리했을 때 — 더 나은 네이밍 (naming), 더 명확한 구조, 기대되는 헤더 (header) 형식을 설명하는 주석 추가 — 그리고 다시 붙여넣었을 때, Claude는 즉시 실제 버그를 찾아냈습니다. 그는 토큰이 완전히 검증되기 전에 next()를 호출하고 있었습니다.
동일한 모델. 동일한 Claude 버전. 하지만 다른 코드 품질. 결과는 완전히 달랐습니다.
2026년의 맥락: 이것이 그 어느 때보다 중요한 이유
Stack Overflow Developer Survey 2025에 따르면, 개발자의 약 76%가 이미 개발 프로세스에서 AI 도구를 사용하고 있거나 사용할 계획을 가지고 있습니다. 2026년에는 그 숫자가 거의 확실히 더 높아질 것입니다. 도입 속도는 둔화되지 않고 있습니다.
하지만 여기서 중요한 점은, 이러한 도구들이 점점 더 '에이전트적 (agentic)'으로 변하고 있다는 것입니다. 이제 단순히 코드 라인을 자동 완성 (autocompleting)하는 수준에 머물지 않습니다. 이들은 다음과 같은 일을 수행합니다:
- 코드베이스 전체에 걸쳐 다단계 작업 수행 (Running multi-step tasks)
- 테스트 자동 작성 및 실행
- Pull Request (PR) 생성, 차이점 (diffs) 검토, 리팩터링 (refactor) 제안
- 로그를 읽고 파일을 추적하여 디버깅 (Debugging) 수행
이러한 에이전트들이 더 자율적으로 변할수록, 여러분의 코드 품질은 에이전트의 성공 여부를 결정짓는 의존 변수가 됩니다. 여러분은 실질적으로 AI가 단순히 제안만 하는 것이 아니라, 읽고, 실행하고, 수정하게 될 코드를 작성하고 있는 것입니다.
만약 여러분의 코드를 읽을 수 없다면, 여러분의 AI 에이전트는 눈을 가린 채 비행하는 것과 같습니다.
즉각적인 개선 사항: 오늘 바로 시작할 수 있는 것들
제가 실제로 큰 효과를 보았던 방법들은 다음과 같습니다:
명명 규칙 (Naming)에 대하여:
- 함수는 동사여야 합니다:
getUserById,validateEmailFormat,sendWelcomeEmail - 불리언 (Boolean)은 질문 형태여야 합니다:
isLoggedIn,hasPermission,shouldRedirect - 배열 (Array)은 복수 명사여야 합니다:
userIds,selectedProducts,pendingOrders
함수 (Functions)에 대하여:
- 하나의 함수는 하나의 작업만 수행해야 합니다. 진심입니다. 함수가 하는 일에 따라 이름을 지으세요. 만약 이름에 "그리고 (and)"가 들어가야 한다면, 함수를 분리하세요.
- 함수 길이를 30줄 미만으로 유지하는 것을 목표로 하세요. 엄격한 규칙은 아니지만, 직관적인 점검 기준으로 삼기 좋습니다.
주석 (Comments)에 대하여:
- '무엇(what)'이 아니라 '왜(why)'를 주석으로 다세요. '무엇'은 코드에 이미 나타나 있습니다. '왜'는 보통 나타나 있지 않습니다.
- JSDoc이나 Python docstring을 사용하세요. 도구가 필요해서라기보다 (도움은 되지만), 함수를 단 한 문장으로 설명하도록 강제하기 위해서입니다.
구조 (Structure)에 대하여:
- 관련된 코드는 서로 가까이 두세요. 하나의 파일만 주어진 AI 에이전트가 다른 세 개의 파일 없이도 그 목적을 이해할 수 있어야 합니다.
- 함수와 함께 사용하는 타입 (type) / 인터페이스 (interface)를 함께 내보내기 (export) 하세요.
일관성 (Consistency)에 대하여:
- 자신(또는 팀)을 위한 간단한 컨벤션 (conventions) 문서를 작성하세요. 규칙이 세 개라도 없는 것보다는 낫습니다.
- 비동기 (async) 패턴을 선택했다면 그것을 고수하세요. 명명 규칙을 선택했다면 그것을 고수하세요.
주의할 점: 과유불급
솔직히 말씀드리면, 이 방식이 지나치게 과해질 위험도 있습니다.
- 과도한 주석 (Over-commenting)은 노이즈를 생성합니다. 모든 줄에 주석이 달려 있다면, 정작 중요한 것이 눈에 띄지 않습니다.
- 과도한 추상화 (Abstraction)는 코드를 더 쉽게 만드는 것이 아니라 오히려 따라가기 어렵게 만들 수 있습니다. 30줄짜리 함수를 3줄짜리 함수 10개로 쪼개는 것은 때때로 그저 미로를 만드는 꼴이 됩니다.
- 명명 (Naming)에 너무 집착하면 줄 길이와 가독성을 해치는 터무니없이 긴 변수 이름이 생길 수 있습니다.
균형이 중요합니다. 목표는 코드를 AI를 위해 완벽하게 만드는 것이 아니라, 코드를 좋게 만드는 것입니다. 좋은 코드는 의도가 명확하고, 단일 책임 (Single Responsibility)을 가지며, 정직한 문서화 (Documentation)가 되어 있기 때문에 결과적으로 AI 에이전트와도 잘 작동하게 됩니다.
AI를 위해 쓰지 마세요. 명확하게 쓰세요. 결국 그 두 가지는 같은 결과로 이어집니다.
마지막으로 한 가지
제가 계속해서 되새기는 Martin Fowler의 인용구가 있습니다:
"컴퓨터가 이해할 수 있는 코드를 작성하는 것은 어떤 바보라도 할 수 있습니다. 훌륭한 프로그래머는 인간이 이해할 수 있는 코드를 작성합니다."
그는 이 말을 1999년에 했습니다. 하지만 2026년인 지금, 저는 다음과 같은 결론을 덧붙이고 싶습니다:
훌륭한 프로그래머는 인간 그리고 AI 에이전트가 모두 이해할 수 있는 코드를 작성합니다. 왜냐하면 여러분의 배포를 돕는 도구들은 그 도구들이 추론할 수 있는 코드만큼만 유용하기 때문입니다.
여러분의 코드베이스 (Codebase)는 여러분의 컨텍스트 (Context)입니다. 읽기 쉽게 만드세요.
참고 문헌 및 출처
-
Stack Overflow Developer Survey 2025 — 개발자들 사이의 AI 도구 도입 현황
-
Cursor — AI Code Editor — 에이전트 기반 코딩 도구
-
Martin Fowler, Refactoring (1999) — 코드 명확성에 대하여
-
Anthropic Claude Context Windows — 2026년 모델 사양
-
OpenAI GPT-4o Technical Details — 컨텍스트 윈도우 (Context Window) 및 기능
-
Clean Code by Robert C. Martin — 명명, 함수, 주석
-
The Pragmatic Programmer — 일반적인 소프트웨어 공학
웹상의 다른 채널에서 저를 찾아보세요:
- Medium: @syedahmershah
- DEV.to: @syedahmershah
- Hashnode: @syedahmershah
- GitHub: @ahmershahdev
- LinkedIn: Syed Ahmer Shah
- Portfolio: ahmershah.dev
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기