티스토리 뷰

1.    트랜잭션의 원칙 ( ACID )

 

-      1 ) A ( Atomicity : 원자성 )

-       : 하나의 트랜잭션은 모두 하나의 단위로 처리되어야 한다.

-       : 어떤 트랜잭션이 AB로 구성되어 있을 경우, AB의 처리 결과는 동일해야 되기 때문에 둘 중 하나라도 실패할 경우 원래의 상태로 돌아가야 한다. ( ALL or Nothing )

 

-      2 ) C ( Consistency : 일관성 )

-       : 트랜잭션이 성공했다면, 데이터 베이스의 모든 데이터는 일관성을 유지해야 한다.

-       : 트랜잭션으로 처리된 데이터와 일반 데이터 사이에는 어떠한 차이도 있으면 안된다.

 

-      3 ) I ( Isolation : 격리 )

-       : 트랜잭션으로 처리되는 중에 외부에서의 간섭은 없어야 한다.

 

-      4 ) D ( Durability : 영속성 )

-       : 트랜잭션이 성공적으로 처리되면, 그 결과는 영속적으로 보관되어야 한다.


[ 1. 트랜잭션 ] (*****)

 

[ 1 - 1. Service 인터페이스 생성 ]

 

더보기

[ + 코드 보기 ]

package org.zerock.myapp.service;

import org.zerock.myapp.exception.ServiceException;

public interface SampleTXService {
	
	public abstract void addData(String data) throws ServiceException;

} // end interface

 

[ 1 - 2. Service 구현 ] (****)

[ + UncategorizedSQLException 예외를 그대로 던져줘야지만 트랜잭션 메니져가 구동된다. (***) ]

[ + 트랜잭션 메니저는 UncategorizedSQLException를 받아야만 분산처리를 해준다. (***) ]

[ + 만약, UncategorizedSQLException를 다른 예외로 감싸서 예외를 던질 경우 오류가 발생하게 된다. ]

 

더보기

[ + 코드 보기 ]

package org.zerock.myapp.service;

import java.util.Objects;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.UncategorizedSQLException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.zerock.myapp.exception.ServiceException;
import org.zerock.myapp.mapper.Sample1Mapper;
import org.zerock.myapp.mapper.Sample2Mapper;

import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.extern.log4j.Log4j2;


@NoArgsConstructor
@Log4j2

@Service
public class SampleTXServiceImpl implements SampleTXService {
	
	// ============================================
	// + 1. 의존성 주입
	// ============================================
	@Setter(onMethod_= {@Autowired})
	private Sample1Mapper mapper1;
	
	@Setter(onMethod_= {@Autowired})
	private Sample2Mapper mapper2;
	
	// ============================================

	@Transactional
	@Override
	public void addData(String data) throws ServiceException {
		
		log.trace("addData({}) invoked.", data);
		
		try {
			
			// ============================================
			// + 의존성 주입 확인
			// ============================================
			Objects.requireNonNull(this.mapper1);
			assert this.mapper2 != null;
			
			// ============================================
			// + 아래 2개의 DML 작업은 1개의 트랜잭션으로 처리해야 한다.
			// ============================================
			this.mapper1.insertCol(data);						// 가정 1 : 소스계좌에서 출금
			this.mapper2.insertCol(data);						// 가정 2 : 타겟계좌에 입금
			
			log.info("\t + Transfer done Successfully!!");
			
		} catch ( UncategorizedSQLException e ) {
			
			// ============================================
			// + Spring TX 메니저가 Global 트랜잭션 처리를 해주려면
			// + 이와 같이 UncategorizedSQLException 예외를 그대로 throw해줘야 한다. (****)
			// + 만약, 이렇게 해주지 않으면, Global 트랜잭션 처리가 되지 않는다.
			// ============================================
			throw e;
			
		} catch(Exception e) { 
			
			log.info("\t + Transfer done Failure....");
			log.info("\t + Error Type : {}", e.getClass());
			log.info("\t + Error Type : {}", e.getClass().getName());
			
			throw new ServiceException (e);
			
		} // try - catch

	} // addData
	
	// ============================================

} // end class

 

[ 1 - 3. test 하기 ]

 

더보기

[ + 코드 보기 ]

package org.zerock.myapp.service;

import static org.junit.jupiter.api.Assertions.assertNotNull;

import java.util.concurrent.TimeUnit;

import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.TestInstance.Lifecycle;
import org.junit.jupiter.api.TestMethodOrder;
import org.junit.jupiter.api.Timeout;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.zerock.myapp.exception.ServiceException;

import lombok.NoArgsConstructor;
import lombok.extern.log4j.Log4j2;

@Log4j2
@NoArgsConstructor

@ExtendWith(SpringExtension.class)
@ContextConfiguration(locations = "file:src/main/webapp/WEB-INF/**/root-context.xml")

@TestInstance(Lifecycle.PER_CLASS)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class SampleTXServiceTests {
	
	// ===================================================
	// + 1. 의존성 주입
	// ===================================================
	@Autowired
	private SampleTXService service;
	
	// ===================================================
	// + 2. 의존성 주입 확인
	// ===================================================
	@BeforeAll
	void beforeAll () {
		log.info("beforeAll() invoked.");
		
		assertNotNull(this.service);
		log.info("\t + this.service : {}", this.service);
	} // beforeAll
	
	// ===================================================
	// + 3. 분산 트랜잭션 처리 (***)
	// + -> 즉, 스프링의 트랜잭션 관리자를 이용한 2PC 수행
	// ===================================================
	@Test
	@Order(1)
	@DisplayName("1. testTransfer")
	@Timeout( value = 3 ,unit = TimeUnit.SECONDS)
	void testTransfer() throws ServiceException {
		
		log.info("testTransfer() invoked.");
		
		String data = "999999999999999999999999999999999999999999999999999999999"; // 하나의 sql문에서 오류
		this.service.addData(data);
		
		log.info("\t + ALL Insert Success!!");
		
	} // testTransfer

} // end class

[ 2. 트랜잭션 전파 ] (***)

 

더보기

[ + 코드 보기 ]

// ===============================================================================
// + 2. @Transactional propagation ( 트랜잭션 전파 )
// + : 2개 이상의 TX가 존재할 경우 로직에 따라 결정해야 된다. (***)
// ===============================================================================
// + NESTED와 SUPPORTS는 상당히 유사하지만,
// + SUPPORTS는 TX가 필요 없다는 것이 전재지만, NESTED는 TX가 필요하다는 전재이다.
// ===============================================================================
// + MANDATORY와 NESTED는 둘 다 TX가 필요하고 중첩이 가능하다는 점에서 유사하지만,
// + MANDATORY는 자신만의 TX를 생성하는 것이 아니라, 이미 존재하는 특정한 TX하에서 수행
// + NESTED는 이미 존재하는 TX하에서 수행하지만, 특정 TX를 따지지는 않는다.
// ===============================================================================
// + REQUIRED는 시작지점에 사용되기에 새로운 TX를 생성하여 실행되게 되고,
// + REQUIRES_NEW는 중간에 새로운 TX를 만들어서 각자 다른 길로 실행되게 된다.
// + 그렇기에 REQUIRED는 근원이 되는 메소드에 붙여줘야 한다. (***)
// ===============================================================================

// 1. @Transactional(propagation = Propagation.REQUIRED)				// Default : 새로운 TX를 만들어 실행 ( 격리성 가짐 )
// 2. @Transactional(propagation = Propagation.MANDATORY)				// 반드시 특정 TX 하에서만 사용 가능
// 3. @Transactional(propagation = Propagation.NESTED)					// 기존 TX가 존재할 경우, 이 TX에 포함되어 수행
// 4. @Transactional(propagation = Propagation.NEVER)					// TX 없이 수행 ( TX 하에서 수행되면, 오류발생 )
// 5. @Transactional(propagation = Propagation.NOT_SUPPORTED)			// 기존 TX가 있는 경우, 그 TX가 끝날때까지 실행을 보류
// 6. @Transactional(propagation = Propagation.REQUIRES_NEW)			// 무조건 자신만의 고유한 TX를 생성하여 실행
// 7. @Transactional(propagation = Propagation.SUPPORTS)				// TX가 필요없지만, 이미 TX 하에 있다면 포함되어 실행

[ 3. 트랜잭션 격리 레벨 ] (***)

 

더보기

[ + 코드 보기 ]

// ===============================================================================
// + 3. @Transactional isolation ( 트랜잭션 격리 레벨 )
// ===============================================================================
// + 대전제 : 각 트랜잭션은 격리된다. ( isolation 성질을 가진다. )
// + 그렇기에 각 트랜잭션끼리는 서로 간섭하지 않는다.
// + 하지만, 대전제는 격리 레벨을 조절함으로써 깨뜨릴 수 있다.
// ===============================================================================
// 1. @Transactional(isolation =Isolation.DEFAULT )	- 사용하는 데이터 소스에 설정된 격리 수준을 따르겠다.
//							- 기본설정의 기본 격리 수준은 READ_COMMITTED이다.

// 2. @Transactional(isolation =Isolation.READ_COMMITTED ) - SELECT 수행시, 읽을 수 있는 데이터는 commit이 완료된 데이터만 읽는다.

// 3. @Transactional(isolation =Isolation.READ_UNCOMMITTED ) - 커밋이 완료되지 않은 데이터도 읽을 수 있다.

// 4. @Transactional(isolation =Isolation.REPEATABLE_READ )	- 테이블의 같은 행에 대해, 2가지 이상의 다른 행위를 하는 트랜잭션이 있을때,
//								- 이 행을 반복적으로 읽더라도 다른 트랜잭션 변경결과를 읽어오지 않는다.

// 5. @Transactional(isolation =Isolation.SERIALIZABLE ) - 가장 강력한 격리 수준
//							- 각 트랜잭션을 먼저 도착한 순서대로 각자의 트랜잭션 처리를 수행한다.
// ===============================================================================

[ 4. 패스워드 암호화 ] (****)

 

[ 4 - 1. pom.xml에 spring security core 디펜던시 추가 ]

 

 

[ 4 - 2. BCryptPasswordEncoder를 통해 암호화 하기 ]

 

더보기

[ + 코드 보기 ]

package org.zerock.myapp.util;

import java.util.Objects;
import java.util.concurrent.TimeUnit;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.TestInstance.Lifecycle;
import org.junit.jupiter.api.TestMethodOrder;
import org.junit.jupiter.api.Timeout;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

import lombok.NoArgsConstructor;
import lombok.extern.log4j.Log4j2;


@NoArgsConstructor
@Log4j2

@TestInstance(Lifecycle.PER_CLASS)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class BCryptPasswordEncoderTests2 {
	
	@Test
	@Order(1)
	@DisplayName("1. testBCryptPasswordEncoder")
	@Timeout(value = 3, unit = TimeUnit.SECONDS)
	void testBCryptPasswordEncoder() {
		
		log.trace("testBCryptPasswordEncoder() invoked.");
		
		String pw = "YOYOYO123456!!";			// 원래 암호
		String pw2 = "YOYOYO123456!!" + "__SALT__";			// 원래 암호 + 솔트
		
		BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
		// + 비밀번호를 해쉬 값으로 암호화해주는 클래스
		
		String cipherText = encoder.encode(pw); // cipherText == Hash Value ( 암호화한 값 )
		boolean isMatched = encoder.matches(pw, cipherText); // 비밀번호와 암호화된 값이 같은 값인지 확인 (***)
		
		Objects.requireNonNull(cipherText);
		log.info("\t + 1. cipherText : {}", cipherText);
		log.info("\t + 2. cipherText.length : {}", cipherText.length());
		
		log.info("\t + 3. isMatched : {}", isMatched);
		
	} // testBCryptPasswordEncoder

} // end class

[ 5. Open API 활용하기 위한 기본 준비 ] (***)

 

[ 5 - 1. 공공데이터 포털에서 Open API 신청 ]

 

 

[ 5 - 2. 참고문서를 통해서 해당 Open API 사용법 알아보기 ]

 

 

[ 5 - 3. pom.xml에 http client 디펜던시 추가 ]

 

 

 

 

 

728x90
댓글
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
최근에 올라온 글
Total
Today
Yesterday
공지사항