진짜 API 주소가 필요할 때 써
앱 내부 데이터 읽기는 서버 컴포넌트가, 내부 변경은 서버 액션이 처리할 수 있어. 라우트 핸들러는 외부가 호출할 안정된 URL이 필요한 webhook, 제3자 연동, 공개 API에 남겨 두는 도구야.
route.ts가 HTTP 메서드를 내보내
app/ 아래 원하는 폴더에 route.ts를 두고 GET, POST, PUT, DELETE 같은 이름의 함수를 내보내. 각 함수는 NextRequest를 받아 NextResponse나 표준 Response를 반환해.
UI와 엔드포인트는 같은 구간을 공유하지 못해
route.ts가 있는 폴더에는 page.tsx를 함께 둘 수 없어. 같은 주소가 HTML 화면인지 HTTP 엔드포인트인지 모호해지기 때문이야. 둘 다 필요하면 하위 경로나 별도 api 구간으로 계약을 나눠.
HTTP가 필요하다는 이유를 설명할 수 있어야 해
단지 익숙하다는 이유로 내부 CRUD를 라우트 핸들러로 우회하면 JSON 변환과 인증 경계를 하나 더 만든다. 외부 소비자, webhook 서명, 안정된 URL 중 무엇이 필요한지 먼저 확인해.
라우트 핸들러를 만들기 전에 소비자를 이름 붙여. Stripe webhook이면 원문 body와 서명 검증, 공개 API면 버전·인증·rate limit, 내부 브라우저 호출이면 서버 액션으로 줄일 수 있는지 확인해. HTTP 계약을 선택했다면 상태 코드와 cache header까지 API의 일부로 문서화해.
서버 액션이 편하다고 외부 연동까지 숨기면 안정된 URL과 표준 HTTP 도구를 잃어. 반대로 모든 내부 함수를 API로 만들면 불필요한 직렬화와 공격 표면이 늘어. 호출자가 앱의 React 트리 안인지 밖인지가 첫 번째 경계야.
GET·POST handler와 같은 경로의 페이지 충돌을 빌드로 확인하고, webhook 원문 body·서명 오류·중복 이벤트를 curl이나 공급자 CLI로 시험해. 공개 응답의 status, content type, cache header가 문서와 맞는지 보고 내부 UI가 URL에 불필요하게 기대지 않는지도 봐. webhook은 같은 사건이 다시 와도 결과가 한 번만 반영되도록 사건 ID를 저장해. 서명 검증과 멱등성은 서로 다른 실패를 막으므로 둘 다 직접 재현해야 해.