우테코 프리코스 6기 4주차 회고

user profile img

신현호

Chat

우테코 프리코스 6기

Post Thumbnail

목차

    서론

    대망의 마지막 주차..! 저는 내심 작년에 나왔던 다리 건너기였으면 좋겠다고 생각을 했는데요.. 아쉽게도 다른 문제였습니다!
    다리 건너기 자체도 난이도가 꽤나 있었던 문제라고 생각해서 마지막 주차 미션으로 나올법도 하다고 생각을 했는데, 이게 왠걸.. 더 어려운 문제가 저를 반겨주었습니다!

    바로 크리스마스 프로모션.. 이전까지의 문제와는 궤를 달리하는 난이도라고 생각합니다. 이전 미션들은 작년에도 나왔던 미션이기에 변별력을 주기에는 어렵다고 생각하셨나봅니다.
    요구조건부터 고봉밥처럼 꽉꽉 눌러 담아져 있는데, 요구사항을 분석하는 것 부터가 어려웠습니다.

    또 이전미션에서 받았던 피드백까지 코드에 녹여내는데 정말 많은 어려움이 있었던 것 같네요!
    저는 이전 문데들은 푸는데 자체는 시간이 그렇게 오래걸리진 않았으나, 이번 미션의 경우는 일단 기능을 완성하는 것 부터가 난이도가 있어서 오래걸렸 던 것 같네요..

    이게 마지막 포스팅은 아니고, 최종 후기글까지 글 하나 추가로 작성할 예정입니다.
    그럼 서론은 여기까지 하고 이번 마지막 미션에서 제가 학습한 부분에 대해서 적어보도록 하겠습니다 :)

    이번에 학습한 내용

    싱글톤 패턴

    싱글톤 패턴에 대해서 들어보신 적 있으신가요? 많이 알려진 디자인 패턴일 수 있지만 간략하게 설명을 해 보겠습니다!

    객체의 인스턴스가 오직 한 개만 생성되어야 한다.

    설명이 생각보다 간단하죠? 근데, 오직 한 개만 생성되어야 한다는게 어떤 의미일까요?
    자바스크립트로 객체를 만들어 보신 분들이라면 생각나는게 있을 거라고 생각합니다.

    자바스크립트에서 객체를 만드는 방법은 다양한데요, 가장 대표적으로 두개가 있을 것 같습니다!

    js

    // case 1.
    const obj = { ... };
    
    // case 2.
    class Something { ... }
    
    const obj = new Something();
    

    ( new Object() 의 경우는 Airbnb 컨벤션상 권장하지 않는 방법이기때문에 제외했습니다.)

    클래스의 경우 생성되는 오브젝트, 즉 인스턴스는 똑같은 클래스를 통해 생성되었어도 다른 객체입니다.
    하지만 리터럴 구문을 단 한개의 객체를 생성합니다.

    한 마디로 리터럴 구문을 통해 싱글톤 디자인 패턴의 객체를 생성할 수 있다는 것 입니다!

    그렇다면, 싱글톤 디자인 패턴을 사용함으로써 얻는 장점은 무엇이 있을까요?

    • 최초 한번만 생성하기때문에 메모리 낭비를 방지하는 효과가 있으며, 이미 생성된 인스턴스를 활용하는 것 이기 때문에 속도 측면에서도 이점이 있다.
    • 싱글톤 인스턴스는 전역으로 사용되는 인스턴스이기 때문에 다른 객체들과 데이터 공유가 쉽다.
    • 인스턴스가 단 한개만 존재함을 보증할 수 있다.

    저는 저번 로또 문제에서의 피드백을 통해 이번 크리스마스 프로모션 미션에서 서비스 레이어에 이 싱글톤 패턴을 적용했습니다.
    서비스 레이어가 여러 인스턴스로 존재해야 하는 객체가 아니라고 생각했고, 그렇다면 싱글톤 패턴을 적용하여 작성하는것이 좋다고 생각했기 때문입니다.

    하여, 싱글톤 패턴을 적용하여 다음과 같이 코드를 작성했습니다.

    js

    import MonthlyEvent from '../domain/MonthlyEvent.js'
    import deepFreeze from '../utils/deepFreeze.js'
    
    // 날짜, 총 주문 금액, 주문 리스트
    const EventService = deepFreeze({
      /**
       * @type {MonthlyEvent} - MonthlyEvent 인스턴스
       */
      monthlyEvent: MonthlyEvent.of(),
    
      /**
       * @param {string} day - 입력된 방문 일자
       */
      initialize(day) {
        this.monthlyEvent.validate(Number(day))
      },
    
      /**
       * @param {import('../utils/JSDocs.js').orderList} orderList - 주문 목록
       * @param {number} orderTotal - 주문 금액 합계
       * @returns {import('../utils/JSDocs.js').appliedEvent} 적용된 이벤트 목록과 총 할인 금액
       */
      applyEvents(orderList, orderTotal) {
        return this.monthlyEvent.apply(orderList, orderTotal)
      },
    
      /**
       * @param {import('../utils/JSDocs.js').eventList} eventList - 적용된 이벤트 목록
       * @param {number} orderTotal - 주문 금액 합계
       * @param {number} totalEventDiscount - 적용된 이벤트 할인 금액 합계
       * @returns {import('../utils/JSDocs.js').totalResults} 이벤트 뱃지와 최종 결제 예정 금액
       */
      totalResults(eventList, orderTotal, totalEventDiscount) {
        return {
          payTotal: this.monthlyEvent.payResult(eventList, orderTotal),
          badge: this.monthlyEvent.badge(totalEventDiscount),
        }
      },
    })
    
    export default EventService
    

    위에서는 싱글톤 패턴의 장점만 적어놨는데, 인스턴스가 하나만 생성되기때문에 생기는 문제들도 다수 존재합니다. 이를 고려하시면 싱글톤 패턴에 대한 이해를 더욱 높일 수 있습니다.

    객체의 깊은 동결

    자바스크립트에서 객체를 동결 시킬 때 사용하는 함수가 있습니다. 바로 Object.freeze() 입니다.
    하지만 이 함수에는 치명적인 단점이 존재하는데, 바로 객체 내부의 객체는 동결시킬 수 없다는 점 입니다.

    예시를 들어보겠습니다.

    js

    // case 1.
    const obj = Object.freeze({ a: 1, b: 1 })
    
    // error!
    obj.a = 2
    
    // case 2.
    const obj = Object.freeze({ a: { key: 1, value: 2 }, b: 1 })
    
    // ???
    obj.a.key = 2
    

    케이스 1의 경우에는 값을 변경할 수 없지만, 케이스 2의 경우에는 값이 정상적으로 변경되는걸 알 수 있습니다.
    따라서, 객체 내부의 객체를 동결시키기 위해서는 내부의 원소까지 다시 동결을 시켜줘야합니다.

    여기서 나오는 개념이 바로 deepfreeze 입니다.

    이번 미션의 경우 객체의 형태가 이전 미션보다 복잡하게 구성되기에 (설계의 문제일 수 있습니다) 객체의 불변성을 유지시켜주기 위해서 deepfreeze를 직접 구현하여 사용했습니다.

    js

    /**
     * @param {object} target - 동결시킬 객체
     * @returns {object} 깊은 동결된 객체
     */
    const deepFreeze = (target) => {
      if (target && typeof target === 'object' && !Object.isFrozen(target)) {
        Object.freeze(target)
        Object.keys(target).forEach((key) => deepFreeze(target[key]))
      }
    
      return target
    }
    
    export default deepFreeze
    

    참고 자료

    레포지토리

    Profile Image

    신현호

    Frontend Developer

    프론트엔드 개발자를 꿈꾸고 있는 대학생입니다. 끊임없이 배우고 성장하는 개발자가 되기 위해 노력하고 있습니다.

    우테코 프리코스 6기

    총 5개의 포스트가 존재합니다.