Next.js는 기본적으로 이미지 파일을 import 하게되면 리소스 URL 방식으로 가져오게 됩니다. 따라서 다음과 같은 import 구문은 string으로 작동하게되죠.
그런데 CRA나 다른 프레임워크를 사용하셨다면 이는 굉장히 불편하게 느껴질겁니다. 왜냐하면 다른 프레임워크는 SVG 파일에 대해서는 URL이 아닌 React 컴포넌트로 변환해 불러오게 됩니다. 이를 이용해 CSS의 fill 속성을 이용해 아이콘의 색상을 변경하는 방식을 사용하는 사람이 존재할 겁니다. (제가 그렇거든요..)
이를 해결하기 위해 next.config.js
를 어떻게 수정하고, 또 기존 URL 방식을 유지하면서 사용할 수 있도록 하는지 방법을 알아보도록 하겠습니다.
Next.js에서는 기본적으로 웹팩 플러그인을 지원합니다. 그리고 SVG파일을 React 컴포넌트로 변환하는 유명한 플러그인 패키지는 @svgr/webpack
입니다. 이 플러그인을 사용해야지만 저희가 원하는게 가능하기 때문에 설치를 진행해봅시다.
Next.js에는 각종 설정을 관리하기 위한 파일인 next.config.js
파일이 존재합니다. 해당 파일의 webpack
을 사용해 기존 Next.js에서 사용하는 웹팩 설정을 수정할 수 있습니다. 이를 이용해 svg 파일 불러오는 규칙을 수정할 예정입니다. 아래 내용을 확인하시고 적용하시면 됩니다.
Next.js 13의 실험적 기능인 TurboPack에서는 @svgr/webpack 사용이 가능하나 기존 규칙인 url 파일 로더를 사용 할 수 없어, 둘 중 한가지만 사용해야합니다.
위 설정을 적용하고 SVG 파일을 import 하게되면 아직 URL로 불러오는 것을 확인 할 수 있습니다. 저는 주로 이미지로서 사용하기 때문에 import 문에 쿼리를 작성해야 React 컴포넌트로 불러오도록 만든 상태입니다. 아래 예제를 보시면 쉽게 이해하실 수 있을 겁니다.
svg 파일 경로에 이상한 문구가 추가된걸 보실 수 있을겁니다. !
를 사용하는건 위 설정에서 resourceQuery
를 통해 읽히는 쿼리 내용입니다. 이를 통해 ...svg!components
로 불러오게되면 @svgr/webpack
을 사용할 수 있도록 웹팩이 도와주는 겁니다. 이제 URL 방식과 React 컴포넌트 방식 두가지를 모두 사용할 수 있게되었습니다!
제가 알려드린대로 사용한다면 이미지가 밖으로 벗어나거나, 아주 작게 보이는 문제가 생겼을 겁니다. 이를 해결하기 위해 실제 svg 파일과 렌더링된 svg 태그를 직접 비교해봤습니다.
width
height
는 제가 컴포넌트 props를 통해 직접 설정했으니 무시해도 됩니다만, viewBox
라는 속성이 누락되어 있는 것을 확인하실수 있을겁니다. 이 속성은 어떤 역할을 하는데 이러한 문제가 생기는지 궁금해집니다. MDN 문서의 내용을 봅시다.
viewBox
속성은 특수한 container element 에 fit 하기 위한 주어진 graphics stretch 의 set 을 명시한다.
말이 어렵지 간단하게 풀어보자면 SVG 파일의 그려진 그림의 캔버스 크기를 설정하는 속성이라 생각하시면 됩니다. 해당속성은min-x min-y width height
의 숫자로 구성된 문자열을 작성하게 되는데, svg 파일을 렌더링 할 때 캔버스 좌상단 좌표, 캔버스 크기를 의미하게 됩니다. 이 속성이 있어야지만 svg태그의width
height
가 의미하는 크기는viewBox
가 의미하는 캔버스 크기로 만들어 줄 수 있습니다.
그림판으로 예를 들어 보겠습니다. 그림판을 열어, 그림을 그리고, 캔버스 크기를 조절해 봅시다. 캔버스 크기를 조절하는 것이 svg태그의 width
height
를 조절하는 것을 의미합니다. 그림 자체는 캔버스의 상대값으로 그려진 것이 아니기 때문에 캔버스 크기를 조절한다고 그림의 사이즈가 같이 바뀌지 않고, 잘리는 현상을 볼 수 있습니다. 이는 제가 설명하는 viewBox
속성이 없는 상태의 svg와 같은 상태라 보시면 됩니다. viewBox
는 그림판을 열었을 때의 캔버스 크기를 의미하여, 그려진 그림의 데이터는 이 값을 기준의 데이터를 가지게 됩니다. 그러하니, viewBox
와 width
height
간의 비율을 안다면, 그림을 렌더링 할 때의 비율도 알 수 있어 캔버스 밖으로 잘리는 일이 발생하지 않게되는 원리입니다.
그러면 이 상황이 왜 발생하는지 svgr의 설정을 찾아보겠습니다.
svg공식 문서에서 최적화 관련 설정이 있는지, 아님 버그인지 찾다보면 svgr에서 svgo라는 SVG파일 최적화 라이브러리를 통해 자동으로 최적화를 진행하는 것을 발견할 수 있습니다. 이 최적화 설정 기본값이 viewBox
라는 속성이 제거하도록 설정이 되어있기 때문에 문제가 발생하는 것입니다.
이를 해결하기 위해 svgo 라이브러리 설정을 바꿔주겠습니다. svgo 설정은 프로젝트 최상단에서 svgo.config.js
파일을 만들어 바꿀 수 있도록 만들어져 있습니다. 이제 공식문서를 참조하여 설정을 바꾸겠습니다.
svgo가 기본 플러그인 프리셋인 preset-default
플러그인을 불러오는데 옵션에 overrides
를 통해 프리셋 설정을 덮어 씌우게 됩니다. 플러그인 중 removeViewBox
라는 플러그인을 false
로 설정해 비활성화하여 서버를 실행해보면 viewBox
가 나타나는 것을 확인하실 수 있습니다.
이로써 모든 문제를 해결하였습니다!
프레임워크는 모든것을 다해주지만, 설정이 유연하지 못하다면, 쓰는 것 만도 못한 상황이 닥치곤 합니다. 대표적으로 구 버전의 react-scripts
(create-react-app)가 있겠네요. 확실히 몇년 전보다는 프레임워크나 라이브러리의 지원이 상당히 유연하게 바뀐거 같습니다. 덕분에 공식문서를 보기만해도 이런 문제를 해결할 수 있는 정도까지 도달하게 되어 굉장히 편해진거 같습니다. 만약 문제가 생겨 설정을 바꿔야 한다면 공식문서를 참조하시는게 어떤가요?