배럴 파일(Barrel File)은 여러 모듈의 export를 index.ts 하나에 모아 다시 내보내(re-export) 진입점을 하나로 만드는 파일이다. 쓰는 쪽은 import { Button, Input } from './components'처럼 한 경로에서 가져온다.
export { Button } from './Button' // 이름 그대로
export { Input as TextInput } from './Input' // 이름 바꿔서
export * from './layout' // 전부(단, default는 빠짐)
export * as utils from './utils' // 네임스페이스로 묶어서
export { default as Modal } from './Modal' // default를 이름 붙여서- 장점: import 경로가 짧아지고, 내부 파일 위치가 바뀌어도 배럴만 고치면 된다. 공개할 API(Application Programming Interface)의 경계 역할도 한다
- 주의:
export *는 default export를 옮기지 않는다 - 번들 크기: 배럴을 import하면 번들러가 그 아래 모듈들을 평가해야 할 수 있다. 부수 효과가 있는 모듈이 섞여 있으면 트리 셰이킹이 깨져 안 쓰는 코드까지 딸려 온다. 개발 서버에서는 불러오는 모듈 수가 늘어 느려지기도 한다
- 순환 참조(Circular Dependency): 배럴을 거쳐 서로를 import하다 보면 순환이 생기기 쉽다. 패키지 안쪽에서는 배럴 대신 직접 경로로 import하는 편이 안전하다
비용은 어디서 실제로 생기나
"배럴이 트리 셰이킹을 깨뜨린다"는 말은 절반만 맞다. ESM의 export { X } from './x'·export *는 정적으로 해석되므로, 프로덕션 번들러는 배럴을 거쳐도 쓰인 export만 남길 수 있다. 새는 곳은 따로 있다.
- 부수 효과 모듈이 묶일 때: 배럴 아래 어떤 컴포넌트 파일이
import './sheet.css'나 전역 등록 코드를 갖고 있으면, 그 컴포넌트를 안 써도 번들러는 그 모듈을 버려도 되는지 확신하지 못한다. JS는 빠지고 CSS만 남는 식으로 새기도 한다.package.json의sideEffects로 순수한 파일을 알려 줄 수 있는데, 이때 CSS처럼 import만으로 효과가 있는 파일은"sideEffects": ["*.css"]로 예외에 넣어야 운영 빌드에서 사라지지 않는다(트리 셰이킹) - 배럴을 동적 import할 때:
import('./features/products')처럼 배럴 자체를 분할 단위로 잡으면 그 청크에 배럴이 노출한 것이 다 들어간다(코드 스플리팅과 동적 임포트) - 트리 셰이킹을 하지 않는 환경: ESM에서
import { A } from './barrel'은 배럴 모듈 전체와 그 정적 import를 모두 평가한다. 트리 셰이킹은 이 위에 얹는 프로덕션 번들러의 최적화일 뿐이다. 개발 서버와 테스트 러너는 빠른 재시작을 위해 이 최적화를 생략하므로, 배럴 하나가 가리키는 모듈 수만큼 변환·실행 비용이 그대로 든다. 번들링하는 개발 서버는 컴파일할 모듈이 늘고, 번들링하지 않는 개발 서버는 브라우저 요청이 그만큼 이어진다. 체감은 첫 진입과 캐시가 깨진 뒤에 몰린다 - 서버·클라이언트 경계가 섞일 때: App Router에서 배럴이
server-only모듈과 클라이언트 컴포넌트를 함께 다시 내보내면, 클라이언트 컴포넌트가 배럴에서 무엇을 가져오든 서버 코드가 클라이언트 모듈 그래프로 끌려와 빌드가 깨진다('use client'와 클라이언트 경계)
절충안은 얇은 배럴이다. 배럴에는 타입·상수·API 함수만 두고, 컴포넌트와 부수 효과가 있는 모듈은 직접 경로로 import한다. Feature-Sliced Design의 공개 API처럼 경계 역할을 살리면서 위 비용을 피한다.
라이브러리 배럴과 optimizePackageImports
아이콘·UI 라이브러리는 진입점 하나에서 수천 개 모듈을 다시 내보낸다. Next.js는 13.5부터 experimental.optimizePackageImports로, 배럴 import를 실제 쓰는 모듈 경로로 바꿔 필요한 것만 불러온다. lucide-react·date-fns·lodash-es·@mui/material 등은 기본으로 적용되고, 다른 패키지는 목록에 추가한다(Next.js 16.3 기준, 아직 experimental). node_modules 패키지용이라 앱 안의 배럴에는 해당하지 않는다. Vercel은 도입 당시 lucide-react가 1,583개에서 333개 모듈로 줄었다고 밝혔다.
module.exports = {
experimental: { optimizePackageImports: ['my-icon-lib'] },
}모듈 문법 자체는 ES 모듈과 CommonJS, 큰 패키지 경계를 나누는 설계는 결합도와 함께 생각한다.
출처: webpack - Tree Shaking: Mark the file as side-effect-free · Next.js - optimizePackageImports · Vercel - How we optimized package imports in Next.js