๐session ์ด๋ ๋ฌด์์ธ๊ฐ?
ํด๋ผ์ด์ธํธ์ ์๋ฒ ๊ฐ์ ์ฐ๊ฒฐ ์ํ๋ฅผ ์๋ฏธ, ์ฆ ํด๋ผ์ด์ธํธ๊ฐ ๋ธ๋ผ์ฐ์ ์ ์ ์ํ์ฌ ์๋ฒ์ ์ ์์ด ์ข ๋ฃํ๊ธฐ ์ ์ ์ํ๋ฅผ ์๋ฏธํ๋ค.
๐session ๋์ ์์
- ์ฌ์ฉ์๊ฐ ๋ก๊ทธ์ธ์ ํ๋ค
- ์๋ฒ์ธก์์๋ ๋ก๊ทธ์ธ ์ ๋ณด๋ฅผ ํตํด ์ฌ์ฉ์๋ฅผ ํ์ธํ๋ค.
- ์๋ฒ์ธก์์ ์ธ์ ์ ์ฅ์์ ์ฌ์ฉ์ ์ธ์ ์์ฑ์ ์์ฒญํ๋ค.
- ์ฌ์ฉ์์ ๊ณ ์ ํ ID๊ฐ์ ๋ถ์ฌํ์ฌ ์ธ์ ์ ์ฅ์์ ์ ์ฅํ๊ณ , ์ด์ ์ฐ๊ฒฐ๋๋ ์ธ์ ID๋ฅผ ๋ฐํํ๋ค.
- ์ฌ์ฉ์๋ ์๋ฒ์์ ํด๋น ์ธ์ ID๋ฅผ ๋ฐ์ ์ฟ ํค์ ์ ์ฅํ๋ค
- ์ธ์ฆ์ด ํ์ํ ์์ฒญ์ ํ ๋ ํค๋์ ์ฟ ํค๋ฅผ ์ค์ด ์์ฒญํ๋ค
- ์๋ฒ์์๋ ์ฟ ํค๋ฅผ ๋ฐ์ ์ธ์ ์ ์ฅ์์ ๋์กฐ๋ฅผ ํ๋ค
- ์ธ์ฆ ํ ์์ฒญ๋ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์จ๋ค
- ์ธ์ฆ์ด ์๋ฃ๋๊ณ ์๋ฒ๋ ์ฌ์ฉ์์๊ฒ ๋ง๋ ๋ฐ์ดํฐ๋ฅผ ๋ณด๋ด์ค๋ค
๐session ๊ณผ cookie
์ ์ฅ์์น | ํด๋ผ์ด์ธํธ | ์๋ฒ |
๋ผ์ดํ์ฌ์ดํด(๋ง๋ฃ์์ ) | ์ฟ ํค ์ ์ฅ์ ์ค์ | ๋ธ๋ผ์ฐ์ ์ข ๋ฃ ์ ์ญ์ |
๋ณด์ | ๋น๊ต์ ์ทจ์ฝ | ์์ |
์๋ | ๋น ๋ฆ | ๋น๊ต์ ๋๋ฆผ |
๐Next.js์์ session๊ณผ cookie์ด์ฉํ๊ธฐ
server ์ปดํฌ๋ํธ์ client ์ปดํฌ๋ํธ ๊ด์ ์์ ํ๋ก์ ํธ์์ ์ฌ์ฉํ ๋ก์ง์ ์ ๋ฆฌํด๋ณด์๋ค.
์๋ฒ ์ปดํฌ๋ํธ - instance.ts, authApi.ts
'use server';
import { cookies } from 'next/headers'; // ์๋ฒ ์ฌ์ด๋์์ ์ฟ ํค๋ฅผ ์ฒ๋ฆฌํ๊ธฐ ์ํ ๋ด์ฅ ๋ชจ๋
import { BASE_URL } from '@/constants/url';
import { auth } from '@/auth';
import { redirect } from 'next/dist/server/api-utils';
interface RequestOptions {
headers?: Record<string, string>;
[key: string]: string | Record<string, string> | undefined;
}
const fetchInstance = async (url: string, options: RequestOptions = {}) => {
const session = await auth();
const accessToken = session?.user?.accessToken;
const headers: RequestOptions['headers'] = {
'Content-Type': 'application/json',
...options.headers,
};
if (accessToken) {
headers.Authorization = `Bearer ${accessToken}`;
}
try {
const response = await fetch(`${BASE_URL}${url}`, {
...options,
headers,
});
//response ๋ก์ง ์๋ต
} catch (error) {
//์๋ฌ ์ฒ๋ฆฌ ๋ก์ง ์๋ต
}
};
export const instance = fetchInstance;
'use server';
import { BASE_URL } from '@/constants/url';
import { instance } from './instance';
export const signIn = async ({ email, password }: { email: string; password: string }) => {
const response = await instance('sign-in', {
body: JSON.stringify({ email, password }),
credentials: 'include',
method: 'POST',
});
return response;
};
export const reissueToken = async (refresh_token: string) => {
const response = await fetch(`${BASE_URL}reissue`, {
headers: {
Cookie: `refresh_token=${refresh_token}`,
},
method: 'POST',
});
// reponse์๋ฌ ๋ก์ง ์๋ต
const cookie = response.headers.get('Set-Cookie');
const { accessToken } = await response.json();
const refreshToken = cookie?.split(';')[0].split('=')[1];
return { accessToken, refreshToken };
};
์ธ์ (Session)
- ์๋ฒ ์ฌ์ด๋์์๋ auth() ํจ์๋ฅผ ํตํด ์ธ์
์ ๋ณด๋ฅผ ๊ฐ์ ธ์จ๋ค.
- ์ฌ๊ธฐ์๋ ๋ณดํต ์ฌ์ฉ์ ์ก์ธ์ค ํ ํฐ๊ณผ ๊ฐ์ ์ธ์ฆ ๊ด๋ จ ์ ๋ณด๊ฐ ํฌํจ๋๋ฉฐ, ์ด๋ฅผ ์ด์ฉํด API ์์ฒญ ์ ์ธ์ฆ์ ์ฒ๋ฆฌํ๋ค.
- fetchInstance ํจ์์์๋ ์ธ์ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์์ ์ก์ธ์ค ํ ํฐ(session?.user?.accessToken)์ ์ถ์ถํ๊ณ , ์ด ํ ํฐ์ Authorization ํค๋์ ๋ฃ์ด API ์์ฒญ ์ ์ฌ์ฉํ๋ค. (Bearer ${accessToken})
์ก์ธ์ค ํ ํฐ(Access Token)
- ์ก์ธ์ค ํ ํฐ์ ์๋ฒ์์ ์ธ๋ถ ์๋น์ค๋ก์ API ์์ฒญ์ ์ธ์ฆํ๊ธฐ ์ํด ์ฌ์ฉ๋๋ค. ์ด ํ ํฐ์ด ์์ด์ผ๋ง ๊ถํ์ด ํ์ํ ์์ ๋ค์ ์ํํ ์ ์๋ค.
- ์ฝ๋์์๋ /sign-in์ด๋ ํ ํฐ ์ฌ๋ฐ๊ธ(reissue) ๊ฐ์ API ์์ฒญ์ ๋ณด๋ผ ๋, ํค๋ ๋๋ ๋ณธ๋ฌธ์ ์ก์ธ์ค ํ ํฐ์ ํฌํจ์์ผ ์์ฒญ์ ์ธ์ฆํ๋ค. ๋ง์ฝ ์ก์ธ์ค ํ ํฐ์ด ๋ง๋ฃ๋๊ฑฐ๋ ์์ ๊ฒฝ์ฐ, ์๋ก์ด ํ ํฐ์ ๋ฐ๊ธ๋ฐ๊ฑฐ๋ ์๋ฌ ์ฒ๋ฆฌ ๋ก์ง์ด ์คํ๋๋ค.
์ฟ ํค(Cookies)
- ์ฟ ํค๋ ์ฃผ๋ก ์๋ฒ ์ฌ์ด๋์์ ์ธ์ ์ ์ ์งํ๋ ๋ฐ ์ฌ์ฉ๋๋ค. reissueToken ํจ์์์๋ refresh_token์ด ์ฟ ํค์ ์ ์ฅ๋์ด, ์ด๋ฅผ ์ด์ฉํด ์๋ก์ด ์ก์ธ์ค ํ ํฐ์ ๋ฐ๊ธ๋ฐ๋๋ค.
- ๋ํ ์๋ฒ๋ Set-Cookie ํค๋๋ฅผ ํตํด ์๋ก์ด ํ ํฐ์ ํด๋ผ์ด์ธํธ์ ์ ์ฅํ ์ ์๋ค. (response.headers.get('Set-Cookie'))
ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ - Header.tsx
'use client';
//style๊ด๋ จ import ์๋ต
import useUserInfo from '@/hook/useUserInfo';
import { useUserInfoStore } from '@/store/memberStore';
import { useSession } from 'next-auth/react';
import { useEffect } from 'react';
import { getUserInfo } from '@/api/memberApi';
export default function Header() {
const userInfo = useUserInfo();
const { setUserInfo } = useUserInfoStore();
const { data: session, update } = useSession();
const accessToken = session?.user?.accessToken;
const refreshToken = session?.user?.refreshToken;
// update();
useEffect(() => {
const storedUserInfo = localStorage.getItem('userInfo');
if (storedUserInfo && !accessToken && !refreshToken) {
localStorage.removeItem('userInfo');
setUserInfo(null);
}
}, [accessToken, refreshToken, setUserInfo]);
// ์ ์ ์ ๋ณด์กฐํ
useEffect(() => {
(async () => {
if (!localStorage.getItem('userInfo') && accessToken && refreshToken) {
const result = await getUserInfo();
const storedUserInfo = { name: result.name, course: result.course, email: result.email };
if (result) {
localStorage.setItem('userInfo', JSON.stringify(storedUserInfo));
setUserInfo(result);
}
}
})();
}, [accessToken, refreshToken, setUserInfo]);
return (
// html ๋ก์ง ์๋ต
);
}
์ธ์ (Session)
- ํด๋ผ์ด์ธํธ ์ฌ์ด๋์์๋ next-auth์ useSession์ ์ฌ์ฉํด ์ฌ์ฉ์ ์ธ์ ์ํ๋ฅผ ๊ด๋ฆฌํ๋ค. ๋ก๊ทธ์ธ ์ ์ก์ธ์ค ํ ํฐ๊ณผ ๋ฆฌํ๋ ์ ํ ํฐ์ ์ธ์ ์ ์ ์ฅํ ํ, ์ด๋ฅผ ์ด์ฉํด ์ธ์ฆ๋ API ์์ฒญ์ ๋ณด๋ผ ์ ์๋ค.
- ํ ํฐ์ ๊ฐ์ ธ์ค๊ณ (session?.user?.accessToken, session?.user?.refreshToken), ์ด ํ ํฐ๋ค์ ์ด์ฉํด ์ฌ์ฉ์ ์ ๋ณด๋ฅผ ์๋ฒ์์ ๋ฐ์์ ์ ์ญ ์ํ์ ์ ์ฅํ๊ฑฐ๋ localStorage์ ์ ์ฅ
์ก์ธ์ค ํ ํฐ(Access Token)
- ์ก์ธ์ค ํ ํฐ์ด ์์ผ๋ฉด, ๊ธฐ์กด์ ์ ์ฅ๋ ์ฌ์ฉ์ ์ ๋ณด๋ฅผ ์ง์ฐ๋๋ก ๋ก์ง์ด ์ฒ๋ฆฌ
์ฟ ํค(Cookies)
- ํด๋ผ์ด์ธํธ ์ฌ์ด๋์์๋ ์ฟ ํค๋ฅผ ์ง์ ์ ์ผ๋ก ๋ค๋ฃจ์ง ์์ง๋ง, credentials: 'include' ์ต์ ์ ํตํด ์ฟ ํค๋ฅผ ์๋์ผ๋ก ์์ฒญ์ ํฌํจ์ํฌ ์ ์๋ค. ์ด๋ฅผ ํตํด ๋ธ๋ผ์ฐ์ ๊ฐ ์๋์ผ๋ก ์ฟ ํค(์: ๋ฆฌํ๋ ์ ํ ํฐ)๋ฅผ ์๋ฒ์ ์ ์กํ์ฌ ์ธ์ ์ด ์ ์ง๋๋ค.
์ ๋ฐ์ ์ผ๋ก ํต์ฌ์ next.js๋ ์๋ฒ ์ปดํฌ๋ํธ์์ ์ฟ ํค๋ฅผ ์ฝ๊ณ ์ฒ๋ฆฌํ ๋ค ํด๋ผ์ด์ธํธ๋ก ํ์ํ ๋ฐ์ดํฐ๋ง ๋ณด๋ด์ฃผ๋ ๋ฐฉ์์ด๊ธฐ ๋๋ฌธ์ ์ฟ ํค ๊ฐ์ ํด๋ผ์ด์ธํธ์์ ์ง์ ๋ ธ์ถ์ํค์ง ์๊ณ ๋ ์ฌ์ฉํ ์ ์๋ค!!
๊ทธ๋ฆฌ๊ณ ์ฐ๋ฆฌ ํ๋ก์ ํธ์์๋ ์ธ์ ๊ณผ ์ฟ ํค, ํ ํฐ์ ๋ชจ๋ ์ฌ์ฉํ๋ค. ๋ณดํต ์ธ์ , ์ฟ ํค vs jwt(ํ ํฐ) ์ด๋ ๊ฒ ๋๋ ์ ๋ก๊ทธ์ธ์ ๊ตฌํํ๋ค. ํ์ง๋ง Next.js์์ ์ ๊ณตํ๋ useSessionํ ์ ์ฌ์ฉํด ์ธ์ ๊ด๋ฆฌ ๊ธฐ๋ฅ์ ํ์ฉํ๋ฉด์๋, ํด๋ผ์ด์ธํธ์์ ํ ํฐ์ ํตํด API ์์ฒญ์ ์ฒ๋ฆฌํ๋ ๋ฐฉ์์ผ๋ก ํผํฉ๋ ๊ตฌ์กฐ๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
์ฐธ๊ณ ์๋ฃ
'Frontend > Next.js' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
Next.js - Infinite Scrollํ์ฉํด์ ๋ฐ์ดํฐ ์ฒ๋ฆฌํ๊ธฐ (1) | 2024.11.03 |
---|---|
Next.js - Auth.js( NextAuth.js) ๋ฅผ ์ด์ฉํ Keycloak๊ณผ์ ์ธ์ฆ ์ฒ๋ฆฌ ๊ณผ์ (0) | 2024.09.24 |
Next.js - Data fetching (React vs Next.js) (0) | 2024.02.11 |
Next.js 14๋ฒ์ - Layouts,Metadata,Dynamic Routes (0) | 2024.02.08 |
Next.js - ์คํฌ๋กค ์ ๋ ๊ฐ์ด๋ ๊ตฌํ (0) | 2023.12.19 |