Study

토끼책을 읽고 - 메시지를 믿을 수 있을까? (2)

Hyunec 2021. 3. 8. 22:14

[v4] 조금 다른 예를 들어보겠습니다.

/*
  1. 제품을 만들기 위한

  부품 (인자로서 사용됨을 표시하기 위해 Dto 표기)
 */
@Setter
@Getter
@NoArgsConstructor
public class PartDto {

  // 모든 곳에서 필요합니다.
  private Boolean steal;

  // 컴퓨터와 핸드폰에서 필요합니다.
  private Boolean semiconductor;

  // 선풍기에서 필요합니다.
  private Boolean propellers;

  // 선풍기와 핸드폰에서 필요합니다.
  private Boolean button;
}

/*
  1. 제품을 만들기 위한 부품이 있는지 확인하고

  있으면 만드는 서비스
 */
public class ProductService {

  public boolean createComputer(PartDto partDto) {
    if (partDto.getSteal() && partDto.getSemiconductor()) {
      // do work...
      return true;
    }

    return false;
  }

  public boolean createFan(PartDto partDto) {
    if (partDto.getSteal() && partDto.getPropellers() && partDto.getButton()) {
      // do work...
      return true;
    }

    return false;
  }

  public boolean createPhone(PartDto partDto) {
    if (partDto.getSteal() && partDto.getSemiconductor() && partDto.getButton()) {
      // do work...
      return true;
    }

    return false;
  }
}

으악! 요즘 누가 이렇게 코드를 짜나!

하지만 종종 이런 코드를 보곤 하죠

이상적인 프로그래밍과는 조금 거리가 있지만 Dto를 공통적으로 쓰고, Dto 생성 후 부분적으로 Setter 사용, (전 글에서 나온 것과 같이) 메시지 인자를 수신받는 곳에서 검증합니다. 만약 제품이 늘어난다면 검증 로직을 어디에 만들어야 될까요?

[v5] 저는 검증 로직을 모으는 것을 선호합니다.

/*
  1. 제품을 만들기 위한

  부품 (인자로서 사용됨을 표시하기 위해 Dto 표기)
 */
@Setter
@Getter
@NoArgsConstructor
public class PartDto {

  public enum PartOf {
    COMPUTER,
    FAN,
    PHONE,
    UNKNOWN
  }

  private PartDto.PartOf partOf;

...

  public boolean naming(PartDto.PartOf partOf) {
    if (partOf.equals(PartOf.COMPUTER) && this.getSteal() && this.getSemiconductor()) {
      this.partOf = PartOf.COMPUTER;
    }

    if (partOf.equals(PartOf.FAN) && this.getSteal() && this.getPropellers() && this.getButton()) {
      this.partOf = PartOf.FAN;
    }

    if (partOf.equals(PartOf.PHONE) && this.getSteal() && this.getSemiconductor() && this.getButton()) {
      this.partOf = PartOf.PHONE;
    }

    if (this.partOf == null) {
      this.partOf = PartOf.UNKNOWN;
      return false;
    }

    return true;
  }
}

/*
  1. 제품을 만들기 위한 부품이 있는지 확인하고

  있으면 만드는 서비스
 */
public class ProductService {

  public boolean create(PartDto partDto) {
    // do work...
    return true;
  }
}

/*
  1. 제품을 만드는 서비스를 호출하는

  객체
 */
@RequiredArgsConstructor
public class Factory {

  private final ProductService productService;

  public void createProduct() {
    PartDto part1 = new PartDto();
    part1.setSteal(true);
    part1.setPropellers(true);
    part1.setButton(true);

    if (part1.naming(PartOf.FAN)) {
      System.out.println("part1 로 FAN 만들기: " + productService.create(part1));
    }

    PartDto part2 = new PartDto();
    part2.setSteal(true);
    part2.setPropellers(true);
    part2.setButton(true);

        // 검증에 실패해 제품 생산을 위한 요청 (메시지) 를 보내지 않습니다.
    if (part2.naming(PartOf.COMPUTER)) {
      System.out.println("part2 로 COMPUTER 만들기: " + productService.create(part2));
    }
  }
}

특히 Dto로 모으는 것을 선호합니다.

1. 객체에 역할이 있는 것처럼, 메시지도 역할을 줄 수 있다.
2. 메시지를 보내는 것도 비용이다. 사전에 검증되어 전달된 메시지는 신뢰한다. (내부용 로직일시에만)
3. 비슷한 성격의 로직만 모음으로서 생각의 범위를 줄일 수 있다.

물론 다른 해결 방법도 여러 가지가 있습니다.

Dto를 더 분류할 수도 있고, 검증만을 하는 서비스를 만들 수도 있고, 만약 API 호출 형태였다면 수신 측에서 검증하는 전 구조가 더 나았겠죠.

무엇이 되었든 중요한 건 약속된 구조로 레이어를 나누고 팀에 공유하는 것이라고 생각합니다.

목표는 지속 가능한 개발이니까요.

회고

사실 이 글을 쓰게 된 계기는 대가의 이론과 내 능력 사이의 괴리, 거기서 오는 설득력의 부족, 배움이 필요한 기술과 한정된 일정에서 오는 이런저런 안 좋은 생각 중에 문득 눈에 들어와서였습니다. 만약 대가의 이론이기에 문자 그대로를 적용해야 된다는 게 기저에 있다면, 재해석과 설득은 너무 힘든 과정이 됩니다.

 

다행히도 옛날이었으면 무심코 지나쳐갔을 내용을 코드로 설명할 수 있는 능력이 생겼고, 내용을 정리하는 과정에서 나쁜 생각은 많이 사라진 것 같습니다.

 

지속 가능한 개발을 유지하되 그 기준을 상향시키자!

회고의 결론은 항상 같은 느낌.

'Study' 카테고리의 다른 글

mongoDB tutorials  (0) 2021.05.18
API Versioning 전략  (0) 2021.04.14
토끼책을 읽고 - 메시지를 믿을 수 있을까? (1)  (0) 2021.03.08
비동기 테스트 만들기  (0) 2021.02.25
Querydsl 로 가는 길  (0) 2020.12.02