Skip to content

Conversation

@NaturalSoda4552
Copy link
Contributor

@NaturalSoda4552 NaturalSoda4552 commented Nov 9, 2025

  1. 작업한 이슈번호

SISC1-195 로그인, 회원가입, 이메일 인증번호 전송 및 이메일 번호 인증 api 연동

  1. 변경 요약 (What & Why)
    무엇을 변경했는지:
  • 로그인 및 회원가입 api 연동
  • 회원가입 페이지에서 사용하는 이메일 인증번호 전송 api와 인증번호 인증 api 연동

왜 변경했는지(문제/목표): 디자인이 변경되었으므로 수정
3) 스크린샷/동영상 (UI 변경 시)
전/후 비교, 반응형(모바일/데스크톱) 캡쳐
Before:
After:

  1. 상세 변경사항 (전부 다)
    라우팅/페이지:
    컴포넌트: LoginForm,jsx, SignUpForm.jsx
    상태관리:
    API 호출:
    스타일:
    기타:

  2. 참고사항

  • 전화번호가 아닌 이메일 인증 방식으로 변경

Summary by CodeRabbit

  • 새로운 기능
    • 회원가입 시 이메일 인증(인증번호 전송/확인) 추가
    • 실사용 로그인/회원가입 API 연동 및 인증 흐름 구현
    • 실시간 비밀번호 정책 확인 UI 및 관련 스타일 추가
  • 개선사항
    • 중복 요청 취소(Abort)로 흐름 안정성 강화
    • 자동 토큰 갱신 및 인증 실패 시 재시도/로그아웃 처리
    • 입력 자동완성(email/password) 및 사용자 안내(alert) 개선

@coderabbitai
Copy link

coderabbitai bot commented Nov 9, 2025

Walkthrough

프런트엔드 인증 흐름을 API 기반으로 전환했습니다. 중앙화된 Axios 인스턴스(api)와 재발급 로직을 추가하고, auth 유틸에 login, signUp, sendVerificationNumber, checkVerificationNumber를 도입했으며 Login/SignUp 컴포넌트에 AbortController 기반 요청 취소와 비동기 API 호출, 비밀번호 정책 UI를 추가했습니다.

Changes

Cohort / File(s) 변경 요약
인증 유틸리티
frontend/src/utils/auth.js
새 유틸 추가: login, signUp, sendVerificationNumber, checkVerificationNumber — 각 함수가 AbortSignal을 받아 API 호출을 수행하고, login은 응답 토큰을 localStorage에 저장
Axios 중앙 관리
frontend/src/utils/axios.js
새 파일: api Axios 인스턴스 추가(기본 URL, withCredentials). 요청 인터셉터로 Authorization 헤더 주입, 응답 인터셉터에서 401 발생 시 /api/auth/reissue로 토큰 재발급 시도 후 원래 요청 재시도 또는 실패 시 토큰 제거 및 /login 리다이렉트 처리
로그인 컴포넌트
frontend/src/components/login/LoginForm.jsx
동기 더미 흐름 제거. useRef 기반 abortRef 도입, handleLogin을 async로 변환해 기존 요청을 취소하고 auth.login 호출. 성공 시 네비게이션, 실패 시 서버 메시지 또는 일반 알림 표시. 입력에 autoComplete 속성 추가
회원가입 컴포넌트
frontend/src/components/signup/SignUpForm.jsx
이메일 인증(발송/검증) API 통합: sendVerificationNumber, checkVerificationNumber, signUp 사용. abortRef로 요청 취소, 비밀번호 정책 실시간 검증 UI 추가, 폼 유효성에 isVerificationSent/isVerificationChecked 포함
스타일
frontend/src/components/LoginAndSignUpForm.module.css
비밀번호 정책 표시용 스타일(.passwordPolicy 등) 추가 — UI 전용 변경

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: 홈으로 네비게이션
Loading
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: 로그인 페이지로 네비게이션
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 분

검토 시 주의할 항목:

  • frontend/src/utils/axios.js의 401 재발급/재시도 로직(무한 루프, 동시 재발급 대비)
  • AbortController/abortRef 사용 시 동시 요청 취소와 참조 정리
  • 토큰(localStorage) 저장·갱신 시점과 Authorization 헤더 동기화
  • SignUpForm의 비밀번호 정책 검사 및 인증 상태 전이 로직

Possibly related PRs

Suggested reviewers

  • gxuoo
  • DongEun02

Poem

🐰 새벽밭에 코드 당근 심었네,
요청은 취소하고 토큰은 보관했네.
이메일로 인증 꽃 피우고, 가입 길을 닦아,
버튼 누르면 집으로 가는 당근 지도! ✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목은 변경사항의 핵심을 명확하게 반영하고 있으며, 로그인, 회원가입, 이메일 인증 API 연동이라는 주요 변경 사항을 정확하게 요약하고 있습니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch SISC1-195-login-signup-verification-api-connect

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a 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

📥 Commits

Reviewing files that changed from the base of the PR and between 402df0d and 3d6f67f.

📒 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)
  • email (16-16)
  • 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)
  • email (15-15)
  • password (17-17)
  • phoneNumber (16-16)
  • verificationNumber (14-14)
frontend/src/components/login/LoginForm.jsx (2)
  • email (16-16)
  • 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)

Comment on lines 51 to 115
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);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

인증 상태를 성공 이후에만 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.

Copy link

@coderabbitai coderabbitai bot left a 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: 인증 확인 실패 시 상태를 초기화하고 사용자에게 피드백을 제공해야 합니다.

  1. API 실패 시 상태 복구 누락: 인증번호 확인에 실패해도 isVerificationChecked가 이전 값을 유지하여 실제로는 인증되지 않았는데도 isFormValid가 true가 될 수 있습니다.
  2. 사용자 피드백 부재: 에러 발생 시 사용자에게 실패 메시지가 표시되지 않습니다.

다음 수정안을 적용하세요:

   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: 인증 상태를 실패 시 초기화하고 새 인증번호 발송 시 확인 상태를 리셋해야 합니다.

이전 리뷰에서 지적된 문제가 부분적으로만 해결되었습니다:

  1. API 실패 시 상태 복구 누락: 인증번호 발송에 실패해도 isVerificationSent가 이전 값을 유지하여 잘못된 상태가 됩니다.
  2. 새 인증번호 발송 시 확인 상태 미리셋: 새 인증번호를 요청할 때 이전 확인 상태(isVerificationChecked)를 무효화하지 않아, 이전 인증번호로 확인된 상태가 그대로 유지됩니다.
  3. 사용자 피드백 부재: 에러 발생 시 콘솔 로그만 남고 사용자에게 오류 메시지가 표시되지 않습니다.

다음 수정안을 적용하세요:

   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

📥 Commits

Reviewing files that changed from the base of the PR and between 3d6f67f and 0c5c31e.

📒 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)
  • email (16-16)
  • 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 속성이 올바르게 email로 설정되어 네이티브 이메일 검증과 모바일 키보드 최적화가 적용됩니다.

Copy link
Contributor

@DongEun02 DongEun02 left a 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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

해당 에러메시지를 alert로 띄우는 건 어떻게 생각하시나요?

Copy link
Contributor

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 = () => {

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

22번째 줄 handlePhoneVerificationSuccess 메서드가 인자로 result를 받고 있는데 사용하질 않아서 지워주시면 되겠습니다.

}
};

return (
Copy link
Contributor

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"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

autoComplete="tel"

Copy link
Contributor

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"

Copy link

@coderabbitai coderabbitai bot left a 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) 다시 잘못된 인증번호로 재검증을 시도하면, 실패하더라도 isVerificationCheckedtrue로 유지됩니다. 일관성을 위해 실패 시 상태를 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

📥 Commits

Reviewing files that changed from the base of the PR and between 0c5c31e and f33a28b.

📒 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!

상태 관리 구조가 이메일 인증 플로우와 비밀번호 실시간 검증을 적절히 지원합니다. AbortController ref를 통한 요청 취소 관리도 올바르게 구성되어 있습니다.


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" 속성도 적절히 추가되었습니다.

Comment on lines +37 to +38
const abortRef = useRef(null);

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

컴포넌트 언마운트 시 진행 중인 요청을 취소하는 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.

Suggested change
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.

Comment on lines 57 to 60
alert(
err.data?.message ||
'로그인에 실패하였습니다. 이메일과 비밀번호를 확인해주세요.'
);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

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.

Suggested change
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.

Comment on lines 73 to 93
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);
}
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

재전송 시 인증 확인 상태를 초기화하지 않는 보안 취약점이 있습니다.

현재 코드에서 사용자가 인증번호를 재전송할 때 isVerificationChecked 상태를 초기화하지 않아 다음과 같은 보안 문제가 발생합니다:

  1. 사용자가 인증번호를 받아 성공적으로 인증 (→ isVerificationChecked = true)
  2. 사용자가 새 인증번호 재전송 요청
  3. 새 인증번호를 확인하지 않았는데도 isVerificationChecked가 여전히 true로 유지됨
  4. 폼 제출이 가능해져 실제로는 새 코드를 검증하지 않은 채 회원가입 진행 가능

이전 리뷰에서도 지적된 사항이나 아직 수정되지 않았습니다.

다음 수정을 적용하세요:

   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.

Copy link

@coderabbitai coderabbitai bot left a 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 상태를 초기화하지 않아 다음과 같은 보안 문제가 발생합니다:

  1. 사용자가 인증번호를 받아 성공적으로 인증 완료 (→ isVerificationChecked = true)
  2. 사용자가 새 인증번호 재전송 요청
  3. 새 인증번호를 확인하지 않았는데도 isVerificationChecked가 여전히 true로 유지됨
  4. 회원가입 버튼이 활성화되어 실제로는 새 코드를 검증하지 않은 채 가입 진행 가능

이전 리뷰에서 여러 차례 지적되었으나 아직 수정되지 않았습니다.

다음 수정을 적용하세요:

 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

📥 Commits

Reviewing files that changed from the base of the PR and between f33a28b and 7c3f25c.

📒 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 || '전송 오류가 발생했습니다.');
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

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.

Comment on lines +113 to 136
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 || '회원가입에 실패하였습니다.');
}
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

컴포넌트 언마운트 시 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 || '회원가입에 실패하였습니다.');
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

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.

Suggested change
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.

@DongEun02 DongEun02 merged commit 330a379 into main Nov 12, 2025
1 check passed
@DongEun02 DongEun02 deleted the SISC1-195-login-signup-verification-api-connect branch November 12, 2025 11:34
@coderabbitai coderabbitai bot mentioned this pull request Jan 29, 2026
22 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants