๐Ÿš€ [Next.js] BFF ์„œ๋ฒ„ ์ฟ ํ‚ค ์œ ์‹ค ๋ฌธ์ œ ๋ฐ ์„œ๋ฒ„ ์—๋Ÿฌ ์ „์—ญ ํ•ธ๋“ค๋ง ์ฒ˜๋ฆฌํ•˜๊ธฐ


์ด ๊ธ€์—์„œ๋Š” ์•„๋ž˜ ๋‘ ๊ฐ€์ง€ ์ฃผ์ œ์— ๋Œ€ํ•ด ๋‹ค๋ฃฐ ๊ฒƒ์ด๋‹ค.

  1. BFF ์„œ๋ฒ„๊ฐ€ ํด๋ผ์ด์–ธํŠธ์˜ ์—ญํ• ์„ ํ•  ๋•Œ, ๋ธŒ๋ผ์šฐ์ €์™€ ๋‹ฌ๋ฆฌ ํ•„์š”ํ•œ ์ฟ ํ‚ค๊ฐ€ ์—†๋Š” ์ƒํƒœ์— ๋Œ€ํ•œ ํ•ด๊ฒฐ ๋ฐฉ์•ˆ
    1. ์„ค๋ช…์„ ๋”ํ•˜์ž๋ฉด BFF์—์„œ API ์š”์ฒญ์„ ํ†ตํ•ด ๋‚ด๋ถ€ ์ž์›์— ์ ‘๊ทผํ•˜๋Š” ๊ฒฝ์šฐ, ์ฆ‰ getServerSideProps๋ฅผ ํ†ตํ•ด ํŽ˜์ด์ง€๋ฅผ ๊ตฌ์„ฑํ•  ๋•Œ ํ•„์š”ํ•œ ๋ธŒ๋ผ์šฐ์ € ์ฟ ํ‚ค ๊ฐ’์„ ๊ฐ€์ง€๊ณ  ์žˆ์ง€ ์•Š์€ ์ฑ„๋กœ ์š”์ฒญ์„ ๋ณด๋‚ด๊ฒŒ ๋˜๋Š” ๊ฒฝ์šฐ
    2. Next๋Š” ์ด๊ฑธ ๋ถˆํ•„์š”ํ•œ ๋„คํŠธ์›Œํฌ ํ†ต์‹ ์„ ๋Š˜๋ฆฌ๋Š” ๋ฐฉํ–ฅ์ด๋ผ๊ณ  ํ–ˆ์œผ๋‚˜, ๋ธŒ๋ผ์šฐ์ €๋“  ์„œ๋ฒ„๋“  API๋ฅผ ์‚ฌ์šฉํ•ด ํ†ต์‹ ํ•˜๋ฏ€๋กœ page route์—์„œ validate, ๊ถŒํ•œ ์ฒดํฌ ๋“ฑ์„ ํ•œ ๋ฒˆ์— ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด ํ•ด๋‹น ๋ฐฉ์‹์„ ์„ ํƒํ–ˆ๋‹ค.
  2. ์ „์—ญ์ ์ธ ์„œ๋ฒ„ ์—๋Ÿฌ ์ฒ˜๋ฆฌ - [ํŽ˜์ด์ง€ ๊ถŒํ•œ ์ฒ˜๋ฆฌ, ErrorBoundary, ๊ฐ ์ปดํฌ๋„ŒํŠธ์—์„œ์˜ ์ฒ˜๋ฆฌ]

BFF ์„œ๋ฒ„๊ฐ€ ํด๋ผ์ด์–ธํŠธ์˜ ์—ญํ• ์„ ํ•  ๋•Œ, ๋ธŒ๋ผ์šฐ์ €์™€ ๋‹ฌ๋ฆฌ ํ•„์š”ํ•œ ์ฟ ํ‚ค๊ฐ€ ์—†๋Š” ์ƒํƒœ์— ๋Œ€ํ•œ ํ•ด๊ฒฐ ๋ฐฉ์•ˆ


๐Ÿซ  ๋ฌธ์ œ์ 

์ฒ˜์Œ์—๋Š” ๋ฌด์—‡์ด ๋ฌธ์ œ์ธ์ง€ ์ดํ•ดํ•˜๋Š” ๊ฒƒ ์กฐ์ฐจ ์–ด๋ ค์› ๋‹ค. BFF ์„œ๋ฒ„๊ฐ€ ํด๋ผ์ด์–ธํŠธ์˜ ์—ญํ• ๊ณผ ์„œ๋ฒ„์˜ ์—ญํ• ์„ ๋™์‹œ์— ํ•˜๊ณ  ์žˆ๊ธฐ์— ๋ˆˆ์— ๋ณด์ด์ง€ ์•Š๋Š” ๋ถ€๋ถ„์„ ์˜๋„์ ์œผ๋กœ ๊ทธ๋ ค๋‚ด์„œ ์ดํ•ดํ•ด์•ผ ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

๋กœ๊ทธ์ธ์„ ๋งˆ์นœ ์œ ์ €๊ฐ€ (๋กœ๊ทธ์ธ์ด ํ•„์š”ํ•œ) ํŠน์ • ํŽ˜์ด์ง€์— ์ ‘์†ํ•˜๋ ค๊ณ  ํ•˜๋ฉด,

๋ธŒ๋ผ์šฐ์ €๋Š” ์„œ๋ฒ„์—๊ฒŒ ํŽ˜์ด์ง€ ์š”์ฒญ์„ ํ•˜๊ณ , ์ดํ›„์— ์‹คํ–‰๋˜๋Š” getServerSideProps ์—์„œ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ tanstack-query๋ฅผ ํ†ตํ•ด prefetch ํ•œ๋‹ค.

  • ๋”ฐ๋ผ์„œ ์„œ๋ฒ„(BFF)์—์„œ ๋™์ž‘ํ•˜๋Š” ์ฝ”๋“œ์ž„์—๋„ ๋‹ค์‹œ API ์š”์ฒญ์„ ๋ณด๋‚ด๊ฒŒ ๋˜๋Š”๋ฐ, ์ด ๋•Œ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ์ฟ ํ‚ค๋ฅผ ํ•จ๊ป˜ ์ „์†กํ•˜์ง€ ์•Š๊ณ  ์žˆ์–ด์„œ UnauthorizedError ๋ฐœ์ƒ

์„œ๋ฒ„์—์„œ prefetch ๊ฒฐ๊ณผ๊ฐ€ ์—๋Ÿฌ์˜€์œผ๋ฏ€๋กœ ๋ธŒ๋ผ์šฐ์ €์—์„œ ๋‹ค์‹œ ๋ฐ์ดํ„ฐ ์š”์ฒญ

  • ์ด ๋•Œ๋Š” ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์š”์ฒญ์„ ๋ณด๋‚ด๊ธฐ ๋•Œ๋ฌธ์— ์ฟ ํ‚ค๊ฐ€ ์„ค์ •๋˜์–ด ์žˆ์Œ

โ†’ prefetching์˜ ์˜๋ฏธ๊ฐ€ ์ „ํ˜€ ์—†๋Š” SSR์„ ํ•˜๊ณ  ์žˆ์—ˆ๋‹ค.

๐Ÿ› ๏ธย ํ•ด๊ฒฐ ๋ฐฉ์•ˆ

๊ทธ๋Ÿฌ๋ฉด ์„œ๋ฒ„์—์„œ ๋ณด๋‚ด๋Š” ์š”์ฒญ์ผ ๊ฒฝ์šฐ์—๋„ ๋ธŒ๋ผ์šฐ์ €์˜ ์ฟ ํ‚ค๋ฅผ ์‹ค์–ด์„œ ์š”์ฒญ์„ ๋ณด๋‚ด์ฃผ๋ฉด ๋ฌธ์ œ๊ฐ€ ํ•ด๊ฒฐ๋˜๊ฒ ๊ตฌ๋‚˜ ์‹ถ์—ˆ๋‹ค.

๋จผ์ € ๊ธฐ์กด์˜ getServerSideProps๊ฐ€ ์–ด๋–ป๊ฒŒ ์งœ์—ฌ์žˆ์—ˆ๋Š”์ง€ ์‚ดํŽด๋ณด๋ฉด, withSessionSsr์ด๋ผ๋Š” HOF๋ฅผ ํ†ตํ•ด req์˜ ์ฟ ํ‚ค๋ฅผ ์ฝ๊ณ  ๊ฒ€์ฆํ•œ ๋’ค์— ์œ ํšจํ•œ ๊ฒฝ์šฐ์— session์— ์œ ์ € ์ •๋ณด๋ฅผ ๋‹ด์•„์„œ ๋‚ด๋ ค์ค€๋‹ค.

// page/.../index.jsx

export const getServerSideProps = withSessionSsr(async (context) => {
    const queryClient = new QueryClient();
    const session = context.req.session; // HOF์—์„œ ๋‹ด์•„์ค€ ์œ ์ € ์ •๋ณด๊ฐ€ ์žˆ์Œ
    if (session) {
        /** session ์ •๋ณด ์„ธํŒ… */
        await queryClient.prefetchQuery(...);
    }

		// ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ prefetch -> ์ด ๋•Œ ์ฟ ํ‚ค๋ฅผ ํ•จ๊ป˜ ๋„˜๊ฒจ์ค˜์•ผ ํ•œ๋‹ค.
    await queryClient.prefetchQuery(...);

    return {
        props: {
            dehydratedState: dehydrate(queryClient),
        },
    };
});
**๋ฐฉ์•ˆ 1 ๋ช…์‹œ์ ์œผ๋กœ ํ—ค๋”์— ์ฟ ํ‚ค๋ฅผ ๋งค๋ฒˆ ๋„ฃ์–ด์ฃผ๊ธฐ**
  • prefetching์‹œ ์ฟ ํ‚ค๋ฅผ ํ•จ๊ป˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ „๋‹ฌ
  • ๋‚ด๋ถ€์—์„œ๋Š” ์ธ์ž๋กœ ๋ฐ›์€ ์ฟ ํ‚ค๋ฅผ ์ง์ ‘ ํ—ค๋”์— ์„ค์ •ํ•˜์—ฌ axios ์š”์ฒญ์„ ๋ณด๋ƒ„
**๋ฐฉ์•ˆ 2 axiosInstance.interceptors ์‚ฌ์šฉํ•˜๊ธฐ**

๋ฐฉ์•ˆ 1์€ ์—ฌ๋Ÿฌ ๊ณณ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ถ”๊ฐ€์ ์œผ๋กœ ๋„˜๊ฒจ์ฃผ๊ณ , ํ—ค๋”๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ์„ค์ •ํ•ด์ฃผ์–ด์•ผ ํ•˜๋Š”๋ฐ, ๋„ˆ๋ฌด ๊ท€์ฐฎ๊ณ  ์ค‘๊ฐ„์— ๋ณ€์งˆ๋  ๊ฐ€๋Šฅ์„ฑ๋„ ํฌ๋‹ค.

  • ์ถ”๊ฐ€ํ•ด์ฃผ์–ด์•ผ ํ•˜๋Š” ์ฝ”๋“œ๊ฐ€ ๋‘ ๊ณณ์ธ๋ฐ ์ด๋ฏธ ๋„ˆ๋ฌด ๋ณต์žกํ•œ ๋‹จ๊ณ„๋ฅผ ๊ฑฐ์ณ ์š”์ฒญ์ด ๊ฐ€๊ณ  ์žˆ๋Š” ์ƒํ™ฉ์— ์ด๊ฒƒ๊นŒ์ง€ ์—ฌ๋Ÿฌ ๊ณณ์—์„œ ์‹ ๊ฒฝ์“ฐ๊ฒŒ ๋˜๋ฉด DX๊ฐ€ ๋„ˆ๋ฌด ๋–จ์–ด์งˆ ๊ฒƒ ๊ฐ™์•˜๋‹ค.

๋”ฐ๋ผ์„œ axiosInstance.interceptors๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ 1์ฐจ์ ์œผ๋กœ ๋ฆฌํŒฉํ† ๋ง ํ–ˆ๋‹ค.

  • ๋งค ์š”์ฒญ ์‹œ ํ•ด๋‹น ์š”์ฒญ์„ ๊ฐ€๋กœ์ฑ„์„œ ์›ํ•˜๋Š” ์ฟ ํ‚ค ๊ฐ’์„ ๋„ฃ์–ด์คŒ (๊ณตํ†ต ํ—ค๋”๋ฅผ ์„ค์ •ํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๊ณ  ๊ฐ ์š”์ฒญ์— ๋Œ€ํ•œ ์ฒ˜๋ฆฌ์ด๋ฏ€๋กœ ๋™์‹œ์— ์š”์ฒญ ๋ณด๋ƒˆ์„ ๊ฒฝ์šฐ์—๋„ ์ฟ ํ‚ค๊ฐ€ ๋ฎ์–ด์”Œ์›Œ์ง€์ง€ ์•Š์Œ)
  • getServerSideProps์—์„œ๋Š”ย prefetching์‹œ ์š”์ฒญ์„ ์ธํ„ฐ์…‰ํŠธํ•  ์ˆ˜ ์žˆ๋„๋ก ์ฒ˜๋ฆฌ๋œ axios ์ธ์Šคํ„ด์Šค๋ฅผ ์ „๋‹ฌ
**๋ฐฉ์•ˆ 3 ์ฟ ํ‚ค๊ฐ€ ์„ค์ •๋œ axiosInstance๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ**

๋‹ค๋งŒ ์œ„ ๋ฐฉ์•ˆ ์‚ฌ์šฉ ์‹œ ๊ฐ ์š”์ฒญ๊ฐ„์˜ ์ถฉ๋Œ์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด์„ (์—ฌ๋Ÿฌ ์š”์ฒญ์ด ๋™์‹œ์— ๋“ค์–ด์™€๋„ ๋…๋ฆฝ์ ์ธ ๊ฐ์ž์˜ ์ฟ ํ‚ค๋ฅผ ๊ฐ€์ ธ์•ผ ํ•จ) ๋งค ๋ฒˆ ์ƒˆ๋กญ๊ฒŒ ์ธ์Šคํ„ด์Šค๋ฅผ ๋งŒ๋“ค์–ด์„œ ๋ณด๋‚ด์•ผ ํ–ˆ๊ณ , ๊ทธ๋ ‡๋‹ค๋ฉด ๊ตณ์ด interceptors๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•„๋„ ๋  ๊ฒƒ ๊ฐ™๋‹ค๋Š” ์˜๊ฒฌ์ด ์žˆ์—ˆ๋‹ค.

thanks to. @ํšŒ์‚ฌ_๋™๋ฃŒ_luke์˜ ์ƒ์‚ฐ์ ์ธ ์ฝ”๋“œ๋ฆฌ๋ทฐ

์ฒ˜์Œ์—๋Š” interceptors๋ผ๋Š” ์ƒˆ๋กœ์šด ๊ฒƒ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์— ๋“ค๋–ด์—ˆ๋Š”๋ฐ ๋•๋ถ„์— ๋‹ค์‹œ ๋…ผ๋ฆฌ์ ์œผ๋กœ ์ƒ๊ฐ์„ ํ•  ์ˆ˜ ์žˆ์—ˆ๊ณ  ๋ถˆํ•„์š”ํ•˜๊ฒŒ ์ธํ„ฐ์…‰ํ„ฐ๋ฅผ ์ถ”๊ฐ€ํ–ˆ๋‹ค๋Š” ๊ฒƒ์„ ๊นจ๋‹ฌ์•˜๋‹ค.

  • ์„œ๋ฒ„์—์„œ ๋ณด๋‚ด๋Š” ์š”์ฒญ์€ย getServerSidePropsย ์—์„œย createServerAxios (์ฟ ํ‚ค๊ฐ€ ๋‹ด๊ธด axios ์ธ์Šคํ„ด์Šค๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜)๋ฅผ ํ†ตํ•ด ์ƒ์„ฑ

์ด๋ ‡๊ฒŒ ์ค‘๊ฐ„์— ์ฟ ํ‚ค๋ฅผ ๋‹ด์•„์„œ ์š”์ฒญ์„ ๋ณด๋‚ผ ์ˆ˜ ์žˆ๋„๋ก ์ฒ˜๋ฆฌํ•จ์œผ๋กœ์จ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

๐ŸŒค๏ธย ์ž์ž˜ํ•œ ์‹œํ–‰ ์ฐฉ์˜ค

queryClient.fetchQuery ์™€ queryClient.prefetchQuery

  • prefetchQuery ๋Š” ์‹คํŒจ ์‹œ์— ๋ณ„๋„๋กœ ์—๋Ÿฌ๋ฅผ ๋˜์ง€์ง€ ์•Š๊ณ  ๊ฒฐ๊ณผ์— ์—๋Ÿฌ๋ฅผ ๋‹ด์•„์„œ ๋ณด๋‚ด์ค€๋‹ค.
    • prefetch ์˜ ๋ชฉ์ ์„ ์ƒ๊ฐํ•ด๋ณด๋ฉด ์ดํ•ด๊ฐ€ ๋˜๋Š” ๋™์ž‘์ด๊ธด ํ•˜๋‹ค. ์˜ค๋ฅ˜ ์—ฌ๋ถ€์™€๋Š” ์ƒ๊ด€์—†์ด ๋ฏธ๋ฆฌ ๋ฐ์ดํ„ฐ๋ฅผ ์ฐŒ๋ฅด๋Š” ๊ฒƒ์ผ ๋ฟ์ด๊ณ  ์ดํ›„์— ๋Œ€ํ•œ ๊ฒฐ๊ณผ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ณณ์—์„œ ๋‹ค์‹œ ์š”์ฒญ์„ ๋ณด๋‚ผ ์ง€, ์—๋Ÿฌ์— ๋Œ€ํ•ด ์ฒ˜๋ฆฌํ•  ์ง€ ๊ฒฐ์ •ํ•˜๋Š”๊ฒŒ ๋งž๋Š” ๊ฒƒ ๊ฐ™๋‹ค.
  • ์‹คํŒจ ์‹œ ์—๋Ÿฌ๋ฅผ ๋ฐ”๋กœ ๋ฐ›์•„์„œ ์ฒ˜๋ฆฌํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด fetchQuery ๋ฅผ ์‚ฌ์šฉํ•˜์ž.

req.cookies ์™€ req.headers.cookie

  • axios์˜ req ์™€ next ์„œ๋ฒ„์˜ req ํ˜•์‹์ด ๋‹ค๋ฅด๊ธฐ ๋•Œ๋ฌธ์— axios req.headers.cookie๋กœ ๋‹ด์•„์ค€ ๊ฐ’์„ next ์„œ๋ฒ„์ชฝ ์ฝ”๋“œ์ธ checkAuthentication์—์„œ ์ฝ์ง€ ๋ชปํ•จ (req.cookies๋กœ ์ ‘๊ทผ)
  • ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด, api ๋ผ์šฐํŠธ์—์„œ ์‚ฌ์šฉํ•˜๋Š” checkAuth() -> withSessionRoute ์—์„œ req.headers.cookie๋ฅผ req.cookies์— ๋„ฃ์–ด์คŒ
    • ์‹ค์ œ๋กœ next-connext ์—์„œ ํ•˜๊ณ  ์žˆ๋Š” ์ž‘์—… (๊ทผ๋ฐ ์™œ ์„œ๋ฒ„์—์„œ ์š”์ฒญ๋ณด๋ƒˆ์„ ๋•Œ๋Š” ์ € ์ฒ˜๋ฆฌ๊ฐ€ ์•ˆ๋˜์–ด์žˆ๋Š” ๊ฑด์ง€ ์ •ํ™•ํ•˜๊ฒŒ ์ดํ•ด x, ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ๋ณด๋ƒˆ๊ฑฐ๋‚˜ bff ๊ฐ€ ๋ณด๋ƒˆ๊ฑฐ๋‚˜ ์–ด์จŒ๋“  ๊ฐ™์€ axios ์š”์ฒญ์ด ๊ทธ๋Œ€๋กœ api ๋ผ์šฐํŠธ๋กœ ๋„˜์–ด๊ฐ€๋Š”๊ฒŒ ์•„๋‹Œ๊ฐ€?) แ„‰แ…ณแ„แ…ณแ„…แ…ตแ†ซแ„‰แ…ฃแ†บ 2023-07-27 แ„‹แ…ฉแ„’แ…ฎ 10 04 44

์ „์—ญ์ ์ธ ์„œ๋ฒ„ ์—๋Ÿฌ ์ฒ˜๋ฆฌ

  • [ํŽ˜์ด์ง€ ๊ถŒํ•œ ์ฒ˜๋ฆฌ, ErrorBoundary, ๊ฐ ์ปดํฌ๋„ŒํŠธ์—์„œ์˜ ์ฒ˜๋ฆฌ]

๐Ÿ“ย ์ „์ฒด์ ์ธ ์ปจ์…‰

  • ์„œ๋ฒ„ ์—๋Ÿฌ ํ•ธ๋“ค๋ง _error.jsx์—์„œ ํ•˜๊ธฐ
    • ์—๋Ÿฌ ๋ฐœ์ƒ ์‹œ ๋ฌด์กฐ๊ฑด ์—ฌ๊ธฐ๋กœ ๋–จ์–ด์ง
    • ๋‹ค๋งŒ ์‹ค์ œ ์—๋Ÿฌ ํ™”๋ฉด์ด ๋„์›Œ์ง€๋Š” ๊ฒƒ์€ production์ผ ๋•Œ๋งŒ์ด๊ณ  dev์—์„œ๋Š” ์—๋Ÿฌ ๋ฐœ์ƒ ํ™”๋ฉด์ด ๋ณด์—ฌ์ง (for ๋””๋ฒ„๊น…)

      pages/_error.jsย is only used in production. In development youโ€™ll get an error with the call stack to know where the error originated from.

    • ๊ฐœ๋ฐœ ๋‚ด๋‚ด ๊ถŒํ•œ ์ฒ˜๋ฆฌ๊ฐ€ ์ž˜ ๋˜๋Š”์ง€ ํ™•์ธํ•  ์ผ์€ ์—†์„๊ฑฐ๋‹ˆ๊นŒ ํ•„์š”ํ•œ ๊ฒฝ์šฐ์—๋งŒ production ๋นŒ๋“œํ•ด์„œ ํ™•์ธ
  • ๊ธฐ์กด์— ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋˜ ์„ธ์…˜ ๊ด€๋ จ HOF์—์„œ ํŽ˜์ด์ง€ ๊ถŒํ•œ ์ฒ˜๋ฆฌ๋„ ํ•จ๊ป˜ ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ™•์žฅ
    • ํŽ˜์ด์ง€์— ๋Œ€ํ•œ ๊ถŒํ•œ ์ฒดํฌ๋„ ํ•  ์ˆ˜ ์žˆ๋„๋ก
    • ์ง€๊ธˆ์€ ํŽ˜์ด์ง€์— ๋Œ€ํ•œ ๊ถŒํ•œ ์ฒดํฌ๋Š” ์—†๊ณ  api ์š”์ฒญ์„ ๋ณด๋‚ด๋Š” ๊ฒฝ์šฐ์— ๊ทธ ์š”์ฒญ์— ๋Œ€ํ•œ ๊ถŒํ•œ ํ™•์ธ + ์‘๋‹ต์œผ๋กœ ํ™•์ธ์ด ๊ฐ€๋Šฅํ•œ๋ฐ, api ์š”์ฒญ์„ ์ฒ˜์Œ๋ถ€ํ„ฐ ๋ณด๋‚ด์ง€ ์•Š๋Š” ํŽ˜์ด์ง€์—์„œ ์ ‘๊ทผ์„ ๋ง‰์•„์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ์Œ

๐Ÿซ  ๋ฌธ์ œ์ 

NEXT๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ์žˆ๋Š” ๋‹ค๋ฅธ ๋ ˆํฌ์—์„œ๋Š” ์ง์ ‘ ์„œ๋ฒ„๋ฅผ ๊ตฌ์„ฑํ•˜๋‹ค ๋ณด๋‹ˆ, ํŽ˜์ด์ง€์— ๋Œ€ํ•œ ์ ‘๊ทผ ๊ถŒํ•œ ์ฒ˜๋ฆฌ๋ฅผ routes๋“ค์ด ๋ชจ์—ฌ ์žˆ๋Š” ํ•œ ๊ณณ์—์„œ ํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ NEXT ์‚ฌ์šฉ ์‹œ์—๋Š” ํ”ํ•˜๊ฒŒ ์‚ฌ์šฉ๋˜๋Š” ํŒจํ„ด์€ ์•„๋‹Œ์ง€ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ์—๋Ÿฌ ํŽ˜์ด์ง€๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ๊ฒƒ์€ ์‰ฌ์› ์œผ๋‚˜, ์šฐ๋ฆฌ๊ฐ€ ๊ตฌ๋ถ„ํ•ด๋‘” ์—๋Ÿฌ statusCode์™€๋Š” ๋ฌด๊ด€ํ•˜๊ฒŒ ๋ชจ๋‘ 500.js๋กœ ๋–จ์–ด์ ธ๋ฒ„๋ ธ๋‹ค.

์•„๋ž˜์˜ ์š”๊ตฌ์‚ฌํ•ญ์„ ๋งŒ์กฑํ•˜๋Š” ๊ตฌ์กฐ๋ฅผ ๋งŒ๋“ค์–ด ๋‚ด๊ธฐ ์œ„ํ•ด ์—ฌ๋Ÿฌ ์‹œ๋„๋ฅผ ํ•ด๋ณด์•˜๋‹ค.

  • ๊ฐ ํŽ˜์ด์ง€์˜ getServerSideProps ๋งˆ๋‹ค ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ์‹ถ์ง€ ์•Š์Œ
  • API (axios) ์—๋Ÿฌ์™€ ์ธ์ฆ๊ณผ ๊ด€๋ จํ•ด์„œ ๊ทธ๋ƒฅ throw ํ•˜๋Š” ์—๋Ÿฌ ๋ชจ๋‘ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ์–ด์•ผ ํ•จ

๐Ÿ› ๏ธย ํ•ด๊ฒฐ ๋ฐฉ์•ˆ

**๋ฐฉ์•ˆ 1 getServerSideProps๋ฅผ tryโ€ฆcatch ๋กœ ๊ฐ์‹ธ์ฃผ๋Š” HOC** โ†’ โŒ
  • ๋ง ๊ทธ๋Œ€๋กœ getServerSideProps๋ฅผ tryโ€ฆcatch๋กœ ๊ฐ์‹ธ์„œ ๋ฐœ์ƒํ•œ ์—๋Ÿฌ์— ๋Œ€ํ•œ ์ฒ˜๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค.
  • ๊ฐ ํŽ˜์ด์ง€๋งˆ๋‹ค ์ถ”๊ฐ€๋ฅผ ํ•ด์ค˜์•ผํ•˜๋ฏ€๋กœ ์š”๊ตฌ์‚ฌํ•ญ 1 ์„ ๋งŒ์กฑ์‹œํ‚ค์ง€ ๋ชปํ•ด์„œ ํŒจ์Šคํ–ˆ๋‹ค.
**๋ฐฉ์•ˆ 2 middleware.js** โ†’ โŒ
  • ๋ฏธ๋“ค์›จ์–ด๋ฅผ ํƒ€๋Š” ์‹œ์ ์—๋Š” ์•„์ง ํ•ด๋‹น ํŽ˜์ด์ง€ ์š”์ฒญ์— ๋Œ€ํ•œ ์‘๋‹ต์ด ์™„๋ฃŒ๋˜์ง€ ์•Š์•„์„œ ํŒ๋‹จํ•  ์ˆ˜๊ฐ€ ์—†์œผ๋ฏ€๋กœ ํŒจ์Šค..
**๋ฐฉ์•ˆ 3 ErrorBoundary ๐Ÿค”**
  • ์ด๊ฑด ํŽ˜์ด์ง€ ๋‹จ์—์„œ ์ง์ ‘ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒƒ๊ณผ ๋‹ค๋ฆ„์ด ์—†์–ด๋ณด์ธ๋‹ค.
  • ํด๋ผ์ด์–ธํŠธ ์—๋Ÿฌ ์ฒ˜๋ฆฌ์— ์ ํ•ฉํ•œ ๊ตฌ์กฐ
  • ํŽ˜์ด์ง€ ๋‹จ์—์„œ ๊ถŒํ•œ์„ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•˜๊ธฐ์—” ๋ชฉ์ ์— ๋งž์ง€ ์•Š์€ ๊ฒƒ ๊ฐ™์•„์„œ ๋„˜์–ด๊ฐ„๋‹ค.
**๋ฐฉ์•ˆ 4 _app.js์˜ PageProps.error** โ†’ โŒ
  • getInitialProps ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” _error.js ํŽ˜์ด์ง€๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ๋˜์ ธ์ง„ ์—๋Ÿฌ๋ฅผ _app.js์—์„œ ํ•œ ๋ฒˆ์— ์„ค์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ์–ด์„œ ์‚ดํŽด๋ณด์•˜๋‹ค.
  • server side์—์„œ ๋ฐœ์ƒํ•œ ์—๋Ÿฌ๋Š” ์•ˆ์žกํ˜€์„œ ํŒจ์Šค..
**๋ฐฉ์•ˆ 5 ** _error.js โœ…
  • Next์—์„œ ์ œ์•ˆํ•˜๋Š” ์ปค์Šคํ…€ ์—๋Ÿฌ ํŽ˜์ด์ง€ ๊ณ ๋„ํ™” ๋ฐฉ๋ฒ•์ด๋‹ค. (๋ฐฉ์•ˆ 4์—์„œ๋„ ์ด ์ปจ์…‰์„ ์‚ฌ์šฉํ•˜์˜€๋‹ค.)
  • ์—ฌ๊ธฐ์„œ ๋‚˜๋Š” res.writeHead ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์—๋Ÿฌ์— ๋”ฐ๋ผ ๋ฏธ๋ฆฌ ๋งŒ๋“ค์–ด๋‘” ์—๋Ÿฌ ํŽ˜์ด์ง€(/403, โ€ฆ)๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ์‹œํ‚ค๋Š” ๋ฐฉ๋ฒ•์„ ์ฐจ์šฉํ–ˆ๋‹ค.
    • ๋ฏธ๋ฆฌ ์ฒ˜๋ฆฌํ•˜๊ณ  ์‹ถ์€ ์—๋Ÿฌ์— ๋Œ€ํ•ด์„œ๋Š” ๊ฐ ํŽ˜์ด์ง€๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ์‹œํ‚ค๊ณ , ๊ฒฐ๊ณผ์ ์œผ๋กœ _error.js์—์„œ๋Š” ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ์—๋Ÿฌ๋งŒ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ๊ฒŒ๋œ๋‹ค.
  • ๊ทธ๋Ÿฐ๋ฐ, ์„œ๋ฒ„ ์—๋Ÿฌ ํ•ธ๋“ค๋Ÿฌ๊นŒ์ง€๋Š” 401, 403โ€ฆ ๋กœ ์ž˜ ์˜ค๋Š”๋ฐ, _error์—์„œ ๋ฐ›์€๊ฑด res.statusCode๋Š” ์ผ๊ด„ 500์ด๋‹ค. ๐Ÿ˜ญ โ†’ response๋Š” ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ์œผ๋‹ˆ๊นŒ 500์ผ ์ˆ˜ ๋ฐ–์—..! ์—๋Ÿฌ ๊ฐ์ฒด๋ฅผ ๋ณด๋ฉด ๋œ๋‹ค.
// page/_error.js
CustomErrorPage.getInitialProps = ({ res, err }) => {
  const errorName = err.name || err.response?.data.code;
  let statusCode = 500;
  switch (errorName) {
    case "UnauthorizedError":
      statusCode = 401;
      break;
    default:
      break;
  }

  if (statusCode !== 500) {
    res.writeHead(302, { Location: `/${statusCode}` });
    res.end();
  }

  return { statusCode, myError: statusCode };
};

๊ด€๋ จํ•ด์„œ ์—ฌ๋Ÿฌ ๋ ˆํผ๋Ÿฐ์Šค๋ฅผ ์ฐพ์•„๋ณด๋‹ค๊ฐ€ ๋‚˜์™€ ๋™์ผํ•œ ์ƒํ™ฉ์— ์ฒ˜ํ•œ ์‚ฌ๋žŒ์˜ ์ด์Šˆ๋ฅผ ๋ฐœ๊ฒฌํ–ˆ๊ณ , ๋ฌด๋ ค 1๋…„ ์ „ ๊ธ€์ด์ง€๋งŒ ๋ˆ„๊ตฐ๊ฐ€์—๊ฒŒ๋ผ๋„ ๋„์›€์ด ๋˜๊ธธ ๋ฐ”๋ผ๋ฉฐ ๋Œ“๊ธ€์„ ๋‹ฌ์•„๋‘์—ˆ๋‹ค!

How to handle unathorized errors thrown by the server ยท vercel/next.js ยท Discussion #39530

๐ŸŒค๏ธย ์ž์ž˜ํ•œ ์‹œํ–‰ ์ฐฉ์˜ค

  • _error.jsx๋Š” ์—†๋‹ค
    • Next์—์„œ ์ œ๊ณตํ•˜๋Š” ์˜ˆ์•ฝ์–ด ํŽ˜์ด์ง€ ์…‹์ด๋ฏ€๋กœ ์ด๋ฆ„์„ ์ •ํ™•ํ•˜๊ฒŒ _error.js๋กœ ํ•ด์•ผํ•œ๋‹ค.



# ์นดํ…Œ๊ณ ๋ฆฌ