Skip to content

Commit aa20c5a

Browse files
committed
feat: React Query 훅 보호 및 빈 타입 처리 개선
- 기존 훅 파일 legacy 폴더로 이동하여 비즈니스 로직 보호 - API 변경사항 감지 및 리포트 자동 생성 - 빈 Request/Response 타입을 interface 대신 type으로 변경 - Response 없음: export type ResponseType = void - Request 없음: export type RequestType = Record<string, never> - 변경사항 처리 가이드 문서 추가 (migration-guide.md) - API 팩토리는 항상 덮어쓰기, 훅은 새로 생성된 API만 생성
1 parent 9163500 commit aa20c5a

File tree

5 files changed

+475
-5
lines changed

5 files changed

+475
-5
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ docs {
9595
- **[GitHub Apps 설정 가이드](./docs/github-apps-simple.md)** - Workflow 파일 예시 포함
9696
- **[Bruno 파일 작성 튜토리얼](./docs/bruno-tutorial.md)** - 단계별 따라하기
9797
- **[Bruno 파일 작성 가이드](./docs/bruno-guide.md)** - 상세 레퍼런스
98+
- **[변경사항 처리 가이드](./docs/migration-guide.md)** - React Query 훅 변경사항 처리 방법
9899

99100
## 라이선스
100101

docs/migration-guide.md

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
# React Query 훅 변경사항 처리 가이드
2+
3+
## 개요
4+
5+
이 프로젝트는 기존 React Query 훅 파일의 비즈니스 로직을 보호하기 위해 다음과 같은 전략을 사용합니다:
6+
7+
1. **API 팩토리 (api.ts)**: 항상 덮어쓰기 (최신 API 시그니처 유지)
8+
2. **React Query 훅**: 기존 파일이 있으면 `legacy/` 폴더로 이동 후 새 파일 생성
9+
10+
## 파일 구조
11+
12+
```
13+
src/apis/
14+
├── Auth/
15+
│ ├── api.ts # 항상 덮어쓰기됨
16+
│ ├── post-signOut.ts # 새로 생성됨
17+
│ ├── legacy/ # 기존 파일 보관
18+
│ │ ├── post-signOut.ts # 기존 파일 (비즈니스 로직 포함)
19+
│ │ └── post-signOut.CHANGES.md # 변경사항 리포트
20+
│ └── index.ts
21+
```
22+
23+
## 동작 방식
24+
25+
### 1. 기존 파일이 없는 경우
26+
27+
새 API가 추가되면 훅 파일이 새로 생성됩니다.
28+
29+
```
30+
Auth/
31+
├── api.ts
32+
├── post-signOut.ts # 새로 생성
33+
└── index.ts
34+
```
35+
36+
### 2. 기존 파일이 있는 경우
37+
38+
기존 훅 파일이 있으면:
39+
40+
1. `legacy/` 폴더로 이동
41+
2. 변경사항 감지 및 리포트 생성
42+
3. 새 훅 파일 생성
43+
44+
```
45+
Auth/
46+
├── api.ts
47+
├── post-signOut.ts # 새로 생성됨
48+
├── legacy/
49+
│ ├── post-signOut.ts # 기존 파일 (보호됨)
50+
│ └── post-signOut.CHANGES.md # 변경사항 리포트
51+
└── index.ts
52+
```
53+
54+
## 변경사항 리포트
55+
56+
각 변경된 훅 파일에 대해 `CHANGES.md` 파일이 생성됩니다.
57+
58+
### 리포트 예시
59+
60+
```markdown
61+
# 변경사항: post-signOut.ts
62+
63+
## 변경 일시
64+
2025-01-XX
65+
66+
## API 정보
67+
- URL: /auth/sign-out
68+
- Method: POST
69+
- Function: postSignOut
70+
71+
## 변경 내용
72+
73+
### response-type
74+
**Response 타입 변경**
75+
- 이전: `SignOutResponse`
76+
- 현재: `SignOutResponseV2`
77+
78+
### api-signature
79+
**API URL 변경**
80+
- 이전: `/auth/sign-out`
81+
- 현재: `/auth/v2/sign-out`
82+
83+
## 권장 조치
84+
1. `legacy/post-signOut.ts` 파일의 비즈니스 로직 확인
85+
2.`post-signOut.ts` 파일과 비교
86+
3. 필요한 커스텀 로직을 새 파일에 수동 병합
87+
4. 병합 완료 후 `legacy/post-signOut.ts` 파일 삭제
88+
```
89+
90+
## 수동 병합 방법
91+
92+
### 1. 변경사항 확인
93+
94+
```bash
95+
# 변경사항 리포트 확인
96+
cat src/apis/Auth/legacy/post-signOut.CHANGES.md
97+
98+
# 기존 파일과 새 파일 비교
99+
diff src/apis/Auth/legacy/post-signOut.ts src/apis/Auth/post-signOut.ts
100+
```
101+
102+
### 2. 커스텀 로직 확인
103+
104+
기존 파일에서 추가한 커스텀 로직을 확인합니다:
105+
106+
```typescript
107+
// legacy/post-signOut.ts
108+
const usePostSignOut = () => {
109+
return useMutation({
110+
mutationFn: authApi.postSignOut,
111+
// 커스텀 옵션
112+
onSuccess: (data) => {
113+
// 커스텀 성공 핸들러
114+
queryClient.invalidateQueries({ queryKey: [QueryKeys.auth.profile] });
115+
},
116+
onError: (error) => {
117+
// 커스텀 에러 핸들러
118+
toast.error('로그아웃 실패');
119+
},
120+
});
121+
};
122+
```
123+
124+
### 3. 새 파일에 병합
125+
126+
새 파일에 커스텀 로직을 추가합니다:
127+
128+
```typescript
129+
// post-signOut.ts
130+
const usePostSignOut = () => {
131+
return useMutation({
132+
mutationFn: authApi.postSignOut,
133+
// 기존 커스텀 로직 추가
134+
onSuccess: (data) => {
135+
queryClient.invalidateQueries({ queryKey: [QueryKeys.auth.profile] });
136+
},
137+
onError: (error) => {
138+
toast.error('로그아웃 실패');
139+
},
140+
});
141+
};
142+
```
143+
144+
### 4. Legacy 파일 정리
145+
146+
병합이 완료되면 legacy 파일을 삭제합니다:
147+
148+
```bash
149+
rm -rf src/apis/Auth/legacy/post-signOut.ts
150+
rm src/apis/Auth/legacy/post-signOut.CHANGES.md
151+
```
152+
153+
## 주의사항
154+
155+
### API 팩토리는 항상 덮어쓰기됨
156+
157+
`api.ts` 파일은 항상 최신 상태로 덮어쓰기되므로, 이 파일에는 커스텀 로직을 추가하지 마세요.
158+
159+
### 훅 파일만 보호됨
160+
161+
React Query 훅 파일만 보호됩니다. 다른 파일들 (queryKeys.ts, index.ts 등)은 덮어쓰기됩니다.
162+
163+
### 변경사항이 없어도 이동됨
164+
165+
API 시그니처가 동일해도 기존 파일은 legacy 폴더로 이동됩니다. 이는 향후 변경사항을 추적하기 위함입니다.
166+
167+
## 자주 묻는 질문
168+
169+
### Q: Legacy 파일을 언제 삭제해야 하나요?
170+
171+
A: 커스텀 로직을 새 파일에 병합한 후 삭제하세요. 병합이 완료되지 않았다면 보관해두는 것이 안전합니다.
172+
173+
### Q: API가 변경되었는데 훅 파일이 업데이트되지 않았어요
174+
175+
A: 훅 파일은 보호되므로 수동으로 병합해야 합니다. `CHANGES.md` 파일을 확인하고 필요한 부분만 업데이트하세요.
176+
177+
### Q: 여러 파일이 변경되었을 때 어떻게 처리하나요?
178+
179+
A: 각 파일마다 별도의 `CHANGES.md` 파일이 생성되므로, 하나씩 확인하고 병합하세요.
180+
181+

src/generator/apiFactoryGenerator.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,8 @@ export function generateApiFactory(
102102
}
103103
}
104104
} else {
105-
const defaultType = `export interface ${responseType} {\n // TODO: Define response type\n}`;
105+
// Response가 없으면 void 타입 사용
106+
const defaultType = `export type ${responseType} = void;`;
106107
if (!typeDefinitions.has(defaultType)) {
107108
typeDefinitions.add(defaultType);
108109
lines.push(defaultType);
@@ -124,15 +125,17 @@ export function generateApiFactory(
124125
}
125126
}
126127
} catch {
127-
const defaultRequestType = `export interface ${requestType} {\n // TODO: Define request type\n}`;
128+
// Request body 파싱 실패시 Record<string, never> 사용
129+
const defaultRequestType = `export type ${requestType} = Record<string, never>;`;
128130
if (!typeDefinitions.has(defaultRequestType)) {
129131
typeDefinitions.add(defaultRequestType);
130132
lines.push(defaultRequestType);
131133
lines.push('');
132134
}
133135
}
134136
} else {
135-
const defaultRequestType = `export interface ${requestType} {\n // TODO: Define request type\n}`;
137+
// Request body가 없으면 Record<string, never> 사용
138+
const defaultRequestType = `export type ${requestType} = Record<string, never>;`;
136139
if (!typeDefinitions.has(defaultRequestType)) {
137140
typeDefinitions.add(defaultRequestType);
138141
lines.push(defaultRequestType);

0 commit comments

Comments
 (0)