๐Ÿง‘‍๐Ÿ’ป ์งง์€ํ˜ธํก/React

[Next.js] ์„œ๋ฒ„ ์‚ฌ์ด๋“œ ๋ Œ๋”๋ง(SSR)๊ณผ cookie ๋กœ๊ทธ์ธ ์ •๋ณด ๋‹ค๋ฃจ๊ธฐ (getInitialProps๋ฅผ ์‚ฌ์šฉํ•ด๋„ ๋˜๋Š”๊ฐ€)

ํ•œ๊ทœ์ง„ 2023. 1. 22. 20:28

๋‘ฅ๋‘ฅ์ฆˆ

๋‘๋‘ฅ ํ”„๋กœ์ ํŠธ๋ฅผ ์‹œ์ž‘ํ•˜๋ฉด์„œ Next.js๋ฅผ ์ฒ˜์Œ ํ”„๋กœ์ ํŠธ์— ์‚ฌ์šฉํ–ˆ๋‹ค. ๊ธฐํš์ƒ ์ด์ „ ํ”„๋กœ์ ํŠธ๋ณด๋‹ค SEO๋ฅผ ๋” ์‹ ๊ฒฝ์จ์•ผํ–ˆ๊ณ , ์ด๋ฏธ์ง€ ์ตœ์ ํ™”์™€ ๊ฐ™์€ ๊ฒƒ๋“ค์„ ์ž๋™์œผ๋กœ ์ œ๊ณตํ•ด์ค€๋‹ค๋Š” ์ ์ด ์ข‹์•˜๋‹ค. ์ด์ „๋ถ€ํ„ฐ ๊ณ„์† ์จ๋ณด๊ณ  ์‹ถ์—ˆ๋˜ ๊ธฐ์ˆ ์ด์—ˆ์–ด์„œ ๊ณต๋ถ€ํ•˜๋ฉด์„œ ํ”„๋กœ์ ํŠธ์— ์‚ฌ์šฉํ•  ๊ฐ€์น˜๊ฐ€ ์žˆ์—ˆ๋‹ค. ์ด๋ฒˆ ํฌ์ŠคํŒ…์€ ์„œ๋ฒ„์‚ฌ์ด๋“œ ๋ Œ๋”๋ง ํ™˜๊ฒฝ์—์„œ ์นด์นด์˜ค ์†Œ์…œ ๋กœ๊ทธ์ธ์„ ๊ตฌํ˜„ํ•˜๋ฉด์„œ ๊ณต๋ถ€ํ–ˆ๋˜ ๋‚ด์šฉ๊ณผ ๋งˆ์ฃผ์ณค๋˜ ๊ณ ๋ฏผ๋“ค์„ ๊ธฐ๋กํ•œ ๊ธ€์ด๋‹ค.

 

๋ชฉ์ฐจ
1. Next.js์˜ ์„œ๋ฒ„ ์‚ฌ์ด๋“œ ๋ Œ๋”๋ง(SSR)
2. ์„œ๋ฒ„ ์‚ฌ์ด๋“œ์—์„œ ์ฟ ํ‚ค ์‚ฌ์šฉํ•˜๊ธฐ
3. getInitialProps๋ฅผ ์‚ฌ์šฉํ•ด๋„ ๋˜๋Š”๊ฐ€?

 

 

1. Next.js์˜ ์„œ๋ฒ„ ์‚ฌ์ด๋“œ ๋ Œ๋”๋ง

๊ธ€์„ ์ค€๋น„ํ•˜๋ฉด์„œ Next.js์—์„œ ์„œ๋ฒ„์‚ฌ์ด๋“œ ๋ Œ๋”๋ง์˜ ๋ฐฉ์‹์„ ์กฐ๊ธˆ ๋” ๊ณต๋ถ€ํ•ด๋ณด์•˜๋‹ค. ๋จผ์ € ๋‹ค๋“ค ์•„๋Š” ์–˜๊ธฐ. Next.js๋Š” SSR์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•˜์ง€๋งŒ, ํŽ˜์ด์ง€๊ฐ€ ๋กœ๋“œ๋œ ์ดํ›„์—๋Š” ์ผ๋ฐ˜์ ์ธ ๋ฆฌ์•กํŠธ์˜ CSR์„ ์ด์šฉํ•œ๋‹ค. ํŽ˜์ด์ง€๊ฐ€ ํ•œ๋ฒˆ ๋ Œ๋”๋ง๋œ ์ดํ›„์— (axios ๋“ฑ์œผ๋กœ) ํŒจ์น˜๋œ ๋ฐ์ดํ„ฐ๋“ค์„ ๋ณด์—ฌ์ค„๋• ํด๋ผ์ด์–ธํŠธ์ชฝ์—์„œ ๋‹ค์‹œ ๋ Œ๋”๋งํ•œ๋‹ค.

 

CSR์—์„  useEffect ๋ฅผ ํ†ตํ•ด ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ Œ๋”๋ง๋ ๋•Œ ๋ฐ์ดํ„ฐ ํŒจ์นญ์„ ํ•  ์ˆ˜ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ ์„œ๋ฒ„์‚ฌ์ด๋“œ ๋ Œ๋”๋ง์„ ํ•˜๋Š” Next.js์—์„œ ์ปดํฌ๋„ŒํŠธ๋Š” ์‚ฌ์ „์— ๋ถˆ๋Ÿฌ์™€์•ผํ•  ๋ฐ์ดํ„ฐ๊ฐ€ ํ•„์š”ํ•  ์ˆ˜ ์žˆ๋‹ค. getInitialProps์™€ getServerSideProps ๋“ฑ์„ ์ด์šฉํ•ด ์„œ๋ฒ„์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ํŒจ์นญํ•˜๊ณ , ๋ฐ˜ํ™˜๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•ด ๋ฏธ๋ฆฌ ๋ Œ๋”๋งํ•œ๋‹ค.

 

์„œ๋ฒ„์‚ฌ์ด๋“œ์˜ ํ๋ฆ„์€ ๋‚ด๊ฐ€ ์ดํ•ดํ•œ ๋ฐ”๋ก , ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

 

  1. Next ์„œ๋ฒ„์—์„œ Get ์š”์ฒญ์„ ๋ฐ›์œผ๋ฉด, ์š”์ฒญ์— ๋งž๋Š” Page๋ฅผ ์ฐพ๋Š”๋‹ค.
  2. _app.js์˜ getInitialProps๊ฐ€ ์žˆ๋‹ค๋ฉด ์‹คํ–‰ํ•œ๋‹ค.
  3. ํ•ด๋‹น Page์˜ getServerSideProps๊ฐ€ ์žˆ๋‹ค๋ฉด ์‹คํ–‰ํ•œ๋‹ค.
  4. pageProps๋“ค์„ ๋ฐ›์•„์™€์„œ ์ •์ ์ธ ํŽ˜์ด์ง€๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

 

React๋Š” ํด๋ผ์ด์–ธํŠธ์—์„œ HTML, CSS, ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๋ชจ๋‘ render() ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•ด ์ƒ์„ฑํ•œ๋‹ค. ๋ฐ˜๋ฉด, Next.js๋Š” ์„œ๋ฒ„์—์„œ HTML์„ ๊ฐ€์ ธ์˜จ ํ›„์—, ํด๋ผ์ด์–ธํŠธ์—์„œ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ฝ”๋“œ์™€ hydrate ํ•˜์—ฌ ๋ธŒ๋ผ์šฐ์ €์— ๋ Œ๋”๋งํ•œ๋‹ค. hydrate() ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•ด ์„œ๋ฒ„์—์„œ ๋ฐ›์•„์˜จ HTML์— ๋ฒˆ๋“ค๋ง๋œ React ์ฝ”๋“œ๋“ค์ด ํ•œ๋ฒˆ ๋” ๋ Œ๋”๋ง ๋˜๋Š” ๊ฒƒ.

 

 

2. ์„œ๋ฒ„ ์‚ฌ์ด๋“œ์—์„œ ์ฟ ํ‚ค ์‚ฌ์šฉํ•˜๊ธฐ

๋‘๋‘ฅ์—์„  ์นด์นด์˜ค ์†Œ์…œ ๋กœ๊ทธ์ธ์„ ์ด์šฉํ•˜๊ณ  ์žˆ๋‹ค. ์›ƒ๊ธด๊ฑด ์ผ๋…„๋„ ๋” ์ „์— ์ผ๋˜ ์นด์นด์˜ค ๋กœ๊ทธ์ธ (์‹คํŒจํ•œ) ๊ธฐ๋ก์ด ์€๊ทผ ์กฐํšŒ์ˆ˜๋ฅผ ๋‹ฌ๋‹ฌํ•˜๊ฒŒ ๋จน๊ณ  ์žˆ๋‹ค. ์ฐพ์•„์˜จ ์‚ฌ๋žŒ๋“ค์—๊ฒŒ ๋ช…์พŒํ•œ ๋‹ต์„ ์ฃผ์ง€ ๋ชปํ•ด์„œ ์€๊ทผ ๋ฏธ์•ˆํ•จ. ๊ฐ„๋‹จํ•˜๊ฒŒ ํ๋ฆ„๋งŒ ์ •๋ฆฌํ•˜๋ฉด ์ด๋ ‡๋‹ค.

 

  1. client๊ฐ€ kakao server์—๊ฒŒ ์ธ๊ฐ€ ์ฝ”๋“œ๋ฅผ ์š”์ฒญํ•œ๋‹ค.
  2. ์ด๋•Œ ์ธ๊ฐ€ ์ฝ”๋“œ๋Š” ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ url์— ์ฟผ๋ฆฌ๋กœ ์ „๋‹ฌ๋œ๋‹ค.
    ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ํŽ˜์ด์ง€๋ฅผ ํ•˜๋‚˜ ๋งŒ๋“ค์–ด๋†“๊ณ  ๊ฑฐ๊ธฐ์„œ ์ฝ”๋“œ๋ฅผ ๋ฐ›์•„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
  3. ์ธ๊ฐ€์ฝ”๋“œ๋ฅผ ๋‹ด์•„ server๋กœ ๋ณด๋‚ธ๋‹ค.
    server๋Š” ๊ทธ ์ฝ”๋“œ๋ฅผ kakao server๋กœ ๋ณด๋‚ด ์นด์นด์˜ค์˜ ํ† ํฐ์„ ๋ฐœ๊ธ‰๋ฐ›๋Š”๋‹ค.
  4. ์„œ๋ฒ„์—์„œ ๋”ฐ๋กœ ํ† ํฐ์„ ๋ฐœ๊ธ‰ํ•ด client๋กœ ์‘๋‹ต์„ ์ค€๋‹ค. ์ž์ฒด์ ์œผ๋กœ ์œ ์ € ์ •๋ณด๋ฅผ ๊ฐ–๊ณ  ์žˆ๊ธฐ ์œ„ํ•จ.

 

ํ˜„์žฌ ๋ชจ๋…ธ๋ ˆํฌ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋‹ค. ์–ด๋“œ๋ฏผ ๋ ˆํฌ์™€ ํ‹ฐ์ผ“ ๋ ˆํฌ ๋‘˜๋กœ ๋‚˜๋‰˜์–ด์ ธ ์žˆ์ง€๋งŒ ๋‘ ์„œ๋น„์Šค๊ฐ„ ์ด๋™์ด ์žˆ์–ด๋„ ๋กœ๊ทธ์ธ์€ ๊ณ„์† ์œ ์ง€๋˜์–ด์•ผ ํ–ˆ๋‹ค. accessToken์€ ํ•ญ์ƒ recoil์—์„œ atom์œผ๋กœ ๊ด€๋ฆฌํ•˜๊ณ , refreshToken๋งŒ cookie์— ์ €์žฅํ•˜๋„๋ก ํ–ˆ๋‹ค. ์ƒˆ๋กœ๊ณ ์นจ์„ ํ•˜๊ฑฐ๋‚˜ ์ฒ˜์Œ ํŽ˜์ด์ง€์— ๋“ค์–ด๊ฐˆ๋•Œ๋งˆ๋‹ค ๋ฆฌํ”„๋ ˆ์‰ฌ ๋กœ์ง์ด ๋Œ์•„ ๋งค๋ฒˆ ์ƒˆ๋กœ์šด accessToken์„ ๋ฐ›์•„์˜จ๋‹ค. ํ•˜์ง€๋งŒ ํด๋ผ์ด์–ธํŠธ์—์„œ ๋ Œ๋”๋ง๋ ๋•Œ ๋ฆฌํ”„๋ ˆ์‰ฌ๋ฅผ ํ•ด์„œ ๋ณด์—ฌ์ค€๋‹ค๋ฉด ์•ฝ๊ฐ„์˜ ๋”œ๋ ˆ์ด๋™์•ˆ ๋กœ๊ทธ์ธ์ด ํ’€๋ฆฐ ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ผ ์ˆ˜ ์žˆ๋‹ค.

 

์ด๋ ‡๊ฒŒ ์„œ๋ฒ„์—์„œ ๋ Œ๋”๋ง๋œ ํŽ˜์ด์ง€์—์„œ๋ถ€ํ„ฐ ๋กœ๊ทธ์ธ ์ •๋ณด๊ฐ€ ๋ณด์—ฌ์ง€๋„๋ก ํ•˜๊ณ  ์‹ถ์—ˆ๋‹ค. ์–ด๋“œ๋ฏผ ์„œ๋น„์Šค๋กœ ์ด๋™ํ•˜๋”๋ผ๋„ ๋กœ๊ทธ์ธ์ด ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์œ ์ง€๋˜๋Š” ๊ฒƒ ์ฒ˜๋Ÿผ ๋ณด์ผ ๊ฒƒ ๊ฐ™์•˜๋‹ค. getInitialProps๋ฅผ ํ†ตํ•ด ์„œ๋ฒ„์‚ฌ์ด๋“œ์—์„œ ์ „์—ญ์œผ๋กœ ๋ฐ์ดํ„ฐ ํŒจ์นญ์„ ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

pages/_app.tsx

MyApp.getInitialProps = async (context: AppContext) => {
  const { ctx, Component } = context;
  const refreshToken = cookies(ctx).refreshToken;
  let pageProps = {};
  let loginData: OauthLoginResponse | null;
  try {
    const response = await AuthApi.REFRESH(refreshToken!);
    loginData = response;
    ctx.res?.setHeader(
      'set-cookie',
      `refreshToken=${response.refreshToken}; path=/; max-age=${response.refreshTokenAge}`,
    );
  } catch (err: any) {
    loginData = null;
  }

  if (Component.getInitialProps) {
    // Component์˜ context๋กœ ctx๋ฅผ ๋„ฃ์–ด์ฃผ์ž
    pageProps = await Component.getInitialProps(ctx);
  }
  // returnํ•œ ๊ฐ’์€ ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ์˜ props๋กœ ๋“ค์–ด๊ฐ€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
  return { pageProps, loginData };
};

export default MyApp;

refresh ์š”์ฒญ์—๋Š” refreshToken์„ ๋‹ด์•„์„œ ๋ณด๋‚ธ๋‹ค. ์„œ๋ฒ„์—์„œ๋Š” window์™€ document ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์— ๋กœ์ปฌ์Šคํ† ๋ฆฌ์ง€์™€ document.cookie์—์„œ ํ† ํฐ์„ ๊ฐ€์ ธ์˜ค๋Š” ๋ฐฉ์‹์€ ์“ธ ์ˆ˜ ์—†๋‹ค. ๋Œ€์‹  ์š”์ฒญ์˜ ์ฟ ํ‚ค์— ๋“ค์–ด์žˆ๋Š” ํ† ํฐ์„ ๋นผ์˜ค๋ฉด ์„œ๋ฒ„ ์‚ฌ์ด๋“œ์—์„œ๋„ ์ฟ ํ‚ค๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

 

getInitialProps๋Š” context๋ผ๋Š” ์ธ์ž๋ฅผ ๋ฐ›๋Š”๋‹ค. context ์•ˆ์—๋Š” req (HTTP ์š”์ฒญ ๊ฐ์ฒด)์™€ res (์‘๋‹ต ๊ฐ์ฒด)๊ฐ€ ์žˆ๋‹ค. ์ด๋ฅผ ์ด์šฉํ•ด ์„œ๋ฒ„ ์‚ฌ์ด๋“œ์—์„œ cookie๋ฅผ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ๋‹ค. next-cookies ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•ด ctx.req์— ๋‹ด๊ธด ์ฟ ํ‚ค๋ฅผ ์‰ฝ๊ฒŒ ๋ฌธ์ž์—ด๋กœ ํŒŒ์‹ฑํ•ด ๊ฐ€์ ธ์™”๋‹ค. refresh๋กœ ๋ฐ›์•„์˜จ ๋ฐ์ดํ„ฐ๋“ค์€ ์ปดํฌ๋„ŒํŠธ์˜ prop์œผ๋กœ ๊ฐ™์ด ๋„˜๊ฒจ์ค„ ์ˆ˜ ์žˆ๋‹ค.

 

refresh ํ›„์— ์ƒˆ๋กœ ๋ฐœ๊ธ‰๋ฐ›์€ ํ† ํฐ๋“ค๋„ ๋‹ค์‹œ ํด๋ผ์ด์–ธํŠธ์˜ ์ฟ ํ‚ค๋กœ ๊ปด์ฃผ๋Š” ์ž‘์—…๋„ ํ•„์š”ํ•˜๋‹ค. ctx.res์˜ ํ—ค๋”์— ์ฟ ํ‚ค๋ฅผ ์„ธํŒ…ํ•ด์ค€๋‹ค. ์ด ๋ถ€๋ถ„์ด ์—†๋‹ค๋ฉด ์ฟ ํ‚ค๊ฐ€ ์ƒˆ ํ† ํฐ์œผ๋กœ ์—…๋ฐ์ดํŠธ๋˜์ง€ ์•Š๊ณ  ์˜›๋‚ ๊ฑฐ๊ฐ€ ๊ทธ๋Œ€๋กœ ๋‚จ์•„์žˆ๊ฒŒ ๋ ๊ฒƒ์ด๋‹ค. refresh ์š”์ฒญ์ด ํด๋ผ์ด์–ธํŠธ์—์„œ ๋ณด๋‚ธ๊ฒŒ ์•„๋‹ˆ๋ผ ์„œ๋ฒ„์—์„œ ๋ณด๋‚ด์„œ ์„œ๋ฒ„๋กœ ๋ฐ›์€ ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ.

 

ํ๋ฆ„์„ ์‰ฝ๊ฒŒ ์ •๋ฆฌํ•˜์ž๋ฉด, ํŽ˜์ด์ง€ ์š”์ฒญ์— ๋‹ด๊ธด ์ฟ ํ‚ค๋ฅผ ๋นผ์™€์„œ refresh api๋ฅผ ํ˜ธ์ถœํ•˜๋Š”๋ฐ ์‚ฌ์šฉํ•˜๊ณ , ๊ทธ ์ƒˆ๋กœ ๋ฐ›์•„์˜จ ํ† ํฐ๋“ค์„ ํŽ˜์ด์ง€ ์‘๋‹ต์˜ ํ—ค๋”์— ๋‹ค์‹œ ๊ปด์„œ ๋ธŒ๋ผ์šฐ์ €๋กœ ๋ณด๋‚ด์ฃผ๋Š” ๊ฒƒ์ด๋‹ค.

 

interface MyAppProps extends AppProps {
  loginData: OauthLoginResponse | null;
}

function MyApp({ Component, pageProps, loginData }: MyAppProps) {
  const initializer = useMemo(
    () =>
      ({ set }: MutableSnapshot) => {
        if (loginData) {
          const auth = {
            userProfile: loginData.userProfile,
            accessToken: loginData.accessToken,
            isAuthenticated: true,
            callbackUrl: (getCookie('redirectUrl') as string) || '/',
          };
          set(authState, auth);
        }
      },
    [loginData],
  );


  return (
  <...์ƒ๋žต>
      <RecoilRoot initializeState={initializer}>
        // ...์ƒ๋žต
      </RecoilRoot>
    </...์ƒ๋žต>
  );
}

Page๋กœ ๋„˜๊ฒจ์ค€ loginData๋ฅผ ์ปดํฌ๋„ŒํŠธ์—์„œ ์œ„ ์ฝ”๋“œ์™€ ๊ฐ™์ด ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค. ๋ฐ›์•„์˜จ ์ •๋ณด๋Š” ๋ฆฌ์ฝ”์ผ์˜ initializeState๋ฅผ ์ด์šฉํ•ด ๋ฐ”๋กœ ์„ธํŒ…ํ–ˆ๋‹ค.

 

๊ฐœ๋ฐœํ™˜๊ฒฝ์ผ๋•Œ ์ฟ ํ‚ค ๋“ฑ๋ก

staging ์„œ๋ธŒ๋„๋ฉ”์ธ์— ๋„์›Œ์ ธ ์žˆ๋Š” ์„œ๋ฒ„๋ฅผ ๊ฐ€์ง€๊ณ  ๋กœ์ปฌ์—์„œ ํด๋ผ์ด์–ธํŠธ ๊ฐœ๋ฐœ์„ ์ง„ํ–‰ํ•˜๊ณ  ์žˆ๋‹ค. ์„œ๋ฒ„(staging.dudoong.com)์™€ ๋กœ์ปฌ ๊ฐœ๋ฐœํ™˜๊ฒฝ(localhost)์ด ๋„๋ฉ”์ธ์ด ๋‹ค๋ฅด๊ธฐ ๋•Œ๋ฌธ์— ์ฟ ํ‚ค๊ฐ€ ์ œ๋Œ€๋กœ ์„ธํŒ…์ด ๋˜์ง€ ์•Š์€ ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์˜€๋‹ค. ๊ทธ๋ƒฅ ํ‰์†Œ์ฒ˜๋Ÿผ ํด๋ผ์ด์–ธํŠธ์—์„œ api ์š”์ฒญ์„ ๋ณด๋‚ผ ๋•Œ๋Š” ํฌ๊ฒŒ ๋ฌธ์ œ๊ฐ€ ๋˜์ง€ ์•Š์•˜๋‹ค. ๊ฐœ๋ฐœ์ž๋„๊ตฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ํ™•์ธ์ด ๋˜์ง€ ์•Š๋”๋ผ๋„ ์‹ค์ œ ์š”์ฒญ์˜ ํ—ค๋”๋ฅผ ์‚ดํŽด๋ณด๋ฉด ์ฟ ํ‚ค๊ฐ€ ์ œ๋Œ€๋กœ ๊ปด์ ธ์žˆ๋‹ค.

 

ํ•˜์ง€๋งŒ ์„œ๋ฒ„ ์‚ฌ์ด๋“œ์—์„œ refresh api๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ ๋ฌธ์ œ๊ฐ€ ์ƒ๊ฒผ๋‹ค. ์ง์ ‘ ctx.req์—์„œ ์ฟ ํ‚ค๋ฅผ ๊ฐ€์ ธ์™€ ์„ธํŒ…ํ•ด์ฃผ์—ˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์ด ๋•Œ๋Š” ๊ฐ™์€ ๋„๋ฉ”์ธ์ด ์•„๋‹Œ ์ฟ ํ‚ค๋Š” ๋ณด์—ฌ์ง€์ง€ ์•Š๋Š”๋‹ค. ๋”ฐ๋ผ์„œ ๊ฐœ๋ฐœํ™˜๊ฒฝ์—์„œ ๋กœ๊ทธ์ธ์„ ํ–ˆ์„ ๋•Œ ์ˆ˜ํ–‰ํ•  ์ž‘์—…์„ ๋”ฐ๋กœ ๋„ฃ์–ด์ฃผ์–ด์•ผ ํ•œ๋‹ค.

 

export const setCredentials = (refresh: OauthLoginResponse) => {
  axiosPrivate.defaults.headers.common[
    'Authorization'
  ] = `Bearer ${refresh.accessToken}`;

  // ๊ฐœ๋ฐœํ™˜๊ฒฝ์—์„œ ๋กœ์ปฌํ˜ธ์ŠคํŠธ๋กœ ์ฟ ํ‚ค ์ง์ ‘ ๋„ฃ์–ด์ฃผ๊ธฐ ์œ„ํ•จ
  if (process.env.NODE_ENV === 'development') {
    setCookie('accessToken', refresh.accessToken, {
      maxAge: refresh.accessTokenAge,
    });
    setCookie('refreshToken', refresh.refreshToken, {
      maxAge: refresh.refreshTokenAge,
    });
  }
};

 

 

3. getInitialProps๋ฅผ ์‚ฌ์šฉํ•ด๋„ ๋˜๋Š”๊ฐ€?

๊ณต์‹๋ฌธ์„œ๋ฅผ ํฌํ•จํ•ด ๋งŽ์€ ๊ธ€์—์„œ getInitialProps ์‚ฌ์šฉ์„ ๊ถŒํ•˜์ง€ ์•Š๊ณ  ์žˆ๋‹ค. ๋‘๋‘ฅ์—์„œ ์ฒ˜์Œ Next๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด์„œ ๊ณต๋ถ€ํ• ๋•Œ ๋ฌผ๋ก  ๋ฌธ์„œ์—์„œ ๋ณด๊ณ  ์ง€๋‚˜๊ฐ”๋˜ ๊ธ€์ด์—ˆ๋‹ค. ๊ทธ๋Ÿผ์—๋„ getInitialProps๋ฅผ ์‚ฌ์šฉํ•œ๊ฑด ์ฒ˜์Œ ์„œ๋น„์Šค์— ์ง„์ž…ํ• ๋•Œ ๋ฌด์กฐ๊ฑด ๋ฆฌํ”„๋ ˆ์‰ฌ ๋กœ์ง์„ ๋Œ์•„ ๋กœ๊ทธ์ธ ์ •๋ณด๋ฅผ ๋ฐ›์•„์™€์•ผ ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์„œ๋น„์Šค์˜ ์–ด๋–ค ํŽ˜์ด์ง€์—์„œ ์ƒˆ๋กœ๊ณ ์นจํ–ˆ์„๋•Œ๋„ ํ•ญ์ƒ ๋กœ๊ทธ์ธ์ด ์œ ์ง€๋˜์–ด์•ผ ํ–ˆ๋‹ค.

 

ํ•˜์ง€๋งŒ ์•ฝ๊ฐ„์˜ ๊ณต๋ถ€๋ฅผ ํ•œ ๋’ค์— ์•Œ๊ฒŒ๋œ ๋ฌธ์ œ์ ๋“ค์ด ์žˆ์—ˆ๋‹ค.

 

a. ์ž๋™ ์ •์  ์ตœ์ ํ™”(Automatic Static Optimization) ๋ถˆ๊ฐ€

Next๋Š” ๋นŒ๋“œํƒ€์ž„์— getInitialProps๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ํŽ˜์ด์ง€๋ฅผ ์ž๋™์œผ๋กœ pre-render ํ•œ๋‹ค. ๋‹ค๋ฅธ๋ง๋กœ ํ•˜๋ฉด, _app.tsx์—์„œ ์ „์—ญ์œผ๋กœ getInitialProps๋ฅผ ์ ์šฉํ•˜๊ฒŒ ๋˜๋ฉด, ๋ชจ๋“  ํŽ˜์ด์ง€๊ฐ€ ๋ฌด์กฐ๊ฑด ์„œ๋ฒ„ ์‚ฌ์ด๋“œ ๋ Œ๋”๋ง์ด ๋˜๋Š”๊ฒƒ์ด๋‹ค.

 

λ  (Server)  server-side renders at runtime (uses getInitialProps or getServerSideProps)
โ—‹  (Static)  automatically rendered as static HTML (uses no initial props)

๋นŒ๋“œ ์‹œ์— ํ„ฐ๋ฏธ๋„์— ๋‚˜์˜ค๋Š” ๋ง์ด๋‹ค. pre-render ํ•œ๋‹ค๋Š” ๊ฒƒ์€ ์ •์  HTMLํŒŒ์ผ๋กœ ์ƒ์„ฑ๋˜์—ˆ๋‹ค๋Š” ๋œป์ด๋‹ค. ์‹ค์ œ๋กœ .next/server/static ํด๋”์— ์ƒ์„ฑ๋œ ํŒŒ์ผ๋“ค์„ ํ™•์ธํ•ด๋ณด๋ฉด ์˜ค๋ฅธ์ชฝ์˜ ๊ฒฝ์šฐ์— ์ •์  HTMLํŒŒ์ผ์ด ์ƒ์„ฑ๋œ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค (ํ•ด๋‹น ๊ฒฝ๋กœ์—๋Š” ์„œ๋ฒ„์—์„œ ์‚ฌ์šฉ๋˜๋Š” ํŒŒ์ผ๋“ค์ด ์ƒ์„ฑ๋œ๋‹ค). getInitialProps๊ฐ€ ์žˆ๋Š” ํŽ˜์ด์ง€๋Š” JSํŒŒ์ผ๋กœ ๋ฒˆ๋“ค๋ง๋˜์–ด ์˜ฌ๋ผ๊ฐ„๋‹ค.

 

(์™ผ) _app.tsx์—์„œ getInitialProps ์‚ฌ์šฉ / (์˜ค) ์‚ฌ์šฉํ•˜์ง€ ์•Š์Œ

ํ•˜์ง€๋งŒ ๋žœ๋”ฉํŽ˜์ด์ง€์™€ ๊ณต์—ฐ ์ƒ์„ธํŽ˜์ด์ง€์™€ ๊ฐ™์ด ์ •์ ์œผ๋กœ ๋ณด์—ฌ์ค„ ์ˆ˜ ์žˆ๋Š” ํŽ˜์ด์ง€๊นŒ์ง€๋„ ํ•ญ์ƒ SSR์„ ๊ฑฐ์ณ์•ผ ํ•œ๋‹ค.

 

b. ํŽ˜์ด์ง€ ์ด๋™๋งˆ๋‹ค getInitialProps ์‹คํ–‰

getInitialProps๋Š” ํด๋ผ์ด์–ธํŠธ ์‚ฌ์ด๋“œ์™€ ์„œ๋ฒ„ ์‚ฌ์ด๋“œ ๋ชจ๋‘์—์„œ ๋™์ž‘ํ•œ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ ์„œ๋ฒ„ ์‚ฌ์ด๋“œ์—์„œ ํŒจ์นญํ•œ ๋ฐ์ดํ„ฐ๋ฅผ pageProps๋กœ ๋„˜๊ฒจ ๋ Œ๋”๋งํ•˜๋Š” ์—ญํ• ์„ ํ•œ๋‹ค. ํ•˜์ง€๋งŒ next/link๋‚˜ next/router๋ฅผ ์ด์šฉํ•ด ํŽ˜์ด์ง€๋ฅผ ์ด๋™ํ•  ๋•Œ, ํด๋ผ์ด์–ธํŠธ ์‚ฌ์ด๋“œ๋กœ getInitialProps๊ฐ€ ๋‚ด๋ ค์™€ ์ž‘๋™ํ•˜๊ฒŒ ๋œ๋‹ค. ๊ทธ๋ ‡๊ฒŒ ๋˜๋ฉด ํ•ด๋‹น ๋กœ์ง์ด ๋ถˆํ•„์š”ํ•˜๊ฒŒ ์•ฑ์˜ ๋ฒˆ๋“ค์— ํฌํ•จ๋˜์–ด ์„ฑ๋Šฅ์— ์ €ํ•˜๊ฐ€ ์ƒ๊ธธ ์ˆ˜ ์žˆ๋‹ค. Next 9๋ฒ„์ „ ์ดํ›„ getServerSideProps์™€ getStaticProps๋กœ ๋ถ„๋ฆฌ๋œ ์ดํ›„๋กœ๋Š” ์ตœ๋Œ€ํ•œ ์‚ฌ์šฉ์„ ์ง€์–‘ํ•˜๋ผ๋Š” ์ด์œ ์ด๊ธฐ๋„ ํ•˜๋‹ค.

 

์ฝ˜์†”์„ ์ฐ๊ณ  ํŽ˜์ด์ง€๋ฅผ ์ด๋ฆฌ์ €๋ฆฌ ์ด๋™ํ•ด๋ณด์•˜๋‹ค. ์ฒซ ๋ Œ๋”๋ง์‹œ์—๋Š” ์„œ๋ฒ„์—์„œ ์‹คํ–‰๋˜๊ณ , ๊ทธ ์ดํ›„๋ก  ๋ธŒ๋ผ์šฐ์ €์˜ ์ฝ˜์†”์—์„œ ๋ณด์—ฌ์ง„๋‹ค. ๋งค ํŽ˜์ด์ง€ ์ด๋™๋งˆ๋‹ค getInitialProps๊ฐ€ ํด๋ผ์ด์–ธํŠธ ์‚ฌ์ด๋“œ์—์„œ ์ˆ˜ํ–‰๋œ๋‹ค๋Š” ๋œป. getServerSideProps๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ํŽ˜์ด์ง€(์ฃผ๋ฌธ,๊ฒฐ์ œ)๊ฐ€ ๋ Œ๋”๋ง๋ ๋•Œ๋Š” getInitialProps๊ฐ€ ๋จผ์ € ์‹คํ–‰๋œ ํ›„์— getServerSideProps๊ฐ€ ์‹คํ–‰๋œ๋‹ค.

 

๋‹คํ–‰ํžˆ recoil์— initializeState๋ฅผ ๋„ฃ๋Š” ๋กœ์ง์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋‚ด์—์„œ ์ตœ์ ํ™”๊ฐ€ ๋˜์–ด์žˆ๋Š”์ง€ ์ตœ์ดˆ 1ํšŒ ์™ธ์—๋Š” ์‹คํ–‰๋˜์ง€ ์•Š๋Š”๋‹ค.

์„ฑ๋Šฅ์€ ๊ทธ๋ ‡๋‹ค์น˜๊ณ , ๋งค ํŽ˜์ด์ง€ ์ด๋™๋งˆ๋‹ค getInitialProps๊ฐ€ ์‹คํ–‰๋˜์–ด ๋ฆฌํ”„๋ ˆ์‰ฌ ์š”์ฒญ์„ ๋ณด๋‚ด๊ฒŒ ๋˜๋Š”๊ฑด ๋‚ด ์ฒ˜์Œ ์˜๋„์™€ ์ „ํ˜€ ๋งž์ง€ ์•Š์•˜๋‹ค. ๋”ฑ ์ฒ˜์Œ ํ•œ๋ฒˆ๋งŒ ๋˜์–ด์•ผํ•˜๋Š”๋ฐ... ์ž๊พธ ํ„ฐ๋ฏธ๋„ ์ฝ˜์†”์— ์ด์ƒํ•œ๊ฒŒ ์ฐํžˆ๊ธธ๋ž˜ ์ด๊ฒŒ ๋ญ์ง€ ํ–ˆ์—ˆ๋Š”๋ฐ ์ด๊ฒŒ ๊ทธ ์ด์œ ์˜€๋‹ค.

 

  try {
    if (ctx.req) {
      const response = await AuthApi.REFRESH(refreshToken!);
      console.log(response.refreshToken);
      loginData = response;
      ctx.res?.setHeader(
        'set-cookie',
        `refreshToken=${response.refreshToken}; path=/; max-age=${response.refreshTokenAge}`,
      );
    } else throw new Error('isClient');
  } catch (err: any) {
    loginData = null;
  }

์šฐ์„  ๊ฐ„๋‹จํ•˜๊ฒŒ ์ˆ˜์ •ํ•ด๋ณด์•˜๋‹ค.

ctx.req๋Š” ์„œ๋ฒ„ ์‚ฌ์ด๋“œ์— ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, ์ตœ์ดˆ 1ํšŒ๋งŒ ์‹คํ–‰๋  ์ˆ˜ ์žˆ๋‹ค.

 

 

 


 

 

 

https://im-designloper.tistory.com/111

https://www.howdy-mj.me/next/hydrate

https://yeoulcoding.me/266