feat: 카카오 OAuth 로그인 플로우 완전 구현 및 인증 토큰 관리 시스템 구축#54
Conversation
There was a problem hiding this comment.
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
-
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. ↩
There was a problem hiding this comment.
Code Review
이번 PR은 카카오 OAuth 로그인 흐름을 구현하고 토큰 관리 시스템을 구축하는 중요한 변경 사항을 포함하고 있습니다. 전반적으로 코드 구조가 개선되었고, Vite 프록시를 사용하여 CORS 문제를 해결한 점이 좋습니다. 몇 가지 개선점을 제안합니다. 토큰 갱신 로직의 상태 코드 확인 부분과 OAuth 리다이렉트 페이지의 비동기 처리 흐름에서 발견된 잠재적인 버그는 반드시 수정이 필요해 보입니다. 또한, API 응답 타입의 정확성, 불필요한 타입 단언 제거, 그리고 setTimeout 사용 대신 더 안정적인 방법을 사용하는 것을 제안했습니다. 이 피드백들이 코드의 안정성과 유지보수성을 높이는 데 도움이 되길 바랍니다.
| 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]); |
There was a problem hiding this comment.
현재 구현은 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) { |
There was a problem hiding this comment.
| export const sizePriceApi = async (): Promise<SizePriceApiResponse> => { | ||
| const response = await fetchInstance.get<SizePriceApiResponse>(SIZE_PRICE_API_PATH); |
There was a problem hiding this comment.
sizePriceApi 함수의 반환 타입이 단일 SizePriceApiResponse 객체에 대한 Promise로 지정되어 있습니다. 하지만 그래프에 사용되는 sizePriceIndex와 같은 API 엔드포인트는 일반적으로 데이터 포인트의 배열을 반환합니다. 만약 API가 배열을 반환한다면, 타입 불일치로 인해 런타임 오류가 발생할 수 있습니다. 실제 API 응답을 확인하고, 필요한 경우 반환 타입을 Promise<SizePriceApiResponse[]>로 수정해주세요.
| 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); |
| setTimeout(() => { | ||
| if (scrollToRiskAnalysis.current) { | ||
| const element = scrollToRiskAnalysis.current; | ||
| const elementTop = element.offsetTop; | ||
| const offset = 80; | ||
|
|
||
| window.scrollTo({ | ||
| top: elementTop - offset, | ||
| behavior: 'smooth', | ||
| }); | ||
| } | ||
| }, 100); |
There was a problem hiding this comment.
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>} /> |
There was a problem hiding this comment.
여기서 as React.RefObject<HTMLDivElement> 타입 단언은 불필요합니다. riskAnalysisRef는 이미 prop이 요구하는 타입인 React.RefObject<HTMLDivElement> 타입이므로, 이 단언은 안전하게 제거할 수 있습니다.
| <InputSection scrollToRiskAnalysis={riskAnalysisRef as React.RefObject<HTMLDivElement>} /> | |
| <InputSection scrollToRiskAnalysis={riskAnalysisRef} /> |
📝 요약 (Summary)
카카오 OAuth 로그인 플로우를 완전히 구현하고, 인증 토큰 관리 시스템을 구축했습니다. 사용자가 카카오 로그인 버튼을 클릭하면 OAuth 인증을 거쳐 자동으로 메인 페이지로 리다이렉트되며, 토큰 만료 시 자동 갱신 기능을 제공합니다.
✅ 주요 변경 사항 (Key Changes)
💻 상세 구현 내용 (Implementation Details)
1. 인증 API 구현 ()
2. 인증 훅 구현 ()
3. OAuthRedirectPage 컴포넌트 개선 ()
4. 인증 토큰 관리 시스템 ()
5. 개발 환경 설정 ()
6. 인증 스토리지 개선 ()
🚀 트러블 슈팅 (Trouble Shooting)
1. CORS 정책 문제
2. React Hook 규칙 위반
3. API 경로 중복 문제
4. 토큰 자동 갱신 로직
📸 스크린샷 (Screenshots)
카카오 OAuth 로그인 플로우:
#️⃣ 관련 이슈 (Related Issues)