์ฌํด๋ ์์ข์ข ํ๋ก์ ํธ๋ฅผ ์งํํ๋ฉฐ ์๋น์ค ๋ด API ํธ์ถ ํ์๋ฅผ ์ต์ ํํ๊ธฐ ์ํด ์ด๋ค ๋ฐฉ๋ฒ๋ค์ ์ฌ์ฉํ๋์ง ๊ทธ ๊ณผ์ ์ ๋ด์ ๊ธ์ ๋๋ค.
์ฌํด๋ ์์ข์ข ํ๋ก์ ํธ๋ฅผ ์์ํ ๋๋ถํฐ, ์ ํฌ ํ์ ๋ชฉํ๋ ์ค์ ์ฌ์ฉ์๋ค์ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์๋ ์๋น์ค๋ฅผ ๋ง๋ค๊ณ , ๊พธ์คํ ๋๋ฉ์ธ ๋ถ์๊ณผ ์ฌ์ฉ์๋ค์ ํผ๋๋ฐฑ์ ๋ฐํ์ผ๋ก ์๋น์ค๋ฅผ ์ง์์ ์ผ๋ก ๋ฐ์ ์์ผ๋๊ฐ๋ ๊ฒ์ด์์ต๋๋ค.
๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ์๋น์ค๋ฅผ ์ค์ ๋ก ์จ๋ผ์ธ ์์ ๋ฐฐํฌํ ์๊ฐ์ด์์ต๋๋ค. ๋ฐ๋ผ์ ๋ง์ API ํธ์ถ์ ๊ณง ๋น์ฉ์ ์๋ฏธํ๊ณ , ๋๊ตฐ๊ฐ๊ฐ ์ ์์ ์ผ๋ก ๋ง์ ์์ API ํธ์ถ์ ํ๊ฑฐ๋ ๊ผญ ํ์ํ์ง ์์ API๊ฐ ํธ์ถ๋๋ ์ํฉ์ ์ต๋ํ ๋ฐฉ์งํ์๋ ๋ง์ ๊ฐ์ง์ผ๋ก ์๋น์ค๋ฅผ ๊ฐ๋ฐํ์ต๋๋ค.
๊ทธ๋์ API ํธ์ถ ์ต์ ํ๋ฅผ ์ํด ๋ค์๊ณผ ๊ฐ์ ๋ฐฉ๋ฒ๋ค์ ์ฌ์ฉํ์ต๋๋ค.
Next.js middleware ๊ธฐ๋ฅ์ ์ด์ฉํ ํ์ด์ง ์์ฒญ ๋ฆฌ๋ค์ด๋ ํธ
Tanstack-query์ staleTime ์ค์ ๋ฐ ์ฟผ๋ฆฌ ๋ฌดํจํ
ํ ๊ธ ๋ฒํผ์ ๋๋ฐ์ด์ค ์ฒ๋ฆฌ ์ ์ฉ
์ ํฌ ์๋น์ค์๋ ๋ก๊ทธ์ธ์ ํด์ผ๋ง ์ ๊ทผํ ์ ์๋ ํ์ด์ง๋ค์ด ์์ต๋๋ค. ๋ด ๊ณํ ํ์ด์ง, ๊ณํ ์์ฑ ํ์ด์ง, ๋ง์ด ํ์ด์ง๊ฐ ๊ทธ ์์์ ๋๋ค.
๋ง์ฝ ๋ก๊ทธ์ธํ์ง ์์ ์ ์ ๊ฐ ํด๋น ํ์ด์ง์ ์ ๊ทผํ๋ ค๊ณ ํ๋ ๊ฒฝ์ฐ, ํ์ด์ง๋ฅผ ์๋ตํ๊ธฐ ์ nextjs middleware์์ ๋ก๊ทธ์ธ ์ฌ๋ถ๋ฅผ ๊ฒ์ฌํด ๋ก๊ทธ์ธ ํ์ง ์์๋ค๋ฉด ๋ก๊ทธ์ธ ํ์ด์ง๋ก ๋ฆฌ๋ค์ด๋ ํธ ์์ผ์ฃผ์์ต๋๋ค.
middleware๋?
์ ์ ๊ฐ ๋ณด๋ธ request์ ์ด์ ๋ํ response ์ฌ์ด์ ์๋ ์ํํธ์จ์ด์ ๋๋ค. ์ฆ, ํน์ ์์ฒญ์ด ์๋ฃ๋๊ธฐ ์ ๊ทธ ์์ฒญ์ ๊ฐ๋ก์ฑ ์ํ๋ ์์ ์ ํ ์ ์์ต๋๋ค.
ํ์ด์ง๋ฅผ ๋ ๋๋งํ๊ธฐ ์ ์ ์๋ฒ ์ธก์์ ์คํ๋๋ ํจ์์ด๊ธฐ ๋๋ฌธ์, ์์ฒญ์ ๋ํ ๊ฒ์ฆ์ ์ฉ๋๋ก ์ฌ์ฉ๋ ์ ์์ต๋๋ค. ์์ฒญ์ ๋ํ ์๋ต์ rewriting, redirecting ๋ฑ์ ํตํด ์์ ํ ์ ์์ต๋๋ค.
๋ ์์ธํ ๋ด์ฉ์ ๊ณต์๋ฌธ์๋ฅผ ์ฐธ๊ณ ํ๋ ๊ฒ ์ข์ ๊ฒ ๊ฐ์ต๋๋ค.
๋ก๊ทธ์ธ ํ์ง ์์ ์ ์ ๊ฐ ์์ ํ์ด์ง๋ค์ ์ ๊ทผํ๋ ๊ฒ์ ๋ง๋ ๊ฒ์ ๊ธฐ๋ฅ์ ์ผ๋ก๋ ์์ฐ์ค๋ฌ์ ๊ณ , ๋ง์ฝ ํน์ ํ์ด์ง์ ์ ๊ทผํด ๊ทธ ํ์ด์ง ์์์ ๋ก๊ทธ์ธ ์ฌ๋ถ๋ฅผ ๊ฒ์ฌํด ๋ฆฌ๋ค์ด๋ ํธ ์ฒ๋ฆฌ๋ฅผ ํ๋ค๋ฉด, ์ด๋ ํ์ด์ง ์๋ต์ด๋ผ๋ API ํธ์ถ๊ณผ ํ์ด์ง์ ์ฌ์ฉ๋๋ data๋ฅผ ๋ถ๋ฌ์ค๋ API ํธ์ถ์ ์ถ๊ฐ์ ์ผ๋ก ์๊ตฌํ๋ ์์ ์ด๊ธฐ์ ๋นํจ์จ์ ์ด๋ผ๊ณ ์๊ฐํ์ต๋๋ค.
๋ฐ๋ผ์, ์ ์ ์๊ฒ ํน์ ํ์ด์ง๋ฅผ ์๋ตํ๊ธฐ ์ด์ ์ middleware์์ ์กฐ๊ฑด์ ๊ฒ์ฌํด ๋ถํ์ํ ํ์ด์ง ์๋ต ๋ฐ API ํธ์ถ์ ๋ง์ ์ ์์์ต๋๋ค.
๋ค์์ ์ค์ middleware ์ฝ๋์ ์ผ๋ถ๋ถ์ ๋๋ค.
middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
const cookies = request.cookies; // 1๋ฒ
const hasAuthCookies = cookies.has('auth'); // 2๋ฒ
if (request.nextUrl.pathname === '/') {
if (hasAuthCookies) {
return NextResponse.redirect(new URL('/home', request.url));
}
return NextResponse.redirect(new URL('/login', request.url));
} else if (request.nextUrl.pathname === '/home') // 3๋ฒ
if (!hasAuthCookies) {
return NextResponse.redirect(new URL('/login', request.url));
}
} else if (request.nextUrl.pathname === '/login') {
if (hasAuthCookies) {
return NextResponse.redirect(new URL('/home', request.url));
}
}
...
cookies
์ ์ ๊ทผํฉ๋๋ค. cookies
์ auth
๋ผ๋ key์ ๋ํ ๊ฐ์ด ์๋์ง ์ฌ๋ถ๋ฅผ hasAuthCookies
๋ผ๋ ๋ณ์์ ์ ์ฅํฉ๋๋ค. ๋ง์ฝ ๋ก๊ทธ์ธ์ด ๋์ด์๋ ์ ์ ์ ์์ฒญ์ด๋ผ๋ฉด auth
๋ผ๋ key์ ๋ํด ํ ํฐ data๊ฐ ๊ฐ์ผ๋ก ๋ค์ด์์ ๊ฒ์
๋๋ค./home
)์ ์ ๊ทผํ์ ๊ฒฝ์ฐ, ๋ง์ฝ ๋ก๊ทธ์ธ๋์ง ์์ ์ ์ ๋ผ๋ฉด๋ก๊ทธ์ธ ํ์ด์ง(/login
)๋ก ๋ฆฌ๋ค์ด๋ ํธ ์ํต๋๋ค. data๊ฐ ๋ณ๊ฒฝ๋ ๊ฐ๋ฅ์ฑ์ด ๊ฑฐ์ ์๋ ํ์ด์ง๊ฐ ์๋ค๋ฉด, ์ด ํ์ด์ง์ ์ ๊ทผํ ๋๋ง๋ค data๋ฅผ ์๋ฒ์์ ์๋ก ๋ฐ์์ค๋ ๊ฒ์ ๋ถํ์ํ API๋ฅผ ํธ์ถํ๋ ๊ฒ์ผ ์ ์์ต๋๋ค. ๊ทธ๋์ ์ ๋ ์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด Tanstack-Query์ staleTime๊ณผ ์ฟผ๋ฆฌ ๋ฌดํจํ ๊ธฐ๋ฅ์ ์ฌ์ฉํ์ต๋๋ค.
์ด๋ฅผ ์ํด ๋ค์๊ณผ ๊ฐ์ ๋จ๊ณ๋ฅผ ๊ฑฐ์ณค์ต๋๋ค.
infinity
๋ก ์ค์ ํฉ๋๋ค. data๋ฅผ ๋ณ๊ฒฝํ๋ useMutation ํจ์๋ค์ด ํธ์ถ ๋ ๋, ์ด๋ค get ์์ฒญ์ ๋ฌดํจํ์์ผ์ค์ผ ํ๋์ง, get ์์ฒญ์ ํด๋น๋๋ ์ฟผ๋ฆฌ ํค๋ ๋ฌด์์ธ์ง ํ ๊ธฐ์ ๋ฌธ์์ ํ๋ก ์ ๋ฆฌํ์ต๋๋ค.
์ด๋ฅผ ํตํด data๋ฅผ ๋ฐ์์ค๋ ์์ฒญ๊ณผ ์ ๋ฐ์ดํธ ํ๋ ์์ฒญ ๊ฐ์ ๊ด๊ณ๋ฅผ ํ ๋์ ํ์ ํ ์ ์์์ต๋๋ค.
์ด๋ฌํ ๊ณผ์ ์ ํตํด, ํน์ data์ ์ ๊ทผ ์ ๋ฌด์กฐ๊ฑด ์๋ฒ๋ก๋ถํฐ data๋ฅผ ๋ฐ์์ค๋ ๊ฒ์ด ์๋๋ผ, data๊ฐ ๋ณ๊ฒฝ์ด ๋์์ ๋๋ง ์๋ฒ์์ ์๋ก์ด data๋ฅผ ๋ฐ์์ด์ผ๋ก์จ ๋ถํ์ํ API ํธ์ถ์ ์ค์์ต๋๋ค.
์์ ๊ณผ์ ์ ์ค์ ๋ก ํ์ด์ง ๋ด์์ ์ด๋ป๊ฒ ์ ์ฉํ๋์ง ์ดํด๋ณด๊ฒ ์ต๋๋ค.
์ ํ์ด์ง๋ ๋ด ๊ณํ ํ์ด์ง์ ๋๋ค. ์ด ํ์ด์ง์์๋ ๋ณธ์ธ์ด ์์ฑํ ๊ณํ๋ค์ ๋ณด์ฌ์ฃผ๋๋ฐ, ์ด data๋ ๋ณธ์ธ์ด ๊ณํ์ ์์ฑํ๊ฑฐ๋ ์ญ์ ํ๊ธฐ ์ ๊น์ง๋ ๋ณ๊ฒฝ๋ ๊ฐ๋ฅ์ฑ์ด ์์ต๋๋ค.
๋ฐ๋ผ์, staleTime์ ๊ธฐ๋ณธ์ ์ผ๋ก infinity
๋ก ์ค์ ํด์ฃผ๊ณ , ๊ณํ ์์ฑ API์ ๊ณํ ์ญ์ API๊ฐ ํธ์ถ๋ ๊ฒฝ์ฐ์๋ง ์ฟผ๋ฆฌ๋ฅผ ๋ฌดํจํํด์ฃผ์์ต๋๋ค.
์์ธํ ๊ณผ์ ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
๋ด ๊ณํ์ ๋ถ๋ฌ์ค๋ get ์์ฒญ์ ๋ํ useQuery์ staleTime์ infinity
๋ก ์ค์ ํฉ๋๋ค.
export const useGetMyPlansQuery = () => {
const { data } = useSuspenseQuery({
queryKey: [QUERY_KEY.MY_PLANS],
queryFn: getMyPlans,
staleTime: Infinity,
});
return { myPlans: data! };
};
๊ณํ์ ์์ฑํ์ ๋, ์ฆ ๊ณํ์ ์์ฑํ๋ mutation ํจ์์ ์์ฒญ์ด ์ฑ๊ณตํ์ ๋, ๊ณํ์ ๋ถ๋ฌ์ค๋ query๋ฅผ ๋ฌดํจํํฉ๋๋ค.
export const usePostNewPlanMutation = () => {
const queryClient = useQueryClient
return useMutation({
mutationFn: postNewPlan,
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: [QUERY_KEY.MY_PLANS],
});
},
});
};
queryClient.invalidateQueries({ queryKey: [QUERY_KEY.MY_PLANS], });
์ด ๋ถ๋ถ์์ ์ฟผ๋ฆฌํค๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํน์ ์ฟผ๋ฆฌ๋ง ๋ฌดํจํ์์ผ์ฃผ์์์ ํ์ธํ ์ ์์ต๋๋ค.
์ด๋ฅผ ํตํด ๋ด ๊ณํ data๋ฅผ ํ์ด์ง์ ์ ๊ทผํ ๋๋ง๋ค ์๋ฒ๋ก๋ถํฐ ๋ฐ์์ค๋ ๊ฒ์ด ์๋๋ผ ๋ด ๊ณํ data๊ฐ ๋ฐ๋๋ ์ํฉ์ธ ๊ณํ ์์ฑ๊ณผ ๊ณํ ์ญ์ ์ ์ฑ๊ณตํ์ ๋๋ง ์๋ก ์๋ฒ์์ data๋ฅผ ๋ฐ์์์ต๋๋ค.
Tanstack-query์ staleTime ๋ฐ ์ฟผ๋ฆฌ ๋ฌดํจํ ๊ธฐ๋ฅ์ ๋ํ ์์ธํ ์ค๋ช ๋ฐ ์๋ฆฌ๋ ๋ฐ๋ก TanStack-query staleTime & invalidQueries๋ฅผ ์ด์ฉํ data ์ํ๊ด๋ฆฌ์ ์์ธํ๊ฒ ์ ๋ฆฌํด๋์์ต๋๋ค.
์ ํฌ ์๋น์ค์๋ ๊ณํ ๊ณต๊ฐ ์ฌ๋ถ, ์๋ฆผ ์ฌ๋ถ ๋ฑ์ ์ํ๋ฅผ toggle ํ ์ ์๋ ํ ๊ธ ๋ฒํผ์ด ์ฌ๋ฌ ํ์ด์ง์ ์กด์ฌํฉ๋๋ค. ์๋ ๊ทธ๋ฆผ์์ ํ๋์์ผ๋ก ๊ฐ์ธ์ ธ์๋ ๋ฒํผ๋ค์ด ๋ชจ๋ ํ ๊ธ ๋ฒํผ์ ๋๋ค.
์ด๋ฌํ ํ ๊ธ ๋ฒํผ์ ํน์ฑ ์, ์ ์ ๋ ๋ฒํผ์ ์ฐ์์ผ๋ก ๋๋ฅผ ์ ์์ต๋๋ค. ๋ง์ฝ ์ ์ ๊ฐ ์งง์ ์๊ฐ ๋์ ๋ฒํผ์ ๊ต์ฅํ ์ฌ๋ฌ ๋ฒ ๋๋ฅธ๋ค๋ฉด ์ด๋ ๊ทธ ํ์ ๋งํผ์ API ํธ์ถ์ ์๋ฏธํ ๊ฒ์ ๋๋ค. ๊ทธ๋์ ํ ๊ธ ๋ฒํผ์ ๋๋ ์ ๋ ๋ฐ์ํ๋ API ํธ์ถ์ ๋ํด ๋๋ฐ์ด์ค๋ฅผ ์ ์ฉํด ์์ ๊ฐ์ ์ํฉ์ ๋ฐฉ์งํ์ต๋๋ค.
๋๋ฐ์ด์ค๋ ?
์ด๋ฒคํธ๋ฅผ ๊ทธ๋ฃนํํ์ฌ ํน์ ์๊ฐ์ด ์ง๋ ํ ํ๋์ ์ด๋ฒคํธ๋ง ๋ฐ์ํ๋๋ก ํ๋ ๊ธฐ์ ์ ๋๋ค.
๋ง์ฝ ์ ์ ๊ฐ ์งง์ ์๊ฐ ๋์ ํน์ ๋ฒํผ์ ์ฐ์ํด์ ๋๋ฅธ๋ค๋ฉด, ์ฌ๋ฌ ๋ฒ์ ์์ฒญ ์ค ๋ง์ง๋ง ์์ฒญ๋ง ์๋ฒ์ ์ ๋ฌํ๋๋ก ํ์ต๋๋ค. ์ ์ ๊ฐ ์์ฃผ ๋๋ฅผ ๊ฐ๋ฅ์ฑ์ด ์๋ ๊ณํ ๊ณต๊ฐ ๋ฒํผ
, ๋ฆฌ๋ง์ด๋ ์๋ฆผ ์ฌ๋ถ ๋ณ๊ฒฝ ๋ฒํผ
์ ํ ๊ธ ๋ฒํผ์ ๋๋ฐ์ด์ค ์ฒ๋ฆฌ๋ฅผ ํด์ค์ผ๋ก์จ ์ ์ฌ์ ์ธ API ํธ์ถ ํ์๋ฅผ ์ค์ผ ์ ์์์ต๋๋ค.
๋ถํ์ํ API ํธ์ถ์ ์ค์ฌ๋ณด์! ๋ผ๋ ์๊ฐ์ผ๋ก ์ฌ๋ฌ๊ฐ์ง ๋ฐฉ๋ฒ๋ค์ ๊ณ ๋ฏผํด๋ณด์๊ณ , ๊ฒฐ๋ก ์ ์ผ๋ก๋ ๋ค์๊ณผ ๊ฐ์ ๋ฐฉ๋ฒ๋ค์ ์ ์ฉํ์ต๋๋ค.
Next.js middleware ๊ธฐ๋ฅ์ ์ด์ฉํ ํ์ด์ง ์์ฒญ ๋ฆฌ๋ค์ด๋ ํธ
Tanstack-query์ staleTime ์ค์ ๋ฐ ์ฟผ๋ฆฌ ๋ฌดํจํ
ํ ๊ธ ๋ฒํผ์ ๋๋ฐ์ด์ค ์ฒ๋ฆฌ ์ ์ฉ
์์ผ๋ก๋ API ํธ์ถ์ ๋ ํจ์จ์ ์ผ๋ก ํ ์ ์๋ ๋ฐฉ๋ฒ๋ค์ ๋ํด ๊ณ ๋ฏผํด๋ณด๊ณ , ํ์ฌ ์ ํฌ ์๋น์ค์ ์ ์ฉํ ์ ์๋ ์ํฉ์ด ๋๋ค๋ฉด ์ ์ฉํ๊ณ ๋ ๊ธ๋ก ์ ๋ฆฌํด๋ณด๋ ค๊ณ ํฉ๋๋ค!