[2장] 모듈 시스템

2022. 4. 19. 16:44책/Node.js 디자인 패턴 바이블

모듈은 주요 애플리케이션들을 구조화하기 위한 부품이다. 모듈은 코드를 작은 유닛으로 나눠줌으로써 개별적으로 개발 가능하고 테스트 가능한 형태로 만들어준다. 또한, 명시적으로 노출시키지 않은 모든 함수들과 변수들을 비공개로 유지해 은닉성을 강화시켜주는 장치이기도 하다. 그리고 여러 언어들은 모듈과 비슷한 개념으로 아래 개념을 사용하고 있다.

  • package (Java, Go, PHP, Rust, Dart)
  • assembly (.NET)
  • library (Ruby)
  • nuit (Pascal dialects)

Node.js의 경우 특이하게 CommonJS(CJS), ECMAScript modules(ESM) 두 가지 모듈 시스템을 사용하고 있다. 이번 장에선 왜 두 가지 형태가 존재하며, 각각의 장단점과 패턴을 알아보도록 하자.

 

1. 모듈의 필요성

모듈이란게 애초에 왜 필요할까? 좋은 모듈 시스템이 소프트웨어 엔지니어링에서 가지는 장점을 알아보자

1) 분할

코드베이스를 여러 파일로 분할하는 방법을 제공한다. 이를 통해 코드를 구조적으로 관리할 수 있고, 각각의 독립적인 기능을 개발 및 테스트할 수 있게된다.

 

2) 재사용

타 프로젝트에서 코드 재사용이 가능하도록 해준다. 모듈로 기능을 구조화해두면 그 기능이 필요한 다른 프로젝트에서도 활용이 가능하다

 

3) 은닉성

보통 복잡한 구현과정은 숨겨둔 채 간단한 인터페이스만 노출시키는 것이 좋은 방식이다. 대부분의 모듈 시스템은 객체, 함수, 클래스 등 필요한 공개 인터페이스를 노출시키면서도 불필요한 코드는 숨김으로써 은닉성을 보장받을 수 있다.

 

4) 종속성 관리

개발자로 하여금 기존 모듈에 의존해 쉽게 빌드할 수 있게 해준다. 또한 모듈 실행에 있어 필요한 일련의 종속성들을 쉽게 임포트할 수 있게 해준다.

 

2. JS와 Node.js에서의 모듈 시스템

Node.js가 처음 만들어질 당시, Node.js는 운영체제의 파일시스템에 직접적으로 접근하는 JS를 위한 서버 런타임으로 구상되었다. 이 때 모듈 관리에 있어 HTML의 <script> 태그나 URL을 통한 리소스 접근에 의존하지 않고, 로칼 파일시스템의 JS 파일에만 의존하는 모듈 시스템을 만들고자 했다. 이를 위해 Node.js는 브라우저가 아닌 환경에서 JS에 모듈 시스템을 제공하도록 고안된 CommonJS(CJU)의 명세를 구현하게 됐다.

 

CommonJS는 시작과 함께 Node.js의 주요 모듈 시스템이 됐고, Webpack같은 모듈 번들러 덕분에 브라우저 환경에서도 유명세를 가지게 된다. 그러나 2015년 ECMAScript 6의 발표와 함께 표준 모듈 시스템 ESM에 대한 공식 제안이 나오게 된다.

 

Node.js는 13.2버전부터 ESM에 대한 안정적인 지원을 하게 됐고, 현재 CommonJS와 ESM이 공존하는 상태가 된 것이다. 물론 미래에 지속 가능한 코드로 남기 위해선 ESM에 익숙해져야 한다.

 

3. 모듈 시스템과 패턴

CommonJS에 대해 구체적으로 알아보기 전에 간단한 모듈 시스템 제작을 위해 정보룰 감추는데 도움이 되는 노출식 패턴에 대해 알아보고 가자. 갑자기 이 얘기를 왜하나 싶겠지만, CommonJS 모듈 시스템이 이 패턴을 기반으로 하기 때문이다.

 

JS의 주요 문제점 중 하나는 네임스페이스가 없다. 즉, 자신만의 스코프가 없이 전역에서 실행되므로 내부 코드나 라이브러리의 주입과정에서 스코프 오염이 발생할 수 있다. 예를 들어 내가 utils라고 선언한 변수가 A라는 라이브러리를 주입하면서 전혀 다른 변수로 덮어쓰여진다면 예측 불가능한 부작용이 발생할 수 있다.

 

이러한 문제를 해결하기 위한 보편적 기법을 노출식 모듈 패턴(revealing module pattern)이라고 하며, 아래와 같은 형식을 가진다.

const myModule = (function() {
    const privateFunc = () => { }
    const privateFunc2 = []
    
    const exported = {
        publicFunc: () => {},
        publicFunc2 : () => {}
    }
    
    return exported
})() // 즉시 실행 함수 표현 (IIFE; Immediately Invoked Function Expression)

console.log(myModule) // {publicFunc: ƒ, publicFunc2: ƒ}
console.log(myModule.privateFunc, myModule.privateFunc2) // undefined undefined

함수 내부에 선언한 변수는 외부에서 접근이 불가능하며, exported로 선언된 객체에만 접근이 가능하다. 이 패턴은 비공개 정보의 은닉은 유지하되 공개될 API를 내보내고 있는데, 이 아이디어가 CommonJS 모듈 시스템에서 주요하게 사용된다.

' > Node.js 디자인 패턴 바이블' 카테고리의 다른 글

[2장] 모듈 시스템(4)  (0) 2022.04.23
[2장] 모듈 시스템(3)  (0) 2022.04.20
[2장] 모듈 시스템(2)  (0) 2022.04.20
[1장] Node.js 플랫폼  (0) 2022.04.16