Spring PSA

Seop·2023년 9월 4일
0

Spring

목록 보기
4/4
post-thumbnail

Spring에는 3가지의 핵심 가치가 존재합니다.
IOC/DI, AOP, PSA 이렇게 총 3가지이죠!

오늘은 그 중에서 PSA에 대해서 알아보려고 합니다!

Portable + Service Abstraction

PSA는 Portable Service Abstraction 의 약자입니다.
그리고 이 단어는 Portable + Service Abstraction의 합성어이지요

그러면 차근차근 하나씩 알아가 봅시다

Service Abstraction

Service abstraction is a design principle that is applied within the service-orientation design paradigm so that the information published in a service contract is limited to what is required to effectively utilize the service[1] The service contract should not contain any superfluous information that is not required for its invocation

WIKI PEDIA - Service Abstraction

서비스 추상화는 실제로 서비스에서 사용되는 부분의 정보만을 제공하고, 불필요한 정보까지는 제공하지 않는다는 원칙입니다.
위키 백과는 이렇게 이야기 하는군요.

스프링에서는 추상화 계층을 이용해서 특정 기술의 구현부를 숨기고 개발자에게 편의성을 제공해 주는 것을 Service Abstraction 이라고 하죠.
추상화 계층을 통해서 어떤 기술은 내부적으로 숨기고, 개발자에게는 편의를 제공해 주는 것이 서비스 추상화라고 할 수 있습니다.

우리가 특정 기술을 사용한다고 해서 그 시술이 어떻게 구현되어있는지 모두 세세하게 알 수는 없겠죠(물론 알면 좋겠지만).

예를 들어 DB와 연동 작업이 필요해서 JDBC Driver를 사용한다고 했을 때, Jdbc의 세세한 구현 모두를 알아야 사용할 수 있는 것이 아닌 것 처럼 말이죠!

그렇다면 앞에 Portable 이 붙은 Service Abstraction은 무슨 뜻일까요??

Portable Service Abstraction

위에서 설명드린 Service Abstraction으로 개발자에게 제공되는 기술을 다른 기술 스택으로 간단하게 변경할 수 있도록 확장성이 추가가 되었다고 보면 됩니다.

이렇게 해서 개발자는 기술 스택이 변경 되더라도 코드의 변경은 딱히 신경쓰지 않아도 되는 견고한 코드를 작성할 수 있게 해줍니다.

별 다른 수정 없이도 기술 전환 및 확장이 가능하다는 점에서 SOLID 원칙 중 OCP에 해당한다고 볼 수 있습니다.

Spring MVC

@Controller 어노테이션을 통해서 우리는 웹 요청을 받는 컨트롤러를 만들 수 있습니다.
그리고 HttpServlet 클래스를 상속 받지 않고도 get, post 등의 요청을 처리할 수 있게 됩니다.
바로 @GetMapping, @PostMapping 등의 어노테이션의 도움을 받아서 처리할 수 있죠!

원래대로라면 HttpServlet 클래스를 상속 받고, doGet(), doPost() 등의 메소드를 오버라이딩해야 하지만, 우리는 어노테이션을 메허드 위에 추가해주는 것으로 보다 간단하게 처리할 수 있습니다

그리고 Spring MVC에서 Spring Webflux로 의존성을 변경하면 기존 톰캣에서 netty 기반으로 바로 전환됩니다.
Spring Web MVC의 추상화 계층 덕분이죠!

@Transactional

트랜잭션이 필요한 매서드에서 작업을 할 때, @Transactional 어노테이션을 붙여서 작업합니다.
해당 어노테이션을 사용해 주면, 트랜잭션의 시작, 트랜잭션의 전파, 커밋, 롤백 등의 작업을 보다 쉽게 관리할 수 있게 도와주죠.

명시적으로 commit(), rollback() 등을 호출하지 않았는지만, 트랜잭션이 마무리되면 커밋을 해주고, 중간에 트랜잭션이 깨지면 롤백을 해줍니다.

그리고 이러한 트랜잭션을 관리하는 TransactionManager의 다양한 구현체들이 존재합니다.

개발자는 위의 구현체들중 원하는 구현체를 마음대로 사용해도 좋고, 언제든지 다른 구현체로 변경할 수 있습니다.
그리고 그 내부 구현은 몰라도 되죠!!

여기서 추상화 계층 예시를 하나 보겠습니다.

TransactionManager의 자식 인터페이스 중 PlatformTransactionManager는 추상화 계층에 속해 있습니다.
위 그림에서 눈여겨 볼 점은 해당 인터페이스를 구현한 비즈니스 로직 또한 추상화 계층의 일원으로 구현했다는 점입니다.

개발자는 여기서 PlatFormTransactionManager를 사용하면 되고, 언제든지 다른 구현체로 변경할 수 있습니다.

참고로 아래와 같은 환경으로 프로젝트를 만들 경우, JpaTransactionManager 가 기본 구현체로 선택됩니다!!

application.yaml

spring:  
  datasource:  
    url: jdbc:mysql://localhost:3306/tmp?rewriteBatchedStatements=true&serverTimezone=Asia/Seoul&characterEncoding=UTF-8  
    username: root  
    password: root1234!  
  jpa:  
    database: mysql  
    database-platform: org.hibernate.dialect.MySQL8Dialect  
    generate-ddl: false  
    open-in-view: false  
    show-sql: true  
    hibernate:  
      ddl-auto: create-drop  
    properties:  
      jdbc:  
      hibernate:  
        format_sql: true  
        jdbc:  
          batch_size: 100  
  output:  
    ansi:  
      enabled: always

Penguin.java

간단한 엔티티

@Getter @Setter  
@Entity  
@Table(name = "penguins")  
@NoArgsConstructor  
public class Penguin {  
  
   @Id @GeneratedValue(strategy = GenerationType.IDENTITY)  
   private Long id;  
   private String name;  
   private int age;  
   private float length;  
   private float weight;  
   private Species species;  
  
   @Builder  
   public Penguin(Long id, String name, int age, float length, float weight, Species species) {  
      this.id = id;  
      this.name = name;  
      this.age = age;  
      this.length = length;  
      this.weight = weight;  
      this.species = species;  
   }  
}

PenguinRepository.java

JPA Repo 사용

@Repository  
public interface PenguinRepository extends JpaRepository<Penguin, Long> {  
}

PenguinService.java

save만 할 수 있는 단순한 Service

@Service  
@RequiredArgsConstructor  
public class PenguinService {  
  
   private final PenguinRepository repository;  
  
   @Transactional  
   public void addOnePenguin(Penguin penguin){  
      Penguin saved = repository.save(penguin);  
      System.out.println("================================");  
      System.out.println("Penguin added!!, ID : " + saved.getId());  
   }  
}

Test.java

POJO 객체 생성 후, Repo에 객체 삽입

@SpringBootTest  
public class TransactionalTest {  
  
   @Autowired  
   private PenguinService service;  
  
   @Test  
   void addOneTest() {  
      Penguin penguin = Penguin.builder()  
         .age(2)  
         .length(150)  
         .name("핑구")  
         .species(Species.EMPEROR_PENGUINS)  
         .weight(30)  
         .build();  
  
      service.addOnePenguin(penguin);  
   }  
}

Transaction Manager 구현체

위와 같은 환경에서는 JpaTransactionManager 가 기본 구현체로 선택되어 있습니다.

구현체를 변경하기 위해서는 @EnableTransactionManagement 어노테이션을 사용한 다음 Config에서 설정을 수정해줘야합니다!


@Cacheable

캐싱 또한 PSA가 사용되는 부분입니다.

스프링에서는 @Cacheable 과 같은 어노테이션을 통해서 간단하게 캐싱을 적용시킬 수 있습니다.
그리고 캐싱은 다양한 구현체가 준비가 되어 있죠

위에 명시된 여러 구현체 중 마음에 드는 구현체를 가져다가 사용하면 되고, 언제든지 갈아끼우면 됩니다.

참고

profile
어제보다 더 나은 개발자가 되고파요

0개의 댓글