지난주 next.js로 토이 프로젝트를 배포하고 작성했던 포스팅에서 Image 컴포넌트의 src에 문자열 경로를 지정하는 경우 이미지 엑박이 뜨고, StaticImageData로 바꿨을 때는 정상적으로 이미지가 뜨는 원인을 알았다.
원인을 파악하기에 앞서 StaticImageData를 콘솔에 찍어보니 다음과 같은 객체를 볼 수 있었다.
import HeroImage from '/public/hero_256x256.png';
export default function Home() {
console.log(HeroImage);
return (
<Image src={HeroImage} alt="요즘뭐보니 대표 이미지" priority />
);
}
// HeroImage 객체
{
src: '/_next/static/media/hero_256x256.0d595165.png',
height: 256,
width: 256,
blurDataURL: '/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fhero_256x256.0d595165.png&w=8&q=70',
blurWidth: 8,
blurHeight: 8
}
src prop에 StaticImageData를 주입하면 왜 width, height 등이 자동으로 제공되는지 알 수 있다.
이렇게 src prop에 StaticImageData를 지정하면 빌드 시, .next/static/media 폴더 하위에 이미지 파일이 생성되지만, 문자열 경로를 지정한 경우 .next/static/media 폴더 하위에 이미지 파일이 생성되지 않는다. 그래서 두 가지 경우에 request url에서 url에 대한 쿼리 스트링 달랐던 것이었다.
- src 경로에 문자열을 지정했을 때: url=/hero_256x256.png&w=640&q=75
- src 경로에 StaticImageData를 지정했을 때: url=/_next/static/media/hero_256x256.0d595165.png&w=640&q=75
https://nextjs.org/docs/pages/building-your-application/optimizing/images#local-images
다시 이미지 엑박 이슈로 돌아가면 먼저 처음에 '배포' 이후에 이미지가 뜨지 않는다고 생각했던 게 오판이었다. 배포 전으로 커밋을 돌렸더니 개발 환경에서도 이미지가 400 Bad Request가 status code로 오면서 이미지가 로딩되지 않았다. 브라우저 주소창에 request url 경로를 그대로 치니 다음과 같은 에러 메시지를 볼 수 있었다.
Unable to optimize image and unable to fallback to upstream image
위의 에러 메시지로 검색해 보니 next-learn 레포에서 다음과 같은 PR과 이슈를 찾을 수 있었다.
https://github.com/vercel/next-learn/pull/355
https://github.com/vercel/next-learn/issues/337
내 프로젝트에서도 배포 직전에 로그인한 사용자와 로그인하지 않은 사용자를 구분해 router protection을 하기 위해 middleware.ts 에서 matcher를 사용한 게 화근이었다.
// 수정 전 middleware.ts
export const config = {
matcher: [
/*
* Match all request paths except for the ones starting with:
* - api (API routes)
* - _next/static (static files)
* - _next/image (image optimization files)
* - favicon.ico (favicon file)
*/
'/((?!api|_next/static|_next/image|favicon.ico).*)',
],
};
_next/image 이하 경로에서 middleware 함수가 호출되지 않아야 하는데, 로컬에 있는 .png 파일의 경우 _next/image 임에도 matcher가 잘못 구성되어 middleware가 호출이 되고 있었다. Fix Middleware matcher to make profile pics work #355와 동일하게 matcher를 수정하자 정상적으로 200 OK 응답이 왔다.
// 수정 후 middleware.ts
// middleware.ts
export const config = {
matcher: [
/*
* Match all request paths except for the ones starting with:
* - api (API routes)
* - _next/static (static files)
* - _next/image (image optimization files)
* - favicon.ico (favicon file)
*/
'/((?!api|_next/static|_next/image|favicon.ico|.*\\.png$).*)',
],
};
배포 문제라고 생각해서 Static Assets이나 next.config.js 옵션 등을 찾아보는 수고를 2시간 이상 한 것 같다... 😇 에러 메시지를 만나기까지가 어려웠다. 흑흑.. 기본적으로 Next.js의 Image 컴포넌트를 쓸 때마다 '이미지 최적화', 그래 좋은데, 그거 뭐가 어떻게 되는건데ㅠㅠ 같은 기분이다. next에서 해주는 이미지 최적화 메커니즘을 전혀 모르니 블랙박스 안에 손 넣고 휘젓는 기분이랄까. 디버깅한다고 이것저것 만져보면서 Sharp Missing In Production 에러를 만나기도 했다. next 내부에서 sharp를 쓴다고 언젠가 귓동냥을 했는데, 이게 이거였구나 하고 또다시 스쳐가는.. 이미지 최적화 너란 녀석... 언젠가 너를 또 혹독하게 만나게 되겠지.
Using Next.js' built-in Image Optimization requires sharp as a dependency.
'돌멩이 하나 > 에러는 미래의 연봉' 카테고리의 다른 글
next.js로 만든 토이 프로젝트 배포 (0) | 2024.09.01 |
---|---|
custom event로 렌더링과 무관한 데이터 추적하기 (0) | 2024.08.11 |
무한 스크롤 이슈 디버깅 과정 (0) | 2024.07.27 |
Checkbox 컴포넌트를 만들면서 알게 된 것들 (0) | 2024.05.22 |
React Query를 활용한 테이블에서 데이터 정렬하기 (0) | 2024.03.21 |