AI 코딩 도구가 초안 코드를 뽑아주는 속도는 점점 빨라지고 있다. 문제는 그 다음이다. Cursor나 Claude로 빠르게 생성한 컴포넌트들은 처음엔 '일단 돌아가는' 수준에서 멈춰 있기 쉽다. 페이지마다 같은 헤더·푸터가 복사되고, 모달이 DOM 어딘가에 끼워지고, 유효성 검사 스키마는 예전 API 그대로 굳어있다. 속도를 얻는 대신 구조 부채가 쌓인다. 이 두 가지 실무 사례는 그 부채를 어떻게 갚는지를 보여준다.
레이아웃 중복, 템플릿 컴포넌트 하나로 끊어내기
velog의 한 개발자는 구직 서비스 'JobDri'를 개발하면서 페이지가 늘어날수록 코드 중복이 눈에 띄게 심해졌다고 털어놨다. 지원서 작성, 결과 확인, 공고 입력 등 서로 다른 페이지들이 <Header>, <Footer>, 그리고 flex h-dvh flex-col로 시작하는 동일한 래퍼 div를 각자 품고 있었다. 비즈니스 로직보다 레이아웃 보일러플레이트가 더 먼저 눈에 들어오는 구조였다.
해법은 Container-Presenter 패턴의 실용적인 적용이었다. MockApplyTemplate이라는 단일 템플릿 컴포넌트를 만들어 공통 레이아웃 책임을 한 곳으로 모으고, 각 페이지는 children과 필요한 props만 넘기는 방식으로 역할을 분리했다. 결과는 명확하다. 헤더 높이를 바꿔야 할 때 파일 하나만 열면 된다. 각 페이지 컴포넌트는 '이 화면에서 무슨 일이 일어나는가'에만 집중할 수 있게 됐다.
모달 아키텍처: z-index 지옥에서 벗어나는 법
레이아웃 정리 이후 더 까다로운 문제가 남았다. 템플릿 안쪽에 배치했던 모달이 CSS stacking context에 갇혀 화면 전체를 제대로 덮지 못하는 현상이었다. AI가 생성한 코드에서 흔히 보이는 함정이다. 컴포넌트 트리 어딘가에 transform이나 overflow: hidden이 섞이는 순간 z-index 숫자 싸움은 의미가 없어진다.
해결 방향은 계층을 아예 분리하는 것이었다. React Fragment(<>...</>)를 활용해 모달과 토스트를 템플릿 컴포넌트 바깥, 즉 DOM 트리의 독립된 형제 노드로 배치했다. 템플릿은 레이아웃 렌더링에만 충실하고, 오버레이 요소들은 템플릿의 stacking context에 영향받지 않는 별도 레이어로 올라간다. 여기에 모달 텍스트들을 constants 디렉터리로 분리해 상수로 관리하니 컴포넌트 내부는 한결 깔끔해졌다. 텍스트 한 줄을 고치려고 컴포넌트 전체를 뒤질 필요가 없어진 것이다.
Zod 4 마이그레이션: 스키마 검증도 구조 부채다
컴포넌트 레이어를 정리했다면, 데이터 경계에서의 부채도 살펴볼 차례다. 2025년 안정 버전으로 출시된 Zod 4는 API 표면을 상당히 바꿨다. dev.to의 Ahmed Mahmoud가 프로덕션 스키마를 마이그레이션하면서 정리한 내용에 따르면, 네 가지 변화가 실제 코드에 직접 닿는다.
첫째, z.string().email() 같은 체이닝 방식 대신 z.email(), z.uuid(), z.url()이 최상위 독립 함수로 올라왔다. 이유는 tree-shaking이다. 메서드 체인 방식에서는 이메일 검증 하나를 쓰더라도 모든 문자열 포맷 로직이 번들에 포함됐다. 이제 실제로 쓰는 것만 번들에 들어간다. 둘째, Zod 3에서 message, invalid_type_error, required_error, errorMap으로 나뉘었던 에러 옵션이 단일 error 파라미터 하나로 통합됐다. 고정 문자열을 넘기거나, 이슈 객체를 받는 함수를 넘기면 된다. 셋째, 에러 포매팅이 z.flattenError, z.treeifyError, z.prettifyError라는 독립 헬퍼로 분리됐다. 폼 필드 에러 처리에는 z.flattenError를 쓰면 된다.
마이그레이션 전략도 실용적이다. Zod 4는 deprecated된 Zod 3 API를 경고와 함께 유지하기 때문에 한 번에 전부 바꿀 필요가 없다. zod@3.25에서 zod/v4 임포트 경로로 먼저 탐색하고, 파일 단위로 점진적으로 옮기는 방식이 안전하다. z.infer, .parse(), .safeParse()는 변경이 없어 대부분의 스키마는 건드리지 않아도 작동한다. 번들 크기가 민감한 엣지 함수나 클라이언트 코드라면 zod/mini를 고려할 만하다. 체이닝 대신 함수형 래핑 방식을 쓰는 동일한 코어의 경량 빌드다.
빠른 코드 이후에 와야 하는 것
두 사례가 공통적으로 가리키는 방향이 있다. AI가 생성한 초안 코드는 '작동하는가'를 빠르게 확인하는 데 탁월하다. 그러나 그 코드가 팀에서 오래 살아남으려면, 레이아웃과 콘텐츠의 책임을 나누고, 오버레이 레이어를 DOM 계층에서 분리하고, 유효성 검사 스키마를 현재의 베스트 프랙티스에 맞게 정렬하는 작업이 반드시 따라와야 한다. 이것은 리팩토링이 아니라 '구조 설계를 의도적으로 완성하는 과정'이다.
AI 코딩 시대에 개발자의 핵심 역량은 더 빠르게 코드를 생성하는 것보다, 생성된 코드가 쌓이는 방향을 설계하는 것으로 이동하고 있다. 레이아웃 템플릿 하나, 모달 레이어 분리 하나, 스키마 마이그레이션 하나—작은 결정들이 코드베이스의 수명을 결정한다.