😎 자바스크립트에는 모듈이 있습니다.
그리고 모듈은 마치 아무것도 모르는 나도 개고수가 된 것 같은 기분을 느끼게 해주죠!
리액트의 hooks를 불러올때도 모듈 문법을 사용하고
리액트에서 완성한 컴포넌트를 내보내기해서 상위 컴포넌트에서 사용하기도하고..
또 애초에 리액트를 import해오기도하고...
express() 서버를 사용하기도하고..
모듈없으면 코드 어케 짜냐?
😎 허접인 나도 로컬환경에선 모듈메이커?
모듈을 만들고 내보내는 방법은 꽤나 다양하다.
하지만 방법이 다양한거지 쓰는게 어렵다곤 하지 않았다.
정말 간단한 문법 하나만으로도 내가 원하는 코드를 export할 수 있고
또 그 코드를 import해올 수 있다. 이번엔 그 방법에 대해서 다뤄보고자 합니다.
😎그전에 모듈은 뭘까요
모던 자바스크립트 딥다이브의 설명에 따르면
모듈이란 애플리케이션을 구성하는 개별적 요소로서 재사용 가능한 코드 조각을 말합니다.
일반적으로 기능을 기준으로 파일 단위로 분리하는데
모듈이 성립하려면 모듈은 자신만의 파일 스코프(모듈 스코프)를 가질 수 있어야합니다.
모듈의 자산은 기본적으로 캡슐화 되어 개별적인 존재로 있지만
모듈을 모듈 밖에서 사용 하기 위해서는 모듈의 자산을 내보내줘야하겠죠?
그것을 export라고 합니다.
내보내진게 있어야 그걸 import해와서 사용할 수 있을 것입니다.
따라서 export를 먼저 살펴보는게 맞겠군요
😎그전에 자바스크립트의 모듈은?
근데 자바스크립트는 기본적으로 웹에서 간단하게 사용하기 위한 목적으로 개발된 스크립트 언어이고
파일 스코프를 지원하지 않았으며 모든 자바스크립트 파일은 하나의 전역을 공유했습니다.
이런 상황에서 모듈을 꾸역꾸역 사용하기위해서는 새로운 시스템이 필요하였고
그래서 제안된것이 CommonJS라고합니다.
이에 관련하여 관심있으신 분들은 해당 키워드로 더 검색을 해보시면 되겠네요
Node.js역시 모듈 시스템으로 CommonJS를 채택했다고 합니다.
또한 Node.js 환경에서는 파일별로 독립적인 파일 스코프를 가집니다
<script type="module" src="app.mjs"> </script>
게다가 ES6부터는 IE를 제외한 대부분의 브라우저에서 ES6 모듈을 사용할 수 있어요!
바로 위와 같은 방식으로요!
눈썰미가 좋으신 분들은 눈치채셨겠지만 mjs 확장자가 뭐지..? 싶습니다.
mjs 확장자는 일반적인 자바스크립트 파일이 아닌 ES6 모듈임을 명확히하기위해 지어주는 확장자입니다.
스크립트 태그에서 모듈로 동작하기 위해서는 type="module"이 핵심적인 역할을 하지만
무엇이 모듈인지 명확히 해주기 위한 사회적인 약속이니 mjs확장자도 사용해주도록 합시다.
반면 ES6모듈(ESM)이 아닌 자바스크립트 파일은
스크립트태그로 분리해서 로드해도 하나의 전역을 공유합니다.
따라서 ESM은 파일 자체의 모듈 스코프를 제공하기 때문에
ESM 파일에서 var 키워드로 선언한 변수 역시도
전역객체의 프로퍼티가 되지 않습니다.
// foo.mjs var x = 'foo' console.log(x) // foo console.log(window.x) // undefined
😎export 키워드
이제 드디어 export를 하는 방법에 대해서 이야기 해보겠습니다.
모듈이 모듈로서의 의미를 갖기 위해서는 사용되어져야 합니다.
따라서 모듈 내부에서 선언한 식별자를 외부에 공개하여 다른 모듈들이 재사용할 수 있게
공개해주는 작업이 필요할 것이고.
또 그 과정에서 어떤 것을 공개하고 어떤 것을 공개하지 않을 것인지에 대한 생각도 필요할 것입니다.
export const variable = '변수의 내보내기'
export function varifunc() {
return 1 + 1
}
--------------------------------
let a = 'hi'
let b = 'no'
let c = 'yes'
export {a, b, c}
-------------------------------
export default x => x * x
다음은 모듈에서 export를 수행하는 세가지 방법을 표현한 코드입니다.
첫번째로 내보내기하고 싶은 변수,클래스,함수 등등의 앞에 export 키워드를 붙여주는 것입니다.
두번째는 객체 리터럴 {}로 묶어 한번에 내보낼 것들을 정해서 내보내는 방법입니다.
세번째는 export하는 값이 단 하나라면 사용 가능한 default 키워드를 이용한 방법입니다.
default 키워드에 대해서는 꽤 특이한 특징들이 있으니 아래에서 더 자세히 다뤄보겠습니다.
exports.식별자 = 식별자
그 이전에 이런식으로 exports 하는 것도 가능합니다.
😎 default 키워드는 왜 다를까요?
default 키워드의 특징은 다음과 같습니다.
export default const foo = () => {}; // -> 문법오류
default 키워드와 함께 지정한 모듈은 let,var ,const 키워드를 사용할 수 없습니다.
만약 저 코드를 오류없이 성립시키려면 다음과 같이 작성해야합니다.export default () => {}
또한 default 키워드를 이용해 export한 모듈은
import할 때에 {} 없이 임의의 이름으로 import할 수 있습니다.
이부분에 대해서는 import를 다루면서 다시 보도록 하겠습니다.import square from './lib.mjs'
😎 import 키워드
다른 모듈에서 export 키워드를 통해 공개한 식별자들은
import 키워드를 이용해 자신의 모듈 스코프로 로드할 수 있습니다.
import의 기본적인 문법은 다음과 같습니다.
import { } from '파일경로' //중괄호 안에 해당 파일 경로에서 받아올 식별자의 이름을 넣습니다.
이때 가장 중요한 부분인데
다른 모듈이 export한 식별자 이름으로 import를 해야한다는 규칙이 있어요!
뭔 소리냐구요?
// mood.mjs export const hiMyname = "xion" // 임포트할 파일 import { hiMyname } from "./mood.mjs"
이렇게 export한 식별자 이름을 그대로 import {} 중괄호안에 넣어주셔야합니다.
만약 이 규칙이 없다면 어떨까요?// mood.mjs export const hiMyname = "xion" export const dummy = "전더미에요" export const garbage = "전쓰레기에요" // 임포트할 파일 import { amb } from "./mood.mjs"
이렇게 여러개의 식별자를 내보내기한 상황에서
import를 해오는 방법은 저것뿐만이 아닙니다. 다르게도 import해올 수 있어요
어떤것을 받아올지 예측하실 수 있으신가요?
안타깝지만.. 전 예측할수가 없군요
따라서 받아올 식별자의 이름을 정확히 표기해주어야 해당 식별자를 잘 import해올 수 있겠죠?
import * as lib from './lib.mjs' console.log(lib.pi) import {pi as PI} from './lib.mjs' console.log(PI)
* as (원하는 네이밍) 을 해주면 해당 링크에서 export한 모든 식별자를
객체의 프로퍼티 형태로 받아옵니다.
즉 위 코드는 lib 객체안에 프로퍼티의 형태로 식별자들을 받아오기때문에
객체의 키를 사용하는 문법중 하나인 점표기법을 통해
import해온 식별자를 사용할 수 있는거죠!
반면 pi as PI 부분을 보겠습니다.
이는 import해온 pi라는 식별자를 PI로 바꿔서 쓰겠다는 의미를 내포합니다.
식별자의 이름을 변경해서 사용하고 싶을 때는 이렇게 사용하면 되겠군요!
이제 default 키워드를 붙여서 export한 식별자를 받아오는 방법을 다시 보겠습니다.
export default x => x*x ---------------------------------- import square from './lib.mjs'
default 키워드를 붙여 export한 식별자는
다음과같이 {} 를 붙이지 않고 import해올 수 있고
받아오는 식별자의 이름도 as 키워드 없이 마음대로 설정할 수 있습니다.
😎require() 함수는 뭔가요?
해당 링크를 참고하면 require() 함수에 대한 동작을 이해할 수 있습니다.
간혹 모듈을 불러오는 코드를 보면
require() 메서드를 사용하는 경우를 왕왕 볼 수 있습니다.
const 모듈명 = require('모듈경로');
require() 메서드의 사용법은 다음과 같아요
모듈명은 다른 모듈에서 export한 변수, 함수, 클래스 등의 이름을 가리키며,
모듈경로는 불러올 모듈의 파일 경로를 나타냅니다.
require() 메서드는 해당 경로에 있는 export 된 식별자를 객체형태로 가져와서 반환합니다.
앞서 살펴본 * as obj 가 생각나네요
실제 용례를 볼까요?
예컨대 다음코드는 express를 import 해온 뒤 루트 url의 요청에 대하여
"hello World"로 응답해주는 코드입니다.
const express = require('express') const app = express() const port = 3000 app.get('/', (req, res) => { res.send('Hello World!') })
코드의 의미를 해석할 필요는 없으니
const express = require('express')에 집중해 보겠습니다.
require 메서드는 Node.js 환경에서 제공해주는 메서드로
동작원리에 대한 설명은 위에 첨부한 해당 링크에서 참고할 수 있습니다.
꽤 복잡하고 긴 소스코드를 갖고있지만... 간단히 기능만을 이야기하면
인자로 넣어준 값을 가지고 어찌저찌 탐색을 해서
해당 인자에 매치되는 경로의 파일이 export한 식별자들을 객체형태로 받아온다.
라고 인식할 수 있습니다.
또한 객체형태로 값을 반환해주기 때문에 객체 디스트럭처링 할당을 통해
필요한 것들만 뽑아 사용하는 것도 가능해요
const { 항목1, 항목2, ... } = require('모듈경로');
이런식으로 말이죠!
엇..그런데 뭔가 이상하지 않나요?
😎require() 메서드는 객체를 반환한다며??
실제로 express를 require해온뒤 반환받은 것을 console.log()로 찍어보면
다음과 같은 내용이 출력됩니다.
이래서 그렇다고 합니다.
아직 잘 이해는 되지 않습니다만
뭐.. 뭔가 해놨겠죠...
아무튼 객체로 준다고함
😎 이제 실제로 모듈을 사용해보자
라고 싱글벙글하면서 .mjs 파일에 코드를 잘 작성하고
export까지 마친 뒤 싱글벙글 import를 해오려고하면...
(node:8036) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension. (Use `node --trace-warnings ...` to show where the warning was created) c:\Users경로\OneDrive\바탕 화면\module\hi.js:1 import { pi , um } from './add.mjs' ^^^^^^ SyntaxError: Cannot use import statement outside a module at internalCompileFunction (node:internal/vm:73:18) at wrapSafe (node:internal/modules/cjs/loader:1166:20) at Module._compile (node:internal/modules/cjs/loader:1210:27) at Module._extensions..js (node:internal/modules/cjs/loader:1300:10) at Module.load (node:internal/modules/cjs/loader:1103:32) at Module._load (node:internal/modules/cjs/loader:942:12) at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:83:12) at node:internal/main/run_main_module:23:47 Node.js v19.5.0
아마 이런 오류를 마주하게 됩니다.
type : "module"을 package.json에 set해줘라 그게 싫으면 .mjs 익스텐션을 사용해라
라는 오류군요
전 package.json에 시키는대로 타입을 세팅해주는 방법을 사용해보겠습니다.
{ "name": "module", "version": "1.0.0", "description": "", "main": "hi.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC" }
제 package.json의 상태는 다음과 같았습니다.
type에 대한 키가 없어서 오류가 난것이군요!{ "name": "module", "version": "1.0.0", "description": "", "type": "module", "main": "hi.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC" }
다음과 같이 type : "module"을 추가해줬습니다.
const pi = 3.14 const um = "준식" export {pi, um} ; -------------------------------------- import { um } from './add.mjs' console.log(um)
이제 다시 import를 하고 잘 동작하는지 확인하기 위해
console.log() 메서드를 이용해 um 변수를 출력해봤습니다.[Running] node "c:\Users\경로\OneDrive\바탕 화면\module\hi.js" 준식
아주 잘 실행되는군요!
이제 자바스크립트에서 아주 쉽고 간편하게
모듈을 만들고 내보내고 직접 사용하는 것 까지 해봤습니다.
수고 많으셨어요
'javascript' 카테고리의 다른 글
프로토타입을 케이크처럼 쉽게 먹는 방법 (3) | 2023.02.28 |
---|---|
v8 엔진으로의 딥다이브 (0) | 2023.02.27 |
DOM을 다루는 실전적인 방법 (0) | 2023.02.14 |
두 배열이 동등한지 비교하는 방법 (0) | 2023.01.30 |
요약 정리는 못 참지 않을까요? (20) 모듈 (1) | 2023.01.28 |