2022-03-21(월)

트랜잭션이 필요한 이유

http://localhost:8080/contact/index.html

20자를 넘는 연락처를 등록해보자

1개 밖에 안 들어감

트랜잭션 적용 전

ml_contact (연락처 테이블)
11 홍길동 [email protected]

오류! 전화번호가 20자리를 초과한다!
오류발생 → 메서드 호출 종료 → 요청 처리 중단
그전까지 입력한 값은 그대로 유지

데이터 처리중 오류발생 시 대응 방법

데이터 변경 작업(입력, 변경, 삭제)에 대한 처리 방법

① 데이터 처리 작업 중 일부 작업이 실패하더라도 그전에 수행한 작업은 그대로 승인하는 경우

승인해도 되는 경우

중간에 안내 메시지만 띄워 주면 됨

일부는 규칙을 따르지 않아서 입력 실패입니다

② 데이터 처리 작업 중 일부 작업에 오류 발생시 그전에 수행한 작업을 모두 취소해야 하는 경우

예)
좌석예약 (결제 실패 → 예약 취소)
일부 좌석 예약 실패 → 전체 예약 취소

all or zero
모두 성공하거나 모두 실패해야 됨

주문 (결제실패 → 주문 취소)

모든 데이터 변경 작업이 옳게 수행되거나 모두 취소되어야 하는 상황
→ 트랜잭션 적용 필요

트랜잭션 적용 후

🔹 트랜잭션 : 여러 개의 데이터 조작 작업(입력, 변경, 삭제)을 한 단위로 묶은 것

데이터 조작 (manipulation)

데이터 처리가 단위 별로 수행된다.

단위에 묶인 모든 작업이 성공하면 DB에 적용

① 트랜잭션 적용 전
즉시 적용

② 트랜잭션 적용 후
한 단위로 묶임
임시 DB
commit 전까지 임시 보관
commit 하면 실제 테이블에 적용

만약 오류가 발생하지 않을 경우에만 입력을 완료하고 싶다면
입력 작업들을 한 단위로 묶어라!

트랜잭션을 설정한다

'스프링부트 트랜잭션 설정' 검색

App 클래스 변경
트랜잭션을 활성화시킨다

@EnableTransactionManagement // 애노테이션으로 트랜잭션을 제어할 수 있게 한다.

트랜잭션 관리자 객체 준비

https://spring.io/projects/spring-framework#learn

  @Bean
  PlatformTransactionManager transactionManager(DataSource ds) {
    return new DataSourceTransactionManager(ds);
  }

GA : 정식 버전
SNAPSHOT : 개발 버전
PRE : 곧 정식 버전

절대 5.3으로 가면 안 됨

https://docs.spring.io/spring-framework/docs/current/reference/html/data-access.html#tx-prog-template

트랜잭션을 적용한다

@RestController 
public class ContactController {

  @Autowired
  ContactDao contactDao;


  @Autowired
  TransactionTemplate transactionTemplate;

스프링에서 설정하는 방법

Spring에서 트랜잭션 적용하기

35 페이지

1.5.1. Using the TransactionTemplate

TransactionTemplate으로 다루는 방법

https://docs.spring.io/spring-framework/docs/current/javadoc-api/

doInTransaction

<<TransactionCallback>>
doInTransaction() {
  트랜잭션으로 묶어서 실행할 작업
}

🔹 TransactionTemplate : 작업 실행 및 트랜잭션 제어

🔹 TransactionManager
• commit()
• rollback()

① excute(↑)
② TransactionTemplate ---call---> ↑
③ TransactionManager
정상 실행 → commit() 호출
예외 발생 → rollback() 호출

객체 지향 : 역할을 쪼개고 각 클래스에게 역할 분담

클래스 관계도를 빨리 파악해야 됨
이 객체가 어떤 일을 하는지 빨리 역할을 파악하기

https://docs.spring.io/spring-framework/docs/current/javadoc-api/

Spring Boot

스프링부트는 패킹한 거

// Spring Boot
• 기본 설정 파일 => *.properties
• 기본 의존 라이브러리 => *.jar
• Web MVC => Spring Framework
• Servlet/JSP => Tomcat Server

스프링부트를 안 쓰고 직접 설정해도 됨

서비스 객체 도입

업무 관련 코드와 트랜잭션 관련 코드를 캡슐화(별도 분리)

① 이전 방식
ContactController ---> ContactDao ---> MyBatis ---> JDBC Driver ---> DBMS

MyBatis는 JDBC Driver 캡슐화 한 거. MyBatis가 내부적으로 호출함.

숨겨져 버림

ContactController
• 요청 파라미터 분석
• 응답 데이터(JSON) 가공
• 업무 로직 + 트랜잭션 제어
ml_contact 테이블, ml_cont_tel 테이블에 insert
=> 분리시키는 게 유지보수에 낫다

② 개선
ContactController
• 요청 파라미터 분석
• 응답 데이터 가공

ContactService
• 업무 로직
• 트랜잭션 제어

ContactController --call--> ContactService

분리 이유
업무 로직을 분리하여 다른 컨트롤러에서 재사용하기 쉽게 한다.

현업에서는 이 방법 사용

'DAO와 테이블 관계' 다시 참고하기
DAO와 테이블 관계 (90-MyList프로젝트2 / 27 페이지)

서비스 객체와 인터페이스

ContactController --call--> ContactService

고객사 A ContactService
A 고객사는 연락처 등록 중 오류가 발생하더라도 그전에 입력한 정보는 승인하기를 원한다

고객사 B ContactService
B 고객사는 연락처 등록 중 오류가 발생하면 그전까지 입력한 정보를 모두 취소하길 원한다.

업무 로직을 별도의 클래스(서비스객체)로 분리하니까
고객사별로 대응하기가 쉬워진다.

개선 사항!
컨트롤러에서 클래스를 바로 사용하게 되면
업무로직 방식이 바뀔 때마다
클래스를 변경해야 한다
=> 유지보수 불편!

요청 파라미터를 가공하는 일은 ContactController가 해야 됨

분리할 때 어떻게 달라지는지..

데이터 처리는 서비스에서 한다

앞으로는 컨트롤러 객체랑 서비스 객체로 분리해야 한다

@Service 붙이기

@Service
public class ContactService {

  @Autowired
  ContactDao contactDao;

ContactServiceTransaction

  @Autowired
  TransactionTemplate transactionTemplate;
@Service
public class ContactServiceTransaction {

  @Autowired
  ContactDao contactDao;

  @Autowired
  TransactionTemplate transactionTemplate;

functional Interface : 추상메서드가 1개 짜리인 인터페이스

람다 문법을 적용하려면 functional Interface여야 한다.

execute의 리턴 값은 doInTransaction의 리턴 값과 같다

익명 inner class를 파라미터 자리에 둔다

람다 문법까지 적용한 transaction 처리

90-MyList프로젝트2 / 35 페이지 알고 있기

@Service가 안 붙으면 인스턴스를 자동 생성하지 않는다

여기만 바꾸면 됨

ContactServiceTransaction
ContactServiceNonTransaction

개선 사항!
컨트롤러에서 클래스를 바로 사용하게 되면
업무로직 방식이 바뀔 때마다
클래스를 변경해야 한다
=> 유지보수 불편!

해결책
인터페이스 문법

1.5.3. Using the TransactionManager

트랜잭션 실행 정책

def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

PROPAGATION_REQUIRED

전파 정책

caller (client)

호출자와 같은 트랜잭션에 묶인다
예외가 발생하면 이전에 수행한 모든 작업들이 취소된다

호출자가 트랜잭션이 없으면
호출자가 트랜잭션에 속해 있지 않으면 새 트랜잭션을 만든 후 실행한다.

PROPAGATION_REQUIRES_NEW

Requires_New

Propagation.SUPPORT

support

Propagation.MANDATORY

Mandatory

Propagation.NOT_SUPPORT

Propagation.NEVER
트랜잭션이 존재하면 에러 뜸

보통은 REQUIRED 사용

DefaultTransactionDefinition 15년 전 기술

2번 방법을 미리 짜놓은 게 TransactionTemplate (1번 방법)

더 쉬운 방법은 @Transactional (3번 방법)

4번째 방법은 AOP
애노테이션조차 붙일 필요 없음

서비스 객체에 인터페이스 적용

특정 클래스에 종속되는 것을 줄일 수 있다

ContactController ---사용---> <<interface>> ContactService

<<interface>> ContactService을 구현한
ContactServiceNonTransaction
ContactServiceTransaction
ContactServiceTransaction2
ContactServiceTransaction3
중에서 아무거나 꽂으면 된다

직접 클래스를 지정하는 대신에 인터페이스를 지정하면 객체를 교체하기 쉽다
=> 유지보수가 쉽다

html template rendering

javascript template engine

자바스크립트 안에 있는 html 코드를 뜯어낸다

오늘은 13.3 까지...

좋은 웹페이지 즐겨찾기