Skip to content

feat: 카카오 OAuth 로그인 플로우 완전 구현 및 인증 토큰 관리 시스템 구축#54

Merged
Dobbymin merged 19 commits intomainfrom
feat#52-graph
Aug 22, 2025
Merged

feat: 카카오 OAuth 로그인 플로우 완전 구현 및 인증 토큰 관리 시스템 구축#54
Dobbymin merged 19 commits intomainfrom
feat#52-graph

Conversation

@Dobbymin
Copy link
Copy Markdown
Contributor

📝 요약 (Summary)

카카오 OAuth 로그인 플로우를 완전히 구현하고, 인증 토큰 관리 시스템을 구축했습니다. 사용자가 카카오 로그인 버튼을 클릭하면 OAuth 인증을 거쳐 자동으로 메인 페이지로 리다이렉트되며, 토큰 만료 시 자동 갱신 기능을 제공합니다.

✅ 주요 변경 사항 (Key Changes)

  • 카카오 OAuth 로그인 플로우 완전 구현 (ticket → refreshToken → accessToken)
  • 인증 토큰 자동 갱신 시스템 구축 (401 오류 시 자동 refreshToken 사용)
  • OAuthRedirectPage 컴포넌트 리팩토링 및 React Hook 규칙 준수
  • API 경로 통합 및 일관성 유지
  • Vite 프록시 설정으로 CORS 문제 해결

💻 상세 구현 내용 (Implementation Details)

1. 인증 API 구현 ()

  • : 카카오 OAuth ticket을 refreshToken으로 교환
  • : refreshToken을 사용하여 accessToken 발급
  • API 경로 통합 및 타입 안전성 확보

2. 인증 훅 구현 ()

  • : ticket을 사용하여 refreshToken 발급
  • : refreshToken을 사용하여 accessToken 갱신
  • : 카카오 로그인 플로우 관리

3. OAuthRedirectPage 컴포넌트 개선 ()

  • React Hook 규칙 준수 (조건부 Hook 호출 제거)
  • 전체 로그인 플로우 자동화 (ticket → refreshToken → accessToken → 메인 페이지)
  • 에러 처리 및 사용자 피드백 개선

4. 인증 토큰 관리 시스템 ()

  • Axios 응답 인터셉터로 401 오류 자동 감지
  • refreshToken을 사용한 자동 토큰 갱신
  • 토큰 갱신 실패 시 자동 로그아웃 처리

5. 개발 환경 설정 ()

  • API 프록시 설정으로 CORS 문제 해결
  • 개발 환경에서 백엔드 API와의 원활한 통신

6. 인증 스토리지 개선 ()

  • JSON 파싱 및 문자열 변환 로직 수정
  • 토큰 저장 및 조회 기능 안정화

🚀 트러블 슈팅 (Trouble Shooting)

1. CORS 정책 문제

  • 문제: 프론트엔드(localhost:5173)에서 백엔드(localhost:8080)로의 요청이 CORS 정책에 의해 차단
  • 해결: Vite 프록시 설정으로 요청을 백엔드로 프록시 처리

2. React Hook 규칙 위반

  • 문제: OAuthRedirectPage에서 조건부 Hook 호출로 인한 오류
  • 해결: 모든 Hook을 컴포넌트 최상단에서 호출하고 조건부 로직을 Hook 내부로 이동

3. API 경로 중복 문제

  • 문제: 과 API 경로에 가 중복되어 404 오류 발생
  • 해결: API 경로에서 제거하고 에서만 관리

4. 토큰 자동 갱신 로직

  • 문제: 401 오류 시 사용자가 수동으로 다시 로그인해야 하는 불편함
  • 해결: Axios 인터셉터로 401 오류 자동 감지 및 refreshToken 사용한 자동 갱신

⚠️ 알려진 이슈 및 참고 사항 (Known Issues & Notes)

  • 백엔드 서버가 실행 중이어야 정상 작동합니다 (localhost:8080)
  • 카카오 OAuth 설정이 올바르게 되어 있어야 합니다
  • 토큰 만료 시간은 백엔드 설정에 따라 달라집니다
  • 개발 환경에서만 Vite 프록시가 작동합니다

📸 스크린샷 (Screenshots)

카카오 OAuth 로그인 플로우:

  • 카카오 로그인 버튼 클릭
  • OAuth 인증 페이지로 리다이렉트
  • 인증 완료 후 자동으로 메인 페이지로 이동
  • 토큰 만료 시 자동 갱신으로 사용자 경험 개선

#️⃣ 관련 이슈 (Related Issues)

@Dobbymin Dobbymin linked an issue Aug 22, 2025 that may be closed by this pull request
@Dobbymin Dobbymin self-assigned this Aug 22, 2025
Copy link
Copy Markdown

@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.

Summary of Changes

Hello @Dobbymin, 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 로그인 플로우를 완벽하게 구현하고, 사용자 인증 토큰을 효율적으로 관리하는 시스템을 구축하는 데 중점을 둡니다. 사용자가 카카오 로그인을 통해 원활하게 서비스에 접근하고, 토큰 만료 시에도 자동 갱신을 통해 끊김 없는 경험을 제공하는 것이 목표입니다. 또한, 개발 환경에서의 API 통신 문제를 해결하고, 메인 페이지의 사용자 경험을 개선하는 UI/UX 변경 사항도 포함되어 있습니다.

Highlights

  • 카카오 OAuth 로그인 플로우 완전 구현: 카카오 OAuth 로그인 플로우가 티켓(ticket)부터 리프레시 토큰(refreshToken), 그리고 액세스 토큰(accessToken)까지의 전체 과정을 포함하여 완전히 구현되었습니다.
  • 인증 토큰 자동 갱신 시스템 구축: Axios 인터셉터를 활용하여 401 오류 발생 시 리프레시 토큰을 사용하여 액세스 토큰을 자동으로 갱신하는 시스템이 구축되어 사용자 재로그인 없이 세션을 유지할 수 있게 되었습니다.
  • OAuthRedirectPage 컴포넌트 리팩토링 및 개선: OAuthRedirectPage 컴포넌트가 React Hook 규칙을 준수하도록 리팩토링되었으며, 로그인 처리 로직이 개선되어 안정성이 향상되었습니다.
  • API 경로 통합 및 CORS 문제 해결: API 경로의 일관성을 위해 '/api' 접두사 관리 방식이 변경되었고, Vite 프록시 설정을 통해 개발 환경에서의 CORS 문제가 효과적으로 해결되었습니다.
  • 메인 페이지 UI/UX 개선: 진단 완료 후 위험도 분석 섹션으로 자동 스크롤되는 기능이 추가되어 사용자가 결과를 즉시 확인할 수 있도록 UI/UX가 개선되었습니다.
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 in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

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 issue 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.

@Dobbymin Dobbymin merged commit 86635bc into main Aug 22, 2025
2 checks passed
@Dobbymin Dobbymin deleted the feat#52-graph branch August 22, 2025 16:30
Copy link
Copy Markdown

@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 로그인 흐름을 구현하고 토큰 관리 시스템을 구축하는 중요한 변경 사항을 포함하고 있습니다. 전반적으로 코드 구조가 개선되었고, Vite 프록시를 사용하여 CORS 문제를 해결한 점이 좋습니다. 몇 가지 개선점을 제안합니다. 토큰 갱신 로직의 상태 코드 확인 부분과 OAuth 리다이렉트 페이지의 비동기 처리 흐름에서 발견된 잠재적인 버그는 반드시 수정이 필요해 보입니다. 또한, API 응답 타입의 정확성, 불필요한 타입 단언 제거, 그리고 setTimeout 사용 대신 더 안정적인 방법을 사용하는 것을 제안했습니다. 이 피드백들이 코드의 안정성과 유지보수성을 높이는 데 도움이 되길 바랍니다.

Comment on lines 15 to +21
useEffect(() => {
if (isSuccess && data) {
authStorage.refreshToken.set(data.refreshToken);

navigate(ROUTER_PATH.ROOT);
if (data) {
const refreshToken = data.refreshToken;
authStorage.refreshToken.set(refreshToken);
navigate(ROUTER_PATH.ROOT, { replace: true });
}

if (isError) {
// 실패 시, 에러 처리 후 로그인 페이지로 이동
toast.error('카카오 로그인에 실패했습니다.');
navigate(ROUTER_PATH.LOGIN);
}
}, [isSuccess, isError, data, navigate]);

return <div>로그인 중입니다...</div>;
}, [data, navigate]);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

critical

현재 구현은 refreshToken을 받자마자 메인 페이지로 이동시켜 레이스 컨디션이 발생할 수 있습니다. accessToken을 받기 전에 페이지가 전환되어 accessToken이 제대로 저장되지 않을 수 있습니다.

이 문제를 해결하려면 navigate 호출을 분리해야 합니다. 현재 useEffect에서는 refreshToken만 저장하고, accessToken을 성공적으로 가져온 후 navigate를 호출하는 새로운 useEffect를 추가하는 것을 권장합니다.

예시:

// 기존 useEffect 수정
useEffect(() => {
  if (data) {
    authStorage.refreshToken.set(data.refreshToken);
  }
}, [data]);

// refreshTokenData를 받아오는 로직은 그대로 둡니다.
const { data: refreshTokenData, isLoading: isRefreshTokenLoading } = useGetRefreshToken();

// 새로운 useEffect 추가
useEffect(() => {
  if (refreshTokenData) {
    authStorage.accessToken.set(refreshTokenData.accessToken);
    navigate(ROUTER_PATH.ROOT, { replace: true });
  }
}, [refreshTokenData, navigate]);

위 코드처럼 로직을 분리하면 인증 흐름이 순차적으로 안전하게 실행됩니다.

async (error) => {
const originalRequest = error.config;

if (error.response.status === 500 && !originalRequest._retry) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

critical

응답 인터셉터가 500 상태 코드에 대해 토큰 갱신을 시도하도록 설정되어 있습니다. 일반적으로 토큰 만료 오류는 401 Unauthorized 상태 코드로 반환됩니다. 토큰 갱신 로직이 올바르게 동작하려면 이 부분을 401로 수정해야 합니다.

Suggested change
if (error.response.status === 500 && !originalRequest._retry) {
if (error.response.status === 401 && !originalRequest._retry) {

Comment on lines +12 to +13
export const sizePriceApi = async (): Promise<SizePriceApiResponse> => {
const response = await fetchInstance.get<SizePriceApiResponse>(SIZE_PRICE_API_PATH);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

sizePriceApi 함수의 반환 타입이 단일 SizePriceApiResponse 객체에 대한 Promise로 지정되어 있습니다. 하지만 그래프에 사용되는 sizePriceIndex와 같은 API 엔드포인트는 일반적으로 데이터 포인트의 배열을 반환합니다. 만약 API가 배열을 반환한다면, 타입 불일치로 인해 런타임 오류가 발생할 수 있습니다. 실제 API 응답을 확인하고, 필요한 경우 반환 타입을 Promise<SizePriceApiResponse[]>로 수정해주세요.

Suggested change
export const sizePriceApi = async (): Promise<SizePriceApiResponse> => {
const response = await fetchInstance.get<SizePriceApiResponse>(SIZE_PRICE_API_PATH);
export const sizePriceApi = async (): Promise<SizePriceApiResponse[]> => {
const response = await fetchInstance.get<SizePriceApiResponse[]>(SIZE_PRICE_API_PATH);

Comment on lines +50 to +61
setTimeout(() => {
if (scrollToRiskAnalysis.current) {
const element = scrollToRiskAnalysis.current;
const elementTop = element.offsetTop;
const offset = 80;

window.scrollTo({
top: elementTop - offset,
behavior: 'smooth',
});
}
}, 100);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

setTimeout을 사용하여 스크롤을 지연시키는 방식은 렌더링 완료를 고정된 시간에 의존하기 때문에 불안정할 수 있습니다. 타임아웃 없이도 스크롤이 올바르게 작동해야 합니다. 만약 레이아웃 계산을 기다리는 등 특별한 이유가 있다면, 브라우저의 렌더링 주기에 더 잘 맞는 requestAnimationFrame을 사용하거나, 주석으로 타임아웃의 필요성을 설명하는 것을 고려해보세요.

      // 진단 완료 후 위험도 분석 섹션으로 스크롤
      if (scrollToRiskAnalysis.current) {
        const element = scrollToRiskAnalysis.current;
        const elementTop = element.offsetTop;
        const offset = 80;

        window.scrollTo({
          top: elementTop - offset,
          behavior: 'smooth',
        });
      }

<div className='flex min-h-screen w-full flex-col'>
<div className='flex w-full gap-4'>
<InputSection />
<InputSection scrollToRiskAnalysis={riskAnalysisRef as React.RefObject<HTMLDivElement>} />
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

여기서 as React.RefObject<HTMLDivElement> 타입 단언은 불필요합니다. riskAnalysisRef는 이미 prop이 요구하는 타입인 React.RefObject<HTMLDivElement> 타입이므로, 이 단언은 안전하게 제거할 수 있습니다.

Suggested change
<InputSection scrollToRiskAnalysis={riskAnalysisRef as React.RefObject<HTMLDivElement>} />
<InputSection scrollToRiskAnalysis={riskAnalysisRef} />

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.

[기능 구현] 그래프 api 구현

1 participant