본문 바로가기

growth-log

CJS와 ESM

예전에 듣다가 중단했던 next 기초강의를 복기할 요량으로 듣고 있다.

( 원래 지난해에 들었어야 했지만.. 이런 저런 사정이 있었다 )

next를 이용한 프론트엔드와 express서버를 이용한 백엔드 구성에 대한 강의인데,

수월히 넘어갈것 같던 프엔쪽이 ..점입가경이어서 아직까지 서버쪽으로 못 넘어가고 있다.

강의 구매 후 2년여의 공백으로 적용되는 라이브러리의 버전이 당시와 많이 달라져,

매 강의마다 버전으로 인한 시행착오가 생겼다. ( react를 17버전을 사용하는 강의에

그대로 따라가긴 뭣해서 react나 node외에 라이브러리 버전을 좀 더 최신화했더니,

초보에게는 뭔가 알수없는 오류의 연속.. )  

ESLint 오류를 넘었더니 이번엔 antd이다. ㅎㅎㅎ

 

⊙ 에러 내용 

1 of 1 error Next.js (15.1.4) out of date (learn more) Server Error Error: Cannot find module '/Users/ .... (이부분은 디렉터리임으로 중간생략)/node_modules/rc-util/es/hooks/useMemo' imported from /Users/.... (이부분은 디렉터리임으로 중간생략)/node_modules/rc-util/es/ref.js This error happened while generating the page.

 

Any console logs will be displayed in the terminal window. finalizeResolution node:internal/modules/esm/resolve (275:11) moduleResolve node:internal/modules/esm/resolve (932:10) defaultResolve node:internal/modules/esm/resolve (1056:11) ModuleLoader.defaultResolve node:internal/modules/esm/loader (654:12) #cachedDefaultResolve node:internal/modules/esm/loader (603:25) ModuleLoader.getModuleJobForRequire node:internal/modules/esm/loader (353:53) new ModuleJobSync node:internal/modules/esm/module_job (341:34) ModuleLoader.importSyncForRequire node:internal/modules/esm/loader (326:11) loadESMFromCJS node:internal/modules/cjs/loader (1414:24) Module._compile node:internal/modules/cjs/loader (1547:5) Object..js node:internal/modules/cjs/loader (1708:10) Module.load node:internal/modules/cjs/loader (1318:32) Function._load node:internal/modules/cjs/loader (1128:12) TracingChannel.traceSync node:diagnostics_channel (322:14) wrapModuleLoad node:internal/modules/cjs/loader (219:24)

 

--> 최초로 antd를 불러서 적용하고 라이브서버를 열면 반영이 잘 되었다.

그러나 페이지를 새로고침 하거나, antd관련된 코드를 반영한 상태로 열면 저런 오류가 나타났다. 

코드 문제는 아니고 어딘가 설정에 미흡함이 있는것이 분명한데, ref.js와 hooks내의

useMemo함수도 분명히 잘 있어서, 이런저런 설정을 바꾸어도 에러가 계속 발생했다.

 

⊙ 해결방법 

next-transpile-modules에서 withTM을 next.config.mjs에 불러와서 antd사용에 관련된

모듈들에 적용해줌으로써 해결할수 있었다. ( next-transpile-module은 next.js에서 외부

모듈을 트랜스파일링 하면서 ESM모듈을 지원해야 할 경우에 사용한다고 한다. )

 

nextjs가 commonjs방식으로 구동을 하고 esm형식의 파일에 대해 트랜스파일링을 하는데,

node-modules내의 파일에 대해서는 이러한 번역(??) 작업을 적용하지 않도록 구성되어 있으므로

이에 예외를 적용해 주기 위해 사용하는 모듈이라 한다. 

antd가 esm으로 구성되면서 생긴 문제라고 봐야 하는건가 ( 이 부분은 추가적인 배움이 있은후에

미래의 내가 코멘트 할 예정이다^^)

 

⊙ ESM과 CommonJS에 대한 내역 정리 

ESModule(ESM)과 CommonJS(CJS) - code 공유, 재사용을 위한 모듈 

- ESM은 브라우저 표준, CJS는 Node의 표준

- ESM에서의 CJS이용은 간편하나 (기존 모듈 대부분 CJS) 반대의 경우 불가능

   

CommonJS는 Node.js의 기본 모듈 시스템 (ESM 이전 방식)

- npmjs.com이나 yarnpkg.com에서 초기에 사용된 방법 

- 각 파일이 별도의 모듈로 처리됨 ( CommonJS 구분으로 import/export)

   const fs=require("fs")

   model.exports = function() {return ... }

- 브라우저가 모듈 시스템이 없었음 (Node.js만 CommonJS로 모듈형태 구현)

 

ES Module은 브라우저에서 모듈작업을 수행 (ES6 by ECMAScript standardization)

- 모듈 처리시 import/ export 구문 활용

   import fs from 'fs'

   export default function() {return..}

- React나 Vue는 ESM구문 사용 (Babel등의 도구로 CommonJS구문 변환수행) 

 

CJS에서의 ESM적용  ( 직접 적용불가, 우회 )

 (1) Node.js에서 ESM 모듈채택 

  ① '.mjs'형식의 모든 ESM파일에  확장자 적용

  ② package.json에 type:"module"으로 선언 (Node에 ESM문법이 적용되었음을 알려줌)

  ** 대부분의 경우 ESM와 CJS의 하이브리드 방식 지원 

(2) 번들러 (Rollup 등)

 - 빌드 타임에 (런타임 아님) 코드의 가져오기와 내보개기 해결 (CJS작성 후 ESModule출력가능)

 

---------------------------------------------------------------------------------------------------

chatGPT에게 묻고 버전도 바꾸고, 캐시 지우고, 설정을 바꾸고 의존성 다시 깔고 이런걸 30번은 한거 같다. 

바벨을 깔아서 antd에 대한 옵션을 줘보거나(next는 기본 SWC를 사용한다고 한다 ),

nextConfig에서 experimental.esmExternals로 설정하거나,

next-with-less로 withLess설정,

next/dynamic을 가져와서 동적 로드설정 등 여러가지 삽질을 다양하게 했다.

사실 정확한 용어의 의미도 분명히 몰라서 확인하기 어려웠다.

필요한 경우에 좀 더 구체적으로 살펴보기 위해 간략히 내용을 정리하고 관련 블로그와 표준 문서의 위치에

대한 흔적을 남긴다.  

 

참조한 문서들 

https://nextjs.org/docs/app/api-reference/config/next-config-js/transpilePackages

https://blog.smilecat.dev/posts/next-transpile-modules

https://narup.tistory.com/280

https://velog.io/@park2348190/AntD%EC%99%80-Next.js-12-14

https://konstantin.digital/blog/es-modules-all-you-need-to-know