9장. 애플리케이션 조립하기

Seungjae·2022년 6월 21일
0

우아한 스터디

목록 보기
10/10

왜 조립까지 신경 써야 할까?


  • 모든 의존성은 애플리케이션의 도메인 코드 방향으로 향해야 도메인 코드가 바깥 계층의 변경으로부터 안전하다!

  • 유스케이스가 영속성 어댑터를 직접 호출하고 스스로 인스턴스화 한다면 의존성 방향이 잘못되게 된다.

    • 이것을 해결하기 위해 우리는 아웃고잉 포트 인터페이스를 도입했다.
    • 유스케이스는 인터페이스만 알아야 하고, 런타임에 이 인터페이스의 구현을 제공받아야 한다.
  • 클래스가 필요로 하는 모든 객체를 생성자로 전달한다면 테스트하기 더 용이하다.

    • 실제 객체 대신 Mock으로 전달할 수 있고, 이로 인해 격리된 단위 테스트를 생성하기가 쉬워진다.
  • 그러면 결국 누군가 객체 인스턴스를 생성해야할텐데… 누가 책임을 가지게 되는가?

    • ⇒ 설정 컴포넌트
  • 설정 컴포넌트 → 아키텍처에 대해 중립적이고 인스턴스 생성을 위해 모든 클래스에 대한 의존성을 가진다.

  • 설정 컴포넌트는 클린 아키텍처 구조에서 원의 가장 바깥쪽에 위치!

  • 설정 컴포넌트는 애플리케이션을 조립하는 것을 책임진다. (우리가 설정한대로!) 세부적으로는 아래와 같은 역할을 수행한다.

  • 웹 어댑터 인스턴스 생성
  • HTTP 요청이 실제로 웹 어댑터로 전달되도록 보장
  • 유스케이스 인스턴스 생성
  • 웹 어댑터에 유스케이스 인스턴스 제공
  • 영속성 어댑터 인스턴스 생성
  • 유스케이스에 영속성 어댑터 인스턴스 제공
  • 영속성 어댑터가 실제로 데이터베이스에 접근할 수 있도록 보장
  • 또한 설정 컴포넌트는 설정 파일이나 커맨드라인 파라미터 등과 같은 설정 파라미터의 소스에도 접근 가능해야한다!

  • 사실 설정 컴포넌트는 책임이 굉장히 많다. → SRP 위반일까?

    • 맞다. SRP 위반이다. 하지만 애플리케이션의 다른 부분을 깔끔하게 유지하기 위해 이처럼 모든 구성 요소들을 알고 조립해주는 바깥쪽 컴포넌트를 둔 것이다.
    • Trade-Off라고 볼 수 있겠다.

평범한 코드로 조립하기


  • 자바의 main 메서드 안에서 웹 컨트롤러부터 영속성 어댑터까지, 필요한 모든 클래스의 인스턴스를 생성한 후 연결한다. 뿐만 아니라 웹 컨트롤러를 HTTP로 노출시키는 로직도 필요하다.

  • 애플리케이션을 조립하는 가장 기본적인 방법이다.

  • 하지만 단점이 존재한다.

    1. 완전한 엔터프라이즈 애플리케이션을 실행하기 위해서는 정말 많은 양의 코드를 작성해야 한다.
    2. 각 클래스가 속한 외부에서 인스턴스를 생성하기 때문에 이 클래스들은 전부 public이어야 한다.
  • package-private 의존성을 유지하면서 지저분한 작업들을 대신해주는 의존성 주입 프레임워크들이 존재한다.

    • ex) 스프링 → 웹과 DB 환경을 지원한다!

스프링 클래스패스 스캐닝으로 조립하기


  • 애플리케이션 컨텍스트 → 스프링을 이용해서 애플리케이션을 조립한 결과물

  • Bean애플리케이션을 구성하는 모든 객체

  • 스프링은 classpath Scanning으로 classpath에서 접근 가능한 모든 클래스를 확인해서 @Component가 붙은 클래스를 찾는다. 그리고 이러한 클래스들의 인스턴스를 모두 만들어 애플리케이션 컨텍스트에 추가한다.

classpath?

  • JVM 혹은 자바 컴파일러가 클래스나 패키지를 찾을 때 기준이 되는 경로를 말한다.

  • 소스 코드(.java)를 컴파일 후 바이트코드(.class)로 변환하고, JVM이 바이트코드로 된 파일을 실행하려면 찾아야 하는데 이 바이트코드까지의 경로를 클래스패스라고 한다.

  • @Component를 활용해서 메타-애너테이션으로 임의의 정보를 더 표현하는 애너테이션을 만들어서 사용할 수도 있다. (책 내의 @PersistenceAdapter)

  • classpath Scanning은 단점이 존재한다.

    1. 프레임워크에 특화된 애너테이션을 붙여야 한다는 점에서 침투적이다.
      1. 특히 다른 개발자들이 사용할 라이브러리나 프레임워크를 만드는 입장에서는 매우 지양해야한다.
    2. 원인을 찾는데 수일이 걸릴 수 있는 숨겨진 부수 효과를 야기할 수 있다.
  • 즉, classpath Scanning + @Component 방식은 애플리케이션 내의 모든 관계를 파악하기 매우 어렵다. 그 관련된 설정 정보가 전부 흩어져있기 때문이다!

스프링의 자바 컨피그로 조립하기


  • 애플리케이션 컨텍스트에 추가할 빈을 생성하는 설정 클래스를 만드는 방식이다.

  • @Configuration을 통해서 스프링의 classpath scanning에서 발견해야할 설정 클래스임을 표시한다.

    • 똑같이 classpath scanning을 사용하지만 모든 빈을 애플리케이션 전체에서 다 확인하며 컨텍스트에 넣는 것이 아닌 설정 클래스에서 모든 설정 관련 작업이 이뤄지기에 그 관계를 파악하기 쉽다.
  • @Bean가 붙은 팩터리 메서드를 통해 빈 생성

  • @EnabledJpaRepositories를 사용하면 우리가 정의한 모든 스프링 데이터 리포지토리 인터페이스의 구현체를 생성해서 제공한다.

    • 설정 클래스뿐만 아니라 메인 애플리케이션에도 붙일 수 있다.
    • 하지만, 그러면 애플리케이션이 시작할 때마다 JPA가 활성화된다. 즉, 영속성이 필요없는 테스트에서도 JPA 리포지토리들을 활성화하게 된다.
    • 즉 이런 ‘기능 애너테이션’을 별도의 설정 모듈로 옮기는 편이 애플리케이션을 더 유연하게 만들고, 항상 모든 것을 한꺼번에 시작할 필요 없게 해준다.
  • 또한 특정 모듈에 대한 설정 클래스를 따로 두어, 특정 모듈만 포함하고 그 다른 모듈의 빈은 모킹해서 애플리케이션 컨텍스트를 만들 수 있다. → 테스트에 큰 유연성

  • 또한 애플리케이션 계층을 스프링 프레임워크에 대한 의존성 없이 깔끔하게 유지할 수 있다. @Component를 여기저기에 안붙여도 되기 때문!

  • 하지만 문제점도 존재한다.

    • 설정 클래스가 생성하는 빈이 설정 클래스와 같은 패키지에 존재하지 않는다면 이 빈들은 public으로 만들어야한다.

유지보수 가능한 SW를 만드는데 어떻게 도움이 될까?


  • 스프링은 우리가 제공하는 부품들을 이용해서 애플리케이션을 조립해준다.

  • 이런 기능을 통해 애플리케이션 전체를 고민하지 않고도 빠르게 개발할 수 있다.

  • 애플리케이션 조립을 책임지는 전용 설정 컴포넌트를 만들면, 서로 다른 모듈로부터 독립되어 코드 상에서 손쉽게 옮겨 다닐 수 있는 응집도 높은 모듈을 만들 수 있다. 물론 설정 컴포넌트를 유지보수하는데의 cost가 생긴다.

profile
코드 품질의 중요성을 아는 개발자 👋🏻

0개의 댓글

관련 채용 정보