๐Ÿš€ ์›น์‚ฌ์ดํŠธ ์†๋„๋ฅผ 2๋ฐฐ๋กœ! Next.js Image ์ปดํฌ๋„ŒํŠธ

ํ˜…ยท2024๋…„ 9์›” 6์ผ

Next.js๋ฅผ ์ฒ˜์Œ ์ ‘ํ–ˆ์„ ๋•Œ Routing์„ ์ œ์ผ ๋จผ์ € ๋ฐฐ์šฐ๊ณ  ๋‹ค์Œ์œผ๋กœ ๋ฐฐ์› ๋˜ ๊ฒŒ Image ์ปดํฌ๋„ŒํŠธ์˜€๋‹ค. Image ์ปดํฌ๋„ŒํŠธ๋Š” img ํƒœ๊ทธ๋ฅผ ์‚ฌ์šฉํ–ˆ์„ ๋•Œ๋ณด๋‹ค ์ด๋ฏธ์ง€ ์ตœ์ ํ™”๋ผ๋Š” ์žฅ์ ์ด ๋šœ๋ ทํ•˜๋‹ค๊ณ  ํ•œ๋‹ค. ํ•˜์ง€๋งŒ ์‚ฌ์šฉํ•ด๋ณด๋‹ˆ ๋ถˆํŽธํ•œ ๊ฒŒ ์ •๋ง ๋งŽ์•˜๋˜ ๊ฒƒ ๊ฐ™๋‹ค. ํ™”์งˆ ์ €ํ•˜ ์ด์Šˆ๋„ ์žˆ์—ˆ๊ณ , ๊ฐ€๋”์”ฉ Image ์ปดํฌ๋„ŒํŠธ์˜ ์ตœ์ ํ™” ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ๊ธ€๋“ค์„ ๋ณด๋ฉด์„œ Image ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์žฅ์ ์ด ๋ญ๊ณ , ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ•ด์•ผ ์ž˜ ์‚ฌ์šฉํ•˜๋Š”์ง€๋ฅผ ์ œ๋Œ€๋กœ ์•Œ๊ณ  ์‚ฌ์šฉํ•ด์•ผ๊ฒ ๋‹ค๊ณ  ์ƒ๊ฐ๋๋‹ค.
๊ทธ๋ž˜์„œ ์ข€ ๋” ๋Šฆ์–ด์ง€๋ฉด ์ˆ˜์ •ํ•˜๊ธฐ ๊ท€์ฐฎ๋‹ค๊ณ  ๋ฏธ๋ฃฐ ๊ฒƒ ๊ฐ™์•„์„œ ํ”„๋กœ์ ํŠธ ์ดˆ๋ฐ˜์— Image ์ปดํฌ๋„ŒํŠธ์— ๋Œ€ํ•ด ํ™•์‹คํžˆ ์ •๋ฆฌํ•˜๊ณ  ๊ฐ€๊ธฐ๋กœ ํ–ˆ๋‹ค.

next/image

Next.js์—์„œ ์ด๋ฏธ์ง€ ์ตœ์ ํ™”๋ฅผ ์œ„ํ•ด ์ œ๊ณต๋˜๋Š” ์ปดํฌ๋„ŒํŠธ๋กœ Next.js ์„œ๋ฒ„๋ฅผ ๊ฑฐ์ณ ์ตœ์ ํ™”๋œ ํฌ๊ธฐ์˜ ์ด๋ฏธ์ง€๋ฅผ ๊ฐ€์ ธ์˜จ๋‹ค.

์•„๋ž˜์™€ ๊ฐ™์€ ์žฅ์ ์„ ๊ฐ€์ง„๋‹ค.

์ตœ์ ํ™”
์ด๋ฏธ์ง€ ํฌ๊ธฐ๋ฅผ ๋ฏธ๋ฆฌ ์ค„์—ฌ์ฃผ์–ด ์‚ฌ์šฉ์ž์˜ ์ด๋ฏธ์ง€ ๋กœ๋”ฉ ์‹œ๊ฐ„์„ ๋‹จ์ถ•์‹œ์ผœ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ํ–ฅ์ƒ์‹œํ‚จ๋‹ค.
์‚ฌ์šฉ์ž ๋””๋ฐ”์ด์Šค ํฌ๊ธฐ ํ˜น์€ ํ™”๋ฉด ํ•ด์ƒ๋„์— ๋งž์ถฐ ์ ์ ˆํ•œ ํฌ๊ธฐ์˜ ์ด๋ฏธ์ง€๋ฅผ ๋‹ค์šด๋กœ๋“œ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค. ๋””๋ฐ”์ด์Šค๊ฐ€ ์ž‘์„ ์ˆ˜๋ก ์„ฑ๋Šฅ ๊ฐœ์„ ์ด ํด ์ˆ˜ ์žˆ๋‹ค.
webp ๊ฐ™์€ ์ตœ์‹  ์ด๋ฏธ์ง€ ํฌ๋งท์„ ์ง€์›ํ•˜์—ฌ ์ด๋ฏธ์ง€ ์šฉ๋Ÿ‰์„ ์ตœ์ ํ™”ํ•˜๊ณ  ์ด๋ฏธ์ง€ ๋กœ๋”ฉ ์‹œ๊ฐ„์„ ์ค„์ธ๋‹ค.
์ง€์—ฐ ๋กœ๋”ฉ
์ด๋ฏธ์ง€๊ฐ€ ๋ทฐํฌํŠธ์— ๊ทผ์ ‘ ํ•  ๋•Œ๊นŒ์ง€ ๋กœ๋”ฉ์„ ์ง€์—ฐ์‹œํ‚จ๋‹ค. ์ด๋Š” ์ดˆ๊ธฐ ํŽ˜์ด์ง€ ๋กœ๋“œ ์‹œ ํ•„์š”ํ•˜์ง€ ์•Š์€ ๋ฆฌ์†Œ์Šค๋ฅผ ๋กœ๋“œํ•˜์ง€ ์•Š์Œ์œผ๋กœ์จ ์„ฑ๋Šฅ์„ ํ–ฅ์ƒ์‹œํ‚จ๋‹ค.
์ž๋™ ์บ์‹ฑ
์„œ๋ฒ„์—์„œ ์ด๋ฏธ์ง€ ํฌ๊ธฐ๋ฅผ ์ž๋™์œผ๋กœ ์กฐ์ ˆํ•˜๊ณ  ์บ์‹ฑํ•œ๋‹ค. ์บ์‹ฑ์„ ํ•˜๋ฉด ์š”์ฒญ ์‹œ ๋น ๋ฅด๊ฒŒ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ์–ด ๋ฐ˜๋ณต๋œ ์š”์ฒญ์— ๋Œ€ํ•œ ์„œ๋ฒ„์˜ ๋ถ€๋‹ด์„ ์ค„์ธ๋‹ค.
placeholder ์ œ๊ณต
Next/Image๋Š” ๋ ˆ์ด์•„์›ƒ์ด ํ”๋“ค๋ฆฌ๋Š” ํ˜„์ƒ(CLS(Cumulative Layout Shift))์„ ๋ฐฉ์ง€ํ•œ๋‹ค. ์ด๋ฏธ์ง€๊ฐ€ ๋กœ๋“œ๋˜๊ธฐ ์ „์—๋„ ์ด๋ฏธ์ง€ ๋†’์ด๋งŒํผ ์˜์—ญ์„ ํ‘œ์‹œํ•ด์„œ ์ด๋ฏธ์ง€๊ฐ€ ๋กœ๋“œ๋œ ํ›„์— ๋ ˆ์ด์•„์›ƒ์ด ํ”๋“ค๋ฆฌ์ง€ ์•Š๋„๋ก ํ•œ๋‹ค.

๋‹ค์Œ๊ณผ ๊ฐ™์ด Image ์ปดํฌ๋„ŒํŠธ๋ฅผ import ํ•˜์—ฌ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

import Image from 'next/image'
 
export default function Page() {
  return (
    <Image
      src="/profile.png"
      width={500}
      height={500}
      alt="Picture of the author"
    />
  )
}

์ผ๋ฐ˜์ ์ธ img ํƒœ๊ทธ์™€ ๋น„์Šทํ•œ ํ•„์ˆ˜ ์†์„ฑ์„ ๊ฐ€์ง„๋‹ค.

src : ์ด๋ฏธ์ง€ ๊ฒฝ๋กœ
alt : ๋Œ€์ฒด ํ…์ŠคํŠธ
width : ์ด๋ฏธ์ง€ ๊ฐ€๋กœ ํฌ๊ธฐ
height : ์ด๋ฏธ์ง€ ์„ธ๋กœ ํฌ๊ธฐ

์•„๋ž˜ ๋‚ด์šฉ๋“ค์€ ์„ ํƒ ์†์„ฑ์ด๋‹ค.
1. fill : (boolean) ์ด๋ฏธ์ง€๋ฅผ ๋ถ€๋ชจ ์ปจํ…Œ์ด๋„ˆ์˜ ํฌ๊ธฐ์— ๋งž์ถฐ์„œ ์ž๋™์œผ๋กœ ์ฑ„์šฐ๋„๋ก ์„ค์ •ํ•œ๋‹ค.
fill ์†์„ฑ์„ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ์—๋Š” width/height ๊ฐ’์„ ์ง€์ •ํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค. ์ด๋ฏธ์ง€ size๋ฅผ ๋ชจ๋ฅด๋Š” ๊ฒฝ์šฐ์— ์œ ์šฉํ•˜๋‹ค.
fill์€ ์ž๋™์œผ๋กœ position: absolute ์†์„ฑ์ด ๋ถ€์—ฌ๋˜๋ฏ€๋กœ, ๋ถ€๋ชจ ์š”์†Œ์—๋Š” position: relative ๋“ฑ๊ณผ ๊ฐ™์€ ์†์„ฑ์ด ๋ถ€์—ฌ๋˜์–ด ์žˆ์–ด์•ผ ํ•œ๋‹ค.

object-fit: "contain"์„ style์— ์ถ”๊ฐ€ํ•ด์ฃผ๋ฉด, ์ด๋ฏธ์ง€๊ฐ€ ์ปจํ…Œ์ด๋„ˆ์— ๋งž๊ฒŒ ๋Š˜์–ด๋‚œ๋‹ค.
object-fit: "cover"์„ style์— ์ถ”๊ฐ€ํ•ด์ฃผ๋ฉด, ์ด๋ฏธ์ง€๋ฅผ ์ „์ฒด ์ปจํ…Œ์ด๋„ˆ์— ์ฑ„์šฐ๊ณ  ์ข…ํšก๋น„๋ฅผ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ์ž˜๋ผ๋‚ธ๋‹ค.

  1. sizes : (String) ๋ฏธ๋””์–ด ์ฟผ๋ฆฌ์™€ ๊ฐ™์ด ์‚ฌ์šฉํ•˜์—ฌ ๋ทฐํฌํŠธ์— ๋”ฐ๋ผ ์ด๋ฏธ์ง€์˜ ํฌ๊ธฐ๋ฅผ ์ง€์ •ํ•œ๋‹ค.
import Image from 'next/image'
 
export default function Page() {
  return (
    <div className="grid-element">
      <Image
        fill
        src="/example.png"
        sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
      />
    </div>
  )
}
  1. quantity : (number) ์ด๋ฏธ์ง€์˜ ํ’ˆ์งˆ์„ ์ง€์ •ํ•œ๋‹ค. 1๋ถ€ํ„ฐ 100๊นŒ์ง€์˜ ์ •์ˆ˜ ๊ฐ’์„ ์ด์šฉํ•œ๋‹ค. ๊ธฐ๋ณธ ๊ฐ’์€ 75์ด๋‹ค.

  2. priority : (boolean) loading="lazy" ์†์„ฑ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  fetchPriority="high" ์†์„ฑ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ด๋ฏธ์ง€๋ฅผ ์šฐ์„ ์ ์œผ๋กœ ๋กœ๋”ฉํ•œ๋‹ค.

  3. placeholder : (String) ์ด๋ฏธ์ง€์˜ ํ”Œ๋ ˆ์ด์Šคํ™€๋”๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋กœ๋”ฉํ•œ๋‹ค.

    blur: ๋ธ”๋Ÿฌ ์ฒ˜๋ฆฌ๋œ ์ด๋ฏธ์ง€๋ฅผ ๋กœ๋”ฉ ์ „์— ํ‘œ์‹œ (์‚ฌ์šฉ ์‹œ blurDataURL ํ•„์š”).
    empty: ๋ณ„๋„์˜ ์ž๋ฆฌ ํ‘œ์‹œ์ž ์—†์ด ๋ฐ”๋กœ ๋กœ๋“œ. ๊ณต๊ฐ„์€ ์ฐจ์ง€ํ•œ๋‹ค. (๊ธฐ๋ณธ๊ฐ’)
    data:image/... : ์ด๋ฏธ์ง€๋ฅผ ํฌํ•จํ•œ ๋ฐ์ดํ„ฐ๋ฅผ Base64๋กœ ์ธ์ฝ”๋”ฉ

  4. onLoadingComplete : ์ด๋ฏธ์ง€๊ฐ€ ์™„์ „ํžˆ ๋กœ๋“œ๋˜๊ณ  ํ”Œ๋ ˆ์ด์Šค ํ™€๋”๊ฐ€ ์ œ๊ฑฐ๋˜๋ฉด ํ˜ธ์ถœ๋˜๋Š” ์ฝœ๋ฐฑ ํ•จ์ˆ˜.

  5. onError : ์ด๋ฏธ์ง€๊ฐ€ ๋กœ๋“œ๋˜์ง€ ์•Š์„ ๊ฒฝ์šฐ ํ˜ธ์ถœ๋˜๋Š” ์ฝœ๋ฐฑ ํ•จ์ˆ˜.

  6. loading : (String) ์ด๋ฏธ์ง€ ๋กœ๋”ฉ ๋ฐฉ๋ฒ•์„ ์ง€์ •ํ•œ๋‹ค. (๊ธฐ๋ณธ ๊ฐ’ = lazy)
    eager ๊ฐ’์„ ์ค„ ๊ฒฝ์šฐ ์ฆ‰์‹œ ๋กœ๋“œํ•œ๋‹ค. ํ•˜์ง€๋งŒ, ์ด ์†์„ฑ์€ ์„ฑ๋Šฅ์ €ํ•˜๋ฅผ ์ผ์œผํ‚ฌ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ, priority ์†์„ฑ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฑธ ๊ถŒ๊ณ ํ•œ๋‹ค.

  7. unoptimized : (boolean) true์ด๋ฉด ์›๋ณธ ์ด๋ฏธ์ง€๊ฐ€ ํ’ˆ์งˆ, ํฌ๊ธฐ ๋˜๋Š” ํ˜•์‹์„ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๊ณ  ๊ทธ๋Œ€๋กœ ์ œ๊ณต๋œ๋‹ค.

์™ธ๋ถ€ ์ด๋ฏธ์ง€

Next.js 14์—์„œ ์›๊ฒฉ ์ด๋ฏธ์ง€๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ๋ณด์•ˆ๊ณผ ์„ฑ๋Šฅ ์ตœ์ ํ™”๋ฅผ ์œ„ํ•ด next.config.js ํŒŒ์ผ์—์„œ ์™ธ๋ถ€ ๋„๋ฉ”์ธ์„ ํ—ˆ์šฉํ•˜๋Š” ์„ค์ •์„ ํ•ด์•ผ ํ•œ๋‹ค.

module.exports = {
  images: {
    remotePatterns: [
      {
        protocol: 'https',
        hostname: 'example.com',
        port: '',
        pathname: '/account123/**',
      },
    ],
  },
}

pathname์— ์™€์ผ๋“œ์นด๋“œ(* ๋˜๋Š” ** )๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ•ด๋‹น ๋„๋ฉ”์ธ์—์„œ ์ œ๊ณต๋˜๋Š” ๋ชจ๋“  ์ด๋ฏธ์ง€๋ฅผ ํ—ˆ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

์ฐธ๊ณ  ์ž๋ฃŒ

<Image>
Goodbye next/image
Next/Image๋ฅผ ํ™œ์šฉํ•œ ์ด๋ฏธ์ง€ ์ตœ์ ํ™”

profile
Frontend๐Ÿ“

0๊ฐœ์˜ ๋Œ“๊ธ€