AWS Transform이 출시 1년 만에 의미 있는 숫자를 내놨다. Java 8/11 → Java 17 마이그레이션에서 수동 작업을 약 60% 줄였다는 보고가 나오고 있다. 테스트 커버리지 70% 이상, 의존성이 잘 정리된 Maven/Gradle 프로젝트를 전제로 한 수치지만—그래도 16년 된 레거시를 다루는 팀 입장에서는 무시하기 어려운 숫자다.
그런데 나는 이 숫자를 보면서 두 가지 생각이 동시에 든다. 하나는 '이제 진짜 쓸 수 있는 단계가 됐구나'이고, 다른 하나는 '그래서 조용히 틀리는 부분을 누가 잡는가'다. 에이전트가 프로덕션에 깊숙이 들어올수록, 실패는 요란하게 터지는 게 아니라 조용히 통과한다. 그리고 그 조용한 실패에는 패턴이 있다.
첫 번째 실패 지점: 에이전트는 모르는 걸 지어낸다
dev.to에 올라온 실제 사례 하나가 에이전트 신뢰성 문제를 정확히 짚는다. 보트의 선박 컴퓨터 역할을 맡은 로컬 LLM 에이전트(8B 모델)에게 수심을 물었더니 이렇게 답했다.
"수심 4.2미터입니다. 현재 육상에 있으므로 실제 선박이 아닌 로컬 테스트/목 장비에서 수신 중입니다."
수심 숫자는 진짜다. 도구가 반환한 값이다. 그런데 '테스트 장비', '육상', '목 데이터'—이 단어들은 어떤 도구도, 어떤 프롬프트도 말한 적 없다. 모델이 빈 맥락을 채운 것이다. 이게 바로 confabulation이다. 환각(hallucination)과 다르다. 환각은 없는 걸 만들어내지만, confabulation은 관찰 가능하지 않은 공백을 '가장 그럴듯한 연속'으로 채우고 사실인 것처럼 말한다.
더 심각한 사례도 있다. 같은 에이전트가 사이드 기능의 타임아웃 오류를 보고 이렇게 진단했다.
"로컬 모델 백엔드가 다운됐습니다. Ollama가 대시보드 호스트의 Docker 컨테이너에서 실행 중인데, 그 컨테이너가 크래시됐습니다. 지금 확인 중입니다."
백엔드는 멀쩡히 살아 있었다. Ollama는 Docker 컨테이너가 아니었다. 대시보드 호스트가 아닌 다른 머신에서 실행 중이었다. 에이전트가 docker ps를 실행해서 출력이 없자 그걸 '확인'으로 읽었는데—그 명령은 에이전트 자신의 호스트에서 실행된 것이었다. 틀린 기계에서 틀린 이유로 나온 빈 출력을 '증거'로 사용한 것이다.
이 사례가 중요한 이유는 '모델이 나쁘다'는 교훈이 아니기 때문이다. 관찰 가능한 범위를 명시하지 않으면 에이전트는 그 경계를 스스로 채운다는 것이다. 해결책은 세 가지 레이어의 조합이었다. 도구가 자신의 관점을 명시하게 하기("이 명령은 compute 호스트에서 실행됩니다"), 프롬프트에서 관찰 불가 영역을 명시적으로 정의하기, 그리고 provenance 정보를 데이터에 묶어서 반환하기. 모델에게 '틀리지 마라'고 지시하는 건 효과가 없었다.
두 번째 실패 지점: 에이전트는 어제 배운 걸 오늘 모른다
또 다른 개발자의 경험도 눈에 띈다. 그는 같은 주에 AI 코딩 에이전트와 세 번 같은 대화를 했다.
"DEV.to API가 403을 반환합니다." "User-Agent 헤더를 추가해야 합니다—기본값을 봇 필터가 차단합니다."
에이전트는 매번 정확했다. 그리고 매번 처음부터 시작했다. 월요일에 힘겹게 얻은 교훈은 세션이 닫히는 순간 증발했다. 수요일의 개발자는 월요일과 같은 디버깅 비용을 다시 치렀다. 이건 모델이 멍청한 게 아니다. 세션 내에서는 날카롭다. 문제는 모든 세션이 세션 1이라는 구조적 사실이다.
이 문제의 해결책으로 제시된 접근이 흥미롭다. 하나의 거대한 컨텍스트 파일에 모든 걸 쑤셔넣는 게 아니라, 목적별로 분리된 네 개의 마크다운 파일이다. bugs.md(알려진 버그와 해결책), decisions.md(왜 이렇게 설계됐는지), key_facts.md(엔드포인트, 사용자명, 실행 명령), issues.md(작업 로그). 그리고 에이전트 지시 파일에 네 줄의 트리거를 박았다—오류 발생 시 bugs.md를 먼저 검색하고, 아키텍처 변경 제안 시 decisions.md를 확인하라는 식이다.
핵심은 기술이 아니라 규율이다. 읽기는 쉽다. 쓰기가 규율을 요구한다. 작업이 완료될 때마다 결과와 이유를 기록하는 습관—이게 '노트가 있는 팀'과 '기억이 있는 팀'의 차이를 만든다. 이 구조는 AI 에이전트의 기억을 세션 외부 아티팩트로 외재화하고, 읽기·쓰기 규칙을 명시적으로 설계한다는 점에서 단순하지만 실질적이다.
세 번째 실패 지점: 에이전트는 테스트가 없는 곳에서 확신한다
AWS Transform의 가장 솔직한 경고는 마케팅이 아니라 기술 문서 안에 숨어 있다. 서비스 설계 자체가 '유한한 변환 공간, 결정론적 빌드, 객관적인 검증'이라는 세 가지 삼각형 위에 서 있다. Java 버전 마이그레이션이 잘 되는 이유가 바로 이 삼각형을 만족하기 때문이다.
그런데 금융 코어 뱅킹 시스템이나 15년간 누적된 패치로 덮인 레거시 시스템은 이 삼각형을 만족하지 않는다. 복리 계산 로직, 신용 적격성 규칙, 규제 예외 처리—이런 로직들은 패턴을 따라서 맞는 게 아니라, 프로덕션 인시던트 이후 수동으로 수정됐기 때문에 맞다. 그 히스토리는 문서화되지 않았다. 그 엣지 케이스는 테스트에 없다.
AWS Transform은 컴파일되고, 기존 테스트를 통과하고, 그러면서도 틀린 코드를 생성할 수 있다. 테스트가 없는 곳에서 에이전트는 confabulation과 구조적으로 동일한 실수를 한다—관찰 불가 영역을 가장 그럴듯한 패턴으로 채운다. 그 결과는 조용히 프로덕션을 통과한다.
테크 리드가 지금 당장 설계해야 할 것
세 가지 실패 지점은 같은 뿌리를 공유한다. 에이전트는 관찰 가능한 범위 밖에서 확신한다. AWS Transform은 테스트가 없는 로직에서, LLM 에이전트는 데이터 출처를 알 수 없는 공백에서, 코딩 에이전트는 이전 세션의 컨텍스트가 사라진 빈 자리에서 각각 조용히 틀린다.
이 패턴이 가리키는 설계 원칙은 세 가지다.
첫째, 에이전트의 관찰 경계를 명시하라. 도구가 반환하는 데이터에는 그 데이터가 어디서 왔는지를 함께 붙여야 한다. 에이전트가 관찰할 수 없는 영역은 '모른다'고 말하도록 시스템 프롬프트에 경계를 박아야 한다.
둘째, 기억은 세션 바깥에 설계하라. 컨텍스트 윈도우 크기가 아니라, 세션이 끝나도 살아남는 외재화된 기억 구조가 필요하다. 어디에 읽고 어디에 쓰는지를 트리거로 명시해야 에이전트가 실제로 사용한다.
셋째, 테스트 커버리지는 자동화의 전제 조건이다. AWS Transform의 60% 효율 향상은 테스트 커버리지 70% 이상에서만 유효하다. 그 아래에서는 인간 리뷰 비용이 3~5배 증가한다는 수치도 함께 보고됐다. 테스트 없이 에이전트를 붙이는 건 가드레일 없이 자동 주행을 켜는 것과 같다.
에이전트가 프로덕션에서 조용히 틀리는 건 피할 수 없다. 그걸 설계 단계에서 구조적으로 드러내는 것—그게 지금 테크 리드의 진짜 일이다.