오늘 React & React-Native 채팅방에서 lodash 사용법을 물어보는 사람이 있었어

// imports
import { union } from 'lodash';

이렇게 했는데 union을 쓸 수 없어 답답하는 것이야. 나는 주로

import _ from 'lodash'

이렇게 작성한 다음에 _.union()을 호출하는 식으로 사용하지. 문제를 제기한 사람은 lodash의 여러 함수중에 union만 필요했기 때문에 저렇게 사용한 걸거야. 우리가 react-native 코딩할 때 import { View, Text } from 'react-native' 이렇게 하는 이유와 같지. react-native 안에 수많은 컴포넌트들 중에서 View, Text만 효율적으로 쓰는 이점 아니겠어?

아무튼 import에서 중괄호의 의미를 알기 위해서 공부 좀 해보자. 다음 SO관련된 질문의 답변을 한번 천천히 읽어봐. 중괄호({})없이 import할 때는 default export가 호출된다. 여러 export 중에서 ‘말하지 않아도 그 변수’ 이런 의미로 default export 변수가 있는데, 문제는 이게 파일에서 하나만 존재할 수 있다는 거야. lodash 처럼 하나의 파일 내에 default가 아닌 여러 변수들을 포함하는 경우에는, 이 변수들 중에 특정해서 import할 때는 중괄호 안에다가 export한 변수의 이름을 써서 import 하는 것야. 이해가 되니?

이론 공부는 어느 정도 익혔으니, lodash가 중괄호로 import할 경우 union이 동작하지 않은 이유가 뭔지 감을 잡아보자. 위에 배운대로 보면 union이 export 함수로 등록되어 있지 않아서 그럴 수 있겠다. 한번 lodash source code를 열어 보자. export default union 이처럼 default export로 코드가 되어 있어서 중괄호가 안먹히는 것이 아닌가 의심해본다. 하지만 좀 더 생각해보면, 이 소스코드들이 결국 빌드되어서 우리는 lodash.js 파일을 사용하는 것이니, 소스를 보려면 최종 결과물을 봐야 할 것 같아 열어봤더니 파일의 맨 끝부분에 다음과 같은 export 코드가 들어 있었다.

// Export lodash.
var _ = runInContext();

// Some AMD build optimizers, like r.js, check for condition patterns like:
if (typeof define == 'function' && typeof define.amd == 'object' && define.amd) {
  // Expose Lodash on the global object to prevent errors when Lodash is
  // loaded by a script tag in the presence of an AMD loader.
  // See http://requirejs.org/docs/errors.html#mismatch for more details.
  // Use `_.noConflict` to remove Lodash from the global object.
  root._ = _;

  // Define as an anonymous module so, through path mapping, it can be
  // referenced as the "underscore" module.
  define(function() {
    return _;
  });
}
// Check for `exports` after `define` in case a build optimizer adds it.
else if (freeModule) {
  // Export for Node.js.
  (freeModule.exports = _)._ = _;
  // Export for CommonJS support.
  freeExports._ = _;
}
else {
  // Export to the global object.
  root._ = _;
}

뭐하는 함수인지 모르겠지만 runInContext() 함수를 _ 파일에 assign하고 그냥 그걸 웹브라우저 용으로 쓸 경우 root._ 로 글로벌 오브젝트로 할당시키거나 node.js나 commonjs 등을 위해서는 freeExports._ 에 할당시켜서 쓰는 것 같은데 사실 무슨 말인지 모르겠어.

결론은 왜 union 만 import하는 게 안되는지 여전히 모르겠어. 좀 더 깊이 들어가보면 좋겠지만, 그렇게 집요한 성격이 아니라서 그냥 여기까지만 하고 무식을 자랑며 마치고자 한다. 감으로 보면, es6/es5 사이의 문법적 차이점일 수도 있을 거 같지만 이 글에서는 결론을 못내겠어. 좀 더 공부해보고 업데이트 할께.

PS1. runInContext는 찾아보니, node V8의 vm 모듈의 함수로 특정 context에서 특정 코드를 실행시키는 역할이야.

PS2. 또 찾아보니, 관련 github issue가 있었는데 이미 구현했다는 이야기인데… 그러면 되야 하는거 아닌가? 왜 안될까?

PS3. 더 찾아보니, import union from 'lodash/union' 혹은 import union from 'lodash-es/union' 혹은 import { union } from 'lodash' 역시 가능하다고 하네, 지금까지 안된다고 가정하고 썼는데 젠장…

PS4. tree shaking 이란 말이 있는데 다음 블로깅 주제로 하면 좋을 것 같아.

PS5. 나랑 소통하려면 아래 twitter, email 연락 줘.