HwangJerry 2023. 7. 25. 15:49

백엔드를 준비하는 정말 많은 사람들이 김영한님의 스프링 강의를 먼저 듣고 시작하게 됩니다. 근데 그 강의 내용 중에 save 메서드가 pk 값을 반환 타입으로 가지는 것을 볼 수 있습니다. 처음에는 "그렇구나" 하고 넘어갔었는데, 나중에 어디선가 CQRS를 듣고, 그리고 CQS를 듣고 이에 갑자기 의문이 생겼습니다.

save메서드는 커맨드 메서드인데, 조회를 위한 pk값을 반환해주는 것은 CQS 위반이 아닌가?

많은 신입 백엔드의 스승님인 김영한 님이 잘못된 것을 남용했을 것 같지 않기에 한번 알아보고자 했습니다.

 

CQS란?

CQS는 Command Query Separation을 의미하며, 데이터를 변경하는 명령(command)과 데이터를 조회하는 쿼리(query)를 철저히 분리하라는 디자인 패턴입니다.

 

CQS를 조금만 검색해보면 다음과 같은 말을 확인할 수 있습니다.

  • command : 객체의 상태를 변경하는 명령은 반환 값을 가질 수 없다.
  • query : 객체의 정보를 반환하는 쿼리는 상태를 변경할 수 없다.

위 멘트가 문제의 발단이었습니다.

@Transactional
public Item save(Item item) {
    return itemRepository.save(item);
}

이를 보면 단순한 service로직인데, save를 하면서 반환값으로 저장한 아이템 객체를 반환하는 것을 알 수 있습니다. 이를 보면 심지어 spring data 프레임워크에서 제공하는 save 기능조차도 커맨드 메서드임에도 item 객체를 반환하게끔 구현되어 있는 것을 알 수 있습니다.

 

설마, 김영한님과 spring data를 만든 사람들이 잘못된 방법을 적용하고 있던 것일까?

역시 아니었습니다. 제가 단단히 잘못 이해하고 있었습니다.

 

가령 예를 들어 아래와 같은 메서드가 있다고 가정해봅시다.

boolean isSafe() {
    if (danger) {
      removeDanger(); // 위험 요소 제거
      return false;
    }
    return true;
}

isSafe는 안전한지를 조회해주는 쿼리 메서드입니다. 하지만 내부 로직을 보면 위험이 존재할 경우에는 그것을 제거한 뒤 위험하다고 알려주게 됩니다. 즉, 이 메서드에게 요청하지도 않은 일을 하고 있는 겁니다. 이렇게 되면 이 메서드를 사용하는 사람은 어디서부터 이 객체가 안전한 상태로 돌입했는지 이해하기 어려울 것입니다.

 

즉, 결론은 다음과 같습니다.

CQS의 핵심은 반환값의 유무나 그 값에 있는 것이 아니라, 커맨드 메서드나 쿼리 메서드는 내부 로직에서 그 메서드가 담당한 책임만을 수행해야 하는 것을 의미합니다.

 

 

참고: 

 

Spring Data 모듈의 save() 는 CQS 를 지키지 않는 것일까? with 참조투명성

목차 배경 Spring Data 는 CQS 를 지키지 않는 것일까? CQS 란? 참조 투명성 배경 최근에 발행했던 글이 하나가 있다. CQRS 패턴에 대한 오해 풀기 라는 글에서 CQS 에 대해서 잠깐 언급한다. [Architecture Pat

wonit.tistory.com