diff --git "a/_posts/blog/posts/You don't know JS yet/2024-12-15-You don't know JS Yet 2\353\266\200 - 8\354\236\245 \353\252\250\353\223\210 \355\214\250\355\204\264.md" "b/_posts/blog/posts/You don't know JS yet/2024-12-15-You don't know JS Yet 2\353\266\200 - 8\354\236\245 \353\252\250\353\223\210 \355\214\250\355\204\264.md" new file mode 100644 index 000000000..12ce2e4cf --- /dev/null +++ "b/_posts/blog/posts/You don't know JS yet/2024-12-15-You don't know JS Yet 2\353\266\200 - 8\354\236\245 \353\252\250\353\223\210 \355\214\250\355\204\264.md" @@ -0,0 +1,161 @@ +--- +title: You don't know JS Yet 2부 - 8장 모듈 패턴 +author: 신성일 +date: 2024-12-11 19:00:00 +0900 +categories: study, YDKJSY +tags: + - "#study" +--- +## 8.1 캡슐화와 최소 노출의 원칙(POLE) + +캡슐화의 목표는 정보(데이터)와 동작(함수)를 한데 묶거나 함께 배치에 공통의 목적을 달성하는 것이다. 이러한 캡슐화는 공통의 목적을 가진 코드 일부분을 별도의 파일로 옮기는 것만으로도 실현할 수 있다. + +캡슐화의 또 다른 목표는 캡슐화된 데이터와 함수의 특정 측면의 가시성을 제어하는 것이다. 캡슐화의 주요 아이디어는 비슷한 코드를 그룹화하고, 공개하고 싶지 않은 세부사항은 접근을 선택적으로 제한하는 것이다. (private, public 등) + +이런 노력을 기울이다보면 자연스럽게 코드를 체계화할 수 있다. 공개/비공개의 경계, 둘의 연결지점이 어딘지 알게되면 개발이 쉬워진다. 데이터와 함수의 과다 노출을 피할 수 있어 높은 품질의 코드도 유지할 수 있다. + + +## 8.2 모듈이란 + +- 모듈은 관련된 데이터와 함수의 모음이다. 이때 숨겨진 비공개 정보와 공개적으로 접근 가능한 정보가 있는데, 후자를 공개 API라고 한다 +- 모듈도 상태를 유지한다. 일부 정보를 장기간 유지하며 해당 정보에 접근하고 이를 업데이트하는 기능도 제공한다. +- 모듈 패턴의 주요 관심사는 느슨한 결합을 통한 모듈화나 기타 프로그램 아키텍처 기술을 통해 시스템 수준의 모듈화를 완전히 수용하는 것 + +이 절에서는 모듈이 아닌 코드패턴과 모듈 패턴을 비교하여 모듈의 특성을 이해해보는 파트 + +### 네임스페이스 (무상태 그룹화) + +데이터 없이 관련된 함수를 그룹으로 묶는 것은 모듈에서 얘기하는 캡슐화가 아니다. 이런 무상태 함수를 모아둔 것은 네임스페이스라고 한다 + +```js +var utils = { + cancelEvnt() { + ... + }, + wait(ms) { + ... + } +} +``` + +### 데이터구조(상태유지그룹화) + +데이터와 상태를 묶어도 데이터의 가시성을 제한하지 않으면 POLE 관점에서 캡슐화가 아니다. 이런 경우 모듈보다는 데이터 구조의 인스턴스라고 하는 것이 적합하다. + +```js +var student = { + records: [ + {id:123, name:"asd"}, {id:513, name:"basd"} + ], + getName(id) { + return this.records.find(s => s.id === id).name; + } +} +``` + + +### 모듈 (상태를 가진 접근 제어) + +클래식 모듈 (노출식 모듈)로 변경한 모습 + +```js +var student = (function defineStudent(){ + var records = [ + {id:123, name:"asd"}, {id:513, name:"basd"} + ]; + + var publicAPI = { + getName + } + + return publicAPI; + + function getName(id) { + return this.records.find(s => s.id === id).name; + } +})(); +``` + +클래식 모듈 패턴에서는 함수가 프로퍼티인 객체를 반환할 필요가 없다. 함수를 직접 반환하면 된다. 이로써 클래식 모듈의 정의를 충족한다. + +위처럼 IIFE를 사용하는 것은 모듈 인스턴스를 하나만 생성하는 것으로 싱글턴이라 불린다. 함수를 따로 빼면 모듈 팩토리로 불리며 다중 인스턴스를 생성할 수 있다 + + +### 클래식 모듈 정의 + +클래식 모듈이 되기 위한 필요조건 +- 적어도 한 번 이상 실행되는 모듈 팩토리 함수가 외부 스코프에 있어야함 +- 모듈의 내부 스코프에는 해당 모듈의 상태를 나타내는 정보가 하나 이상 있어야하고, 이는 외부에서 접근할 수 없어야한다 +- 모듈은 하나 이상의 함수를 공개 API로 반환해야한다. 이 함수는 내부 스코프의 숨겨진 상태를 클로저를 통해 보존, 관리한다. + +## 8.3 Node.js의 CommonJS 모듈 + +클래식 모듈은 모듈 팩토리 혹은 IIFE를 사용해 정의하고, 다른 코드나 모듈과 함께 하나의 파일에서 묶일 수 있다. +하지만 CommonJS 모듈은 파일 기반이여서 모듈을 만들 때 별도의 파일을 정의해야한다. + +```js +module.exports.getName = getName; + +var records = [ + {id:123, name:"asd"}, {id:513, name:"basd"} +]; + +function getName(id) { + return this.records.find(s => s.id === id).name; +} +``` + +- records 와 getName이 모듈의 최상위 스코프에 있지만 전역 스코프는 아니다. 따라서 여기 있는 코드들은 기본적으로 바깥코드에 비공개이다. +- CommonJS에서는 `module.exports` 를 통해 모듈의 공개 API를 정의한다 + +일부 개발자는 exports 객체를 다음과 같이 바꾸는 습관이 있지만, 이러한 방식은 여러 모듈이 순환적으로 종속되는 경우 예기치 않은 동작이 발생하는 등 몇가지 특이점이 있기에 추천하지 않는다. + +```js +module.exports = { + ... +} +``` + +여러개를 동시에 내보내고 싶으면 다음과 같은 방식이 권장된다. + +```js +Object.assign(module.exports, { + ... +}) +``` + + +이렇게 내보낸 모듈의 인스턴스를 추가하려면 `require()` 메서드를 사용하라 + +```js +var Student = require("/path/to/student.js") + +Student.getName(82); +``` + +CommonJs 모듈은 IIFE 모듈 정의 방식과 유사하게 싱글턴 인스턴스처럼 작동한다. 동일한 모듈을 몇번이나 `require()`로 불러와도 모두 같은 인스턴스에 대한 참조를 얻는다. + +이때 `require()` 함수를 사용하면 지정된 모듈 파일의 전체 공개 API를 불러온다. 일부만 불러오려면 다음과 같이 해야한다. + +```js +var getName = require("/path/to/student.js").getName; + +var { getName } = require("/path/to/student.js"); +``` + +클래식 모듈처럼 CommonJs 모듈 API에서 공개적으로 내보내진 메서드는 내부 모듈 세부 사항에 대한 클로저를 유지한다. 이를 통해 프로그램이 살아있는 동안 모듈 싱글턴의 상태가 유지된다. + + +## 8.4 최신 ES 모듈 + +기본적으로는 CommonJS와 유사하다 +- 파일 기반 +- 모듈 인스턴스는 싱글턴임 +- 모든 것은 기본적으로 비공개. + +차이점은 다음과 같다. +- 파일 상단에 `"use strict"`가 없어도 엄격모드로 실행됨 +- `module.exports`가 아닌 `export` 키워드를 사용해 모듈의 공개 API에 특정 내용을 노출함 +- `require()` 대신 `import` 키워드를 사용한다. + +내보내기를 할 때 `default`를 붙여 모듈을 노출하는 형태를 `기본 내보내기`라고 하며 비교적 간단한 문법으로 import 가능하다. `default`가 붙지 않은 내보내기는 `기명 내보내기`라고 한다. \ No newline at end of file