본문 바로가기
backend/NestJS

[NestJS]NestJS에서 Transaction(트랜잭션) 활용

by BK0625 2023. 1. 27.
반응형

 

프로젝트를 개발하면서 한 객체를 데이터베이스에 넣고 해당 인덱스를 활용하여 또 다른 insert 작업을 해야하는 상황이 발생했다. 만약 중간 단계에서 에러가 난다면 해당 작업 도중 발생한 insert를 모두 되돌려야 하는데 이럴 때 트랜잭션 단위로 묶어서 처리를 해줄 수 있다. 트랜잭션이 무엇인지 정리하고 프로젝트 도중 트랜잭션을 활용한 내용으로 공유하도록 하겠다.

 

트랜잭션(Transcation)

 트랜잭션의 사전적 정의는 시스템에서 사용되는 더 이상 쪼갤 수 없는 업무 처리의 최소 단위라고 말할 수 있다. 데이터베이스에서의 트랜잭션은 데이터베이스 서버에 여러 개의 클라이언트가 동시에 엑세스를 하거나 응용프로그램이 갱신을 처리할 때 중단 될 수 있는 등 데이터 부정합을 방지하고자 할 때 사용한다.

 

 예를 들어 insert 쿼리를 하고 해당 데이터를 활용하여 또 다른 insert를 해야 할 때 두번째 insert에서 에러가 난다면 처음 했던 insert 쿼리를 되돌려야 할 때 트랜잭션을 사용하면 쉽게 되돌릴 수 있다. 이렇게 전부 완료되거나, 하나도 완료되지 않아야 한다는 것이 트랜잭션의 중요한 특성이라고 할 수 있겠다. 그래서 해당 작업이 모두 수행이 되면 COMMIT 작업을 하여 올바르게 적용된 데이터를 데이테베이스에 반영시키고 실패했을 때에는 해당 트랜잭션이 시작하기 이전의 상태로 데이터베이스를 되돌린다.

 

활용방법

NestJS에서 TypeORM을 통한 트랜젝션 활용에는 데코레이터를 사용하는 방법도 있지만 공식 문서에서 queryRunner를 사용한 트랜잭션 제어를 권유하고 있기 때문에 queryRunner를 활용하여 트랜잭션을 사용하였다.

 

앞서말한대로 데이터를 하나 insert 하고 그 데이터의 id  값을 바탕으로 다른 insert가 진행되었기 때문에 총 3번의 insert를 하나의 트랜잭션으로 묶었다.

 

private dataSource : DataSource

먼저 DataSource를 선언한다.

 

 

 

 

 

queryRunner 생성, 연결 및 트랜잭션 시작

 
async insertCocktail(insertDto){
        const queryRunner = this.dataSource.createQueryRunner(); //queryRunner 생성
        await queryRunner.connect();  //queryRunner 연결
        await queryRunner.startTransaction(); //트랜잭션 시작

        try{
            const cocktail = new CocktailEntity();
            cocktail.name = insertDto.name;
            cocktail.imgUrl = insertDto.imgUrl;
            cocktail.dosu = insertDto.dosu;

            const res = await queryRunner.query(
                "insert into cocktail(name, dosu, imgUrl) values ('"+cocktail.name+"',"+cocktail.dosu+",'"+cocktail.imgUrl+"')"
            );
 

그리고 실행하려는 함수에서 위 코드대로 queryRunner를 생성하고 연결한 뒤 startTransaction()으로 트랜잭션을 시작한다. 이렇게 되면 해당 queryRunner는 하나의 트랜잭션으로 묶이게 된다. 

 

 

 

 

 

 

catch문과 finally문

 

그리고 해당 insert 작업이 실패했을 시에 rollback 할 수 있도록 catch 문을 작성한다. 그리고 queryRunner를 생성한 뒤엔 꼭 finally 문을 통해 queryRunner.release()를 해줘야 한다. 

 

 

 

 

 

 

 

 

const find = await queryRunner.query(
                "select * from cocktail where name='"+cocktail.name+"';"
            ); //같은 queryRunner로 묶어야 롤백이나 select가 됨

 

 

그리고 해당 데이터를 다시 조회 하기 위해 select 작업을 진행했다. 이 때 commit 이 된 상태가 아니기 때문에 다른 repository로 쿼리를 날리면 데이터를 select 하지 못하기 때문에 queryRunner로 직접 쿼리를 날렸다.

 

 

 

 const resAlcho = await this.insertAlchoRecipe(id, insertDto.alcho, queryRunner);
            const resJuice = await this.insertJuiceRecipe(id, insertDto.juice, queryRunner);
 

위에서 select한 id 값을 사용하여  각각 다른 테이블에 insert 작업을 해주었다. 하나의 트랜잭션으로 묶여야하기 때문에 queryRunner를 인자로 전달하여 각각의 쿼리를 수행하였다.

 

 

 

 

조회한 id값을 사용하여 insert 작업을 실행

 

그리고 두번의 insert 작업까지 모두 성공하면 Commit 작업을, 둘 중 하나라도 실패하면 Rollback 작업을 실행한다.

 

둘 다 성공하면 커밋 하나라도 실패하면 롤백

 

 

상황이 복잡해서 설명이 장황하지만 정리하면 그리 어렵지 않다.

 

async test(){
        const queryRunner = this.dataSource.createQueryRunner(); //queryRunner 생성
        await queryRunner.connect();  //queryRunner 연결
        await queryRunner.startTransaction(); //트랜잭션 시작

        try{
            await queryRunner.query(
                ""//원하는 트랜잭션 동작
            )

            //모든 동작 수행시 커밋
            await queryRunner.commitTransaction();
        }catch(err){
            this.logger.error(err);
            //동작 중간에 에러가 발생할 경우엔 롤백
            await queryRunner.rollbackTransaction()
        }finally{
            // queryRunner는 생성한 뒤 꼭 release
            await queryRunner.release();
        }
    }

 

수행하고자 하는 모든 동작을 하나의 queryRunner로 묶고 모든 동작을 수행했으면 커밋을 하여 데이터베이스에 해당 요소를 반영하고 그러지 못했을시에는 데이터베이스를 트랜잭션 시행 이전으로 되돌리는 것이다.

 

 

 

 

해당 포스팅은 공부하면서 정리한 내용으로 틀린 내용이 있을 수 있습니다.

모든 지적 감사히 받겠습니다:)

반응형

'backend > NestJS' 카테고리의 다른 글

[NestJS] VSCode nest cli 명령어 오류  (0) 2023.03.10
[NestJS] .env를 사용해서 MySQL 연동하기  (0) 2023.03.09
[TypeORM] insert와 save  (0) 2023.01.31
[NestJS]캐시 및 NestJS에서의 캐시 사용  (0) 2023.01.18
[NestJS] NestJS란?  (0) 2023.01.16