Skip to content

Commit b851720

Browse files
committed
refactor(auth): unify oauth request and error handling
1 parent 2f6053c commit b851720

1 file changed

Lines changed: 30 additions & 69 deletions

File tree

src/features/auth-dialog/model/use-signup-form.ts

Lines changed: 30 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { useState } from 'react'
22

3-
import { ApiError } from '@/shared/api/base-api'
3+
import { ApiError, baseApi } from '@/shared/api/base-api'
44

55
import {
66
initialSignupFormData,
@@ -12,47 +12,36 @@ import {
1212
} from './signup.schema'
1313
import { useSignupMutation } from './use-signup-mutation'
1414

15-
/*
16-
외부에서 성공 시 실행할 콜백 전달받음
17-
*/
1815
type UseSignupFormParams = {
1916
onSuccess: () => void
2017
}
2118

2219
export function useSignupForm({ onSuccess }: UseSignupFormParams) {
23-
/*
24-
상태 관리 (UI 상태)
25-
*/
26-
27-
// 입력값 상태
20+
// 폼 입력값 상태
2821
const [formData, setFormData] = useState<SignupFormData>(initialSignupFormData)
2922

30-
// 필드별 에러 메시지
23+
// 필드별 에러 메시지 상태
3124
const [fieldErrors, setFieldErrors] = useState<SignupFieldErrors>({})
3225

33-
// 사용자가 입력을 건드렸는지 여부
26+
// 사용자가 한 번이라도 건드린 필드 여부
3427
const [touchedFields, setTouchedFields] = useState<Partial<Record<SignupFieldName, boolean>>>({})
3528

36-
// 이메일 인증 여부 (현재 mock)
29+
// 이메일 인증 여부
3730
const [isEmailVerified, setIsEmailVerified] = useState(false)
3831

3932
// 공통 에러 메시지
4033
const [error, setError] = useState('')
4134

42-
// submit 로딩 상태
35+
// 회원가입 요청 로딩 상태
4336
const [loading, setLoading] = useState(false)
4437

4538
// 이메일 인증 버튼 로딩 상태
4639
const [isVerifying, setIsVerifying] = useState(false)
4740

48-
/*
49-
React Query mutation
50-
*/
41+
// 회원가입 mutation
5142
const { mutateAsync } = useSignupMutation()
5243

53-
/*
54-
입력값 변경 핸들러
55-
*/
44+
// 입력값 변경 시 상태 갱신 및 실시간 검증
5645
const handleChange = (field: SignupFieldName) => (e: React.ChangeEvent<HTMLInputElement>) => {
5746
const value = e.target.value
5847

@@ -62,26 +51,22 @@ export function useSignupForm({ onSuccess }: UseSignupFormParams) {
6251
[field]: value
6352
}
6453

65-
// 이메일 변경 시 인증 초기화
54+
// 이메일이 바뀌면 기존 인증 상태 초기화
6655
if (field === 'email') {
6756
setIsEmailVerified(false)
6857
}
6958

70-
/*
71-
실시간 검증 (이미 터치된 필드만)
72-
*/
7359
setFieldErrors((prevErrors) => {
7460
const nextErrors = { ...prevErrors }
7561

62+
// 이미 터치된 필드만 실시간 검증
7663
if (touchedFields[field]) {
7764
const message = validateSignupField(field, value, nextFormData)
7865
if (message) nextErrors[field] = message
7966
else delete nextErrors[field]
8067
}
8168

82-
/*
83-
비밀번호 변경 시 confirmPassword도 재검증
84-
*/
69+
// 비밀번호 변경 시 비밀번호 확인도 다시 검증
8570
if (field === 'password' && touchedFields.confirmPassword) {
8671
const confirmMessage = validateSignupField(
8772
'confirmPassword',
@@ -99,13 +84,11 @@ export function useSignupForm({ onSuccess }: UseSignupFormParams) {
9984
return nextFormData
10085
})
10186

102-
// 공통 에러 초기화
87+
// 공통 에러 메시지 초기화
10388
if (error) setError('')
10489
}
10590

106-
/*
107-
blur 시 검증
108-
*/
91+
// blur 시 해당 필드 검증
10992
const handleBlur = (field: SignupFieldName) => () => {
11093
setTouchedFields((prev) => ({
11194
...prev,
@@ -122,9 +105,7 @@ export function useSignupForm({ onSuccess }: UseSignupFormParams) {
122105
})
123106
}
124107

125-
/*
126-
이메일 인증 (현재 mock)
127-
*/
108+
// 이메일 인증 처리
128109
const handleVerifyEmail = async () => {
129110
const emailError = validateSignupField('email', formData.email, formData)
130111

@@ -133,7 +114,7 @@ export function useSignupForm({ onSuccess }: UseSignupFormParams) {
133114
email: true
134115
}))
135116

136-
// 이메일 형식이 틀리면 인증 진행 안 함
117+
// 이메일 형식이 올바르지 않으면 인증 요청 중단
137118
if (emailError) {
138119
setFieldErrors((prev) => ({
139120
...prev,
@@ -146,12 +127,12 @@ export function useSignupForm({ onSuccess }: UseSignupFormParams) {
146127
setError('')
147128

148129
try {
149-
// TODO: 실제 API로 교체
130+
// TODO: 실제 이메일 인증 API로 교체
150131
await new Promise((resolve) => setTimeout(resolve, 1500))
151132

152133
setIsEmailVerified(true)
153134

154-
// 이메일 에러 제거
135+
// 인증 완료 시 이메일 에러 제거
155136
setFieldErrors((prev) => {
156137
const next = { ...prev }
157138
delete next.email
@@ -166,16 +147,12 @@ export function useSignupForm({ onSuccess }: UseSignupFormParams) {
166147
}
167148
}
168149

169-
/*
170-
submit 처리
171-
*/
150+
// 회원가입 submit 처리
172151
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
173152
e.preventDefault()
174153
setError('')
175154

176-
/*
177-
모든 필드 touched 처리
178-
*/
155+
// 제출 시 모든 필드를 touched 처리
179156
setTouchedFields({
180157
nickname: true,
181158
email: true,
@@ -184,30 +161,24 @@ export function useSignupForm({ onSuccess }: UseSignupFormParams) {
184161
inviteCode: true
185162
})
186163

187-
/*
188-
전체 검증
189-
*/
164+
// 전체 폼 검증
190165
const nextErrors = validateSignupForm(formData, isEmailVerified)
191166
setFieldErrors(nextErrors)
192167

193-
// 에러 있으면 submit 중단
168+
// 검증 에러가 있으면 요청 중단
194169
if (Object.keys(nextErrors).length > 0) return
195170

196171
setLoading(true)
197172

198173
try {
199-
/*
200-
React Query mutation 실행
201-
*/
174+
// 회원가입 요청
202175
await mutateAsync({
203176
email: formData.email.trim(),
204177
nickname: formData.nickname.trim(),
205178
password: formData.password
206179
})
207180

208-
/*
209-
성공 처리
210-
*/
181+
// 성공 시 폼 초기화
211182
setFormData(initialSignupFormData)
212183
setFieldErrors({})
213184
setTouchedFields({})
@@ -227,8 +198,9 @@ export function useSignupForm({ onSuccess }: UseSignupFormParams) {
227198
const serverErrors = error.data as Record<string, string>
228199
const mappedErrors: SignupFieldErrors = {}
229200

201+
// 서버 에러 키를 폼 필드 에러로 매핑
230202
Object.entries(serverErrors).forEach(([key, message]) => {
231-
if (key in initialSignupFormData) {
203+
if (Object.prototype.hasOwnProperty.call(initialSignupFormData, key)) {
232204
mappedErrors[key as SignupFieldName] = message
233205
}
234206
})
@@ -255,22 +227,13 @@ export function useSignupForm({ onSuccess }: UseSignupFormParams) {
255227
}
256228
}
257229

258-
/*
259-
TODO: OAuth 로그인
260-
*/
230+
// OAuth 로그인 URL 조회 및 팝업 열기
261231
const handleOAuth = async (provider: 'google' | 'kakao') => {
262232
try {
263-
const response = await fetch(`/api/auth/${provider}/url`)
264-
265-
// 응답 상태 체크
266-
if (!response.ok) {
267-
throw new Error(`OAuth 로그인 URL 요청에 실패했습니다. (${response.status})`)
268-
}
269-
270-
const result = await response.json()
271-
const url = result?.url
233+
// 공통 baseApi를 사용해 base URL 정책 통일
234+
const response = await baseApi.get(`/auth/${provider}/url`)
235+
const url = response.data?.url
272236

273-
// 에러 메세지
274237
if (!url) {
275238
throw new Error('OAuth 로그인 URL이 응답에 포함되어 있지 않습니다.')
276239
}
@@ -281,16 +244,14 @@ export function useSignupForm({ onSuccess }: UseSignupFormParams) {
281244
}
282245
}
283246

247+
// 입력 필드 스타일 계산
284248
const inputClassName = (field: SignupFieldName) =>
285249
`h-12 w-full rounded-xl border px-3 text-sm outline-none transition-colors ${
286250
fieldErrors[field]
287251
? 'border-red-400 focus:border-red-500'
288252
: 'border-slate-200 focus:border-[#56c1c9]'
289253
}`
290254

291-
/*
292-
외부 반환
293-
*/
294255
return {
295256
formData,
296257
fieldErrors,

0 commit comments

Comments
 (0)