JavaScript - 상대 경로를 깔끔하게 관리하는 법 ① (모듈, index.js 활용)

김동현·2022년 2월 2일
26

🟣 모듈(Module)이란

  • 개발하는 어플리케이션의 크기가 커지면 언젠간 파일을 여러개로 분리해야 함
    • 코드를 분리해서 작성하는 것이 직관성, 유지보수, 코드를 가독성에 더 도움이 되기 때문
  • 이때 분리한 파일 각각을 모듈(module) 이라 함

🔵 모듈 내보내고 가져오기 (import, export 구문)

기본

  • 모듈은 특수한 지시자인 importexport 를 통해 다른 모듈을 불러오거나 외부 모듈에 함수나 변수를 내보낼 수 있음
    • import : 외부 모듈의 기능을 가져옴 (모듈 가져오기)
    • export : 외부 모듈에서 해당 변수나 함수에 접근 가능 (모듈 내보내기)
// a.ts  
export a : number = 3;
// b.ts
import {a} from '/a.ts';

console.log(a+4); // *7 expected*

특수 문법

  • as : 가져온 모듈의 변수나 함수명에 alias 를 붙여주는 문법
    // b.ts
    import {a as A} from '/a.ts'
    
    console.log(A+4); // *7 expected*
  • * : 대부분의 언어에서 지원하듯 모든 것 을 의미하는 연산자
    • default Module 로 선언한 것은 받아오지 못한다.

      // a.ts
      export a : number = 3;
      export b : string = 'Hello World';
      export c  = () : void => console.log('It is C');
      // b.ts
      import * as File from '/a.ts'
      
      console.log(File.a + 4); // 7 expected
      console.log(File.b + '!'); // 'Hello World!' expected
      File.c();  // It is C expected
  • export default : 하나의 모듈에서 대표적으로 내보낼 함수나 변수를 선언하는 문법. 이를 사용할 경우 import A form ‘./A’ 가 가능해지고 즉시 A를 사용할 수 있게 된다.
    • 필자는 회사에 입사전까지 export default 문을 적극적으로 애용했다. export default 를 사용하는 것이 훨씬 코드가 깔끔하다고 느껴졌기 때문이었고, eslint 에서도 prefer-default-module 이라는 문법이 존재했기 때문이다.
    • 하지만 사내에 들어와서 코드를 작성하다보니 export default 의 페혜가 느껴졌다.
      1. intellisense 가 잡히지를 않는다.
      2. 후에 서술한 export * from ‘filePath’ 에서 모듈로서 잡히지 않는다 → 코드가 오히려 더 더러워진다.
      • 아래와 같은 코드를 작성하고야 말았던 것이다.

사실 정확히 이야기하면 export default 로 선언한다고 export * from ‘filePath’를 사용하지 못하는 것은 아니다.
export {default as Foo} from ‘./Foo’;
이런 식으로 작성할 수 있다. 하지만 후술하겠지만, 이런 방식은 오히려 코드를 복잡하게 만들 뿐이다.

  • index.js (index.ts) : 이를 사용할 경우 import 문에서 해당 폴더만 작성까지만 파일경로를 작성해줘도 자동으로 index.js 파일을 import 를 하게 된다
    root
    ├── Foo
    │		└── **index.ts**
    └── app.tsx
    // app.tsx
    import {getValue} from 'Foo';
    import {getValue} from 'Foo/index';
    import {getValue} from 'Foo/index.ts';
    • 위의 3가지 import 문은 전부 같은 결과를 낳는 코드이다.

🟠 export * from ‘filePath’

  • 이제 원래 하고 싶은 이야기로 돌아온다. 어떻게 하면 깔끔하게 import , export 를 쓸 수 있을까?
  • 해답은 모든 폴더에 index.ts 를 넣어주고, index.ts 안에 해당 폴더에 있는 모든 모듈들을 export * from ... 을 선언한다. 이다.
  • 무슨 말인지 와닿지 않을 것 같아 예시를 들어보겠다.
root
├── navigation
│		├── navigation.method.ts
│		├── navigation.screen.tsx
│		└── index.ts
├── screens
│		├── phone-call
│		│		├── phone-call.const.ts
│		│		├── phoen-call.screen.tsx
│		│		└── index.ts
│		├── memo
│		│		├── memo.const.ts
│		│		├── memo.screen.tsx
│		│		└── index.ts
│		└── index.ts
└── app.tsx
  • 위는 현재 사내 프로젝트 구조를 극도로 단순화한 모형이다.

    • 모든 폴더 아래에 index.ts 가 있는 것을 볼 수 있다. 각각의 index.ts 코드를 보면 다음과 같다.
      • root/navigation/index.ts

      • root/screens/phone-call/index.ts
      • root/screens/memo/index.ts

      • root/screens/index.ts
  • 그렇다면 최종적으로 이 모든 파일들을 불러오는 app.tsx 에서는 어떨까?


  • 위에서 이전에 작성한 코드와는 비교도 안되게 깔끔해진 것을 알 수 있다.

  • 종합하여 현재 나는 현업을 포함하여 사이드프로젝트를 진행할 때 아래와 같이 파일 구조를 만든다.

    1. export default 사용을 배제하고 named export 를 사용한다.
    2. 모든 폴더에는 index.ts 가 존재한다.
    3. index.ts 는 해당 폴더안에 있는 모든 파일들을 export * from ‘...’ 으로 내보낸다.
    4. import 문을 사용하는 곳에서는 최상위 폴더의 이름만 작성해주면 해당 폴더 안에 있는 모든 모듈들이 import 된다.

🔴 문제점은 없나?

  • 문제점이 존재한다! 가령 위의 예제에서 phone-call.screen.tsx 에서 navigation 쪽 코드를 불러온다고 가정해보자.
    • 그렇다면 아래와 같이 작성을 해야한다.

      import {getNavigationValue} from '../../navigation';
    • 기껏 상대경로를 찾는것을 최대한 줄일려고 했지만 상위 폴더로 가는 .. 을 막을수는 없었다.

  • 이러한 문제점을 해결할 수 있는 방안을 다음 포스트에 적어보도록 하겠다.

Reference

profile
Frontend Developer

5개의 댓글

comment-user-thumbnail
2022년 2월 2일

글 잘봤습니다! webpack 설정을 통해서 alias 별칭 설정을 통해 절대 경로 세팅이 된다면 말씀하신 상대경로에 대한 문제점이 해결될 것 같네요!

1개의 답글
comment-user-thumbnail
2022년 2월 4일

리액트 프로젝트라면, jsconfig.json (또는 타입스크립트+리액트라면 tsconfig.json) 의 path alias 설정하면 절대경로로 모듈 가져오는게 가능합니다.

vue 프로젝트라면, import sample from '@/foo/bar' // 이때 @/ 는 프로젝트의 최상위 경로가 됩니다.

1개의 답글
comment-user-thumbnail
2022년 2월 19일

이 내용 찾고 있었는데 딱 정리 잘해주셔서 감사합니다!

답글 달기