[기능 구현] 카카오 OAuth 리다이렉트 페이지 및 인증 API 연동 구현#45
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 인증 플로우를 프론트엔드에서 완전히 구현하여 사용자가 카카오 로그인을 통해 안전하게 인증을 처리하고 애플리케이션의 메인 페이지로 이동할 수 있도록 합니다. 이는 OAuth 리다이렉트 페이지, 인증 API 연동, 그리고 관련 라우팅 및 에러 처리를 포함합니다.
Highlights
- OAuth 리다이렉트 페이지 구현: 카카오 인증 후
/oauth/redirect경로로 리다이렉트되는 전용 페이지를 생성하고, URL 파라미터(ticket, error)를 처리하여 인증 티켓을 검증하거나 OAuth 에러를 표시합니다. - 인증 API 모듈 및 훅 개발: 백엔드
/api/auth/ticket엔드포인트와 통신하여 인증 티켓을 검증하는 API 모듈(get-ticket.api.ts)과 React Query를 활용한useGetAuthTicket훅을 구현하여 안전하고 효율적인 데이터 페칭을 가능하게 합니다. - 카카오 로그인 버튼 개선: 카카오 로그인 버튼 클릭 시 OAuth 서버로 명시적인 리다이렉트 URL을 전달하도록 수정하여, 인증 플로우의 안정성을 높였습니다.
- 라우팅 및 상태 관리: 새로운
/oauth/redirect라우트를 추가하고, React Query를 이용한 로딩 및 에러 상태 관리, 그리고localStorage에refreshToken을 저장하는 로직을 포함합니다.
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 인증 흐름을 구현하기 위한 리다이렉트 페이지와 API 연동을 추가합니다. 전반적으로 기능 구현은 잘 이루어졌지만, 코드의 유지보수성, 보안, 그리고 React Query의 효율적인 사용 측면에서 몇 가지 개선점을 제안합니다. 특히, localStorage에 토큰을 저장하는 방식은 보안에 취약할 수 있으므로 httpOnly 쿠키 사용을 강력히 권장합니다. 또한, 여러 useEffect 훅을 통합하고 React Query의 enabled 옵션을 활용하여 코드를 더 깔끔하고 효율적으로 만들 수 있습니다.
|
|
||
| export const fetchInstance = initInstance({ | ||
| baseURL: BASE_URL, | ||
| withCredentials: true, |
| console.log('OAuth 인증 성공!', authData); | ||
|
|
||
| // refreshToken을 localStorage에 저장 | ||
| localStorage.setItem('refreshToken', authData.refreshToken); |
| export const KakaoLoginButton = () => { | ||
| const kakaoLogin = () => { | ||
| // OAuth 서버로 리다이렉트하되, 리다이렉트 URL을 명시적으로 지정 | ||
| const redirectUri = encodeURIComponent(`${window.location.origin}/oauth/redirect`); |
There was a problem hiding this comment.
리다이렉트 URL 경로가 하드코딩되어 있습니다. ROUTER_PATH 상수를 사용하면 코드의 일관성과 유지보수성을 높일 수 있습니다. import { ROUTER_PATH } from '@/shared'를 추가하고 ROUTER_PATH.OAUTH_REDIRECT를 사용하세요.
| const redirectUri = encodeURIComponent(`${window.location.origin}/oauth/redirect`); | |
| const redirectUri = encodeURIComponent(`${window.location.origin}${ROUTER_PATH.OAUTH_REDIRECT}`); |
|
|
||
| export const GET_TICKET_API_PATH = '/api/auth/ticket'; | ||
|
|
||
| interface GetTicketApiResponse { |
| export const useGetAuthTicket = (ticket: string) => { | ||
| return useQuery({ | ||
| queryKey: GetAuthTicketQueryKey.ticket(ticket), | ||
| queryFn: () => getTicketApi(ticket), | ||
| }); | ||
| }; |
There was a problem hiding this comment.
useQuery의 enabled 옵션을 사용하여 조건부로 쿼리를 실행할 수 있도록 useGetAuthTicket 훅을 수정하는 것을 권장합니다. 이렇게 하면 불필요한 API 호출을 방지하고 코드를 더 명확하게 만들 수 있습니다. useQuery의 옵션을 받을 수 있도록 훅의 인자를 확장해주세요. 이 변경은 get-ticket.api.ts에서 GetTicketApiResponse 타입을 export한 후에 적용해야 합니다.
| export const useGetAuthTicket = (ticket: string) => { | |
| return useQuery({ | |
| queryKey: GetAuthTicketQueryKey.ticket(ticket), | |
| queryFn: () => getTicketApi(ticket), | |
| }); | |
| }; | |
| export const useGetAuthTicket = ( | |
| ticket: string, | |
| options?: Omit<UseQueryOptions<GetTicketApiResponse>, 'queryKey' | 'queryFn'>, | |
| ) => { | |
| return useQuery({ | |
| queryKey: GetAuthTicketQueryKey.ticket(ticket), | |
| queryFn: () => getTicketApi(ticket), | |
| ...options, | |
| }); | |
| }; |
| useEffect(() => { | ||
| if (oauthError) { | ||
| setError(errorDescription || `OAuth 인증 오류: ${oauthError}`); | ||
| } | ||
| }, [oauthError, errorDescription]); | ||
|
|
||
| // ticket이 없으면 에러 처리 | ||
| useEffect(() => { | ||
| if (!oauthError && !ticket) { | ||
| setError('인증 토큰이 없습니다. 다시 로그인해주세요.'); | ||
| } | ||
| }, [ticket, oauthError]); |
There was a problem hiding this comment.
두 개의 useEffect 훅이 모두 URL 파라미터를 검증하고 에러 상태를 설정하는 역할을 합니다. 이 로직을 하나의 useEffect로 통합하면 코드가 더 간결해지고, 상태 업데이트 로직을 한 곳에서 관리할 수 있어 잠재적인 race condition을 방지할 수 있습니다.
| useEffect(() => { | |
| if (oauthError) { | |
| setError(errorDescription || `OAuth 인증 오류: ${oauthError}`); | |
| } | |
| }, [oauthError, errorDescription]); | |
| // ticket이 없으면 에러 처리 | |
| useEffect(() => { | |
| if (!oauthError && !ticket) { | |
| setError('인증 토큰이 없습니다. 다시 로그인해주세요.'); | |
| } | |
| }, [ticket, oauthError]); | |
| useEffect(() => { | |
| if (oauthError) { | |
| setError(errorDescription || `OAuth 인증 오류: ${oauthError}`); | |
| } else if (!ticket) { | |
| setError('인증 토큰이 없습니다. 다시 로그인해주세요.'); | |
| } | |
| }, [oauthError, errorDescription, ticket]); |
| const shouldCallApi = !!ticket && !oauthError; | ||
| const { | ||
| data: authData, | ||
| error: apiError, | ||
| isLoading, | ||
| } = useGetAuthTicket(shouldCallApi ? ticket : ''); |
There was a problem hiding this comment.
useGetAuthTicket 훅을 호출할 때 삼항 연산자를 사용하여 빈 문자열을 전달하는 대신, React Query의 enabled 옵션을 사용하는 것이 더 효율적이고 명확한 방법입니다. 이렇게 하면 조건이 충족되지 않았을 때 불필요한 API 요청이 발생하는 것을 막을 수 있습니다. 이 변경을 적용하려면 useGetAuthTicket 훅의 수정이 필요합니다.
const { data: authData, error: apiError, isLoading } = useGetAuthTicket(ticket || '', {
enabled: !!ticket && !oauthError,
});
| // API 에러 처리 | ||
| useEffect(() => { | ||
| if (apiError) { | ||
| console.error('API 호출 중 오류:', apiError); |
| // 성공 시 처리 | ||
| useEffect(() => { | ||
| if (authData && !isLoading && !apiError && shouldCallApi) { | ||
| console.log('OAuth 인증 성공!', authData); |
| useEffect(() => { | ||
| if (authData && !isLoading && !apiError && shouldCallApi) { | ||
| console.log('OAuth 인증 성공!', authData); | ||
|
|
||
| // refreshToken을 localStorage에 저장 | ||
| localStorage.setItem('refreshToken', authData.refreshToken); | ||
|
|
||
| // 메인 페이지로 이동 | ||
| navigate(ROUTER_PATH.MAIN, { replace: true }); | ||
| } | ||
| }, [authData, isLoading, apiError, navigate, shouldCallApi]); |
There was a problem hiding this comment.
성공 처리 useEffect의 조건문(if (authData && !isLoading && !apiError && shouldCallApi))이 다소 복잡하고, 의존성 배열에 불필요한 항목이 포함되어 있습니다. useQuery의 data가 존재하면 isLoading은 false이고 error는 null인 상태이므로, if (authData)로 조건을 단순화할 수 있습니다. 의존성 배열도 [authData, navigate]로 줄일 수 있습니다.
// 성공 시 처리
useEffect(() => {
if (authData) {
console.log('OAuth 인증 성공!', authData);
// refreshToken을 localStorage에 저장
localStorage.setItem('refreshToken', authData.refreshToken);
// 메인 페이지로 이동
navigate(ROUTER_PATH.MAIN, { replace: true });
}
}, [authData, navigate]);
📝 요약 (Summary)
카카오 OAuth 인증 플로우를 완성하기 위해 리다이렉트 페이지와 인증 API 연동 기능을 구현했습니다. 사용자가 카카오 로그인 후 안전하게 인증을 처리하고 메인 페이지로 이동할 수 있도록 했습니다.
✅ 주요 변경 사항 (Key Changes)
💻 상세 구현 내용 (Implementation Details)
OAuth 리다이렉트 페이지 구현
/oauth/redirect- OAuth 인증 후 리다이렉트되는 전용 페이지ticket,error,error_description파라미터 추출 및 검증인증 API 모듈 구현
get-ticket.api.ts: 백엔드 API와 통신하여 티켓 검증getAuthTicket.ts: React Query를 활용한 인증 훅 구현카카오 로그인 버튼 개선
라우터 설정
/oauth/redirect경로를 Auth 라우트에 추가ROUTER_PATH.OAUTH_REDIRECT추가🚀 트러블 슈팅 (Trouble Shooting)
/api/auth/ticket로 설정되어 있으므로, 실제 서버와 일치하는지 확인 필요📸 스크린샷 (Screenshots)
[OAuth 리다이렉트 페이지 스크린샷을 여기에 추가해주세요]
#️⃣ 관련 이슈 (Related Issues)