개발자가 코드를 작성하는 에이전트가 남기는 좁은 길
요약
코딩 에이전트가 코드를 작성하는 시대에 개발자들은 직접 모든 것을 하거나, 아니면 에이전트에게 전적으로 의존하는 극단적인 방식 모두 실패할 수 있습니다. 성공적인 개발자는 이 두 가지 사이의 '좁은 길'을 걸어야 하며, 이는 단순히 녹색 체크 표시만으로는 검증되지 않는 의미적 오류를 파악하는 비판적 사고 능력을 요구합니다.
핵심 포인트
- 에이전트 코드는 양적 결과물일 뿐, 의미적 정확성(meaning)이 중요함.
- AI 도구 사용 시 개발자의 인식과 실제 속도 간의 괴리가 존재함.
- 개발자는 에이전트가 생성한 코드의 비판적 검증 능력을 갖춰야 함.
Claude Code나 Codex 같은 코딩 에이전트들이 전체 작업을 수행할 만큼 충분히 좋아지자, 우리 업계의 첫 반응은 매우 거셌습니다. 개발자들이 곧 대체될 것이라고 생각했죠. 하지만 1년이 지난 지금 상황은 그보다 더 흥미롭습니다. 에이전트들이 이제 상당량의 코드를 작성하고 있으며, 이는 되돌릴 수 없는 추세입니다. 그러나 동시에 누군가는 여전히 정확한 코드가 어떤 모습인지 알아야 하며, 이 '누군가'는 비판적 사고방식을 가진 개발자여야 한다는 것도 분명해지고 있습니다. 제 관점에서는 에이전트와 인간 사이의 균형점이 눈에 보일 지점에 와 있으며, 그 균형을 솔직하게 설명하자면 그것은 좁은 길(narrow sidewalk)입니다.
한쪽 끝에는 이전처럼 계속 일하는 개발자가 있어 모든 줄을 직접 손으로 작성하고 에이전트를 장난감 취급합니다. 다른 쪽 끝에는 모든 것을 에이전트에게 맡기고 학습을 멈추는 개발자가 있습니다. 코드가 컴파일되고 테스트가 녹색(green)으로 나오면 말이죠. 이 두 사람 모두 실패할 수밖에 없습니다. 중간의 길은 좁고, 본 글에서는 실제로 그 위를 걷기 위해 무엇이 필요한지 실제 코드를 통해 설명하고자 합니다.
양적 데이터가 보여주는 양쪽 끝의 모습
METR는 2025년 초에 무작위 대조 시험(randomized controlled trial)을 진행했습니다. 이 연구에는 숙련된 오픈 소스 개발자 16명이 참여하여 자신들의 저장소에서 실제 이슈 246개를 다루었습니다. AI 도구를 사용했을 때 그들은 19% 더 오래 걸렸습니다. 더욱 흥미로운 부분은 인식(perception)이었습니다. 연구 전에는 AI가 자신들을 24% 더 빠르게 만들 것이라고 예상했지만, 속도가 느려진 후에도 여전히 20% 더 빨라졌다고 믿었습니다. 따라서 에이전트가 얼마나 도움이 되는지에 대한 우리의 느낌은 신뢰할 수 있는 측정 기준이 아닙니다.
2026년 2월에 METR는 후속 연구의 설계를 변경해야 했다고 보고했습니다. 주된 이유는 많은 개발자들이 AI 없이 일하는 것을 원하지 않게 되었고, 개발자들이 이제 2025년 초보다 더 빠를 가능성이 높았기 때문입니다. 이 두 가지 결과를 종합해 보면 그 좁은 길을 매우 잘 설명합니다. 예전 방식의 작업으로 돌아가는 것은 비현실적이지만, 에이전트 결과물에 대한 우리의 자체적인 인식을 믿는 것 또한 안전하지 않습니다.
녹색 체크 표시, 잘못된 의미
코딩 에이전트가 실제 시스템에서 작업할 때, 한 가지 실패 사례가 반복적으로 나타납니다. 코드는 타입 검사를 통과하고, 테스트도 성공하며, 빌드 상태는 녹색이지만, 여전히 동작은 잘못된 경우가 있습니다. 왜냐하면 실수는 구문(syntax)에 있는 것이 아니라 의미(meaning)에 있기 때문입니다. 체크는 오직 누군가에게 검증하도록 지시받은 것만 확인할 수 있으며, 에이전트는 코드와 매우 자주 그 체크까지 작성합니다.
구체적인 예를 들어보겠습니다. 한 엔드포인트가 사용자에게 어떤 권한이 있는지 외부 접근 서비스에 요청하고, 사용자가 아무 권한이 없을 경우 요청을 거부합니다. 전형적으로 생성된 버전은 다음과 같습니다:
public interface IPermissionClient
{
Task<IReadOnlySet<string>> GetPermissionsAsync(string userId, CancellationToken ct);
...
이 코드는 컴파일되고, 자연스럽게 읽히며, 심지어 '실패 시 폐쇄(failing closed)'에 대한 주석까지 포함하고 있어 올바른 보안 직관을 보여줍니다. 스텁(stub)에게 빈 권한 세트를 제공하고 403 에러를 예상하는 테스트는 통과합니다. 문제는 접근 서비스가 다운된 경우에도 빈 세트가 반환된다는 것입니다. 따라서 서비스 장애가 발생하면 모든 사용자에게 403이 전달되고, UI에는 접근 권한이 없다고 표시됩니다. 사용자는 권한에 대해 지원팀에 문의하지만, 아무도 실제로 문제가 있는 접근 서비스를 확인하지 않습니다.
이를 파악하려면 '사용자가 거부된 경우'와 '권한을 요청할 수 없는 경우(authority could not be asked)'가 두 가지 다른 답변이며, 이들이 서로 다른 상태 코드, 메시지, 경고를 유발한다는 것을 알아야 합니다. 이 클래스 안의 어떤 타입이나 테스트도 그 지식을 인코딩하지 못합니다. 해결책은 이러한 차이를 잃어버릴 수 없게 만드는 것입니다:
using System.Diagnostics;
public abstract record AccessDecision
...
이제 거부와 서비스 장애는 다른 타입이 되며, 스위치문(switch)을 사용하면 호출자가 각각의 경우를 처리하게 됩니다. 서비스 장애는 503 에러가 되어 모니터링 시스템에서 알림이 발생하고, 아무도 알림을 받지 않는 403 대신에 그렇게 됩니다.
Fail-Open Defaults Hide Behind Healthy Deployments
두 번째 패턴은 활성화된 적이 없었기 때문에 통과하는 검사입니다. 검증 코드는 설정이 누락되었을 때 건너뛰도록 작성되는 경우가 많으며, 이는 개발자 머신에서 편리합니다:
using System.ComponentModel.DataAnnotations;
using Microsoft.Extensions.Options;
...
배포 시 발급자(issuer) 설정이 통과되지 않으면 모든 토큰을 신뢰하고 서비스가 시작되며, 헬스 체크는 정상이고 로그에 이상한 점은 아무것도 보이지 않습니다. 오직 거부되어야 할 요청인 배포된 서비스에 대한 부정적 테스트를 통해서만 아무것도 검사되지 않았다는 것을 알 수 있습니다. .NET에서는 보안 설정이 누락되었을 때 아예 시작을 거부하는 것이 더 좋은 기본값입니다:
public static class TokenOptionsSetup
{
public static IServiceCollection AddTokenOptions(this IServiceCollection services)
...
ValidateOnStart는 호스트가 시작될 때 데이터 어노테이션 유효성 검사를 실행하므로, 누락된 발급자는 프로덕션 환경에서 조용한 구멍이 되는 대신 시작 시 OptionsValidationException을 발생시킵니다.
양방향 테스트 (Test Both Directions)
두 수정 사항 모두 테스트가 긍정적인 경우만큼 부정적인 경우를 신중하게 증명할 때만 가치가 있습니다. 여기서 중요한 것은 바로 이러한 테스트들이며, 장애(outage) 테스트는 생성된 테스트 스위트에서 보통 빠지는 부분입니다:
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.Extensions.Options;
...
이 기사의 모든 코드는 게시 전에 .NET 10에서 실행되었으며, 단순한 버전(naive versions)도 포함하여 첫 번째 방법이 실제로 장애 발생 시 403을 반환하는지, 그리고 두 번째 방법이 구성되지 않았을 때 정말로 모든 발급자를 신뢰하는지 확인했습니다.
반복되는 같은 패턴 (The Same Patterns, Again and Again)
위의 두 예시는 특별하지 않습니다. 실제로는 에이전트가 작성한 코드와 함께 프로덕션 환경까지 살아남는 실수는 몇 가지 형태를 따르는 경향이 있습니다:
- 테스트가 버그가 존재하는 경계를 정확하게 모의합니다. 통합(integration)의 각 측면은 다른 쪽 측면의 스텁(stub)을 기준으로 테스트되므로, 예를 들어 403이 의도되었으나 401과 같은 불일치는 두 스위트 모두에게 보이지 않습니다.
- 전제가 잘못되었고, 모든 검사가 그것에 동의합니다. 잘못된 극성으로 읽은 기능 플래그(feature flag)나 반대 방향을 가리킨다고 가정하는 좌표계는 서로 완벽하게 일관되지만 세상에 대해서는 틀린 코드와 테스트를 생성합니다.
- 녹색 체크가 이름보다 적게 측정합니다.
&&대신;로 연결된 릴리스 단계(release step)는 기다려야 할 CI 결과를 무시합니다. 타입 검사(type check)를 통해 구성 요소를 발견하는 테스트는 일치하지 않는 것을 조용히 건너뛸 수 있으므로, 실행할 때마다 녹색 상태를 유지하면서 커버리지(coverage)가 줄어듭니다. - 소비자 대신 생산자가 검증됩니다. 제공하는 데이터의 값을 확인하는 것은 소비자가 그 값의 다른 사본을 읽는 경우 아무것도 증명하지 못합니다.
- 읽기 쉽고 비례적이라는 것은 테스트할 수 없습니다. 페이지가 정확하고 완전히 테스트되었더라도 여전히 하나의 파일에 대해 거의 동일한 30개의 행을 보여줄 수 있습니다.
- 가드레일이 에이전트를 막습니다. 정책(policy)이 특정 동작을 차단할 때, 올바른 조치는 결정을 인간에게 돌려주는 것이지, 블록 주변의 다른 경로를 찾는 것이 아닙니다.
이 모든 것들의 공통점은 그 어떤 것도 자동화만으로는 포착되지 않는다는 것입니다. 그것들은 시스템이 무엇을 의미해야 하는지 아는 사람—도메인(domain), 아키텍처(architecture), 그리고 누가 무엇을 읽는지—에 의해 포착됩니다.
보도를 걷기 (Walking the Sidewalk)
그렇다면 실제로 보도를 걷는다는 것은 어떤 모습일까요? 제 견해로는 두 부분으로 나뉘며, 둘 다 지속적인 학습이 필요합니다.
첫 번째 부분은 코드가 어떻게 작동하는지 계속 학습하는 것입니다. 우리가 대부분의 코드를 다시 손으로 작성할 것이기 때문이 아니라, HTTP 의미론(semantics), 실패 모드(failure modes), 타입 설계(type design) 및 우리가 작업하는 플랫폼에 대한 지식이 바로 그럴듯한 답변이 언제 틀렸는지 인식하게 해주는 요소이기 때문입니다. 여기서 학습을 멈추는 개발자는 에이전트를 더 이상 검토할 수 없게 되며, 그러면 에이전트는 전혀 감독받지 못하게 됩니다.
두 번째 부분은 에이전트와 함께 작업하는 방법을 배우는 것입니다. 즉, 에이전트가 추론할 수 없는 컨텍스트와 제약 조건을 제공하는 방법, 부정적인 경우를 테스트하기 위해 주변에 검사(checks)를 배치하는 방법, 그리고 '녹색 빌드'를 증거로 받아들이기보다 그 출력물을 비평하는 방법을 배우는 것입니다. 이것은 그 자체로 하나의 기술이며, 아직 매우 초기 단계입니다.
제가 생각하기에 앞으로 몇 년 동안 잘 해낼 개발자들은 이 두 가지 모두를 동시에 계속하는 사람들일 것입니다. 보도는 좁지만, 무관해지거나 아무도 설명할 수 없는 프로덕션 사고(production incident)로 끝나지 않는 유일한 길입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기