Skip to content
5 changes: 3 additions & 2 deletions src/api/api.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import axios from 'axios';
import { useAuthStore } from '@/store/auth.store';

const api = axios.create({
baseURL: process.env.NEXT_PUBLIC_BASE_URL,
Expand Down Expand Up @@ -70,8 +71,7 @@ api.interceptors.response.use(
} catch (refreshError) {
// refreshToken도 만료되었거나 재발급 실패
console.error('토큰 재발급 실패:', refreshError);
localStorage.removeItem('accessToken');
localStorage.removeItem('refreshToken');
useAuthStore.getState().logout();
throw refreshError;
} finally {
// 재발급 완료 후 Promise 초기화 (성공/실패 관계없이)
Expand All @@ -94,6 +94,7 @@ api.interceptors.response.use(
}

console.error('인증이 만료되었습니다. 다시 로그인하세요.');
useAuthStore.getState().logout();
}
return Promise.reject(error);
}
Expand Down
16 changes: 13 additions & 3 deletions src/app/_components/common/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,7 @@ const Header = () => {

const handleLogout = () => {
logout();
clearUser();
router.push('/');

setIsProfileOpen(false);
};

Expand Down Expand Up @@ -134,12 +132,24 @@ const Header = () => {
href="/business"
className={dropdownItem}
role="menuitem"
onClick={(e) => {
if (!isAuthenticated) {
e.preventDefault();
setOpenLogin(true);
}
}}
>
작성하기
</Link>
<button
type="button"
onClick={() => setOpenUpload(true)}
onClick={() => {
if (!isAuthenticated) {
setOpenLogin(true);
} else {
setOpenUpload(true);
}
}}
className={`${dropdownItem} w-full text-left`}
role="menuitem"
>
Expand Down
22 changes: 16 additions & 6 deletions src/app/expert/detail/components/ExpertDetailSidebar.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
'use client';

import { useState } from 'react';
import { useRouter } from 'next/navigation';
import { useExpertStore } from '@/store/expert.store';
import { useBusinessStore } from '@/store/business.store';
import { useEvaluationStore } from '@/store/report.store';
import { useUserStore } from '@/store/user.store';
import { useAuthStore } from '@/store/auth.store';
import { useExpertReportDetail } from '@/hooks/queries/useExpert';
import GrayPlus from '@/assets/icons/gray_plus.svg';
import GrayCheck from '@/assets/icons/gray_check.svg';
import WhitePlus from '@/assets/icons/white_plus.svg';
import BusinessPlanDropdown from './BusinessPlanDropdown';
import { ExpertDetailResponse } from '@/types/expert/expert.detail';
import LoginModal from '@/app/_components/common/LoginModal';

interface ExpertDetailSidebarProps {
expert: ExpertDetailResponse;
Expand All @@ -22,9 +25,9 @@ const ExpertDetailSidebar = ({ expert }: ExpertDetailSidebarProps) => {
const planId = useBusinessStore((s) => s.planId);
const hasExpertUnlocked = useEvaluationStore((s) => s.hasExpertUnlocked);
const user = useUserStore((s) => s.user);
const hasAccessToken =
typeof window !== 'undefined' && !!localStorage.getItem('accessToken');
const isMember = hasAccessToken && !!user;
const [openLogin, setOpenLogin] = useState(false);
const isAuthenticated = useAuthStore((s) => s.isAuthenticated);
const isMember = isAuthenticated && !!user;

const { data: reportDetails = [], isLoading: isLoadingReports } =
useExpertReportDetail(expert.id, {
Expand Down Expand Up @@ -63,10 +66,17 @@ const ExpertDetailSidebar = ({ expert }: ExpertDetailSidebarProps) => {
? '신청완료'
: '전문가 연결';

const requireAuth = () => {
if (isAuthenticated) return true;
setOpenLogin(true);
return false;
};

const handleConnect = () => {
if (!expert) return;

if (shouldShowCreateButton) {
if (!requireAuth()) return;
router.push('/business');
return;
}
Expand Down Expand Up @@ -112,15 +122,15 @@ const ExpertDetailSidebar = ({ expert }: ExpertDetailSidebarProps) => {
<button
onClick={handleConnect}
disabled={shouldShowCreateButton ? false : disabled}
className={`ds-text mt-8 flex w-full items-center justify-center gap-1 rounded-lg px-8 py-[10px] font-medium ${
shouldShowCreateButton || (!disabled && !hasRequested)
className={`ds-text mt-8 flex w-full items-center justify-center gap-1 rounded-lg px-8 py-[10px] font-medium ${shouldShowCreateButton || (!disabled && !hasRequested)
? 'bg-primary-500 hover:bg-primary-700 cursor-pointer text-white'
: 'cursor-not-allowed bg-gray-200 text-gray-500'
}`}
}`}
>
<ButtonIcon className="h-5 w-5 shrink-0" />
<span>{buttonText}</span>
</button>
<LoginModal open={openLogin} onClose={() => setOpenLogin(false)} />
</aside>
);
};
Expand Down
1 change: 1 addition & 0 deletions src/store/user.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export const useUserStore = create<UserState>()(
}
} catch (error) {
console.error('유저를 찾을 수 없습니다', error);
set({ user: null });
}
},
}),
Expand Down
16 changes: 12 additions & 4 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
{
"compilerOptions": {
"target": "ES2017",
"lib": ["dom", "dom.iterable", "esnext"],
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
Expand All @@ -11,7 +15,7 @@
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"jsx": "react-jsx",
"incremental": true,
"plugins": [
{
Expand All @@ -20,7 +24,9 @@
],
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
"@/*": [
"src/*"
]
}
},
"include": [
Expand All @@ -30,5 +36,7 @@
".next/types/**/*.ts",
".next/dev/types/**/*.ts"
],
"exclude": ["node_modules"]
"exclude": [
"node_modules"
]
}