Next.js에서 img태그 사용 시 주의사항

#next.js#performance

최근 담당한 서비스의 CWV개선작업 중 이상한 현상을 발견했다. 페이지에 <img>태그를 사용하면 이미지가 강제로 preload처리되는 현상이었다. CWV개선을 위해서는 리소스의 우선순위 최적화가 필수인데. 위의 현상때문에 주요 리소스의 로드를 방해하도록 html이 렌더되었다.

export default function Page() {
  return (<>
    <img src="a.png" />
  </>);
}

// 렌더링 결과:
// "<html><head>
//   <link rel="preload" href="a.png" /> ???
// </head> ...생략... </html>"

위 현상 때문에 LCP를 많이 깎아먹고 있었고 이리저리 소스코드를 보다 보니 이 문제가 next.js가 아니라 react-dom의 의도된 동작이라는 것을 알았다.

[Fizz] Preload “suspensey” images

해당 PR은 이미지들의 loading, fetchPriority 속성에 따라 몇 가지 조건에 해당할 경우 이미지들을 preload처리하는 내용이다.

특이한 건 이후에 <picture>안의 <img>들은 preload하지 않는 PR도 발견했다.

[Fizz][Float] <img> inside <picture> should not preload during SSR

위의 내용을 정리하면. react-dom/server 의 스트리밍 버전인 ReactDomFizzServer의 renderToPipableStream함수는. 이미지가 <picture>바깥에 있거나, fetchPriority="low" 속성을 주지 않을 경우. 강제로 preload처리한다.

리소스 우선순위를 조절해야 하는 경우. <img>태그를 직접 사용하려면 위의 조건을 만족하도록 적절히 수정하거나, next.js가 제공하는 <Image>태그를 사용해야 한다.


위 현상의 경우 별다른 가이드가 없기도 하고. 사실 <img>태그들을 왜 preload처리하는지 이해가 안 된다. 심지어 이미지 태그가 20개면 20개 다 preload하는데 도대체 왜 그런지…

next.js는 개발자들이 이미지를 사용할 때 대부분 <Image> 등 제공된 API들만 사용하길 바란 듯 하다. 그랬다면 위 문제는 없었을 것이긴 하다…

사실 next.js덕분에 개발자 입장에서는 다양한 렌더링 전략을 요구사항에 맞춰 쉽게 구현할 수 있어 좋긴 하다, 그런데 일전의 Suspense사태도 그렇고. 올바른 방향으로 가고 있는지는 잘 모르겠다.