Skip to content

[FEAT] 관리자 Oauth 로그인 관련 Redirect 추가#166

Merged
aaaaaattt merged 24 commits intodevelopfrom
feat/oauth-login
Oct 15, 2025
Merged

[FEAT] 관리자 Oauth 로그인 관련 Redirect 추가#166
aaaaaattt merged 24 commits intodevelopfrom
feat/oauth-login

Conversation

@aaaaaattt
Copy link
Collaborator

@aaaaaattt aaaaaattt commented Oct 14, 2025

#️⃣연관된 이슈

📝작업 내용

  • Oauth 로그인을 통해 DB에 등록되지 않은 사용자를 위한 관리자 등록 페이지
  • kakao Login 이후 인가코드를 백엔드에 전송하기 위한 임시 대기 페이지(KakaoCallback)
  • 각각의 route 추가
  • 관리자 회원가입 api추가
  • 관리자 페이지의 component 분리
  • 관리자 로그인 페이지 아이디, 비밀번호 입력필드 제거

이번 PR에서 작업한 내용을 간략히 설명해주세요(이미지 첨부 가능)

스크린샷 (선택)

  • 관리자 페이지
스크린샷 2025-10-14 오후 4 30 32

💬리뷰 요구사항(선택)

리뷰어가 특별히 봐주었으면 하는 부분이 있다면 작성해주세요

  • Lgoin 과정에서 백엔드와 주고받는 token관련 명세와 이후의 개략적인 흐름을 오늘 전달받게 되었는데 작업된 내용이랑 별개의 branch로 PR을 올려야 할 것 같습니다.
  • (추가) commit을 해도 등록된 husky가 작동을 하지 않는 것 같습니다! 한번 확인 바랍니다.
  • 현재 node 서버를 임시로 로컬에서 띄워서 테스트 과정 도중에 완료된 부분 위주로 PR을 올려서, 주석 처리된 부분에 이후작업을 간단히 명시해둔 부분이 있습니다.
  • 이후에 token 검증 절차에서 인터셉터가 활용될 것 같아서 로그인 부분에서는 fetch가 아닌 axios가 활용될 것 같고, 전반적으로 header에 accessToken을 주고 받기 위해서 fetch -> axios로 리팩토링 과정이 필요할 것 같습니다.

Summary by CodeRabbit

  • New Features

    • Kakao social login flow with in-app callback and loading state.
    • Admin signup page with responsive layout, client-side validation, API submission, inline errors, toast feedback, and automatic post-success navigation.
    • Signup button added to the login screen and new public routes for signup and auth callback.
  • Bug Fixes / UI Changes

    • Removed the traditional login input from the admin login page.

@aaaaaattt aaaaaattt self-assigned this Oct 14, 2025
@aaaaaattt aaaaaattt added 🛠️ Refactor 리팩터링 이슈 ✨ Feature 기능 구현 이슈 🌈 frontend 프론트엔드 이슈 labels Oct 14, 2025
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @aaaaaattt, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

이 PR은 관리자 OAuth 로그인 및 등록 기능을 도입합니다. 카카오 로그인 리다이렉트를 처리하고, 데이터베이스에 등록되지 않은 관리자를 위한 전용 회원가입 페이지를 제공하는 새로운 라우트를 설정합니다. 이러한 변경 사항은 관리자 로그인 프로세스를 간소화하고 새로운 관리자를 위한 등록 경로를 제공하는 것을 목표로 합니다.

Highlights

  • 새로운 라우트 추가: 관리자 OAuth 로그인 리다이렉트 및 관리자 회원가입을 위한 새로운 라우트가 추가되었습니다.
  • 카카오 로그인 콜백 페이지 구현: 카카오 OAuth 인가 코드를 처리하고 백엔드로 전송하기 위한 임시 대기 페이지(KakaoCallback)가 구현되었습니다.
  • 관리자 회원가입 페이지 개발: 데이터베이스에 등록되지 않은 사용자를 위한 관리자 회원가입 페이지가 추가되었으며, 관련 컴포넌트가 분리되었습니다.
  • 카카오 로그인 연동: 로그인 버튼 컴포넌트에 카카오 OAuth 로그인 기능이 통합되어, 환경 변수를 통해 API 키와 리다이렉트 URI를 사용합니다.
  • 관리자 회원가입 API 추가: 관리자 사용자 등록을 위한 백엔드 API 호출 로직이 추가되었습니다.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@coderabbitai
Copy link

coderabbitai bot commented Oct 14, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Added Kakao OAuth initiation and callback handling, introduced an admin signup page with form, styles, types, and API helper, and updated routes and route constants to include callback and signup keys.

Changes

Cohort / File(s) Summary
Routing constants & routes
src/constants/routerPath.ts, src/pages/Routes.tsx
Added ROUTE_PATH.COMMON.CALLBACK = 'login/redirect' and ROUTE_PATH.COMMON.SIGNUP = 'signup'; registered routes mapping COMMON.CALLBACKKakaoCallback and COMMON.SIGNUPAdminSignupPage.
Kakao login flow
src/pages/admin/Login/component/LoginButton/index.tsx, src/pages/admin/Login/KakaoCallback.tsx
LoginButton builds Kakao OAuth URL (uses env REST_API_KEY, REDIRECT_URI) and redirects; KakaoCallback extracts code from URL, POSTs { authorizationCode } to backend /auth/kakao/login, renders LoadingSpinner, and logs responses (token/navigation handling left as placeholders).
Admin signup page & form
src/pages/admin/Signup/Page.tsx, src/pages/admin/Signup/components/HeaderTitle/index.tsx, src/pages/admin/Signup/components/SignupForm/index.tsx, src/pages/admin/Signup/components/SignupForm/index.styled.ts, src/pages/admin/Signup/type/signup.ts, src/pages/admin/Signup/api/signup.ts
Added AdminSignupPage and Layout; HeaderTitle; SignupForm using react-hook-form with validation (name, email, studentId, department, phoneNumber); presentational styled components; SignupFormInputs type; postSignupForm POST helper to ${VITE_API_BASE_URL}/auth/register.
Login page minor change
src/pages/admin/Login/Page.tsx
Removed LoginInput import and its JSX; page now renders Logo and LoginButton only.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant LoginBtn as LoginButton
  participant KakaoAuth as Kakao OAuth
  participant Callback as KakaoCallback
  participant Backend as Backend (/auth/kakao/login)

  User->>LoginBtn: Click "카카오 로그인"
  LoginBtn->>KakaoAuth: Redirect (client_id, redirect_uri, response_type=code)
  KakaoAuth-->>User: Authenticate & consent
  KakaoAuth->>Callback: Redirect to /login/redirect?code=...
  Callback->>Callback: Extract authorizationCode
  Callback->>Backend: POST { authorizationCode }
  Backend-->>Callback: Response (user status / tokens)
  Callback-->>User: Show LoadingSpinner (token/navigation handling placeholder)
Loading
sequenceDiagram
  autonumber
  actor User
  participant SignupPage as AdminSignupPage
  participant Form as SignupForm
  participant API as Backend (/auth/register)

  User->>SignupPage: Open /signup
  SignupPage->>Form: Render form
  User->>Form: Fill fields & Submit
  Form->>API: POST signup JSON
  API-->>Form: 200 OK / Error
  alt Success
    Form-->>User: Show success toast
    Form->>SignupPage: navigate('/login') after delay
  else Error
    Form-->>User: Show error toast
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

I hop to catch a flying code,
A click, a redirect down the road.
I nibble fields and send them through,
A spinner hums — a signup new.
Little rabbit cheers, “Welcome you!” 🥕

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title clearly indicates that redirect functionality for admin OAuth login is being added, which directly corresponds to the new KakaoCallback component and associated route entries in this PR. It succinctly highlights a key implementation detail without unnecessary noise.
Linked Issues Check ✅ Passed The changes introduce a KakaoCallback component and integrate the Kakao login button flow satisfying issue #131, and they implement a complete administrator signup page with form handling, API helper, types, and routing satisfying issue #165, thereby addressing all linked coding objectives.
Out of Scope Changes Check ✅ Passed All modifications, including new routes, components, API helpers, styled elements, and the removal of the legacy LoginInput component, directly support the OAuth login flow and administrator signup objectives outlined in the linked issues, with no unrelated functionality altered.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ 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 feat/oauth-login

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0f57197 and a4c53cc.

📒 Files selected for processing (1)
  • src/pages/admin/Signup/components/SignupForm/index.tsx (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/pages/admin/Signup/components/SignupForm/index.tsx (5)
src/pages/admin/Signup/type/signup.ts (1)
  • SignupFormInputs (1-7)
src/pages/admin/Signup/api/signup.ts (1)
  • postSignupForm (3-15)
src/styles/theme.ts (1)
  • theme (3-10)
src/shared/components/Form/InputField/OutlineInputField.tsx (1)
  • OutlineInputField (9-20)
src/shared/components/Button/index.tsx (1)
  • Button (17-64)
🔇 Additional comments (3)
src/pages/admin/Signup/components/SignupForm/index.tsx (3)

11-24: LGTM!

The form setup with mode: 'onTouched' provides good UX by validating fields as users interact with them. The default values and state destructuring are properly configured.


48-109: LGTM!

The form fields are properly configured with appropriate validation rules. The use of OutlineInputField with error handling provides good user feedback.


123-123: LGTM!

The submit button correctly shows loading state ('제출중...') while isSubmitting is true, preventing duplicate submissions.


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
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

이번 PR은 관리자 OAuth 로그인 및 회원가입 기능 추가에 대한 내용이네요. 전반적으로 기능 구현의 골격은 잘 잡혀있지만, 몇 가지 개선이 필요한 부분이 보입니다. 특히 KakaoCallback 컴포넌트의 하드코딩된 URL과 에러 처리, SignupForm의 비동기 처리 로직 오류 등은 반드시 수정이 필요합니다. 또한, 코드 일관성과 잠재적 버그를 방지하기 위한 몇 가지 제안 사항들을 포함했습니다. 아래의 상세 리뷰를 확인해주세요.

Comment on lines 27 to 64
const onSubmit = async (signupFormValue: SignupFormInputs) => {
try {
console.log('회원가입 data:', signupFormValue);
postSignupForm(signupFormValue)
.then(() => {
toast.success('제출 성공!', {
style: {
backgroundColor: theme.colors.primary,
color: 'white',
},
duration: 1000,
});
setTimeout(() => {
navigate(`/`);
}, 1000);
})
.catch(() => {
toast.error('제출 실패!', {
duration: 1000,
style: {
backgroundColor: 'white',
color: theme.colors.error,
},
});
});

toast.success('회원가입 완료!', {
style: { backgroundColor: theme.colors.primary, color: 'white' },
duration: 1000,
});

setTimeout(() => {
navigate('/login');
}, 1000);
} catch (error) {
toast.error('회원가입 실패', { duration: 1000 });
}
};
Copy link
Contributor

Choose a reason for hiding this comment

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

critical

onSubmit 함수의 비동기 로직에 심각한 오류가 있습니다.

  1. async 함수 내에서 await 대신 .then()을 사용하여 postSignupForm을 호출하고 있어, API 호출이 완료되기를 기다리지 않고 바로 다음 코드가 실행됩니다.
  2. API 호출 직후 성공 토스트(회원가입 완료!)와 페이지 이동(navigate('/login')) 로직이 중복되어 실행됩니다. 이로 인해 API 호출 결과와 상관없이 항상 성공 메시지가 표시되고 페이지가 이동하는 버그가 발생합니다.

await를 사용하여 비동기 흐름을 명확히 하고, 성공/실패에 따른 처리를 try...catch 블록 안에서 일관되게 관리해야 합니다.

  const onSubmit = async (signupFormValue: SignupFormInputs) => {
    try {
      await postSignupForm(signupFormValue);
      toast.success('회원가입 완료!', {
        style: { backgroundColor: theme.colors.primary, color: 'white' },
        duration: 1000,
      });

      setTimeout(() => {
        navigate('/login');
      }, 1000);
    } catch (error) {
      toast.error('회원가입에 실패했습니다.', {
        duration: 1000,
        style: {
          backgroundColor: 'white',
          color: theme.colors.error,
        },
      });
    }
  };


// 1-1. accessToken, refreshToken 발급
// localStorage.setItem('accessToken', res.data.accessToken);
// localStorage.setItem('refreshToken ', res.data.accessToken);
Copy link
Contributor

Choose a reason for hiding this comment

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

high

주석 처리된 코드이지만, refreshToken을 저장하는 부분에 두 가지 문제가 있습니다.

  1. 'refreshToken ' 키에 불필요한 공백이 포함되어 있습니다.
  2. refreshToken을 저장해야 하는 곳에 res.data.accessToken이 할당되어 있습니다. res.data.refreshToken을 사용해야 합니다.
Suggested change
// localStorage.setItem('refreshToken ', res.data.accessToken);
// localStorage.setItem('refreshToken', res.data.refreshToken);


useEffect(() => {
const code = new URL(window.location.href).searchParams.get('code');
if (!code) return;
Copy link
Contributor

Choose a reason for hiding this comment

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

high

카카오 로그인 시 사용자가 동의를 거부하는 등 에러가 발생하면 error 쿼리 파라미터가 URL에 포함될 수 있습니다. 이 경우에 대한 에러 처리가 누락되었습니다. 사용자에게 에러 상황을 안내하고 로그인 페이지로 돌려보내는 등의 처리가 필요합니다.

console.log(code);
const fetchToken = async () => {
try {
const res = await axios.post('http://localhost:8080/api/auth/kakao/login', {
Copy link
Contributor

Choose a reason for hiding this comment

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

high

API 요청 URL이 하드코딩되어 있습니다. 개발, 스테이징, 프로덕션 등 다양한 환경에 대응하기 어렵고 보안에도 취약할 수 있습니다. .env 파일에 환경 변수로 분리하여 관리하는 것을 권장합니다.

예: const API_URL = import.meta.env.VITE_API_URL;

Suggested change
const res = await axios.post('http://localhost:8080/api/auth/kakao/login', {
const res = await axios.post(`${import.meta.env.VITE_API_BASE_URL}/auth/kakao/login`, {

Comment on lines +33 to +35
} catch (error) {
console.log('error:', error);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

high

API 요청 실패 시 catch 블록에서 에러를 콘솔에만 출력하고 있습니다. 사용자 경험을 위해 에러 토스트 메시지를 띄우거나, 에러 페이지로 이동시키는 등 명시적인 에러 처리를 추가하는 것이 좋습니다.

Comment on lines 18 to 20
<Button to='/signup' width='100%' type='submit'>
{'회원가입'}
</Button>
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

Button 컴포넌트에 to prop을 전달하여 링크로 사용하면서 type='submit'을 함께 사용하고 있습니다. 이 버튼은 폼을 제출하는 용도가 아니므로, 혼동을 피하기 위해 type='submit'을 제거하는 것이 좋습니다.

Suggested change
<Button to='/signup' width='100%' type='submit'>
{'회원가입'}
</Button>
<Button to='/signup' width='100%'>
{'회원가입'}
</Button>

</Button>
<KakaoButtonWrapper>
<Button width='100%' type='submit'>
<Button width='100%' type='submit' onClick={handleKakaoLogin}>
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

카카오 로그인 버튼은 폼 제출(submit)이 아닌 onClick 핸들러를 통해 페이지를 이동시킵니다. 따라서 type'button'으로 명시하거나 제거하여 의도를 명확히 하는 것이 좋습니다. 기본 typesubmit인 경우 의도치 않은 폼 제출이 발생할 수 있습니다.

Suggested change
<Button width='100%' type='submit' onClick={handleKakaoLogin}>
<Button width='100%' type='button' onClick={handleKakaoLogin}>

Comment on lines 26 to 28
[`@media (max-width: ${theme.breakpoints.web})`]: {
padding: '1.5rem',
},
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

미디어 쿼리에서 theme.breakpoints.web 너비 이하일 때의 padding 값이 기본값과 동일하게 1.5rem으로 설정되어 있습니다. 이 미디어 쿼리 블록은 불필요하므로 제거해도 될 것 같습니다.

element: <KakaoCallback />,
},
{
path: COMMON.SINGUP,
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

routerPath.ts 파일에서 SINGUP에 오타가 있습니다. 해당 파일을 수정하면 이 부분도 자동으로 해결되겠지만, 일관성을 위해 함께 확인해주세요. COMMON.SIGNUP으로 사용해야 합니다.


const onSubmit = async (signupFormValue: SignupFormInputs) => {
try {
console.log('회원가입 data:', signupFormValue);
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

개발 중에 사용된 console.log 구문이 남아있습니다. 프로덕션 코드에 포함되지 않도록 제거해주세요.

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: 7

🧹 Nitpick comments (5)
src/pages/admin/Login/component/LoginButton/index.tsx (1)

18-20: Use route constant instead of hardcoded path.

For consistency with the rest of the codebase, use ROUTE_PATH.COMMON.SIGNUP (after fixing the typo) instead of the hardcoded '/signup' path.

After fixing the typo in routerPath.ts, apply this diff:

+import { ROUTE_PATH } from '@/constants/routerPath';
+
 import styled from '@emotion/styled';
 import { Button } from '@/shared/components/Button';

 const REST_API_KEY = import.meta.env.VITE_KAKAO_REST_API_KEY;
 const REDIRECT_URI = import.meta.env.VITE_KAKAO_REDIRECT_URI;

 export const LoginButton = () => {
   const handleKakaoLogin = () => {
     const kakaoAuthUrl = `https://kauth.kakao.com/oauth/authorize?client_id=${REST_API_KEY}&redirect_uri=${REDIRECT_URI}&response_type=code`;
     window.location.href = kakaoAuthUrl;
   };

   return (
     <Container>
       <Button width='100%' type='submit'>
         {'로그인'}
       </Button>
-      <Button to='/signup' width='100%' type='submit'>
+      <Button to={`/${ROUTE_PATH.COMMON.SIGNUP}`} width='100%' type='submit'>
         {'회원가입'}
       </Button>
src/pages/admin/Signup/api/signup.ts (1)

3-15: Consider adding a request timeout.

The fetch call has no timeout, which could cause the signup form to hang indefinitely if the backend is unresponsive.

Consider adding a timeout using AbortController:

 export const postSignupForm = async (formData: SignupFormInputs): Promise<SignupFormInputs> => {
   const url = `${import.meta.env.VITE_API_BASE_URL}/auth/register`;
+  const controller = new AbortController();
+  const timeoutId = setTimeout(() => controller.abort(), 10000); // 10 second timeout
+
+  try {
-  const response = await fetch(url, {
+    const response = await fetch(url, {
-    method: 'POST',
+      method: 'POST',
-    headers: {
+      headers: {
-      'Content-Type': 'application/json',
+        'Content-Type': 'application/json',
-    },
+      },
-    body: JSON.stringify(formData),
+      body: JSON.stringify(formData),
+      signal: controller.signal,
-  });
+    });
+    clearTimeout(timeoutId);

-  if (!response.ok) throw new Error('회원 가입 양식을 제출하지 못했습니다.');
-  return await response.json();
+    if (!response.ok) throw new Error('회원 가입 양식을 제출하지 못했습니다.');
+    return await response.json();
+  } catch (error) {
+    clearTimeout(timeoutId);
+    if (error instanceof Error && error.name === 'AbortError') {
+      throw new Error('요청 시간이 초과되었습니다.');
+    }
+    throw error;
+  }
 };
src/pages/admin/Login/KakaoCallback.tsx (2)

21-32: Remove or implement commented-out logic.

The commented-out sections indicate incomplete implementation. Based on the PR description, token handling will be in a separate PR, but these comments should either be removed or converted to proper TODO comments with issue references.

Apply one of these approaches:

Option 1: Remove if implementation is planned soon

-        // CASE 1) 기존 회원
-
-        // 1-1. accessToken, refreshToken 발급
-        // localStorage.setItem('accessToken', res.data.accessToken);
-        // localStorage.setItem('refreshToken ', res.data.accessToken);
-        // ------------------------------------------------------------
-        // 2-2 main 페이지 이동
-        // navigate('/'); // 로그인 후 홈으로 이동
-
-        // CASE 2) 기존 회원
-        // 2-1. 임시 토큰
-        // 2-2. navigate('/signup')

Option 2: Convert to proper TODO with issue reference

-        // CASE 1) 기존 회원
-        // 1-1. accessToken, refreshToken 발급
-        // localStorage.setItem('accessToken', res.data.accessToken);
-        // localStorage.setItem('refreshToken ', res.data.accessToken);
-        // ------------------------------------------------------------
-        // 2-2 main 페이지 이동
-        // navigate('/'); // 로그인 후 홈으로 이동
-
-        // CASE 2) 기존 회원
-        // 2-1. 임시 토큰
-        // 2-2. navigate('/signup')
+        // TODO(#issue-number): Implement token handling and navigation based on user status
+        // - Existing users: Store access/refresh tokens and navigate to main page
+        // - New users: Store temporary token and navigate to signup page

14-36: Add timeout to the axios request.

Similar to the signup API, the axios call should have a timeout to prevent indefinite hanging.

Apply this diff:

     const fetchToken = async () => {
       try {
-        const res = await axios.post('http://localhost:8080/api/auth/kakao/login', {
+        const res = await axios.post(`${import.meta.env.VITE_API_BASE_URL}/auth/kakao/login`, {
           authorizationCode: code,
-        });
+        }, {
+          timeout: 10000, // 10 second timeout
+        });
src/pages/admin/Signup/components/SignupForm/index.styled.ts (1)

48-52: Remove unused ErrorMessage styled component

The ErrorMessage in src/pages/admin/Signup/components/SignupForm/index.styled.ts is never imported or used (the only other ErrorMessage is a local definition in ClubDetailEdit/Page.tsx); delete it or clarify its intended purpose.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ce21771 and f467d26.

📒 Files selected for processing (10)
  • src/constants/routerPath.ts (1 hunks)
  • src/pages/Routes.tsx (2 hunks)
  • src/pages/admin/Login/KakaoCallback.tsx (1 hunks)
  • src/pages/admin/Login/component/LoginButton/index.tsx (1 hunks)
  • src/pages/admin/Signup/Page.tsx (1 hunks)
  • src/pages/admin/Signup/api/signup.ts (1 hunks)
  • src/pages/admin/Signup/components/HeaderTitle/index.tsx (1 hunks)
  • src/pages/admin/Signup/components/SignupForm/index.styled.ts (1 hunks)
  • src/pages/admin/Signup/components/SignupForm/index.tsx (1 hunks)
  • src/pages/admin/Signup/type/signup.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (8)
src/pages/admin/Signup/components/SignupForm/index.styled.ts (1)
src/styles/theme.ts (1)
  • theme (3-10)
src/pages/admin/Login/component/LoginButton/index.tsx (1)
src/shared/components/Button/index.tsx (1)
  • Button (17-64)
src/pages/admin/Signup/Page.tsx (3)
src/pages/admin/Signup/components/HeaderTitle/index.tsx (1)
  • HeaderTitle (3-9)
src/pages/admin/Signup/components/SignupForm/index.tsx (1)
  • SignupForm (12-146)
src/styles/theme.ts (1)
  • theme (3-10)
src/pages/admin/Login/KakaoCallback.tsx (1)
src/shared/components/LoadingSpinner.tsx (1)
  • LoadingSpinner (19-19)
src/pages/Routes.tsx (2)
src/pages/admin/Login/KakaoCallback.tsx (1)
  • KakaoCallback (6-42)
src/pages/admin/Signup/Page.tsx (1)
  • AdminSignupPage (5-12)
src/pages/admin/Signup/api/signup.ts (1)
src/pages/admin/Signup/type/signup.ts (1)
  • SignupFormInputs (1-7)
src/pages/admin/Signup/components/HeaderTitle/index.tsx (2)
src/pages/user/Main/components/ClubListSection/Club.styled.ts (1)
  • TextWrapper (94-101)
src/styles/theme.ts (1)
  • theme (3-10)
src/pages/admin/Signup/components/SignupForm/index.tsx (5)
src/pages/admin/Signup/type/signup.ts (1)
  • SignupFormInputs (1-7)
src/pages/admin/Signup/api/signup.ts (1)
  • postSignupForm (3-15)
src/styles/theme.ts (1)
  • theme (3-10)
src/shared/components/Form/InputField/OutlineInputField.tsx (1)
  • OutlineInputField (9-20)
src/shared/components/Button/index.tsx (1)
  • Button (17-64)
🪛 GitHub Actions: Dongarium FE CI/CD
src/pages/Routes.tsx

[error] 13-13: ESLint: import/order violation - './admin/Login/KakaoCallback' import should occur before import of './admin/Login/Page'.

🪛 GitHub Check: lint
src/pages/admin/Login/KakaoCallback.tsx

[failure] 3-3:
axios import should occur before import of react

src/pages/Routes.tsx

[failure] 14-14:
./admin/Signup/Page import should occur before import of ./user/Apply/Page


[failure] 13-13:
./admin/Login/KakaoCallback import should occur before import of ./admin/Login/Page

src/pages/admin/Signup/components/SignupForm/index.tsx

[failure] 61-61:
'error' is defined but never used


[failure] 9-9:
@/pages/admin/Signup/api/signup import should occur before import of @/shared/components/Button


[failure] 6-6:
@/pages/admin/Signup/components/SignupForm/index.styled import should occur before import of @/shared/components/Button

🔇 Additional comments (6)
src/pages/admin/Signup/type/signup.ts (1)

1-7: LGTM!

The type definition is clear and matches the form fields described in the PR objectives.

src/pages/admin/Signup/components/HeaderTitle/index.tsx (1)

3-22: LGTM!

The component provides a clean, styled header for the admin signup page with appropriate theming.

src/pages/Routes.tsx (1)

40-40: Update route path after fixing the typo.

Once the typo in ROUTE_PATH.COMMON.SINGUP is corrected to SIGNUP (see comment on routerPath.ts), update this reference accordingly.

src/pages/admin/Signup/Page.tsx (1)

5-32: LGTM!

The component provides a well-structured layout with proper responsive design using theme breakpoints. The composition of HeaderTitle and SignupForm is clean and maintainable.

src/pages/admin/Login/KakaoCallback.tsx (1)

3-3: Fix the import ordering violation.

Move the axios import to follow the import order convention (third-party imports after React imports).

Apply this diff:

 import { useEffect } from 'react';
 import { useNavigate } from 'react-router-dom';
-import axios from 'axios';
 import { LoadingSpinner } from '@/shared/components/LoadingSpinner';
+import axios from 'axios';

Likely an incorrect or invalid review comment.

src/pages/admin/Signup/components/SignupForm/index.styled.ts (1)

3-15: LGTM: Well-structured responsive wrapper.

The responsive design pattern is clean, with the width and media query breakpoint properly aligned at 48rem.

CLUBDETAIL: 'clubs/:clubId',
LOGIN: 'login',
CALLBACK: 'login/redirect',
SINGUP: 'signup',
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix the typo in the constant name.

The constant SINGUP appears to be a typo and should be SIGNUP.

Apply this diff to correct the typo:

-    SINGUP: 'signup',
+    SIGNUP: 'signup',

Note: This change will require updating all references to ROUTE_PATH.COMMON.SINGUP throughout the codebase (e.g., in Routes.tsx).

🤖 Prompt for AI Agents
In src/constants/routerPath.ts at line 17, the constant name SINGUP is a typo
and should be renamed to SIGNUP; update the exported constant key from SINGUP to
SIGNUP and keep its value 'signup' unchanged, then update all usages across the
codebase (e.g., replace ROUTE_PATH.COMMON.SINGUP with ROUTE_PATH.COMMON.SIGNUP
in Routes.tsx and any other files) to avoid breaking references.

Comment on lines +8 to +11
const handleKakaoLogin = () => {
const kakaoAuthUrl = `https://kauth.kakao.com/oauth/authorize?client_id=${REST_API_KEY}&redirect_uri=${REDIRECT_URI}&response_type=code`;
window.location.href = kakaoAuthUrl;
};
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

❓ Verification inconclusive

Verify environment variables are defined.

Ensure that VITE_KAKAO_REST_API_KEY and VITE_KAKAO_REDIRECT_URI are properly configured in the environment files. Missing values will result in an incomplete OAuth URL.

Run the following script to check if environment variable examples or documentation exist:


🏁 Script executed:

#!/bin/bash
# Check for .env files and documentation mentioning Kakao environment variables
fd -t f '\.env' --exec cat {} \; | rg -i 'kakao|VITE_KAKAO'
fd -t f 'README' --exec cat {} \; | rg -i 'kakao|environment|\.env'

Length of output: 0


Add and document Kakao OAuth environment variables

  • Add VITE_KAKAO_REST_API_KEY and VITE_KAKAO_REDIRECT_URI to .env.example (or equivalent).
  • Document their usage in the README and ensure they’re set in all deployment environments.
🤖 Prompt for AI Agents
In src/pages/admin/Login/component/LoginButton/index.tsx lines 8-11, you need to
switch the hardcoded REST_API_KEY and REDIRECT_URI to use Vite env vars and add
+ document those variables: add VITE_KAKAO_REST_API_KEY and
VITE_KAKAO_REDIRECT_URI to .env.example with short descriptions and example
values, update the README (Auth / Kakao section) to show how to obtain and set
those vars for local, staging and production, and verify your deployment
configurations (CI/CD, hosting env settings) include the two VITE_KAKAO_* vars;
ensure the component reads them via import.meta.env.VITE_KAKAO_REST_API_KEY and
import.meta.env.VITE_KAKAO_REDIRECT_URI (and include a brief runtime
fallback/error log if missing).

const code = new URL(window.location.href).searchParams.get('code');
if (!code) return;

console.log(code);
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Remove debug console.log statements.

Console.log statements should be removed before merging to production.

Apply this diff to remove debug logs:

-    console.log(code);
     const fetchToken = async () => {
       try {
         const res = await axios.post('http://localhost:8080/api/auth/kakao/login', {
           authorizationCode: code,
         });
-        console.log('응답res ', res);

         // CASE 1) 기존 회원
         ...
       } catch (error) {
-        console.log('error:', error);
+        console.error('Kakao login failed:', error);
       }

Consider using a proper logger or at least console.error for error cases.

Also applies to: 19-19, 34-34

🤖 Prompt for AI Agents
In src/pages/admin/Login/KakaoCallback.tsx around lines 13, 19 and 34, remove
the debug console.log statements (e.g., console.log(code)) before merging to
production; replace any necessary runtime diagnostics with a proper logger or,
for errors only, use console.error, and ensure no stray console.log calls remain
in those lines of the file.

Comment on lines 1 to 10
import { useForm, FormProvider } from 'react-hook-form';
import { useNavigate } from 'react-router-dom';
import { toast } from 'sonner';
import { Button } from '@/shared/components/Button';
import { OutlineInputField } from '@/shared/components/Form/InputField/OutlineInputField';
import * as S from '@/pages/admin/Signup/components/SignupForm/index.styled';
import { theme } from '@/styles/theme';

import { postSignupForm } from '@/pages/admin/Signup/api/signup';
import type { SignupFormInputs } from '@/pages/admin/Signup/type/signup';
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix import order per ESLint rules.

The linter indicates that local imports should come before shared component imports. Reorder as follows:

Apply this diff:

 import { useForm, FormProvider } from 'react-hook-form';
 import { useNavigate } from 'react-router-dom';
 import { toast } from 'sonner';
+import * as S from '@/pages/admin/Signup/components/SignupForm/index.styled';
+import { postSignupForm } from '@/pages/admin/Signup/api/signup';
+import type { SignupFormInputs } from '@/pages/admin/Signup/type/signup';
 import { Button } from '@/shared/components/Button';
 import { OutlineInputField } from '@/shared/components/Form/InputField/OutlineInputField';
-import * as S from '@/pages/admin/Signup/components/SignupForm/index.styled';
 import { theme } from '@/styles/theme';
-
-import { postSignupForm } from '@/pages/admin/Signup/api/signup';
-import type { SignupFormInputs } from '@/pages/admin/Signup/type/signup';
🧰 Tools
🪛 GitHub Check: lint

[failure] 9-9:
@/pages/admin/Signup/api/signup import should occur before import of @/shared/components/Button


[failure] 6-6:
@/pages/admin/Signup/components/SignupForm/index.styled import should occur before import of @/shared/components/Button

🤖 Prompt for AI Agents
In src/pages/admin/Signup/components/SignupForm/index.tsx around lines 1-10, the
import order violates ESLint (local imports should come before shared imports);
reorder so all local project imports (any starting with "@/pages", "@/styles",
and the component's own styled import) appear before shared/library imports
(like "@/shared/..." and external libs). Specifically, move the imports for S
(index.styled), theme, postSignupForm, and SignupFormInputs above the shared
Button and OutlineInputField imports, and keep third-party imports (react,
react-router-dom, sonner) at the top.

Comment on lines 13 to 14
import { KakaoCallback } from './admin/Login/KakaoCallback';
import { AdminSignupPage } from './admin/Signup/Page';
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix the import ordering violations.

The ESLint import/order rule requires imports to be ordered alphabetically within their group. The new imports should be placed before the existing ./admin/Login/Page and ./user/Apply/Page imports.

Apply this diff to fix the import order:

 import { ClubDetailPage } from '@/pages/user/ClubDetail/Page';
 import { MainPage } from '@/pages/user/Main/Page.tsx';
 import { ApplicationDetailPage } from './admin/ApplicationDetail/Page';
+import { KakaoCallback } from './admin/Login/KakaoCallback';
 import { LoginPage } from './admin/Login/Page';
+import { AdminSignupPage } from './admin/Signup/Page';
 import { ClubApplicationPage } from './user/Apply/Page';
-
-import { KakaoCallback } from './admin/Login/KakaoCallback';
-import { AdminSignupPage } from './admin/Signup/Page';
-
📝 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
import { KakaoCallback } from './admin/Login/KakaoCallback';
import { AdminSignupPage } from './admin/Signup/Page';
import { ClubDetailPage } from '@/pages/user/ClubDetail/Page';
import { MainPage } from '@/pages/user/Main/Page.tsx';
import { ApplicationDetailPage } from './admin/ApplicationDetail/Page';
import { KakaoCallback } from './admin/Login/KakaoCallback';
import { LoginPage } from './admin/Login/Page';
import { AdminSignupPage } from './admin/Signup/Page';
import { ClubApplicationPage } from './user/Apply/Page';
🧰 Tools
🪛 GitHub Actions: Dongarium FE CI/CD

[error] 13-13: ESLint: import/order violation - './admin/Login/KakaoCallback' import should occur before import of './admin/Login/Page'.

🪛 GitHub Check: lint

[failure] 14-14:
./admin/Signup/Page import should occur before import of ./user/Apply/Page


[failure] 13-13:
./admin/Login/KakaoCallback import should occur before import of ./admin/Login/Page

🤖 Prompt for AI Agents
In src/pages/Routes.tsx around lines 13 to 14, the new imports violate the
ESLint import/order alphabetical rule; move the imports for
./admin/Login/KakaoCallback and ./admin/Signup/Page so they appear
alphabetically before the existing ./admin/Login/Page and ./user/Apply/Page
imports (i.e., reorder the import block so all relative imports are alphabetized
within their group).

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 (12)
src/pages/admin/Login/KakaoCallback.tsx (5)

13-13: Remove debug console.log statements.

Development console.log statements should be removed before merging to production.

Remove lines 13, 19, and 34:

-    console.log(code);
     const fetchToken = async () => {
       try {
         const res = await axios.post('http://localhost:8080/api/auth/kakao/login', {
           authorizationCode: code,
         });
-        console.log('응답res ', res);
         
         // ... rest of code
       } catch (error) {
-        console.log('error:', error);
       }
     };

10-11: Use useSearchParams and handle OAuth errors.

  1. React Router's useSearchParams is more idiomatic than parsing window.location.href.
  2. When users deny consent or Kakao OAuth fails, an error query parameter is sent. This case is not handled, leaving users stuck on a loading screen.

Apply this diff:

+import { useNavigate, useSearchParams } from 'react-router-dom';
-import { useNavigate } from 'react-router-dom';

 export const KakaoCallback = () => {
   const navigate = useNavigate();
+  const [searchParams] = useSearchParams();

   useEffect(() => {
-    const code = new URL(window.location.href).searchParams.get('code');
+    const code = searchParams.get('code');
+    const error = searchParams.get('error');
+    
+    if (error) {
+      console.error('Kakao OAuth error:', error);
+      navigate('/login');
+      return;
+    }
+    
     if (!code) return;

16-18: Use environment variable for API URL.

The API URL is hardcoded to localhost:8080, which will fail in production. Use the VITE_API_BASE_URL environment variable for consistency with other API calls (e.g., signup.ts).

Apply this diff:

-        const res = await axios.post('http://localhost:8080/api/auth/kakao/login', {
+        const res = await axios.post(`${import.meta.env.VITE_API_BASE_URL}/auth/kakao/login`, {
           authorizationCode: code,
         });

21-32: Fix typo in commented refreshToken storage.

While this code is commented (pending token flow implementation per PR description), line 25 has two errors:

  1. 'refreshToken ' key contains an extra space
  2. Should use res.data.refreshToken, not res.data.accessToken

Fix before uncommenting:

         // 1-1. accessToken, refreshToken 발급
         // localStorage.setItem('accessToken', res.data.accessToken);
-        // localStorage.setItem('refreshToken ', res.data.accessToken);
+        // localStorage.setItem('refreshToken', res.data.refreshToken);

33-35: Provide user feedback on error.

The error handler only logs to console, leaving users stuck on a loading screen. Add user-visible error handling (toast notification or redirect to login).

Apply this diff:

       } catch (error) {
-        console.log('error:', error);
+        console.error('Kakao login error:', error);
+        // TODO: Add toast notification
+        navigate('/login');
       }
src/pages/admin/Signup/Page.tsx (1)

26-28: Remove redundant media query.

The web breakpoint media query sets padding: '1.5rem', which is identical to the default padding: '0 1.5rem' (vertical padding is already 0 in both). This media query can be removed.

Apply this diff:

   padding: '0 1.5rem',
   boxSizing: 'border-box',

-  [`@media (max-width: ${theme.breakpoints.web})`]: {
-    padding: '1.5rem',
-  },
   [`@media (max-width: ${theme.breakpoints.mobile})`]: {
     padding: '1rem',
   },
src/pages/admin/Login/component/LoginButton/index.tsx (2)

18-20: Remove type='submit' from navigation button.

The signup button uses the to prop for navigation, so type='submit' is semantically incorrect and could cause confusion.

Apply this diff:

-      <Button to='/signup' width='100%' type='submit'>
+      <Button to='/signup' width='100%'>
         {'회원가입'}
       </Button>

22-27: Change type='submit' to type='button' on Kakao login button.

The Kakao login button uses onClick for navigation, not form submission. Using type='submit' may cause unintended form submission behavior.

Apply this diff:

-        <Button width='100%' type='submit' onClick={handleKakaoLogin}>
+        <Button width='100%' type='button' onClick={handleKakaoLogin}>
           <ButtonContent>
src/pages/Routes.tsx (1)

34-41: Fix typo: SINGUP → SIGNUP.

The route key COMMON.SINGUP contains a typo. This should be COMMON.SIGNUP for consistency and correctness. The typo originates from src/constants/routerPath.ts and should be fixed there first, then updated here.

Apply this diff:

       {
-        path: COMMON.SINGUP,
+        path: COMMON.SIGNUP,
         element: <AdminSignupPage />,
       },
src/constants/routerPath.ts (1)

16-17: Fix typo: SINGUP → SIGNUP.

The constant name contains a typo. Change SINGUP to SIGNUP.

Apply this diff:

     LOGIN: 'login',
     CALLBACK: 'login/redirect',
-    SINGUP: 'signup',
+    SIGNUP: 'signup',
   },
src/pages/admin/Signup/api/signup.ts (1)

3-15: Use axios for consistency and handle empty response bodies safely.

The codebase uses axios elsewhere (see KakaoCallback.tsx), but this function uses fetch, creating inconsistency. Additionally, response.json() will throw if the server returns an empty body on successful POST, which is common for registration endpoints.

Refactor to use axios:

+import axios from 'axios';
 import type { SignupFormInputs } from '../type/signup';

-export const postSignupForm = async (formData: SignupFormInputs): Promise<SignupFormInputs> => {
+export const postSignupForm = async (formData: SignupFormInputs): Promise<void> => {
   const url = `${import.meta.env.VITE_API_BASE_URL}/auth/register`;
-  const response = await fetch(url, {
-    method: 'POST',
-    headers: {
-      'Content-Type': 'application/json',
-    },
-    body: JSON.stringify(formData),
-  });
-
-  if (!response.ok) throw new Error('회원 가입 양식을 제출하지 못했습니다.');
-  return await response.json();
+  try {
+    await axios.post(url, formData);
+  } catch (error) {
+    throw new Error('회원 가입 양식을 제출하지 못했습니다.');
+  }
 };
src/pages/admin/Signup/components/SignupForm/index.tsx (1)

27-63: Fix the async signup flow—success fires even on failure

postSignupForm isn’t awaited, so both success toasts and navigations fire immediately, even if the request rejects. The extra toast.success/navigate outside the promise chain guarantees duplicate notifications and a second redirect. This also leaves the ESLint failure ('error' is defined but never used). Await the API call once, keep a single success branch, and handle errors in one catch to keep the UX and lint clean.

-  const onSubmit = async (signupFormValue: SignupFormInputs) => {
-    try {
-      console.log('회원가입 data:', signupFormValue);
-      postSignupForm(signupFormValue)
-        .then(() => {
-          toast.success('제출 성공!', {
-            style: {
-              backgroundColor: theme.colors.primary,
-              color: 'white',
-            },
-            duration: 1000,
-          });
-          setTimeout(() => {
-            navigate(`/`);
-          }, 1000);
-        })
-        .catch(() => {
-          toast.error('제출 실패!', {
-            duration: 1000,
-            style: {
-              backgroundColor: 'white',
-              color: theme.colors.error,
-            },
-          });
-        });
-
-      toast.success('회원가입 완료!', {
-        style: { backgroundColor: theme.colors.primary, color: 'white' },
-        duration: 1000,
-      });
-
-      setTimeout(() => {
-        navigate('/login');
-      }, 1000);
-    } catch (error) {
-      toast.error('회원가입 실패', { duration: 1000 });
-    }
-  };
+  const onSubmit = async (signupFormValue: SignupFormInputs) => {
+    try {
+      await postSignupForm(signupFormValue);
+
+      toast.success('회원가입 완료!', {
+        style: { backgroundColor: theme.colors.primary, color: 'white' },
+        duration: 1000,
+      });
+
+      setTimeout(() => {
+        navigate('/login');
+      }, 1000);
+    } catch {
+      toast.error('회원가입 실패', {
+        duration: 1000,
+        style: {
+          backgroundColor: 'white',
+          color: theme.colors.error,
+        },
+      });
+    }
+  };
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ce21771 and da861db.

📒 Files selected for processing (10)
  • src/constants/routerPath.ts (1 hunks)
  • src/pages/Routes.tsx (2 hunks)
  • src/pages/admin/Login/KakaoCallback.tsx (1 hunks)
  • src/pages/admin/Login/component/LoginButton/index.tsx (1 hunks)
  • src/pages/admin/Signup/Page.tsx (1 hunks)
  • src/pages/admin/Signup/api/signup.ts (1 hunks)
  • src/pages/admin/Signup/components/HeaderTitle/index.tsx (1 hunks)
  • src/pages/admin/Signup/components/SignupForm/index.styled.ts (1 hunks)
  • src/pages/admin/Signup/components/SignupForm/index.tsx (1 hunks)
  • src/pages/admin/Signup/type/signup.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (8)
src/pages/admin/Signup/components/SignupForm/index.tsx (5)
src/pages/admin/Signup/type/signup.ts (1)
  • SignupFormInputs (1-7)
src/pages/admin/Signup/api/signup.ts (1)
  • postSignupForm (3-15)
src/styles/theme.ts (1)
  • theme (3-10)
src/shared/components/Form/InputField/OutlineInputField.tsx (1)
  • OutlineInputField (9-20)
src/shared/components/Button/index.tsx (1)
  • Button (17-64)
src/pages/admin/Login/component/LoginButton/index.tsx (1)
src/shared/components/Button/index.tsx (1)
  • Button (17-64)
src/pages/admin/Login/KakaoCallback.tsx (1)
src/shared/components/LoadingSpinner.tsx (1)
  • LoadingSpinner (19-19)
src/pages/Routes.tsx (2)
src/pages/admin/Login/KakaoCallback.tsx (1)
  • KakaoCallback (6-42)
src/pages/admin/Signup/Page.tsx (1)
  • AdminSignupPage (5-12)
src/pages/admin/Signup/components/HeaderTitle/index.tsx (2)
src/pages/user/Main/components/ClubListSection/Club.styled.ts (1)
  • TextWrapper (94-101)
src/styles/theme.ts (1)
  • theme (3-10)
src/pages/admin/Signup/components/SignupForm/index.styled.ts (1)
src/styles/theme.ts (1)
  • theme (3-10)
src/pages/admin/Signup/api/signup.ts (1)
src/pages/admin/Signup/type/signup.ts (1)
  • SignupFormInputs (1-7)
src/pages/admin/Signup/Page.tsx (3)
src/pages/admin/Signup/components/HeaderTitle/index.tsx (1)
  • HeaderTitle (3-9)
src/pages/admin/Signup/components/SignupForm/index.tsx (1)
  • SignupForm (12-146)
src/styles/theme.ts (1)
  • theme (3-10)
🪛 GitHub Actions: Dongarium FE CI/CD
src/pages/admin/Signup/components/SignupForm/index.tsx

[error] 61-61: ESLint: 'error' is defined but never used. (no-unused-vars)

🪛 GitHub Check: lint
src/pages/admin/Signup/components/SignupForm/index.tsx

[failure] 61-61:
'error' is defined but never used

🔇 Additional comments (3)
src/pages/admin/Signup/components/HeaderTitle/index.tsx (1)

3-22: LGTM!

The HeaderTitle component is well-structured and follows the project's styling patterns. The implementation is clean and straightforward.

src/pages/admin/Signup/type/signup.ts (1)

1-7: LGTM!

The type definition is clear and appropriate for the signup form data contract.

src/pages/admin/Signup/Page.tsx (1)

5-12: LGTM!

The page structure is clean and well-organized, composing HeaderTitle and SignupForm within a responsive Layout.

Comment on lines +4 to +11
const REST_API_KEY = import.meta.env.VITE_KAKAO_REST_API_KEY;
const REDIRECT_URI = import.meta.env.VITE_KAKAO_REDIRECT_URI;

export const LoginButton = () => {
const handleKakaoLogin = () => {
const kakaoAuthUrl = `https://kauth.kakao.com/oauth/authorize?client_id=${REST_API_KEY}&redirect_uri=${REDIRECT_URI}&response_type=code`;
window.location.href = kakaoAuthUrl;
};
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

Validate environment variables before use.

The REST_API_KEY and REDIRECT_URI could be undefined if environment variables are not set. This would result in a malformed OAuth URL at runtime.

Add validation and error handling:

 const REST_API_KEY = import.meta.env.VITE_KAKAO_REST_API_KEY;
 const REDIRECT_URI = import.meta.env.VITE_KAKAO_REDIRECT_URI;

 export const LoginButton = () => {
   const handleKakaoLogin = () => {
+    if (!REST_API_KEY || !REDIRECT_URI) {
+      console.error('Kakao OAuth configuration missing');
+      return;
+    }
     const kakaoAuthUrl = `https://kauth.kakao.com/oauth/authorize?client_id=${REST_API_KEY}&redirect_uri=${REDIRECT_URI}&response_type=code`;
     window.location.href = kakaoAuthUrl;
   };
📝 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 REST_API_KEY = import.meta.env.VITE_KAKAO_REST_API_KEY;
const REDIRECT_URI = import.meta.env.VITE_KAKAO_REDIRECT_URI;
export const LoginButton = () => {
const handleKakaoLogin = () => {
const kakaoAuthUrl = `https://kauth.kakao.com/oauth/authorize?client_id=${REST_API_KEY}&redirect_uri=${REDIRECT_URI}&response_type=code`;
window.location.href = kakaoAuthUrl;
};
const REST_API_KEY = import.meta.env.VITE_KAKAO_REST_API_KEY;
const REDIRECT_URI = import.meta.env.VITE_KAKAO_REDIRECT_URI;
export const LoginButton = () => {
const handleKakaoLogin = () => {
if (!REST_API_KEY || !REDIRECT_URI) {
console.error('Kakao OAuth configuration missing');
return;
}
const kakaoAuthUrl = `https://kauth.kakao.com/oauth/authorize?client_id=${REST_API_KEY}&redirect_uri=${REDIRECT_URI}&response_type=code`;
window.location.href = kakaoAuthUrl;
};
// …rest of the component
};
🤖 Prompt for AI Agents
In src/pages/admin/Login/component/LoginButton/index.tsx around lines 4 to 11,
the code builds the Kakao OAuth URL using REST_API_KEY and REDIRECT_URI without
checking they exist; validate both import.meta.env values before using them, and
handle missing values by preventing navigation and surfacing an error (e.g.,
disable the button and show a console/error message or throw a clear error).
Specifically, if either REST_API_KEY or REDIRECT_URI is falsy, do not construct
or assign window.location.href; instead set an error state or log a descriptive
message indicating which env var is missing so the UI can avoid creating a
malformed OAuth URL and inform the developer/operator.

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: 0

♻️ Duplicate comments (2)
src/pages/admin/Login/component/LoginButton/index.tsx (1)

4-11: Critical: Add runtime validation for environment variables.

The environment variables REST_API_KEY and REDIRECT_URI can be undefined if not configured, leading to a malformed OAuth URL (https://kauth.kakao.com/oauth/authorize?client_id=undefined&redirect_uri=undefined&response_type=code). This will cause silent failures in the Kakao login flow.

Apply this diff to add validation:

 const REST_API_KEY = import.meta.env.VITE_KAKAO_REST_API_KEY;
 const REDIRECT_URI = import.meta.env.VITE_KAKAO_REDIRECT_URI;
 
 export const LoginButton = () => {
   const handleKakaoLogin = () => {
+    if (!REST_API_KEY || !REDIRECT_URI) {
+      console.error('Kakao OAuth configuration missing');
+      return;
+    }
     const kakaoAuthUrl = `https://kauth.kakao.com/oauth/authorize?client_id=${REST_API_KEY}&redirect_uri=${REDIRECT_URI}&response_type=code`;
     window.location.href = kakaoAuthUrl;
   };
src/pages/admin/Signup/components/SignupForm/index.tsx (1)

1-9: Fix import order to resolve pipeline failure.

The import of @/styles/theme on line 5 should come after the shared component imports to comply with the project's ESLint import order rules.

Apply this diff:

 import { useForm, FormProvider } from 'react-hook-form';
 import { useNavigate } from 'react-router-dom';
 import { toast } from 'sonner';
 import { postSignupForm } from '@/pages/admin/Signup/api/signup';
-import { theme } from '@/styles/theme';
 import * as S from '@/pages/admin/Signup/components/SignupForm/index.styled';
 import { Button } from '@/shared/components/Button';
 import { OutlineInputField } from '@/shared/components/Form/InputField/OutlineInputField';
+import { theme } from '@/styles/theme';
 import type { SignupFormInputs } from '@/pages/admin/Signup/type/signup';
🧹 Nitpick comments (3)
src/pages/admin/Login/component/LoginButton/index.tsx (1)

15-17: Clarify the purpose of the login button.

The first button labeled "로그인" has no onClick handler or to prop, making it non-functional. If this is a placeholder for future token-based login functionality (per the PR notes mentioning token verification work in a separate branch), consider adding a comment or removing it temporarily to avoid user confusion.

If this is intentional, add a comment:

-      <Button width='100%' type='button'>
+      {/* TODO: Implement token-based login - see separate PR */}
+      <Button width='100%' type='button' disabled>
         {'로그인'}
       </Button>
src/pages/admin/Signup/components/SignupForm/index.tsx (1)

36-44: Log the caught error for debugging.

The error parameter in the catch block is unused (flagged by the linter). Logging it would help with debugging signup failures.

Apply this diff:

     } catch (error) {
+      console.error('Signup failed:', error);
       toast.error('회원가입 실패!', {
         duration: 1000,
         style: {
           backgroundColor: 'white',
           color: theme.colors.error,
         },
       });
     }
src/pages/admin/Login/Page.tsx (1)

9-9: Consider clarifying the temporary removal of LoginInput.

The commented-out LoginInput component should either be removed entirely or enhanced with a TODO comment explaining its temporary removal, since the PR notes indicate that token-based login will be implemented in a separate branch.

Apply this diff to clarify:

-      {/* <LoginInput /> */}
+      {/* TODO: Re-enable LoginInput after token verification flow is implemented (separate PR) */}

Or simply remove the comment if it's not needed:

-      {/* <LoginInput /> */}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between da861db and 68a4bb2.

📒 Files selected for processing (7)
  • src/constants/routerPath.ts (1 hunks)
  • src/pages/Routes.tsx (2 hunks)
  • src/pages/admin/Login/KakaoCallback.tsx (1 hunks)
  • src/pages/admin/Login/Page.tsx (1 hunks)
  • src/pages/admin/Login/component/LoginButton/index.tsx (1 hunks)
  • src/pages/admin/Signup/Page.tsx (1 hunks)
  • src/pages/admin/Signup/components/SignupForm/index.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/pages/Routes.tsx
  • src/pages/admin/Login/KakaoCallback.tsx
  • src/pages/admin/Signup/Page.tsx
🧰 Additional context used
🧬 Code graph analysis (2)
src/pages/admin/Signup/components/SignupForm/index.tsx (5)
src/pages/admin/Signup/type/signup.ts (1)
  • SignupFormInputs (1-7)
src/pages/admin/Signup/api/signup.ts (1)
  • postSignupForm (3-15)
src/styles/theme.ts (1)
  • theme (3-10)
src/shared/components/Form/InputField/OutlineInputField.tsx (1)
  • OutlineInputField (9-20)
src/shared/components/Button/index.tsx (1)
  • Button (17-64)
src/pages/admin/Login/component/LoginButton/index.tsx (1)
src/shared/components/Button/index.tsx (1)
  • Button (17-64)
🪛 GitHub Actions: Dongarium FE CI/CD
src/pages/admin/Signup/components/SignupForm/index.tsx

[error] 5-5: @/styles/theme import should occur after import of @/shared/components/Form/InputField/OutlineInputField import/order

🪛 GitHub Check: lint
src/pages/admin/Signup/components/SignupForm/index.tsx

[failure] 36-36:
'error' is defined but never used


[failure] 5-5:
@/styles/theme import should occur after import of @/shared/components/Form/InputField/OutlineInputField

🔇 Additional comments (2)
src/pages/admin/Signup/components/SignupForm/index.tsx (1)

47-126: LGTM: Clean form implementation with appropriate validation.

The form structure is well-organized with proper validation rules for each field:

  • Email pattern validation
  • 6-digit student ID format
  • Korean phone number format (010-XXXX-XXXX)
  • Required field checks with helpful error messages

The use of FormProvider and react-hook-form follows best practices, and the disabled state during submission prevents duplicate submissions.

src/constants/routerPath.ts (1)

16-17: LGTM: Route constants correctly defined.

The new route constants are appropriately named and structured:

  • CALLBACK for Kakao OAuth redirect handling
  • SIGNUP for the admin signup page

These align well with the new components (KakaoCallback and AdminSignupPage) introduced in this PR.

- OAuth 일원화
@ganimjeong
Copy link
Contributor

npm install 했는데도 husky가 안되시는건가용?

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

🧹 Nitpick comments (1)
src/pages/admin/Signup/components/SignupForm/index.tsx (1)

66-116: Consider extracting validation patterns for reusability.

The validation patterns (email, studentId, phoneNumber) are defined inline and could be reused across the codebase. Consider extracting them to a shared constants file.

Example:

// src/constants/validationPatterns.ts
export const VALIDATION_PATTERNS = {
  email: {
    value: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
    message: '올바른 이메일 형식이 아닙니다.',
  },
  studentId: {
    value: /^\d{6}$/,
    message: '학번은 숫자 6자리여야 합니다.',
  },
  phoneNumber: {
    value: /^\d{2,3}-\d{3,4}-\d{4}$/,
    message: '올바른 전화번호 형식이 아닙니다.',
  },
} as const;

Then use:

{...methods.register('email', {
  required: '이메일을 입력하세요.',
  pattern: VALIDATION_PATTERNS.email,
})}

Note: The phone number pattern requires dashes, which might be inconvenient for users pasting numbers without formatting. Consider adding input masking or accepting both formats.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3937106 and a8f092d.

📒 Files selected for processing (1)
  • src/pages/admin/Signup/components/SignupForm/index.tsx (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/pages/admin/Signup/components/SignupForm/index.tsx (5)
src/pages/admin/Signup/type/signup.ts (1)
  • SignupFormInputs (1-7)
src/pages/admin/Signup/api/signup.ts (1)
  • postSignupForm (3-15)
src/styles/theme.ts (1)
  • theme (3-10)
src/shared/components/Form/InputField/OutlineInputField.tsx (1)
  • OutlineInputField (9-20)
src/shared/components/Button/index.tsx (1)
  • Button (17-64)
🪛 GitHub Actions: Dongarium FE CI/CD
src/pages/admin/Signup/components/SignupForm/index.tsx

[error] 36-36: ESLint: 'error' is defined but never used. (no-unused-vars). Command: npm run lint

🪛 GitHub Check: lint
src/pages/admin/Signup/components/SignupForm/index.tsx

[failure] 36-36:
'error' is defined but never used

Comment on lines 26 to 45
const onSubmit = async (signupFormValue: SignupFormInputs) => {
try {
await postSignupForm(signupFormValue);
toast.success('회원가입 완료!', {
style: { backgroundColor: theme.colors.primary, color: 'white' },
duration: 1000,
});
setTimeout(() => {
navigate(`/login`);
}, 1000);
} catch (error) {
toast.error('회원가입 실패!', {
duration: 1000,
style: {
backgroundColor: 'white',
color: theme.colors.error,
},
});
}
};
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

Fix unused error variable and navigation timing issue.

Two concerns:

  1. Pipeline failure: The error parameter on Line 36 is unused. Either prefix it with an underscore (_error) to mark as intentionally unused, or log it for debugging.

  2. Navigation timing hazard: The setTimeout on Lines 33-35 can trigger navigation after the component unmounts, potentially causing React warnings or failed navigations if the user closes the tab or navigates elsewhere during the 1-second delay.

Apply this diff to address both issues:

-    } catch (error) {
+    } catch (_error) {
       toast.error('회원가입 실패!', {
         duration: 1000,
         style: {
           backgroundColor: 'white',
           color: theme.colors.error,
         },
       });
     }

For the navigation timing issue, consider one of these patterns:

Option 1: Use toast's onAutoClose callback (cleaner)

     try {
       await postSignupForm(signupFormValue);
       toast.success('회원가입 완료!', {
         style: { backgroundColor: theme.colors.primary, color: 'white' },
         duration: 1000,
+        onAutoClose: () => navigate('/login'),
       });
-      setTimeout(() => {
-        navigate(`/login`);
-      }, 1000);

Option 2: Store timeout ID and cleanup in useEffect

useEffect(() => {
  return () => {
    // cleanup any pending timeouts when component unmounts
  };
}, []);

// In onSubmit:
const timeoutId = setTimeout(() => navigate('/login'), 1000);
// Store timeoutId for cleanup if needed
🧰 Tools
🪛 GitHub Actions: Dongarium FE CI/CD

[error] 36-36: ESLint: 'error' is defined but never used. (no-unused-vars). Command: npm run lint

🪛 GitHub Check: lint

[failure] 36-36:
'error' is defined but never used

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 (3)
src/pages/admin/Signup/components/SignupForm/index.tsx (3)

1-9: Reorder imports per ESLint rules.

Local project imports (@/styles, @/pages types) should come before shared component imports (@/shared). Move Lines 8-9 before Lines 6-7.

Apply this diff:

 import { useForm, FormProvider } from 'react-hook-form';
 import { useNavigate } from 'react-router-dom';
 import { toast } from 'sonner';
 import { postSignupForm } from '@/pages/admin/Signup/api/signup';
 import * as S from '@/pages/admin/Signup/components/SignupForm/index.styled';
+import type { SignupFormInputs } from '@/pages/admin/Signup/type/signup';
+import { theme } from '@/styles/theme';
 import { Button } from '@/shared/components/Button';
 import { OutlineInputField } from '@/shared/components/Form/InputField/OutlineInputField';
-import { theme } from '@/styles/theme';
-import type { SignupFormInputs } from '@/pages/admin/Signup/type/signup';

36-36: Fix unused error variable and naming conflict.

The catch parameter errors is unused (causing pipeline failure) and shadows the errors from formState (Line 24), which is confusing.

Apply this diff to fix both issues and add error logging:

-    } catch (errors) {
+    } catch (error) {
+      console.error('Signup failed:', error);
       toast.error('회원가입 실패!', {

33-35: Fix navigation timing hazard.

setTimeout can trigger after component unmount, causing React warnings or failed navigation. Use toast's onAutoClose callback instead.

Apply this diff:

       toast.success('회원가입 완료!', {
         style: { backgroundColor: theme.colors.primary, color: 'white' },
         duration: 1000,
+        onAutoClose: () => navigate('/login'),
       });
-      setTimeout(() => {
-        navigate(`/login`);
-      }, 1000);
🧹 Nitpick comments (1)
src/pages/admin/Signup/components/SignupForm/index.tsx (1)

54-119: Consider extracting repetitive field pattern.

Each form field follows the same pattern: FormFieldLabelOutlineInputField with register, invalid, message. Consider extracting a reusable FormFieldWrapper component to reduce duplication and improve maintainability.

Example:

const FormFieldWrapper = ({ 
  label, 
  name, 
  placeholder, 
  validation, 
  error 
}: FormFieldWrapperProps) => (
  <S.FormField>
    <S.Label>{label}</S.Label>
    <OutlineInputField
      placeholder={placeholder}
      {...methods.register(name, validation)}
      invalid={!!error}
      message={error?.message}
    />
  </S.FormField>
);

Then use it like:

<FormFieldWrapper 
  label="이름" 
  name="name"
  placeholder="이름을 입력하세요."
  validation={{ required: '이름을 입력하세요.' }}
  error={errors.name}
/>
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a8f092d and 0f57197.

📒 Files selected for processing (1)
  • src/pages/admin/Signup/components/SignupForm/index.tsx (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/pages/admin/Signup/components/SignupForm/index.tsx (5)
src/pages/admin/Signup/type/signup.ts (1)
  • SignupFormInputs (1-7)
src/pages/admin/Signup/api/signup.ts (1)
  • postSignupForm (3-15)
src/styles/theme.ts (1)
  • theme (3-10)
src/shared/components/Form/InputField/OutlineInputField.tsx (1)
  • OutlineInputField (9-20)
src/shared/components/Button/index.tsx (1)
  • Button (17-64)
🪛 GitHub Actions: Dongarium FE CI/CD
src/pages/admin/Signup/components/SignupForm/index.tsx

[error] 36-36: 'errors' is defined but never used @typescript-eslint/no-unused-vars

🪛 GitHub Check: lint
src/pages/admin/Signup/components/SignupForm/index.tsx

[failure] 36-36:
'errors' is defined but never used

Comment on lines +109 to +114
placeholder='010-0000-0000'
{...methods.register('phoneNumber', {
required: '전화번호를 입력하세요.',
pattern: {
value: /^\d{2,3}-\d{3,4}-\d{4}$/,
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 | 🟡 Minor

Phone number pattern mismatch with placeholder.

The regex pattern allows 2-3 digit area codes (\d{2,3}), but the placeholder shows 010-0000-0000 (3 digits). If only 3-digit area codes like 010 are valid, update the pattern to /^010-\d{3,4}-\d{4}$/. Otherwise, update the placeholder to reflect the flexibility, e.g., 010-1234-5678 또는 02-1234-5678.

Apply this diff if only 3-digit area codes are intended:

                   pattern: {
-                    value: /^\d{2,3}-\d{3,4}-\d{4}$/,
+                    value: /^010-\d{3,4}-\d{4}$/,
                     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
placeholder='010-0000-0000'
{...methods.register('phoneNumber', {
required: '전화번호를 입력하세요.',
pattern: {
value: /^\d{2,3}-\d{3,4}-\d{4}$/,
message: '올바른 전화번호 형식이 아닙니다.',
placeholder='010-0000-0000'
{...methods.register('phoneNumber', {
required: '전화번호를 입력하세요.',
pattern: {
value: /^010-\d{3,4}-\d{4}$/,
message: '올바른 전화번호 형식이 아닙니다.',
🤖 Prompt for AI Agents
In src/pages/admin/Signup/components/SignupForm/index.tsx around lines 109-114,
the phone placeholder "010-0000-0000" conflicts with the current regex allowing
2-3 digit area codes; fix by either (A) if only 3-digit area codes like 010 are
valid, change the validation pattern to require "010" specifically (replace
current pattern with one matching /^010-\d{3,4}-\d{4}$/), or (B) if both 2- and
3-digit area codes are allowed, update the placeholder to reflect that
flexibility (e.g., "010-1234-5678 또는 02-1234-5678"); apply the corresponding
change to the register call and/or placeholder text so pattern and placeholder
match.

@aaaaaattt aaaaaattt merged commit 2ec3e89 into develop Oct 15, 2025
11 checks passed
@aaaaaattt aaaaaattt deleted the feat/oauth-login branch October 15, 2025 06:55
ganimjeong pushed a commit that referenced this pull request Oct 27, 2025
…ogin

[FEAT] 관리자 Oauth 로그인 관련 Redirect 추가
ganimjeong pushed a commit that referenced this pull request Oct 27, 2025
…ogin

[FEAT] 관리자 Oauth 로그인 관련 Redirect 추가
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨ Feature 기능 구현 이슈 🌈 frontend 프론트엔드 이슈 🛠️ Refactor 리팩터링 이슈

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEAT] 동아리 운영자 회원가입 페이지 [FEAT] 로그인 카카오 API 연결

3 participants