본문 바로가기

항해 99/Spring

Spring PSA

 

PSA(Portable Service Abstraction)

환경의 변화와 관계 없이 일관된 방식의 기술로의 접근 환경을 제공하는 추상화 구조

특정 클래스가 추상화된 상위 클래스를 일관되게 바라보며 하위 클래스의 기능을 사용하는 것이 PSA의 기본 개념

 

PSA가 적용된 코드는 개발자의 기존에 작성된 코드를 수정하지 않으면서 확장할 수 있으며, 어느 특정 기술에 특화되어 있지 않은 코드이다.

 

Spring에서 동작할 수 있는 라이브러리들은 POJO 원칙을 지키기 위해 PSA 형태의 추상화가 되어 있으며, Spring Web MVC, Spring Transaction, Spring Cache, Spring Data, 메일 서비스 등의 다양한 PSA를 제공하고 있다.

 

Spring Web MVC

일반적인 서블릿의 형태

Servlet을 사용하려면 HttpServlet을 상속받고 doGet(), doPost() 등 오버라이딩하여 사용해야 한다.

public class CocoServlet extends HttpServlet {

    // GET
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        super.doGet(request, response);
    }
    
    // POST
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        super.doPost(request, response);
    }
}

 

Spring Web MVC의 형태

@Controller
class OwnerController {  
    do something.. 

    @GetMapping("/owners/new")
    @LogExecutionTime
    public String initCreationForm(Map<String, Object> model) {
	Owner owner = new Owner();	model.put("owner", owner);
	return VIEWS_OWNER_CREATE_OR_UPDATE_FORM;
    } 

    @PostMapping("/owners/new")
    @LogExecutionTime
    public String processCreationForm(@Valid Owner owner, BindingResult result) {
	    if (result.hasErrors()) {
		    return VIEWS_OWNER_CREATE_OR_UPDATE_FORM;
	    }
	    else {
		    this.owners.save(owner);
		    return "redirect:/owners/" + owner.getId();
	   }
    } 

do something.. 

} // end OwnerController
  • 일반 클래스에 @Controller 어노테이션을 사용하면 요청을 매핑할 수 있는 컨트롤러 역할을 수행하는 클래스가 됨
  • 해당 클래스에서는 @GetMapping과 @PostMapping 어노테이션을 사용해서 요청해서 요청을 매핑할 수 있습니다
    • 서블릿을 Low level로 개발하지 않고도, Spring Web MVC를 사용하면 이렇게 서블릿을 간편하게 개발할 수 있음(뒷 단에 spring이 제공해주는 여러 기능들이 숨겨져 있기 때문)
    • 즉, HttpServlet을 상속받고 doGet(), doPost()를 구현하는 등의 작업을 하지 않아도 되는 것임
  • Service Abstraction(서비스 추상화)의 목적 중 하나가 이러한 편의성을 제공하는 것임
    • Spring Web MVC는 코드는 거의 그대로 둔 상태에서 톰캣이 아닌 다른 서버로 실행하는 것도 가능함
    • 프로젝트의 spring-boot-starter-web 의존성 대신 spring-boot-starter-webflux 의존성을 받도록 바꿔주기만 하면 Tomcat이 아닌 netty 기반으로 실행할 수 있음

Spring Web MVC는 @Controller, @RequestMapping과 같은 어노테이션과 뒷단의 여러가지 복잡한 인터페이스들 그리고 기술들을 기반으로 하여 사용자가 기존 코드를 거의 변경하지 않고, 웹 기술 스택을 간편하게 바꿀 수 있도록 해줌

 

Spring Transation

Low level로 트랜잭션 처리를 하려면 명시적으로 setAutoCommit()과 commit(), rollback()을 호출해야 하는데, @Transactional 어노테이션을 사용하면 단순하게 메서드에 어노테이션을 붙여줌으로써 트랜잭션 처리가 간단하게 이루어짐

 

JDBC를 사용하는 DatasourceTransactionManager, JPA를 사용하는 JpaTransactionManager, Hibernate를 사용하는 HibernateTransactionManager를 유연하게 바꿔서 사용할 수 있음

 

즉, 기존 코드는 변경하지 않은 채로 트랜잭션을 실제로 처리하는 구현체를 사용 기술에 따라 바꿀 수 있는 것임

 

Spring Cache

JCacheManager, ConcurrentMapCacheManager, EhCacheCacheManager와 같은 여러가지 구현체를 사용할 수 있음

사용자는 @Cacheable 어노테이션을 붙여줌으로써 구현체를 크게 신경쓰지 않아도 필요에 따라 바꿔 쓸 수 있음

 

서비스에 적용하는 PSA 기법

서비스 추상화(Service Abstraction)는 추상화의 개념을 애플리케이션에서 사용하는 서비스에 적용하는 기법

  • Java 콘솔 애플리케이션에서 클라이언트가 데이터베이스에 연결하기 위해 JdbcConnector를 사용하기 위한 서비스 추상화의 다이어그램

JdbcConnector 인터페이스가 애플리케이션에서 이용하는 하나의 서비스가 되는 것임

 

DbClient 클래스는 OracleJdbcConnector, MarinaDBJdbcConnector, SQLiteJdbcConnector와 같은 구현체에 직접적으로 연결해서 얻는 것이 아닌 JdbcConnector 인터페이스를 통해 간접적으로 연결되어 Connection 객체를 얻을 수 있게 된다

 

DbClient 클래스에서 JdbcConnector 구현체를 사용하더라도 Connection을 얻는 방식은 getConnection() 메서드로 다른 구현체와 동일하다.

 

일관된 방식으로 해당 서비스의 기능을 사용할 수 있는 것임

 

애플리케이션에서 특정 서비스를 이용할 때, 서비스의 기능을 접근하는 방식 자체를 일관되게 유지하면서 기술 자체를 유연하게 사용할 수 있도록 하는 것을 PSA(일관된 서비스 추상화)라고 한다.

 

예제 코드 - JdbcConnector 서비스 사용

DbClient

public class DbClient {
    public static void main(String[] args) {
        JdbcConnector connector = new SQLiteJdbcConnector();

        DataProcessor processor = new DataProcessor(connector);
        processor.insert();
    }
}

 

DataProcessor

public class DataProcessor {
    private Connection connection;

    public DataProcessor(JdbcConnector connector) {
        this.connection = connector.getConnection();
    }

    public void insert() {
        System.out.println("inserted data");
    }
}

 

JdbcConnector

public interface JdbcConnector {
    Connection getConnection();
}

 

MariaDBJdbcConnector

public class MariaDBJdbcConnector implements JdbcConnector {
    @Override
    public Connection getConnection() {
        return null;
    }
}

 

OracleJdbcConnector

public class OracleJdbcConnector implements JdbcConnector {
    @Override
    public Connection getConnection() {
        return null;
    }
}

 

SQLiteJdbcConnector

public class SQLiteJdbcConnector implements JdbcConnector {
    @Override
    public Connection getConnection() {
        return null;
    }
}

 

  • DbClient 클래스는 SQLiteJdbcConnector 구현체의 객체를 생성하여 JdbcConnector 인터페이스 타입의 변수에 할당한다(업캐스팅)
  • 이후 데이터를 저장하는 기능을 하는 DataProcessor 클래스의 생성자로 JdbcConnector 객체를 전달하고 있다(DI : 의존성 주입)
  • 다른 애플리케이션에서 SQLite 데이터베이스를 사용하는 것이 아닌 Oracle이나 MariaDB를 사용해야 한다면, JdbcConnector 서비스 모듈을 그대로 사용하여 new SQLiteJdbcConnector() 또는 new MariaDBJdbcConnector()으로 바꿔서 사용하면 된다.

PSA가 필요한 이유

 

PSA는 어떤 서비스를 이용하기 위한 접근 방식을 일관된 방식으로 유지하여 애플리케이션에서 사용하는 기술이 변경되더라도 최소한의 변경만으로 변경된 요구 사항을 반영하기 위해 사용한다.

 

즉, PSA를 통해 애플리케이션의 요구 사항 변경에 유연하게 대처할 수 있다.

 

Spring은 Spring Web MVC, Spring Transaction, Spring Cache, Spring Data, 메일 서비스 등 다양한 PSA를 지원한다.

'항해 99 > Spring' 카테고리의 다른 글

Spring Actuator  (1) 2024.04.18
즉시로딩, 지연로딩, N+1 문제  (0) 2024.04.13
Spring MVC, IoC/DI  (0) 2024.04.11
Spring Context  (0) 2024.04.10
Spring AOP  (0) 2024.04.09