Skip to content

Commit 52de439

Browse files
authored
Merge pull request #209 from Umc8th-Snack/feat/settingchange/#208
[Feat] 소셜/일반 로그인 분기
2 parents 09b2135 + 488ef4b commit 52de439

7 files changed

Lines changed: 149 additions & 51 deletions

File tree

src/pages/auth/AuthSuccessPage.tsx

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import { useEffect, useState } from 'react';
22
import { useNavigate, useSearchParams } from 'react-router-dom';
33

44
import { userApi } from '@/shared/apis/user';
5-
import { useAuth } from '@/shared/context/AuthContext';
5+
import { LOGIN_METHOD_STORAGE_KEY, LOGIN_PROVIDER_HINT_KEY } from '@/shared/constants/authConstants';
6+
import { type LoginMethod, useAuth } from '@/shared/context/AuthContext';
67
import { tokenUtils } from '@/shared/utils/auth';
78

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

1415
useEffect(() => {
16+
const toLoginMethod = (value: string | null | undefined): LoginMethod | null => {
17+
if (value === 'email' || value === 'kakao' || value === 'google' || value === 'unknown') {
18+
return value;
19+
}
20+
return null;
21+
};
22+
23+
const determineLoginMethod = (): LoginMethod => {
24+
const providerFromParam = toLoginMethod(searchParams.get('provider'));
25+
26+
const providerHintRaw = sessionStorage.getItem(LOGIN_PROVIDER_HINT_KEY);
27+
const providerFromHint = toLoginMethod(providerHintRaw);
28+
if (providerHintRaw) {
29+
sessionStorage.removeItem(LOGIN_PROVIDER_HINT_KEY);
30+
}
31+
32+
const storedMethod = toLoginMethod(localStorage.getItem(LOGIN_METHOD_STORAGE_KEY));
33+
34+
const resolvedMethod = providerFromParam ?? providerFromHint ?? storedMethod ?? 'unknown';
35+
console.log('🧭 [AUTH SUCCESS] 로그인 방식 판별:', resolvedMethod);
36+
return resolvedMethod;
37+
};
38+
1539
const handleAuthSuccess = async () => {
1640
console.log('🔄 [AUTH SUCCESS] OAuth 콜백 처리 시작');
1741
console.log('📝 [AUTH SUCCESS] URL 파라미터:', Object.fromEntries(searchParams));
1842

43+
const loginMethod = determineLoginMethod();
44+
1945
// URL 파라미터에서 에러 확인
2046
const error = searchParams.get('error');
2147
if (error) {
@@ -71,14 +97,12 @@ const AuthSuccessPage = () => {
7197

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

84108
console.log('🏠 [AUTH SUCCESS] 홈으로 이동');
@@ -97,7 +121,7 @@ const AuthSuccessPage = () => {
97121
nickname: userInfo.nickname,
98122
email: userInfo.email,
99123
};
100-
login(existingToken, userData);
124+
login(existingToken, userData, loginMethod);
101125
void navigate('/', { replace: true });
102126
return;
103127
} catch (retryError) {

src/pages/settings/components/SettingsDropdown/SettingsDropdown.tsx

Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ interface SettingsDropdownProps {
1717
const SettingsDropdown = ({ open, setOpen, onShowConsentModal }: SettingsDropdownProps) => {
1818
const dropdownRef = useRef<HTMLDivElement>(null!);
1919
const navigate = useNavigate();
20-
const { logout: authLogout } = useAuth();
20+
const { logout: authLogout, loginMethod } = useAuth();
2121
const logoutMutation = useLogout();
2222

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

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

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

38-
// 홈페이지로 리다이렉트
3936
console.log('🏠 [SETTINGS] 홈페이지로 리다이렉트');
4037
void navigate('/');
4138
} catch (error) {
42-
// 에러가 발생해도 로컬 상태는 정리
4339
console.error('❌ [SETTINGS] 로그아웃 중 오류 발생:', error);
4440
console.log('🧹 [SETTINGS] 오류 발생 시에도 로컬 상태 정리');
4541
authLogout();
4642
void navigate('/');
4743
}
4844
};
4945

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

5348
if (!open) return null;
5449

@@ -59,24 +54,30 @@ const SettingsDropdown = ({ open, setOpen, onShowConsentModal }: SettingsDropdow
5954
{settingsData.map((section, i) => (
6055
<div key={section.category} className={i === 0 ? '' : 'mt-6'}>
6156
<h3 className="text-20px-medium text-black-70 mb-4 pl-3">{section.category}</h3>
62-
<ul className="flex flex-col gap-1.5 pl-3">
57+
<ul className="flex flex-col gap-1.5 pr-3 pl-3">
6358
{section.items.map((item) => (
6459
<li key={item.label}>
65-
<button
66-
onClick={() => {
67-
setOpen(false);
68-
if (item.label === '정보 동의 설정') {
69-
onShowConsentModal?.();
70-
} else if (item.path) {
71-
void navigate(item.path);
72-
} else if (item.onClick) {
73-
void item.onClick();
74-
}
75-
}}
76-
className="text-18px-medium text-black-50 w-full cursor-pointer text-left transition-colors hover:text-black"
77-
>
78-
{item.label}
79-
</button>
60+
{item.kind === 'info' ? (
61+
<p className="text-16px-regular text-black-40 py-1 leading-relaxed">
62+
{item.label}
63+
</p>
64+
) : (
65+
<button
66+
onClick={() => {
67+
setOpen(false);
68+
if (item.label === '정보 동의 설정') {
69+
onShowConsentModal?.();
70+
} else if (item.path) {
71+
void navigate(item.path);
72+
} else if (item.onClick) {
73+
void item.onClick();
74+
}
75+
}}
76+
className="text-18px-medium text-black-50 w-full cursor-pointer text-left transition-colors hover:text-black"
77+
>
78+
{item.label}
79+
</button>
80+
)}
8081
</li>
8182
))}
8283
</ul>
Lines changed: 61 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,71 @@
11
import type { NavigateFunction } from 'react-router-dom';
22

3-
export const getSettingsData = (navigate: NavigateFunction, handleLogout?: () => Promise<void>) => [
4-
{
5-
category: '계정',
6-
items: [
7-
{ label: '비밀번호 변경', path: '/settings/password' },
8-
{ label: '이메일 변경', path: '/settings/email' },
9-
],
10-
},
11-
{
3+
import { LOGIN_METHOD_STORAGE_KEY } from '@/shared/constants/authConstants';
4+
import type { LoginMethod } from '@/shared/context/AuthContext';
5+
6+
type SettingsItemKind = 'button' | 'info';
7+
8+
export interface SettingsItem {
9+
label: string;
10+
path?: string;
11+
onClick?: () => void;
12+
kind?: SettingsItemKind;
13+
}
14+
15+
export interface SettingsSection {
16+
category: string;
17+
items: SettingsItem[];
18+
}
19+
20+
interface GetSettingsDataParams {
21+
navigate: NavigateFunction;
22+
loginMethod?: LoginMethod | null;
23+
handleLogout?: () => Promise<void>;
24+
}
25+
26+
const isSocialLogin = (method?: LoginMethod | null) => method === 'kakao' || method === 'google';
27+
28+
export const getSettingsData = ({ navigate, loginMethod, handleLogout }: GetSettingsDataParams): SettingsSection[] => {
29+
const sections: SettingsSection[] = [];
30+
31+
if (!isSocialLogin(loginMethod)) {
32+
sections.push({
33+
category: '계정',
34+
items: [
35+
{ label: '비밀번호 변경', path: '/settings/password' },
36+
{ label: '이메일 변경', path: '/settings/email' },
37+
],
38+
});
39+
} else {
40+
const providerLabel = loginMethod === 'kakao' ? '카카오' : loginMethod === 'google' ? 'Google' : '소셜 로그인';
41+
sections.push({
42+
category: '계정',
43+
items: [{ label: `${providerLabel} 계정으로 로그인 중이에요.`, kind: 'info' }],
44+
});
45+
}
46+
47+
sections.push({
1248
category: '기타',
1349
items: [
1450
{ label: '정보 동의 설정' },
1551
{ label: '회원 탈퇴', path: '/settings/delete' },
1652
{
1753
label: '로그아웃',
18-
onClick:
19-
handleLogout ||
20-
(async () => {
21-
// 폴백: handleLogout이 전달되지 않은 경우
22-
localStorage.removeItem('accessToken');
23-
void navigate('/');
24-
}),
54+
onClick: handleLogout
55+
? () => {
56+
void handleLogout();
57+
}
58+
: () => {
59+
void (async () => {
60+
// 폴백: handleLogout이 전달되지 않은 경우
61+
localStorage.removeItem('accessToken');
62+
localStorage.removeItem(LOGIN_METHOD_STORAGE_KEY);
63+
void navigate('/');
64+
})();
65+
},
2566
},
2667
],
27-
},
28-
];
68+
});
69+
70+
return sections;
71+
};

src/shared/components/modal/loginModal/EmailLoginForm.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React, { useState } from 'react';
22
import { useNavigate } from 'react-router-dom';
33

4+
import { LOGIN_PROVIDER_HINT_KEY } from '@/shared/constants/authConstants';
45
import { useAuth } from '@/shared/context/AuthContext';
56
import { useLogin } from '@/shared/hooks/useAuth';
67

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

3738
if (response.token) {
3839
console.log('🔐 [LOGIN FORM] AuthContext login 호출');
39-
login(response.token, response.data);
40+
sessionStorage.removeItem(LOGIN_PROVIDER_HINT_KEY);
41+
login(response.token, response.data, 'email');
4042
} else {
4143
console.error('❌ [LOGIN FORM] Access Token을 찾을 수 없습니다.');
4244
}

src/shared/components/modal/loginModal/LoginModal.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import GoogleIcon from '@/assets/GoogleIcon.svg?react';
44
import XIcon from '@/assets/XIcon.svg?react';
55
import KakaoIcon from '@/shared/assets/icons/logo-kakao.svg?react';
66
import SnackIcon from '@/shared/assets/snack.svg?react';
7+
import { LOGIN_PROVIDER_HINT_KEY } from '@/shared/constants/authConstants';
78
import { getGoogleAuthUrl } from '@/shared/utils/googleAuth';
89
import { redirectToKakaoLogin } from '@/shared/utils/kakaoAuth';
910

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

2526
const handleEmailLoginClick = () => {
27+
sessionStorage.removeItem(LOGIN_PROVIDER_HINT_KEY);
2628
setModalMode('emailLogin');
2729
};
2830

2931
const handleEmailSignupClick = () => {
32+
sessionStorage.removeItem(LOGIN_PROVIDER_HINT_KEY);
3033
setModalMode('emailSignup');
3134
};
3235

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

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

4246
const handleGoogleLogin = () => {
4347
console.log('🔵 [LOGIN MODAL] Google 로그인 버튼 클릭');
48+
sessionStorage.setItem(LOGIN_PROVIDER_HINT_KEY, 'google');
4449
// Google OAuth 페이지로 리다이렉트
4550
window.location.href = getGoogleAuthUrl();
4651
};
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export const LOGIN_METHOD_STORAGE_KEY = 'loginMethod';
2+
export const LOGIN_PROVIDER_HINT_KEY = 'loginProviderHint';

0 commit comments

Comments
 (0)