AI가 코드를 뽑은 뒤, 개발자에게 남는 것들

AI가 코드를 뽑은 뒤, 개발자에게 남는 것들

웹훅 신뢰, hydration 타이밍, 가격 산수—AI가 끝내 대신 계산해주지 못한 것들의 목록

AI 코딩 프로덕션 버그 React hydration localStorage race condition 도제 수업 기술 부채 웹훅 신뢰
광고

AI가 코드를 생성해주는 속도는 이제 놀랍지 않다. 놀라운 건 그 이후다. '몇 주 만에 프로덕션을 뚫었다'는 빌드-인-퍼블릭 포스트들이 쏟아지는 사이, 조용히 쌓이는 것들이 있다. 웹훅이 떨어졌는데 주문은 없는 상황, 전환율 대시보드가 한 달 동안 거짓말을 하고 있던 상황, 즐겨찾기 페이지를 북마크로 열었을 때만 데이터가 사라지는 버그. AI는 이 목록을 만들어주지 않았다. 프로덕션이 만들었다.

수업료는 항상 뒤에 온다

dev.to에 올라온 한 편의 자기고백이 눈에 띈다. 항공·여행 분야에서 10년을 버틴 프로덕트 매니저가 2026년 초, AI의 힘을 빌려 혼자 여행용 eSIM 스토어를 만들었다. 25개 언어, 190개국, 모바일·웹·백엔드. 첫 유료 주문까지 걸린 시간은 몇 주. 그는 이렇게 쓴다. "처음 여섯 주는 공짜였다. 그다음 네 달은 아니었다."

그가 겪은 실패를 나열하면 이렇다. Stripe 웹훅이 정상적으로 발화했는데 다운스트림 이벤트가 유실되어 고객은 결제됐지만 주문은 생성되지 않았다. PostHog 분석이 실제 판매의 절반만 잡아냈다—확인 페이지 이벤트가 브라우저에서 발화하지만, 결제 직후 탭을 닫는 고객은 그 페이지를 보지 못한다. 그 결과 3%로 읽힌 전환율의 실제 값은 16%였다. 한 달 동안 5배 틀린 숫자로 퍼널을 최적화하고 있었던 셈이다.

언어 설정 버그도 있었다. 고객 언어를 찾지 못하면 fr(프랑스어)로 폴백하도록 기본값이 설정되어 있었고, 대부분의 경우 언어를 찾지 못했다. 일본 고객과 브라질 고객에게 프랑스어 이메일이 날아갔다. 아무도 불평하지 않았다. 그냥 돌아오지 않았다. 그는 "불평은 데이터지만 침묵은 그렇지 않다"고 적는다.

Cloudflare 봇 차단을 켰더니 eSIM 공급사의 서버-투-서버 웹훅이 막혔다. 기계는 캡차를 풀지 못한다. 주문이 처리 대기 상태로 쌓이는 동안 그는 보안을 강화했다는 뿌듯함을 느끼고 있었다. Next.js의 점진적 정적 생성이 디스크를 가득 채워 사이트가 내려앉기도 했다—프레임워크 기본값이 조용히 만든 장애였다. 가격 책정은 처음부터 잘못됐다. 3유로짜리 판매 건에서 카드 수수료 고정비가 마진 전체를 잡아먹고 있었다.

그가 내린 결론은 명료하다. "저 목록에 코딩 문제는 단 하나도 없다. 잘못 작성된 함수도 없다." 웹훅을 눈으로 확인했어도 신뢰하지 않는 감각, 클라이언트 이벤트가 원래 적게 잡힌다는 지식, 프레임워크 기본값을 먼저 의심하는 습관, 보안 설정이 통합 변경임을 아는 이해. 이것들이 시니어가 수년의 인시던트를 통해 체화하는 것들이고, AI는 그것을 만들어주지 않는다. 그는 이를 두고 "도제 수업료를 선불 대신 고객에게 전가한 것"이라고 부른다.

그 버그는 북마크로 열었을 때만 나타났다

AI가 생성한 코드의 한계가 거시적 운영 판단에서만 드러나는 건 아니다. 컴포넌트 레벨의 로직에도 AI가 쉽게 놓치는 종류의 함정이 있다. dev.to에서 한 프론트엔드 개발자가 공유한 버그가 그 구조를 잘 보여준다.

앱의 즐겨찾기 기능은 localStorage에 데이터를 저장한다. 서버 렌더링 환경에서 hydration mismatch를 피하기 위해 초기 상태를 빈 배열로 두고, useEffect에서 storage를 읽는 표준 패턴을 적용했다. 동시에, 즐겨찾기 페이지에는 12시간 이상 지난 데이터를 자동 갱신하는 로직이 별도 useEffect로 존재했다. 둘 다 합리적인 결정이었다.

문제는 React의 effect 실행 순서에 있었다. React는 트리의 아래에서 위로, 자식 effect 먼저 실행한다. 즐겨찾기 페이지를 URL로 직접 열면—북마크, 새로고침, 외부 링크—데이터 프로바이더와 페이지가 같은 커밋에서 마운트된다. 이때 페이지의 effect(갱신 로직)가 프로바이더의 effect(storage 로드)보다 먼저 실행된다. 갱신 함수가 읽는 favorites 상태는 아직 빈 배열이다. 빈 배열로 API를 호출하고, 빈 결과를 localStorage에 덮어쓴다. 잠시 후 프로바이더가 storage를 읽으면 이미 비어 있다.

앱 내에서 링크를 클릭해 이동하면? 프로바이더는 이미 마운트되어 있고 effect는 오래전에 실행됐다. 즐겨찾기는 정상적으로 로드된 상태다. 갱신 함수는 populated된 배열을 읽는다. 완벽하게 동작한다. 같은 페이지, 같은 코드, 같은 데이터—도달 방법만 다를 뿐인데 결과가 갈렸다. 개발 중에 링크 클릭으로만 이동하는 개발자는 이 버그를 절대 만날 수 없다.

최종 수정은 갱신 함수가 React 상태 대신 localStorage를 직접 읽도록 바꾸는 것이었다. 상태의 순서 의존을 제거하면 타이밍 문제 자체가 사라진다. 더불어 '빈 즐겨찾기'와 '아직 로드되지 않은 즐겨찾기'를 렌더링 레벨에서 구분해야 한다는 교훈도 따라왔다—로딩 중인데 "즐겨찾기가 없습니다"를 보여주는 순간, 반환 사용자는 데이터가 사라졌다고 느낀다.

AI가 보지 못하는 것의 정체

두 사례는 서로 다른 레이어의 이야기처럼 보이지만 같은 구조를 가리킨다. AI는 내가 기술한 시스템에 대해 추론한다. 그리고 내 기술이 틀렸을 때, AI의 답변은 유창하고 그럴듯하고 확신에 차 있으며 완전히 틀리다.

eSIM 스토어 창업자는 이것을 정확히 짚는다. "모델이 추론하는 것은 내가 설명한 시스템이고, 문제는 바로 그 설명이 깨져 있다는 것이었다." 버그를 재현하기 전에는 어떤 수정도 받아들이지 않겠다는 원칙이 그를 살렸다. 그는 이 반사 행동을 도제 수업이 사람에게 드릴하는 '권위 있어 보이는 답을 믿지 않는 습관'이라고 부른다. 그리고 채팅 인터페이스는 정확히 그 반사 행동을 훈련에서 지워낸다고 말한다.

hydration 버그도 마찬가지다. AI에게 "즐겨찾기가 가끔 사라진다"고 설명하면, AI는 localStorage 파싱 오류나 상태 업데이트 로직 실수를 먼저 살핀다. effect 실행 순서와 마운트 타이밍의 교차 지점을 재현하려면, 먼저 개발자 자신이 '앱 내 네비게이션'과 'URL 직접 로드'가 다르게 작동할 수 있다는 사실을 알아야 한다. 그 맥락 없이 AI에게 코드를 붙여 넣으면, AI는 없는 버그를 고친다.

그래서 개발자에게 남는 것

AI가 가장 잘 작동하는 순간은 두 사례 모두에서 분명하다. eSIM 창업자는 '무엇을 보여줘야 하는지 알고 난 뒤' 어드민 대시보드를 AI와 하루 만에 완성했다고 말한다. 마이그레이션, 번역 파이프라인, 데이터 감사 스크립트—비즈니스의 지루한 표면적에서 AI의 레버리지가 가장 크다고. hydration 버그의 경우, 원인을 파악한 뒤 구조적 수정 방향을 빠르게 구현하는 데 AI는 충분히 유용하다.

패턴이 보인다. AI는 '무엇을 만들어야 하는가'가 명확해진 다음부터 탁월하다. 그 명확함을 만드는 과정—무엇이 깨졌는지 진단하고, 어떤 질문을 먼저 던져야 하는지 판단하고, 대시보드 숫자를 신뢰하기 전에 의심하고, 북마크로 열어보는 것—은 여전히 사람의 몫이다.

도제 수업이 사라지고 있다는 걱정은 맞다. 하지만 그 수업이 가르쳤던 것의 정체를 이 두 사례는 구체적으로 보여준다. CRUD 엔드포인트 작성법이 아니었다. 인시던트가 끝난 뒤 시니어가 맨 처음 던진 질문이 왜 그 질문이었는지를 이해하는 것이었다. 그리고 그 이해는, 지금도 프로덕션을 직접 맞닥뜨리기 전까지는 생기지 않는다.

출처

더 많은 AI 트렌드를 Seedora 앱에서 확인하세요