본문으로 건너뛰기

라우팅

이 페이지는 프론트엔드 라우팅 표면에 대한 reference 문서입니다. Lumie는 Next.js App Router만 사용합니다. 라우트 그룹이 주요 사용자 영역을 조직하고, 레이아웃이 접근을 강제하며, app/api/[...path]/route.ts는 브라우저 측 모든 API 호출이 지나는 same-origin 프록시입니다.

소스 경로

경로역할
lumie-frontend/app/(marketing)//, /about, /pricing, /features, /blog 같은 공개 페이지
lumie-frontend/app/(auth)/로그인과 회원가입 진입점
lumie-frontend/app/(onboarding)/layout.tsxowner 온보딩 가드와 리다이렉트
lumie-frontend/app/admin/layout.tsx직원 전용 가드와 로그인 후 복귀 경로 처리
lumie-frontend/app/dashboard/layout.tsx학생 대시보드 셸
lumie-frontend/app/auth/callback/page.tsx로그인 후 쿼리 refresh와 최종 리다이렉트
lumie-frontend/app/auth/refresh/page.tsx무중단 refresh 경계
lumie-frontend/app/[customId]/page.tsx공개 academy 랜딩 페이지
lumie-frontend/app/api/[...path]/route.tsNEXT_PUBLIC_API_BASE로 향하는 same-origin 프록시
lumie-frontend/src/entities/session/api/getServerUser.ts보호 레이아웃이 사용하는 서버 측 /v1/me 읽기
lumie-frontend/src/entities/tenant/lib/useTenantCustomIdFromPath.tspathname에서 클라이언트 측 tenant custom ID 파싱
lumie-frontend/src/shared/api/serverFetch.ts/api를 다시 거치는 SSR fetch 경계

라우트 그룹과 주요 경로

경로 형태목적비고
//about, /pricing, /features, /blog 같은 마케팅 경로공개 사이트공유 헤더와 푸터를 가진 app/(marketing) 아래 구현
/login, /signup인증 진입점app/(auth) 아래 구현
/onboardingowner 온보딩 플로우app/(onboarding)/layout.tsx에서 보호
/admin/...직원 및 academy 운영app/admin/layout.tsx에서 보호
/dashboard/...학생 경험자체 클라이언트 레이아웃과 사이드바 셸 사용
/auth/callback로그인 후 리다이렉트 결정me 쿼리를 refresh하고 기본 인증 경로로 이동
/auth/refresh무중단 refresh 브리지refresh 쿠키는 있지만 access token을 다시 세워야 할 때 사용
/:customId공개 academy 랜딩 페이지app/[customId]/page.tsx로 구현
/api/[...path]same-origin 프록시 라우트브라우저 요청을 백엔드 게이트웨이로 전달

app/page.tsx는 없습니다. 사이트 루트는 app/(marketing)/page.tsx에서 옵니다.

/api/healthapp/api/health/route.ts에 별도로 있는 로컬 라우트입니다. 프론트엔드 liveness 용도로만 사용해야 하며, 백엔드 프록시는 검사하지 않습니다.

접근 제어

라우트 보호는 클라이언트 컴포넌트 곳곳에 흩어두지 않고 레이아웃에서 처리합니다.

온보딩

app/(onboarding)/layout.tsx는 다음을 수행합니다.

  • getServerUser()로 현재 사용자를 조회
  • 익명 사용자를 로그인 또는 세션 refresh로 리다이렉트
  • 학생을 /dashboard로 리다이렉트
  • 이미 온보딩을 마친 owner를 /admin으로 리다이렉트

관리자

app/admin/layout.tsx는 다음을 수행합니다.

  • 서버에서 현재 사용자를 확인
  • 로그인 후 복귀를 위해 요청된 관리자 경로를 보존
  • 학생을 /dashboard로 리다이렉트
  • 아직 온보딩하지 않은 owner를 /onboarding으로 리다이렉트

대시보드

app/dashboard/layout.tsx는 대시보드 셸을 제공하는 클라이언트 레이아웃입니다. 자체적으로 서버 리다이렉트를 하지는 않으며, 세션 플로우가 이미 수립되었다고 가정합니다.

테넌트 인지 라우팅

현재 두 가지 테넌트 인지 패턴이 있습니다.

  • /:customId는 academy의 공개 랜딩 페이지와 로그인 진입점을 렌더링합니다
  • /api 프록시는 커스텀 도메인 해석이 활성화되면 요청 host에서 tenant slug를 해석합니다

클라이언트 코드는 useTenantCustomIdFromPath()로 현재 tenant custom ID를 pathname에서 가져올 수도 있습니다. 이는 academy 브랜딩 랜딩 페이지에서 academy별 로그인과 회원가입이 동작하도록 인증 플로우에서 사용됩니다.

라우트 상태로서의 쿼리 파라미터

일부 UI 상태는 의도적으로 URL에 인코딩됩니다.

  • ?auth=login|register&callbackUrl=/target은 공유 인증 모달을 엽니다
  • 학생 목록과 Q&A 같은 리스트 페이지는 필터, 검색, 정렬, 페이지 상태를 search param에 유지합니다
  • parseStudentListParams()parseQnaListParams() 같은 헬퍼 파서는 라우트 코드와 query key 사이의 URL 파싱을 맞춰줍니다

이렇게 하면 리스트 뷰를 북마크할 수 있고, TanStack Query도 같은 라우트 상태에 대해 안정적인 key를 사용할 수 있습니다.

프록시 라우트 동작

app/api/[...path]/route.ts는 UI를 렌더링하지 않더라도 라우팅 표면의 일부입니다. 이 라우트는 다음을 수행합니다.

  • 요청을 NEXT_PUBLIC_API_BASE로 전달
  • origin 같은 브라우저 전용 또는 안전하지 않은 전달 헤더 제거
  • 커스텀 도메인 해석이 성공하면 X-Tenant-Slug 부착
  • localhost 프록시 모드에 맞게 Set-Cookie 헤더 재작성
  • Node fetch 압축 해제 후 오래된 압축 헤더 제거

현재 프록시 불변식은 소스에서 직접 확인할 수 있습니다.

const targetUrl = `${getApiBase()}${url.pathname}${url.search}`;
headers.delete('host');
headers.delete('x-tenant-id');
headers.delete('x-user-id');
headers.delete('x-user-role');
headers.delete('origin');

같은 handler는 DomainSecure를 제거하고, SameSite=NoneSameSite=Lax로 낮춰 로컬 개발 중 http://localhost:3000에 인증 쿠키가 저장되도록 Set-Cookie를 다시 씁니다.

검증 가능한 프록시 요청

next devhttp://localhost:3000에서 실행 중이고 NEXT_PUBLIC_API_BASE가 도달 가능한 백엔드를 가리킬 때, 프론트엔드 리포지토리에서 다음을 실행합니다.

cd lumie-frontend
curl -i -H 'X-Tenant-Slug: demo' http://localhost:3000/api/v1/me

예상 성공 신호:

  • 요청은 로컬 app/api/health/route.ts가 아니라 app/api/[...path]/route.ts에서 처리됩니다
  • 인증 쿠키가 없으면 응답은 401 또는 403 같은 백엔드 인증 응답이어야 합니다
  • 유효한 lumie_access_token 쿠키가 있으면 같은 경로가 200을 반환해야 합니다
  • 두 경우 모두 프록시된 응답이 오면 프론트엔드 라우트가 존재하고 Next.js 404를 반환하는 대신 NEXT_PUBLIC_API_BASE로 전달했다는 뜻입니다

관련 페이지