-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
refactor : useMutation으로 api 호출 방식 통일 #299
Conversation
1932b02
to
b3e7f7d
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
마참내......... 코멘트 쪼끔 남겨놨습니다 확인해주세용. 고생하셨습니다!! ^ㅅ^
src/home/apis/authVerification.ts
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tanstack query를 적용하면서 apis/authVerification
파일 내에 api 함수와 useMutation이 같이 위치하게 됐는데요.
useMutation은 home/hooks
로 분리하는 게 폴더 구조상 적절해 보입니다!
아니면 같은 파일 내에 위치시키신 이유가 따로 있을까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
저도 다른 API 호출 코드들과 통일성을 위해 apis
폴더 아래에는 axios를 이용한 API 호출 코드만 남기고 useMutation()
을 반환하는 커스텀 훅은 home/hooks
아래로 위치시키는게 좋을 것 같습니다!
추가적으로 postAuthVerificationEmail()
, getAuthVerificationCheck()
함수도 별도의 파일로 분리하는게 좋을 것 같네요.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
c532f3a
분리 했습니다!
GetAuthVerificationCheckResponse, | ||
PostAuthVerificationEmailResponse, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
확인해보니까 이 두 타입은 이제 사용하는 곳이 없던데, 한번 확인해보시고 이번 PR에서 멸종 ㄱㄱ 어때요
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
c532f3a
멸종 시켰습니다
쏜애플 - 멸종 이라는 노래도 들어보세요
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#300 과 작업 부분이 많이 겹치는 거 같은데 conflict 해결 가능하실까요??
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
수고하셨습니다👏👏
추가적으로 #262 에서 언급한 postAuthSignUp()
부분도 tanstack query를 사용해서 API 호출 상태를 관리하면 좋을 것 같네요!
src/home/apis/authVerification.ts
Outdated
//이전과 같은 이메일일 경우 errorBoundary로 가지 않고 에러 처리 | ||
if (error.response?.status === 400) return false; | ||
// 나머지는 errorBoundary로 처리 | ||
return true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
//이전과 같은 이메일일 경우 errorBoundary로 가지 않고 에러 처리 | |
if (error.response?.status === 400) return false; | |
// 나머지는 errorBoundary로 처리 | |
return true; | |
return error.response?.status !== 400 |
으로 줄일 수도 있을 것 같네요!
setAuthed(false); | ||
setError('인증 메일 재전송에 실패했습니다.'); | ||
}, | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
} | |
onSettled: () => { | |
setEmailSending(false); | |
resetTimer(); | |
}, |
다른 콜백들과의 일관성을 위해 mutation이 성공하거나 실패했을 때 호출되는 onSettled()
콜백에서 setEmailSending()
, resetTime()
를 호출하면 어떨까요?
추가적으로 onSettled()
콜백을 사용하면 동기적으로 mutation 결과를 기다릴 필요가 없으니 mutateAsync()
도 mutate()
로 바꿀 수 있을 것 같네요
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
onSettled 라는게 있군요
공식문서좀 읽어보는 습관을 들여야 하는데.. 감사합니다
mutateAsync에 관한 내용은 밑의 코멘트에 달아놓겠습니다!
setEmailError(res.error?.response?.data.message || '이메일을 다시 확인해주세요.'); | ||
} | ||
|
||
await postAuthVerificationEmailMutation.mutateAsync( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
onSuccess()
, onError()
함수는 mutation 결과에 따라 tanstack query가 동기적으로 호출해주니 mutateAsync()
를 사용할 필요가 없을 것 같아요!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
아주 좋은 방식인 것 같습니다! 다만 제가 mutateAsync를 사용한 이유는 React Hook Form의 isSubmitting 상태를 활용하여 버튼을 비활성화 하기 때문인데요.
const onEmailSubmit = async () => {
const fullEmail = parseFullEmail(email);
postAuthVerificationEmailMutation.mutate(
{ email: fullEmail, verificationType: 'SIGN_UP' },
{
onSuccess: () => {
onConfirm(fullEmail);
},
onError: (error: AxiosError<AuthErrorData>) => {
setEmailError(error.response?.data.message || '이메일을 다시 확인해주세요.');
},
}
);
};
mutate는 비동기적으로 API를 호출하는 작업이 있지만, 반환값은 void입니다.
mutate 내부 로직까진 정확히 알지 못하지만, 일반적으로 비동기 작업 이후의 콜백 함수(onSuccess, onError 등)는 태스크 큐로 들어가고, onEmailSubmit 함수는 콜 스택에서 빠지며 실행 흐름이 종료됩니다.
export const EmailForm = ({ onConfirm }: EmailFormProps) => {
const { email, emailError, onEmailSubmit, onChange } = useEmailForm({ onConfirm });
const {
handleSubmit,
formState: { isSubmitting },
} = useForm();
return (
<StyledSignupContentContainer onSubmit={handleSubmit(onEmailSubmit)}>
<StyledSignupContentTitle>회원가입</StyledSignupContentTitle>
{/* ... */}
</StyledSignupContentContainer>
);
};
RHF에서는 handleSubmit에 전달된 함수의 실행 상태에 따라 isSubmitting이 업데이트됩니다. 따라서 onEmailSubmit 함수가 실행을 완료하면 isSubmitting도 false(제출 완료)로 변경됩니다.
onEmailSubmit이 콜 스택에서 빠져나가면서 비동기 작업의 콜백 함수들이 태스크 큐에서 실행되고, 이로 인해 실행 흐름이 다음과 같이 진행될 가능성이 높습니다:
- 버튼을 누르자마자 제출이 완료되고 버튼은 활성화 상태
- 비동기 작업(API 호출) 진행
- 콜백 함수 동작에 따라 다음 컴포넌트로 이동하거나 에러 표시
mutateAsync를 사용하여 onEmailSubmit의 실행을 await하지 않으면, 중복 제출을 방지하기 위한 버튼 비활성화 기능이 의도대로 작동하기 힘들었습니다.
RHF의 formState를 사용하지 않고 별도의 상태를 관리하면 이 문제를 쉽게 해결할 수 있는데, 준이 코멘트 달아준 emailSending 부분이 그러한 방식인 것 같습니다. 혹시 이외에 더 좋은 방법이 있다면 알려주세요. 바로 적용해보겠습니다.
두 컴포넌트 모두 제가 작성한 것이 아니기 때문에 어떤 방식이 더 나은지는 판단하기 어려운데요. 아무튼 두 가지 방식 중 하나로 통일하는 것이 좋다고 생각해 코멘트를 남겼습니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
오.. 자세한 설명 감사합니다👏👏
버튼을 disabled
상태로 만드는 것이 API 중복 호출을 방지하기 위함이라면 formState.isSubmitting
대신 mutation.isPending
으로 버튼의 disabled
상태를 관리하면 어떨까요? (지금은 EmailForm
컴포넌트에서 postAuthVerificationEmailMutation
이 선언되어 있지 않아서 EmailForm
컴포넌트에서 postAuthVerificationEmailMutation
의 상태를 가져오려면 useMutationState()
을 사용해야 될 것 같지만)
tkdodo씨는 mutateAsync()
는 사용자에게 프로미스의 제어권을 넘기기 때문에 수동으로 프로미스 에러 처리를 해줘야 해서 mutate()
사용을 권장하더라구요
Since mutateAsync gives you control over the Promise, you also have to catch errors manually, or you might get an unhandled promise rejection.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
코멘트에서도 말했듯 기능은 유사하지만 각자 사용하는 상태가 달라서 고민하고 있었는데, 아주 좋은 방법인 것 같아서 바로 반영했습니다!
추후에 리액트 훅 폼으로 폼 입력 및 에러 관리, mutation.isPending으로 제출 상태 관리 방식으로 통일하면 좋을 것 같습니다.
const { | ||
handleSubmit, | ||
formState: { isSubmitting }, | ||
} = useForm(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
react-hook-form으로 form 제출 상태를 관리하는게 좋네요👍
email error도 react-hook-form으로 관리할 수 있을 것 같은데 나중에 해보는 걸루..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
좋습니다~
수정하면서 추후에 진행하면 좋을 것 같은 이슈가 있어서 의견 써봅니다.
위에 기재해놓은 hook들은 인증 메일 전송, 검증, 재전송에 사용되는 hook 들입니다. 또 세부적으로 봤을때 비밀번호 재설정쪽 hook만 react-hook-form을 활용했는데요. 일단은 이슈 범위를 조금 벗어나는 것 같아서 코멘트로 남깁니다! |
35dec61
to
2e2815e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nijuy |
확실히 지금 훅들은 특정 컴포넌트에 강하게 결합되어 있고 재사용성도 떨어져 커스텀 훅으로 분리하는 효과가 많이 떨어지는 것 같네요(오히려 depth만 깊어져서 코드 파악하기가 어려운 느낌..) 특히 tanstack query와 react hook form을 사용해서 직접 관리해야 하는 상태들도 줄이면 훨씬 좋아질 것 같아요(e.g., |
1️⃣ 어떤 작업을 했나요? (Summary)
기존 코드에 영향을 미치지 않는 변경사항
기존 코드에 영향을 미치는 변경사항
useMutation을 통해 api 호출 방식을 통일 했습니다.
juun이 얘기한 대로
try catch를 적용하면 promise가 reject되지 않기 때문에 errorBoundary를 랜더링 시킬 수 없습니다.
수정된 코드입니다.
throwOnError를 통해 처리 가능한 에러 status 이외의 에러는 상위 컴포넌트인 errorBoundary를 랜더링 시키도록 했습니다.
버그 픽스
2️⃣ 알아두시면 좋아요!
emailForm과 emailAuth에는 중복 제출 방지를 위한 버튼 disable 로직이 존재합니다.
void인 mutate를 적용하면 await에서 실행 흐름을 기다리지 않기 때문에
isSubmitting같이 제출 상태를 체크하는 부분이 제대로 작동하지 않습니다.
때문에 mutateAsync를 통해 promise를 반환하도록 했습니다.
근데 throwOnError에서 status 잘 쓴건지 모르겠네요
혹시 throw하면 안되는 혹은 throw 해야하는(errorBoundary 띄워야 하는) status가 있는지 한번 봐주시면 감사하겠습니다
3️⃣ 추후 작업
리뷰 환영합니다.
4️⃣ 체크리스트 (Checklist)
main
브랜치의 최신 코드를pull
받았나요?