Angular 2로 개발하다 보면 프로그램의 규모가 커지며, 디렉토리를 정리해야 할 때가 온다. 대개 다음과 같이 정리가 되는데,

app/
  services/
    auth/
      auth.service.ts
    ... 그 외!
  components/
    home/
      home.component.ts
      home.component.html
      home.component.scss
    login/
      login.component.ts
      login.component.html
      login.component.scss
    ... 그 외!
  modules/
  ... 그 외!

디렉토리의 깊이가 깊어짐에 따라 서로 서로 참조하기가 골치아파진다. 가령 home.component.ts 안에 있는 HomeComponentauth.service.ts 내에서 참조하려면 다음과 같은 import 구문을 생성해야 할 것이다.

import { HomeComponent } from '../../components/home/home.component';

그나마 이 정도는 양반이다. 가령 조금 더 복잡한 경우를 생각해보자. 내가 경험했던 경우인데,

app/
  services/
    auth/
      auth.service.ts
      auth.can-activate.ts
      auth.can-deactivate.ts
    file-list/
      file-list.service.ts
      file-list.can-activate.ts
      file-list.can-deactivate.ts
    ... 그 외!
  components/
    dashboard/
      dashboard.component.ts
      dashboard.component.html
      dashboard.component.scss
      dashboard-nav.component.ts
      dashboard-nav.component.html
      dashboard-nav.component.scss
      file-list.component.ts
      user-list.component.ts
      editor.component.ts
    ... 그 외!
  modules/
  ... 그 외!

위를 보시면 dashboard 디렉토리 내에 조금 많은 파일들이 보인다. 한 화면에서 많은 컴포넌트들이 쓰이는데, 각각 컴포넌트가 덩치가 무거워지자 한 파일 내에서 관리하기에는 가독성이 많이 떨어져서 이를 분리해놓은 것인데, 다른 디렉토리로 관리하기에는 새롭게 등장하는 녀석은 아니라 하나의 디렉토리에서 관리하게 된 경우이다.

이런 경우 가령 file-list.service.ts 파일 내에서 DashboardComponentFilelistComponent 등을 동시에 참조해야 하는 경우, 기존의 방식대로라면 각각 참조해야 하는 것이다. 다음과 같이 말이다.

import { DashboardComponent } from '../../components/dashboard/dashboard.component';
import { FilelistComponent } from '../../components/dashboard/file-list.component';

이런 것들이 많아지면 가독성이 현저히 저하된다.

그래서 이런 문제를 해결하기 위해 barrel 이라는 개념이 있다. ECMAScript 6 의 Destructuring 도입으로 인해 사용되는 이 녀석은, 한 디렉토리 내에 index 파일을 놓고, index 파일 내에서 해당 디렉토리 내에 존재하는 모든 파일을 export 하는 것이다. (Angular 공식 홈페이지에서는 다음의 링크에서 해당 개념을 소개하고 있다.)

실제로 사용하면 다음과 같은 모습이 된다.

app/
  services/
    auth/
      auth.service.ts
      auth.can-activate.ts
      auth.can-deactivate.ts
    file-list/
      file-list.service.ts
      file-list.can-activate.ts
      file-list.can-deactivate.ts
    ... 그 외!
  components/
    index.ts
    dashboard/
      dashboard.component.ts
      dashboard.component.html
      dashboard.component.scss
      dashboard-nav.component.ts
      dashboard-nav.component.html
      dashboard-nav.component.scss
      file-list.component.ts
      user-list.component.ts
      editor.component.ts
      index.ts
    ... 그 외!
  modules/
  ... 그 외!
// app/components/dashboard/index.ts
export * from './dashboard.component';
export * from './dashboard-nav.component';
export * from './file-list.component';
export * from './user-list.component';
export * from './editor.component';
// app/components/index.ts
export * from './dashboard';
// export * from './dashboard/index' 와 같은 표현이다!
// 암묵적(implicit) 표현임.
// app/services/file-list/file-list.service.ts
import { DashboardComponent, FilelistComponent } from '../components';

‘barrel’이라는 이 좋은 개념을 쓰다보면 Angular 개발이 참 재미있다. 별 문제도 없는 것 같다.

그 런 데 !

이 개념은 일반적인 경우에서는 잘 동작하나, 순환 참조 라는 예외적인 경우가 발생하는 경우에는 문제가 된다. 순환 참조는 특히 서비스에서 더욱 빈번하게 발생하므로, 굉장히 주의해야 한다.(순환참조에 관한 이야기는 다음의 글 등을 참조하시면 된다.)

따라서 서비스에서 다른 서비스를 참조할 때에는 꼭 barrel 을 사용하지 말고 직접 참조하도록 하자.

(이유는 barrel의 동작 원리를 잘 생각해보면 알 수 있다.)

  • ㄴㄴㄴ
// app/services/file-list/file-list.service.ts
import { AuthService } from '../index';
  • ㅇㅇㅇ
// app/services/file-list/file-list.service.ts
import { AuthService } from '../auth/auth.service';