입구에서 한 번 막으면 충분하다는 착각
빠르게 제품을 만들다 보면 인증 설계를 단순화하고 싶은 유혹이 생긴다. "미들웨어에서 로그인 여부만 확인하면 되지 않나?" 하는 생각이다. 그런데 Next.js App Router 환경에서 이 생각은 꽤 위험한 단순화다. Server Component, Server Action, Route Handler, Proxy가 각자 다른 실행 컨텍스트에서 돌아가는 이 구조에서 인증 경계를 한 곳에 몰아두면, 나머지 지점이 열린 창문이 된다.
velog의 'Next.js 인증 경계와 스트리밍 화면 설계'는 이 문제를 정면으로 다룬다. 핵심 메시지는 명확하다. Proxy는 안내 데스크다. 빠른 1차 판단을 돕지만, 실제 금고 앞에서는 다시 열쇠를 확인해야 한다. 데이터를 읽고 쓰는 모든 지점에서 인증(Authentication)과 인가(Authorization)를 재검증하는 것이 App Router 시대 인증 설계의 기본값이어야 한다.
스트리밍이 보안 경계를 복잡하게 만드는 이유
문제는 여기서 끝나지 않는다. App Router의 스트리밍 UI, 즉 Suspense 경계를 활용하면 사용자 경험은 좋아지지만, 동시에 보안 경계가 훨씬 섬세해진다. 공개 레이아웃과 개인 데이터를 한 컴포넌트 안에 섞어두면 캐시나 스트리밍 타이밍 문제로 민감한 정보가 의도치 않게 노출될 수 있다.
올바른 접근은 공개 Shell과 개인 데이터 영역을 명확하게 분리하는 것이다. 공지처럼 누구나 볼 수 있는 콘텐츠는 먼저 렌더링하고, 결제 내역이나 개인 피드백은 Suspense 안에서 스트리밍하되 그 컴포넌트 내부에서도 세션과 권한을 다시 확인하는 구조다. userId를 props로 넘겼다는 사실이 보안 검증을 대신하지 않는다. 서버에서 세션을 다시 읽거나, DB 조건에 소유자 제한을 걸어야 한다.
'Server Action은 버튼이 숨겨져 있으니 안전하다'는 오해
Server Action은 편리하다. 하지만 public API처럼 취급해야 한다. 화면에서 관리자에게만 버튼을 보여줬어도, 누군가 직접 요청을 만들어 호출할 수 있다. Server Action 내부에서 auth()를 호출하고, DB 업데이트 조건에 소유자나 역할(role) 조건을 반드시 포함해야 한다. 관리자 기능이라면 Proxy → Page → Server Action의 세 단계 방어가 필요하고, 각 단계에서 권한을 독립적으로 검증해야 한다. 공통 헬퍼인 requireUser()와 requireRole()을 만들어 권한 확인 누락 자체를 구조적으로 막는 것도 좋은 실천이다.
장애는 숫자가 먼저 말한다
인증 설계가 제대로 됐는지 판단하는 가장 확실한 방법은 운영 지표다. 401, 403, 302 루프 비율을 경로별로 분리해서 모니터링하면 인증 장애를 코드보다 빠르게 포착할 수 있다. 실제로 모바일 Safari 사용자 중 일부가 대시보드에 진입할 때마다 로그인 페이지로 튕기는 증상이 생겼다면, SameSite 쿠키 설정 문제나 Proxy matcher 과다 적용을 먼저 의심해야 한다. 이때 로그에 requestId를 심어두면 302와 401이 같은 요청에서 반복되는지 즉시 확인할 수 있다.
'HTML/CSS는 너무 단순해서 못 올리겠어'와 같은 심리
조금 다른 맥락에서 비슷한 심리 구조를 들여다볼 필요가 있다. dev.to에 올라온 한 글은 이런 고백으로 시작한다. "히어로 섹션 하나 공유하면 사람들이 나를 진짜 개발자가 아니라고 생각할 것 같았다." 인증 설계가 복잡하게 느껴지는 것과 정반대의 문제처럼 보이지만, 본질은 같다. 기초를 단순하다고 얕보거나, 빠르게 만든 것을 보여주기 부끄러워하는 태도다.
4시간 만에 HTML과 CSS만으로 Netflix 클론을 완성한 사례는 흥미롭다. 이 사례가 던지는 메시지는 '빠름'이 아니라 '집중'과 '완성'이다. 복잡한 프레임워크나 클라우드 아키텍처에 압도되어 기초 완성도를 낮추는 것보다, 범위를 좁히고 끝까지 만들어내는 것이 더 강한 실력 증명이다. 프로덕트 사고로 보면, 작은 것이라도 완성하고 공개하는 것이 가장 빠른 피드백 루프다.
빠르게 빌드한다는 것의 진짜 의미
두 이야기는 결국 같은 지점을 가리킨다. 빠르게 만드는 것과 제대로 설계하는 것은 대립하지 않는다. App Router 인증 설계에서 Proxy 하나에 의존하면 속도는 올라가지만 구조는 무너진다. 반대로 HTML/CSS 결과물을 '너무 단순해서 못 올리겠다'고 망설이면 실력은 있지만 실천이 없다.
빠르게 짓되 설계를 놓치지 않으려면, 먼저 경계를 명확히 그어야 한다. 인증 경계는 Proxy, Server Component, Server Action 세 층위에 각각 존재한다. 공개와 개인 데이터의 경계는 Suspense 경계와 일치해야 한다. 그리고 내가 만든 것이 작든 크든, 그 경계를 직접 그어봤다는 사실 자체가 이미 설계 감각이 자라고 있다는 증거다.
앞으로의 실천 방향
Next.js App Router 생태계는 계속 빠르게 변한다. React Compiler의 자동 메모이제이션이 정착되고, Partial Prerendering이 안정화될수록 공개 영역과 개인 영역의 렌더링 경계는 더욱 정밀하게 제어할 수 있게 된다. 그때도 핵심 원칙은 바뀌지 않는다. 데이터를 읽고 쓰는 지점마다 다시 확인하는 것, 그리고 만든 것을 세상에 내놓는 것. 설계는 완벽할 때 공개하는 게 아니라, 공개하면서 완성된다.