서비스 추상화 (토비의 스프링 vol.1 5장)
사용자 레벨 조정 기능 추가
- 필드 타입이 int일 때
public class User { private int level; public int getLevel() { return level; } public void setLevel(int level) { this.level = level; } } public class Main { public static void main(String[] args) { User user = new User(); user.setLevel(1000); } } -
문제점 : 잘못된 값이나 범위를 넘는 값이 들어올 수 있다
- enum(이늄) : 특정한 값만을 가지게 하는 열거 타입
- enum 사용
public enum Level { BASIC(1), SILVER(2), GOLD(3); private final int value; Level(int value) { this.value = value; } public int intValue() { // DB에 값을 저장할 때 return value; } public static Level valueOf(int value){ // DB에서 값을 가져올 때 switch(value) { case 1: return BASIC; case 2: return SILVER; case 3: return GOLD; default: throw new AssrtionError("Unknown value: " + value); } } }
사용자 레벨을 올리는 작업을 하던 중 문제가 발생해 중단되면 지금까지 작업한 내용은 어떻게 될까?
트랜잭션 경계 설정
- 트랜잭션 : 더 이상 나눌 수 없는 단위 작업
- 특성
- 원자성(Automicity) : 트랜잭션에서 정의된 연산들은 모두 성공적으로 실행되던지 아니면 전혀 실행되지 않은 상태로 남아 있어야 한다 (All or Nothing)
- 일관성(Consistency) : 트랜잭션이 실행 되기 전의 데이터베이스 내용이 잘못 되어 있지 않다면 트랜잭션이 실행된 이후에도 데이터베이스의 내용에 잘못이 있으면 안된다
- 고립성(Isolation) : 트랜잭션이 실행되는 도중에 다른 트랜잭션의 영향을 받아 잘못된 결과를 만들어서는 안된다
- 지속성(Durability) : 트랜잭션이 성공적으로 수행되면 그 트랜잭션이 갱신한 데이터베이스의 내용은 영구적으로 저장된다
- 특성
- 트랜잭션 롤백(transaction rollback) : 작업 중 문제가 발생했을 때, 트랜잭션 처리 과정에서 발생한 변경 사항을 모두 취소하고 트랜잭션을 종료
-
트랜잭션 커밋(transaction commit) : 모든작업을 정상적으로 처리하겠다고 확정하는 명령어
- 트랜잭션의 경계 : 트랜잭션이 시작되고 끝나는 위치
-
트랜잭션 경게설정 : 트랜잭션을 시작하고 종료시키는 작업
- 트랜잭션 경계설정 구조
public void upgradeLevels() throws Exception { //(1) DB Connection 생성 //(2)트랜잭션 시작 try { //(3)DAO 메소드 호출 //(4)트랜잭션 커밋 } catch(Exception e){ //(5)트랜잭션 롤백 throw e; } finally { // (6)DB Connection 종료 } } - 한 트랜잭션에서 동작하게 하려면 해당 DB 커넥션을 사용해야함
- 파라미터로 넘겨주면 문제가 있음(기술에 독립적이지 않음)
트랜잭션 동기화
Connection 오브젝트를 저장소에 보관해두고, 필요할 때 가져다 쓰자

스피링이 제공하는 TransactionSynchronizationManager 사용
- 하나의 트랜잭션 안에서 여러 개의 DB에 데이터를 넣는 작업을 할 수 있을까?
- JDBC의 Connection을 이용한 방식은 로컬 트랜잭션이다
- 하나의 DB Connection에 종속된다
- 글로벌 트랜잭션 방식 사용
- JDBC의 Connection을 이용한 방식은 로컬 트랜잭션이다
- JTA(Java Transaction API) : XA 리소스 (분산 리소스) 간의 트랜잭션을 처리하는 API

-
JTA호출 -> 트랜잭션 시작 -> 트랜잭션 ID 생성 -> 자원 접근 -> 트랜잭션에 참여 요청 -> XA 프로토콜을 통한 동기화 -> 커밋 or 롤백 명령 -> 최종 상태 확인 후 트랜잭션 종료
- 여러 데이터, 자원을 일관되게 관리
**하이버네이트를 이용한 트랜잭션 관리 코드는 JDBC나 JTA와 달리 독자적인 트랜잭션 관리 API를 사용한다
특정 기술환경에 따라 코드가 바뀔 수 있다
트랜잭션 서비스 추상화

- PlatformTransactionManager : 스프링이 제공하는 트랜잭션 경계설정을 위한 추상 인터페이스
- 사용할 DB의 DataSource를 생성자 파라미터로 넣어DataSourceTransactionManger 객체 생성
- JTA 이용 시 DataSourceTransactionManger -> JTATransactionManger 하이버네이트 HibernateTransactionManger JPA JPATransactionManger
단일 책임 원칙
DI는수평적인 분리든 수직적인 구분이든 결합도를 낮추는 것에 있어 핵심적인 역할을 한다
- 단일 책임 원칙(Single Responsibility Principle) : 하나의 모듈은 한 가지 책임을 가져야 한다
- 장점
- 유지보수가 용이해짐
- 자연스러운 디자인 패턴 적용
- 테스트가 쉬움
JavaMail 서비스 추상화
메일 서버에 부담도 주지 않고 매번 검증을 하지 않도록 테스트용 JavaMail을 사용하자
- 운영 메일 서버를 통한 테스트는 서버에 큰 부담
- 테스트 서버를 사용하는 방법을 UserService와 JavaMail 사이에도 적용하자
방법 1
- JavaMail과 동일한 인터페이스를 갖는 코드가 동작하도록 해보자
- JavaMail에서는 Session 오브젝트를 만들어야 메일 메시지를 생성할 수 있는데 Session은 더 이상 상속이 불가능한 final 클래스이고 생성자가 모두 private이여서 직접 생성도 불가능하다
방법 2
- 서비스 추상화를 적용하자
- 스프링에서 제공하는 추상화 기능을 사용
- JavaMail의 서비스 추상화 인터페이스
package org.springframework.mail; public interface MailSender { void send(SimpleMailMessage simpleMessage) throws MailException; void send(SimpleMailMessage[] simpleMessages) throws MailException; }

테스트 대역
테스트할 대상이 의존하는 오브젝트를 DI를 통해 바꾸는 기법이 많이 사용됨
- 테스트 대역 : 테스트 대상과 연관된 오브젝트를 대신하여 그 기능만 충실히 수행하는 오브젝트(가짜 의존성)
- 종류
- Dummy
- 객체가 인스턴스화 되기는 하지만 실제로 테스트에 사용되지 않는 객체
- 보통 매개변수를 채우기 위한 용도로 사용
- Fake
- 프로덕션에는 적합하지 않지만, 실제 동작하는 구현을 제공
- Stub
- 테스트 대상 오브젝트의 의존객체로 존재
- 테스트에 맞게 단순히 원하는 동작을 수행
- 메소드를 통해 전달하는 파라미터와 달리 코드 내부에서 간접적 사용
- Spy
- 호출된 내역을 기록, 기록한 내용은 테스트 결과를 검증할 때 사용
- Mock
- 테스트 대상의 간접적인 출력 결과를 검증하고, 테스트 대상 오브젝트와 의존 오브젝트 사이에서 일어나는 일을 검증
- 기대한 대로 상호작용하는 행위를 검증, 기대한 대로 동작하지 않으면 Exception을 발생할 수 있다
- Mock은 Stub이자 Spy도 된다

- Dummy
댓글남기기