Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 30 additions & 6 deletions src/pages/auth/AuthSuccessPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { useEffect, useState } from 'react';
import { useNavigate, useSearchParams } from 'react-router-dom';

import { userApi } from '@/shared/apis/user';
import { useAuth } from '@/shared/context/AuthContext';
import { LOGIN_METHOD_STORAGE_KEY, LOGIN_PROVIDER_HINT_KEY } from '@/shared/constants/authConstants';
import { type LoginMethod, useAuth } from '@/shared/context/AuthContext';
import { tokenUtils } from '@/shared/utils/auth';

const AuthSuccessPage = () => {
Expand All @@ -12,10 +13,35 @@ const AuthSuccessPage = () => {
const [errorMessage, setErrorMessage] = useState<string>('');

useEffect(() => {
const toLoginMethod = (value: string | null | undefined): LoginMethod | null => {
if (value === 'email' || value === 'kakao' || value === 'google' || value === 'unknown') {
return value;
}
return null;
};

const determineLoginMethod = (): LoginMethod => {
const providerFromParam = toLoginMethod(searchParams.get('provider'));

const providerHintRaw = sessionStorage.getItem(LOGIN_PROVIDER_HINT_KEY);
const providerFromHint = toLoginMethod(providerHintRaw);
if (providerHintRaw) {
sessionStorage.removeItem(LOGIN_PROVIDER_HINT_KEY);
}

const storedMethod = toLoginMethod(localStorage.getItem(LOGIN_METHOD_STORAGE_KEY));

const resolvedMethod = providerFromParam ?? providerFromHint ?? storedMethod ?? 'unknown';
console.log('🧭 [AUTH SUCCESS] 로그인 방식 판별:', resolvedMethod);
return resolvedMethod;
};

const handleAuthSuccess = async () => {
console.log('🔄 [AUTH SUCCESS] OAuth 콜백 처리 시작');
console.log('📝 [AUTH SUCCESS] URL 파라미터:', Object.fromEntries(searchParams));

const loginMethod = determineLoginMethod();

// URL 파라미터에서 에러 확인
const error = searchParams.get('error');
if (error) {
Expand Down Expand Up @@ -71,14 +97,12 @@ const AuthSuccessPage = () => {

// 토큰이 있으면 login 함수 호출, 없으면 사용자 정보만으로도 처리
if (accessToken || currentToken) {
login(accessToken || currentToken || '', userData);
login(accessToken || currentToken || '', userData, loginMethod);
console.log('✅ [AUTH SUCCESS] 로그인 완료 (토큰 저장)');
} else {
// HttpOnly 쿠키 방식인 경우, 토큰 없이 사용자 정보만 저장
// 이 경우 AuthContext를 수정해야 할 수도 있음
console.log('✅ [AUTH SUCCESS] 로그인 완료 (HttpOnly 쿠키 방식)');
// 임시로 빈 토큰으로 처리 (나중에 AuthContext 수정 필요)
login('http-only-cookie', userData);
login('http-only-cookie', userData, loginMethod);
}

console.log('🏠 [AUTH SUCCESS] 홈으로 이동');
Expand All @@ -97,7 +121,7 @@ const AuthSuccessPage = () => {
nickname: userInfo.nickname,
email: userInfo.email,
};
login(existingToken, userData);
login(existingToken, userData, loginMethod);
void navigate('/', { replace: true });
return;
} catch (retryError) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ interface SettingsDropdownProps {
const SettingsDropdown = ({ open, setOpen, onShowConsentModal }: SettingsDropdownProps) => {
const dropdownRef = useRef<HTMLDivElement>(null!);
const navigate = useNavigate();
const { logout: authLogout } = useAuth();
const { logout: authLogout, loginMethod } = useAuth();
const logoutMutation = useLogout();

useOutsideClick(dropdownRef, () => setOpen(false));
Expand All @@ -26,29 +26,24 @@ const SettingsDropdown = ({ open, setOpen, onShowConsentModal }: SettingsDropdow
console.log('🚪 [SETTINGS] 로그아웃 버튼 클릭');

try {
// 서버 로그아웃 API 호출 (refresh token 무효화)
console.log('📡 [SETTINGS] 서버 로그아웃 API 호출 시작');
await logoutMutation.mutateAsync();
console.log('✅ [SETTINGS] 서버 로그아웃 API 성공');

// 로컬 상태 정리 (토큰 제거, 사용자 정보 삭제)
console.log('🧹 [SETTINGS] 로컬 상태 정리 시작');
authLogout();

// 홈페이지로 리다이렉트
console.log('🏠 [SETTINGS] 홈페이지로 리다이렉트');
void navigate('/');
} catch (error) {
// 에러가 발생해도 로컬 상태는 정리
console.error('❌ [SETTINGS] 로그아웃 중 오류 발생:', error);
console.log('🧹 [SETTINGS] 오류 발생 시에도 로컬 상태 정리');
authLogout();
void navigate('/');
}
};

// settingsData를 컴포넌트 내부에서 생성하여 handleLogout 함수를 전달
const settingsData = getSettingsData(navigate, handleLogout);
const settingsData = getSettingsData({ navigate, handleLogout, loginMethod });

if (!open) return null;

Expand All @@ -59,24 +54,30 @@ const SettingsDropdown = ({ open, setOpen, onShowConsentModal }: SettingsDropdow
{settingsData.map((section, i) => (
<div key={section.category} className={i === 0 ? '' : 'mt-6'}>
<h3 className="text-20px-medium text-black-70 mb-4 pl-3">{section.category}</h3>
<ul className="flex flex-col gap-1.5 pl-3">
<ul className="flex flex-col gap-1.5 pr-3 pl-3">
{section.items.map((item) => (
<li key={item.label}>
<button
onClick={() => {
setOpen(false);
if (item.label === '정보 동의 설정') {
onShowConsentModal?.();
} else if (item.path) {
void navigate(item.path);
} else if (item.onClick) {
void item.onClick();
}
}}
className="text-18px-medium text-black-50 w-full cursor-pointer text-left transition-colors hover:text-black"
>
{item.label}
</button>
{item.kind === 'info' ? (
<p className="text-16px-regular text-black-40 py-1 leading-relaxed">
{item.label}
</p>
) : (
<button
onClick={() => {
setOpen(false);
if (item.label === '정보 동의 설정') {
onShowConsentModal?.();
} else if (item.path) {
void navigate(item.path);
} else if (item.onClick) {
void item.onClick();
}
}}
className="text-18px-medium text-black-50 w-full cursor-pointer text-left transition-colors hover:text-black"
>
{item.label}
</button>
)}
</li>
))}
</ul>
Expand Down
79 changes: 61 additions & 18 deletions src/pages/settings/components/SettingsDropdown/settingsData.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,71 @@
import type { NavigateFunction } from 'react-router-dom';

export const getSettingsData = (navigate: NavigateFunction, handleLogout?: () => Promise<void>) => [
{
category: '계정',
items: [
{ label: '비밀번호 변경', path: '/settings/password' },
{ label: '이메일 변경', path: '/settings/email' },
],
},
{
import { LOGIN_METHOD_STORAGE_KEY } from '@/shared/constants/authConstants';
import type { LoginMethod } from '@/shared/context/AuthContext';

type SettingsItemKind = 'button' | 'info';

export interface SettingsItem {
label: string;
path?: string;
onClick?: () => void;
kind?: SettingsItemKind;
}

export interface SettingsSection {
category: string;
items: SettingsItem[];
}

interface GetSettingsDataParams {
navigate: NavigateFunction;
loginMethod?: LoginMethod | null;
handleLogout?: () => Promise<void>;
}

const isSocialLogin = (method?: LoginMethod | null) => method === 'kakao' || method === 'google';

export const getSettingsData = ({ navigate, loginMethod, handleLogout }: GetSettingsDataParams): SettingsSection[] => {
const sections: SettingsSection[] = [];

if (!isSocialLogin(loginMethod)) {
sections.push({
category: '계정',
items: [
{ label: '비밀번호 변경', path: '/settings/password' },
{ label: '이메일 변경', path: '/settings/email' },
],
});
} else {
const providerLabel = loginMethod === 'kakao' ? '카카오' : loginMethod === 'google' ? 'Google' : '소셜 로그인';
sections.push({
category: '계정',
items: [{ label: `${providerLabel} 계정으로 로그인 중이에요.`, kind: 'info' }],
});
}

sections.push({
category: '기타',
items: [
{ label: '정보 동의 설정' },
{ label: '회원 탈퇴', path: '/settings/delete' },
{
label: '로그아웃',
onClick:
handleLogout ||
(async () => {
// 폴백: handleLogout이 전달되지 않은 경우
localStorage.removeItem('accessToken');
void navigate('/');
}),
onClick: handleLogout
? () => {
void handleLogout();
}
: () => {
void (async () => {
// 폴백: handleLogout이 전달되지 않은 경우
localStorage.removeItem('accessToken');
localStorage.removeItem(LOGIN_METHOD_STORAGE_KEY);
void navigate('/');
})();
},
},
],
},
];
});

return sections;
};
4 changes: 3 additions & 1 deletion src/shared/components/modal/loginModal/EmailLoginForm.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';

import { LOGIN_PROVIDER_HINT_KEY } from '@/shared/constants/authConstants';
import { useAuth } from '@/shared/context/AuthContext';
import { useLogin } from '@/shared/hooks/useAuth';

Expand Down Expand Up @@ -36,7 +37,8 @@ const EmailLoginForm = ({ onClose }: EmailLoginFormProps) => {

if (response.token) {
console.log('🔐 [LOGIN FORM] AuthContext login 호출');
login(response.token, response.data);
sessionStorage.removeItem(LOGIN_PROVIDER_HINT_KEY);
login(response.token, response.data, 'email');
} else {
console.error('❌ [LOGIN FORM] Access Token을 찾을 수 없습니다.');
}
Expand Down
5 changes: 5 additions & 0 deletions src/shared/components/modal/loginModal/LoginModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import GoogleIcon from '@/assets/GoogleIcon.svg?react';
import XIcon from '@/assets/XIcon.svg?react';
import KakaoIcon from '@/shared/assets/icons/logo-kakao.svg?react';
import SnackIcon from '@/shared/assets/snack.svg?react';
import { LOGIN_PROVIDER_HINT_KEY } from '@/shared/constants/authConstants';
import { getGoogleAuthUrl } from '@/shared/utils/googleAuth';
import { redirectToKakaoLogin } from '@/shared/utils/kakaoAuth';

Expand All @@ -23,10 +24,12 @@ const LoginModal = ({ isOpen, onClose }: ModalProps) => {
const [modalMode, setModalMode] = useState<ModalMode>('social');

const handleEmailLoginClick = () => {
sessionStorage.removeItem(LOGIN_PROVIDER_HINT_KEY);
setModalMode('emailLogin');
};

const handleEmailSignupClick = () => {
sessionStorage.removeItem(LOGIN_PROVIDER_HINT_KEY);
setModalMode('emailSignup');
};

Expand All @@ -36,11 +39,13 @@ const LoginModal = ({ isOpen, onClose }: ModalProps) => {

const handleKakaoLogin = () => {
console.log('🟡 [LOGIN MODAL] Kakao 로그인 버튼 클릭');
sessionStorage.setItem(LOGIN_PROVIDER_HINT_KEY, 'kakao');
redirectToKakaoLogin();
};

const handleGoogleLogin = () => {
console.log('🔵 [LOGIN MODAL] Google 로그인 버튼 클릭');
sessionStorage.setItem(LOGIN_PROVIDER_HINT_KEY, 'google');
// Google OAuth 페이지로 리다이렉트
window.location.href = getGoogleAuthUrl();
};
Expand Down
2 changes: 2 additions & 0 deletions src/shared/constants/authConstants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const LOGIN_METHOD_STORAGE_KEY = 'loginMethod';
export const LOGIN_PROVIDER_HINT_KEY = 'loginProviderHint';
Loading
Loading