bobob.app
개발2026-03-087분 읽기

Cursor와 Codex로 웹서비스를 만들 때 실제로 막히는 부분

AI 코딩 도구로 화면을 빠르게 만든 뒤에도 라우트, 데이터 파일, 배포, sitemap, Play 결과 흐름은 사람이 다시 묶어 확인해야 했던 운영 기록입니다.

버그 티켓 접수선 바로 하기

막히는 곳은 코드 작성이 아니라 경계다

AI 코딩 도구는 컴포넌트를 잘 만든다. 비슷한 패턴의 페이지도 빠르게 늘린다. 여기까지는 진짜 편하다. 그런데 실제 웹서비스는 컴포넌트만으로 끝나지 않는다. 라우트가 정적 생성되는지, sitemap이 새 페이지를 포함하는지, canonical이 맞는지, 배포된 도메인이 같은 응답을 내는지 확인해야 한다.

문제는 이 경계가 눈에 잘 안 보인다는 점이다. 로컬에서는 페이지가 보이는데 빌드에서 빠질 수 있고, 배포는 됐지만 Search Console에는 예전 sitemap이 남을 수 있다. 도메인 redirect가 앱 코드가 아니라 플랫폼 설정에서 먼저 처리될 수도 있다. 이럴 때 괜히 UI만 다시 만지면... 하루가 그냥 녹는다.

내가 자주 보는 신호

  • - 로컬 200과 라이브 200을 따로 확인한다
  • - sitemap index와 child sitemap을 둘 다 읽는다
  • - Search Console 제출 목록과 실제 URL 검사를 분리한다
  • - Vercel 프로젝트 도메인 설정이 앱 redirect보다 먼저 작동하는지 본다
  • - AI가 만든 UI보다 사용자가 실제로 누를 첫 행동을 먼저 확인한다

AI는 많은 코드를 만들 수 있지만 운영 신호를 책임지지는 않는다. 결국 사람이 “이건 버그 신호인지, 그냥 의견인지”를 계속 구분해야 한다. 그 판단이 늦으면 코드는 많아지고 원인은 흐려진다.

실제로 많이 막힌 경계

경계겉으로 보이는 상태실제 확인
라우트로컬에서는 페이지가 보임정적 생성 대상에 들어갔는지 본다
콘텐츠파일은 추가됨Blog 목록, category, search, feed가 같이 바뀌었는지 본다
sitemapXML이 열림제출할 URL만 들어갔는지, noindex 후보가 빠졌는지 본다
배포Vercel 배포가 성공함canonical host에서 같은 HTML이 나오는지 본다
상호작용버튼이 보임모바일/데스크톱에서 결과 상태까지 실제로 도달하는지 본다

이 경계들은 AI가 한 번에 보기 어렵다. 컴포넌트는 그럴듯하게 만들 수 있어도, canonical host와 Search Console의 discovered pages는 코드 밖의 시간표를 탄다. 그래서 작업을 마칠 때는 "파일 diff가 예쁘다"보다 "외부에서 봐도 같은 상태인가"를 확인한다.

API 응답은 캡처보다 handoff가 먼저다

AI 코딩 도구로 API 화면을 붙일 때도 비슷한 일이 생긴다. DevTools Network 탭에서 response body를 보면 당장은 빠르다. 하지만 그 JSON을 그대로 캡처해서 넘기면 누가 어떤 필드를 봐야 하는지, 에러 위치가 어디인지, 토큰이나 내부 URL을 지웠는지 다시 확인해야 한다. DevTools는 관찰 화면이고, handoff는 다음 사람이 바로 움직이게 하는 짧은 기록이다.

그래서 API 응답을 볼 때는 `copy api response from devtools`에서 멈추지 않고 JSON Formatter의 API response report로 줄인다. root type, top-level key, depth, sensitive-looking key, duplicate key, useful JSON path를 같이 남기면 "응답이 이상함"이라는 말보다 훨씬 낫다. 필요한 필드 하나만 볼 때는 JSONPath Tester로 이어가서 `$.data.items[0].id` 같은 경로를 따로 남긴다.

DevTools에서 본 것바로 부족한 점Bobob에서 남기는 것
Network response JSON길고 nested object가 많아 핵심 필드가 묻힌다API response report의 root shape, depth, useful paths
API error bodymessage만 복사하면 code, requestId, validation field가 빠진다error response 구조와 복사 전 redaction checklist
배열 payload첫 row만 보고 전체 구조를 착각하기 쉽다top-level count, large array warning, JSONPath wildcard
민감한 keytoken, cookie, email, internal URL이 같이 복사될 수 있다sensitive-looking key warning과 redacted handoff

이 흐름은 JSON pretty printer를 하나 더 쓰자는 말이 아니다. DevTools에서 본 응답을 issue, PR, QA 메모에 붙일 수 있는 형태로 줄이는 일이다. AI가 "이 필드가 있을 것"이라고 코드를 짜도 실제 응답이 다르면 화면은 깨진다. 그때 필요한 것은 예쁜 캡처가 아니라, 어떤 응답을 봤고 어떤 필드를 근거로 다음 코드를 고칠지 남긴 작은 보고서다.

내가 작업을 자르는 기준

AI 도구를 쓰면 한 번에 여러 파일을 고치기 쉽다. 하지만 작은 웹서비스에서는 고친 파일 수보다 확인 가능한 단위가 더 중요했다. 그래서 지금은 작업을 아래처럼 자른다.

작업 단위같이 봐야 하는 것멈추는 기준
Blog 글 하나목록, category, search, sitemap, feed대표 글이면 400단어와 구조 신호를 넘겼는지
Play 하나JSON metadata, engine, 결과 링크, 관련 Blog모바일/데스크톱에서 결과까지 도달했는지
공개 route 하나canonical, h1, title, description, structured datalocalhost와 canonical host가 같은 의도를 보이는지
운영 문서 하나하네스 기대 문구, 최신 숫자, stop rule발견/색인/노출을 같은 말로 섞지 않았는지

이 표를 두면 AI에게 맡길 범위도 좁아진다. “이 페이지를 더 예쁘게 만들어줘”보다 “이 대표 글이 feed와 sitemap에 들어가도 될 만큼 구조를 갖췄는지 보강해줘”가 낫다. 질문이 좁아지면 결과도 좁아지고, 하네스 실패가 제품 판단으로 읽힌다.

이번 사이트에서 얻은 교훈

bobob.app은 특히 Blog, Play, Tools archive가 같이 있으니 경계가 더 많다. 새 글을 하나 넣으면 단순히 `/blog/slug`만 생기는 것이 아니다. 홈 최신 글, category hub, 전역 검색, sitemap, feed, llms.txt까지 이어진다. 새 Play도 결과 링크와 관련 Blog 연결이 맞아야 한다. 이 연결을 놓치면 페이지 수는 늘지만 사이트 신호는 흐려진다.

이번에 더 크게 배운 것은 “페이지가 생겼다”와 “사이트가 더 좋아졌다”가 다르다는 점이다. 글이 하나 늘어도 archive 후보라면 sitemap에 들어가면 안 된다. Play가 하나 늘어도 결과 링크가 비어 있으면 한 판 하고 끝난다. 검색 결과에 새 항목이 보이더라도 match signal이 왜 나왔는지 설명하지 못하면 사용자는 바로 돌아간다. AI가 만든 파일을 그대로 공개 표면으로 밀어 넣으면 이런 경계가 흐려진다.

확인하지 않으면 남는 어긋남

  • - 로컬 라우트는 열리지만 production build의 static params에서 빠지는 경우가 있다.
  • - Blog 본문은 길어졌지만 meta description은 예전 짧은 문장으로 남을 수 있다.
  • - Play JSON은 추가됐지만 related Blog가 noindex 후보를 가리킬 수 있다.
  • - sitemap URL 수는 맞지만 Search Console이 아직 이전 숫자를 보고 있을 수 있다.
  • - 하네스가 녹색이어도 실제 첫 화면에서 대표 글보다 약한 글이 먼저 보일 수 있다.

이 어긋남은 대부분 작은 차이처럼 보인다. 하지만 새 사이트에서는 작은 차이가 곧 사이트 설명이 된다. 처음 온 사람은 저장소 구조를 보지 않는다. 홈, Blog 첫 화면, Play 결과, 검색 결과, trust page 정도만 보고 이 사이트가 직접 운영되는지 판단한다. 그래서 AI로 빠르게 만든 뒤에는 사람이 느리게 묶어야 한다.

바로 해보기

버그 티켓 접수선은 로그와 증상 티켓에서 실제로 접수할 단서만 받는 게임이다. 초록 접수 도장이 찍힌 TypeError, 500, 재현 조건 같은 티켓을 잡고 “느낌상 이상함” 같은 붉은 반려 티켓은 흘려보내는 연습을 한다.

막힘을 줄이는 작업 단위

AI 도구로 만든 화면은 한 번에 커지기 쉽다. 그래서 작업 단위를 라우트 하나, 콘텐츠 묶음 하나, Play 결과 흐름 하나처럼 작게 자른다. 한 번에 홈, 블로그, 검색, sitemap, Play까지 다 고치면 실패했을 때 어디서 깨졌는지 모른다. 작은 단위로 자르면 하네스 실패도 제품 판단으로 읽을 수 있다.

이번 Blog + Play 정리도 같은 방식이었다. 먼저 대표 글을 고르고, 짧은 글을 보관 후보로 돌리고, sitemap/feed/search가 대표 글만 보게 만들었다. 그 다음 브라우저에서 실제 목록을 봤다. AI가 빠르게 수정해도 이 순서를 지키지 않으면 “작업은 많은데 왜 공개 표면은 그대로 얇은가”라는 문제가 남는다.

마감할 때 남기는 검증 묶음

작은 변경이라도 마지막에는 몇 가지 묶음을 같이 본다. `npm run harness:blog-play-mvp`는 대표 글 수, noindex 후보, Play 연결, generated-outline 문구를 막아준다. `harness:blog-play-quality`는 실제 렌더링된 Blog/Play 페이지에서 h1, canonical, title, description, 구조화 데이터를 본다. `harness:submitted-url-health`는 제출 sitemap URL이 실제로 200 HTML인지 다시 확인한다. 그리고 build는 정적 생성 단계에서 빠진 route가 없는지 보여준다.

이 검증 묶음은 AI를 못 믿어서가 아니다. AI가 잘하는 일과 운영자가 봐야 하는 일이 다르기 때문이다. AI는 빠르게 고칠 수 있고, 사람은 어떤 표면을 공개할지 판단한다. 둘 사이에 하네스가 있으면 “빨리 만들었다”와 “공개해도 된다” 사이의 빈틈이 줄어든다.

다음에도 Cursor나 Codex로 화면을 먼저 만들 것이다. 다만 이제는 화면이 생긴 순간을 완료로 보지 않는다. route, content registry, sitemap, feed, search, Play result, external discovery log까지 이어졌을 때 비로소 하나의 작은 웹서비스 조각으로 본다. 이 순서를 지키는 것이 지금 bobob.app에서 가장 큰 병목이자 가장 필요한 품질 관리다.

읽고 나서 해볼 것

Cursor와 Codex로 웹서비스를 만들 때 실제로 막히는 부분 - bobob.app | bobob.app