지금까지는 모든 변수, 함수, 클래스를 main.js 파일 하나에 다 적었습니다. 프로그램이 커지면 메뉴 데이터와 계산 함수는 cafe.js에, 주문을 실행하는 코드는 main.js에 나누어 담는 편이 읽기와 고치기에 훨씬 좋습니다. 자바스크립트에서는 다른 파일에서도 쓸 이름 앞에 export를 붙여 내보내고, 쓸 파일에서 import로 가져옵니다.
먼저 한 파일 안에 모든 코드를 몰아넣었을 때와 두 파일로 나눴을 때가 어떻게 달라지는지 봅니다.
export로 내보내고 import로 가져오기
1단원 내 컴퓨터에서 돌려 보기에서 작업 폴더에 만든 package.json 파일을 다시 확인합니다.
{
"type": "module"
}
폴더의 package.json에 "type": "module"이 들어 있으면 각 .js 파일이 독립된 모듈이 됩니다. 한 파일 안에서 만든 변수나 함수는 기본적으로 그 파일 안에서만 보이며, 앞에 export를 붙인 이름만 밖으로 열립니다.
먼저 cafe.js 파일에 가격 상수와 계산 함수를 만들고 앞에 export를 붙입니다.
// cafe.js
export const latte = 4500;
export function total(qty) {
return latte * qty;
}
이제 같은 폴더의 main.js에서 중괄호 { } 안에 가져올 이름을 적고, from './cafe.js'로 파일 경로를 적어 가져옵니다.
// main.js (같은 폴더 …)
import {
latte,
total,
} from './cafe.js';
console.log('단가:', latte);
console.log('합계:', total(2));
단가: 4500
합계: 9000
- 내보내기(
export):export const,export function,export class처럼 선언 앞에export를 붙입니다.export를 붙이지 않은 이름은cafe.js안에서만 쓸 수 있습니다. - 가져오기(
import):import { 이름 }뒤에from './파일.js'를 적습니다. 중괄호 안의 이름은 내보낸 이름과 글자까지 같아야 합니다. - 경로와 확장자: 같은 폴더에 있는 내 파일은 반드시 현재 폴더를 뜻하는
./로 시작하고, 끝에.js확장자까지 다 적습니다('./cafe.js').
가져온 파일이 먼저 한 번 실행된다
node main.js로 실행했을 때 cafe.js와 main.js 가운데 어느 파일의 코드가 먼저 돌까요? 아래 그림을 한 단계씩 넘기며 실행 차례를 확인해 보세요.
main.js의 맨 위나 중간에 console.log가 있더라도, Node는 import로 연결된 ./cafe.js를 먼저 끝까지 한 번 실행한 뒤에야 main.js의 본문을 실행합니다.
// cafe.js
console.log('1. cafe 준비');
export const tax = 400;
이어서 main.js에서 cafe.js를 가져옵니다.
// main.js (cafe.js 가져옴 …)
console.log('2. main 시작');
import {
tax,
} from './cafe.js';
console.log('3. 세금:', tax);
1. cafe 준비
2. main 시작
3. 세금: 400
main.js의 2번 줄에 console.log('2. main 시작')을 import보다 위에 적더라도, 실행하기 전에 import가 가리키는 cafe.js가 먼저 돌아 1. cafe 준비가 첫 줄에 찍힙니다. 따라서 import 문장은 늘 파일 맨 위에 모아서 적습니다.
또한 같은 ./cafe.js를 여러 번 가져오더라도 cafe.js는 처음 가져올 때 딱 한 번만 실행되고, 그 뒤로는 이미 만들어진 값을 함께 씁니다.
자주 틀리는 곳
1. 경로 끝에 .js 확장자나 ./를 빼먹음
파이썬(from cafe import tax)이나 번들러 환경과 달리, Node에서 직접 실행할 때는 ./와 .js 확장자를 모두 적어야 합니다. from './cafe'처럼 .js를 빼먹으면 파일을 찾지 못해 ERR_MODULE_NOT_FOUND 오류가 납니다.
import {
tax,
} from './cafe';
Error [ERR_MODULE_NOT_FOUND]: Cannot find module 'cafe' imported from main.js { code: 'ERR_MODULE_NOT_FOUND', url: 'cafe'}2. export하지 않은 이름을 가져오려 함
cafe.js에 변수 secret이 있더라도 앞에 export를 붙이지 않았다면 다른 파일에서는 가져올 수 없습니다. 내보내지 않은 이름이나 오타가 난 이름을 import { secret }로 가져오려 하면 실행을 시작할 때 SyntaxError(내보낸 이름 없음)로 멈춥니다.
3. package.json에 "type": "module"이 빠짐
새 폴더를 만들고 package.json에 "type": "module"을 넣지 않으면, Node가 옛 모듈 방식(CommonJS)으로 파일을 읽어 import나 export 줄에서 모듈 밖이라는 SyntaxError 오류를 냅니다. 새 작업 폴더를 만들 때는 package.json을 먼저 확인하세요.
요약
export: 변수(const), 함수(function), 클래스(class) 선언 앞에 붙여 다른 파일에서 가져다 쓸 수 있게 내보낸다.import { 이름 } from './파일.js': 다른 파일이 내보낸 이름을 가져온다. Node에서는 상대 경로./와 파일 확장자.js까지 빠짐없이 적는다.- 실행 차례:
node main.js를 실행하면import로 연결된 파일(cafe.js)이 먼저 딱 한 번 실행된 뒤main.js본문이 실행된다. - 작업 폴더의
package.json에"type": "module"이 들어 있어야 표준import와export가 동작한다.
연습 문제
문제 1
같은 폴더에 menu.js와 main.js가 있습니다. 터미널에서 node main.js를 실행했을 때 출력되는 세 줄을 순서대로 쓰세요.
// menu.js
console.log('메뉴판 열기');
export const tea = 4000;
아래는 같은 폴더의 main.js입니다.
// main.js (menu.js 가져옴 …)
import {
tea,
} from './menu.js';
console.log('주문 시작');
console.log(tea + 500);
힌트
import로 가져온 menu.js가 먼저 한 번 실행된 뒤에 main.js의 본문이 위에서 아래로 실행됩니다.
정답
메뉴판 열기
주문 시작
4500
node main.js를 돌리면import가 가리키는menu.js가 먼저 실행되어메뉴판 열기를 찍고tea에4000을 붙여 내보냅니다.- 이어서
main.js가주문 시작과4000 + 500인4500을 차례로 찍습니다.
문제 2
같은 폴더의 calc.js에서 내보낸 add 함수를 가져오려는데 아래처럼 ERR_MODULE_NOT_FOUND 오류가 났습니다. main.js의 3번 줄을 어떻게 고쳐야 할까요?
import {
add,
} from './calc';
Error [ERR_MODULE_NOT_FOUND]: Cannot find module 'calc' imported from main.js { code: 'ERR_MODULE_NOT_FOUND', url: 'calc'}힌트
Node에서 같은 폴더의 파일을 가져올 때는 파일 확장자까지 모두 적어야 합니다.
정답
- 고칠 곳:
'./calc'를'./calc.js'로 고칩니다.
// main.js (calc.js 가져옴 …)
import {
add,
} from './calc.js';- 경로 끝에
.js확장자를 빠뜨리면 Node가 파일을 찾지 못해ERR_MODULE_NOT_FOUND오류를 냅니다.
문제 3
point.js 파일에서 기본 적립률 상수 rate = 0.1과 적립금 계산 함수 point(p)(p * rate를 돌려줌) 두 가지를 밖으로 내보내고, main.js에서 두 이름을 모두 가져와 0.1과 450을 찍도록 두 파일의 빈칸을 완성하세요.
0.1
450
// point.js — 내보내기 …
const rate = 0.1;
function point(p) {
return p * rate;
}
힌트
point.js에서는 const와 function 앞에 export를 붙이고, main.js에서는 import { rate, point }와 from './point.js'로 가져옵니다.
정답
먼저 point.js에서 두 선언 앞에 export를 붙입니다.
// point.js
export const rate = 0.1;
export function point(p) {
return p * rate;
}이어서 main.js에서 두 이름을 가져와 실행합니다.
// main.js (point.js 가져옴 …)
import {
rate,
point,
} from './point.js';
console.log(rate);
console.log(point(4500));0.1
450