Design Pattern

Kaydenna92·2022년 12월 9일
0

JavaScript

목록 보기
8/15

Design Pattern?

  • 개발을 하면서 발생하는 다양한 이슈들을 해결하는데 도움을 주는 일종의 증명된 기술이라고 할 수 있다.
  • 패턴이 정확한 해결책을 제공해주는 것은 아니다. 일종의 도구일 뿐이고, 이 패턴들을 어떻게 활용해서 어떤 식으로 개발할 것인지는 온전히 개발자의 역량에 달려있다.

패턴이란?

  • 각기 다른 소프트웨어 모듈이나 기능을 가진 다양한 응용 소프트웨어 시스템들을 개발할때도 서로간에 공통되는 설계문제가 존재하며 이를 처리하는 해결책 사이에도 공통점이 있다.
  • 이러한 유사점을 패턴이라 한다
  • 패턴은 공통의 언어를 만들어주고, 팀원 사이의 의사소통을 원할하게 해주는 아주 중요한 역할을 한다.

패턴의 목적?

  • 의사소통표현력의 강화

Modules

  • 모듈이란 쉽게 말해서 레고 블록의 블록 한 개와 같은 것이다.
  • 모듈은 탄탄한 어플리케이션 구조를 만드는데 꼭 필요한 요소이다.
  • 모듈은 프로젝트의 코드를 깨끗한 구조로 정돈하는데 도움을 준다.

1. Object Literal

// 가장 일반적이고 간단한 형태, 
// module이란 변수에 중괄호와 함께 프로퍼티와 메서드를 선언하는 형태.
let module = {
	someProp: 'hello',
    someMethod: function() {
    	console.log(this.someProp);
        }
    };
  • object literal 은 사용하기 간단하고 유용하지만 가장 큰 문제가 있다.
    바로 보안이 취약하다는 점.
  • 일반적으로 자바스크립트는 브라우저에서 사용자에게 직접 노출되기 때문에 보안에 취약할 수 밖에 없다. 따라서 글로벌 공간에 변수를 선언하는 것을 되도록이면 피해야한다.
  • 글로벌 공간에 어쩔 수 없이 변수를 선언해야한다면 Namespace를 이용하는 방법도 있다.
// 즉시실행함수를 사용하여 객체를 리턴하고, battery변수를 함수 안에 선언하여 보호하는 방법.
// 그러나 이 방법을 사용하면 새로운 phone을 만들기 위해서 계속해서 똑같은 즉시실행함수를 몇 번이고 적어주어야한다.
const phone = (function () {
  let battery = 0;
  return {
    rechargeBattery: function() {
      battery = 100;
    },
    showRemainBattery: function() {
      return battery;
    }
  }
})();
phone.showRemainBattery(); // 0
phone.rechargeBattery();
phone.showRemainBattery(); // 100
// 위의 코드를 재활용하여 사용하기 위해 createPhone라는 이름의 함수를 만들어 쓸 수 있음.
// 그러나 이 방법도, 여전히 글로벌에 선언되므로 완전하지 않음.
function cretePhone() {
  let battery = 0;
  return {
    rechargeBattery: function() {
      battery = 100;
    },
    showRemainBattery: function() {
      return battery;
    }
  }
}
const phone1 = createPhone();
phone1.rechargeBattery();
const phone2 = createPhone();
console.log(phone1.showRemainBattery());
console.log(phone2.showRemainBattery());
// 다시한번 createPhone 함수를 즉시실행함수로 감싸는 방법을 사용할 수 있다. 
(function phoneBattery() {
  function createPhone() {
  let battery = 0;
  return {
    rechargeBattery: function() {
      battery = 100;
    },
    showRemainBattery: function() {
      return battery;
    }
  }
}
const phone1 = createPhone();
const phone2 = createPhone();
})();

🧐 여기서 즉시실행함수란?

  • 정의되자마자 즉시 실행되는 함수를 말한다.
  • 다음과 같이 소괄호로 함수를 감싸서 실행하는 문법을 사용한다
(function() {
  console.log('즉시실행함수');
})();
// 화살표 함수로도 가능하다.
(() => {
  console.log('즉시실행함수');
})();

즉시실행함수에 익명함수를 사용해야 하는가?

  • 선언과 동시에 호출되고 반환되어 재사용할 수 없기 때문에 이름을 지어주는 것은 의미가 없음.

즉시실행함수를 왜 사용하는가?

  1. 필요없는 전역 변수의 생성을 줄일 수있다.
    함수를 생성하면 그 함수는 전역 변수로써 남아있게 되는데, 많은 변수의 생성은 전역 스코프를 오염시킬 수 있다. 따라서 즉시실행함수로 선언하면 내부 변수가 전역으로 저장되지 않기때문에 전역 스코프의 오염을 줄일 수 있다.
  2. private 한 변수를 만들 수 있다.
    즉시실행함수는 외부에서 접근할 수 없는 자체적인 스코프를 가지게 된다.
    이는 클로저의 사용목적과도 비슷하며 내부 변수를 외부로부터 private하게 보호할 수 있는 장점이 있다.

즉시실행함수를 어떻게 활용할까?

  1. 단 한번의 사용만 필요한 함수
    즉시실행함수는 한 번의 실행 이후 없어지기 때문에 단 한번의 사용만 필요한 함수에 사용된다. 예를 들어, 변수를 초기화 하는 함수가 있다.
let isAdult;
(function init(age) {
    let currentAge = age;
    if (age >= 20) {
        isAdult = true;
    } else {
        isAdult = false;
    }
})(20);
console.log(isAdult); //  true
console.log(currentAge); //  Uncaught ReferenceError: currentAge is not defined

성인임을 나타내는 isAdult 변수를 즉시실행함수를 활용해 초기화하는 예제. init 함수에 입력되는 age 인자에 따라 다른값을 할당하게 된다.
이후 isAdult 값을 콘솔로 찍어보면 true 가 할당됨을 알 수 있고, 내부변수인 currentAge는 전역으로 저장되지 않음을 확인 할 수 있다.


2. 자바스크립트 모듈
자바스크립트 모듈을 만들때에도 즉시실행함수가 많이 활용된다. 숫자를 세기 위한 Counter 싱글튼 객체를 구현해보며 알아보자!

const Counter = (function counterIIFE() {
    // 현재 counter 값을 저장하기 위한 변수
    let current = 0;
    return {
        getCurrentValue: function () {
            return current;
        },
        increaseValue: function () {
            current = current + 1;
            return current;
        },
        decreaseValue: function () {
            current = current - 1;
            return current;
        },
    };
})();
console.log(Counter.getCurrentValue()); // 0
console.log(Counter.increaseValue()); // 1
console.log(Counter.decreaseValue()); // 0
  • 즉시실행함수의 반환 객체에 현재 current 값을 출력하는 getCurrentValue 함수, 현재 current 값에 1을 더하는 increaseValue 함수 그리고 현재 current 값에 1을 빼는 decreaseValue 함수를 정의했다.
  • 전역에서 반환 객체의 함수를 통해 current 값을 얻거나 수정 할 수 있다.
  • 위의 예제에서 current 변수는 private 하기 때문에 클로저를 통한 접근 외에는 접근 및 수정이 불가능하다.

Singletons

  • 한 클래스에서 인스턴스를 한개만 생성하도록 제한하는 패턴
  • 인스턴스가 하나도 없는 경우에만 인스턴스를 만드는 메소드를 포함하여 클래스를 생성하는 방법으로 적용할 수 있다.
  • 이미 인스턴스가 존재한다면, 함수는 그 인스턴스의 reference를 리턴하도록 한다.

singletons 패턴이 필요한 때는 아래와 같은 상황이다.

let userModule = (function () {
  let users = [];
  let userId = 0;
  return {
    create: (username, password) => {
      let user = { id: userId, username, password };
      users.push(user);
      userId++;
      return user;
    },
    get: (username) => {
      let targetUser;
      users.forEach((user) => {
        if (user.username === username) {
          targetUser = user;
        }
      });
      return targetUser;
    }
  };
})();
console.log(userModule.create('Julia', 'hello123'));
console.log(userModule.create('Julia', 'hello123'));
console.log(userModule.create('Julia', 'hello123'));
console.log(userModule.create('Paul', 'hello456'));

  • username이 'Julia'인 데이터는 하나만 있어야 하는데, 위 코드를 실행하면 Julia 데이터가 이미 있음에도 불구하고 계속 새로 생성한다.
let userModule = (function () {
  let userAddress = {};
  let users = [];
  let userId = 0;
  return {
    create: (username, password) => {
      if (!userAddress.hasOwnProperty(username)) {
        let user = { id: userId, username, password };
        userAddress[username] = users.length + '';
        users.push(user);
        userId++;
        return user;
      } else {
        return users[userAddress[username]];
      }
    },
    get: (username) => {
      return (userAddress[username]) ? users[userAddress[username]] : null;
    }
  };
})();
console.log(userModule.create('Julia', 'hello123'));
console.log(userModule.create('Julia', 'hello123'));
console.log(userModule.create('Julia', 'hello123'));
console.log(userModule.create('Paul', 'hello456'));
console.log(userModule.get('Julia'));
console.log(userModule.get('Paul'));
console.log(userModule.get('Mike'));


코드를 살짝 바꾸면 원하는 결과가 나온다.
아래 코드는 singleton을 적용해 본 다른 예제.

let mySingleton = (function () {
  let instance;
  function init () {
    function privateMethod () {
      console.log("I'm private");
    }
    let privateVariable = "I'm also private";
    let privateRandomNumber = Math.random();
    return {
      publicMethod: function () {
        console.log('The public can see me!');
      },
      publicProperty: "I'm also public",
      getRandomNumber: function () {
        return privateRandomNumber;
      }
    }
  }
  return {
    getInstance: function () {
      if (!instance) {
        instance = init();
      }
      return instance;
    }
  }
})();
let singleA = mySingleton.getInstance();
let singleB = mySingleton.getInstance();

위와 같이 mySingleton이 리턴하는 객체의 getInstance 메서드는 instance 변수에 아무것도 할당되지 않았을 대는 init 함수를 실행하여 리턴되는 객체를 instance 변수에 할당하고, 만약에 이미 instance 변수에 객체가 할당되어 있다면 할당되어있는 인스턴스 변수의 값을 리턴한다.

Factory Pattern

  • 객체를 생성할 때 고려되는 방법 중 하나이다.
  • factory 패턴은 비슷한 객체를 공장에서 찍어내듯 반복적으로 생성할 수 있게 하는 패턴이다.
  • 이 때, new 키워드를 사용한 constructor 함수가 아닌 그냥 일반함수에서 객체를 반환하는 것을 factory function이라고 한다.
profile
persistently

0개의 댓글