'ISR이 왜 안 되지?' 라는 질문으로 시작한 트러블슈팅이, 결국 세 개의 전혀 다른 문제가 얽혀 있었다는 걸 뒤늦게 깨닫는 경험—프론트엔드 실무에서 꽤 자주 마주치는 패턴이다. Velog에 공유된 On-Demand ISR 도입기는 그 과정을 정직하게 기록한 사례다. 그리고 공교롭게도, 이 경험은 요즘 Claude Code 같은 AI 코딩 도구를 쓸 때 반복되는 실패 구조와 놀랍도록 닮아 있다.
세 개의 원인이 하나처럼 보였던 이유
문제의 증상은 단순했다. CMS에서 수정한 글이 웹사이트에 반영되지 않는다. 하지만 원인은 단순하지 않았다. 첫째는 CMS 자체의 데이터 문제, 둘째는 시간 기반 ISR(revalidate = 600)만 설정된 상태에서 '수정'에 대한 무효화 트리거가 없었던 구조 문제, 셋째는 S3 Presigned URL 방식에서 발생한 이미지 캐싱과 객체 삭제 타이밍의 원자성 문제였다.
특히 세 번째 이슈가 인상적이다. 썸네일을 교체했는데도 이전 이미지가 계속 보이는 건 브라우저 캐시 때문이었고, 더 심각한 건 S3 객체 삭제와 DB 저장이 하나의 트랜잭션으로 묶여 있지 않아서 발생한 404였다. 업로드 도중 실패하면 S3에는 기존 파일이 지워졌지만 DB에는 그 URL이 그대로 남는 상태. 이건 ISR 문제도, CMS 문제도 아니었다—데이터 정합성의 문제였다.
해결 방향은 명확했다. On-Demand 재검증 API(POST /api/revalidate)를 추가해 CMS에서 글이 수정될 때마다 해당 경로와 태그를 즉시 무효화하도록 연결했다. blog:list:ko, blog:article:{articleId} 같은 태그 기반 무효화 덕분에 목록과 상세 페이지를 한 번에 갱신할 수 있게 됐다. S3 쪽은 이미지 업로드와 발행 단계를 분리하고, 발행 시점에만 실제 파일 교체가 일어나도록 흐름을 재설계했다.
AI의 자신감은 요구사항의 완전성과 무관하다
이 트러블슈팅 사례와 나란히 읽어야 할 글이 있다. dev.to에 공유된 "When Claude Code Gets It Wrong"은 AI 코딩 에이전트의 실패 패턴을 세 가지로 정리한다. 그리고 그 첫 번째가 바로 '틀린 문제를 완벽하게 푸는 것'이다.
글 속 시니어 개발자 Margaret의 말이 핵심을 찌른다. "Claude Code gave you a confident, well-structured, functional solution to the problem you described. It was not the solution to the problem you had." 세션 버그를 고쳐달라고 했더니, 세션 토큰은 제대로 초기화됐다. 하지만 만료 기준이 '로그인 시각'으로 고정돼 있어서, 한 시간째 작업 중인 사용자가 갑자기 로그아웃 당하는 UX 재앙이 잠복해 있었다. 코드는 틀리지 않았다. 요구사항이 불완전했을 뿐이다.
두 번째 실패 모드는 '지식의 시한'. Claude Code가 알고 있는 라이브러리 API나 보안 패턴은 특정 시점에 고정돼 있다. 빠르게 변하는 생태계에서 '그때는 맞았지만 지금은 틀린' 코드가 자신감 있게 제안된다. 세 번째는 '그럴듯한 날조'. 존재하지 않는 함수나 파라미터를 마치 실제인 것처럼 생성한다. 이건 런타임에서 터지기 때문에 그나마 잡기 쉬운 편이다.
검증 루프를 설계하는 사람이 주도권을 쥔다
여기서 Superset이라는 도구가 흥미롭게 연결된다. GeekNews에 소개된 이 AI 에이전트 전용 IDE는 Claude Code, Codex, Cursor Agent 같은 여러 코딩 에이전트를 병렬로 실행하면서 각 작업을 독립된 Git worktree로 격리하는 구조다. 중앙 대시보드에서 모든 에이전트의 상태를 모니터링하고, 내장 Diff 뷰어로 변경 사항을 즉시 검토할 수 있다.
Superset이 풀려는 문제는 사실 ISR 트러블슈팅 사례와 같은 맥락에 있다. 여러 에이전트가 동시에 코드를 수정하면 충돌이 생기고, 변경 사항의 의도가 흐릿해지고, 어느 순간 '누가 무엇을 왜 바꿨는지' 추적이 어려워진다. worktree 격리와 중앙 모니터링은 그 혼란을 구조적으로 막는 장치다. AI가 코드를 많이 생성할수록, 검증과 조율의 레이어가 더 정교해져야 한다는 방향성의 반영이다.
실무에서 가져갈 세 가지
세 개의 사례를 관통하는 교훈은 하나다. AI 도구의 출력 자신감은 요구사항의 완전성을 보장하지 않는다. ISR이 안 된다고 느껴질 때, 실제로는 CMS 데이터 문제일 수도, 이미지 원자성 문제일 수도 있다. Claude Code가 깔끔한 코드를 내놓을 때, 그게 비즈니스 로직을 완전히 담보하지는 않는다.
첫째, 문제를 레이어별로 분리하는 습관. ISR인가 CMS인가를 반복하는 대신, 데이터 레이어·캐시 레이어·인프라 레이어를 순서대로 짚어가는 구조적 접근이 디버깅 시간을 줄인다. 둘째, AI에게 요구사항이 아닌 '엣지 케이스'를 명시적으로 던지기. '세션 버그 고쳐줘'가 아니라 '활성 세션의 만료 기준은 마지막 활동 시각 기준이어야 한다'처럼 슬라이딩 만료 조건을 함께 전달하면 결과물이 달라진다. 셋째, 변경 사항을 작은 단위로 격리하고 즉시 검토하는 루프. Superset의 worktree 격리가 이걸 구조화하는 방식이고, Next.js의 On-Demand ISR이 캐시 무효화를 명시적으로 제어하는 방식이 이걸 구현한다.
AI가 코드를 더 많이, 더 빠르게 생성하는 시대일수록—무엇이 문제인지 정의하고, 어떤 조건이 충족되어야 하는지 명확히 하고, 생성된 결과를 검증하는 루프를 설계하는 능력이 개발자의 핵심 역량이 된다. 코드 생성은 AI에게, 문제 정의와 검증 설계는 사람에게. 그 경계가 명확할수록 실무는 버틴다.