핵심 정리
// user.ts
export interface User {
id: string;
name: string;
}
export type UserId = User["id"];
export function formatUser(user: User) {
return `${user.id}: ${user.name}`;
}
// app.ts
import { formatUser, type User } from "./user";공개 경계
먼저 자주 보는 import/export 형태를 한 번에 보면 아래 여섯 가지입니다.
export const name = ...export function fn() {}export type T = ...export default ...import { x, type T } from "./mod"import * as mod from "./mod"export { x } from "./mod"
1) 모듈은 파일 단위 공개 API를 만든다
ESM 기반 TypeScript에서는 파일 하나가 모듈 경계입니다. 즉 파일을 나눈다는 것은 단순 정리가 아니라, 무엇을 밖으로 공개할지와 무엇을 내부 구현으로 남길지를 정하는 일입니다.
export function sum(a: number, b: number) {
return a + b;
}2) named export가 기본 선택인 경우가 많다
여러 항목을 함께 내보내고, import하는 쪽에서 이름을 명확히 읽게 하려면 named export가 더 안정적인 경우가 많습니다.
export function parse() {}
export const version = "1.0.0";default export도 가능하지만, 프로젝트 전체에서 이름 일관성이 흔들리기 쉬워 팀 규칙이 없으면 named export가 무난합니다.
import { parse, version } from "./parser";3) 타입만 필요하면 import type
값이 아니라 타입 정보만 가져오는 경우에는 import type이 의도를 더 분명히 보여 줍니다.
import type { User } from "./user";이 패턴은 코드 독자에게 "런타임 의존성인지, 타입 계약인지"를 바로 구분하게 해 줍니다.
import { formatUser, type User } from "./user";4) default export와 namespace import는 용도가 더 좁다
대표 항목 하나를 파일 기본값처럼 내보내면 export default가 맞을 수 있습니다.
반대로 모듈 전체를 하나의 이름공간처럼 읽고 싶으면 namespace import가 맞습니다.
export default function createUser() {}
import createUser from "./create-user";
import * as math from "./math";다만 이름 일관성과 공개 표면 가시성 때문에, 팀 규칙이 약한 코드베이스에서는 named export를 기본값으로 두는 편이 읽기 쉽습니다.
5) 재export는 공개 표면을 한곳에 모을 때 쓴다
여러 파일에 흩어진 공개 항목을 한 진입점으로 모으고 싶다면 re-export 패턴을 씁니다.
export { Button } from "./Button";
export type { ButtonProps } from "./Button";barrel 파일은 편리하지만, 너무 무겁게 키우면 경계가 다시 흐려질 수 있으니 공개 API를 모으는 용도로만 쓰는 편이 좋습니다.
6) 파일 경계가 거칠수록 의존성이 더 깔끔해진다
모듈 시스템은 함수들을 여기저기 흩뿌리는 대신, 파일 단위로 공개 API를 좁히게 만듭니다. 그래서 TypeScript 프로젝트를 읽을 때는 함수 본문보다 먼저 어떤 파일이 무엇을 export하는지 보는 편이 빠릅니다.
7) export 문법보다 경계 설계가 더 중요하다
같은 기능을 여러 파일에서 중복 export하거나, 내부 helper까지 전부 밖으로 내보내기 시작하면 모듈 경계가 흐려집니다. 즉 핵심은 문법 선택보다 "이 파일의 공개 표면이 얼마나 작고 분명한가"입니다.
언제 무엇을 export할까
체크포인트
- 파일 단위 공개 API를 설계한다: 모듈
- 여러 공개 항목을 제공한다: named export 우선
- 타입 이름만 공개한다:
export type - 타입만 가져온다:
import type - 모듈 전체를 이름공간처럼 읽는다: namespace import
- 공개 표면을 한곳에 모은다: re-export
- 내부 helper까지 다 export하고 있다: 경계 다시 점검
- default export를 쓴다: 팀 규칙과 일관성 확인
주의할 점
값 import와 타입 import를 구분하지 않으면 코드 의도와 의존성 경계를 읽기 어려워질 수 있습니다.
// ✅ 타입만 필요할 때는 type import
import type { User } from "./user";
// ✅ 런타임 값이 필요할 때는 일반 import
import { formatUser } from "./user";
// ❌ 타입만 필요해도 일반 import를 섞어 의도가 흐림
import { User } from "./user";참고 링크
1 sources