svg 아이콘을 포함한 컴포넌트의 스토리북을 만들때 반드시 오류가 난다.
Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object.
👉 "엘리먼트 타입이 유효하지 않습니다: (기본 내장 컴포넌트의 경우) 문자열이거나 (합성 컴포넌트의 경우) 클래스나 함수여야 하는데, 객체를 받았습니다."
Icon을 컴포넌트처럼 사용하고 있는데, <Icon /> 에서 Icon이 클래스나 함수여야 하지만, 객체라서 컴포넌트로 만들수 없다. 즉 엘리먼트 타입이 유효하지 않다는 오류가 나온것이다.
webpack은 .svg 파일을 보면 기본적으로 파일 경로를 문자열로 반환하거나, file-loader/url-loader 설정에 따라 객체로 변환합니다.
해결
storybook에서 svg 처리하는 방식을 지정해주면된다.
이전에 svg를 import 해서 컴포넌트 방식을 사용하기 위해 “@svgr/webpack 을 사용했었다.
next.config.ts 에 적용하듯이, .storybook/main.ts에 웹펙 설정부분을 추가한다.
import type { StorybookConfig } from "@storybook/nextjs";
const config: StorybookConfig = {
stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"],
addons: [
"@storybook/addon-essentials",
"@storybook/addon-onboarding",
"@chromatic-com/storybook",
"@storybook/experimental-addon-test",
],
framework: {
name: "@storybook/nextjs",
options: {},
},
staticDirs: ["../public"],
// webpackFinal을 통해 Webpack 설정을 변경
webpackFinal: async (config) => {
if (!config.module || !config.module.rules) {
return config;
}
config.module.rules = [
...config.module.rules.map((rule) => {
if (!rule || rule === "...") {
return rule;
}
if (rule.test && /svg/.test(String(rule.test))) {
return { ...rule, exclude: /\.svg$/i };
}
return rule;
}),
{
test: /\.svg$/,
use: ["@svgr/webpack"],
},
];
return config;
},
};
export default config;
해석하자면 다음과 같다.
module의 rules 배열을 순회하면서, 해당 rule에 exclude: /\\.svg$/i 를 적용한다.
즉 file-loader가 svg 모듈을 무시하도록 한다.
그 후에 새로운 file-loader > “@svgr/webpack” 을 추가한다.
참고