throw new Error('@prisma/client did not initialize yet. Please run "prisma generate" and try to import it again.');
npx prisma generate를 하고 npm start로 서버를 실행하려고 했을 때, 이런 오류가 발생했다.
이 에러가 날 당시 코드는 아래와 같았다.
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
export default prisma;
내 프로젝트는 CommonJS가 아닌 ESM을 사용하고 있었기 때문에, 기본값이 CommonJS인 Prisma와 호환성 문제가 발생한 것이다.
Object.defineProperty(exports, "__esModule", { value: true }); ^ReferenceError: exports is not defined in ES module scopeThis file is being treated as an ES module because it has a '.js' file extension and 'C:\Users\...(중략)\package.json' contains "type": "module". To treat it as a CommonJS script, rename it to use the '.cjs' file extension.
import { createRequire } from 'module'
const require = createRequire(import.meta.url)
const { PrismaClient } = require('@prisma/client')
그래서 이렇게 createRequire을 이용해 ESM 환경에서 CommonJS 모듈을 로드하고자 변경해봤지만, 소용이 없었다...
prisma generate를 하면 기본적으로 node_modules/.prisma/client 경로에 클라이언트가 생성된다는데, 내 프로젝트에서는 그렇지 않았다. 추적해본 결과 src/generated/prisma 안의 index.js와 edge.js에서 내가 정의한 스키마가 보였다. 이런 식으로...
import { PrismaClient } from '../generated/prisma/index.js';
const prisma = new PrismaClient();
export default prisma;
그래서 이렇게 경로를 변경해보았지만, 이번엔 이런 에러가 발생했다.
import { PrismaClient } from '../generated/prisma/index.js';
^^^^^^^^^^^^ SyntaxError: The requested module '../generated/prisma/index.js' does not provide an export named 'PrismaClient'
stack overflow에 있는 해결책들을 모두 시도해보았지만 실패했다...why?
그런데 더 알아보던 와중 "tsx"라는 Node.js의 확장 도구의 존재를 알게 되었다.
tsx
tsx (TypeScript Execute) - The easiest way to run TypeScript in Node.js
tsx.is
❓ TSX
- 타입스크립트 코드를 쉽게 실행할 수 있도록 해주는 Node.js 확장 도구.
- 일반적으로는 node로 명령어로 실행하지만, tsx를 사용하려면 tsx 명령어로 실행한다.
- ESM과 CommonJS 두 모듈 시스템을 원활하게 지원한다.
- 모듈 호환성 말고도 간단한 설정, esbuild를 활용한 빠른 컴파일 속도, tsconfig.json 경로 지원이라는 장점이 있다.
❗ TSX의 작동 방식
1. 백그라운드에서 esbuild를 사용하여 TypeScript 코드를 JavaScript로 빠르게 변환
2. ESM과 CommonJS 간의 원활한 가져오기/내보내기를 지원하여 모듈 시스템 간의 호환성 문제를 해결
3. 변환된 코드를 Node.js 런타임에서 실행
4. tsx watch 명령을 사용하면 파일 변경을 감지하고 자동으로 재시작(dev 스크립트에서 사용)
아래의 명령어로 설치 후,
npm install -D tsx
package.json의 스크립트를 다음과 같이 업데이트하면 간편하게 사용할 수 있다.
"start": "tsx index.ts",
"dev": "tsx watch index.ts",
나는 그전에 node.js에서 타입스크립트를 사용하기 위해 ts-node를 사용했었는데, 이런 이유로 tsx로 변경하게 되었다.
그리고 ts-node에 비해 tsx의 속도는 23%정도 빠르다고 하니, 속도가 중요한 프로젝트에서 적합한 도구이다.
🤔 commonJS말고 ESM을 선택한 이유는?
1. 정적 분석 가능 -> 트리 셰이킹 용이 -> 번들 크기 감소
2. 브라우저 환경에서 비동기 로드 지원 -> 로딩 속도 향상
3. require보다 가독성 향상
많은 라이브러리들이 순수한 ESM으로 구현하고, commonJS는 지원을 중단하는 추세에 있다. 하지만 현재 많은 프로젝트들이 commonJS로 작성되어있으니, 동기적으로 로드되는 commonJS의 작동 방식을 이해하고 있는 것이 좋겠다.
참고
'트러블 슈팅' 카테고리의 다른 글
[Axios] 서버에서 사용자가 업로드한 파일을 받아오지 못하는 에러 핸들링 (0) | 2025.05.03 |
---|---|
[Next.js + Zustand] localStorage hydration 에러 핸들링 (0) | 2025.05.02 |
[Next.js] Image 경고(LCP, 종횡비) 해결하기 (0) | 2025.04.10 |
[React/Zustand] 리액트 훅은 함수 컴포넌트 내부에서만 호출될 수 있습니다. (0) | 2025.03.02 |