[Pre Onboarding-TIL]트랜잭션

연꽃·2021년 11월 13일
0

Pre Onboarding

목록 보기
8/12

트랜잭션이란?

트랜잭션(Transaction)은 데이터베이스의 상태를 변환시키는 하나의 논리적 기능을 수행하기 위한 작업의 단위 또는 한꺼번에 모두 수행되어야 할 일련의 연산 데이터베이스에서 여러 작업이 동시에 같은 데이터를 다룰 때 이 작업을 서로 분리하는 단위가 된다.

트랜잭션의 성질(ACID)

  • Atomicity : 원자성
    트랜잭션의 연산은 반드시 모두 반영되도록 완료되거나, 아니면 전혀 반영되지 않도록 복구되어야한다.

  • Consistency : 일관성
    트랜잭션이 그 실행을 성공적으로 완료하면 언제나 일관성 있는 데이터베이스 상태로 변환한다.

  • Isolation : 격리성
    둘 이상의 트랜잭션이 동시에 병행 실행되는 경우 어느 하나의 트랜잭션 실행중에 다른 트랜잭션의 연산이 끼어들 수 없다.

  • Durability : 영속성
    성공적으로 완료된 트랜잭션의 결과는 시스템이 고장나더라도 영구적으로 반영되어야 한다.

왜 트랜잭션을 사용하는 걸까???

트랜잭션을 사용해야하는 예시로 은행서비스를 많이 든다. 다음과 같은 예시를 살펴보자. A가 B에게 500원을 송금한다고 가정해 보자. 그렇다면 A의 계좌에서 500원을 줄이고 B의 계좌에서 500을 늘려야 한다. 그런데 알 수 없는 오류로 인해 A의 계좌에서 500원은 줄었지만 B의 계좌에서 500원이 증가하지 않는다면 A의 500원이 사라지는 문제가 생긴다. 이러한 오류가 생기지 않게 하기 위해서 트랜잭션을 사용한다.

구체적인 적용

위 예시상황에 대해서 구체적으로 트랜잭션을 어떻게 사용하는 것일까? A의 계좌에서 500원을 줄이는 것과 B의 계좌에 500원을 늘리는 일을 하나의 트랜잭션으로 묶어서 수행해야한다. 그리고 만약 A의 계좌에서 500원이 줄어들고 예상치못한 오류가 발생하여 B의 계좌에서 500원이 늘지 않았을 경우, 그 전에 일어났던 거래 즉, A의 계좌에서 500원을 줄이는 수행을 원상태도 되돌려야 한다. 그림으로 나타내면 다음과 같다.

코드의 구현

송금 API를 다음과 같이 구현하였다.

 async remit(updateWithdrawInfo: UpdateWithRemitDto, user: number): Promise<any> {
        const { withdrawAmount, toAccountNumber, fromAccountNumber } = updateWithdrawInfo;
        // 해당 출금 계좌 정보 조회
        const fromAccount = await this.findByAccountNumber(fromAccountNumber);
        // 해딩 입금 계좌 정보 조회
        const toAccount = await this.findByAccountNumber(toAccountNumber);
        // 해당 출금 계좌 권한 조회
        const auth = await this.authCheck(fromAccount.id, user);
        // queryRunner 설정 -> 트랜잭션을 사용하는 방법 중 하나
        const queryRunner: QueryRunner = this.connection.createQueryRunner();
        await queryRunner.connect();
        await queryRunner.startTransaction();
        try {
            // 해당 출금 계좌 잔액 조회 
            const exFromAccount: Account = await this.balanceCheck(queryRunner, fromAccount, withdrawAmount);
            // 해당 입금 계좌 잔액 조회
            const exToAccount: Account = await this.balanceCheck(queryRunner, toAccount, withdrawAmount);
            // 해당 출금 계좌 거래 가능 조회
            const amountAfterTransaction: number = await this.confirmTradable(exFromAccount, withdrawAmount);
            // 출금 내역 생성 및 정산
            const withdraw = await this.withdraw(queryRunner, exFromAccount, withdrawAmount, amountAfterTransaction);
            // 입금 내역 생성 및 정산
            const deposit = await this.deposit(queryRunner, exToAccount, withdrawAmount);
            this.interlink(queryRunner, withdraw, deposit);
            await queryRunner.commitTransaction();
            return REMITTANCE_SUCCESS_MSG(fromAccountNumber, toAccountNumber, withdrawAmount, amountAfterTransaction);
        } catch (error) {
            console.error(error);
            await queryRunner.rollbackTransaction();
            throw error;
        } finally {
            await queryRunner.release();
        }
    }
profile
우물에서 자라나는 중

0개의 댓글