프론트엔드 코드 아키텍처¶
코드/시스템 3문서 — 백엔드 코드 아키텍처 · 프론트엔드 코드 아키텍처(이 문서) · 시스템·인프라 아키텍처(전체 지도 + 서버·배포·운영).
Next.js 15 / React 19 / App Router. 이 문서는 실제 코드(mvp-front/src)를 정독해 작성했다 — 경로·패턴은 코드에서 확인한 것이다. 백엔드 API 계약은 backend-architecture, 반응형 UI 토큰은 responsive·디자인 시스템.
1. 레이어 구조 (FSD 변형)¶
src/ 직하 레이어는 다음과 같다(코드 확인 — 과거 문서가 빠뜨린 shared·store·providers가 실제로는 핵심이다).
| 레이어 | 역할 |
|---|---|
app/ |
App Router 조립 — page.tsx/layout.tsx 라우팅 + BFF Route Handler. 비즈니스 로직은 두지 않음(단 (private)/layout.tsx는 fetchMemberRole() 후 리다이렉트하는 서버 가드 포함). |
features/ |
도메인 기능 묶음 (UI + 상태 + API) |
entities/ |
도메인 엔티티 (스키마·변환·타입·정책) |
shared/ |
가장 큰 레이어 — 공용 UI(에디터·드로잉·사이드바·다이얼로그·아이콘), API 클라이언트(shared/api/http/*), 상수·유틸 |
store/ |
Zustand 전역 스토어 (member.store.ts) |
providers/ |
React 전역 Provider (query·interceptor·session·analytics·toast 등) |
layout/ |
공용 Header/Footer/ColumnLayout |
config/ |
사이트 메타(site.ts) |
mocks/ |
MSW(Mock Service Worker) 핸들러 |
types/·styles/·assets/·stories/ |
공유 타입·전역 CSS·정적 파일·Storybook |
- 의존성 방향: 원칙은
app → features → entities → shared, 그리고 entity 내부에서infrastructure → core(역방향 금지). 단 이 방향 규칙은 ESLint로 강제돼 있지 않다 —eslint.config.mjs에no-restricted-imports같은 규칙이 없어 컨벤션 수준이다(강제는 후속 과제).
2. App Router 라우트 그룹 (5개)¶
src/app/의 라우트 그룹은 5개다(과거 문서의 3개는 틀렸음).
| 그룹 | 주요 라우트 | layout 가드 |
|---|---|---|
(auth) |
/login · /register |
이미 로그인 시 /dashboard·/select-role로 redirect |
(home) |
/(홈) · /list/{study-rooms,teachers} · /community/column[/[id]] |
공개 탐색·커뮤니티 |
(private) |
/dashboard[/connections|qna|study-consultation|study-news] · /study-rooms/new · /study-rooms/[id]/* · /study-note/{new,[id]} · /mypage · /settings · /admin/*(관리자) |
SessionGuard + 사이드바 |
(profile) |
/select-role |
역할 미배정(ROLE_MEMBER) 전용 — 역할 있으면 redirect |
(public) |
/inquiry/* · /invite/* · /open-challenge/* · /profile/{student,teacher}/[id] · /study-room-preview/[id]/[teacherId] |
인증 불필요 |
(이외 dev 전용 src/app/test-drawing/. 화면 기획·IA 매핑은 §6.)
3. BFF (app/api) + axios 4-client¶
프론트는 백엔드를 BFF Route Handler로 감싼다 — 쿠키·CORS를 흡수하고 백엔드로 프록시.
- catch-all:
app/api/v1/[...path]/route.ts(GET/POST/PUT/DELETE/PATCH 전부). 쿠키(Authorization·refresh·sid) 필터링 +ROLE_TEACHER→teacher·ROLE_STUDENT→studentpath 정규화 후 프록시. - 전용 핸들러:
auth/{login,logout,refresh,session,clear-session}·member/info·dashboard. 그리고api/drawing-session-ingest(iPad 필기 캡처, dev). - axios 4 인스턴스(
shared/api/http/http.transport.ts→http.client.ts의api.{public,private,bff.client,bff.server}): privateHttp— 인증 필요. 클라=/api/v1(BFF 경유), 서버=직접 백엔드. 401 인터셉터로 refresh 자동 재시도(인터셉터는 이 인스턴스에만).publicHttp— 비인증, 직접 백엔드.clientToBffHttp/serverToBffHttp— 클라·서버에서 BFF 직접 호출(refresh·session).- 인증이 쿠키 기반인 이유·전송 계층(SameSite·도메인)은 backend-architecture §5 · infra §2-4.
4. 상태 · 스택 (package.json 확인)¶
- Zustand —
store/member.store.ts1개({ member, isAuthenticated, setMember, clearMember }). 전역 세션 캐시. - TanStack Query
^5—providers/query-provider.tsx설정. 서버 상태는 각 featurehooks/(또는services/query.ts)와 entityhooks/의useQuery/useMutation. - Zod
^3— entitycore/*.domain.schema.ts·infrastructure/*.dto.schema.ts, feature 폼 스키마,types/sharedSchema(공통 response 래퍼). - Tiptap v3 (
@tiptap/react ^3) —shared/components/editor/. 커스텀 익스텐션:CustomImage·FileAttachment·DrawingExtension·Embed·LinkPreview·SlashCommand·KeyboardShortcuts. - Tailwind v4 — 스타일. 토큰은 디자인 시스템에서.
- providers:
global·query·interceptor(axios 설치) ·session(+session-guard) ·analytics·toast·pen-select-guard.
5. entity 내부 구조 (변환 3단)¶
엔티티는 스키마·변환·타입·정책을 한 폴더에 모은다. 구성은 엔티티마다 다르며, member가 가장 완전한 예다.
entities/member/
├── schema.ts base Zod 스키마(raw)
├── core/ member.domain.schema.ts(도메인 변환·transform) · member.factory.ts
├── infrastructure/ member.dto.schema.ts(API DTO) · member.adapters.ts · member.repository.ts · member.keys.ts
├── mapper/ member.mapper.ts (getDisplayName: nickname>name>fallback>email)
├── policy/ ability.ts(buildAbility) · rules.ts(역할별 권한 매트릭스)
├── hooks/ use-member-query.ts
├── types/ MemberDTO · Role · FrontendMember …
└── index.ts (public entry)
- DTO → Domain 변환은 3단: ①
adapters가 응답 envelope(sharedSchema.response(dto))만 Zod 파싱 → ②factory가domain.schema.parse()로 도메인 스키마 적용(transform으로displayName등 파생) → ③mapper의 순수 유틸을 transform 안에서 사용. 즉 adapter=검증, factory=변환, mapper=유틸. - 편차 인정:
study-room엔티티는core+infrastructure+types만 있고mapper/policy/hooks가 없다. 엔티티마다 필요한 폴더만 둔다. - 타입 네이밍은
MemberDTO(외부 비노출)와 도메인 타입FrontendMember/FrontendTeacherBasicInfo처럼Frontendprefix를 실제로 쓴다(과거 문서의 "prefix 제거"는 사실과 다름).
6. feature 내부 구조 + 화면/IA¶
feature는 api|services / hooks / components / model / index.ts 조합이나 구성이 통일돼 있지 않다(코드 기준).
features/study-rooms:api/(역할별room.api.{base,student,teacher}.ts+ query options) ·components/{common,create,sidebar,student-invitation}·hooks/·model/(Zod 폼) ·data/.index.ts는api·model·hooks만 re-export(components는 의도적 제외).components/sidebar/는 자체services/api.ts를 둠.features/auth:api/가 아니라services/(api.ts·query.ts) +schemas/·components/·hooks/·keys.ts·types.ts.index.ts없음(public entry 미존재).
화면·라우팅 IA: 내부 IA/SEO 메타는 Notion IA/URL 구조도(메인페이지·스터디룸·연결·대시보드·질문모아보기·설정·수업노트…)에서 관리하고, 실제 라우트는 §2 그룹과 매핑된다. 로그인 후 역할별 흐름(보호자=자녀 연결·조회, 선생님=룸 생성·수업노트)으로 갈린다.
7. 에디터 ↔ 미디어 연동¶
Tiptap 이미지·첨부는 백엔드 미디어 규약(backend-architecture §7)을 따른다.
- 업로드 성공 시
shared/components/editor/model/use-image-upload.ts가media://${mediaId}문자열을 만들어 노드에 심는다(CustomImage·FileAttachment노드에mediaIdattribute). 저장 시mediaIds동봉, 조회 시 백엔드가 실제 presigned URL로 치환 — 프론트는 참조 토큰만 다루고 실제 S3 URL은 모른다. DrawingExtension(iPad 필기)은documentId/pdfUrlattribute +data-type="drawing"직렬화, 수집은app/api/drawing-session-ingest.
8. 반응형¶
브레이크포인트·반응형 UI 패턴의 SSOT는 responsive·디자인 시스템. 프론트 구현은 그 토큰을 Tailwind v4로 받아 쓴다.
관련¶
backend-architecture · infra · responsive · engineering-principles · 디자인 시스템