Study

TDD DDD BDD 기본

lovineff 2021. 4. 6. 12:16

TDD (Test Driven Development)

[의미]

테스트 주도 개발

매우 짧은 개발 서클의 반복을 갖는 소프트웨어 개발 프로세스

새로운 기능에 대한 자동화된 테스트케이스를 작성하고 해당 케이스를 통과하는 가장 짧고 가독성이 좋고 유지보수성

뛰어난 코드를 작성

(실패하는 테스트 케이스를 먼저 작성한 후에 개발을 진행한다!)

일단 테스트를 통과하는 코드를 작성하고 상황에 맞게 리팩토링


요구되는 기능에 대한 테스트 케이스를 작성하고, 짧고 가독성이 좋고 유지보수성이 뛰어난 코드를 작성.

이후 상황에 맞게 리팩토링하고, 실제 코드로 작성한다.

 

[장점]

요구사항 이해도 향상

새로운 기능을 추가하기 위해선 테스트 코드를 먼저 작성해야함.

테스트 코드를 작성하기 위해서 요구사항과 명세를 분명히 이해하고 있어야 한다.

이로써 코드를 작성하기 전에 요구사항에 집중할 수 있게된다.


기존 기능 정상 동작 확인 가능

새로운 기능 개발시 기존 기능이 정상 동작하는지 확인이 가능하다.


코드 리팩토링

코드 작성시 프로젝트 네이밍 규칙에 맞춰야 하며, 동시에 확장성까지 고려해야한다.

코드가 많아지는 경우 기능 별로 함수를 나누게 되는데 그 과정에서 테스트 코드가 중심을 잡아줄 수 있다.

리팩토링과 동시에 테스트를 수행하면 의존성 문제도 해결이 가능하다.

 

[단점]

코드량 증가

비지니스 로직, 코드 디자인에도 많은 시간이 소요되는데 테스트 코드를 먼저 작성해야하므로 코드량과 생산 시간이 증가한다.

그러므로, 개발 시간이 여유롭지 않은 프로젝트에서는 도입이 매우 어려울수 있다.

진입장벽

테스트 코드 작성에 대한 학습이 필요하며, 익숙해지는데 많은 시간이 필요하다.

주객전도

테스트 코드를 작성하는데 어려움이 생기는 부분이 있을수가 있으며, 이런 상황에서 테스트 코드를 위해 구조를 변경해야 하는 경우가 발생할 수 있다.

모든 상황에 대해 테스트 코드를 작성하게 되면 배보다 배꼽이 더 커지는 경우가 발생할 수 있다.

 


DDD (Domain Driven Design)

도메인

사전적 의미로 영역, 집합

유사한 업무의 집합 (DDD에서 도메인은 비지니스 도메인을 의미)

 

[특징]

소프트웨어의 복잡성 최소화

데이터 중심의 접근법을 탈피하여 순수한 도메인의 모델과 로직에 집중

모든 문서와 코드에 동일한 표현과 단어로 구성된 단일화된 언어체계 구축

분석, 설계, 구현까지 통일된 방식으로 커뮤니케이션 가능

 

DDD의 핵심 목표는 "Loosly coupling", "High cohesion"

  (어플리케이션 또는 그 안의 모듈간의 의존성은 최소화하고, 응집성은 최대화)

 

기술보다 도메인이 더 높은 우선순위를 가져야함

분석 모델링부터 코드까지 항상 같이 움직이는 모델 구조를 지향

 

도메인 모델의 영역 (계층형 아키텍)

PRESENTATION LAYER

표현 영역 또는 UI 영역. 사용자의 요청을 받아 응용 영역에 전달하고, 응용 영역의 처리 결과를 다시 사용자에게 보여주는 역할을 한다. 

(Controller 영역, DispatcherServlet에게 요청과 응답을 전달하는 역할)

사용자가 시스템을 사용할 수 있는 (화면) 흐름을 제공하고 제어

사용자의 요청을 알맞은 응용 서비스에 전달하고 결과를 사용자에게 제공한다.

사용자의 세션을 관리한다.

APPLICATION LAYER

응용 영역. 시스템이 사용자에게 제공해야 할 기능을 구현한다. (Service 영역)

사용자의 요청을 처리하기 위해 리포지터리로부터 도메인 객체를 구하고, 도메인 객체를 사용한다.

로직을 직접 수행하기보다는 도메인 모델에 로직 수행을 위임한다.

도메인 객체 간의 실행 흐름을 제어

*트랜잭션 처리

도메인 영역에서 발생시킨 이벤트를 처리

 

*트랜잭션이란? 데이터베이스의 상태를 변화시키기 해서 수행하는 작업의 단위.

(예) (게시판 사용자가 게시글을 작성하고, 올리기 버튼을 누른다 -> 그 후에 다시 게시판에 돌아왔을때, 자신의 글이 포함되어 업데이트된 게시판을 보게 된다) 는 일련의 과정.

DOMAIN LAYER

도메인 영역. 도메인 모델을 구현한다. (이름, 주소, 상품, 주문서 등)

INFRASTRUCTURE LAYER

구현 기술에 대한 것을 다룬다. (외부 API, 데이터베이스, 외부 라이브러리 사용 등)

 

도메인 기본 요소

Entity

  • 식별자를 가진다.
  • 각 엔티티는 서로 다른 식별자를 갖는다.
  • Set 메서드를 넣지 않는다.
  • set 메서드는 도메인의 핵심 개념이나 의도를 코드에서 사라지게 한다.
//1. 배송정보를 바꾸는 메서드
changeShippingInfo() vs setShippingInfo()
//2. 결제를 완료했다는 설정을 하는 메서드
completePayment() vs setOrderState()
// 전자 메서도가 로직의 흐름을 이해하기 쉽다.

 

Value

불변을 원칙으로 한다.

데이터 값을 객체로 대체 가능

식별자가 존재하지 않는다.

 

Agrregate

관련된 객체를 하나로 묶음

군집에 속한 객체를 관리하는 루트 엔티티를 갖는다(상위 수준에서 도메인 모델 간의 관계를 파악할 수 있다.)

애그리거트에 속한 객체는 유사하거나 동일한 라이프사이클을 갖는다

각 애그리거트는 자기 자신을 관리할뿐 다른 애그리거트는 관리하지 않는다.

(예) 주문 애그리거트는 배송지 변경, 주문 상품 변경 등 자신은 관리하지만 회원 비밀번호 변경, 상품 가격 변경 등은 하지 않는다.

*주문(Order) 애그리거트: 주문, 배송지 정보, 주문자, 주문목록, 총 결제금액의 하위 모델이 있다. 이때 이 하위 개념을 표현한 모델을 하나로 묶어서 '주문'이라는 상위 개념으로 표현할 수 있다.

즉, 주문은 Root Aggregate가 된다.

Root Aggregate를 중점으로 종속되어 있는 엔티티들은 동일한 라이프사이클(하나의 트랜잭션)을 가진다.

 

Repository

엔티티나 밸류가 요구사항에서 도출되는 도메인 모델이라면, 리포지터리는 구현을 위한 도메인 모델

애그리거트 단위로 도메인 객체를 저장하고 조회하는 기능을 정의한다.

애그리거트를 구하는 리포지터리 메서드는 완전한 애그리거트를 제공해야 한다.

리포지터리가 완전한 애그리거트를 제공하지 않으면, 필드나 값이 올바르지 않아 애그리거트의 기능을 실행하는 도중에 NullPointerException과 같은 문제가 발생하게 된다.

리포지터리는 애그리거트(루트) 단위로 존재하며 테이블 단위로 존재하는 것이 아니다.


Domain Service

한 애그리거트에 넣기 애매한 도메인 개념을 구현하려면 애그리거트에 억지로 넣기보다는 도메인 서비스를 이용해서 도메인 개념을 명시적으로 드러내면 된다.

응용 영역의 서비스가 응용 로직을 다룬다면 도메인 서비스는 도메인 로직을 다룬다.

도메인 영역의 애그리거트나 밸류와 같은 다른 구성요소와 비교할 때 다른 점은 상태 없이 로직만 구현한다.

서비스를 사용하는 주체는 애그리거트가 될 수도 있고 응용 서비스가 될 수도 있다.

애그리거트 메서드를 실행할 때 도메인 서비스를 인자로 전달하지 않고 반대로 도메인 서비스의 기능을 실행할 때 애그리거트를 전달하기도 한다.

특정 기능이 응용 서비스인지 도메인 서비스인지 감을 잡기 어려울 때는 해당 로직이 애그리거트의 상태를 변경하거나 애그리거트의 상태 값을 계산하는지 검사해 보면 된다.

(예) 계좌이체 로직은 계좌 애그리거트의 상태를 변경한다. 결제 금액 로직은 주문 애그리거트의 주문 금액을 계산한다.

이 두 로직은 각각 애그리거트를 변경하고 애그리거트의 값을 계산하는 도메인 로직이다.

도메인 로직이면서 한 애그리거트에 넣기 적합하지 않으므로 이 두 로직은 도메인 서비스로 구현하게 된다.

 

 


BDD (Behavior Driven Development), 행동 주도 개발

TDD에 DDD의 스타일을 적용하여 탄생

테스트 케이스 자체가 요구사양이 되도록 개발하는 방식

TDD에서는 단위 테스트로 작성된 테스트 코드의 가독성을 높고 테스트 케이스에 대한 문서를 작성했으나, BDD는 이것을 통합테스트와 시나리오 테스트까지 확장하여 각각에 해당하는 문서를 대체

테스트 메소드의 이름을 "이 클래스가 어떤 행위를 해야한다"라는 식의 문장으로 작성하여 행위를 위한 테스트에 집중한다.

 

 


번외

OOP(Object Oriented Programming) vs DDD

Object vs Domain

객체(Object)는 추상화, 구체화할 수 있는 특정 요소만을 표현

도메인(Domain)은 사용자가 사용하는 모든 것을 설명

 

“고양이는 사과를 먹는다.”

객체의 관점에서는 “고양이”와 “사과”를 표현할 수 있고, “먹는다”는 객체가 하는 행위로 별도로 표현합니다.

도메인의 관점에서는 “고양이”, “사과”, “먹는다”, “고양이는 사과를 먹는다.” 모두 각각 도메인이라고 할 수 있습니다

 

도메인은 사용자에 따라 계속 바뀔수 있고, 형태가 고정되어 있지 않다.