[개발공부] TDD와 Hexagonal 아키텍처

이준영·2025년 2월 25일
0

개발공부

목록 보기
2/3
post-thumbnail

개요

테스트 코드 작성을 효율적으로 하고 싶어 공부를 시작했습니다.
우선 TDD가 언제 어디서 등장하는 개념인지 알아봤습니다.(참고)
그리고 테스트 코드 없이 개발 된 코드의 테스트 코드를 작성해보고 TDD로 일부를 다시 개발도 해보았습니다.
이 과정에서 테스트 우선 개발과 그렇지 않은 개발 방법 간의 차이를 느끼게 되었고 느낀 내용을 정리해보려고 합니다.

테스트 코드 작성

위 강의를 참고하여 TDD 방법으로 코드를 작성해보았으며, 그 과정에서 새롭게 찾아보며 배운 내용은 아래와 같습니다.

  • Hexagonal Architecture
  • Code Coverage(jacoco)
  • Assert

Hexagonal Architecture

강의 초반부에 어딘가 어렵게 느껴지는 부분이 있었는데 알고보니 일반적인 layered-architecture가 아닌 Hexagonal-architecture로 개발하기 때문이었습니다.

Heaxagonal-architecture의 핵심은 도메인이 중심에 있고 도메인으로 향하는 여러개의 port-adapter가 있다는 점으로 이해했습니다. Rest API, 외부 API, Datasource 등의 adapter가 사용가능한 port를 도메인에서 제공하는 점이 장점이 되어 MSA에서 많이 활용 된다고 합니다. 도메인 자체에는 어떠한 외부와의 의존성을 갖지 않고 각각의 port와 adapter를 통한 의존성을 갖게 하여 외부 모듈의 수정이 발생해도 도메인에 영향이 없도록 한다는 점이 장점이라고 이해하였습니다.
하지만 단점도 존재하는데, 크게 와닿았던건 신규 기능 개발 시 전체 레이어에 대한 변경이 이루어지기 때문에 개발 비용이 커지고 비효율적이게 된다는 것이었습니다.

그래서 어떤 케이스에서는 Hexagonal-architecture는 제거(port-adapter 제거)하고 관심사 별로 도메인을 나눈 뒤 DTO를 통한 엄격한 레이어 분리만 적용한 것을 보았습니다.
이 점은 어떤 한 아키텍처에 종속될 필요없이 프로젝트에 맞는 아키텍처의 장점을 끌어다가 적용할 수 있겠다고 생각을 확장시켜주었습니다.

// Layered-architecture
com.example.project
 ├── controller        // Web API (REST Controller)
 ├── service           // 비즈니스 로직 (Service Layer)
 ├── repository        // 데이터 접근 (DAO, Repository)
 ├── entity            // JPA Entity (Domain Model)
 ├── dto               // 데이터 전송 객체 (DTO)
 ├── config            // 설정 파일 (Security, DB 등)
 ├── exception         // 예외 처리
 ├── util              // 유틸리티 클래스
// Hexagonal-architecture
com.example.project
 ├── application        // Use Case, Service (비즈니스 로직)
 │   ├── port          // Port 인터페이스 (Inbound, Outbound)
 │   ├── service       // Application Service (유즈케이스)
 ├── domain            // 핵심 도메인 모델 (Entity, Aggregate, VO)
 ├── infrastructure    // Adapter (DB, API, Message Queue 등)
 │   ├── repository    // DB Adapter (JPA, MyBatis 등)
 │   ├── restclient    // 외부 API 통신 Adapter
 │   ├── event         // 메시징 (Kafka, RabbitMQ 등)
 ├── adapter           // Web Adapter (Controller, CLI 등)
 │   ├── web           // API Controller
 │   ├── cli           // CLI Adapter

Code Coverage

강의를 따라 코드를 작성하다 보니 명확한 장점을 하나 느끼게 되었습니다.
테스트를 통해 거의 모든 코드를 테스트 할 수 있겠다는 점 이었습니다. 다시 말하자면, 테스트를 작성하고 해당 테스트에 필요한 코드만을 작성하다보니 불필요한 코드가 없는 것과 동시에 모든 코드가 테스트에 필요한 코드라는 것이 명확해짐을 알게되었습니다.

이는 개발자 스스로에게도 코드에 대한 신뢰를 높일 수 있으며 다른 사람에게 코드가 정확히 동작함을 보여줄 수 있다는 장점으로 다가왔습니다.
왜냐하면, 코드를 작성한 뒤 테스트 코드를 작성할 때는 하나의 메서드를 보고 개발자의 상상으로 테스트 코드를 작성하다보니 놓치는 부분이 있다는 것을 발견하였기 때문입니다.

그래도 이런 부분은 jacoco나 IDE에서 제공하는 Code Coverage 도구를 통해 식별할 수는 있었습니다.

Assert

이 내용은 비교적 작은 부분인데, 강의에서 dto를 record로 구현하며 Assert를 통해 입력값을 검증하는 것을 보았습니다. 이제껏 validater만을 사용하여 입력값을 검증하였는데 복잡한 것 없이 객체의 속성이 변경 되거나 최초 생성 시에 직관적으로 검증 할 수 있다는 점에서 장점으로 느꼈습니다.

Assert를 사용하게 되면 Assertion관련 예외를 throw하게 되는데 해당 Exception에 대한 처리만 추가로 진행하면 됩니다.

public record GetUserResponse(
        Long id,
        String username,
        String password
) {
    public GetUserResponse {
        Assert.notNull(id, "ID는 필수입니다.");
        Assert.hasText(username, "사용자 ID는 필수입니다.");
        Assert.hasText(password, "사용자 PW는 필수입니다.");
    }
}

예시 코드 입니다.
entity class의 경우 아래와 같이 구현할 수 있습니다.

@Entity
@Table(name="users")
@Getter
@NoArgsConstructor
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String username;
    private String password;

    public User(final String username, final String password) {
        Assert.hasText(username, "사용자 ID는 필수입니다.");
        Assert.hasText(password, "사용자 PW는 필수입니다.");
        this.username = username;
        this.password = password;
    }

    public void update(final String username, final String password) {
        Assert.hasText(username, "사용자 ID는 필수입니다.");
        Assert.hasText(password, "사용자 PW는 필수입니다.");
        this.username = username;
        this.password = password;
    }
}

마치며

개인적으로 TDD를 하는 것에 대한 비효율성을 많이 들어왔는데 이번 기회를 통해 제대로 된 TDD를 경험해본 것 같고 익숙해지기만 한다면 생각보다 생산성이 떨어지지 않을 수도 있겠다는 생각을 했습니다.

그렇게만 된다면 TDD를 통해 얻는 장점이 많이 있기 때문에 단순히 코드 작성하는 데 시간이 더 오래 걸린다는 이유로 TDD를 무시할 수는 없겠다고 생각하여 앞으로 개인 프로젝트에서 테스트 코드를 많이 사용해보면서 실력을 향상시켜야겠다고 생각했습니다.

참고

https://www.youtube.com/watch?v=qECd2q3USqA
https://tech.kakaopay.com/post/home-hexagonal-architecture/
https://gngsn.tistory.com/258
https://velog.io/@juhyeon1114/Java-Assert-%EB%AA%A8%EB%A5%B4%EB%A9%B4-%EB%84%8C-%EC%A3%BC%EB%8B%88%EC%96%B4-Spring-Assert
https://chanho0912.tistory.com/99

profile
환영합니다!

0개의 댓글