231017(화) 성장
🚤 성장일지 7.0
책 행복한 이기주의자(웨인 다이어)
의 내용에 자극받아 시작하는 소박한 성장기록
살아있는 꽃과 죽은 꽃은 어떻게 구별하는가?<br/> 성장하고 있는 것이 살아 있는 것이다.<br/> 생명의 유일한 증거는 성장이다!
⚛ (7.0)<완전 개편>
파인만 학습법
을 알게 된만큼, 성장일지는 정말 그 날의 키워드 중심으로 간단하게 정리하도록 한다.
Next.js에서의 svg
보통 React를 사용할 때는 아래와 같이 svg 파일을 React Component로 변환해서 사용하곤 했다.
import { ReactComponent as Logo } from './logo.svg'; // <Logo />
그런데 Next.js에서는 이렇게 사용하면 에러가 발생한다. 이유는 Next.js에서는 svg를 모듈로 인식하지 않기 때문이다. Next.js에서 svg를 사용하는 방법은 여러가지가 있는데 그 중 2가지를 기록해둔다.
1. import 후 Image 컴포넌트 사용(비추)
이 방법은 사실 정말 간단하다. svg를 import하고 Image 컴포넌트를 사용하면 된다. 하지만 이 방법은 svg를 React Component로 변환하는 것이 아니라 그냥 이미지로 사용하는 것이기 때문에 svg의 장점을 살릴 수 없다. 예를 들어, svg의 이미지의 크기나 색상 등을 props 혹은 css 형태로 변경할 수 없다. 또한 Next.js 공식문서에도 나와있듯이 Image 컴포넌트로서의 최적화에 있어서 효율성이 떨어진다.
import Logo from './logo.svg'; <Image src={Logo} />;
2. @svgr/webpack
사용
이 2번째 방법이 흔히 권장되는 방법인데, 그 방법은 다음과 같다. @svgr/webpack
을 사용하면 svg를 React Component로 변환해주는 webpack loader를 사용할 수 있다. 이렇게 하면 svg를 React Component로 변환해서 사용할 수 있다.
그 구체적인 방법은 참고에 너무 잘나와있어서 그대로 따라하면 된다.
다만, 2가지 주의점(혹은 팁)이 있다.
- 위의 방법대로 했는데, 변환된 svg component가 서버 컴포넌트에서는 작동하지 않는 에러가 발생한다.(
Module parse failed: Unexpected token
) 아래와 같이 하면 해결할 수 있다.
webpack(config) { // SVG 가져오기를 처리하는 기존 규칙을 가져오기 const fileLoaderRule = config.module.rules.find(rule => rule.test?.test?.('.svg')); config.module.rules.push( // ?url로 끝나는 svg 가져오기에만 기존 규칙을 적용 { ...fileLoaderRule, test: /\.svg$/i, resourceQuery: /url/, // *.svg?url }, // 다른 모든 *.svg 가져오기를 React 구성 요소로 변환 { test: /\.svg$/i, // issuer: /\.[jt]sx?$/, // *.svg를 가져오는 파일 => 이 부분을 삭제한다. resourceQuery: { not: /url/ }, // *.svg?url 제외 use: ['@svgr/webpack'], }, ); // svg에 대한 처리를 했으므로 *.svg를 무시하도록 파일 로더 규칙을 수정 fileLoaderRule.exclude = /\.svg$/i; return config; },
위에 주석에도 있듯이 issuer
를 삭제해야 한다. 아주 정확한 이유는 아니지만 찾아보니 issuer가 svg 파일을 가져오는 파일
을 명시하는 것인데, 실제 서버에서 실행되는 서버 컴포넌트는 빌드된 파일이기 때문에 명시된 파일이 없어서 그런 것 같다.
(추가) 좀더 방법을 찾아보던 중 문제 관련 깃헙 이슈 코멘트에서 아래와 같이 적용해서도 해결할 수 있었다.
webpack(config, { isServer }) { // SVG 가져오기를 처리하는 기존 규칙을 가져오기 const fileLoaderRule = config.module.rules.find(rule => rule.test?.test?.('.svg')); config.module.rules.push( // ?url로 끝나는 svg 가져오기에만 기존 규칙을 적용 { ...fileLoaderRule, test: /\.svg$/i, resourceQuery: /url/, // *.svg?url }, // 다른 모든 *.svg 가져오기를 React 구성 요소로 변환 { test: /\.svg$/i, issuer: fileLoaderRule.issuer, // *.svg?url 제외 resourceQuery: { not: [...fileLoaderRule.resourceQuery.not, /url/] }, // *.svg?url 제외 use: ['@svgr/webpack'], }, ); // svg에 대한 처리를 했으므로 *.svg를 무시하도록 파일 로더 규칙을 수정 fileLoaderRule.exclude = /\.svg$/i; return config; },
보니까 svg를 가져오는 파일을 명시할 때, 위에서 적용한 *.svg?url
규칙까지 포함돼서 명시되는 것 같다. 그래서 issuer
를 fileLoaderRule.issuer
로 명시해주면 해결된다.
- svg component에 대한 custom 타입 정의해주기
기본적으로 위의 과정을 거쳐서 svg를 react component처럼 사용하게 되면 잘 작동한다! 다만 아쉬운 점은 해당 컴포넌트에 마우스를 올리면 any
타입이 나온다는 것이다. 이 부분은 직접 타입은 선언해줌으로써 해결할 수 있다. 나같은 경우 root 디렉토리에 svg-component.d.ts
를 만들어주었다.
declare module '*.svg' { import React from 'react'; const svg: React.FC<React.SVGProps<SVGSVGElement>>; export default svg; }
그리고 tsconfig.json
에 아래와 같이 추가해주면 된다.
{ "compilerOptions": { "include": ["svg-component.d.ts", "next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"] } }
이제 마우스를 올려보면 React.FC<React.SVGProps<SVGSVGElement>>
타입이 잘 나오는 걸 확인할 수 있다.(그래도 any가 나오면 vscode 재시작해보기~!)
📝 회고
오늘 하루가 뭔가 후우웅하고 지나갔다... 정신 바짝 차리고 살아야겠다잉!!! 시간을 인지하면서 살자!
참고
- Next.js 공식문서: SVG
- SVGR with Next.js
- Reddit: App router + SVGR + Server Components not working
- 위의 이슈 코멘트
undefined