ADR003: 기본(Default) 내보내기를 피하고 명명된(Named) 내보내기를 선호하기
CommonJS가 주요 작성 형식이던 시절에는, 모듈에서 한 가지 것만 내보내는 것이 모범 사례였습니다.
출처: 문서
본문
맥락
CommonJS가 주요 작성 형식이던 시절에는 module.exports = ... 형식을 사용해 모듈에서 한 가지 것만 내보내는 것이 모범 사례였습니다. 이는 "한 가지 일을 잘 하라"는 UNIX 철학과 일치했습니다. 모듈은 내부 구조를 알 필요 없이(const localName = require('the-module');) 소비될 수 있었습니다.
이제 ESModules가 주요 작성 형식입니다. ESModules는 내보내기의 컴파일 시점 검증과 표준 정의 의미론 등 수많은 이점을 가집니다. 소비자가 import localName from 'the-module';을 할 수 있게 해주는 "default exports"라는 유사한 메커니즘도 있습니다. 이는 본질적으로 import { default as localName } from 'the-module';과 동일합니다.
하지만 default export를 피해야 할 이유는 많습니다. 이전에도 다른 문서들에서 기록되었습니다.
요약하면 다음과 같습니다.
-
개발자가 모듈의 로컬 이름을 만들도록 부추겨 간접성을 더하고, 인지 부하를 늘리며 코드 이해를 느리게 합니다:
import TheListThing from 'not-a-list-thing'; -
코드를 자동으로 이름 바꾸고 리팩터링할 수 있는 IDE 같은 도구를 방해합니다.
-
가져온 멤버가 전적으로 소비하는 개발자의 몫이므로 오타와 실수를 부추깁니다.
-
default 프로퍼티를 소비자가 수동으로 지정해야 하므로 CommonJS interop에서 지저분합니다. 이는 종종 Babel의 모듈 interop에 숨겨져 있습니다.
-
이름 충돌로 인해 re-export를 깨뜨리고, 개발자가 각각 수동으로 이름을 짓도록 강제합니다.
명명된(named) 내보내기를 사용하면 기호(symbol) 이름을 바꿀 필요가 없어지는 데 도움이 되며, 이는 수많은 이점을 가집니다. 몇 가지 예는 다음과 같습니다.
-
"Find All References"와 "Go To Definition" 같은 IDE 도구.
-
수동 코드베이스 검색("grep" 등)이 고유한 기호로 더 쉬워집니다.
결정
꼭 필요할 때(예: React.lazy 모듈)를 제외하고는 default export 사용을 중단합니다. default를 절대 사용하고 싶지 않은 사람들을 위한 해결 방법이 있습니다.
const Component = React.lazy(() => import('../path/to/Component').then(m => ({ default: m.Component })),);
결과
우리는 가능한 한 명시적으로 코드베이스에서 default export를 제거하기 위해 적극적으로 노력할 것입니다. 연결된 컴포넌트가 있나요? export const ConnectedComponent = connect(Component)처럼 하세요.
lint 규칙과 같은 도구를 추가해 default export에서 벗어나는 마이그레이션을 돕겠습니다.