이전에 Next.js Image 컴포넌트가 어떻게 동작하는지 설명했습니다. 이번에는 Next.js 이미지 컴포넌트를 가지고 어떻게 이미지 최적화를 시도했는지 설명드리도록 하겠습니다. 설명에 앞서서 제가 한 방식이 옳은 방식인지 아직 모르겠으며 시도하고 있는 과정 중에 있음을 말씀드립니다.
Next.js 에서 기본적으로 제공하고 있는 이미지 최적화 기술에는 이미지 파일 크기 최적화
, CLS 방지
, lazy loading
, 이미지 크기 최적화
가 있습니다. 이 글에서는 적용기~~(라 쓰고 분투기라 읽는다)~~를 위주로 소개해드리고자 하므로 각각의 자세한 설명은 공식 홈페이지 또는 다른 블로그를 참고하시기를 바랍니다.
간단하게만 설명을 드리자면, 다음과 같습니다.
Size Optimization
이미지의 파일 크기를 줄여서 네트워크를 통해 가져오는 이미지 로드 시간을 줄일 수 있습니다.
기능 구현을 위해 사용하는 방법으로는 1) 적절한 이미지 크기 요청, 2) 이미지 압축률 높이기 가 있습니다.
Visual Stability
화면에 페이지가 렌더링된 이후에 이미지 소스의 로딩이 완료되면서 이미지가 차지하는 공간만큼 다른 컨텐츠가 밀려나게 됩니다. (Cumulative Layout Shift, a.k.a. CLS)
이를 위해 Next.js 에서는 Image 태그에서 width, height 를 props 로 받아서 이미지 크기만큼 공간을 미리 잡아줌으로써 이미지가 로딩된 이후에 Layout Shift 가 발생하지 않도록 막습니다.
Faster Page Loads
이미지를 한꺼번에 불러오게 되면 페이지 가장 상단에 있는 이미지가 늦게 불러와질 수 있으며, 이 경우 사용자에게 빈 이미지 공간을 보여주게 됩니다.
현재 viewport 에 해당하는 이미지를 먼저 불러오고, 보이지 않는 이미지는 이후에 스크롤이 되어서 해당 이미지 위치가 viewport 에 진입했을 때 불러오도록 합니다.
Asset Flexibility
서비스를 빌드할 때 이미지 resizing 을 하는 것이 아니라 요청 시점에 필요한 크기를 담아서 서버에 요청합니다. 따라서 local 에 있는 (보통 assets 나 public 폴더에 있는) 이미지뿐만 아니라 서버에서 요청한 이미지도 이미지 요청 시점에 적절한 이미지 크기로 조절해서 요청합니다.(= On-demand image resizing
)
Next.js 의 이미지 캐시는 on-Demand 로 동작합니다. 즉, 이미지 리소스 요청 시점에 캐시를 만들게 되는데 해당 캐시 파일은 <**distDir**>/cache/images
하위에 저장이 됩니다. (저의 경우는 .next/cache/images 하위에 저장이 되었습니다.)
이 시점에서 문제가 발생하게 됩니다. 제가 다니는 회사에서는 Next.js SSR 서비스를 쉽게 관리하기 위해서 docker 를 사용하고 있는데, 새로 빌드를 하게 되면 새로운 docker 이미지가 만들어지면서 이전에 있던 빌드 파일이 모두 사라지게 되었습니다.
해결을 위해서는 docker 이미지가 교체되어도 <distDir>/cache 폴더가 남아있도록 개발해야만 했습니다.
제가 여기서부터 잘못 해결한 것 같다는 생각이 듭니다. 쓸데없는 삽질을 더 보고 싶지 않으시다면 아래로 이동해주세요. Docker setting 을 제대로 했다면 두 번째 시도부터는 하지 않아도 됐을 것 같습니다.. (아직 확실치는 않음)
docker 이미지가 교체돼도 /cache 폴더를 남아있게 하기 위해서 SRE 파트 팀원 분과 협업을 진행했습니다. 처음 제안주신 방법은 기존 docker 이미지를 새로운 이미지로 교체하기 전에 S3에 cache 파일을 업로드한 후, 교체 후 이미지를 실행시키기 이전에 다시 S3 에 있던 데이터를 저장하는 방식 이었습니다.
- name: Save Image cache
run: |
POD=`kubectl get pods --no-headers -o custom-columns=":metadata.name" | grep [저장할 폴더명]`
kubectl cp $POD:/app/.next/cache/images/ ./images -c [저장할 폴더명]
- name: Copy image cache to webview container
run: |
POD=`kubectl get pods --no-headers -o custom-columns=":metadata.name" | grep [저장할 폴더명]`
kubectl cp images $POD:/app/.next/cache/ -c [저장할 폴더명]
kubectl exec $POD -c [저장할 폴더명] -- ls /app/.next/cache/images
CI/CD 쪽을 직접 세팅하지는 않아서 자세한 사항은 모르지만 이미지를 교체하기 전에 hook 같은 것을 이용해서 해당 작업을 진행했습니다.