-
Notifications
You must be signed in to change notification settings - Fork 1
아키텍처
Yeonu Kim edited this page May 31, 2026
·
2 revisions
이 프로젝트는 클린 아키텍처(Clean Architecture) 를 기반으로 설계되었습니다. 각 레이어는 명확한 책임을 가지며, 의존성은 항상 안쪽(도메인) 방향으로만 흐릅니다.
Domain
↑
Usecase
↑
Infrastructure / Application / Presenter
↑
Widget / Page (UI)
| 레이어 | 위치 | 역할 |
|---|---|---|
| Domain | src/feature/*/domain/ |
비즈니스 엔티티, 스키마, 타입을 정의합니다. 외부 의존성이 없는 순수한 레이어입니다. |
| Usecase | src/feature/*/usecase/ |
핵심 비즈니스 로직을 담당합니다. 타입(인터페이스)과 impl 구현체를 분리하여 정의합니다. |
| Infrastructure | src/infrastructure/ |
API 클라이언트, 토큰 저장소 등 외부 시스템과의 연동을 담당합니다. |
| Application | src/feature/*/application/ |
@tanstack/react-query를 사용해 Usecase를 감싸는 서버 상태 관리 훅입니다. |
| Presenter | src/feature/*/presenter/ |
폼 유효성 검사, 입력 상태 관리 등 UI 로직을 처리합니다. React 훅을 포함할 수 있습니다. |
| Widget | src/widgets/*/ui/ |
재사용 가능한 UI 컴포넌트입니다. Context를 통해 하위 레이어를 주입받아 사용합니다. |
| Page | src/pages/ |
라우트 단위의 화면입니다. Widget을 조합하는 역할만 담당하며, 자체적인 비즈니스 로직을 갖지 않습니다. |
레이어 간 결합도를 낮추기 위해 의존성 주입(Dependency Injection) 을 사용합니다.
App.tsx에서 Infrastructure → Usecase → Application 순서로 인스턴스를 생성하고, Context에 등록합니다.
// src/App.tsx
const api = implApi({ externalCall });
const tokenRepository = implTokenRepository({ setToken });
const authUsecase = implAuthUsecase({ api, tokenRepository });
const authQuery = useAuthQuery({ authUsecase });Usecase, Infrastructure, Application 레이어는 여러 패키지(Widget, Page)에서 공통으로 사용됩니다. 이를 위해 Context API로 제공하여 어디서든 쉽게 주입받을 수 있도록 설계했습니다.
| Context | 파일 | 제공하는 것 |
|---|---|---|
QueryContext |
src/feature/shared/context/query-context.ts |
Application 레이어 (react-query 훅 모음) |
UsecaseContext |
src/feature/shared/context/usecase-context.ts |
Usecase 인스턴스 |
TokenContext |
src/feature/shared/context/token-context.ts |
현재 액세스 토큰 상태 |
Widget과 Page에서는 useGuardContext를 사용해 Context 값을 안전하게 주입받습니다. Context가 등록되지 않은 상태에서 접근하면 명시적인 에러가 발생합니다.
const { authQuery } = useGuardContext(QueryContext);로그인 기능을 예시로 전체 흐름을 설명합니다.
Page (sign-in.tsx)
└─ Widget (sign-in-form.tsx)
├─ authFormPresenter.useValidator() ← Presenter: 폼 유효성 검사
└─ useGuardContext(QueryContext)
└─ authQuery.useSignIn() ← Application: 서버 상태 관리
└─ authUsecase.signIn() ← Usecase: 비즈니스 로직 처리
└─ api[POST /api/...]() ← Infrastructure: HTTP 요청
UI를 수정할 때는
src/widgets/와src/pages/만 수정하면 됩니다.
- Page: Widget을 조합하는 껍데기 역할입니다. 직접 비즈니스 로직을 작성하지 않습니다.
-
Widget:
useGuardContext로 Context를 받아 Application/Presenter 레이어를 사용합니다. -
공통 원자 컴포넌트 (Button, Input, Card 등):
src/widgets/common/ui/에 위치합니다.
모든 Usecase 함수는 UsecaseResponse<T> 타입을 반환합니다. (src/feature/shared/response.ts)
type UsecaseResponse<T> = Promise<
| { type: 'success'; data: T }
| { type: 'error'; code: string; message: string }
>Application 레이어에서 response.type으로 성공/실패를 분기 처리합니다.
| 도구 | 위치 | 설명 |
|---|---|---|
| MSW | src/mocks/ |
개발(DEV) 환경에서 자동 활성화되는 API 모킹 |
| Storybook | src/stories/ |
컴포넌트 단위 UI 개발 및 문서화 |
| Biome | biome.json |
린터 및 포매터 설정 |