이 글에서 정리하는 내용
목표는 Storybook의 동적 import 실패 메시지를 실제 실패 요청·변환 로그와 연결해 원인 파일을 찾는 것입니다. React 컴포넌트와 Vite alias를 알고 시작합니다. @storybook/react-vite 프로젝트를 기준으로 경로·alias·정적 파일·배포 캐시를 구분하고 확인한 원인에만 설정을 바꿉니다.
- 내 증상이 이거면 여기부터 보세요
- 먼저 적용할 핵심 수정 코드
- 왜 이런 오류가 생기는가
- 실제 작업에서 점검하는 순서
- 그래도 안 될 때 볼 예외 케이스
- 다음에 같은 문제를 줄이는 체크리스트
내 증상이 이거면 여기부터 보세요

Storybook은 앱 개발 서버와 비슷해 보이지만 별도의 Vite 환경에서 실행됩니다. 그래서 앱에서는 잘 열리는 컴포넌트도 Storybook 안에서는 alias, 정적 파일, 캐시, addon 버전 때문에 동적 import가 실패할 수 있습니다.
| 증상 | 실제 에러 메시지 | 먼저 볼 위치 | 바로 해볼 조치 | 이동할 섹션 |
|---|---|---|---|---|
| 브라우저 콘솔에 동적 import 실패 | Failed to fetch dynamically imported module |
Storybook 캐시/모듈 URL | 캐시 삭제 후 재실행 | 핵심 수정 코드 |
| 앱은 되는데 Storybook만 alias 실패 | Cannot resolve alias @/components |
viteFinal alias | 앱 Vite alias와 동일하게 설정 | 핵심 수정 코드 |
| 특정 story 파일만 실패 | Failed to load url |
story import 경로 | 대소문자와 named export 확인 | 예외 케이스 |
| 업데이트 후 갑자기 실패 | requested module does not provide export |
addon/framework 버전 | storybook 패키지 버전 정렬 | 왜 생기는가 |
이 오류는 브라우저 Network 탭의 실패한 모듈 URL과 HTTP 상태를 함께 남겨야 재현이 쉽습니다. 404면 story 경로·대소문자, 500이면 Vite 변환 로그, export 오류면 실제 named export를 확인하고 개인 경로나 토큰은 제외합니다.
TypeError: Failed to fetch dynamically imported module
Failed to load url /src/components/Button.stories.tsx
The requested module does not provide an export named
Cannot resolve alias @/components
가장 먼저 브라우저 개발자 도구의 Network 탭에서 실패한 모듈 요청을 확인합니다. 404이면 경로·대소문자·캐시·배포 청크를, 차단됨이면 확장 프로그램을, 500이면 Storybook 터미널의 변환 오류를 먼저 봅니다. 같은 오류 문구라도 HTTP 상태에 따라 해결 경로가 달라집니다.
alias 문제를 일부러 만들어 원인 로그와 연결하기
이미 실행되는 React Vite Storybook의 복사본에서 시작합니다. 앱과 Storybook이 쓰는 패키지 세부 버전은 npm ls storybook @storybook/react-vite vite로 기록합니다. src/components/Button.tsx를 사용하는 story가 먼저 정상 표시되는지 확인하세요. 아래 두 파일은 최소 CSF 예제로 작성한 전체 파일입니다.
src/components/Button.tsx — 전체 파일
export function Button() {
return <button type="button">확인</button>;
}
src/components/Button.stories.tsx — 정상 시작 전체 파일
import type { Meta, StoryObj } from "@storybook/react-vite";
import { Button } from "./Button";
const meta = {
title: "Diagnostics/Button",
component: Button
} satisfies Meta<typeof Button>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Basic: Story = {};
재현할 때는 story의 import 경로만 @missing/components/Button으로 바꿉니다. 등록하지 않은 alias이므로 Vite가 모듈을 찾지 못해야 합니다. 바깥쪽 화면의 Failed to fetch 문구뿐 아니라 터미널의 Failed to resolve import와 실패한 story 요청의 HTTP 상태를 함께 읽으세요. 버전에 따라 바깥 오류 문구는 달라질 수 있습니다.
첫 수정은 경로를 ./Button으로 원복하는 것입니다. 같은 story가 다시 표시되면 최소 재현은 닫혔습니다. 실제 프로젝트에서 @/components/Button을 유지해야 할 때만 다음 절의 viteFinal에서 @ alias를 등록합니다. 최신 builder는 앱 Vite 설정을 병합할 수 있으므로 항상 alias가 없다고 가정하지 말고 실제 적용 구성을 확인합니다.
| 실패 요청/로그 | 점검 내용 |
|---|---|
| 500 + Failed to resolve import | import 경로·대소문자·alias |
| 404 정적 이미지 | public 기준 URL과 staticDirs |
| 오래된 배포 chunk 404 | 배포 파일 보존·캐시 갱신·서비스워커 |
| 요청 차단 또는 CORS | 브라우저 확장·origin·프록시 |
캐시를 비우는 작업은 이전 변환 결과가 남았다는 근거가 있을 때 진행합니다. lockfile과 node_modules를 한꺼번에 지우면 재현 입력이 바뀌어 원인 추적이 어려워집니다. 먼저 한 파일 수정으로 정상/실패를 왕복 확인하세요.
먼저 적용할 핵심 수정 코드
원인 설명을 오래 읽기 전에 아래 설정부터 현재 코드와 대조해보세요. 먼저 Storybook 캐시를 비우고, .storybook/main.ts의 framework, stories, staticDirs, viteFinal이 실제 앱의 Vite 설정과 충돌하지 않는지 확인합니다. 중요한 것은 오류를 덮는 옵션을 추가하는 것이 아니라, 실행 환경과 설정 파일이 같은 기준으로 동작하게 만드는 것입니다.
Storybook Vite 설정 예시
import type { StorybookConfig } from "@storybook/react-vite"
import { fileURLToPath } from "node:url"
import { mergeConfig } from "vite"
const config: StorybookConfig = {
framework: "@storybook/react-vite",
stories: ["../src/**/*.stories.@(ts|tsx)"],
staticDirs: ["../public"],
viteFinal: async (config) =>
mergeConfig(config, {
resolve: {
alias: {
"@": fileURLToPath(new URL("../src", import.meta.url)),
},
},
}),
}
export default config
앱에서 쓰는 alias를 Storybook Vite 환경에도 알려줘야 story import가 같은 기준으로 해석됩니다. Storybook 공식 문서 기준으로 Vite builder를 사용할 때는 viteFinal에서 Storybook의 Vite 설정을 추가로 조정할 수 있습니다.
캐시 제거 후 패키지 버전과 빌드를 함께 확인합니다
설정을 바꾼 뒤에도 이전 모듈 URL이 남아 있으면 같은 오류처럼 보일 수 있습니다. 사용하는 패키지 매니저에 맞는 명령만 실행합니다.
rm -rf node_modules/.cache/storybook
rm -rf node_modules/.vite-storybook
pnpm list storybook @storybook/react-vite --depth 0
pnpm run storybook
pnpm run build-storybook
Remove-Item -Recurse -Force ./node_modules/.cache/storybook -ErrorAction SilentlyContinue
Remove-Item -Recurse -Force ./node_modules/.vite-storybook -ErrorAction SilentlyContinue
pnpm list storybook @storybook/react-vite --depth 0
pnpm run storybook
pnpm run build-storybook
캐시 삭제 후 개발 서버만 확인하지 말고 정적 빌드도 실행합니다. 개발 서버는 통과하지만 build-storybook에서 실패한다면 public 경로, 대소문자, 정적 파일 또는 배포 설정 차이를 우선 확인합니다. Storybook 패키지 일부만 다른 버전이면 같은 major·minor 범위로 정렬한 뒤 lockfile 변경을 검토합니다.
왜 이런 오류가 생기는가
Vite builder는 story 파일을 브라우저가 동적으로 불러오게 만듭니다. 이때 생성된 모듈 URL이 캐시에 남아 있거나 실제 파일 경로와 어긋나면 브라우저는 모듈을 가져오지 못합니다. 네트워크 탭에서 실패한 모듈 URL이 404인지, MIME/type 문제인지, 브라우저 확장 차단인지 먼저 분리하면 원인 범위가 줄어듭니다.
앱의 Vite 설정과 Storybook의 Vite 설정은 자동으로 완전히 같아지지 않습니다. alias, plugin, define 값이 앱에서는 있는데 Storybook에는 없으면 앱 전용 import가 story 안에서 깨집니다.
Storybook 패키지를 일부만 업데이트하면 framework, addon, builder 버전이 엇갈릴 수 있습니다. 이 경우 코드가 아니라 패키지 조합 때문에 모듈 export 오류가 보입니다.
실제 작업에서 점검하는 순서
먼저 실패한 모듈 요청이 404·500·차단 중 무엇인지 나눕니다. 그다음 Storybook 터미널의 첫 오류와 .storybook/main.ts의 stories·viteFinal을 대조하고, 앱 Vite 설정과 다른 alias가 있는지 확인합니다.
두 번째로 한 번에 여러 설정을 바꾸지 않습니다. Storybook Failed to fetch dynamically imported module를 해결하다 보면 관련 파일을 전부 고치고 싶어지지만, 그러면 어떤 변경이 실제 해결책이었는지 알기 어렵습니다. 핵심 설정 하나를 바꾸고 검증 명령을 실행한 뒤 다음 설정으로 넘어가야 합니다.
저장소에는 .storybook/main.ts, 사용하는 패키지 매니저의 lockfile, Storybook·builder·addon 버전을 함께 남깁니다. 로컬 캐시를 지워야만 통과하는 상태가 아니라 새 환경에서 build-storybook이 재현 가능하게 성공해야 합니다.
그래도 안 될 때 볼 예외 케이스
캐시를 비운 뒤에도 같은 동적 import 오류가 나면 실패 URL의 파일명 대소문자, story의 default·named export, @storybook/react-vite와 addon 버전 정렬을 확인합니다. 로컬 개발 서버와 정적 Storybook 빌드를 모두 실행해야 환경별 차이를 찾을 수 있습니다.
다음에 같은 문제를 줄이는 체크리스트

Storybook 오류는 컴포넌트 로직보다 실행 환경 차이에서 먼저 찾는 편이 빠릅니다. 앱 개발 서버와 Storybook 개발 서버가 같은 파일을 같은 방식으로 읽는지 확인하면 원인이 좁혀집니다.
결국 Storybook Failed to fetch dynamically imported module은 한 줄짜리 우회 코드보다 확인 순서가 중요합니다. 에러 문구를 단계별로 나누고, 설정 파일과 실행 명령을 같은 기준으로 맞추면 같은 문제를 훨씬 짧게 끝낼 수 있습니다. 공식 기준은 Storybook React Vite, Storybook viteFinal, Storybook staticDirs, Vite Troubleshooting 문서에서 확인했습니다.
함께 확인할 관련 오류
공식 근거와 실습 확인 범위
공식 문서 확인일: 2026-09-13. 작성·검수 기준: 1.0(2026-09-12). 아래 문서는 이 글에서 사용하는 명령·설정의 근거입니다.
- https://storybook.js.org/docs/builders/vite
- https://storybook.js.org/docs/api/main-config/main-config-vite-final
- https://storybook.js.org/docs/api/main-config/main-config-static-dirs
- https://vite.dev/guide/troubleshooting
명령·구성은 공식 문서와 정적으로 대조했습니다. 별도로 명시한 실행 검증 외에 실제 VS Code UI·브라우저·배포·계정 연결 성공을 보장하지 않습니다. 독자는 본문에 제시한 같은 절차로 수정 전후 결과를 비교하세요.
이 글이 도움이 되었나요?
개발환경 학습 순서
필수 2개 · 전체 11개
읽음 기록 관리
전체 과정 목차 (11개)
- 필수 학습 · pnpm 사용법: 설치·기본 명령어·워크스페이스 설정
- 선택 참고 · Yarn 사용법: Corepack 설치와 Classic·Modern 차이
- 필수 학습 · 환경변수 .env 파일 차이: local, example, development 기준
- 선택 참고 · npm install 의존성 충돌 해결: ERESOLVE 오류가 날 때 해결법
- 선택 참고 · pnpm install 오류 해결: lockfile·workspace 점검 순서
- 선택 참고 · Vite import.meta.env undefined 해결: VITE_ prefix 기준
- 선택 참고 · ESLint flat config 규칙 적용 안됨 해결: eslint.config.js 기준
- 선택 참고 · Tailwind CSS Unknown at rule 경고 해결: VS Code와 PostCSS 설정 확인
- 선택 참고 · Storybook Failed to fetch dynamically imported module 해결 (Vite) 현재 글
- 선택 참고 · VS Code Copilot 디버깅: Agent Debug Log와 Chat Debug
- 시점·기록 · VS Code 1.113 업데이트: MCP와 Thinking Effort 변화
새 글 받아보기
RSS 리더에서 BlogFlow의 새 글을 확인할 수 있습니다.