S E P H ' S
[Spring] 6. Spring PSA 본문
PSA (Portable Service Abstraction) 일관된 서비스 추상화
PSA는 환경의 변화와 관계없이 일관된 방식의 기술로의 접근 환경을 제공하는 추상화 구조를 말한다. 이는 POJO 원칙을 철저히 따른 Spring의 기능으로 Spring에서 동작할 수 있는 Library들은 POJO 원칙을 지키도록 PSA 형태의 추상화가 되어있음을 의미한다. PSA가 적용된 코드라면 코드가 변경되지 않고도 다른 기술로 간편하게 바꿀 수 있는 확장성을 지니게 된다.
쉽게 말해 개발자가 개발시에는 내부적으로 어떻게 동작하는지는 살펴볼 일이 거의 없다. 내부적으로 작동하는 기능들을 숨기고, 추상화하여 개발자에게 제공하기 때문이다. Spring은 Spring Web MVC, Spring Transaction, Spring Cache 등의 다양한 PSA를 제공한다.
기본적으로 Spring에서는 PlatformTransactionManager 이라는 최상위 Manager를 사용하고, 각각 사용자의 선언에 따라서 JPATransactionManager, DatasourceTransactionManager, HibernateTransactionManager등을 상황에 맞게 의존성 주입을 받아 사용한다.
Spring이 Spring Web MVC에 대한 PSA를 제공한다고 했다. 추후에 다뤄볼 Servlet 기반으로 Application을 활용하지만 Spring을 사용한다면 개발자는 Servlet에 대한 코드를 직접 다룰 필요가 없다. @Controller, @GetMapping 등의 어노테이션만 잘 활용한다면 보이지 않는 곳에서 Spring이 원하는 기능을 편리하게 처리해준다. 즉 PSA는 서비스 추상화를 통해서 개발자에게 인터페이스를 제공하여 편리성을 극대화 할 수 있는 기법이다.
Spring에서 제공되는 PSA들로 Spring Web MVC, Spring Transaction, Spring Cache 를 언급했다. 각각이 모두 포스팅을 여러개를 잡아먹어도 이상하지 않을 정도로 양이 꽤 되니 각각에 대해서는 차근차근 알아보고 이번 포스트에서는 PSA를 어떤 식으로 이뤄냈는지를 초점을 두고 보도록 하자.
Spring Web MVC
Spring Web MVC는 Model, View, Controller 세가지 구성요소를 사용하여 사용자의 다양한 HTTP Requests를 처리하고 단순한 텍스트 형식의 응답부터 REST 형식, View를 표시하는 html 을 리턴하는 응답까지 다양한 응답을 할 수 있도록 하는 프레임워크이다.
Spring Web MVC는 HTTP Request가 오면 DispatcherServlet이라 불리는 Servlet이 요청을 처리할 Controller를 지정한다. 이때, 이 Servlet이 PSA의 수혜를 받는다. 요청을 받고 처리하는 과정까지 일어날 수 있는 다양한 경우를 위한 기능들이 추상화 되어있고 Spring은 그러한 형태로 기능을 제공한다.
수많은 기능들 중 하나의 예시로 보통 Servlet을 사용하려면 HttpServlet을 상속받고 doGet(), doPost() 등의 메소드를 오버라이딩하여 사용해야 한다. 그리고 일반 클래스에 @Controller 어노테이션을 사용하면 요청을 매핑할 수 있는 컨트롤러 역할을 하는 클래스가 되고 @GetMapping, @PostMapping과 같은 어노테이션을 사용해서 요청을 매핑할 수 있다.
이렇게 Servlet을 위한 모든 기능을 Low Level로 개발하지 않고도, 어노테이션 단 몇개로 간편하게 개발할 수 있다. HttpServlet을 상속받고 doGet(), doPost() 등의 메소드를 구현하는 작업을 굳이 하지 않아도 되기 때문이다. 이외에도 개발자가 이러한 작업을 하지 않아도 되는 부분들은 더 다양하다.
이렇게 Spring Web MVC는 추상화로써 편의성을 제공한다. 또한 코드를 거의 그대로 둔 상태에서 톰캣이 아닌 다른 서버로 실행하는 것도 가능하다. 어노테이션과 상위의 인터페이스들을 활용하여 사용자가 기존 코드를 거의 변경하지 않고, 웹 기술 스택을 간편하게 바꿀 수 있도록 해주는 것이다.
Spring Transaction
먼저 트랜잭션에 대한 이해가 필요하다. 만약 DB의 데이터를 수정하는 도중에 예외가 발생한다면 이전의 상태로 되돌아가져야 한다는 것은 다들 알 것이다. 이렇게 여러 작업을 진행하다가 문제가 발생하면 이전 상태로 롤백(rollback)하기 위해 사용되는 것이 트랜잭션이다. 트랜잭션은 더 이상 쪼갤 수 없는 최소 작업 단위를 의미한다. 트랜잭션은 commit으로 성공하거나 rollback으로 실패 이후 취소 되어야 한다. 하지만 모든 트랜잭션이 동일한 것은 아니고 속성에 따라 동작 방식을 다르게 할 수 있다.
ACID
트랜잭션이 안전하도록 보장하기 위해 만족해야 하는 특성
Atomicity (원자성) : 트랜잭션과 관련된 일은 모두 실행되던지 모두 실행되지 않도록 해야 한다.
Consistency (일관성) : 트랜잭션이 성공했다면 DB는 그 일관성을 유지한다. 일관성은 특정한 조건을 두고 그 조건을 만족하는지 확인하는 방식으로 검사할 수 있다.
Isolation (독립성) : 트랜잭션을 수행하는 도중 다른 연산작업이 끼어들지 못하도록 한다. 임계영역을 두는 것으로 해결할 수 있다.
Durability (영속성) : 완료된 트랜잭션의 결과는 완전히 반영되어야 한다. 그리고 로그를 남겨 이 로그를 활용, 트랜잭션 수행 전 상태로 되돌릴 수 있어야 한다.
"트랜잭션은 commit으로 성공하거나 rollback으로 실패 이후 취소 되어야 한다." 는 것때문에 트랜잭션 처리를 위해 반드시 commit(), rollback()을 명시적으로 호출해야 한다.
하지만 개발자가 DB 기술을 다른 것을 사용할 수도 있다. 그렇기 때문에 한가지 기술에 종속되지 않고 다양한 기술에도 적용할 수 있도록 트랜잭션 관리 부분을 추상화하여 제공하고 있다.
Spring이 제공하는 트랜잭션 경계 설정을 위한 추상 인터페이스는 PlatformTransactionManager이다. 이것을 통해 트랜잭션을 공유하고 커밋하고 롤백할 수 있다.
또한 트랜잭션 코드와 비즈니스 코드가 복잡하여 분리가 필요한 경우 @Transactional 어노테이션을 사용할 수 있는데 이것은 AOP를 적용한 어노테이션이다. @Transactional 어노테이션을 사용하여 트랜잭션 경계 설정 또한 가능하다. 이는 AOP를 활용한 프록시 객체덕분이다. Spring AOP 방식 트랜잭션은 메소드 단위로 관리된다.
Transaction 에 대해서 더 알아보자면 흥미로운 것들이 많다. AOP 개념 까지 있기 때문에 어렵고 복잡하지만 그 과정을 추상화를 통해 얼마나 개발자는 편리하게 사용하는지를 알아가는 과정은 매우 즐거울 것 같다. 그래서 결국 '추상화'를 초점으로 보자면 핵심은 개발자가 원하는 기술 스택으로 PSA를 통해 유연하게 바꿔 사용할 수 있다는 것이다.
Spring Cache
마찬가지로 캐시에 대한 간단한 이해를 하고 넘어가보자. 캐시는 서버의 부담을 줄이고 성능을 높이기 위해 사용되는 기술이다. 어떤 요청을 처리하는데 DB 조회 시간이 오래 걸리거나 계산이 복잡한 경우에 적용하여 요청 결과를 저장해두고 가져옴으로써 빠르게 처리할 수 있는 기술이다.
Spring 프로젝트의 캐싱 사용법에는 메소드에 캐싱을 적용함으로써 캐시에 보관된 정보로 메소드 실행 횟수를 줄인다. 대상 메소드가 실행될 때마다 추상화되어 적용된 캐싱이 해당 메소드가 같은 인자로 이미 실행되었는지 확인하는 동작을 한다. 해당 메소드가 실행되서 저장된 데이터가 존재한다면 실제 메소드를 실행하지 않고 저장된 결과를 반환하며, 해당 인자로 실행된 메소드의 데이터가 존재하지 않으면 메소드를 실행하여 그 결과를 캐싱한 뒤에 사용자에게 반환해서 다음 호출에 사용할 수 있게 한다. 다만 이 방법은 인자가 같으면 호출 결과가 변하지 않는다는 것이 보장되는 메소드에만 적용이 가능하다.
핵심은 캐시는 값을 저장해두고 불러오고 동일한 결과를 반환하는 경우에 용이하다. 만약 매번 다른 결과를 반환하는 상황에 캐시를 적용하면 캐시를 저장하거나 확인하는 작업 때문에 오히려 더욱 성능이 떨어지게 되는 상황을 초래한다.
로컬 캐싱 글로벌 캐싱
Spring Cache는 캐시 기능의 추상화를 지원하며 EhCache, memcache, Redis 등의 추가적인 캐시 저장소와 빠르게 연동하여 bean으로 설정할 수 있도록 한다. 만약 추가적인 캐시 저장소와 연결하지 않으면 ConcurrentHashMap 기반의 Map 저장소가 자동으로 추가되고 이때 사용되는 저장소는 로컬 캐시 저장소이다.
로컬 캐싱은 서버 내부 저장소에 캐시 데이터를 저장하는 것이다. 따라서 속도는 빠르나 서버 간의 데이터 공유가 안된다는 단점이 있다. 반면 글로벌 캐싱은 서버 내부 저장소가 아닌 별도의 캐시 서버를 두어 서버에서 캐시 서버를 참조하도록 하는 것이다. 캐시 데이터를 얻으려고 할 때마다 네트워크 트래픽이 발생하여 로컬 캐싱보다는 속도가 느리지만 서버간의 데이터 공유가 가능하므로 로컬 캐싱의 적합성 문제와 중복된 캐시 데이터로 인한 서버 자원 낭비 등의 문제점을 해결할 수 있다.
핵심
이러한 캐싱기능은 스프링에서는 추상화 즉, PSA로 제공된다. 제공되는 어노테이션을 통해 적용하면 되므로 신경쓸 부분은 캐시 기술을 선정하고 관련 설정을 넣는 것과 실제 스토리지를 사용하는 것이 전부이다. 스토리지 사용의 이유는 캐시 추상화로 캐시 로직은 작성하지 않아도 되나, 실제 캐시 스토어를 제공하는 것이 아니기 때문에 실제 스토리지를 사용해야한다.
또한 Spring Cache는 캐시 관련 어노테이션을 통해 @Transaction 과 마찬가지로 AOP를 이용하여 코드에 영향을 최소화하면서 다양한 캐시 기능을 일관성 있게 사용할 수 있도록 해준다.
정리
PSA 까지 스프링의 3대 요소에 대해 알아봤다. 알아가면 알아갈수록 정말 치밀하고 잘 만들어진 기술들이라는 것을 깨닫게 된다. 아직 다루지 않은 것들은 각 요소들의 대한 더 깊은 내용은 다른 요소들과 연관되어 있거나 선행적으로 개념 이해가 필요하기 때문에 아직 다루지 않았다. 이후에는 PSA를 적용한 여러 Spring 기술들에 대한 포스팅을 할 예정이다.
출처
'Programing & Coding > Spring' 카테고리의 다른 글
[Spring] 8. Annotation & 스프링(Spring)에서 자주 사용하는 Annotation (0) | 2023.06.11 |
---|---|
[Spring] 7. Servlet, Servlet Container, Spring MVC (0) | 2023.05.29 |
[Spring] 5. Spring AOP - 총정리 (2) (0) | 2023.05.22 |
[Spring] 4. Spring AOP 와 디자인 패턴 (1) (0) | 2023.05.18 |
[Spring] 3. Spring 핵심 3대요소 (IoC/DI, AOP, PSA) (2) | 2023.05.07 |