클로저를 활용한 모듈패턴

seunghw·2022년 9월 26일
10
post-thumbnail

시작

모듈패턴에 대해서 다양한 방법들이 있는데 그 중에서 클로저를 활용한 모듈 패턴에 대해서 적어보려고 합니다.

모듈패턴을 이해하기 전

모듈패턴을 이해하기 전에 우선 먼저 알아야하는 개념들이 있습니다.

즉시실행함수

즉시 실행 함수 표현(IIFE, Immediately Invoked Function Expression)은 정의되자마자 즉시 실행되는 Javascript Function 를 말합니다.

모든 코드를 즉시 실행 함수로 감싸면 모든 변수는 즉시 실행 함수의 지역 변수가 되는데 이를 활용하여 전역 변수의 사용을 제한하는 방법입니다.

즉, 외부에서는 접근이 불가능한 독립적인 스코프를 가지게 됩니다. 이로인해서 모듈패턴은 일반적으로 IIFE와 함께 쓰입니다.

우리는 이와 같은 방법을 라이브러리 등에서 자주 볼 수 있습니다.


(function () {
   let foo = 10;
})();

console.log(foo)

클로저

클로저란 이미 실행이 종료된 함수의 변수나 함수를 참조할 수 있는 링크를 가진 내부 함수를 뜻합니다.

클로저의 핵심은 스코프를 이용해서, 변수의 접근 범위를 닫는데에 있습니다.

따라서 함수를 리턴하는 것 만큼이나 변수가 선언된 위치가 중요합니다.

외부 함수보다 중첩 함수가 더 오래 유지되는 경우 중첩 함수는 이미 생명 주기가 종료한 외부 함수의 변수를 참조할 수 있습니다.
이러한 중첩 함수를 클로저라고 부릅니다.


const add = function (a) {
	return function (b) {
		return a + b;
	}
}

const addData = add(5);

addData(3) // 8
addData(20) // 25

일반적인 함수는 함수 실행이 끝나고 나면 함수 내부의 변수를 사용할 수 없습니다. 하지만 이와 다르게 클로저는 외부함수 실행이 끝나더라도 외부함수 내 변수가 메모리 상에 저장됩니다.
내부 함수는 외부 함수에 선언된 변수에 접근 가능합니다.

클로저는 이처럼 상태가 의도치 않게 변경되지 않도록 안전하게 은닉하고 특정 함수에게만 상태 변경을 허용하여 상태를 안전하게 변경하고 유지하기 위해 사용합니다.

💡 스코프란?
식별자가 유효한 범위. 자신이 선언된 위치에 의해 다른 코드가 식별자 자신을 참조할 수 있는 유효범위가 결정된다.

모듈패턴이란?

모듈 패턴은 JavaScript 에서 중요한 패턴 중 하나이며 가장 일반적으로 사용되는 디자인 패턴입니다. 프로젝트의 코드 단위를 명확하게 분리하고 구성하는데 큰 도움이 됩니다.

클래스를 모방해서 관련이 있는 변수와 함수를 모아 즉시실행함수()로 감싸서 하나의 모듈을 만들어 클로저를 기반으로 동작합니다.

모듈패턴은 일반적으로 위에서 나온 개념들을 합쳐서 작성합니다.

var Counter = (function () { //즉시실행함수로 감싼다
  // private 변수
  var num = 0;

  // 클로저
  //외부로 공개할 데이터나 메서드를 프로퍼티로 추가한 객체를 반환한다.
    return {
    increase() {
      return ++num;
    },
    decrease() {
      return --num;
    }
  };
}());

// private 변수는 외부로 노출되지 않는다.
console.log(Counter.num); // undefined

console.log(Counter.increase()); // 1
console.log(Counter.increase()); // 2
console.log(Counter.decrease()); // 1
console.log(Counter.decrease()); // 0

Counter는 값으로 담고 있는 프로퍼티들을 담은 객체를 반환하고 있습니다. Counter의 스코프 밖에서 바라볼 때 Counter의 그 어떠한 프로퍼티에도 접근을 할 수 없습니다.

즉시실행함수는 한 번만 실행되므로 Counter가 호출될 때마다 num 변수가 초기화될 일은 없습니다.

즉시실행함수가 반환한 클로저는 Counter 변수에 할당되어 호출됩니다.

이 때 이 클로저는 자신이 정의된 위치에 의해 결정된 상위 스코프인 즉시실행 함수의 렉시컬 스코프를 기억하고 있어서 카운트 상태를 유지하기 위해 변수 num을 참조하고 변경할 수 있습니다.

결국 num은 클로저로 인해서 외부에서 접근할 수 없고 메서드들을 통해서 접근해야만 합니다.

💡 렉시컬 스코프란?
함수를 어디에 정의했는지에 따라 상위 스코프를 결정하는 것. 즉, 함수의 상위 스코프는 언제나 자신이 정의된 스코프

모듈패턴을 쓰는 이유와 특징

❗️ 자바스크립트는 private 등의 접근 제한자가 없기때문에 클로저를 모듈패턴을 활용하여 이러한 특징들을 흉내내는 것입니다.

1. 캡슐화

캡슐화, 즉 정보은닉을 구현하기 위해서 모듈패턴을 사용합니다.

따라서 이에 따른 오류로부터 보다 안전하게 값을 보호할 수 있습니다. 클로저를 통해 불필요한 전역 변수 사용을 줄이고, 스코프를 이용해 값을 보다 안전하게 다룰 수 있습니다.

또한 모든 관련 코드를 단일 논리 블록에 캡슐화할 수 있으므로 유지 관리가 더 쉬워집니다.

캡슐화는 객체의 상태를 데이터와 기능을 하나의 단위로 묶는 것입니다. 하나의 객체 안에 넣어서 묶는 것.
데이터(속성)와 기능(메서드)들이 느슨하게 결합

1) 은닉
구현은 숨기고, 동작은 노출

2) 느슨한 결합에 유리
언제든 구현을 수정할 수 있습니다.

💡 느슨한결합이란?
코드 실행 순서에 따라 절차적으로 코드를 작성하는 것이 아니라, 코드가 상징하는 실제 모습과 닮게 코드를 모아 결합하는 것을 의미

2. 전역변수의 억제

  • 전역 변수가 좋지 않은 이유는, 전역 변수는 다른 함수 혹은 로직 등에 의해 의도되지 않은 변경을 초래하는 side effect때문인데 side effect를 최소화하면, 의도되지 않은 변경을 줄일 수 있습니다.
    특정 속성과 기능을 공개로 노출하고 개체 자체 내에서 속성과 기능의 범위를 제한하여 private로 만들 수도 있습니다.
    그렇기 때문에 해당 변수는 함수 범위 외부에서 액세스할 수 없습니다. 이를 통해서 전역 스코프의 변수들과 이름이 충돌하는 문제를 줄일 수 있습니다.

3. 재사용성

단일 코드 단위를 전체 애플리케이션에서 재사용할 수 있습니다. 모듈로 묶인 기능은 재사용할 수 있으며 여러 지점에서 동일한 기능을 정의할 필요가 없습니다.

참조

https://developer.mozilla.org/ko/docs/Glossary/IIFE
https://stackoverflow.com/questions/17776940/javascript-module-pattern-with-example
https://www.patterns.dev/posts/module-pattern/
https://medium.com/%EC%98%A4%EB%8A%98%EC%9D%98-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-%EB%AA%A8%EB%93%88-%ED%8C%A8%ED%84%B4-d5ba2c94eeb5

profile
Lumos

2개의 댓글

comment-user-thumbnail
2022년 10월 4일

글 내용은 좋은데 핵심 키워드의 색상이 사용자가 보기에 편하지 않아서 색상 수정 해주시면 다른 분들도 더 잘 읽으실 것 같아요~!

1개의 답글