* 트랜잭션 (Transaction, TX)
가. 분산 TX ( == Global TX)
- 한 요청 처리에 2개 이상의 연결(Connection)을 사용하여 SQL 처리를 하는 트랜잭션
- 동일한 DB에 2개 이상의 연결 (JDBC Connection)들을 사용
- 각 연결 (JDBC Connection)마다, 각각 다른 DB를 사용
- 개발자가 직접 TCL 사용 불가! (TX 관리자가 대신 수행)
- 스프링은 @Transactional 어노테이션으로 TX 관리
- 2PC 사용 (완전하지 않음) : 2 Phase Commit
ex) 2개의 데이터 소스 사용 (각각 연결된 DB가 다름)
1st phase : 현재 분산 TX에 참여한 각각의 데이터 소스에게 Commit할 준비가 되어 있는지 물어봄
2nd phase : 각 데이터 소스에 연결된 DB의 TX 관리자에게 Commit 부탁
- TX관리자 (규약 : X/OPEN XA (분산TX)) : WAS, Spring, Database
나. 지역 TX ( == Local TX)
- 1개의 연결 (JDBC Connection)으로, 여러개의 SQL(DML) 문장을 수행하는 경우의 트랜잭션
- 이때 TCL (commit/rollback 문장) 사용
- 분산 TX 관리는 애시당초 불가능!
* Transactional Boundary (하나의 TX로 처리할 Connection의 범위)
- 하나의 TX로 처리(All or nothing)할 DML 작업의 범위
* 계좌이체 : DataSource 객체 활용 (Connention Pool)
* 항공사의 2가지 시스템 : (1) 예약(티켓), (2) 발권
* Connection.commit(), Connection.rollback(), => for local TX
try {
Connection c1 = ...
Connection c3 = ...
Connection c2 = ...
c1.SQL(DML, 10번)
c2.SQL(DML, 2번)
c3.SQL(DML, 1번)
c1.commit(); ---> OK : 영속성 부여완료 (영구기록)
c2.commit(); ---> Network 끊김 -> 오류발생
c3.commit();
} catch (SQLException e) {
c1.rollback(); ---> XX : 이미 try 블록에서 영속성 부여완료
c2.rollback(); ---> XX : 이미 끊어진 연결이기 때문
c3.rollback(); ---> XX : 수행조차 안 됨
}
---
* 트랜잭션 전파(Transaction Propagation)
@Transactional // 분산 트랜잭션 처리 => 즉, 스프링의 트랜잭션 관리자를 이용한 2PC 수행(***)
1. @Transactional(propagation=Propagation.REQUIRED) // Default
기본 : 새로운 TX 만들어 실행 (격리성을 가짐)
2. @Transactional(propagation=Propagation.MANDATORY)
반드시 특정(***) TX 하에서만 수행가능
비유: 물이 반드시 필요는 한대, 자기의 물을 만들지 않고, 이미 흐르는 `특정` 물에 자기 몸을 담가서 같이 흘러가겠다)
3. @Transactional(propagation=Propagation.NESTED)
기존 TX 이 존재하는 경우, 이 Tx에 포함되어 수행
(비유: 흐르는 물에 몸을 담그겠다. 대신 어떤 물이든 괜찮다!!!)
4. @Transactional(propagation=Propagation.SUPPORTS)
TX가 필요없으나(***), TX 하에 있다면 포함되어 실행
(비유: 굳이 흐르는 물이 있으면 몸을 담그고, 없으면 오히려 좋다!)
5. @Transactional(propagation=Propagation.NEVER)
TX 없이 수행. 만일 TX하에서 수행하면 오류발생
(비유: 난 절대 물이 필요없다. 오히려 물에 담그면 죽는다!)
6. @Transactional(propagation=Propagation.NOT_SUPPORTED)
기존 TX가 있는 경우, 이 TX이 끝날 때까지 실행을 잠시 보류한 후, 기존 TX이 끝나면 재수행
(비유: 나 역시 물이 필요한데, 이미 흐르는 물이 있으면, 다 지나갈때까지 기다렸다, 나만의 물을 만들어 흘러가겠다)
7. @Transactional(propagation=Propagation.REQUIRES_NEW)
무조건 자신만의 고유한 TX 생성하여 실행
(비유: 기존에 흐르는 물이 있더라도, 나만의 물을 별도로 만들어 가겠다)
---
* 트랜잭션 격리레벨(Transaction Isolation Level)
DQL작업과 관련이 있음
대전제 : 각 트랜잭션 격리된다(Isolation 성질을 가진다). 즉, 트랜잭션들끼리는 서로 간섭하지 않는다
하지만, 위의 대전제는 격리수준(Isolation Level)을 조절함으로써, 깨뜨릴 수 있다!
1. DEFAULT : 사용하는 데이터소스에 설정된 격리수준을 그대로 따르겠다!! => 기본설정 / 기본 격리수준은 READ_COMMITTED 임
2. READ_COMMITTED : SELECT 수행시, 읽을 수 있는 데이터는 commit이 완료된 데이터만 읽을 수 있다!!
3. READ_UNCOMMITTED : READ_COMMITTED의 반대 => 즉, 커밋이 완료되지 않는 데이터도 읽을 수 있다!!
4. REPEATABLE_READ : 테이블의 같은 행에 대해, 두가지 이상의 다른 행위를 하는 트랜잭션들이 있을때,
이 행을 반복적으로 읽어온다 하더라도, 다른 트랜잭션 변경결과를 읽어오지 않는다
5. SERIALIZABLE : 가장 강력한 격리수준으로, 각 트랜잭션을 먼저 도착한 순서대로, 각자의 트랜잭션 처리를 수행하고, 그 다음 트랜잭션 수행하고, 그 다음 ....
---
* Password 암호화
* Bcrypt => 수리적으로 단방향 해쉬 생성
- 특징: 1비트까지 동일한 데이터라고 해도, 매번 해쉬를 수행할 때마다, 다른 해쉬값을 생성
- 로그인(NOT 회원가입) 수행시, 사용자가 입력한 암호데이터와 회원테이블에 저장된 해쉬값을 비교
- 사용자가 입력한 암호(평문-Plain Text) => 동일한 Bcrypt 알고리즘통과
=> 해쉬값(Cipher Text) 생성하긴 하는데, 매번 다르다!!!
=> 그런데 위의 특징때문에, 매번 같은 데이터(암호)라고 해도, 다른 해쉬값이 생성되는데 어떻게 비교하느냐!?
- 매번 같은 데이터에 대해서, 다른 해쉬값을 생성하더라도, 수리적으로 평문과 이전 해쉬값을 비교해도, 단 1비트라도 다르지 않으면 동일한 결과(=즉, 같다!!!!)가 나오고, 다른 해쉬값하고 비교해도 동일한 결과(=즉, 같다!!!)가 나오게 수리적으로 처리.
@Log4j2
@NoArgsConstructor
@TestInstance(Lifecycle.PER_CLASS)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class BCryptPasswordEncoderTests {
@Disabled
@Test
@Order(1)
@DisplayName("1. testBCryptPasswordEncoder")
@Timeout(value = 10, unit =TimeUnit.SECONDS)
void testBCryptPasswordEncoder() {
log.trace("testBCryptPasswordEncoder() invoked.");
String password = "John1234!!"; // Plain Text
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
String cipherText = encoder.encode(password);
log.info("\t+ cipherText : {}", cipherText);
} // testBCryptPasswordEncoder
@Disabled
@Test
@Order(2)
@DisplayName("2. testMatches")
@Timeout(value = 10, unit =TimeUnit.SECONDS)
void testMatches() {
log.trace("testMatches() invoked.");
String plainText = "John1234!!"; // Raw Password
// Case1 : 저장된 해쉬값과 입력한 암호의 비교
// BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
// String cipherText = encoder.encode(plainText);
// log.info("\t+ cipherText : {}", cipherText);
//
//// ------------------------------------
//
// // plainText : 로그인 창에서 사용자가 입력한 Raw Password
// // cipherText : 사용자 테이블의 암호컬럼에 저장된 해쉬값
// boolean isMatched = encoder.matches(plainText, cipherText);
// log.info("\t+ 2. isMatched : {}", isMatched);
// Case2 : 같은 암호(Plain text)에 대해서 여러번 해쉬값을 생성하여 비교
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
for(int i=0; i<10; i++) {
String cipherText = encoder.encode(plainText);
log.info("\t+ [{}]. cipherText : {}", i, cipherText);
boolean isMatched = encoder.matches(plainText, cipherText);
log.info("\t+ [{}]. isMatched : {}",i, isMatched);
} // for
} // testMatches
@Test
@Order(3)
@DisplayName("3. testBCryptPasswordEncoderWithSalt")
@Timeout(value = 10, unit =TimeUnit.SECONDS)
void testBCryptPasswordEncoderWithSalt() {
log.trace("testBCryptPasswordEncoderWithSalt() invoked.");
String password = "John1234!!"; // Plain Text
String salt = "SALT_";
// 암호의 해쉬값 생성시, "salt"추가 권장
// "Salt" - 사용자가 가입시 입력한 암호 + 내부에서 지정된 추가 문자열
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
String cipherText = encoder.encode(salt + password);
log.info("\t+ 1. cipherText : {}", cipherText);
// ---
log.info("\t+ 2. isMatchWithSalt : {}", encoder.matches(salt+password, cipherText) );
} // testBCryptPasswordEncoderWithSalt
} // end class
BCryptPasswordEncoderTests.java
---
* Git & Github
1. git : 버전관리(VCS, Version Control System) 도구
2. github : 원격저장소, 내 PC에 저장해 놓은 버전관리를 위해서 만든 원본파일 + 변경내역등을 백업하는 웹사이트.
git을 이용한 버전관리의 큰 흐름을 우선적으로 이해하고 실습하자!!! (GUI로)
그 다음에 이 큰 흐름을 명령어 (CLI)로 그대로 수행
https://git-scm.com/downloads
'국비학원' 카테고리의 다른 글
[국비지원] KH 정보교육원 118일차 (0) | 2022.09.18 |
---|---|
[국비지원] KH 정보교육원 117일차 (0) | 2022.09.14 |
[국비지원] KH 정보교육원 115일차 (0) | 2022.09.13 |
[국비지원] KH 정보교육원 114일차 (0) | 2022.09.08 |
[국비지원] KH 정보교육원 113일차 (0) | 2022.09.07 |