7. 서비스 추상화

1. 서비스란 무엇인가?

스프링에 대한 오해

  • 스프링은 @Controller, @Service, @Repository 만 기계적으로 찍어내는 방법이다? -> 적어도 이 세가지 빈은 구분해서 만들어야 한다는 최소한의(!) 스프링의 가이드

스프링 애플리케이션 빈이 존재하는 계층 구조

3개의 전형적인(stereotype) 애노테이션을 사용하는 애플리케이션 빈의 위치

서비스는 일반적인 용어라서 쓰이는 곳(컨텍스트) 에 따라 다른 의미를 가진다.

  • 이 때문에, 서비스라는 단어를 이해하기가 생각보다 어렵다 ;;

서비스(개발쪽에서, 특히 백엔드) 에 대한 통용되는 개념

서비스는 클라이언트에게 서비스를 제공해주는 오프젝트나 모듈

  • 클라이언트가 존재해야 한다!

  • 클라이언트가 없다면 서비스가 아니다.

서비스는 일반적으로 상태를 가지지 않는다.

  • 상태를 가지지 않기 때문에, 싱글톤 빈으로 관리해도 된다.

서비스의 종류

  • 애플리케이션 서비스(application service)

    • 정의 : 애플리케이션 계층에 존재하는 서비스입니다. 주로 사용자의 요청(Use Case)을 처리하고, 도메인 객체나 도메인 서비스를 조합하여 비즈니스 로직을 수행하도록 조율(Orchestration)하는 역할을 한다.

    • 특징

      • @Service 애노테이션: 스프링 프레임워크와 같은 환경에서 빈(Bean)으로 등록되어 관리될 때 흔히 @Service 애노테이션을 사용한다.

      • 상태 비저장(Stateless) : 일반적으로 상태를 가지지 않고, 요청에 필요한 데이터를 받아 처리한 후 결과를 반환한다.

      • 트랜잭션 경계 : 트랜잭션의 시작과 끝을 담당하는 경우가 많다.

  • 도메인 서비스(domain service)

    • 정의 : 특정 도메인 객체에 비즈니스 로직을 포함하기 애매할 때 사용하는 서비스이다. 즉, 하나의 엔티티(Entity)나 값 객체(Value Object)에 속하지 않는 도메인 로직을 처리한다.

    • 특징

      • 도메인 계층 소속: 애플리케이션 서비스와 달리, 도메인 모델의 일부로 간주된다.

      • 상태 비저장(Stateless): 일반적으로 상태를 가지지 않는다.

      • 도메인 객체 간의 상호작용: 여러 도메인 객체가 관련된 복잡한 비즈니스 규칙이나 계산을 수행할 때 사용된다.

  • 인프라 서비스(infrastructure service)

    • 정의 : 도메인 로직이나 애플리케이션 로직에 직접 참여하지 않고, 기술적인 기능을 제공하는 서비스이다. 서비스 추상화의 대상이 되기도 한다.

    • 특징

      • 인프라스트럭처 계층 소속: 애플리케이션의 핵심 비즈니스 로직과는 분리되어 기술적인 문제를 해결한다.

      • 외부 시스템 연동: 데이터베이스 접근, 메시징 큐, 이메일 발송, 파일 시스템 접근, 외부 API 호출 등과 관련된 기능을 제공한다.

      • 구현 기술에 의존: 특정 기술(예: SMTP, JDBC, JPA, Kafka)을 사용하여 구현된다.

      • 추상화: 도메인 계층이나 애플리케이션 계층에서는 인프라 서비스의 구체적인 구현 기술을 알 필요 없이, 인터페이스를 통해 사용하는 것이 좋다.. 이를 통해 기술 변화에 유연하게 대처할 수 있다.

2. 애플리케이션 서비스 도입

애플리케이션 서비스

  • @Service 빈으로 구성

  • Application/Service 계층에 존재

  • 애플리케이션/도메인 로직 - 도메인 오브젝트/엔티티 활용

  • 인프라 서비스의 도움이 필요

  • 가장 중요한 도메인/애플리케이션 비즈니스 로직

  • 인프라 레이어에 존재하는 기술에 가능한 의존하지 않도록 만들어야 함

    • 추상화에 의존해야 하지, 구체적인 것에 의존해서는 안된다.

  • PaymentService - ExRateService 에 적용된 DIP

기존의 DataClient 안에 있던 비지니스 로직에 서비스 레이어를 추가하자.

  • 기존 DataClient 이름을 OrderClinet 로 변경하자.

3. 기술에 독립적인 애플리케이션 서비스

현재 OrderService 를 자세히 보면 JPA 에 굉장히 의존적이다. (변경에 취약하다)

OrderService 의 문제점

  • 데이터 액세스 기술의 하나인 JPA 에 의존

  • JPA 를 사용하는 OrderRepository 클래스에 의존

    • JpaRepository 라고 이름을 변경해도 이상하지 않다.

  • JPA Transaction Manger 에 의존

Order 의 문제점

  • @Entity 가 붙은 JPA 엔티티로 작성

  • @Entity 라이브러리가 붙은 경우 컴파일 시점에만 JPA 라이브러리에 의존

  • 만약 Order 클래스 안에 비지니스 로직을 추가할 경우 JPA 기술과 관련된 내용이 들어가지 않는다.

    • 애노테이션 외의 코드로 JPA 와 연관성이 없다.

  • JPA 를 사용하지 않고 다른 용도로 사용한다면? 런타임에는 JPA 라이브러리에 의존하지 않는다.

Order 에서 JPA 메타데이터 분리

  • 애노테이션(@Entiry) 은 컴파일타임 라이브러리 의존성만 가진다.

  • 엔티티 동작에는 영향을 주지 않기 때문에, 엔티티 클래스를 다른 데이터 기술에서 사용해도 된다.

  • 그래도 제거하고 싶다면 외부 XML 디스크립터를 사용할 수 있다.

resources/META-INF/orm.xml

  • 해당 경로에 orm.xml 파일을 만들자.

Order 내 JPA 어노테이션을 제거하자.

  • XML 디스크립터 덕분에 애노테이션을 제거하고도 이전과 같이 코드가 잘 동작한다.

4. OrderRepository DIP

특정 기술(JPA) 에 의존하지 않는 애플리케이션 서비스 만들기

  • JPA Repository - OrderRepository 에 의존하지 않도록 변경

    • 추상화에 의존하도록 만들어 구체 클래스에 의존하지 않도록 변경해준다.

코드 수정

  • DIP 원칙을 지키기 위해서 OrderRepository 는 상위 모듈(order) 패키지에 생성하자.

    • 상위 모듈은 하위 모듈에 의존해서는 안된다.

테스트 추가

  • 기존에는 OrderClient 실행을 통해서 코드가 잘 동작하는지 확인했다.

  • 이제는 테스트 코드를 추가해 기능을 테스트하자.

5. 트랜잭션 서비스 추상화

OrderService

  • JPA 를 사용하는 Repository 클래스에 의존 (지난 챕터에서 해결)

  • JPA Transaction Manager 에 의존

Transaction 은 데이터 기술에 따라 방법이 다르다.

  • JDBC

  • JPA

  • MyBatis

  • Jooq

JPA 트랜잭션 예시

JDBC 트랜잭션

추상화

  • 구현이 복잡함과 디테일을 감추고 중요한 것만 남기는 기법

  • 여러 인프라 서비스 기술의 공통적이고 핵심적인 기능을 인터페이스로 정의하고 이를 구현하는 어댑터(interface) 를 만들어 일관된 사용이 가능하게 만드는 것이 서비스 추상화

    • 우리가 만드는 코드는 추상화하기 쉽다! (OrderRepository 인터페이스)

    • 하지만, JPA 같은 라이브러리 기술들을 우리가 자유롭게 추상화하기 쉽지 않기 때문에, 어댑터(PlatformTransactionManager)를 사용해야 한다.

코드 수정

  • 어댑터들(JpaTrasactionManager, DataSourceTransactionManager, ...) 은 대부분 스프링이 제공한다.

6. JDBC 데이터 엑세스 기술

이제 어뎁터(PlatformTransactionManager) 를 활용해 JPA 에서 JDBC 로 데이터 엑세스 기술을 변경해보자.

JdbcClient

  • Spring 6.1 에서 추가

  • SQL 을 사용하는 JDBC 데이터 처리 코드를 유연하게 작성하도록 도와줌

  • 일종의 템플릿 / 콜백

  • 스프링의 JdbcTemplate 의 대체 기술

DataSourceTransactionManager

  • JDBC 의 Connection 을 이용하는 트랜잭션 매니저

  • Connection 을 리턴하는 DataSource 오브젝트 필요

JDBC 데이터 엑시스용 구성 정보

  • DataSource

  • DataSourceTransactionManager

코드 수정

  • 중요한 것은, 데이터 엑시스 기술을 JPA -> JDBC 로 변경했는데, OrderService 코드를 한줄도 건들지 않았다는 것이다!

7. 트랜젝션 테스트

OrderService 에서 기술 관련 코드 제거

  • 현재 OrderSerivce 코드에서 기술 관련 코드를 많이 제거하므로, 데이터 엑세스 기술이 변경되어도 기존 코드는 영향을 받지 않는다.

  • 하지만 TransactionTamplate, PlatformTransactionManager 와 같은 기술과 관련된 코드가 계속 등장한다.

  • 트랜잭션의 시작과 종료는 보통 애플리케이션 서비스 메서드 실행 전후이기 때문에, 트랜잭션 로직은 서비스 레이어 안에 포함되어야 한다.

트랜잭션 테스트

  • 트랜잭션이 필요한 곳에 정확하게 적용되었는지 테스트 하기는 매우 어려움

  • JDBC 처럼 자동 커밋이 되거나 Spring Data JPA 처럼 기본 레포지토리 구현에서 트랜잭션을 알아서 적용해주는 기술을 사용할 때는 트랜잭션이 바르게 적용되지 않는 것을 놓치기 쉽다.

  • 모든 작업이 성공하면 하나의 트랜잭션으로 진행된 것인지 여러개의 트랜잭션으로 쪼개진 것인지 확인하기 어려움

  • 트랜잭션 중간에 실패하는 케이스를 만들 수 있다면 롤백 여부로 확인할 수 있음

테스트 코드 작성

  • JDBC 는 트랜잭션 로직을 강제하지 않기 때문에, OrderService.createOrder(), .createOrders() 에서 트랜젝션 로직을 뺐다.

  • 그러니, 당연하게도 트랜잭션 테스트가 실패하고 만다..

트랜잭션 추가

  • 트랜잭션을 추가함으로 테스트는 성공했다.

  • 하지만, 다시 OrderServiceTransactionTemplate() 같은 기술적인 객체에 의존적인 코드로 변경되었다.

8. 트랜잭션 프록시

이전에 기술적인 객체에 의존적인 코드를 트랜잭션 프록시를 통해서 개선해보자.

데코레이터 패턴

  • 오브젝트의 코드를 변경하지 않고 새로운 기능을 런타임에 부여하는 패턴

프록시 패턴

  • 타킷을 대신해서 존재하며 접근을 제거하거나 보안, 지연, 원격 접속 등의 기능을 제공

  • 내 생각에는 데코레이터, 프록시 패턴 모두 비슷한 기능을 하는 것으로 보여진다. (그냥 프록시라고 보자)

트랜잭션 프록시

  • OrderService 인터페이스 추출

  • 트랜잭션 부가 기능을 제공하는 OrderServiceTxProxy 프록시

코드 수정

  • 프록시 패턴 적용 후 트랜잭션 테스트가 정상적으로 수행되는 것을 볼 수 있다.

  • 결론적으로, 서비스 추상화를 통해 본인의 책임만을 수행하는 구조를 만들어나갈 수 있다.

9. @Transactional 과 AOP

트랜잭션 프록시 적용

  • 동일한 OrderService 인터페이스를 구현한 프록시를 OrderClient 에 주입

스프링이 만들어주는 트랜잭션 프록시

  • @Transactional 애노테이션이 붙은 클래스의 메소드가 트랜잭션 안에서 실행되도록 프록시를 만들어줌

  • 지금까지 우리가 작업했던 복잡했던 코드를 대체해준다!

코드 수정

  • 복잡했던 프록시 패턴을 @Transaction 을 사용해 선언적으로 변경했다!

  • 직접 프록시 패턴을 사용한다면 수 많은 반복되는 코드를 만들어낸다.

스프링의 프록시 AOP (Aspect Oriented Programming)

  • AOP 는 스프링에서 그다지 성공하지 못한 핵심 기술 중의 하나

  • 활용 용도가 제한적이면서 막상 사용하기가 매우 어렵다.. (난이도가 높다)

  • 스프링이 만들어 놓은 트랜잭션과 보안 기술에서는 유용하게 활용된다.

  • 직접 활용하려면 꽤 많은 학습이 필요하다!!

  • AOP 는 아니더라도 데코레이터/프록시 패턴의 동작원리를 이해하고 필요한 곳에 활용할 수 있다.

Last updated