개념
트랜젝션은 최소의 코드에만 적용하는 것이 좋다. 이 원칙을 지키기 위해서 다음을 확인 해봐야 한다.
- 트랜젝션을 시작(데이터 베이스 커넥션을 생성)은 DBMS에 작업하는 코드부터 시작해서 가능한 빠르게 COMMIT을 해 트랜젝션을 종료하는 것이 좋다
: 일반적으로 데이터베이스 커넥션은 개수가 제한적이어서 각 단위 프로그램이 커넥션을 소유하는 시간이 길어질수록 사용 가능한 여유 커넥션의 개수는 줄어들기 때문이다. 그리고 어느 순간에는 각 단위 프로그램에서 커넥션을 가져가기 위해 기다려야하는 상황이 발생할 수도 있다.
- 네트워크 작업은 트랜젝션에서 제거한다.
: 네트워크 장애가 날 경우 웹 서버 뿐 아니라 DBMS까지 락이 걸릴 수 있다.
- 하나로 묶어야하는 작업 단위는 하나의 트랜젝션 안에 넣는다.
: 예를 들어 게시글 내용 저장과 글의 첨부파일 정보 저장은 하나의 트랜젝션 안에서 실행되어야, 실패했을 경우 한 단위로 롤백된다. 서로 성격이 다른 경우 트랜젝션을 분리한다. 예를 들어 게시글 작성의 알림 메일 발송 이력을 저장하는 작업은 앞선 작업과 별개의 트랜젝션으로 실행한다.
- 단순조회(Query)문은 트랜젝션을 사용하지 않는다.
사용법
위의 원칙을 지켜서 댓글을 작성한 후 알림을 보내는 코드를 수도코드로 짜보면 아래와 같다.
class PostDto {
title: string;
content: string;
attachments: string;
// constructor 생략
isValid(){
// attachments의 유효성 검사
}
}
export class Post {
async create(userId: number, dto: PostDto){
// 유저 정보 확인
const user = await userRepository.getUser(userId);
if(!user){
throw new Error("존재하지 않는 유저입니다.")
}
if(!user.isLogined){
throw new Error("로그인이 필요합니다.")
}
// 글 내용 오류 발생 여부 확인
dto.isValid()
// 글과 첨부파일 객체 생성
const post = Post.of(dto.title, dto.content)
const attachment = Attachment.of(dto.attachments)
// <==== DB 커넥션 생성 + 트랜젝션 시작
// 사용자 입력 정보, 첨부 파일 정보를 DBMS에 저장
const id = await transaction(() =>{
persist([post, attachments])
})
// <==== 트랜젝션 종료
// 저장된 내용을 DB에서 조회(Query)
const savedPost = postRepository.getOne(id)
// 게시물 등록에 대한 알림 발송
const success = await publishEvent(savedPost)
// 알림 메일 발송 이력 DB에 저장 <==== DB 커넥션 생성 + 트랜젝션 시작
if(success){
const event = Event.of(savedPost)
await transaction(() =>{
persist(event)
})
}
// <==== 트랜젝션 종료
// 처리 완료
}
}
- 위의 예시에서는 DB에 저장하는 작업을 트랜젝션 단위로, '작성글과 첨부파일을 저장'할 때 한 번, '알림 메일 발송 이력 저장'할 때 한 번.두 번 나눴다.
- 이메일 발송과 같이 네트워크 작업은 트랜젝션을 사용하지 않았다.
- post나 user 조회와 같이 단순조회(Query)문은 트랜젝션을 사용하지 않았다.
- 유효성 확인, 조회 와 같이 트랜젝션이 필요하지 않은 코드들은 단 한 줄이라도 트랜젝션에서 넣지 않았다.
이와 같이 트랜젝션은 꼭 필요한 코드에만 최소한으로 적용하는 것이 좋다.

Reference
Mysql 8.0 5장
'백엔드 개발 > 백엔드 일기' 카테고리의 다른 글
#064. DB 스키마 작성 기본 개념 (0) | 2023.11.06 |
---|---|
#063. 단건 쿼리를 복수형 쿼리로 바꾸기 + map 생성방법 (0) | 2023.09.12 |
#024. TS 선언문(:타입)과 단언문(as 타입) 구분해 사용하기 (0) | 2023.01.26 |
#023. 무중단 배포: Readiness 와 Liveness 설정하기 (1) | 2022.12.30 |
#022. github의 graphql explorer로 private repository 접근하기 (0) | 2022.11.09 |