2025년 08월 01일
11

AI 코드가 결국 '다시 짜야 하는 코드'가 되는 이유(그리고 해결책)

AI
KKingmo

Changmo Oh

@KKingmo

전체 카테고리 보기

요즘 개발자들은 대부분 AI로 코드를 만든다.
"이거 구현해줘라"고 하면 몇 초 만에 결과가 나온다. 처음엔 생산성이 폭발한 것처럼 보인다.

하지만 프로젝트가 커지면 금방 드러난다.
AI가 만든 코드는 빠를 수는 있어도, 그대로 쌓이면 코드베이스를 망가뜨리기 쉽다.

문제는 AI 성능이 아니다.
문제는 아직도 많은 개발자가 AI를 알아서 구현해주는 개발자처럼 쓰고 있다는 점이다.

"알아서 잘 짜줘"가 망하는 이유

예를 들어 이런 요청이 있다.

유저가 결제하면 포인트 차감하고 로그 남기는 API 만들어줘. 트랜잭션 처리랑 에러 처리도 잘 해줘.

이 요청은 구체적으로 보이지만, 실제로는 비어 있는 부분이 너무 많다.

  • 어떤 트랜잭션 패턴을 써야 하는지
  • 어떤 예외 체계를 따라야 하는지
  • 어떤 서비스 계층을 거쳐야 하는지
  • 어떤 로그 포맷을 써야 하는지
  • 어떤 테스트를 통과해야 하는지

이런 기준이 없으면 AI는 그럴듯한 평균값으로 코드를 만든다.

그래서 같은 프로젝트 안에서도 결과가 계속 갈린다.

  • 어떤 파일은 QueryRunner
  • 어떤 파일은 dataSource.transaction
  • 어떤 파일은 서비스에서 예외 처리
  • 어떤 파일은 컨트롤러에서 대충 처리

이런 식으로 쌓이면 코드는 돌아갈 수는 있어도, 유지보수는 어려워진다.

템플릿 방식은 좋다. 하지만 거기서 끝나면 안 된다

이 문제를 줄이기 위해 많은 사람이 템플릿 방식을 쓴다.

예를 들어 트랜잭션 구조를 미리 정해두고, AI에게는 특정 영역만 채우게 하는 식이다.

async function serviceMethodTemplate(dto: InputDto): Promise<OutputDto> {
  return this.dataSource.transaction(async (manager) => {
    // TODO: validate
    // TODO: main logic
    // TODO: return dto
  });
}

이 방식은 확실히 효과가 있다.

  • 구조가 통일되고
  • 실수가 줄어들고
  • AI의 자유도를 낮춰서 품질 편차를 줄일 수 있다

하지만 이것만으로는 부족하다.

템플릿은 형태는 통제할 수 있어도,
의도와 정확성까지 보장하지는 못하기 때문이다.

즉 틀은 맞는데 내용이 틀릴 수 있다.

  • 검증 순서가 잘못될 수 있고
  • 도메인 규칙을 어길 수 있고
  • 락이 필요한 상황을 놓칠 수 있고
  • 반환값은 맞아 보여도 부작용이 잘못 들어갈 수 있다

결국 템플릿은 시작점일 뿐, 정답은 아니다.

더 나은 방식은 AI를 생성기가 아니라 제한된 변환기로 쓰는 것이다

진짜 중요한 건 템플릿보다 앞단이다.

코드를 만들기 전에 먼저 고정해야 할 것들이 있다.

1. 계약을 먼저 정한다

함수 시그니처, DTO, 에러 타입, 반환 구조를 먼저 확정한다.

2. 도메인 규칙을 먼저 적는다

언제 성공하고, 언제 실패하며, 어떤 상태 전이가 가능한지 먼저 정리한다.

3. 구현 규칙을 먼저 박아둔다

트랜잭션, 로깅, 저장, 조회, 예외 처리, 테스트 방식 같은 팀 규칙을 명시한다.

4. 검증 기준을 먼저 만든다

테스트, 타입체크, 린트, 아키텍처 룰을 준비한다.

그 다음에야 AI에게 구현을 맡긴다.

이때 AI의 역할은 알아서 잘 만드는 개발자가 아니다.
이미 정해진 규칙을 만족하도록 코드를 변환해서 채워 넣는 도구에 가깝다.

이 관점 전환이 중요하다.

이제 중요한 건 프롬프트가 아니라 컨텍스트다

많은 사람이 아직도 프롬프트 문장을 다듬는 데 집중한다.

  • 확장성 있게 짜줘
  • 시니어답게 짜줘
  • 클린 코드로 짜줘

이런 말은 보기엔 좋아도 기준이 모호하다.
AI 입장에서는 해석의 여지가 너무 크다.

실제로 더 강한 방법은 문장을 잘 쓰는 게 아니라,
작업 맥락을 구조화해서 넣는 것이다.

좋은 입력은 보통 이런 요소를 가진다.

  • 이 기능의 목적
  • 관련 도메인 규칙
  • 참고해야 할 기존 파일
  • 반드시 따라야 할 구현 규칙
  • 건드리면 안 되는 제약
  • 통과해야 할 테스트
  • 출력 범위

예를 들면 이런 식이다.

ChargePointService.execute()를 구현해라.
반드시 dataSource.transaction만 사용한다.
상태 검증은 PaymentDomainService를 통한다.
로그는 AuditLogService.append()만 호출한다.
실패 시 PointChargeFailedException으로 감싼다.
아래 테스트 4개를 통과해야 한다.
출력은 service method body만 작성한다.

이 정도까지 내려가면 AI가 제멋대로 설계할 여지가 거의 없다.

가장 강한 방식은 생성보다 검증 가능한 생성이다

AI를 잘 쓰는 팀은 코드 생성 자체에 감탄하지 않는다.
대신 검증 루프를 만든다.

  1. 스펙을 먼저 쓴다
  2. 테스트를 먼저 만든다
  3. AI에게 좁은 범위만 구현시킨다
  4. 자동 검증을 돌린다
  5. 실패하면 결과 로그로 다시 수정시킨다

이 구조가 강한 이유는 명확하다.

AI는 빠르게 만든다.
검증 시스템은 틀린 걸 걸러낸다.

즉 생산성과 안정성을 동시에 가져갈 수 있다.

앞으로 중요한 건 템플릿이 아니라 규칙 파일이다

좋은 팀은 템플릿 몇 개만 관리하지 않는다.
프로젝트의 규칙 자체를 자산으로 만든다.

예를 들면 이런 것들이다.

  • 서비스 작성 규칙
  • 예외 처리 규칙
  • DB write 정책
  • 이벤트 발행 규칙
  • 테스트 규칙
  • 파일 구조 규칙
  • 금지 패턴 목록

이런 규칙이 팀원 머릿속에만 있으면 AI를 안정적으로 쓸 수 없다.
AI가 참조할 수 있는 명시적 문서와 예제로 있어야 한다.

그래야 AI가 매번 비슷한 품질로, 같은 기준을 따라 움직인다.

AI 시대 개발자의 역할은 코더보다 설계자에 가깝다

이제 개발자의 경쟁력은 단순히 빨리 구현하는 데서 나오지 않는다.

더 중요한 건 다음이다.

  • 어떤 규칙을 표준으로 삼을지 정하는 능력
  • 어떤 제약을 먼저 고정할지 판단하는 능력
  • 어떤 검증으로 품질을 보장할지 설계하는 능력
  • AI가 흔들 수 없는 개발 시스템을 만드는 능력

AI 시대에는 코드를 잘 치는 사람보다,
AI가 실수하기 어려운 구조를 만드는 사람이 더 강하다.

결론

예전에는 이렇게 했다.

  • 요구사항을 말로 설명한다
  • AI가 코드를 만든다
  • 사람이 다시 뜯어고친다

조금 발전하면 이렇게 된다.

  • 템플릿을 준다
  • AI는 빈칸만 채운다

하지만 더 나은 방식은 여기서 한 단계 더 간다.

  • 규칙을 먼저 고정한다
  • 계약을 먼저 정의한다
  • 검증 기준을 먼저 만든다
  • AI는 제한된 범위만 구현한다
  • 통과 여부는 사람이 아니라 시스템이 판단한다

AI를 잘 쓰는 핵심은 프롬프트 문장력이 아니다.
핵심은 규칙, 계약, 검증을 먼저 설계하는 것이다.

AI에게 "알아서 잘 짜라"고 하지 마라.
템플릿만 주고 끝내지도 마라.

그보다 먼저 해야 할 일은 따로 있다.
AI가 제멋대로 움직일 수 없게 만드는 개발 시스템을 만드는 것이다.


요약

  1. 말로 설명만 하면 AI는 평균적인 코드를 만든다.
  2. 템플릿은 구조를 통제하는 데 유효하다.
  3. 하지만 더 중요한 건 규칙, 계약, 검증을 먼저 고정하는 것이다.
  4. AI는 창작자가 아니라 제한된 변환기로 써야 한다.
  5. 생산성의 차이는 프롬프트가 아니라 시스템 설계에서 나온다.