-
Notifications
You must be signed in to change notification settings - Fork 2
[FE] SISC1-195 [FEAT] 로그인, 회원가입, 이메일 인증번호 전송 및 이메일 번호 인증 api 연동 #90
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
[FE] SISC1-195 [FEAT] 로그인, 회원가입, 이메일 인증번호 전송 및 이메일 번호 인증 api 연동 #90
Conversation
Walkthrough프런트엔드 인증 흐름을 API 기반으로 전환했습니다. 중앙화된 Axios 인스턴스(api)와 재발급 로직을 추가하고, Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant LoginForm
participant auth
participant api
participant Backend
User->>LoginForm: 이메일/비밀번호 입력 후 로그인 시도
LoginForm->>LoginForm: 이전 요청 취소 (AbortController)
LoginForm->>auth: login({email,password}, signal)
auth->>api: POST /api/auth/login
api->>Backend: 요청 전달
Backend-->>api: accessToken, refreshToken 응답
api-->>auth: 응답 반환 (토큰 저장)
auth-->>LoginForm: 성공 응답
LoginForm-->>User: 홈으로 네비게이션
sequenceDiagram
actor User
participant SignUpForm
participant auth
participant api
participant Backend
User->>SignUpForm: 인증번호 발송 요청 (이메일)
SignUpForm->>SignUpForm: 이전 요청 취소 (AbortController)
SignUpForm->>auth: sendVerificationNumber({email}, signal)
auth->>api: POST /api/email/send?email=...
api->>Backend: 요청 전달
Backend-->>api: 성공 응답
api-->>auth: 응답 반환
auth-->>SignUpForm: isVerificationSent = true
User->>SignUpForm: 인증번호 입력 및 확인
SignUpForm->>auth: checkVerificationNumber({email,code}, signal)
auth->>api: POST /api/email/verify
Backend-->>api: 검증 결과
auth-->>SignUpForm: isVerificationChecked = true
User->>SignUpForm: 회원가입 제출
SignUpForm->>auth: signUp(payload, signal)
auth->>api: POST /api/auth/signup
Backend-->>api: 완료 응답
SignUpForm-->>User: 로그인 페이지로 네비게이션
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 분 검토 시 주의할 항목:
Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
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.
Actionable comments posted: 3
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
frontend/src/components/login/LoginForm.jsx(3 hunks)frontend/src/components/signup/SignUpForm.jsx(3 hunks)frontend/src/utils/auth.js(1 hunks)frontend/src/utils/axios.js(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
frontend/src/utils/axios.js (1)
frontend/src/utils/auth.js (5)
res(16-16)res(23-23)res(25-25)res(36-39)res(47-50)
frontend/src/components/signup/SignUpForm.jsx (2)
frontend/src/components/login/LoginForm.jsx (4)
password(17-17)abortRef(37-37)nav(14-14)frontend/src/utils/auth.js (6)
sendVerificationNumber(35-42)sendVerificationNumber(35-42)checkVerificationNumber(43-53)checkVerificationNumber(43-53)signUp(5-18)signUp(5-18)
frontend/src/utils/auth.js (3)
frontend/src/components/signup/SignUpForm.jsx (5)
nickname(13-13)password(17-17)phoneNumber(16-16)verificationNumber(14-14)frontend/src/components/login/LoginForm.jsx (2)
password(17-17)frontend/src/utils/axios.js (3)
res(20-20)api(3-6)api(3-6)
frontend/src/components/login/LoginForm.jsx (2)
frontend/src/components/signup/SignUpForm.jsx (2)
abortRef(23-23)nav(25-25)frontend/src/utils/auth.js (2)
login(20-33)login(20-33)
| setVerificationSent(true); | ||
|
|
||
| // 도중에 요청 시 전 요청 취소 | ||
| abortRef.current?.abort(); | ||
| abortRef.current = new AbortController(); | ||
|
|
||
| // 인증번호 발송 로직 & api 자리 | ||
| try { | ||
| await sendVerificationNumber({ email: email }, abortRef.current.signal); | ||
|
|
||
| // 인증번호 발송 로직 | ||
| alert('인증번호가 발송되었습니다.'); | ||
| alert('인증번호가 발송되었습니다.'); | ||
| } catch (err) { | ||
| console.log('status:', err.status); | ||
| console.log('data:', err.data); | ||
| console.log('message:', err.message); | ||
| } | ||
| }; | ||
| const handleSignUp = (e) => { | ||
| const handleCheckVerificationNumber = async () => { | ||
| setVerificationChecked(true); | ||
|
|
||
| // 도중에 요청 시 전 요청 취소 | ||
| abortRef.current?.abort(); | ||
| abortRef.current = new AbortController(); | ||
|
|
||
| // 인증번호 발송 로직 & api 자리 | ||
| try { | ||
| await checkVerificationNumber( | ||
| { email: email, verificationNumber: verificationNumber }, | ||
| abortRef.current.signal | ||
| ); | ||
|
|
||
| setVerificationChecked(true); | ||
| alert('인증되었습니다.'); | ||
| } catch (err) { | ||
| console.log('status:', err.status); | ||
| console.log('data:', err.data); | ||
| console.log('message:', err.message); | ||
| } | ||
| }; | ||
|
|
||
| const handleSignUp = async (e) => { | ||
| e.preventDefault(); | ||
|
|
||
| // api 자리 | ||
| // 도중에 요청 시 전 요청 취소 | ||
| abortRef.current?.abort(); | ||
| abortRef.current = new AbortController(); | ||
|
|
||
| try { | ||
| await signUp( | ||
| { | ||
| nickname, | ||
| email, | ||
| password, | ||
| phoneNumber, | ||
| }, | ||
| abortRef.current.signal | ||
| ); | ||
|
|
||
| // localStorage.setItem('authToken', 'dummy-token-12345'); | ||
| nav('/login'); // 회원가입 성공 시 로그인 페이지 이동 | ||
| alert('회원가입이 완료되었습니다.'); | ||
| nav('/login'); | ||
| } catch (err) { | ||
| console.log('status:', err.status); | ||
| console.log('data:', err.data); | ||
| console.log('message:', err.message); | ||
| } |
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.
인증 상태를 성공 이후에만 true로 설정해야 합니다.
setVerificationSent(true)와 setVerificationChecked(true)가 API 호출 전에 실행되고 실패 시에도 복구되지 않아, 서버가 인증을 거부해도 두 플래그가 true로 유지됩니다. 그 결과 실제 인증 없이도 isFormValid가 통과되어 회원가입이 진행되는 치명적인 논리 오류가 발생합니다. 성공한 뒤에만 true로 설정하고, 실패 시에는 false로 되돌리며 새 인증번호를 보낼 때 확인 상태를 초기화해주세요.
다음 수정안을 참고해주세요:
const handleSendVerificationNumber = async (e) => {
e.preventDefault();
- setVerificationSent(true);
-
- // 도중에 요청 시 전 요청 취소
+ setVerificationSent(false);
+ setVerificationChecked(false);
+ // 도중에 요청 시 전 요청 취소
abortRef.current?.abort();
abortRef.current = new AbortController();
// 인증번호 발송 로직 & api 자리
try {
await sendVerificationNumber({ email: email }, abortRef.current.signal);
+ setVerificationSent(true);
+ setVerificationChecked(false);
alert('인증번호가 발송되었습니다.');
} catch (err) {
+ setVerificationSent(false);
console.log('status:', err.status);
console.log('data:', err.data);
console.log('message:', err.message);
}
};
const handleCheckVerificationNumber = async () => {
- setVerificationChecked(true);
-
- // 도중에 요청 시 전 요청 취소
+ setVerificationChecked(false);
+ // 도중에 요청 시 전 요청 취소
abortRef.current?.abort();
abortRef.current = new AbortController();
// 인증번호 발송 로직 & api 자리
try {
await checkVerificationNumber(
{ email: email, verificationNumber: verificationNumber },
abortRef.current.signal
);
setVerificationChecked(true);
alert('인증되었습니다.');
} catch (err) {
+ setVerificationChecked(false);
console.log('status:', err.status);
console.log('data:', err.data);
console.log('message:', err.message);
}
};Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In frontend/src/components/signup/SignUpForm.jsx around lines 51 to 115, the
verification flags are set before the API calls which leaves them true on
failure; move setVerificationSent(true) to only run after sendVerificationNumber
resolves, move setVerificationChecked(true) to only run after
checkVerificationNumber resolves, and in each catch ensure the corresponding
flag is set to false; additionally, when sending a new verification number reset
verificationChecked to false so prior successful checks are invalidated when a
new code is requested.
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.
Actionable comments posted: 1
♻️ Duplicate comments (2)
frontend/src/components/signup/SignUpForm.jsx (2)
68-87: 인증 확인 실패 시 상태를 초기화하고 사용자에게 피드백을 제공해야 합니다.
- API 실패 시 상태 복구 누락: 인증번호 확인에 실패해도
isVerificationChecked가 이전 값을 유지하여 실제로는 인증되지 않았는데도isFormValid가 true가 될 수 있습니다.- 사용자 피드백 부재: 에러 발생 시 사용자에게 실패 메시지가 표시되지 않습니다.
다음 수정안을 적용하세요:
const handleCheckVerificationNumber = async () => { + setVerificationChecked(false); + // 도중에 요청 시 전 요청 취소 abortRef.current?.abort(); abortRef.current = new AbortController(); // 인증번호 발송 로직 & api 자리 try { await checkVerificationNumber( { email: email, verificationNumber: verificationNumber }, abortRef.current.signal ); setVerificationChecked(true); alert('인증되었습니다.'); } catch (err) { + setVerificationChecked(false); + if (err.name !== 'AbortError') { + alert('인증번호 확인에 실패했습니다. 다시 시도해주세요.'); + } console.log('status:', err.status); console.log('data:', err.data); console.log('message:', err.message); } };
49-67: 인증 상태를 실패 시 초기화하고 새 인증번호 발송 시 확인 상태를 리셋해야 합니다.이전 리뷰에서 지적된 문제가 부분적으로만 해결되었습니다:
- API 실패 시 상태 복구 누락: 인증번호 발송에 실패해도
isVerificationSent가 이전 값을 유지하여 잘못된 상태가 됩니다.- 새 인증번호 발송 시 확인 상태 미리셋: 새 인증번호를 요청할 때 이전 확인 상태(
isVerificationChecked)를 무효화하지 않아, 이전 인증번호로 확인된 상태가 그대로 유지됩니다.- 사용자 피드백 부재: 에러 발생 시 콘솔 로그만 남고 사용자에게 오류 메시지가 표시되지 않습니다.
다음 수정안을 적용하세요:
const handleSendVerificationNumber = async (e) => { e.preventDefault(); + setVerificationSent(false); + setVerificationChecked(false); // 도중에 요청 시 전 요청 취소 abortRef.current?.abort(); abortRef.current = new AbortController(); // 인증번호 발송 로직 & api 자리 try { await sendVerificationNumber({ email: email }, abortRef.current.signal); setVerificationSent(true); alert('인증번호가 발송되었습니다.'); } catch (err) { + setVerificationSent(false); + if (err.name !== 'AbortError') { + alert('인증번호 발송에 실패했습니다. 다시 시도해주세요.'); + } console.log('status:', err.status); console.log('data:', err.data); console.log('message:', err.message); } };
🧹 Nitpick comments (1)
frontend/src/components/signup/SignUpForm.jsx (1)
58-58: ES6 객체 속성 단축 표기법을 사용하여 코드를 간결하게 만드세요.객체 속성명과 변수명이 동일한 경우 ES6 단축 표기법을 사용하면 코드가 더 간결해집니다.
다음 수정안을 참고하세요:
- await sendVerificationNumber({ email: email }, abortRef.current.signal); + await sendVerificationNumber({ email }, abortRef.current.signal);await checkVerificationNumber( - { email: email, verificationNumber: verificationNumber }, + { email, verificationNumber }, abortRef.current.signal );await signUp( { nickname, email, password, phoneNumber, }, abortRef.current.signal );Also applies to: 75-77, 97-103
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
frontend/src/components/signup/SignUpForm.jsx(3 hunks)frontend/src/utils/axios.js(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- frontend/src/utils/axios.js
🧰 Additional context used
🧬 Code graph analysis (1)
frontend/src/components/signup/SignUpForm.jsx (2)
frontend/src/components/login/LoginForm.jsx (4)
password(17-17)abortRef(37-37)nav(14-14)frontend/src/utils/auth.js (6)
sendVerificationNumber(35-42)sendVerificationNumber(35-42)checkVerificationNumber(43-53)checkVerificationNumber(43-53)signUp(5-18)signUp(5-18)
🔇 Additional comments (3)
frontend/src/components/signup/SignUpForm.jsx (3)
1-10: API 통합을 위한 import 구성이 적절합니다.useRef를 통한 요청 취소 관리와 auth.js의 API 함수들이 올바르게 import되었습니다.
20-23: 인증 상태 관리 구조가 적절합니다.이메일 인증 흐름을 추적하기 위한 상태 변수들이 올바르게 선언되었습니다.
145-145: 이전 리뷰 지적사항이 수정되었습니다.이메일 입력 필드의 type 속성이 올바르게
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.
고생하셨습니다! 코멘트 확인해 주세요. 추가적으로 로그인, 회원가입, 인증 실패 시에 경고창을 띄워주시면 될 것 같습니다. 콘솔에 찍은 메시지를 띄우기만 하면 될 것 같아요!
- 스웨거 보니 비밀번호 정책이 나와있는데 해당 조건도 화면에 띄워주셔야 합니다.
| } catch (err) { | ||
| console.log('status:', err.status); | ||
| console.log('data:', err.data); | ||
| console.log('message:', err.message); |
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.
해당 에러메시지를 alert로 띄우는 건 어떻게 생각하시나요?
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.
로그인 실패 또한 마찬가지입니다
| @@ -32,17 +34,31 @@ const LoginForm = () => { | |||
|
|
|||
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.
22번째 줄 handlePhoneVerificationSuccess 메서드가 인자로 result를 받고 있는데 사용하질 않아서 지워주시면 되겠습니다.
| } | ||
| }; | ||
|
|
||
| return ( |
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.
88번째 줄에 이메일 input 태그에 autoComplete="email" 추가하라고 크롬에서 알려주고 있는데 저도 이거에 대해선 잘 몰라서 한 번 찾아보시면 좋을 것 같습니다
98번째 줄 또한 autoComplete="current-password"을 추가하라고 하네요
| id="phoneNumber" | ||
| value={phoneNumber} | ||
| onChange={(e) => setPhoneNumber(e.target.value)} | ||
| placeholder="ex) 01012345678" |
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.
autoComplete="tel"
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.
199번째 줄과 209번째 줄은 autoComplete="new-password"
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.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
frontend/src/components/signup/SignUpForm.jsx (1)
240-248: 비밀번호 확인 필드에 autoComplete 속성을 추가하세요.일관성과 사용자 경험 향상을 위해 비밀번호 확인 입력 필드에도
autoComplete속성을 추가하는 것이 좋습니다.다음 수정을 적용하세요:
<input type="password" id="confirm-password" value={confirmPassword} onChange={(e) => setConfirmPassword(e.target.value)} placeholder="비밀번호를 한번 더 입력해주세요" + autoComplete="new-password" />
♻️ Duplicate comments (1)
frontend/src/components/signup/SignUpForm.jsx (1)
94-111: 재검증 실패 시 상태를 초기화해야 합니다.사용자가 이미 인증에 성공한 후(
isVerificationChecked = true) 다시 잘못된 인증번호로 재검증을 시도하면, 실패하더라도isVerificationChecked가true로 유지됩니다. 일관성을 위해 실패 시 상태를false로 초기화하는 것이 좋습니다.다음 수정을 적용하세요:
const handleCheckVerificationNumber = async () => { // 도중에 요청 시 전 요청 취소 abortRef.current?.abort(); abortRef.current = new AbortController(); // 인증번호 발송 로직 & api 자리 try { await checkVerificationNumber( { email: email, verificationNumber: verificationNumber }, abortRef.current.signal ); setVerificationChecked(true); alert('인증되었습니다.'); } catch (err) { + setVerificationChecked(false); alert(err.data?.message || '인증에 실패했습니다.'); } };
🧹 Nitpick comments (1)
frontend/src/components/signup/SignUpForm.jsx (1)
113-136: LGTM!회원가입 API 연동이 올바르게 구현되어 있으며, 성공/실패 시 사용자 피드백이 적절히 제공됩니다. 이전 리뷰의 피드백이 반영되었습니다.
선택사항: 사용자가 요청을 취소한 경우(AbortError) 오류 알림을 표시하지 않도록 개선할 수 있습니다:
} catch (err) { + if (err.name === 'AbortError') return; alert(err.data?.message || '회원가입에 실패하였습니다.'); }이는 lines 89, 109의 다른 에러 핸들러에도 동일하게 적용할 수 있습니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
frontend/src/components/LoginAndSignUpForm.module.css(1 hunks)frontend/src/components/login/LoginForm.jsx(6 hunks)frontend/src/components/signup/SignUpForm.jsx(4 hunks)
✅ Files skipped from review due to trivial changes (1)
- frontend/src/components/LoginAndSignUpForm.module.css
🧰 Additional context used
🧬 Code graph analysis (2)
frontend/src/components/signup/SignUpForm.jsx (1)
frontend/src/utils/auth.js (6)
sendVerificationNumber(35-42)sendVerificationNumber(35-42)checkVerificationNumber(43-53)checkVerificationNumber(43-53)signUp(5-18)signUp(5-18)
frontend/src/components/login/LoginForm.jsx (2)
frontend/src/components/signup/SignUpForm.jsx (2)
abortRef(36-36)nav(38-38)frontend/src/utils/auth.js (2)
login(20-33)login(20-33)
🔇 Additional comments (13)
frontend/src/components/login/LoginForm.jsx (4)
1-1: LGTM! 필요한 import가 올바르게 추가되었습니다.useRef와 login 함수 import가 적절하게 추가되어 API 연동과 요청 취소 기능을 지원합니다.
Also applies to: 11-12
22-22: LGTM! 사용하지 않는 매개변수가 제거되었습니다.이전 리뷰 코멘트에서 지적된 사용하지 않는
result매개변수가 올바르게 제거되었습니다.
43-44: LGTM! 중복 요청 방지 로직이 올바르게 구현되었습니다.이전 요청을 취소하고 새 AbortController를 생성하는 패턴이 올바르게 구현되어, 사용자가 빠르게 여러 번 제출할 때 발생할 수 있는 race condition을 방지합니다.
88-88: LGTM! autoComplete 속성이 추가되어 UX가 개선되었습니다.이전 리뷰 코멘트에서 제안된
autoComplete속성이 올바르게 추가되어, 브라우저의 자동 완성 기능을 지원하고 사용자 경험이 개선되었습니다.Also applies to: 99-99
frontend/src/components/signup/SignUpForm.jsx (9)
1-10: LGTM!인증 API 연동을 위한 import 구조가 적절합니다.
useRef를 통한 AbortController 관리와 인증 유틸리티 함수 import가 올바르게 구성되어 있습니다.
12-18: LGTM!비밀번호 정책이 명확하게 정의되어 있으며, 각 규칙에 대한 테스트 함수가 적절합니다. 8~20자, 대소문자, 숫자, 특수문자 요구사항이 일반적인 보안 기준을 충족합니다.
27-36: LGTM!상태 관리 구조가 이메일 인증 플로우와 비밀번호 실시간 검증을 적절히 지원합니다.
AbortControllerref를 통한 요청 취소 관리도 올바르게 구성되어 있습니다.
40-47: LGTM!비밀번호 입력 시 실시간 검증을 수행하여 사용자에게 즉각적인 피드백을 제공하는 구조가 우수합니다.
62-71: LGTM!폼 유효성 검증 로직이 모든 필수 조건(닉네임, 이메일, 인증 완료, 전화번호, 비밀번호 정책 만족, 비밀번호 일치)을 적절히 확인합니다.
passwordValid.every(Boolean)을 사용한 비밀번호 검증 방식도 간결합니다.
164-187: LGTM!이메일 입력 필드와 인증번호 발송 버튼이 적절히 구현되어 있습니다. 이전 리뷰에서 지적된
type="phoneNumber"이슈가type="email"로 수정되었으며, 버튼 상태 관리(전송 중, 재전송)가 사용자 경험을 개선합니다.
188-207: LGTM!인증번호 입력 및 확인 UI가 명확하게 구현되어 있습니다. 인증번호가 발송된 후에만 확인 버튼이 활성화되는 로직이 적절합니다.
209-218: LGTM!전화번호 입력 필드가 적절히 구성되어 있으며, 이전 리뷰에서 요청된
autoComplete="tel"속성이 추가되었습니다.
220-239: LGTM!비밀번호 입력 필드에 실시간 정책 검증 UI가 추가되어 사용자 경험이 크게 향상되었습니다. 이전 리뷰에서 요청된
autoComplete="new-password"속성도 적절히 추가되었습니다.
| const abortRef = useRef(null); | ||
|
|
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.
컴포넌트 언마운트 시 진행 중인 요청을 취소하는 cleanup이 필요합니다.
abortRef를 사용하여 요청 취소를 관리하고 있지만, 컴포넌트가 언마운트될 때 진행 중인 요청을 취소하는 로직이 없습니다. 이는 메모리 누수나 "unmounted component에서 state 업데이트" 경고를 유발할 수 있습니다.
다음 useEffect를 추가하여 cleanup을 구현하세요:
const abortRef = useRef(null);
+
+useEffect(() => {
+ return () => {
+ abortRef.current?.abort();
+ };
+}, []);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const abortRef = useRef(null); | |
| const abortRef = useRef(null); | |
| useEffect(() => { | |
| return () => { | |
| abortRef.current?.abort(); | |
| }; | |
| }, []); | |
🤖 Prompt for AI Agents
In frontend/src/components/login/LoginForm.jsx around lines 37 to 38, you must
add a useEffect cleanup that aborts any in-flight request via abortRef: on mount
return a cleanup function that checks if abortRef.current is an AbortController
and calls abort(), then clears abortRef.current; ensure any place creating a new
AbortController assigns it to abortRef.current so the cleanup can cancel ongoing
requests and prevent state updates after unmount.
| alert( | ||
| err.data?.message || | ||
| '로그인에 실패하였습니다. 이메일과 비밀번호를 확인해주세요.' | ||
| ); |
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.
Axios 에러 구조가 잘못되어 서버 에러 메시지가 표시되지 않습니다.
Axios에서 발생한 에러의 응답 데이터는 err.response.data에 있지만, 현재 코드는 err.data?.message에 접근하고 있습니다. 이로 인해 서버에서 전달한 에러 메시지가 표시되지 않고 항상 기본 메시지만 표시됩니다.
다음과 같이 수정하세요:
} catch (err) {
alert(
- err.data?.message ||
+ err.response?.data?.message ||
'로그인에 실패하였습니다. 이메일과 비밀번호를 확인해주세요.'
);
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| alert( | |
| err.data?.message || | |
| '로그인에 실패하였습니다. 이메일과 비밀번호를 확인해주세요.' | |
| ); | |
| alert( | |
| err.response?.data?.message || | |
| '로그인에 실패하였습니다. 이메일과 비밀번호를 확인해주세요.' | |
| ); |
🤖 Prompt for AI Agents
In frontend/src/components/login/LoginForm.jsx around lines 57 to 60, the error
handling accesses err.data?.message which is incorrect for Axios errors; change
it to read the server message from err.response?.data?.message (with optional
chaining) and fall back to the existing default string — i.e., use
err.response?.data?.message || '로그인에 실패하였습니다. 이메일과 비밀번호를 확인해주세요.' so the
server-provided message is shown when available.
| const handleSendVerificationNumber = async (e) => { | ||
| e.preventDefault(); | ||
|
|
||
| // 도중에 요청 시 전 요청 취소 | ||
| abortRef.current?.abort(); | ||
| abortRef.current = new AbortController(); | ||
|
|
||
| setIsSending(true); | ||
|
|
||
| // 인증번호 발송 로직 & api 자리 | ||
| try { | ||
| await sendVerificationNumber({ email: email }, abortRef.current.signal); | ||
|
|
||
| setVerificationSent(true); | ||
| alert('인증번호가 발송되었습니다.'); | ||
| } catch (err) { | ||
| alert(err.data?.message || '전송 오류가 발생했습니다.'); | ||
| } finally { | ||
| setIsSending(false); | ||
| } | ||
| }; |
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.
재전송 시 인증 확인 상태를 초기화하지 않는 보안 취약점이 있습니다.
현재 코드에서 사용자가 인증번호를 재전송할 때 isVerificationChecked 상태를 초기화하지 않아 다음과 같은 보안 문제가 발생합니다:
- 사용자가 인증번호를 받아 성공적으로 인증 (→
isVerificationChecked = true) - 사용자가 새 인증번호 재전송 요청
- 새 인증번호를 확인하지 않았는데도
isVerificationChecked가 여전히true로 유지됨 - 폼 제출이 가능해져 실제로는 새 코드를 검증하지 않은 채 회원가입 진행 가능
이전 리뷰에서도 지적된 사항이나 아직 수정되지 않았습니다.
다음 수정을 적용하세요:
const handleSendVerificationNumber = async (e) => {
e.preventDefault();
+ // 재전송 시 이전 인증 상태 초기화
+ setVerificationChecked(false);
+
// 도중에 요청 시 전 요청 취소
abortRef.current?.abort();
abortRef.current = new AbortController();
setIsSending(true);
// 인증번호 발송 로직 & api 자리
try {
await sendVerificationNumber({ email: email }, abortRef.current.signal);
setVerificationSent(true);
alert('인증번호가 발송되었습니다.');
} catch (err) {
alert(err.data?.message || '전송 오류가 발생했습니다.');
} finally {
setIsSending(false);
}
};🤖 Prompt for AI Agents
In frontend/src/components/signup/SignUpForm.jsx around lines 73 to 93, the
resend verification flow doesn't reset the verification-checked state, allowing
a previously successful verification to remain true after requesting a new code;
update the handler to explicitly reset verification state before sending (e.g.,
setIsVerificationChecked(false) and optionally clear the verification code
input/state such as setVerificationCode('')), then proceed with aborting
previous request and sending the new verification number so the user must verify
the newly issued code.
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.
Actionable comments posted: 3
♻️ Duplicate comments (4)
frontend/src/components/login/LoginForm.jsx (2)
37-38: 컴포넌트 언마운트 시 cleanup이 여전히 누락되었습니다.
abortRef를 통해 요청 취소를 관리하고 있지만, 컴포넌트가 언마운트될 때 진행 중인 요청을 취소하는 로직이 없습니다. 이는 메모리 누수나 언마운트된 컴포넌트에서 상태 업데이트 경고를 유발할 수 있습니다.다음과 같이 cleanup useEffect를 추가하세요:
const abortRef = useRef(null); + +useEffect(() => { + return () => { + abortRef.current?.abort(); + }; +}, []);
56-62: Axios 에러 구조가 여전히 잘못되어 서버 메시지가 표시되지 않습니다.Axios 에러의 응답 데이터는
err.response.data에 있지만, 현재 코드는err.data?.errorMessage에 접근하고 있습니다. 이로 인해 서버에서 전달한 에러 메시지가 표시되지 않습니다.다음과 같이 수정하세요:
} catch (err) { console.dir(err); alert( - err.data?.errorMessage || + err.response?.data?.message || '로그인에 실패하였습니다. 이메일과 비밀번호를 확인해주세요.' ); }frontend/src/components/signup/SignUpForm.jsx (2)
73-93: 재전송 시 인증 확인 상태를 초기화하지 않는 심각한 보안 취약점이 여전히 존재합니다.사용자가 인증번호를 재전송할 때
isVerificationChecked상태를 초기화하지 않아 다음과 같은 보안 문제가 발생합니다:
- 사용자가 인증번호를 받아 성공적으로 인증 완료 (→
isVerificationChecked = true)- 사용자가 새 인증번호 재전송 요청
- 새 인증번호를 확인하지 않았는데도
isVerificationChecked가 여전히true로 유지됨- 회원가입 버튼이 활성화되어 실제로는 새 코드를 검증하지 않은 채 가입 진행 가능
이전 리뷰에서 여러 차례 지적되었으나 아직 수정되지 않았습니다.
다음 수정을 적용하세요:
const handleSendVerificationNumber = async (e) => { e.preventDefault(); + // 재전송 시 이전 인증 상태 초기화 + setVerificationChecked(false); + // 도중에 요청 시 전 요청 취소 abortRef.current?.abort(); abortRef.current = new AbortController(); setIsSending(true); // 인증번호 발송 로직 & api 자리 try { await sendVerificationNumber({ email: email }, abortRef.current.signal); setVerificationSent(true); alert('인증번호가 발송되었습니다.'); } catch (err) { alert(err.data?.errorMessage || '전송 오류가 발생했습니다.'); } finally { setIsSending(false); } };
94-111: 인증 실패 시 상태를 명시적으로 초기화하세요.인증 시도 전에
setVerificationChecked(false)를 호출하여 실패 시에도 상태가 일관되게 유지되도록 하는 것이 좋습니다. 현재는 실패 시 상태 변화가 없어 이전 성공 상태가 그대로 유지될 수 있습니다.참고로, 이 함수의 에러 처리(
err.response?.data?.message)는 올바르지만, 파일 내 다른 함수들(handleSendVerificationNumber,handleSignUp)과 일관성이 없습니다.다음과 같이 수정하세요:
const handleCheckVerificationNumber = async () => { + setVerificationChecked(false); + // 도중에 요청 시 전 요청 취소 abortRef.current?.abort(); abortRef.current = new AbortController(); // 인증번호 발송 로직 & api 자리 try { await checkVerificationNumber( { email: email, verificationNumber: verificationNumber }, abortRef.current.signal ); setVerificationChecked(true); alert('인증되었습니다.'); } catch (err) { alert(err.response?.data?.message || '인증에 실패했습니다.'); } };
🧹 Nitpick comments (1)
frontend/src/components/signup/SignUpForm.jsx (1)
240-248: 비밀번호 확인 필드에도 autoComplete 속성을 추가하세요.비밀번호 필드에는
autoComplete="new-password"가 추가되었지만, 비밀번호 확인 필드에는 누락되었습니다. 일관성과 브라우저 자동완성 기능을 위해 추가하는 것이 좋습니다.다음과 같이 수정하세요:
<input type="password" id="confirm-password" value={confirmPassword} onChange={(e) => setConfirmPassword(e.target.value)} placeholder="비밀번호를 한번 더 입력해주세요" + autoComplete="new-password" />
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
frontend/src/components/login/LoginForm.jsx(6 hunks)frontend/src/components/signup/SignUpForm.jsx(4 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
frontend/src/components/signup/SignUpForm.jsx (1)
frontend/src/utils/auth.js (6)
sendVerificationNumber(35-42)sendVerificationNumber(35-42)checkVerificationNumber(43-53)checkVerificationNumber(43-53)signUp(5-18)signUp(5-18)
frontend/src/components/login/LoginForm.jsx (2)
frontend/src/components/signup/SignUpForm.jsx (1)
abortRef(36-36)frontend/src/utils/auth.js (2)
login(20-33)login(20-33)
🔇 Additional comments (4)
frontend/src/components/login/LoginForm.jsx (1)
89-89: autoComplete 속성이 올바르게 추가되었습니다.브라우저 자동완성 기능을 위한
autoComplete속성이 이메일과 비밀번호 입력 필드에 적절하게 추가되어 사용자 경험이 개선되었습니다.Also applies to: 100-100
frontend/src/components/signup/SignUpForm.jsx (3)
12-18: 비밀번호 정책이 명확하게 정의되었습니다.비밀번호 규칙이 테스트 함수와 함께 명확하게 정의되어 있어 유지보수성과 확장성이 우수합니다.
40-47: 실시간 비밀번호 검증이 잘 구현되었습니다.비밀번호 정책을 실시간으로 검증하고 UI에 반영하는 로직이 명확하게 구현되어 있습니다.
isFormValid가 모든 검증 조건을 포함하여 폼 제출을 적절히 제어하고 있습니다.Also applies to: 62-71
229-238: 비밀번호 정책 UI가 효과적으로 구현되었습니다.비밀번호 정책을 시각적으로 표시하고 각 조건의 충족 여부를 실시간으로 보여주어 사용자 경험이 크게 개선되었습니다.
| setVerificationSent(true); | ||
| alert('인증번호가 발송되었습니다.'); | ||
| } catch (err) { | ||
| alert(err.data?.errorMessage || '전송 오류가 발생했습니다.'); |
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.
Axios 에러 구조가 잘못되었습니다.
err.data?.errorMessage는 Axios 에러 구조가 아닙니다. 서버 응답은 err.response?.data에 있습니다.
다음과 같이 수정하세요:
} catch (err) {
- alert(err.data?.errorMessage || '전송 오류가 발생했습니다.');
+ alert(err.response?.data?.message || '전송 오류가 발생했습니다.');
} finally {🤖 Prompt for AI Agents
In frontend/src/components/signup/SignUpForm.jsx around line 89, the alert uses
a non-Axios error path (err.data?.errorMessage); change it to read the server
message from err.response?.data (e.g., err.response?.data?.errorMessage) and
fall back to the default message, so capture the error message from Axios
response properly before alerting.
| const handleSignUp = async (e) => { | ||
| e.preventDefault(); | ||
|
|
||
| // api 자리 | ||
| // 도중에 요청 시 전 요청 취소 | ||
| abortRef.current?.abort(); | ||
| abortRef.current = new AbortController(); | ||
|
|
||
| try { | ||
| await signUp( | ||
| { | ||
| nickname, | ||
| email, | ||
| password, | ||
| phoneNumber, | ||
| }, | ||
| abortRef.current.signal | ||
| ); | ||
|
|
||
| // localStorage.setItem('authToken', 'dummy-token-12345'); | ||
| nav('/login'); // 회원가입 성공 시 로그인 페이지 이동 | ||
| alert('회원가입이 완료되었습니다.'); | ||
| nav('/login'); | ||
| } catch (err) { | ||
| alert(err.data?.errorMessage || '회원가입에 실패하였습니다.'); | ||
| } | ||
| }; |
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.
컴포넌트 언마운트 시 cleanup이 누락되었습니다.
abortRef를 사용하여 요청 취소를 관리하고 있지만, 컴포넌트가 언마운트될 때 진행 중인 요청을 취소하는 로직이 없습니다.
다음과 같이 cleanup useEffect를 추가하세요:
const abortRef = useRef(null);
+
+useEffect(() => {
+ return () => {
+ abortRef.current?.abort();
+ };
+}, []);
const nav = useNavigate();Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In frontend/src/components/signup/SignUpForm.jsx around lines 113 to 136, the
component uses abortRef to cancel in-flight signup requests but lacks a cleanup
on unmount; add a useEffect that returns a cleanup function which calls
abortRef.current?.abort() (and optionally sets abortRef.current = null) to
ensure any pending request is aborted when the component unmounts; place this
useEffect inside the component so it runs once on mount/unmount (empty deps).
| alert('회원가입이 완료되었습니다.'); | ||
| nav('/login'); | ||
| } catch (err) { | ||
| alert(err.data?.errorMessage || '회원가입에 실패하였습니다.'); |
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.
Axios 에러 구조가 잘못되었습니다.
err.data?.errorMessage는 Axios 에러 구조가 아닙니다. 서버 응답은 err.response?.data에 있습니다.
다음과 같이 수정하세요:
} catch (err) {
- alert(err.data?.errorMessage || '회원가입에 실패하였습니다.');
+ alert(err.response?.data?.message || '회원가입에 실패하였습니다.');
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| alert(err.data?.errorMessage || '회원가입에 실패하였습니다.'); | |
| } catch (err) { | |
| alert(err.response?.data?.message || '회원가입에 실패하였습니다.'); | |
| } |
🤖 Prompt for AI Agents
In frontend/src/components/signup/SignUpForm.jsx around line 134, the error
handling uses err.data?.errorMessage which is not the Axios error shape; change
it to read the server message from err.response?.data?.errorMessage (with a safe
optional chaining fallback to a generic message) so the alert shows the real
server error when available.
SISC1-195 로그인, 회원가입, 이메일 인증번호 전송 및 이메일 번호 인증 api 연동
무엇을 변경했는지:
왜 변경했는지(문제/목표): 디자인이 변경되었으므로 수정
3) 스크린샷/동영상 (UI 변경 시)
전/후 비교, 반응형(모바일/데스크톱) 캡쳐
Before:
After:
상세 변경사항 (전부 다)
라우팅/페이지:
컴포넌트: LoginForm,jsx, SignUpForm.jsx
상태관리:
API 호출:
스타일:
기타:
참고사항
Summary by CodeRabbit