본문으로 바로가기
728x90
반응형

목차

     

    개요

     

    이 챕터는 스프링 개발자가 많이 저지르는 “구조적 실수”에 대해서 다룬다.

     

    개발에는 정답이 없지만 “이렇게 개발하면 유지보수나 확장성 관점에서 좋지 못하다”라고 알려진 안티패턴은 존재한다.

     

    소개할 안티패턴은 기초적이지만 누구나 실수할 법한 내용이다.

     

    6.1 스마트 UI

    : 스마트 UI 패턴은 에릭 에반스의 저서 도메인 주도 설계에서 소개돼 유명해진 안티패턴이다.

    • 스마트 UI 패턴은 다음과 같은 특징을 가진 코드를 말한다.
      • 스마트 UI는 데이터 입출력을 UI 레벨에서 처리한다.
      • 스마트 UI는 비즈니스 로직도 UI 레벨에서 처리한다.
      • 스마트 UI는 데이터 베이스와 통신하는 코드도 UI 레벨에서 처리한다.

     

    : 백엔드 개발자가 왜 UI를 이야기하는 것인가 ?

    • 스마트 UI의 개념이 나오는 것은 백엔드 개발자도 “백엔드 개발자의 UI”를 신경써야 하기 때문이다.
      • 즉, 백엔드 API를 뜻한다.
    • 컨트롤러는 API만드는 컴포넌트이다.
      • 즉, 컨트롤러는 스프링에서 UI를 만드는 도구라고 볼 수 있다.

     

    : 일반적으로 UI는 사용자의 입출력을 위한 창구로만 사용돼야 한다.

    • 입력을 받고 이를 뒷단으로 넘겨 비즈니스 로직을 실행하는 역할 정도만 해야 한다.
    • 스마트 UI에서 말하는 “스마트”는 UI 코드가 똑똑하게 동작해서가 아닌 지나치게 많은 일을 처리하고 있는 상황을 비꼬는 표현이다.

     

     

    : 컨트롤러의 역할

    • API 호출 방식을 정의한다.
    • 어떤 비즈니스 로직을 실행할 것인지 결정한다.
    • API 호출 결과를 어떤 포맷으로 응답할지 정의한다.
    • 컨트롤러의 가장 큰 역할은 엔드포인트를 정의하고 API 사용자의 요청을 받아 그 결과를 응답 포맷에 맞춰 반환하는 것이다.
    • 컨트롤러에 비즈니스 로직이 있어서는 안된다. 데이터베이스 관련 로직이 있어도 안 된다.
      • 이러한 로직을 컨트롤러에 두는 것은 컨트롤러의 목적과 다르다. 이는 명백히 책임을 과하게 부여하는 행동이다.

     

    6.2 양방향 레이어드 아키텍처

    : 양방향 레이어트 아키텍처는 레이어들의 의존 관계에 양방향 의존이 발생하는 경우를 칭한다.

    • 양방향 레이어트 아키텍처는 레이어드 아키텍처를 지향하는 프로젝트에서 많이 발생하는 안티패턴이다.

     

    : 레이어드 아키텍처의 장점

    • 단순하고 직관적인 구조
      • 어떤 컴포넌트를 개발하거나 찾아야할 때 컴포넌트를 어디에 위치시켜야할지 고민할 필요가 없다.
    • 레이어드 아키텍처는 “레이어 간 의존 방향은 단방향을 유지해야한다”라는 것이다.

     

    : 레이어간 양방향 참조가 생겼을 떄 이를 해결하는 방법

    • 이 절에서는 크게 두 가지에 대해 소개함

     

    6.2.1 레이어별 모델 구성

    : 첫 번째 해결방법은 레이어별 모델을 따로 만드는 것

    • 예를 들어 Presentation에서 PostCreateReqest를 만들었다면 Business에서는PostCreateCommand를 만드는 것
      • 다음과 같은 명명 규칙을 사용할 수 있음
        • ~Request 클래스는 API 요청을 처리하는 모델이다.
        • ~Command 클래스는 서비스에 어떤 생성, 수정, 삭제 요청을 보낼 때 사용하는 DTO이다.
    • 이 방법을 사용함으로 얻는 장점
      • 클라이언트가 API 요청을 보내시는 시점의 요청 본문과 서비스 컴포넌트에서 사용하는 DTO를 분리할 수 있게 된다는 점
    • 이 방법을 사용함으로 얻는 단점
      • 작성해야 하는 코드의 양이 늘어난다. 레이어별로 각기 다른 DTO를 갖게 한다면 CRUD DTO * 레이어개수 만큼 늘어난다.
      • 작성해야하는 코드의 양이 늘어나다는 것은 “조직 관점에서 비용이 증가한다”는 의미이다.
        • 그러니 모델은 적당히 세분화되고 적당히 통합돼야한다.
        • 그러나 DTO를 어디까지 세분화할 것이냐는 문제에는 마땅한 정답이 없다.

    💡코드 중복과 코드 유사성

    • 코드 중복과 코드 유사성은 다르다 (Post, PostCreateRequest, PostCreateCommand)
    • 필요에 따라 만들어진 코드는 말 그대로 필요하니까 존재하는 것이 당연하다.
    • 중복은 역할과 책임, 구현 등이 비슷할 때를 중복이라 부른다. 그래서 데이터 형태가 유사하다고 해서 중복이라 보기는 어렵다.
      • 목적도 같고 해결 방법이 같을 때 중복이라고 부른다.
    • 유사한 데이터를 여러개 만드는 것을 두려워 하지마라
      • 몇몇 멤버 변수들이 겹친다고 데이터 모델을 어정쩡하게 공유하는 것보다 역할과 책임에 따라 확실하게 모델을 구분하는 편이 훨씬 낫다.

     

    6.2.2 공통 모듈 구성

    : 공통으로 참조하는 코드를 별도의 모듈로 분리하자.

    • 모든 레이어가 단방향으로 참조하는 공통 모듈을 만들고, PostCreateRequest 같은 모델을 거기에 배치하는 것이다.
      • 예를 들어 PostCreateRequest 클래스를 core 패키지로 옮기고 모든 레이어가 이 core 라는 모듈에 의존하도록 변경하는 것
      • 공통 모듈로 분리한다는 전략은 범용적으로 사용할 수 있는 유틸성 클래스들을 한 곳에 모아둘때 유용하다.
    • 그러나 이 방법은 몇 가지 의문점이 생긴다.
      • core 모듈은 레이어라고 봐야하나?
      • core 모듈이 레이어라면 모든 레이어가 바라보는 하나의 레이어를 두는 것은 괜찮은가?
    • 의문점에 대해 명확히 대답하기 어렵다. core 모듈의 역할이 현재로선 모호하기 때문
    • 공통 코드를 한 곳에 모으라는 것은 공통으로 참조할 수 있는 모듈을 만들어 보라는 것이지 공통된 레이어를 만들라는 의미가 아니다.
      • 이러한 맥락에서 core는 모듈이며 레이어가 아니다.
      • 상세한 내용은 8장 “레이어드 아키텍처”와 9장 “모듈”에서 알아보자.

     

    6.3 완화된 레이어 아키텍처

    : 컨트롤러가 리포지터리를 사용하는 것은 괜찮을까 ?

    • 일반적으로 이처럼 2개 이상의 레이어를 건너뛰어 통신하는 구조도 안티패턴으로 분류한다.

     

    : 완화된 레이어 아키텍처

    • 상위 레이어에 모든 하위 레이어에 접근할 수 있는 권한을 주는 구조를 가리켜 완화된 “레이어드 아키텍처”라고 부른다.
    • 표현 그대로 “레이어드 아키텍처이기는 한데 제약을 조금 완화했다”라는 의미
      • “제약”이란 “레이어 간 통신은 인접한 레이어에서만 이루어져야한다”입니다.

     

    : 완화된 레이어드 아키텍처는 왜 안티패턴으로 규정되는가?

    • 스마트 UI 같은 코드가 만들어지기 때문이다.
    • 이런 구조는 기능 개발을 위한 코드가 어디에 어떻게 들어가야 할지 한눈에 파악하기가 힘들다.

     

    6.4 트랜잭션 스크립트

    : 네 번째로 소개할 안티패턴은 “트랜잭션 스크립트”

    • 트랜잭션 스크립트는 비즈니스 레이어에 위치하는 서비스 컴포넌트에서 발생하는 안티패턴이다. 트랜잭션 스크립트는 서비스 컴포넌트의 구현이 사실상 어떤 “트랜잭션이 걸려있는 스크립트”를 실행하는 것처럼 보일 떄를 말한다,

     

    : 트랜잭션 스크립트의 특징

    • 트랜잭션 스크립트 같은 코드는 서비스의 역할이 무엇인지, 아니면 객체지향을 스프링에 어떻게 적용해야한느지 모르는 개발자들이 개발할 떄 많이 만들어진다.
    • 특히나 스프링을 배운지 얼마 안된 개발자들이 코드를 작성하면 이런 유형의 코드가 만들어진다.

     

    : 트랜잭션 스크립트 패턴을 피하려면

    • 트랜잭션 스크립트 패턴을 피하려면 서비스의 역할이 무엇인지 재고해야한다.
      • 7장에서 자세히 알아보자.

     

    : 비즈니스 로직은 어디에 위치해야 하나 ?

    • 단순하게 생각하면 비즈니스 로직은 서비스 컴포넌트에 있는 것이 맞는 것처럼 보인다.
      • 하지만 이는 반은 맞고 반은 틀렸다.
    • 더 나은 답변은 “비즈니스 로직은 도메인 모엘에 위치해야한다” 이다.
      • 능동적인 객체가 협력하는 것을 강조했던 객체지향을 떠올리면 이것은 당연한 이야기이다.
    • ❗비즈니스 로직이 처리되는 “주 영역”은 도메인 모델이어야 한다.
      • 서비스 컴포넌트가 아니다. 서비스는 도메인을 불러와 도메인에 일을 시키는 정도의 역할만 해야한다.

     

    : 애플리케이션의 본질은 도메인이다.

    • 서비스는 도메인을 위한 무대일 뿐이다. 그러니 서비스는 도메인이 협력할 무대만 제공하고 그 이상의 역할을 하지 않는 것이 좋다.
    • 애플리케이션에 도메인 객체가 없다면 도메인 객체를 먼저 만드는 작업을 해야한다.
      • 데이터 덩어리로 간주되는 구조체를 도메인 객체로 만들고, 서비스 로직에 있는 비즈니스 로직을 도메인으로 옮겨야 한다.
      • 디자인 패턴이나 SOLID를 논하기 전에 능동적인 도메인을 만드는 것이 먼저이다. 그러고 나서 도메인끼리 협력하게 만들어야 한다.
    728x90
    반응형