Skip to main content
입문

모듈 해석: import 문은 어떤 파일을 가리키는가

import 문자열, 가져오는 파일의 위치, 패키지 공개 범위와 환경 조건을 분리해 보고 모듈을 찾지 못했을 때 확인할 순서를 익힌다.

마지막 검증 재검증 정책: 제품 버전 의존: 새 주요 버전마다 재검증
목차
표준·구현·측정·해석 표시는 무엇인가요?
  • 표준웹 표준이나 언어 명세가 정한 동작
  • 구현특정 기술이나 브라우저가 실제로 구현한 동작
  • 측정명시한 환경에서 직접 실행해 관찰한 결과
  • 해석앞선 근거에서 도출한 설계 판단
  • 미확인아직 공식 근거나 재현 결과를 확인하지 못한 내용

30초 요약

이 글에서 기억할 것은 네 문장이다.

  1. import의 문자열은 실제 파일 주소가 아니라 풀어야 할 요청이다.
  2. ./로 시작하는 요청은 그 요청을 적은 파일의 위치에서 출발한다.
  3. 패키지 이름은 그 패키지가 공개한 대상과 현재 환경을 확인한다.
  4. 오류가 나면 문자열, 가져오는 파일, 설정, 환경을 차례로 고정한다.

축소해서 쓰면 다음과 같다.

선택된 모듈 = 해석 규칙(지정자, 가져오는 모듈, 설정, 활성 조건)

이 식은 특정 도구의 실제 함수 모양이 아니다. 문자열 하나만 보고 대상을 단정하지 않기 위한 사고 도구다.

지정자는 답이 아니라 해석할 요청이다

import theme from "./theme.js"
import Button from "@scope/ui/button"

첫 줄의 "./theme.js"와 둘째 줄의 "@scope/ui/button"이 모듈 지정자다. 첫 번째는 현재 파일과의 위치 관계를 나타내는 상대 지정자, 두 번째는 패키지 이름으로 시작하는 요청이다.

여기서 호스트(host)는 자바스크립트를 실행하거나 모듈을 준비하는 환경을 뜻한다. 브라우저와 Node.js가 대표적이고, 애플리케이션을 만들 때는 Webpack·Vite·Metro 같은 빌드 도구도 자체 해석 규칙을 적용한다.

그래서 같은 문자열이 보여도 먼저 “어느 파일이, 어떤 환경에서 요청했는가”를 물어야 한다.

상대 지정자는 가져오는 파일에서 출발한다

다음 두 파일이 똑같이 "./theme.js"를 가져온다고 하자.

src/
├─ header/
│  ├─ index.js     import "./theme.js"
│  └─ theme.js
└─ profile/
   ├─ index.js     import "./theme.js"
   └─ theme.js

프로젝트 루트나 현재 터미널 폴더가 기준이라고 외우면 파일을 옮겼을 때 이유를 놓치기 쉽다. 기준은 요청을 작성한 모듈이다.

현재 Node.js의 ECMAScript 모듈에서는 "./theme.js"처럼 상대 지정자에 파일 확장자를 적어야 한다. 그러나 이것을 자바스크립트 전체의 규칙으로 확대하면 안 된다. 뒤에서 보듯 빌드 도구는 설정된 확장자를 순서대로 시도할 수 있다.

패키지 이름은 공개된 대상을 찾는다

"@scope/ui""@scope/ui/button"은 현재 파일 옆의 경로가 아니다. 해석기는 먼저 해당 패키지를 찾고, 그 패키지가 공개한 대상을 확인한다.

{
  "name": "@scope/ui",
  "exports": {
    ".": "./dist/index.js",
    "./button": "./dist/button.js"
  }
}

이 설정에서 "@scope/ui/button"dist/button.js로 연결된다. 반면 dist/private.js가 실제로 존재하더라도 exports에 공개되지 않았다면 패키지의 공식 하위 경로가 아니다.

즉, “패키지가 설치되어 있는가”와 “그 경로를 공개했는가”는 별도 질문이다.

조건이 달라지면 같은 패키지도 다른 파일을 고른다

패키지는 같은 공개 이름에 여러 후보를 둘 수 있다.

{
  "exports": {
    ".": {
      "browser": "./dist/browser.js",
      "default": "./dist/index.js"
    }
  }
}

여기서 browserdefault는 조건 이름이다. 해석기가 browser 조건을 활성화하면 첫 파일을, 그렇지 않으면 기본 후보를 선택할 수 있다. 어떤 조건을 활성화하는지는 실행 환경과 도구 설정에 달려 있다. exports 객체에서는 위에서부터 확인해 처음 일치한 키가 우선하므로, 넓게 일치하는 default는 마지막에 두어야 한다.

판단할 때는 조건 이름을 추측하지 않고, 실제 해석기를 기준으로 활성 조건과 선택 결과를 확인한다.

React Native에서는 플랫폼도 해석 입력이다

Button.ios.js
Button.android.js
 
import Button from "./Button"

따라서 React 코드에서 같은 import Button from "./Button"을 보더라도 iOS 빌드와 Android 빌드가 같은 파일을 사용한다고 단정할 수 없다. 플랫폼별 파일을 추가하거나 삭제한 뒤 예상과 다른 코드가 실행된다면 컴포넌트 상태보다 해석 결과를 먼저 확인한다.

도구의 편의 규칙을 보편 규칙으로 외우지 않는다

예를 들어 import "./Button"이 한 프로젝트에서는 성공하고 다른 프로젝트에서는 실패해도 모순이 아니다. 서로 다른 해석기가 서로 다른 후보 목록과 설정을 사용했을 수 있다.

바뀐 입력달라질 수 있는 결과
가져오는 파일의 위치상대 지정자의 기준 경로
별칭과 확장자 후보 순서먼저 선택되는 파일
패키지 exports접근 가능한 하위 경로와 선택 대상
import·require, 개발·운영 조건선택되는 조건부 대상
iOS·Android 같은 플랫폼Metro가 고르는 플랫폼별 파일

찾을 수 없음은 해석의 입력을 고정해서 좁힌다

다음 순서로 확인한다.

  1. 오류에 나온 지정자 전체와 그것을 가져온 파일 경로를 기록한다.
  2. ./../로 시작하는지, 패키지 이름으로 시작하는지 분류한다.
  3. 상대 지정자라면 가져오는 파일 기준의 경로와 확장자 후보를 확인한다.
  4. 패키지 지정자라면 설치 여부와 exports에 공개된 경로인지 분리해 확인한다.
  5. 별칭, 확장자 순서, 활성 조건과 대상 플랫폼을 확인한다.
  6. 실행 중인 도구가 실제로 선택한 절대 경로나 진단 출력을 확인한다.
오류가 생긴 지점먼저 볼 근거
파일을 옮긴 뒤 상대 import 실패가져오는 파일 기준의 계산 결과
패키지 내부 파일은 있는데 접근 실패exports에 공개된 하위 경로
웹과 Node.js 결과가 다름각 해석기의 조건과 별칭 설정
iOS와 Android에서 다른 코드 실행Metro 플랫폼과 확장자 선택 순서

이 순서가 끝나기 전 무작정 패키지를 다시 설치하거나 캐시를 지우면 잠시 증상이 사라져도 어떤 입력이 잘못되었는지 남지 않는다.

이 글의 경계

이번 글은 지정자 하나가 어느 모듈로 연결되는지에 집중했다. 다음 내용은 분리한다.

  • 여러 모듈 요청의 관계를 반복해서 따라가는 과정: 앞 글의 주제
  • TypeScript나 JSX를 실행 대상 코드로 바꾸는 과정: 변환
  • 여러 모듈을 출력 파일로 묶는 과정: 번들링
  • 패키지와 버전을 내려받아 배치하는 과정: 패키지 설치
  • 찾은 모듈을 연결하고 어떤 순서로 실행하는가: 모듈 평가

다음 글에서는 해석된 모듈들이 TypeScript·JSX 변환과 번들링을 거쳐 실행 가능한 산출물이 되는 과정을 살펴본다.

Active recall

기억에서 꺼내 보기

답을 쓰지 않아도 됩니다. 먼저 머릿속으로 답한 뒤 펼쳐서 정답·이유·흔한 오해를 비교하세요.

  1. 01같은 import 문자열이면 언제나 같은 파일을 가리킬까?

    정답

    아니다. 지정자뿐 아니라 가져오는 모듈의 위치, 해석 도구의 설정과 활성 조건도 대상 선택에 관여할 수 있다.

    왜 그런가

    상대 지정자는 가져오는 파일을 기준으로 계산하고, 패키지 이름은 공개된 하위 경로와 조건을 확인할 수 있다.

    관련 설명 다시 읽기
    지금 어느 정도 기억났나요?

    선택한 상태와 다음 복습일은 이 브라우저에만 저장됩니다.

  2. 02서로 다른 폴더의 두 index.js가 모두 ./theme.js를 가져오면 같은 파일일까?

    정답

    각 index.js의 위치를 기준으로 계산하므로 서로 다른 theme.js를 가리킬 수 있다.

    왜 그런가

    상대 지정자의 기준점은 프로젝트 루트가 아니라 가져오는 모듈의 위치다.

    관련 설명 다시 읽기
    지금 어느 정도 기억났나요?

    선택한 상태와 다음 복습일은 이 브라우저에만 저장됩니다.

  3. 03패키지 폴더 안에 파일이 존재하면 패키지 이름 뒤에 경로를 붙여 언제나 가져올 수 있을까?

    정답

    아니다. package.json에 exports가 있으면 그 패키지가 공개한 하위 경로만 허용될 수 있다.

    왜 그런가

    파일의 물리적 존재와 패키지가 공개한 하위 경로는 다른 문제다.

    관련 설명 다시 읽기
    지금 어느 정도 기억났나요?

    선택한 상태와 다음 복습일은 이 브라우저에만 저장됩니다.

  4. 04모듈을 찾을 수 없다는 오류가 나면 캐시 삭제 전에 무엇을 기록할까?

    정답

    실패한 지정자, 그 지정자를 가져온 파일, 사용한 도구와 설정, 활성 플랫폼·조건을 먼저 기록한다.

    왜 그런가

    해석의 입력을 고정해야 어느 단계에서 예상과 달라졌는지 재현할 수 있다.

    관련 설명 다시 읽기
    지금 어느 정도 기억났나요?

    선택한 상태와 다음 복습일은 이 브라우저에만 저장됩니다.

출처와 검증 범위

아래 날짜는 링크를 마지막으로 열어 본 날입니다. 문서 상단의 검증일은 글의 설명과 적용 범위를 다시 확인한 날입니다.

  1. ECMAScript Language Specification — HostLoadImportedModuleEcma International · 표준 명세 · 확인 2026-08-04
  2. ECMAScript modules — import SpecifiersNode.js · 공식 문서 · 확인 2026-08-04
  3. Modules Packages — Package entry pointsNode.js · 공식 문서 · 확인 2026-08-04
  4. Modules Packages — Conditional exportsNode.js · 공식 문서 · 확인 2026-08-04
  5. Resolvewebpack · 공식 문서 · 확인 2026-08-04
  6. Shared Options — resolve.conditionsVite · 공식 문서 · 확인 2026-08-04
  7. Shared Options — resolve.aliasVite · 공식 문서 · 확인 2026-08-04
  8. Shared Options — resolve.extensionsVite · 공식 문서 · 확인 2026-08-04
  9. Module ResolutionMetro · 공식 문서 · 확인 2026-08-04
  10. Package Exports SupportMetro · 공식 문서 · 확인 2026-08-04
  11. Platform-Specific CodeReact Native · 공식 문서 · 확인 2026-08-04
이 문서의 마지막까지 읽었습니다.