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
41 changes: 22 additions & 19 deletions src/components/auth/LoginGuard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ interface LoginGuardProps {
}

export function LoginGuard({ children }: LoginGuardProps) {
const { tokens, setTokens, clearTokens, isLoggedIn } = useAuthStore();
const { tokens, setTokens, clearTokens, isLoggedIn, isLoading } = useAuthStore();
const { bridge, isAvailable } = useNativeBridge();
const {
data: userInfo,
Expand All @@ -29,6 +29,17 @@ export function LoginGuard({ children }: LoginGuardProps) {
const { data: activities } = useActivities(5);
const { data: storesData } = useMyStores({ page: 0, size: 10 });
const pathname = usePathname();

// 디버깅용 로그
console.log('🛡️ [LoginGuard] 렌더링, 상태:', {
tokens: !!tokens,
isLoggedIn,
isLoading,
userInfoLoading,
userInfo: !!userInfo,
userInfoError: !!userInfoError,
isAvailable
});

// 개발 모드에서 임시 로그인 (테스트용)
const handleDevLogin = () => {
Expand All @@ -48,24 +59,27 @@ export function LoginGuard({ children }: LoginGuardProps) {
window.location.reload();
};

// 로딩 중이거나 토큰이 없으면 적절한 UI 표시
if (userInfoLoading || (isLoggedIn === false && !tokens)) {
const loadingText = userInfoLoading
? "로그인 상태를 확인하는 중..."
: "로그인 화면으로 이동 중...";
// 사용자 정보 로딩 중일 때만 로딩 화면 표시 (토큰이 있을 때)
if (userInfoLoading && isLoggedIn && tokens) {
console.log('🛡️ [LoginGuard] 사용자 정보 로딩 화면 표시');

return (
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
<div className="text-center">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600 mx-auto mb-4"></div>
<p className="text-gray-600">{loadingText}</p>
<p className="text-gray-600">사용자 정보를 불러오는 중...</p>
</div>
</div>
);
}

// 토큰이 없고 로딩이 끝났으면 로그인 필요 화면 표시
// 토큰이 없거나 로그인 상태가 아니면 로그인 필요 화면 표시
if (!isLoggedIn || !tokens) {
console.log('🛡️ [LoginGuard] 로그인 필요 화면 표시:', {
isLoggedIn,
hasTokens: !!tokens,
isLoading
});
// 기존 로그인 필요 화면으로 바로 이동
return (
<div className="min-h-screen bg-gray-50 flex items-center justify-center px-4">
Expand Down Expand Up @@ -203,17 +217,6 @@ export function LoginGuard({ children }: LoginGuardProps) {
);
}

// 토큰이 있는데 여전히 사용자 정보 로딩 중
if (userInfoLoading && (isLoggedIn || tokens)) {
return (
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
<div className="text-center">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600 mx-auto mb-4"></div>
<p className="text-gray-600">사용자 정보를 불러오는 중...</p>
</div>
</div>
);
}

// 개발 환경에서 캐시 정보 표시를 위한 데이터
const cacheInfo = getCacheInfo();
Expand Down
26 changes: 26 additions & 0 deletions src/hooks/useAuth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,33 @@ export const useAuth = () => {

// 컴포넌트 마운트 시 토큰 초기화
useEffect(() => {
console.log('🔥 [useAuth] useEffect 시작, 현재 상태:', {
tokens: !!tokens,
isLoggedIn,
isLoading
});

// 즉시 초기화 시도
initializeTokens();

// fallback: 2초 후에도 여전히 로딩 중이면 강제 초기화
const fallbackTimer = setTimeout(() => {
const currentState = useAuthStore.getState();
console.log('🔥 [useAuth] Fallback 타이머 실행, 현재 상태:', {
tokens: !!currentState.tokens,
isLoggedIn: currentState.isLoggedIn,
isLoading: currentState.isLoading
});

if (currentState.isLoading) {
console.log('🔥 [useAuth] 여전히 로딩 중 - 강제 초기화');
currentState.initializeFromLocalStorage();
}
}, 2000);

return () => {
clearTimeout(fallbackTimer);
};
}, [initializeTokens]);


Expand Down
102 changes: 67 additions & 35 deletions src/store/useAuthStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,27 +14,20 @@ interface AuthState {
initializeFromLocalStorage: () => void;
}

// Store 생성
export const useAuthStore = create<AuthState>()(
persist(
(set) => ({
tokens: null,
isLoggedIn: false,
isLoading: true,

setTokens: (tokens) => {
set({
tokens,
isLoggedIn: tokens !== null,
isLoading: false,
});
},


// 로컬스토리지에서 accessToken 확인 및 자동 로그인
initializeFromLocalStorage: () => {
(set) => {
// 초기화 함수 정의
const initializeFromLocalStorage = () => {
console.log('🚀 [initializeFromLocalStorage] 시작');

try {
const accessToken = localStorage.getItem("accessToken");
console.log('🚀 [initializeFromLocalStorage] accessToken:', !!accessToken);

if (accessToken) {
console.log('🚀 [initializeFromLocalStorage] 토큰 발견, 로그인 상태로 설정');
const tokenObject = {
accessToken: accessToken,
refreshToken: "",
Expand All @@ -53,40 +46,79 @@ export const useAuthStore = create<AuthState>()(
Date.now().toString()
);
}
console.log('🚀 [initializeFromLocalStorage] 완료: 로그인됨');
} else {
console.log('🚀 [initializeFromLocalStorage] 토큰 없음, 로그아웃 상태로 설정');
set({ isLoading: false, isLoggedIn: false, tokens: null });
console.log('🚀 [initializeFromLocalStorage] 완료: 로그아웃됨');
}
} catch (error) {
console.error('토큰 초기화 실패:', error);
console.error('🚀 [initializeFromLocalStorage] 에러:', error);
set({ isLoading: false, isLoggedIn: false, tokens: null });
}
},
};

setLoading: (loading) => {
set({ isLoading: loading });
},
// 즉시 초기화 시도 (브라우저 환경에서만)
if (typeof window !== 'undefined') {
console.log('🏗️ [Store] 브라우저 환경 감지, 즉시 초기화 시도');
setTimeout(() => initializeFromLocalStorage(), 50);
}

clearTokens: () => {
localStorage.removeItem("accessToken");
localStorage.removeItem("auth-storage-timestamp");
set({
tokens: null,
isLoggedIn: false,
isLoading: false,
});
},
}),
return {
tokens: null,
isLoggedIn: false,
isLoading: true,

setTokens: (tokens) => {
set({
tokens,
isLoggedIn: tokens !== null,
isLoading: false,
});
},


// 로컬스토리지에서 accessToken 확인 및 자동 로그인 (외부에서 호출 가능)
initializeFromLocalStorage,

setLoading: (loading) => {
set({ isLoading: loading });
},

clearTokens: () => {
localStorage.removeItem("accessToken");
localStorage.removeItem("auth-storage-timestamp");
set({
tokens: null,
isLoggedIn: false,
isLoading: false,
});
},
};
},
{
name: "auth-storage",
partialize: (state) => ({
tokens: state.tokens,
isLoggedIn: state.isLoggedIn,
}),
onRehydrateStorage: () => (state) => {
onRehydrateStorage: () => (state, error) => {
console.log('🔄 [Zustand] onRehydrateStorage 호출됨', { state: !!state, error });

if (error) {
console.error('🔄 [Zustand] 복원 에러:', error);
}

if (state) {
state.setLoading(false);
// 초기 설치시 토큰 초기화 확실히 수행
setTimeout(() => state.initializeFromLocalStorage(), 0);
console.log('🔄 [Zustand] 상태 복원 성공, 토큰 초기화 실행');
state.initializeFromLocalStorage();
} else {
console.log('🔄 [Zustand] 상태 없음 - 새 설치로 간주');
// 새 설치인 경우 직접 초기화
setTimeout(() => {
const currentState = useAuthStore.getState();
currentState.initializeFromLocalStorage();
}, 100);
}
},
}
Expand Down