Post

Nextjs-Optimizing(5) - Image caching

๐Ÿ“Œ ์‹œ์ž‘ํ•˜๋ฉฐ

์ด์ „ Nextjs-Optimizing(4) ํฌ์ŠคํŒ…์—์„œ static assets์— ๋Œ€ํ•ด ์‚ดํŽด๋ณด์•˜๋‹ค.
์‚ฌ์‹ค ๊ทธ๋•Œ๋Š” โ€œ๊ทธ๋ ‡๊ตฐ! ์ข‹์•„! ๐Ÿค—โ€ ํ•˜๊ณ  ๋„˜์–ด๊ฐ”๋Š”๋ฐ, ๋‚˜์ค‘์— ์ด๊ฑธ ์‹ค์ œ๋กœ ์‚ฌ์šฉํ•  ์ผ์ด ์ƒ๊ธฐ๋‹ˆ ์•ฝ๊ฐ„์˜ ์‹œํ–‰์ฐฉ์˜ค๋ฅผ ๊ฒช๊ฒŒ ๋˜์–ด, ์ด๋ฅผ ํ•ด๊ฒฐํ–ˆ๋˜ ๊ณผ์ •์„ ์ •๋ฆฌํ•ด๋ณด๊ณ ์ž ํ•œ๋‹ค.

๐Ÿ–ผ๏ธ Image Caching

๐Ÿ’ฅ ๋ฌธ์ œ ์ƒํ™ฉ

๋‚˜๋Š” public ํด๋” ๋‚ด์˜ staticํ•œ ์ด๋ฏธ์ง€๋ฅผ ์ž์ฃผ ์‚ฌ์šฉํ–ˆ๋Š”๋ฐ, ํ•ด๋‹น ์ด๋ฏธ์ง€๋Š” ๋ฐ”๋€” ํ™•๋ฅ ์ด ๊ฑฐ์˜ ์—†๋Š” ์ด๋ฏธ์ง€์˜€๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ์ด ์ด๋ฏธ์ง€๋ฅผ ๋ Œ๋”๋งํ•  ๋•Œ๋งˆ๋‹ค ๋„คํŠธ์›Œํฌ ์š”์ฒญ์„ ๋ณด๋‚ด๋Š” ๊ฒƒ์€ ๋„ˆ๋ฌด ๋น„ํšจ์œจ์ ์œผ๋กœ ๋А๊ปด์ ธ, โ€œ์ด๊ฑธ ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ•˜์ง€?โ€ ๊ณ ๋ฏผํ•˜๋‹ค๊ฐ€, ์ด์ „ ํฌ์ŠคํŒ…๊ณผ ํ•จ๊ป˜ Next.js ๊ณต์‹๋ฌธ์„œ๋ฅผ ๋‹ค์‹œ ํ›‘์–ด๋ณด๋ฉฐ ์ด๋ฏธ์ง€ ์บ์‹ฑ ์ ์šฉ์„ ์‹œ๋„ํ•˜๊ฒŒ ๋˜์—ˆ๋‹ค.

๐ŸŽ‡ ์บ์‹œ๋ž€?

๋จผ์ €, ์บ์‹œ(Cache) ๊ฐ€ ๋ฌด์—‡์ธ์ง€ ๊ฐ„๋‹จํžˆ ์งš๊ณ  ๋„˜์–ด๊ฐ€์ž.
์บ์‹œ๋Š” ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์›น์‚ฌ์ดํŠธ์˜ ์ด๋ฏธ์ง€, ๊ธ€๊ผด, HTML, CSS, JS ๋“ฑ์„ ์ž„์‹œ๋กœ ์ €์žฅํ•ด๋‘๋Š” ๊ณต๊ฐ„์ด๋‹ค.

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

์ฆ‰, ์บ์‹œ์— ํ•ด๋‹น ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ๋‹ค๋ฉด ๋ธŒ๋ผ์šฐ์ €๋Š” ๋‹ค์‹œ ๋‹ค์šด๋กœ๋“œํ•˜์ง€ ์•Š๊ณ  ๋ฐ”๋กœ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ ๋•Œ๋ฌธ์— ๋กœ๋”ฉ ์‹œ๊ฐ„์ด ๋‹จ์ถ•๋œ๋‹ค.

๐ŸŽ Next.js์—์„œ Image Cache ํ•˜๊ธฐ

Next.js ๊ณต์‹ ๋ฌธ์„œ๋ฅผ ๋ณด๋ฉด, Next.js๋Š” immutable assets์— ๋Œ€ํ•ด ์•„๋ž˜์™€ ๊ฐ™์€ Cache-Control ํ—ค๋”๋ฅผ ์ž๋™์œผ๋กœ ์„ค์ •ํ•œ๋‹ค๊ณ  ์„ค๋ช…ํ•œ๋‹ค.

์ด๊ฑธ ๋ณด๊ณ ๋Š” โ€œ์–ด? ๊ทธ๋Ÿฌ๋ฉด ๋”ฐ๋กœ ์„ค์ • ์•ˆ ํ•ด๋„ ๋˜๋Š” ๊ฑฐ ์•„๋ƒ?โ€ ๋ผ๊ณ  ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ์‚ฌ์‹ค์€ โ€œ์–ด๋–ป๊ฒŒ ์ด๋ฏธ์ง€๋ฅผ ๊ฐ€์ ธ์˜ค๋Š”๊ฐ€โ€์— ๋”ฐ๋ผ ์ ์šฉ ์—ฌ๋ถ€๊ฐ€ ๋‹ฌ๋ผ์ง„๋‹ค.

๐Ÿ’› ์ฐธ๊ณ  ์‚ฌํ•ญ

Next.js๋Š” next/image ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ด์šฉํ•ด static image๋ฅผ ๊ฐ€์ ธ์˜ฌ ๊ฒฝ์šฐ, Cache-Control: public, max-age=31536000, immutable์„ ์ž๋™ ์„ค์ •ํ•œ๋‹ค.

์ด๋Ÿฐ ๊ฒฝ์šฐ์—๋Š” ํŒŒ์ผ๋ช…์— SHA ํ•ด์‹œ๊ฐ€ ํฌํ•จ๋˜๊ธฐ ๋•Œ๋ฌธ์—, ํŒŒ์ผ ๋‚ด์šฉ์ด ๋ฐ”๋€Œ๋ฉด ์ด๋ฆ„๋„ ๋ฐ”๋€Œ๊ฒŒ ๋˜๊ณ , ์˜ค๋ž˜ ์บ์‹œํ•ด๋„ ๋ฌธ์ œ ์—†์ด ์•ˆ์ •์ ์œผ๋กœ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค.

ํ•˜์ง€๋งŒ /public/images/profile.svg ์™€ ๊ฐ™์€ ์ง์ ‘ ๊ฒฝ๋กœ ๊ธฐ๋ฐ˜์˜ ์ •์  ์ด๋ฏธ์ง€ ์ ‘๊ทผ (<img src="/images/xxx.svg" />)์—๋Š” ์ž๋™์œผ๋กœ Cache-Control์ด ์ ์šฉ๋˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์ด ๊ฒฝ์šฐ์—๋Š” ์ง์ ‘ ํ—ค๋”๋ฅผ ์„ค์ •ํ•ด์ค˜์•ผ ํ•œ๋‹ค.

๐Ÿฅ  ์˜ˆ์ œ

Next.js์—์„œ๋Š” next.config.js์—์„œ headers() ์„ค์ •์„ ํ†ตํ•ด ํŠน์ • ๊ฒฝ๋กœ์— ๋Œ€ํ•œ ์ปค์Šคํ…€ HTTP ํ—ค๋”๋ฅผ ์ง์ ‘ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
module.exports = {
  async headers() {
    return [
      {
        source: '/about',
        headers: [
          {
            key: 'x-custom-header',
            value: 'my custom header value',
          },
          {
            key: 'x-another-custom-header',
            value: 'my other custom header value',
          },
        ],
      },
    ];
  },
};

๐Ÿ˜ซ ๋ฌธ์ œ ์ƒํ™ฉ

๊ทธ๋Ÿฐ๋ฐ ์œ„์ฒ˜๋Ÿผ ์บ์‹œ๋ฅผ ์ ์šฉํ•˜๊ณ ์ž ํ–ˆ์„ ๋•Œ ์˜๋„์น˜ ์•Š์€ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค.

๋‚˜๋Š” /public ํด๋” ๋ฐ”๋กœ ์•„๋ž˜์— ๋ชจ๋“  static assets(image)์„ ๋ณด๊ด€ํ•˜๊ณ  ์žˆ์—ˆ๊ธฐ ๋•Œ๋ฌธ์—, public ํด๋” /์˜ ๋ชจ๋“  ๋‚ด์šฉ์— ์บ์‹œ ํ—ค๋”๋ฅผ ์ ์šฉํ•˜๋ ค๊ณ  ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์„ค์ •ํ–ˆ์—ˆ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
async headers() {
  return [
    {
      source: '/:path*',
      headers: [
        {
          key: 'Cache-Control',
          value: 'public, max-age=31536000, immutable',
        },
      ],
    },
  ];
}

๊ทธ๋Ÿฐ๋ฐ ์ด ์„ค์ •์„ ์ ์šฉํ•˜๋‹ˆโ€ฆ ์ด๋ฏธ์ง€๊ฐ€ ์•„์˜ˆ ๋กœ๋”ฉ๋˜์ง€ ์•Š๊ณ , ๋กœ๊ทธ์ธ๋„ ์ž‘๋™ํ•˜์ง€ ์•Š๋Š” ์‹ฌ๊ฐํ•œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค. ์•Œ๊ณ  ๋ณด๋‹ˆ ์ด ์„ค์ •์€ ๋ง ๊ทธ๋Œ€๋กœ ๋ชจ๋“  ๊ฒฝ๋กœ/์— ๋งค์นญ๋˜๊ธฐ ๋•Œ๋ฌธ์—, ์ •์  ํŒŒ์ผ๋ฟ ์•„๋‹ˆ๋ผ ํŽ˜์ด์ง€ ๋ผ์šฐํŠธ๋‚˜ API๊นŒ์ง€ ๋ชจ๋‘ ์บ์‹œ๋˜์–ด ์˜๋„์น˜ ์•Š์€ ์ƒํ™ฉ์ด ๋ฐœ์ƒํ•œ ๊ฒƒ์ด์—ˆ๋‹ค.

๐Ÿค— ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•

์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด /public/images ํด๋”๋ฅผ ๋”ฐ๋กœ ๋งŒ๋“ค์–ด ์ด๋ฏธ์ง€๋“ค์„ ์˜ฎ๊ธด ๋’ค source๋ฅผ ์•„๋ž˜์™€ ๊ฐ™์ด ์ž‘์„ฑํ•ด์ฃผ์—ˆ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
async headers() {
  return [
    {
      source: '/images/:path*',
      headers: [
        {
          key: 'Cache-Control',
          value: 'public, max-age=31536000, immutable',
        },
      ],
    },
  ];
}

โœ… ๊ฒฐ๊ณผ

์ด์ œ /public/images/profile.svg ํŒŒ์ผ์„ network ํƒญ์—์„œ ํ™•์ธํ–ˆ์„ ๋•Œ, ์ž‘์„ฑํ–ˆ๋˜ ๋‚ด์šฉ ๊ทธ๋Œ€๋กœ ํ‘œ์‹œ๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

์‘๋‹ต ์ฝ”๋“œ: 200 OK

ํ—ค๋”: Cache-Control: public, max-age=31536000, immutable

๐Ÿ—‚๏ธ์ฐธ๊ณ  ์‚ฌ์ดํŠธ

This post is licensed under CC BY 4.0 by the author.