11import { useState } from 'react'
22
3- import { ApiError } from '@/shared/api/base-api'
3+ import { ApiError , baseApi } from '@/shared/api/base-api'
44
55import {
66 initialSignupFormData ,
@@ -12,47 +12,36 @@ import {
1212} from './signup.schema'
1313import { useSignupMutation } from './use-signup-mutation'
1414
15- /*
16- 외부에서 성공 시 실행할 콜백 전달받음
17- */
1815type UseSignupFormParams = {
1916 onSuccess : ( ) => void
2017}
2118
2219export 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