일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- UX
- 30daysdowoonchallenge
- REST_API
- 코드스테이츠
- 회고
- Next.js
- redux
- javascript
- superstarjypnation
- 자바스크립트
- CSS
- html
- 프로토타입
- 큐
- mysemester
- useState
- vercel
- web
- 자료구조
- 카카오
- UI
- 프로그래머스
- Til
- 백준
- 해시테이블
- 스택
- level1
- 생활코딩
- 운영체제
- React
- Today
- Total
데굴데굴
[Next.js] Next.js 13 vercel 배포 시 이미지 로딩 실패 문제 (ENOENT: no such file or directory) 본문
[Next.js] Next.js 13 vercel 배포 시 이미지 로딩 실패 문제 (ENOENT: no such file or directory)
aemaaeng 2023. 10. 6. 11:57서론
Next.js 13 프로젝트를 vercel로 배포하고 나니 이미지 로딩에 실패하는 문제가 발생했다.
이 문제를 해결하느라 몇날 며칠을 골머리를 앓아 블로그에 기록을 남겨두려고 한다.
글은 총 두 편으로 올릴 예정인데, 이 글에 나온 방법은 성능 측면에서 좋은 방법이 아니기에 실제로 이 방식을 쓰기보다는 그냥 나의 삽질 여정으로 봐주면 좋을 것 같다.
문제 상황
프로젝트를 vercel로 배포한 후 이미지가 포함된 글에 들어가게 되면 오류가 뜨며 페이지가 로딩되지 않았다.
[Error: ENOENT: no such file or directory, open 'public/media/247845.jpeg'] {
errno: -2,
code: 'ENOENT',
syscall: 'open',
path: 'public/media/247845.jpeg'
}]
vercel에서 서버 로그를 확인하니 디렉토리나 파일이 존재하지 않는다는 ENOENT: no such file or directory
오류가 떠있었다.
문제의 원인
가설 1. .gitignore
에 public/media
폴더가 포함되어 있기 때문이다.
public/media
폴더는 용량이 큰 사진과 영상, 음성 파일이 포함되어 있어 .gitignore
에 포함시키고 원격 저장소에는 올리지 않았다.
처음엔 이게 문제인가 싶었지만 배포는 로컬에서 vercel-cli로 하고 있었고 deployment summary를 확인했을 때 static assets에 잘 들어가있었다. 또, 같은 폴더에 있는 영상과 음성 파일은 아무 문제없이 잘 불러오고 있었기 때문에 이것 때문은 아닌 것 같았다.
가설 2. fs.readFile()
로 파일을 직접 읽어오는 코드가 있기 때문이다.
영상과 음성은 별도의 가공 없이 바로 렌더링하고 있었지만 이미지는 로딩 전 블러 이미지를 보여주기 위해 plaiceholder
라이브러리를 사용한 함수를 한 번 거친 후에 렌더링되고 있었다.
plaiceholder
로 placeholder 이미지를 생성하려면 파일의 버퍼가 필요한데 그러려면 fs.readFile()
로 파일을 읽어와야 했다. 이 과정에서 이미지 파일을 찾지 못해 ENOENT
에러가 발생하는 것으로 판단했다.
해결 방법
내가 떠올린 해결 방법은 두 가지였다.
- 절대 경로를 이용한다.
- 실행 환경에 따라 이미지를 다른 방식으로 불러온다.
보통의 경우라면 절대 경로를 적용하면 바로 해결된다.
하지만 내 프로젝트에서는 절대 경로를 이용하면 위 캡쳐처럼 서버리스 함수 용량 초과가 발생해 배포 자체가 불가능했다.
따라서 2번의 방법을 선택했다. (하지만 처음에 언급한 것처럼 이 방법은 완전한 해결 방법이 아니었다.)
process.env.NODE_ENV
를 이용해 개발 환경과 배포 환경을 구별하여 파일 접근 방식을 다르게 하는 byEnv()
함수를 작성했다.
const byEnv = async (src: string) => {
if (process.env.NODE_ENV === "development") {
return await fs.readFile(path.join("./public", src));
} else {
return await fetch(`${process.env.NEXT_PUBLIC_API_DOMAIN}${src}`).then(
async (res) => Buffer.from(await res.arrayBuffer())
);
}
};
// utils/getBase64.ts
import { promises as fs } from "fs";
import path from "node:path";
import { getPlaiceholder } from "plaiceholder";
const getBase64 = async (src: string) => {
const byEnv = async (src: string) => {
if (process.env.NODE_ENV === "development") {
return await fs.readFile(path.join("./public", src));
} else {
return await fetch(`${process.env.NEXT_PUBLIC_API_DOMAIN}${src}`).then(
async (res) => Buffer.from(await res.arrayBuffer())
);
}
};
const buffer = await byEnv(src);
const {
metadata: { height, width },
...plaiceholder
} = await getPlaiceholder(buffer, { size: 10 });
return {
...plaiceholder,
img: { src, height, width },
};
};
export default getBase64;
이렇게 하면 배포 환경에서는 이미지 url을 이용해 파일을 불러오기 때문에 경로 문제가 더 이상 발생하지 않고 잘 불러와졌다.
산 넘어 산
문제가 해결된 줄 알고 신나게 배포를 해보았지만 fetch()
로 이미지를 불러오기 때문인지 로딩이 너무나도 오래 걸렸다.
심지어 사진이 많이 포함된 페이지에서는 서버리스 함수가 실행 시간을 초과해 페이지가 아예 다운되어버리는 치명적인 상황이 발생했다.
결국 `fetch()`를 쓰지 않고 절대 경로를 이용해야 했는데 이를 위해서는 서버리스 함수의 용량 초과 문제를 해결해야 했다.
글이 너무 길어질 것 같아 그 과정은 다음 글에 이어서 써보겠다.
'Programming' 카테고리의 다른 글
[Next.js] Next.js 13 배포 시 serverless function 용량 초과 문제 pre-build scripts로 해결하기 (0) | 2023.10.06 |
---|---|
[Next.js] Next.js 13 <Image> plaiceholder 라이브러리 적용기 (1) | 2023.09.21 |
<Deploy> 배포 과정 자동화하기 (with Github actions) (0) | 2022.12.07 |
<WEB> Lighthouse로 웹사이트 성능 분석하기 (0) | 2022.12.05 |
번들링과 웹팩 (0) | 2022.11.23 |