본문으로 건너뛰기

아키텍처

프론트엔드는 분리된 아키텍처를 사용합니다. Next.js App Router가 app/에서 라우팅, 레이아웃, 서버 엔트리포인트를 담당하고, 제품 코드는 src/에 두어 Feature-Sliced Design으로 구성합니다.

레이어링

리포지토리 수준 규칙은 다음과 같습니다.

shared -> entities -> features -> widgets -> app/

이 규칙은 파일시스템에서 다음과 같이 드러납니다.

레이어여기에 들어가는 것
src/sharedAPI 기본 요소, env 설정, 범용 UI, provider, 횡단 관심 유틸리티
src/entities도메인 타입, 스키마, 쿼리 헬퍼, 생성된 클라이언트, 그리고 실제로 필요할 때의 엔티티 UI
src/features로그인, 온보딩, 학생 관리, 리포트 생성 같은 사용자 대상 액션
src/widgets랜딩 헤더, 학생 사이드바, 인증 모달 같은 상위 수준 조합
app라우트 트리, 레이아웃, 로딩 상태, 에러 바운더리, route handler, 리다이렉트

루트 수준 예외는 의도된 것입니다.

  • components/ui/에는 포크된 shadcn 기본 요소가 들어갑니다
  • hooks/use-mobile.tslib/utils.tscomponents.json이 그 위치를 기대하므로 리포지토리 루트에 둡니다

App Router 경계

app/은 얇게 유지합니다. 주요 책임은 다음과 같습니다.

  • (marketing), (auth), (onboarding) 같은 레이아웃 기반 라우트 그룹화
  • app/admin/layout.tsx 같은 서버 레이아웃에서 접근 제어 강제
  • app/api/[...path]/route.ts 같은 route handler 노출
  • 라우트 트리 가까이에 로딩/에러 바운더리 정의

대부분의 비즈니스 로직은 페이지 안에 직접 구현하지 않고 src/에서 가져옵니다.

데이터 접근 경계

프론트엔드에는 서로 다른 세 가지 접근 경로가 있습니다.

경로사용 목적주요 파일
/api를 통한 브라우저 fetch일반적인 클라이언트 측 제품 요청src/shared/api/base.ts, src/shared/api/orval-mutator.ts, app/api/[...path]/route.ts
SSR 또는 서버 레이아웃 중의 서버 fetch인증 확인과 서버 전용 읽기src/shared/api/serverFetch.ts, src/entities/session/api/getServerUser.ts
공개 서버 읽기테넌트 홈페이지와 공개 academy 조회라우트 페이지에서 사용하는 엔티티 수준 serverFetch 헬퍼

브라우저는 항상 ENV.API_URL을 대상으로 하며, 이 값은 /api로 하드코딩되어 있습니다. 이렇게 하면 인증 쿠키 처리, 테넌트 헤더 전달, refresh 재시도, 에러 정규화가 모두 프론트엔드가 제어하는 하나의 경계에 모입니다.

소스 경로

계약소스 경로
라우트 그룹, 보호 레이아웃, route handlerlumie-frontend/app/**
same-origin 프록시lumie-frontend/app/api/[...path]/route.ts
브라우저 fetch 기본 요소lumie-frontend/src/shared/api/base.ts, lumie-frontend/src/shared/api/orval-mutator.ts
서버 인증 fetchlumie-frontend/src/entities/session/api/getServerUser.ts, lumie-frontend/src/shared/api/serverFetch.ts
Query client 수명주기lumie-frontend/src/shared/lib/query-client.ts, lumie-frontend/src/shared/providers/QueryProvider.tsx
공유 API 코드용 세션 사이드 채널lumie-frontend/src/shared/api/sessionAccessor.ts, lumie-frontend/src/shared/lib/sessionCache.ts

쿼리 및 세션 아키텍처

TanStack Query는 QueryProvider에서 한 번만 초기화됩니다. getQueryClient()는 다음을 생성합니다.

  • 서버에서는 요청별 query client를 생성해 요청 간 캐시 누수를 방지
  • 브라우저에서는 클라이언트 내비게이션 동안 유지되는 단일 query client 생성

세션 상태는 의도적으로 분리되어 있습니다.

  • getServerUser()는 서버 레이아웃에서 라우트 보호를 담당합니다
  • useMe()useMeQuery()는 클라이언트 코드에서 인증된 사용자를 노출합니다
  • sessionAccessorsessionCache는 공유 API 코드가 FSD 레이어 규칙을 깨지 않고 테넌트 세션 데이터를 읽거나 지울 수 있게 해줍니다

UI 조합

주요 셸은 전역 단일 컴포넌트가 아니라 widget 또는 라우트 로컬 셸로 구성합니다.

  • 마케팅 페이지는 app/(marketing)/layout.tsx를 사용합니다
  • 관리자 페이지는 AdminShell을 사용합니다
  • 대시보드 페이지는 StudentSidebar, Header, 공유 사이드바 기본 요소를 사용합니다

이렇게 하면 라우트별 레이아웃 선택은 해당 라우트 그룹 가까이에 두고, 재사용 가능한 제품 로직은 src/ 안에 남겨둘 수 있습니다.

검증

cd lumie-frontend
rg -n "getServerUser|serverFetch|sessionAccessor|sessionCache|getQueryClient|QueryProvider" \
app src
npm run type-check

성공 기준은 grep이 라우트 보호, 서버 fetch, 세션 캐시, query client 경계를 찾아내고, npm run type-check가 TypeScript 오류 없이 완료되는 것입니다.

관련 페이지