Next.js

Next.js๋Š” ๋ฆฌ์•กํŠธ ๊ธฐ๋ฐ˜์˜ ์›น ํ”„๋ ˆ์ž„์›Œํฌ๋กœ, ์„œ๋ฒ„ ์‚ฌ์ด๋“œ ๋ Œ๋”๋ง(SSR)์„ ์ง€์›ํ•œ๋‹ค.

๋ผ์šฐํŒ… ๋ฐฉ์‹

  • ํŽ˜์ด์ง€ ๋ผ์šฐํŒ…(Page routing): pages ๋””๋ ‰ํ† ๋ฆฌ ํ•˜์œ„์— ์žˆ๋Š” ํŒŒ์ผ๊ณผ ๋””๋ ‰ํ† ๋ฆฌ ๊ตฌ์กฐ๋ฅผ URL ๊ฒฝ๋กœ๋กœ ๋งคํ•‘ํ•˜๋Š” ๋ผ์šฐํŒ… ๋ฐฉ์‹์ด๋‹ค.
  • ์•ฑ ๋ผ์šฐํŒ…(App routing): ๋ฆฌ์•กํŠธ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ, ์ŠคํŠธ๋ฆฌ๋ฐ ๋“ฑ ์ถ”๊ฐ€ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” ์ƒˆ๋กœ์šด ๋ผ์šฐํŒ… ๋ฐฉ์‹์ด๋‹ค.

๋ Œ๋”๋ง ๋ฐฉ์‹

SSR (Server-Side Rendering)

์„œ๋ฒ„ ์‚ฌ์ด๋“œ ๋ Œ๋”๋ง์€ ์„œ๋ฒ„๊ฐ€ ํด๋ผ์ด์–ธํŠธ์˜ ์ ‘์† ์š”์ฒญ์„ ๋ฐ›์„ ๋•Œ๋งˆ๋‹ค ํŽ˜์ด์ง€๋ฅผ ๋งค๋ฒˆ ์ƒˆ๋กญ๊ฒŒ ์ƒ์„ฑํ•ด ์™„์„ฑ๋œ HTML์„ ์‘๋‹ตํ•˜๋Š” ์‚ฌ์ „ ๋ Œ๋”๋ง ๋ฐฉ์‹์ด๋‹ค. ์ตœ์‹  ๋ฐ์ดํ„ฐ์˜ ๋ฐ˜์˜์ด ์ค‘์š”ํ•œ ํŽ˜์ด์ง€์— ์ ํ•ฉํ•˜์ง€๋งŒ, ๋งค ์š”์ฒญ๋งˆ๋‹ค ํŽ˜์ด์ง€๋ฅผ ์ƒˆ๋กญ๊ฒŒ ์ƒ์„ฑํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์„œ๋ฒ„์—์„œ ๋ถ€ํ•˜๋ฅผ ์œ ๋ฐœํ•˜๊ฑฐ๋‚˜ ์‘๋‹ต ์†๋„๊ฐ€ ๋Šฆ์–ด์งˆ ์ˆ˜ ์žˆ๋‹ค.

sequenceDiagram autonumber participant User as ์‚ฌ์šฉ์ž participant Browser as ๋ธŒ๋ผ์šฐ์ € participant Server as ์„œ๋ฒ„ User->>Server: ์ดˆ๊ธฐ ์š”์ฒญ Server->>Server: ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰ Server-->>Browser: ์™„์„ฑ๋œ HTML Browser-->>User: ํ™”๋ฉด ๋ Œ๋”๋ง Note over User: FCP Server -->> Browser: ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๋ฒˆ๋“ค Browser->>Browser: ํ•˜์ด๋“œ๋ ˆ์ด์…˜ Browser-->>User: ์ƒํ˜ธ์ž‘์šฉ ์ œ๊ณต Note over User: TTI

SSR์„ ์ ์šฉํ•˜๋ ค๋ฉด ํŽ˜์ด์ง€ ์ปดํฌ๋„ŒํŠธ์—์„œ getServerSideProps ํ•จ์ˆ˜๋ฅผ ์„ ์–ธํ•˜๊ณ  ๋‚ด๋ณด๋‚ด๋ฉด ๋œ๋‹ค. getServerSideProps ํ•จ์ˆ˜๋Š” ์‚ฌ์ „ ๋ Œ๋”๋ง์—์„œ ํŽ˜์ด์ง€ ์ปดํฌ๋„ŒํŠธ๋ณด๋‹ค ๋จผ์ € ์‹คํ–‰๋˜์–ด ์„œ๋ฒ„์—์„œ ํŽ˜์ด์ง€ ์ปดํฌ๋„ŒํŠธ์— ํ•„์š”ํ•œ ๋กœ์ง์„ ์‹คํ–‰ํ•˜๊ณ , ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ํŽ˜์ด์ง€ ์ปดํฌ๋„ŒํŠธ์— ํ”„๋กœํผํ‹ฐ๋กœ ์ „๋‹ฌํ•œ๋‹ค.

export function getServerSideProps() {
    const data = "Hello, world!";
    return { props: { data } };
}

export default function Home({ data }) {
    console.log(data); // "Hello, world!"
    return (...);
}

SSG (Static Site Generation)

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

sequenceDiagram autonumber participant User as ์‚ฌ์šฉ์ž participant Browser as ๋ธŒ๋ผ์šฐ์ € participant Server as ์„œ๋ฒ„ rect rgb(245, 245, 255) Note over Server: ๋นŒ๋“œ ํƒ€์ž„ Server->>Server: ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰ end rect rgb(245, 245, 255) Note over User, Server: ๋Ÿฐํƒ€์ž„ User->>Server: ์ดˆ๊ธฐ ์š”์ฒญ Server-->>Browser: ์™„์„ฑ๋œ HTML Browser-->>User: ํ™”๋ฉด ๋ Œ๋”๋ง Note over User: FCP end

SSG๋ฅผ ์ ์šฉํ•˜๋ ค๋ฉด ํŽ˜์ด์ง€ ์ปดํฌ๋„ŒํŠธ์—์„œ getStaticProps ํ•จ์ˆ˜๋ฅผ ์„ ์–ธํ•˜๊ณ  ๋‚ด๋ณด๋‚ด๋ฉด ๋œ๋‹ค. getStaticProps ํ•จ์ˆ˜๋Š” ๋นŒ๋“œ ํƒ€์ž„์— ์‹คํ–‰๋˜์–ด ํŽ˜์ด์ง€ ์ปดํฌ๋„ŒํŠธ์— ํ•„์š”ํ•œ ๋กœ์ง์„ ์‹คํ–‰ํ•˜๊ณ , ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ํŽ˜์ด์ง€ ์ปดํฌ๋„ŒํŠธ์— ํ”„๋กœํผํ‹ฐ๋กœ ์ „๋‹ฌํ•œ๋‹ค.

export function getStaticProps() {
    const data = "Hello, world!";
    return { props: { data } };
}

export default function Home({ data }) {
    console.log(data); // "Hello, world!"
    return (...);
}

๊ฐœ๋ฐœ ๋ชจ๋“œ์—์„œ๋Š” ์ฝ”๋“œ ์ˆ˜์ • ์‚ฌํ•ญ์„ ๋น ๋ฅด๊ฒŒ ๋ฐ˜์˜ํ•˜๊ธฐ ์œ„ํ•ด SSG ๋ฐฉ์‹์ž„์—๋„ ์ ‘์† ์š”์ฒญ์„ ๋ฐ›์„ ๋•Œ๋งˆ๋‹ค ํŽ˜์ด์ง€๋ฅผ ๋งค๋ฒˆ ์ƒˆ๋กญ๊ฒŒ ์ƒ์„ฑํ•œ๋‹ค.

ISR (Incremental Static Regeneration)

์ฆ๋ถ„ ์ •์  ์žฌ์ƒ์„ฑ์€ SSG ๋ฐฉ์‹์œผ๋กœ ๋นŒ๋“œ ํƒ€์ž„์— ์ƒ์„ฑํ•œ ์ •์  ํŽ˜์ด์ง€๋ฅผ ์ผ์ • ์ฃผ๊ธฐ๋งˆ๋‹ค ๋‹ค์‹œ ์ƒ์„ฑํ•ด ์ตœ์‹  ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ˜์˜ํ•˜๋Š” ๋ Œ๋”๋ง ๋ฐฉ์‹์ด๋‹ค. ์ผ์ • ์‹œ๊ฐ„์ด ์ง€๋‚˜๊ธฐ ์ „๊นŒ์ง€๋Š” ๋นŒ๋“œ ํƒ€์ž„์— ์ƒ์„ฑํ•ด ์บ์‹œํ•ด๋‘” HTML์„ ์‘๋‹ตํ•˜๊ณ , ์‹œ๊ฐ„์ด ์ง€๋‚˜ ์บ์‹œ๊ฐ€ ๋งŒ๋ฃŒ๋˜๋ฉด, ๋งŒ๋ฃŒ๋œ HTML์„ ์‘๋‹ตํ•˜๋Š” ๋™์‹œ์— ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ์ƒˆ HTML์„ ์ƒ์„ฑํ•ด ์บ์‹œํ•œ๋‹ค. ๋น ๋ฅธ ์‘๋‹ต๊ณผ ์ตœ์‹  ๋ฐ์ดํ„ฐ์˜ ๋ฐ˜์˜์„ ๋™์‹œ์— ๋‹ฌ์„ฑํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ์‹์ด๋‹ค.

sequenceDiagram autonumber participant User as ์‚ฌ์šฉ์ž participant Browser as ๋ธŒ๋ผ์šฐ์ € participant Server as ์„œ๋ฒ„ rect rgb(245, 245, 255) Note over Server: ๋นŒ๋“œ ํƒ€์ž„ Server->>Server: ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰ end rect rgb(245, 245, 255) Note over User, Server: ๋Ÿฐํƒ€์ž„ User->>Server: ์š”์ฒญ alt ์บ์‹œ ์œ ํšจ Server-->>Browser: ์บ์‹œ๋œ HTML else ์บ์‹œ ๋งŒ๋ฃŒ Server-->>Browser: ๊ธฐ์กด์— ์บ์‹œ๋œ HTML par ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์žฌ์ƒ์„ฑ Server->>Server: ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰ Server->>Server: ์ƒˆ HTML๋กœ ์บ์‹œ ๊ฐฑ์‹  end end Browser-->>User: ํ™”๋ฉด ๋ Œ๋”๋ง Note over User: FCP end

ISR์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ํŽ˜์ด์ง€ ์ปดํฌ๋„ŒํŠธ์—์„œ getStaticProps ํ•จ์ˆ˜์— revalidate ์˜ต์…˜์„ ์„ค์ •ํ•˜๋ฉด ๋œ๋‹ค. revalidate ์˜ต์…˜์€ ํŽ˜์ด์ง€๊ฐ€ ์บ์‹œ๋œ ํ›„ ๋‹ค์‹œ ์ƒ์„ฑ๋˜๊ธฐ๊นŒ์ง€์˜ ์‹œ๊ฐ„์„ ์ดˆ ๋‹จ์œ„๋กœ ์ง€์ •ํ•œ๋‹ค.

export function getStaticProps() {
    const data = "Hello, world!";
    return { props: { data }, revalidate: 10 }; // 10์ดˆ๋งˆ๋‹ค ์žฌ์ƒ์„ฑ
}

export default function Home({ data }) {
    console.log(data); // "Hello, world!"
    return (...);
}

ISR์€ ๋ฐ์ดํ„ฐ์˜ ๊ฐฑ์‹  ์‹œ์ ์„ ์˜ˆ์ธกํ•˜๊ธฐ ์–ด๋ ต๋‹ค. ๋งŒ์•ฝ revalidate ์ฃผ๊ธฐ๊ฐ€ 60์ดˆ๋ผ๋ฉด, ์‚ฌ์šฉ์ž๋Š” ์ตœ๋Œ€ 60์ดˆ ๋™์•ˆ์€ ๋งŒ๋ฃŒ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด๊ฒŒ ๋œ๋‹ค. ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•œ ๋ Œ๋”๋ง ๋ฐฉ์‹์ด ์ฃผ๋ฌธํ˜• ์žฌ์ƒ์„ฑ(On-demand ISR)์œผ๋กœ, ์ผ์ • ์ฃผ๊ธฐ์— ๋”ฐ๋ผ ํŽ˜์ด์ง€๋ฅผ ์žฌ์ƒ์„ฑํ•˜๋Š” ๋Œ€์‹  API๋ฅผ ํ†ตํ•ด ํŽ˜์ด์ง€๋ฅผ ์žฌ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

์ด ๋ฌธ์„œ๋ฅผ ์ธ์šฉํ•œ ๋ฌธ์„œ