[개발일지] 24.01.24 - nestJS의 트랜잭션에 대해서

태양신 니카·2024년 1월 24일
0

개발일지

목록 보기
4/5
post-thumbnail

score를 insert 하고 update 할 수 있는 엔드포인트를 형성하는 작업을 하던 중 처음으로 트랜잭션을 사용해야하는 경우가 있었다.

그래서 이번 일지에서는 트랜잭션이 뭐고 어떤 상황에서 사용했는지, 어떻게 사용했는지에 대해 이야기해보려한다.

Transaction이란


쪼갤 수 없는 최소 단위의 업무를 의미한다.

이렇게 처리를 해야하는 이유로는 DB 쿼리가 실패했을 경우 데이터를 안전하게 되돌리기 위해서이다.

우리가 중요하게 생각하는 돈에 대한 것 중 입출금에 대해서 트랜잭션으로 반드시 관리를 해야한다.

A가 10000원을 B에게 송금을 한다면 이 과정 속에서 A의 잔고가 -10000원이 되어야하고 B의 잔고는 +10000원이 되어야한다.
만약 중간에 오류가 나서 A만 잔고가 감소하게 된다면 10000원의 행방이 사라진다.

이런 사태를 방지하기 위해 저 일련의 과정을 '트랜잭션'으로 지정한다.

쿼리들이 성공적으로 모두 처리된다면 commit 하여 트랜잭션이 성공적으로 종료되었음을 트랜잭션 매니저에게 알린다.

만약 중간에 오류가 발생했다면 rollback을 수행하여 트랜잭션이 시작되기 전의 상태로 되돌리고 exception을 던져준다.

TypeORM으로 트랜잭션 관리하기


spring에서는 트랜잭션을 관리하기 위해 어노테이션을 사용할 수 있다.

현재 내가 하고 있는 nestJS 프로젝트에서는 TypeORM의 DataSource를 활용하여 관리할 수 있다.

먼저 트랜잭션을 관리했어야하는 상황은 다음과 같다.

프론트에서 업데이트된 스코어 리스트들을 받아온다.
스코어 리스트들을 하나씩 순회하면서 각 스코어 레코드별로 업데이트를 진행한다.

이를 위해서 공식문서를 참고하여 다음과 같이 코드를 작성하였다.

async updateScores(applicationId: number, updateScoresDto: UpdateScoresDto){

        const queryRunner = this.dataSource.createQueryRunner();

        await queryRunner.connect();
        await queryRunner.startTransaction();

        try{
            for(const newScore of updateScoresDto.scores){
                await queryRunner.manager.update(ScoreBoard, {
                    id: newScore.id, application: applicationId
                },
                {score: newScore.newScore})
            }
            await queryRunner.commitTransaction(); //성공 시 커밋
        } catch (err) {
            console.log(err);
            await queryRunner.rollbackTransaction(); //실패 시 롤백
            throw new InternalServerErrorException("트랜잭션 중 오류 발생, 롤백 진행완료");
        } finally {
            await queryRunner.release(); //쿼리러너 종료
        }
    }

공식문서에서는 DataSource의 queryRunner를 사용하는 것을 권장한다.

기본적인 메소드 순서는 connect, startTransaction, commitTransaction or rollbackTransaction 그리고 release가 있다.

이 사이사이에 우리가 할 쿼리들을 manager든 repository등 사용하여 구현하면 된다.

profile
원피스를 찾아서

0개의 댓글

관련 채용 정보