Realworld with FastAPI
Realworld by FastAPI + layerd Architecture, TDD 도입시도
Realworld with FastAPI
시스템 아키텍처 및 API 문서
전체 아키텍처
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
┌─────────────────────────────────────────────────────────────────────────────┐
│ FastAPI Application │
│ (main.py) │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ API Layer │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ auth │ │ article │ │ comment │ │ profile │ │ tag │ │
│ │ router │ │ router │ │ router │ │ router │ │ router │ │
│ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ │
└───────┼────────────┼────────────┼────────────┼────────────┼─────────────────┘
│ │ │ │ │
▼ ▼ ▼ ▼ ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 🔧 Core Layer (dependencies.py) │
│ │
│ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ │
│ │ get_current_user │ │ get_*_service │ │ get_*_repository │ │
│ │ (JWT 인증) │ │ (서비스 주입) │ │ (레포지토리 주입) │ │
│ └──────────────────┘ └──────────────────┘ └──────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ security.py (Argon2 + JWT) │ │
│ │ • hash_password() • verify_password() • create/verify_token() │ │
│ └──────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ Service Layer │
│ ┌───────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ UserService │ │ ArticleService │ │ CommentService │ │
│ └───────────────┘ └─────────────────┘ └─────────────────┘ │
│ ┌─────────────────┐ │
│ │ ProfileService │ ← 비즈니스 로직 처리 │
│ └─────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ Repository Layer │
│ (interfaces.py로 추상화) │
│ ┌────────────────┐ ┌────────────────┐ ┌────────────────┐ │
│ │ UserRepository │ │ArticleRepository│ │CommentRepository│ │
│ └────────────────┘ └────────────────┘ └────────────────┘ │
│ ┌────────────────┐ ┌────────────────┐ ┌────────────────┐ │
│ │ TagRepository │ │FavoriteRepository│ │FollowRepository│ │
│ └────────────────┘ └────────────────┘ └────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ Model Layer │
│ ┌──────┐ ┌─────────┐ ┌─────────┐ ┌──────┐ ┌────────┐ ┌──────┐ │
│ │ User │ │ Article │ │ Comment │ │ Tag │ │Favorite│ │Follow│ │
│ └──────┘ └─────────┘ └─────────┘ └──────┘ └────────┘ └──────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ Database (SQLite + SQLModel) │
└─────────────────────────────────────────────────────────────────────────────┘
API 엔드포인트
Auth (인증)
| Method | Endpoint | 설명 | 인증 |
|---|---|---|---|
POST | /users | 회원가입 | ❌ |
POST | /users/login | 로그인 | ❌ |
GET | /user | 내 정보 조회 | ✅ |
PUT | /user | 내 정보 수정 | ✅ |
요청/응답 예시:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// POST /users (회원가입)
{
"user": {
"email": "user@example.com",
"password": "password123",
"username": "user1"
}
}
// 응답
{
"user": {
"email": "user@example.com",
"username": "user1",
"token": "jwt.token.here"
}
}
Articles (게시글)
| Method | Endpoint | 설명 | 인증 |
|---|---|---|---|
GET | /articles | 게시글 목록 조회 | ❌ |
GET | /articles?author={username} | 작성자로 필터링 | ❌ |
GET | /articles?tag={tag} | 태그로 필터링 | ❌ |
GET | /articles?favorited={username} | 좋아요한 글 필터링 | ❌ |
POST | /articles | 게시글 작성 | ✅ |
GET | /articles/{slug} | 게시글 상세 조회 | ❌ |
PUT | /articles/{slug} | 게시글 수정 | ✅ |
DELETE | /articles/{slug} | 게시글 삭제 | ✅ |
POST | /articles/{slug}/favorite | 좋아요 | ✅ |
DELETE | /articles/{slug}/favorite | 좋아요 취소 | ✅ |
요청/응답 예시:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// POST /articles (게시글 작성)
{
"article": {
"title": "How to train your dragon",
"description": "Ever wonder how?",
"body": "It takes a Jacobian",
"tagList": ["dragons", "training"]
}
}
// 응답
{
"article": {
"slug": "how-to-train-your-dragon",
"title": "How to train your dragon",
"description": "Ever wonder how?",
"body": "It takes a Jacobian",
"tagList": ["dragons", "training"],
"favorited": false,
"favoritesCount": 0,
"author": { ... }
}
}
Comments (댓글)
| Method | Endpoint | 설명 | 인증 |
|---|---|---|---|
GET | /articles/{slug}/comments | 댓글 목록 조회 | ❌ |
POST | /articles/{slug}/comments | 댓글 작성 | ✅ |
DELETE | /articles/{slug}/comments/{id} | 댓글 삭제 | ✅ |
요청/응답 예시:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// POST /articles/{slug}/comments
{
"comment": {
"body": "Great article!"
}
}
// 응답
{
"comment": {
"id": 1,
"body": "Great article!",
"author": { ... }
}
}
Profiles (프로필)
| Method | Endpoint | 설명 | 인증 |
|---|---|---|---|
GET | /profiles/{username} | 프로필 조회 | ❌ |
POST | /profiles/{username}/follow | 팔로우 | ✅ |
DELETE | /profiles/{username}/follow | 언팔로우 | ✅ |
응답 예시:
1
2
3
4
5
6
7
8
9
// GET /profiles/{username}
{
"profile": {
"username": "jake",
"bio": "I work at statefarm",
"image": "https://...",
"following": false
}
}
Tags (태그)
| Method | Endpoint | 설명 | 인증 |
|---|---|---|---|
GET | /tags | 태그 목록 조회 | ❌ |
응답 예시:
1
2
3
4
// GET /tags
{
"tags": ["reactjs", "angularjs", "dragons"]
}
요청 흐름도 (예: 게시글 작성)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
┌──────────┐ POST /articles ┌──────────┐
│ Client │ ─────────────────▶ │ article │
│ │ + JWT Token │ router │
└──────────┘ └────┬─────┘
│
┌──────────────────────┼──────────────────────┐
│ ▼ │
│ ┌─────────────────────────────────────┐ │
│ │ get_current_user() │ │
│ │ JWT 검증 → User 객체 반환 │ │
│ └─────────────────────────────────────┘ │
│ │ │
│ ┌──────────────────▼──────────────────┐ │
│ │ ArticleService │ │
│ │ create_article() │ │
│ │ • slug 생성 │ │
│ │ • 태그 처리 │ │
│ │ • 응답 빌드 │ │
│ └──────────────────┬──────────────────┘ │
│ │ │
│ ┌──────────────────▼──────────────────┐ │
│ │ ArticleRepository │ │
│ │ TagRepository │ │
│ │ (DB 저장) │ │
│ └──────────────────┬──────────────────┘ │
│ │ │
└──────────────────────┼──────────────────────┘
│
┌──────────┐ { article: {...} } │
│ Client │ ◀────────────────────────┘
│ │
└──────────┘
디렉토리 구조
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
app/
├── main.py # 앱 진입점, 라우터 등록
├── api/ # ** API 엔드포인트
│ ├── auth.py # POST /users, /users/login, GET/PUT /user
│ ├── article.py # /articles CRUD, /favorite
│ ├── comment.py # /articles/{slug}/comments
│ ├── profile.py # /profiles/{username}, /follow
│ └── tag.py # GET /tags
├── core/ # ** 핵심 설정
│ ├── database.py # DB 연결 (SQLite + SQLModel)
│ ├── dependencies.py # DI 컨테이너 (의존성 주입)
│ └── security.py # 인증 (Argon2 + JWT)
├── services/ # ** 비즈니스 로직
├── repositories/ # ** 데이터 접근
├── models/ # ** DB 모델
└── schemas/ # ** 요청/응답 스키마
아키텍처 특징
| 특징 | 설명 |
|---|---|
| 레이어드 아키텍처 | API → Service → Repository → Model |
| 의존성 역전 (DIP) | interfaces.py로 Repository 추상화 |
| 의존성 주입 (DI) | FastAPI Depends()를 통한 자동 주입 |
| 보안 | Argon2 해싱 + JWT 인증 |
| DB | SQLite + SQLModel (ORM) |
| 테스트 | TDD지향, 테스트 목록 작성 후 기능 개발 |
This post is licensed under CC BY 4.0 by the author.
