본문 바로가기
  • 시 쓰는 개발자
Spring

Portable Service Abstraction

by poetDeveloper 2024. 3. 4.

백기선님의 스프링 강의를 듣고 작성하였습니다. 내용이 어려워서 종종 내용 확인해보고 틀린 부분은 보완하겠습니다.

 

스프링 핵심 3요소(Spring Triangle) 中 "서비스 추상화", PSA(Portable Service Abstraction)에 대해서 알아보자.

 

PSA : 내부 동작을 추상화

  • PSA는 한마디로 "잘 만든 인터페이스"이다. 단어에서도 알 수 있듯이, 서비스를 추상화하는 작업이다. 잘 추상화 해놓았기 때문에 우리는 Low Level을 직접 다룰 일이 적다.
  • 예를 들어 우리는 서블릿을 기반으로 두고 있음에도 불구하고 코딩할 때 Servlet이나 HttpServlet을 쓰지 않는다. 이것을 모두 Spring 웹 MVC에서는 @GetMapping, @PostMapping 등이 대체하고 있기 때문이다. DB에 접근할 때도 직접적인 접근보다 JPA를 사용해서 접근할 수 있고, DB connection도 @Transactional이 수행해준다.
  • 그리고 Portable이 들어간 이유는, 손쉽게 이식이 가능하기 때문이다. @GetMapping을 그대로 사용하면서 Tomcat에서 Netty 서버로 바꿀 수도 있다.

서블릿 예시

서블릿을 예로 들어보자. 원래는 아래와 같이 코드를 짜야한다. 서블릿을 사용하기 위해 HttpServlet을 상속받고, doGet과 doPost를 오버라이딩 해서 사용하는데 저것 자체로 복잡해보인다.

public class TestServlet extends HttpServlet {

    // GET
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
	super.doGet(req, resp);
    }

    // POST
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
	super.doPost(req, resp);
    }
}

 

하지만 우리가 Spring Web MVC에서는 @GetMapping, @PostMapping으로 대체할 수 있다. 모두 내부에서 잘 추상화되어 있기 때문이다.

 

Transaction (@Transactional)

Transaction이 가지는 가장 큰 특징은 All or Nothing이라고 생각한다. 모두 반영되거나, 모두 철회하거나이다. 만약 DB에 일부만 반영된다면 그것만큼 번거로운 게 없다. 온라인에서 옷을 사는데 재고가 없는 옷을 구매했을 때 돈만 빠져나가고 재고가 없다고 뜨면 굉장히 난감할 것이다. Low Level로 Transaction을 처리하는 과정을 살펴보자.

try {
    dbConnection = getDBConnection();
    dbConnection. setAutoCommit(false); // SQL이 여러번 날아가도 커밋하지 않고 일단 기다림.

    preparedStatementInsert = dbConnection.prepareStatement(insertTableSQL);
    preparedStatementInsert. setInt(1, 999);
    ....
    preparedStatementInsert.executeUpdate; // 여기서 업데이트 되어도 롤백되므로 상관없음
    ....
    ....
    dbConnection.commit(); // 모든 작업이 잘 수행되고 여기서 커밋하라고 할 때 비로소 커밋
    System.out.println("Done!");
    
} catch (SQLException e){
    System.out.println(e.getMessage());
    dbConnection.rollback(); // 한개라도 에러가 나면 여기로 돌아와서 롤백시킴
}

주석에 유의하며 읽어보자. 일단 SQL이 들어와도 커밋하지 않고 기다린다. 그리고 모든 작업이 끝나고 마지막에 커밋을 하는 것을 알 수 있고, 만약 과정중에 하나라도 에러가 나면 catch로 들어가서 롤백시키게 되는 것이다. 그런데 이것을 하나하나 구현하지 않고 @Transactional을 붙여서 편하게 사용할 수 있다.

그래서 @Transactional도 PSA라고 할 수 있다. 위 과정을 생략하기에 굉장히 추상화되어있고, Portable한 이유는 코드 변경 없이 구현체를 JpaTransacionManager(JPA), DatasourceTransactionManager(JDBC), HibernateTransactionManager(Hibernate) 등으로 바꿀 수 있기 때문이다.