본문으로 건너뛰기

파일 나누기: import와 export

자바스크립트 목차오류와 모듈레슨 57 / 76

목차

지금까지는 모든 변수, 함수, 클래스를 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 가운데 어느 파일의 코드가 먼저 돌까요? 아래 그림을 한 단계씩 넘기며 실행 차례를 확인해 보세요.

4단계 중 1단계. `menu.js`에서 `export`를 붙인 `menu`와 `priceWithTax` 두 개만 파일 밖으로 열린 창구에 걸립니다. `export`를 붙이지 않은 2번 줄의 `TAX`는 `menu.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

끝까지 내려오면 저절로 표시됩니다. 이 기기에만 저장합니다.

자바스크립트 목차로