Skip to content

Commit 232aff8

Browse files
committed
docs : 타입 명세 변경, 확장 정리
1 parent 50e1201 commit 232aff8

4 files changed

Lines changed: 136 additions & 0 deletions

File tree

CLAUDE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ PostgreSQL+pgvector · S3/MinIO
6464
| SSE 이벤트 스펙 | [`docs/event-stream.md`](./docs/event-stream.md) |
6565
| **디자인 시스템** | [`docs/design-system.md`](./docs/design-system.md) |
6666
| UI 패턴 (4-state, 폼, 토스트, 키보드) | [`docs/ui-patterns.md`](./docs/ui-patterns.md) |
67+
| **프론트엔드 타입 시스템** | [`docs/frontend-types.md`](./docs/frontend-types.md) |
6768
| 보안 (인증, 암호화, 개인정보) | [`docs/security.md`](./docs/security.md) |
6869
| 옵저버빌리티 (trace, 로그, AI 비용) | [`docs/observability.md`](./docs/observability.md) |
6970
| 환경 변수 카탈로그 | [`docs/environment.md`](./docs/environment.md) |

docs/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
### 디자인·프론트엔드
3333
- [`design-system.md`](./design-system.md) — 토큰, 컬러, 타이포그래피, 컴포넌트 인벤토리
3434
- [`ui-patterns.md`](./ui-patterns.md) — 반복되는 UX 패턴, 상태 처리 (loading/empty/error)
35+
- [`frontend-types.md`](./frontend-types.md) — TypeScript 타입 네이밍·레이어별 흐름·정의 위치
3536

3637
### 보안·운영
3738
- [`security.md`](./security.md) — 인증·인가, 토큰 암호화, 개인정보 처리

docs/coding-conventions.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,16 @@
2121
허용: `id`, `url`, `uri`, `api`, `db`, `pg`, `mq`, `ai`, `llm`, `rag`, `stt`, `tts`, `pdf`
2222
비권장: `usr`, `msg`, `cfg`, `tmp``user`, `message`, `config`, `temp` 사용
2323

24+
### 1.4 타입 (TypeScript)
25+
26+
프론트엔드 TypeScript 타입 규약은 분리 문서로 관리한다 → [**`frontend-types.md`**](./frontend-types.md).
27+
28+
요점만:
29+
- PascalCase + 의미 있는 접미사 (`Dto` / `Request` / `Response` / `Result` / `Model` / `Props` / `State` / `Options` / `Id`).
30+
- `I` / `T` Hungarian prefix 금지.
31+
- 타입은 사용되는 레이어에 산다. `Dto → Entity → Model → Props` 단방향.
32+
- 전역 `types/` 폴더 금지. 슬라이스 내부에서 시작, 3곳 이상 중복 시 `shared` 승격.
33+
2434
---
2535

2636
## 2. 함수·메서드 설계

docs/frontend-types.md

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
# 프론트엔드 타입 시스템 — TypeScript
2+
3+
> StackUp 프론트엔드의 TypeScript 타입 규약. **타입은 사용되는 레이어에 산다.** 이름의 접미사가 책임 경계를 선언한다.
4+
>
5+
> 상위 컨텍스트 — [coding-conventions.md](./coding-conventions.md) (언어 공통 규약), [`frontend/CLAUDE.md`](../frontend/CLAUDE.md) (FSD 구조)
6+
> 적용 범위 — [`frontend/src/`](../frontend/src/)
7+
8+
---
9+
10+
## 목차
11+
12+
1. [원칙](#1-원칙)
13+
2. [접미사 ↔ 레이어 매트릭스](#2-접미사--레이어-매트릭스)
14+
3. [레이어별 타입 흐름](#3-레이어별-타입-흐름)
15+
4. [`type` vs `interface`](#4-type-vs-interface)
16+
5. [정의 위치](#5-정의-위치)
17+
6. [shared 승격 임계](#6-shared-승격-임계)
18+
7. [안티패턴](#7-안티패턴)
19+
20+
---
21+
22+
## 1. 원칙
23+
24+
- **PascalCase + 의미 있는 접미사**. 접미사가 타입의 책임·레이어를 신고한다.
25+
- `I` / `T` Hungarian prefix 금지 — TypeScript 컴파일러가 `type` / `interface` 를 충분히 구분한다.
26+
- **단방향 흐름**: `Dto → Entity → Model → Props`. 역방향 import 금지.
27+
- **전역 `types/` 폴더 금지**. 슬라이스 내부에서 시작, 진짜 공유될 때만 끌어올린다.
28+
29+
---
30+
31+
## 2. 접미사 ↔ 레이어 매트릭스
32+
33+
| 접미사 | 레이어 | 책임 ||
34+
|---|---|---|---|
35+
| `XxxDto` | `shared/api/` (OpenAPI 자동 생성) | 백엔드 wire format 스냅샷. 수정 금지 | `SessionDto`, `UserDto` |
36+
| `XxxRequest` / `XxxPayload` | API 호출부, 이벤트 publisher | HTTP 요청 body, RabbitMQ 메시지 payload | `CreateSessionRequest`, `ResumeAnalyzedPayload` |
37+
| `XxxResponse` | API 호출부 | HTTP 응답 raw shape (수동 정의 시) — **함수 반환에 사용 ✗** | `LoginResponse` |
38+
| `Xxx` (접미사 없음) | `domain/{slice}/model/` | 비즈니스 엔터티. Dto에서 매핑됨 | `User`, `Session`, `Resume` |
39+
| `XxxResult` | 도메인 / 유즈케이스 함수 반환 | 비즈니스 로직 출력 (HTTP 비종속) | `AnalysisResult`, `ValidationResult` |
40+
| `XxxModel` | `features/*/ui/`, `widgets/*/ui/` | UI 표시 전용 가공 데이터 | `SessionListItemModel`, `ServiceCardModel` |
41+
| `XxxProps` | 컴포넌트 옆 | React props | `ButtonProps`, `HomeHeroProps` |
42+
| `XxxState` | store / state machine | **명시적** 상태 모델. 단발 `useState` shape 에 사용 ✗ | `AuthState`, `SessionMachineState` |
43+
| `XxxOptions` | 함수·훅 설정 | 옵셔널 인자 묶음 | `UseTypewriterOptions` |
44+
| `XxxId` | 어디든 | branded identifier | `type SessionId = number & { readonly __brand: 'SessionId' }` |
45+
46+
---
47+
48+
## 3. 레이어별 타입 흐름
49+
50+
```
51+
[Backend OpenAPI]
52+
│ openapi-typescript 자동 생성
53+
54+
shared/api/generated.ts ── XxxDto
55+
│ 매퍼 함수 (toUser, toSession)
56+
57+
domain/{slice}/model/ ── Xxx (Entity)
58+
│ feature / widget 에서 UI 가공
59+
60+
features|widgets/*/ui/ ── XxxModel
61+
│ props 로 주입
62+
63+
React Component ── XxxProps
64+
```
65+
66+
**경계 규칙**:
67+
68+
- `XxxDto``shared/api/` 밖으로 새지 않는다. UI · 도메인이 Dto 를 직접 import 하면 anti-corruption layer 가 무너진다.
69+
- `Xxx` (Entity) 는 `domain/` 이 소유. features / widgets 는 read-only 로 참조한다.
70+
- `XxxModel` 은 슬라이스 내부에 머문다. 다른 슬라이스로 export 하지 않는다 — UI 표시 형태가 누수되면 변경 비용이 폭증한다.
71+
72+
---
73+
74+
## 4. `type` vs `interface`
75+
76+
`type` 기본값. `interface` 는 두 경우에만:
77+
78+
1. 클래스가 `implements` 할 때 (`ErrorBoundary` 등)
79+
2. declaration merging 필요 시 (외부 라이브러리 augmentation)
80+
81+
```ts
82+
// 좋음
83+
type SessionStatus = 'READY' | 'IN_PROGRESS' | 'COMPLETED';
84+
type CreateSessionRequest = { userId: UserId; mode: SessionMode };
85+
type AnalysisResult = { score: number; suggestions: string[] };
86+
87+
// 좋음 — implements 가 필요한 케이스
88+
interface ErrorBoundaryProps { children: ReactNode; fallback: ReactNode }
89+
class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> { ... }
90+
```
91+
92+
---
93+
94+
## 5. 정의 위치
95+
96+
| 무엇 | 어디 |
97+
|---|---|
98+
| 컴포넌트 props (단일 사용) | `{Component}.tsx` 상단, **export 안 함** |
99+
| 외부에서도 import 되는 props | `{Component}.types.ts` |
100+
| 도메인 엔터티 | `domain/{slice}/model/types.ts` |
101+
| 슬라이스 내 `XxxModel` | `features/{slice}/model/types.ts` 또는 `ui/` 내부 |
102+
| API `XxxDto` | `shared/api/generated.ts` (OpenAPI 재생성으로만 갱신) |
103+
104+
**전역 `src/types/` 폴더는 만들지 않는다.** 슬라이스 내부에서 시작하고, 진짜 공유될 때만 끌어올린다.
105+
106+
---
107+
108+
## 6. shared 승격 임계
109+
110+
타입을 `shared/` 로 올리는 시점은 **서로 다른 슬라이스 3곳 이상에서 동일한 shape 이 반복될 때**. 그 전엔 슬라이스 안에 산다.
111+
112+
> "언젠가 쓸지도 모르니 미리 shared 로" 는 항상 잘못된 추상화로 끝난다.
113+
114+
---
115+
116+
## 7. 안티패턴
117+
118+
-`XxxResponse` 를 함수 반환 타입으로 사용 — `XxxResult` 로.
119+
- ❌ UI 컴포넌트가 `SessionDto` 를 props 로 받음 — `SessionListItemModel` 로 매핑.
120+
- ❌ 로컬 `useState<{ ... }>` shape 에 `XxxState` 이름 붙이고 export.
121+
-`export type Props = { ... }` 같은 generic export — `{Component}Props` 로.
122+
-`src/types/` 또는 `src/shared/types/` 전역 폴더 (junk drawer 화).
123+
-`IXxx`, `TXxx` Hungarian prefix.
124+
-`Dto``features/` / `widgets/` / `domain/` 에서 import 되는 경우 — boundary 위반.

0 commit comments

Comments
 (0)