gyunam.blog

[weekly] [nb] 웹 개발 시작하기(5)

자바스크립트에서 모듈을 사용하는 이유에 대해 설명해 주세요.

모듈(module)

이 모듈(module) 이란 용어는 프로그래밍에서 굉장히 오래전부터 사용되었다.

간단한 역사

1960년 ~ 1970년

algol, simula, modula 등의 초창기 객체지향 패러다임 언어에서 사용되었다.

특히 modula 는 프로그래밍 언어 이름에도 module 이 들어가는 만큼, 가장 적극적으로 module 이란 용어를 사용하고 개념을 정립했다.

1980년 ~ 1990년

ada, pascal 에서도 module 의 개념이 적극적으로 도입되고, 이 시기 부터 거의 대부분의 프로그래밍 언어가 이 module 이라는 개념을 사용하기 시작한다.

프로그래밍의 황금기를 연 c 언어 역시 공식적으로 module 개념을 언급하지는 않지만, module 의 개념을 사용한다. .h(헤더) 와 .c(소스) 파일을 나누는 것이 바로 그것.

황금기의 절정을 연 pyhon 은 import 구문으로 module 을 명확하게 사용한다.

모듈의 의의

이렇게 많은 프로그래밍 언어들이 module 이라 개념을 사용하게 된 것은 당연 필요했기 때문이다.

javascript 에서는 아래의 큰 이유들이 존재한다.

전역 변수 충돌 방지

javascript 는 원래 웹 개발에 사용되기 위해서 만들어졌다는 것을 기억해야 한다.

안 좋은 예


<script>
  var PI = 3.14;
  function add(a, b) { return a + b; }
</script>

<script>
  var PI = '3.14';
  function add(a, b) { return a + ' and ' + b; }
</script>

javascript 는 <script> 태그로 html 파일 내부에 작성할 수 있다.

이 방법은 필요할 때 마다 javascript 를 작성하고 적용할 수 있는 장점이 있는 반면, 변수는 전역(global) 로 동작한다.

module 로 코드를 작성하면 변수 충돌을 방지 할 수 있다.

좋은 예


// math.js
export const PI = 3.14;
export function add(a, b) {
  return a + b;
}

코드 분리

기능 또는 객체별로 코드를 분리할 수 있기 때문에 하나의 코드 파일에 적정 수의 코드를 유지할 수 있다.

안 좋은 예


// index.js
function login(username, password) {
  return username === 'admin' && password === '1234';
}

if (login('admin', '1234')) {
  console.log('Welcome!');
}

지금 코드는 짧으니 괜찮아 보인다.

하지만 login 에 이어서 logout, create, update.. 등등의 기능들이 모두 하나의 파일에 들어있다고 생각해보자.

적게는 수백 줄, 많게는 수천 줄까지 가는 건 어렵지 않을 것이다.

이렇게 되면, 어떤 문제가 발생했을 때, 문제가 발생한 부분을 찾는 것도 어렵거니와, 이 파일 안에 어떤 변수나 함수가 또 연관되어있는지 찾는게 어렵다.

규모가 큰 게임의 경우 코드의 길이를 다 합치면 억 단위 까지도 가는 경우가 있으니, 그야말로 백사막에서 소금 한 조각 찾기라 할 수 있다.

좋은 예


export function login(username, password) {
  return username === 'admin' && password === '1234';
}

이것도 굳이 면밀히 따지자면 좋다고 말할 수 없지만, 어쨋든 목적은 코드 분리기 때문에 괜찮다!

재사용성

아까 위에서 분리하는 개념에서 이어진다.

기능 혹은 객체 별로 분리한 것을 다른 곳에서 계속 해서 사용할 수 있다.


export function formatDate(date) {
  return date.toISOString().split('T')[0];
}

즉, javascript 에서 '뭔가 똑같은 코드를 두 번이상 작성하네?' 싶으면 죄다 빼내서 분리하면 된다.

그래서 가능한 하나의 함수는 하나의 기능만 동작하도록 유지하는 것이 좋다.

의존성 명확한 가독성

import 구문이 있고 없고의 차이는 굉장히 크다.

없을 때


<script>
  function add(a, b) { return a + b }
</script>
<!-- 수 백개의 html 코드 -->
<script>
  const result = add( 0, 1 )
</script>

저 두 javascript 사이에 수백 개의 html 코드가 있다고 생각해보자.

대체 어디서 정의된 add() 란 함수를 사용한건지 알 수가 없다.

이 이유로 상기했던 전역 변수 충돌이 발생할 수 있는 것이다. add() 가 만들어져있는지 모르고 또 만들 수도 있기 때문!

있을 때


import { add } from './add.mjs'

const result = add( 0, 1 )

javascript 에 명확하게 포함한다(import) 라는 구문이 들어가 있기 때문에 훨씬 가독성이 좋아진다.

add() 함수를 수정해야하는 상황이 오면, add.mjs 를 참고해야겠구나 라고 추측하기가 쉽다.

최적화

사실 이 최적화라는 것은 번들링(bundling)을 진행하는 속도와 그 결과물을 서비스를 올릴 때 리소스 용량 이 줄어드는 것을 보면 체감이 된다.

그 밖에 최적화라 해봐야 하나의 모듈에 비슷한 계열의 기능들을 묶어서 쓸 수 있다는 점?


// math.mjs
export function add(a, b) { return a + b }
export function subtract(a, b) { return a - b }
export function multiply(a, b) { return a * b }
export function divide(a, b) { return a / b }


import * as Maths from './math.js'

console.log(Maths.add(2, 3))

마무리

내가 javascript 를 배워서 쓸 때엔 당연히 있는 기능이라 써서 소중함을 몰랐다.

module 이라는 기능이 없는 상태에서 코드를 작성해보니, 불편함을 바로 체감할 수 있었다.

아주 간단한 express 코드로 외부 api 요청, 응답만 할 때 예외처리만 섞어도 코드는 수백 줄이 넘어간다.

그리고 특정 변수, 함수를 찾기 위해서 한 코드 내에서 올라갔다 내려갔다 하는 것이 엄청난 고역이었다.