객체를 객체답게의 기준

2024년 2월 23일

이는 정답이 적혀 있는 글이 아닙니다. 저의 주관적인 판단이며, 고민의 기록을 남겨놓기 위해 기재해놓습니다.

우테코 Lv1 에서의 두번째 미션인 로또의 수업 과정에서 구매한 로또와, 당첨 번호에 대한 중복된 유효성 검증을 제거 해줄때 조합으로 해결해주는 방법을 배울수 있었다.

- Lotto Class

export default class LottoNumber {
  #numbers;

  constructor(numbers) {
    this.#numbers = numbers.sort((a, b) => a - b);
    this.#validateLottoNumbers();
  }

  has(targetNumber){
    if(this.#numbers.includes(targetNumber)){
      throw new Error(ERROR_MESSAGE)
    }
  }

- WinningLotto Class

class WinningLotto {
  constructor(winningLotto, bonusNumber) {
    if (winningLotto.has(bonusNumber)) {
      throw new Error("보너스 번호는 당첨 번호와 중복될 수 없습니다.");
    }

    this.winningLotto = winningLotto;
    this.bonusNumber = bonusNumber;
  }
}

new WinningLotto(new Lotto([1,2,3,4,5,6]), 7);

위 처럼 아예 Lotto 클래스 자체에서 직접 보너스 넘버에 대한 중복을 판별해주는 has 라는 메서드를 만들어주는 것이다.

이러한 조합을 이용한 구현은 경험해보지 못했어서 이번 수업을 통해 새롭게을 배울 수 있었다.


하지만 본인은 프리코스때도 그렇고, 단일 로또는 단일 로또, 당첨 번호는 당첨 번호 (로또 번호 + 보너스 번호) 이기 때문에 따로 관리해주어야지 하지 않나? 라고 생각했다.

하지만 위의 코드 예시처럼 당첨 번호에 한해서는 동일한 유효성 검증을 해주어야하는 것이 맞다고 생각했다.

하지만 위처럼 has 라는 메서드를 만들어주는 것이 아니라, 아예 당첨 번호에 대한 입력값을 받았을때 해당 입력값이 Lotto 클래스를 거치고 해당 값을 get함수로 받아왔다.

class WinningLotto {
  
  constructor(winningNumbers, bonusNumber) {
    this.#winningNumbers = new Lotto(winningNumbers).getLottoNumber();
    this.#bonusNumber = Number(bonusNumber);
    this.validateBonusNumber();
  }

이런식으로 구현한 이유를 정리해보자면

  1. 해당 this.#winningNumbers는 로또 번호이기도 하지만, 당첨 번호로써 로또라는 어플리케이션 내의 비즈니스 로직에서 중요한 도메인이기도 하기 때문에, 필드 값으로 관리해주는것이 맞다고 생각했다. 하지만 당첨 번호는 단일 Lotto 번호에도 포함이 되는 부분이니 인스턴스로 거쳐서 틀을 만들어 준후 (유효성 검증을 거친 후) 해당 값을 get 함수로 가져왔다.

  2. WinningLotto 클래스 내부에서 get 함수를 통해 Lotto 인스턴스의 필드 값을 가져온다고 하더라도, 외부에서 해당 값에 대한 조작을 하지는 않는다. (값의 순수성이 보장 된다.)

  3. 또 저 has 메서드와 비교를 해보자면, Lotto 클래스는 단일 로또에 대한 인스턴스인데, targetNumber라는 외부 인자를 받아서 유효성 검증을 해주는 것은 Lotto 클래스가 아니라 WinningLotto 클래스에서 해주는것이 더 맞다고 생각했다.


하지만 여기서 get함수를 통해서 값을 가져온다는 것과, 수업시간에 배웠던 객체는 객체스럽게라는 키워드와 상충되지 않나? 라는 생각 또한 들었다.

그와 동시에 has 라는 메서드를 만들어서 해당 인스턴스 자체에서 동작하도록 하는 관점에선 동의하는 바이지만, 저 보너스 부분에 대한 중복 검사를 Lotto 에서 해주는것이 맞을까 ? 라는 생각이 계속 든다.

뭐가 더 나은것인지는 더 고민해보아야할 부분 같은데, 일단 테코블의 글 링크로 갈음해본다.

getter를 사용하는 대신 객체에 메시지를 보내자


- 리뷰어의 피드백

단순히 특정 객체의 프로퍼티를 외부에서 접근해서 쓰고 안쓰고라기 보다는 어떤 로직을 연산하는 책임을 어디에 두어야 하느냐가 주된 관점이 아닐까 싶습니다. 아래의 예시처럼 사용처에서 호출하는 객체의 프로퍼티를 불러와서 직접 연산을 처리하는 것과 호출하는 객체에 연산을 위임하는 차이가 아닐까요?

// 1.
const instance = new Class()

return instance.a + instance.b

// 2.
const instance = new Class()

return instance.getTotal() // instance.a + instance.b를 반환

어떤 로직을 연산하는 책임을 어디에 두어야 하느냐가 주된 관점이 아닐까 싶습니다. 라는 말에서 힌트를 얻었다.

get 함수를 쓰느냐 마느냐가 중요한것이 아니라, 결국 get 함수를 써서 값을 가져오더라도 이 값의 연산을 책임에 어디에 둘것이느냐를 더 고심해보아야 할것 같다.