From 05f4fe4e941da4bbae640ad47ebe9ed4041cd6e9 Mon Sep 17 00:00:00 2001 From: yoosangwook Date: Wed, 7 May 2025 17:16:26 +0900 Subject: [PATCH] refactor: enhance session management and routing in middleware and API authentication --- next.config.ts | 8 ++--- src/app/@header/default.tsx | 2 +- src/app/api/auth/logout/route.ts | 15 +++++++++ src/app/api/auth/route.ts | 48 +++++++++++++++++++++++++++-- src/app/page.tsx | 8 +++++ src/components/Login.tsx | 29 ++++++++--------- src/components/ui/Main.tsx | 17 ++++++++-- src/components/ui/common/Header.tsx | 28 +++++++++-------- src/libs/axios.ts | 7 +++-- src/libs/session.ts | 45 ++++++++++++++++++++++----- src/middleware.ts | 9 +++--- src/types/Auth.ts | 30 ++++++++++++++++++ 12 files changed, 194 insertions(+), 52 deletions(-) create mode 100644 src/app/api/auth/logout/route.ts create mode 100644 src/types/Auth.ts diff --git a/next.config.ts b/next.config.ts index bc718b5..1a3917a 100644 --- a/next.config.ts +++ b/next.config.ts @@ -12,10 +12,10 @@ const nextConfig: NextConfig = { source: '/api/user/login', destination: `${process.env.NEXT_PUBLIC_QSP_API_URL}/api/user/login`, }, - // { - // source: '/api/:path*', - // destination: `${process.env.NEXT_PUBLIC_API_URL}/api/:path*`, - // }, + { + source: '/api/:path*', + destination: `${process.env.NEXT_PUBLIC_API_URL}/api/:path*`, + }, ] }, } diff --git a/src/app/@header/default.tsx b/src/app/@header/default.tsx index 33c708b..aff8409 100644 --- a/src/app/@header/default.tsx +++ b/src/app/@header/default.tsx @@ -1,5 +1,5 @@ import Header from '@/components/ui/common/Header' -export default function page() { +export default async function page() { return
} diff --git a/src/app/api/auth/logout/route.ts b/src/app/api/auth/logout/route.ts new file mode 100644 index 0000000..390f8d1 --- /dev/null +++ b/src/app/api/auth/logout/route.ts @@ -0,0 +1,15 @@ +import { sessionOptions } from '@/libs/session' +import { SessionData } from '@/types/Auth' +import { getIronSession } from 'iron-session' +import { cookies } from 'next/headers' +import { NextResponse } from 'next/server' + +export async function GET(request: Request) { + const cookieStore = await cookies() + const session = await getIronSession(cookieStore, sessionOptions) + + session.destroy() + // return redirect('/login') + + return NextResponse.json({ code: 200, message: 'Logout successful' }) +} diff --git a/src/app/api/auth/route.ts b/src/app/api/auth/route.ts index f9fc859..8cbbc1a 100644 --- a/src/app/api/auth/route.ts +++ b/src/app/api/auth/route.ts @@ -1,6 +1,12 @@ -import { axiosInstance } from '@/libs/axios' +import { cookies } from 'next/headers' import { NextResponse } from 'next/server' +import { getIronSession } from 'iron-session' +import { axiosInstance } from '@/libs/axios' +import { sessionOptions } from '@/libs/session' + +import type { SessionData } from '@/types/Auth' + export async function POST(request: Request) { const { loginId, pwd } = await request.json() @@ -10,5 +16,43 @@ export async function POST(request: Request) { }) console.log('🚀 ~ result ~ result:', result) - return NextResponse.json('ok') + if (result.data.result.code === 200) { + const cookieStore = await cookies() + const session = await getIronSession(cookieStore, sessionOptions) + + console.log('start session edit!') + session.langCd = result.data.data.langCd + session.currPage = result.data.data.currPage + session.rowCount = result.data.data.rowCount + session.startRow = result.data.data.startRow + session.endRow = result.data.data.endRow + session.compCd = result.data.data.compCd + session.agencyStoreId = result.data.data.agencyStoreId + session.storeId = result.data.data.storeId + session.userId = result.data.data.userId + session.category = result.data.data.category + session.userNm = result.data.data.userNm + session.userNmKana = result.data.data.userNmKana + session.telNo = result.data.data.telNo + session.fax = result.data.data.fax + session.email = result.data.data.email + session.lastEditUser = result.data.data.lastEditUser + session.storeGubun = result.data.data.storeGubun + session.pwCurr = result.data.data.pwCurr + session.pwdInitYn = result.data.data.pwdInitYn + session.apprStatCd = result.data.data.apprStatCd + session.loginFailCnt = result.data.data.loginFailCnt + session.loginFailMinYn = result.data.data.loginFailMinYn + session.priceViewStatCd = result.data.data.priceViewStatCd + session.groupId = result.data.data.groupId + session.storeLvl = result.data.data.storeLvl + session.custCd = result.data.data.custCd + session.builderNo = result.data.data.builderNo + session.isLoggedIn = true + console.log('end session edit!') + + await session.save() + } + + return NextResponse.json({ code: 200, message: 'Login is Succecss!!' }) } diff --git a/src/app/page.tsx b/src/app/page.tsx index ac97ef3..b803534 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,6 +1,14 @@ import Main from '@/components/ui/Main' +import { sessionOptions } from '@/libs/session' +import type { SessionData } from '@/types/Auth' +import { getIronSession } from 'iron-session' +import { cookies } from 'next/headers' export default async function Home() { + const cookieStore = await cookies() + const session = await getIronSession(cookieStore, sessionOptions) + console.log('🚀 ~ Home ~ session:', session) + return ( <>
diff --git a/src/components/Login.tsx b/src/components/Login.tsx index c17373f..66a3f0c 100644 --- a/src/components/Login.tsx +++ b/src/components/Login.tsx @@ -1,9 +1,9 @@ 'use client' -import { axiosInstance } from '@/libs/axios' +import { useReducer, useState } from 'react' +import { useRouter } from 'next/navigation' import { useQuery } from '@tanstack/react-query' -import { useEffect, useReducer, useState } from 'react' -import { AxiosResponse } from 'axios' +import { axiosInstance } from '@/libs/axios' interface AccountState { loginId: string @@ -11,6 +11,7 @@ interface AccountState { } export default function Login() { + const router = useRouter() //비밀번호 보이기 숨기기 const [pwShow, setPwShow] = useState(false) //ID 저장 체크박스 @@ -26,31 +27,31 @@ export default function Login() { pwd: '', }) + interface LoginData { + code: number + message: string | null + } + const { data: loginData, isPending, error, - } = useQuery({ + } = useQuery({ queryKey: ['login', 'account'], - queryFn: () => { - const result = axiosInstance('').post(`/api/auth`, { + queryFn: async () => { + const { data } = await axiosInstance('').post(`/api/auth`, { loginId: account.loginId, pwd: account.pwd, }) - return result + router.push('/') + + return data }, enabled: isLogin, staleTime: 0, retry: false, }) - useEffect(() => { - if (loginData) { - console.log('🚀 ~ Login ~ loginData:', loginData) - setIsLogin(false) - } - }, [loginData]) - return ( <>
diff --git a/src/components/ui/Main.tsx b/src/components/ui/Main.tsx index a9eb336..37de8e7 100644 --- a/src/components/ui/Main.tsx +++ b/src/components/ui/Main.tsx @@ -1,16 +1,27 @@ 'use client' import { useHeaderStore } from '@/store/header' -import { useRouter } from 'next/navigation' +import { useSideNavState } from '@/store/sideNavState' +import { usePathname, useRouter } from 'next/navigation' import { useEffect } from 'react' export default function Main() { const router = useRouter() + const pathname = usePathname() const { setBackBtn } = useHeaderStore() + const { reset } = useSideNavState() + /** + * 헤더 뒤로가기 버튼 컨트롤 + * 사이드바 초기화 컨트롤 + */ useEffect(() => { - setBackBtn(false) - }, []) + if (pathname === '/') { + setBackBtn(false) + } + //사이드바 초기화 + reset() + }, [pathname]) return ( <> diff --git a/src/components/ui/common/Header.tsx b/src/components/ui/common/Header.tsx index 580545d..443e078 100644 --- a/src/components/ui/common/Header.tsx +++ b/src/components/ui/common/Header.tsx @@ -1,16 +1,17 @@ 'use client' -import { useEffect, useState } from 'react' - import Link from 'next/link' import { usePathname, useRouter } from 'next/navigation' import { Swiper, SwiperSlide } from 'swiper/react' +import { useSideNavState } from '@/store/sideNavState' +import { useHeaderStore } from '@/store/header' + import type { HeaderProps } from '@/types/Header' import 'swiper/css' -import { useSideNavState } from '@/store/sideNavState' +import { axiosInstance } from '@/libs/axios' // type HeaderProps = { // name: string //header 이름 @@ -20,27 +21,26 @@ import { useSideNavState } from '@/store/sideNavState' export default function Header({ name }: HeaderProps) { const router = useRouter() const pathname = usePathname() - const { sideNavIsOpen, setSideNavIsOpen, reset } = useSideNavState() - const [isShowBackBtn, setIsShowBackBtn] = useState(false) + const { sideNavIsOpen, setSideNavIsOpen } = useSideNavState() + const { backBtn } = useHeaderStore() if (pathname === '/login') { return null } - useEffect(() => { - if (pathname !== '/') { - setIsShowBackBtn(true) + const handleLogout = async () => { + const { data } = await axiosInstance(null).get('/api/auth/logout') + if (data.code === 200) { + router.push('/login') } - //사이드바 초기화 - reset() - }, [pathname]) + } return ( <>
- {isShowBackBtn && ( + {backBtn && (
@@ -112,7 +112,9 @@ export default function Header({ name }: HeaderProps) {
  • - +
  • diff --git a/src/libs/axios.ts b/src/libs/axios.ts index db7a625..5f1b076 100644 --- a/src/libs/axios.ts +++ b/src/libs/axios.ts @@ -1,7 +1,8 @@ import axios from 'axios' -export const axiosInstance = (url: string) => { - let baseURL = url !== '' || url === undefined ? url : process.env.NEXT_PUBLIC_API_URL +export const axiosInstance = (url: string | null | undefined) => { + const baseURL = url || process.env.NEXT_PUBLIC_API_URL + return axios.create({ baseURL, headers: { @@ -22,7 +23,7 @@ axios.interceptors.request.use( ) // Response interceptor -axiosInstance.interceptors.response.use( +axios.interceptors.response.use( (response) => transferResponse(response), (error) => { // 에러 처리 로직 diff --git a/src/libs/session.ts b/src/libs/session.ts index a61c755..21a0479 100644 --- a/src/libs/session.ts +++ b/src/libs/session.ts @@ -1,5 +1,7 @@ +import type { SessionData } from '@/types/Auth' import { getIronSession, SessionOptions } from 'iron-session' import { cookies } from 'next/headers' +import { redirect } from 'next/navigation' export const sessionOptions: SessionOptions = { cookieName: 'onsitesurvey_session', @@ -14,15 +16,34 @@ export const sessionOptions: SessionOptions = { }, } -export interface SessionData { - username: string | null - email: string | null - isLoggedIn: boolean -} - export const defaultSession: SessionData = { - username: '', - email: '', + langCd: null, + currPage: 0, + rowCount: 0, + startRow: 0, + endRow: 0, + compCd: null, + agencyStoreId: null, + storeId: null, + userId: null, + category: null, + userNm: null, + userNmKana: null, + telNo: null, + fax: null, + email: null, + lastEditUser: null, + storeGubun: null, + pwCurr: null, + pwdInitYn: null, + apprStatCd: null, + loginFailCnt: null, + loginFailMinYn: null, + priceViewStatCd: null, + groupId: null, + storeLvl: null, + custCd: null, + builderNo: null, isLoggedIn: false, } @@ -31,3 +52,11 @@ export const getSession = async () => { const session = await getIronSession(cookieStore, sessionOptions) return session } + +export const logout = async () => { + const cookieStore = await cookies() + const session = await getIronSession(cookieStore, sessionOptions) + + session.destroy() + return redirect('/login') +} diff --git a/src/middleware.ts b/src/middleware.ts index 5fe31f3..115fd8a 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -2,16 +2,17 @@ import { NextResponse } from 'next/server' import { cookies } from 'next/headers' import type { NextRequest } from 'next/server' import { getIronSession } from 'iron-session' -import { SessionData, sessionOptions } from './libs/session' +import { sessionOptions } from './libs/session' +import type { SessionData } from './types/Auth' export async function middleware(request: NextRequest) { const cookieStore = await cookies() const session = await getIronSession(cookieStore, sessionOptions) // todo: 로그인 기능 추가 시 주석 해제 - // if (!session.isLoggedIn) { - // return NextResponse.redirect(new URL('/login', request.url)) - // } + if (!session.isLoggedIn) { + return NextResponse.redirect(new URL('/login', request.url)) + } return NextResponse.next() } diff --git a/src/types/Auth.ts b/src/types/Auth.ts new file mode 100644 index 0000000..8920afc --- /dev/null +++ b/src/types/Auth.ts @@ -0,0 +1,30 @@ +export interface SessionData { + langCd: null + currPage: Number | null + rowCount: Number | null + startRow: Number | null + endRow: Number | null + compCd: null + agencyStoreId: null + storeId: null + userId: null + category: null + userNm: null + userNmKana: null + telNo: null + fax: null + email: null + lastEditUser: null + storeGubun: null + pwCurr: null + pwdInitYn: null + apprStatCd: null + loginFailCnt: Number | null + loginFailMinYn: null + priceViewStatCd: null + groupId: null + storeLvl: null + custCd: null + builderNo: null + isLoggedIn: boolean +}