[Spring] 서비스 추상화(PSA)

DYKO·2022년 11월 11일
1

Spring Framework

목록 보기
6/7

💡 PSA 개요

스프링 삼각형의 마지막 요소인 PSA(Portable Service Abstractions)는 일관성 있는 서비스 추상화를 뜻한다. 여기서 추상화란 하위 시스템의 공통점을 뽑아내서 분리시키는 것을 말한다. 이를 통해 하위 시스템을 알지 못하거나 변경이 있더라도 일관된 방식으로 접근할 수 있게 한다. 뿐만 아니라 서비스 추상화를 함으로써 '단일 책임 원칙'을 준수하며 코드가 간결해지고 작업 목적이 분명하게 드러나 객체 지향적인 코드를 작성할 수 있다.

💡 Spring에서의 PSA

스프링 프레임워크에서는 트랜잭션과 같이 기능은 유사하지만 사용 방법이 다른, 로우 레벨의 다양한 기술에 대해서 추상 인터페이스와 일관성 있는 접근 방법을 제공해주는 것을 말한다. 대표적으로 스프링의 트랜잭션 추상화를 통해 스프링에서 어떻게 서비스를 추상화 하는 지 알 수 있다.

트랜잭션 서비스 추상화의 필요성

먼저 DB의 트랜잭션이 아닌 비즈니스 로직 내에서 트랜잭션 API를 활용해야할 때가 있다. 이때, 트랜잭션 API가 변경될 때마다 트랜잭션 처리를 해주는 코드를 수정해야 한다. 즉, DB 작업을 하는 객체는 기술환경에 종속되는 구조가 되고 만다. 이러한 문제를 해결하기 위해 스프링에서는 여러 트랜잭션 처리 기술의 공통점을 뽑아내어 분리함으로써 특정 트랜잭션 기술에 의존적이지 않고 독립적인 트랜잭션 서비스를 제공하고 있다. 이를 통해 비즈니스 로직에서는 어떤 트랜잭션 기술를 사용하던간에 일관된 방법으로 DB에 접근할 수 있다.

스프링의 트랜잭션 서비스 추상화

일반적으로 스프링에서는 아래의 트랜잭션 기능을 제공하고 있다.

  • JDBC/Connection
  • JTA/UserTransaction
  • Hibernate/Transaction

스프링에서 제공하는 트랜잭션 추상화 기술을 사용하면 애플리케이션에서 직접 각 기술의 트랜잭션 API를 이용하지 않고도 일관된 방식으로 트랜잭션을 제어할 수 있다. 스프링에서 제공하는 트랜잭션 추상화 계층구조는 다음과 같다.

트랜잭션 경계설정을 위한 추상 인터페이스 PlatformTransactionManager가 있다. 이를 적용한 예제 코드를 보자.

public class UserService {
  // 트랜잭션 매니저를 빈으로 분리
  @Autowired
  PlatformTransactionManager transactionManager;
  
  public void setTransactionManager(PlatformTransactionManager transactionManager) {
  	this.transactionManager = transactionManager;
  }
  
  public void upgradeLevles() {
  	// Start transaction
    TransactionStatus status = this.transactionManager.getTransaction(new DeafultTransactionDefinition());

    try {
        List<User> users = userDao.getAll();
        for(User user : users){
            if(canUpgradeLevel(user)) {
                upgradeLevel(user);
            }
        }
        this.transactionManager.commit(status);
    } catch (Exception e) {
        this.transactionManager.rollback(status);
        throw e;
    }
  }
}

PlatformTransactionManager를 bean으로 등록하지 않고 직접 UserSerivce에서 선언하여 생성해줄 수도 있지만, 이렇게 하게 되면 트랜잭션 API를 JTA로 변경하려면 선언부의 코드를 PlatformTransactionManager transactionManager = new JTATransactionManager()로 수정해야 하는 불편함이 있다. 따라서 DI를 받은 인스턴스 변수를 사용하도록 구현하고 트랜잭션 매니저를 XML설정 파일에 빈으로 등록해야 한다.

<bean id="userService" class="springbook.user.service.UserService">
  <property name="userDao" ref="userDao"/>
  <property name="transactionManager" ref="transactionManager"/>
</bean>

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="dataSource"/>
</bean>

이로써 UserService는 트랜잭션 기술과 완전히 독립적인 코드가 되었다. 여기서 JTA 트랜잭션을 사용하고 싶다면 설정 파일에서 transactionManager 빈의 정보를 아래와 같이 고치면 된다.

<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>

💡 서비스 추상화와 단일 책임 원칙

수직, 수평 계층구조와 의존관계

기술과 서비스에 대한 추상화 기법을 이용하면 특정 기술환경에 종속되지 않는 포터블한 코드를 만들 수 있다. 같은 애플리케이션 로직을 담은 코드지만 각각 담당하는 코드의 기능적인 관심, 내용에 따라 분리한 경우 같은 계층에서 이뤄진 수평적 분리라고 볼 수 있다. 반면, 트랜잭션 추상화의 경우 애플리케이션의 비즈니스 로직과 그 하위에서 동작하는 로우 레벨의 트랜잭션 기술이라는 다른 계층의 특성을 갖는 코드를 분리한 것으로 수직적 분리다.
이렇게 추상화를 통해 수직, 수평 계층구조로 객체 간, 기술환경 간에 결합도를 낮추고 서로 독립적으로 확장될 수 있도록 하는 데는 스프링의 DI가 매우 중요한 역할을 하고 있다. 이와 같이 관심, 책임, 성격이 다른 코드를 깔끔하게 분리함으로써 DI의 가치는 빛을 발한다. 즉, DI는 모든 스프링 기술의 기반이 되는 핵심 엔진이자 원리이며, 좋은 설계와 코드를 만드는 모든 과정에서 사용되는 가장 중요한 핵심 도구이다.

단일 책임 원칙

서비스 추상화와 같이 적절한 분리를 함으로써 객체지향 설계의 원칙 중 하나인 단일 책임 원칙(Single Responsibility Principle) 을 충족할 수 있다. 단일 책임 원칙은 "하나의 모듈은 한 가지 책임을 가져야 한다" 또는 "하나의 모듈이 바뀌는 이유는 한 가지여야 한다"라고 설명할 수 있다.
실제로 위의 예시에서 JDBC API를 직접 접근하도록 구현하게 되면 UserService 객체는 트랜잭션 관리와 사용자 레벨 관리라는 두 가지 책임을 갖게 된다. 그리고 그 두 가지로 인해 모듈이 수정될 수 있다. 하지만 트랜잭션 서비스 추상화를 통해 트랜잭션 관리는 스프링의 트랜잭션 서비스에서 책임지고, UserService는 사용자 레벨 관리에 대한 로직만 책임지게 되었다.

이렇게 단일 책임 원칙을 잘 지키게 되면, 변경사항이 발생했을 때 수정 대상이 명확해진다는 장점이 있다. 기술이 바뀌면 기술 계층과의 연동을 담당하는 기술 추상화 계층의 설정만 바꿔주면 되고, 데이터 액세스와 관련된 변경이 발생하면 해당 로직을 담고 있는 DAO만 수정하면 된다. 이러한 장점이 크지 않게 느껴질 수 있지만, 복잡한 서비스일수록 이런 명확성은 매우(*1000) 큰 장점이라는 걸 뼈 저리게 느낄 수 있다.
(feat. 경험담... 전혀 분리가 안된 서비스를 리팩토링하다 보면 단일 책임 원칙이 얼마나 중요한 지 몸소 느끼고 있다...)



참고문헌

토비의 스프링 3.1 Vol. 1 스프링의 이해와 원리, 이일민, 에이콘출판
서비스 추상화 - 양봉수 블로그
[Spring] 기술의 공통점을 담은 서비스 추상화(PSA) - 춤추는 개발자

profile
엔지니어가 되는 그 날 까지!

0개의 댓글