아키텍처
프론트엔드는 분리된 아키텍처를 사용합니다. Next.js App Router가 app/에서 라우팅, 레이아웃, 서버 엔트리포인트를 담당하고, 제품 코드는 src/에 두어 Feature-Sliced Design으로 구성합니다.
레이어링
리포지토리 수준 규칙은 다음과 같습니다.
shared -> entities -> features -> widgets -> app/
이 규칙은 파일시스템에서 다음과 같이 드러납니다.
| 레이어 | 여기에 들어가는 것 |
|---|---|
src/shared | API 기본 요소, env 설정, 범용 UI, provider, 횡단 관심 유틸리티 |
src/entities | 도메인 타입, 스키마, 쿼리 헬퍼, 생성된 클라이언트, 그리고 실제로 필요할 때의 엔티티 UI |
src/features | 로그인, 온보딩, 학생 관리, 리포트 생성 같은 사용자 대상 액션 |
src/widgets | 랜딩 헤더, 학생 사이드바, 인증 모달 같은 상위 수준 조합 |
app | 라우트 트리, 레이아웃, 로딩 상태, 에러 바운더리, route handler, 리다이렉트 |
루트 수준 예외는 의도된 것입니다.
components/ui/에는 포크된 shadcn 기본 요소가 들어갑니다hooks/use-mobile.ts와lib/utils.ts는components.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 handler | lumie-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 |
| 서버 인증 fetch | lumie-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()