placeholder에 blur를 부여하면 아래 사진과 같은 효과를 볼 수 있다.리모트 이미지의 경우에는 추가적으로 blurDataURL을 입력해주어야한다.
하지만 로컬 이미지가 동적이라면?
현재 내가 만드는 블로그 같은 경우는 포스트의 이미지 컴포넌트에 src로 로컬 경로를 받으면 해당 경로를 할당한다.
하지만 이런 방식으로 구현한다면 Image 컴포넌트가 로컬 이미지로 취급하지 않고 리모트 이미지로 취급하기에 placeholder의 효과를 볼 수 없게 된다.왜냐면 import로 불러온 이미지를 src에 넣는 케이스와 문자열 경로를 넣는 케이스와 내부에서 다르게 처리하기 때문인듯 하다.보시다싶이 import를 통해 이미지를 불러올 경우 객체 형태로 불러오게 되며 프로퍼티로 blurDataURL과 원본 크기 또한 가지고 있다.하지만 단순히 파일 경로를 문자열로 넣을 경우에는 blurDataURL나 width 같은 정보들이 없기 때문에 이를 일일히 입력해주어야한다.
(물론 입력한다고 lazy loading이 적용되지는 않았다)하지만 import의 경우 동적인 값을 기반으로 불러올 수 없기 때문에 이를 해결할 방법을 찾아야 했다.
내가 해결한 방법
나는 이 문제를 해결하기 위해 import 대신에 require()를 사용하여 이미지를 가져오는 방식으로 수정했다.기존의 코드는 다음과 같다.
이렇게 하면 이미지를 props의 경로에 따라 동적으로 가져올 수 있게 된다.하지만 당연히 문제는 이것만으로 끝나지 않았다.
첫번째 이슈, 빌드 오류
위와 같은 방법으로 작성 후 페이지에 들어가보니 예상치 못한 오류가 나타났다.
./public/robots.txt
Module parse failed: Unexpected character ' ' (1:1)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
| User-agent: *
| Allow: /
갑자기 뜬금없는 robots.txt 관련 오류가 나타났다.
어디서 연관이 생겼길래 robots.txt에 에러가 생겼는지 알 수가 없어서 일단 robots.txt을 제거하고 재빌드를 해보니 이번엔 사이트맵에서 동일한 오류가 났다.현재 사용하고 있는 next-sitemap 라이브러리와 충돌이 일어난건지 robots.txt, sitemap.xml, sitemap-0.xml 이 3가지 파일에서 빌드시 에러 요소가 나타났다.원인 불명이고 stackoverflow나 깃허브 이슈를 봐도 관련 내용이 없길래 이를 어쩌지 하다가 해결방법이 떠올랐다.
해결법
사실 해결이라기엔 뭐한게 그냥 문제의 근원을 없애버렸다.그저 웹팩 빌드 시 사이트맵과 robot.txt를 제외하는것.마치 교통사고를 줄이기 위해서 자동차를 없애는 무식한 방법이라 마음에 걸리긴 했지만 저 3개의 파일 자체가 빌드와 크게 상관이 없는 파일들이라 그냥 실행하기로 했다.제거 방법으로는 npm을 통해 ignore-loader를 설치한 후 next.config.js에서 예외 처리를 실행했다.
위와 같이 입력할 시 robots.txt, sitemap.xml, sitemap-0.xml 이 3가지 파일을 빌드시 제외하게 된다.
하지만 예외처리 한다고해서 sitemap-0.xml이 자동 생성 되지 않는것은 아니라서 SEO에는 문제가 없었다.
두번째 이슈, gif
첫번째 문제점을 고치자 두번째 문제점이 나왔다.
이번 이슈는 Image 컴포넌트의 gif blurDataURL 미지원.만약 gif를 불러온 후 콘솔에 찍어보면 blurDataURL가 없는 걸 볼 수 있다.
그렇다보니 gif의 경우에는 blurDataURL를 추가적으로 입력해주어야한다.
1constPostContentImg=({ src, alt }:Props)=>{2const image =require(`../../../public${src}`).default;3const[loaded, setLoaded]=useState(false);456return(7<PostContentImgBox>8<div className="image-box">9{10 image.src.includes('.gif')?11<Image12 src={image}13 alt={alt}14 placeholder='blur'15 blurDataURL={image.src}16 fill
17 loading="lazy"18 sizes="100%"19/>20:21<Image22 src={image}23 alt={alt}24 placeholder='blur'25 fill
26 loading ='lazy'27 sizes="100%"28/>29}30</div>31{alt &&<figcaption className="image-desc">{alt}</figcaption>}32</PostContentImgBox>33)34}3536exportdefaultPostContentImg;37
이를 해결하기 위해 이미지 파일의 확장자에 대한 분기처리를 추가하였다.
이렇게만 해줘도 기본적으로 사이즈를 가지고 있기에 CLS는 일어나지 않지만, 시각적인 부분에서 이미지 영역에 대한 정보를 얻을 수 없다는 점이 아쉬웠다.그래서 스켈레톤 UI까진 아니어도 간단하게 로드 전에 gif의 영역을 표시하도록 수정하였다.
Image 컴포넌트의 onLoad를 활용해서 이미지 로딩이 완료되는걸 판단하여 배경색을 변경하도록 수정하였다.추가적으로 데스크탑 환경의 경우 레이아웃에서 포스트 영역의 가로 최대 80%만 차지하도록 구현하였는데,
이때 종횡비도 유지하기 위해 종횡비를 계산 후 컴포넌트에 부여하여 원본 비율을 유지하도록 변경하였다.