클로저로 구현하는 디자인 패턴

자바스크립트 클로저를 통해 디자인 패턴을 구현해보겠습니다

·

3 min read

클로저는 주변 상태(어휘적 환경)에 대한 참조와 함께 묶인(포함된) 함수의 조합입니다(mdn 참고). 클로저를 통한 설계를 연습도 할 겸, 클로저와 호환이 잘 되는 네 개의 디자인 패턴을 소개하고, 예시 코드를 작성해보겠습니다. 예시 코드들은 간단한 사용자 설정 관리 시스템을 구현하는 코드들로 작성 해보겠습니다.

모듈 패턴(Module Pattern)

  • 모듈 패턴은 특정 기능을 캡슐화하고, 공개 인터페이스만을 외부에 노출하는 패턴입니다. 여기서 캡슐화는 객체의 상태를 나타내는 프로퍼티와 프로퍼티를 참조하고 조작할 수 있는 동작인 메서드를 하나로 묶는 것을 말합니다.

  • 모듈 패턴은 클로저를 사용하기 적합한 디자인 패턴 중 하나입니다. 클로저 자체가 상태를 안전하게 변경하고 유지하기 위해 활용되기 때문입니다.

      const userSettingsModule = (function () {
          let settings = {
              theme: 'dark',
              language: 'ko'
          };
    
          return {
              getSetting: function (key) {
                  return settings[key];
              },
              setSetting: function (key, value) {
                  settings[key] = value;
              }
          }
      })();
    
      userSettingsModule.getSetting('theme'); // 'dark'
      userSettingsModule.setSetting('theme', 'light');
      userSettingsModule.getSetting('theme'); // 'light'
    

    이 예시에서 settings 객체는 모듈 내부에서만 접근 가능하고, getSettingsetSetting 메서드를 통해서만 조작할 수 있습니다. 클로저를 통해 settings의 상태가 안전하게 캡슐화됩니다.

팩토리 패턴(Factory Pattern)

  • 팩토리 패턴은 객체를 생성하는 인터페이스를 정의하고, 인스턴스화는 서브클래스에서 수행하게 하는 패턴입니다.

  • 클로저를 사용하여 특정 인터페이스를 만족하는 객체를 동적으로 생성하고 반환할 수 있습니다. 클로저를 통해 생성된 객체 각각이 자신만의 독립적인 렉시컬 환경(=정적 환경)을 가질 수 있어서 객체마다 다른 상태를 유지할 수 있습니다.

    • 참고: 렉시컬 환경은 실행할 스코프 범위 안에 있는 변수와 함수를 프로퍼티로 저장하는 객체입니다.
  •   function settingsFactory() {
          return {
              theme: 'dark',
              language: 'ko',
              changeSetting: function (key, value) {
                  this[key] = value;
              }
          };
      }
    
      const userSettings = settingsFactory();
      userSettings.theme; // 'dark'
      userSettings.changeSetting('theme', 'light');
      userSettings.theme; // 'light'
    

    이 예시에서 settingsFactory 함수는 각 사용자 설정 객체를 생성합니다. 생성된 객체는 독립적인 설정 값을 유지하며, 클로저를 통해 각 객체의 상태를 관리합니다.

전략 패턴(Strategy Pattern)

  • 전략 패턴은 여러 알고리즘을 정의하고, 런타임에 알고리즘을 선택할 수 있게 해주는 패턴입니다.

  • 각 전략을 클로저로 구현하여 특정 컨텍스트에서 다양한 알고리즘을 쉽게 교체할 수 있습니다.

  •   function themeStrategy(theme) {
          const themes = {
              dark: function () {
                  console.log('apply dark theme');
              },
              light: function () {
                  console.log('apply light theme');
              }
          };
    
          return themes[theme] || themes.dark;
      }
    
      const currentTheme = themeStrategy('light');
      currentTheme(); // apply light theme
    

    전략 패턴에서는 themeStrategy 함수가 여러 테마 적용 전략 중 하나를 선택해 반환 합니다. 반환된 함수(전략)는 클로저를 통해 자신이 호출될 때 필요한 정보(여기서는 콘솔 로그 메시지)를 "기억"합니다.

명령 패턴(Command Pattern)

  • 명령 패턴은 요청을 객체로 캡슐화하여, 사용자가 보낸 요청을 다양한 요청 종류와 파라미터와 함께 저장, 실행, 취소할 수 있게 해주는 패턴입니다.

  • 명령을 클로저로 캡슐화하여, 실행할 명령과 함께 모든 필요한 정보를 보관할 수 있습니다. 이를 통해 명령의 실행을 지연시키거나, 취소, 재시도 하는 등의 작업을 유연하게 처리할 수 있습니다.

  •   function createChangeSettingCommand(settings, key, value) {
          const previous = settings[key];
          return {
              execute: function() {
                  settings[key] = value;
              },
              undo: function() {
                  settings[key] = previous;
              }
          };
      }
    
      const settings = {
          theme: 'dark',
          language: 'ko'
      };
    
      const changeThemeCommand = createChangeSettingCommand(settings, 'theme', 'dark');
      changeThemeCommand.execute();
      settings.theme; // 'dark'
      changeThemeCommand.undo();
      settings.theme; // 'light'
    

    명령 패턴에서 createChangeSettingCommand 함수는 설정을 변경하는 명령 객체를 생성합니다. 이 객체는 executeundo 메서드를 통해 설정 변경 작업을 수행하고 되돌릴 수 있습니다. 클로저를 통해 이전 상태(previous)를 기억하고, 필요할 때 원상태로 되돌릴 수 있습니다.

마치며

이처럼 클로저는 각 패턴의 중요한 구성 요소로서, 상태를 안전하게 캡슐화하고, 필요한 데이터와 함수를 "기억"하는 데 도움을 줍니다. 이 네 가지 패턴은 클로저와의 호환성이 좋아서 프론트엔드 개발에서 널리 사용되며, 특히 자바스크립트 같은 언어의 기능을 활용할 때 유용합니다. 프론트엔드 코드를 설계하시거나, 리팩터링 할 때 이 블로그 글이 도움이 됐으면 좋겠습니다.