Batch System With Node.js, Lambda, and PostgreSQL
- Part 3. 데이터베이스 테스트 (feat. Jest)

목차

Part 1. 배치 시스템 설계 (링크)
Part 2. 데이터 업로더 (feat. Law SQL) (링크)
Part 3. 데이터베이스 테스트 (feat. Jest)
Part 4. 결과 알람 (feat. Slack) (링크)
Part 5. 스케쥴링 (feat. AWS EventBridge rules) (링크)


데이터베이스 테스트

유닛 테스트, 통합 테스트, 성능 테스트가 애플리케이션 코드를 테스트 하는 것이라면 데이터베이스 테스트는 DB 내 데이터가 정상인지 체크하는 것이다.
데이터베이스 테스트는 다양한 개념을 포함하고 있지만 여기선 DB 내 데이터의 무결성(정확성, 일관성, 유효성)을 확인하는 것이라 볼 수 있다.
배치 시스템에는 데이터베이스 테스트가 필수적이다.

데이터베이스 테스트를 하는 이유

1. 즉시 오류를 발견하기 어려움
배치 후 오류가 즉시 발견되지 않다가 오류를 포함한 데이터를 사용할 때 에러가 발생할 수 있다.

2. DB 제약조건으로 확인이 불가능한 오류 체크
DB 또는 애플리케이션 코드가 실행되는데엔 전혀 문제가 없는 데이터이지만 비지니스 로직에 맞지 않는 데이터가 있을 수 있다.
예를 들면 어떤 병원의 매출이 발생한 날짜는 폐업한 날짜 이후일 수 없다.

3. 데이터 담당자와의 협업
원본 데이터는 데이터 사이언티스트가 작업하는 경우가 많은데 오류가 있다면 빠른 피드백을 줘야 한다.
데이터에 오류가 없도록 하는건 데이터 담당자의 몫이지만, 오류에 대한 느린 피드백은 백엔드 개발자의 잘못일 수 있다.

Database 제약조건

데이터베이스 테스트를 작성하기 전 DB의 제약조건을 최대한 활용해야 한다.
DB의 제약조건은 강력해서 웬만한 오류들은 잡아낼 수 있다.

1. DB Column 속성
DB Column을 생성할 때 type, not null, Primary Key 등을 설정해준다.
데이터의 무결성을 위한 가장 기본적인 사항이다.
데이터의 특성과 확장성을 고려해서 처음부터 정확하게 설정해주는게 좋다.

2. Constraints
DB Constraints 기능을 사용하면 DB에 입력되는걸 차단할 수 있다.
Constraints에는 5가지 종류가 있다.
     - Not Null
     - Primary Key
     - Foreign Key
     - Unique
     - Check

여기서 Primary Key, Foreign Key, Not Null은 보통 Column을 생성할 때 만들어진다.
추가적인 제약조건을 만들기 위해선 Unique, Check를 활용하는 것이 중요하다.

  (1) Unique Constraints
     Primary Key로 설정된 칼럼은 Unique 조건이 생긴다.
     하지만 이외에도 Unique가 되야하는 조건이 있다면 생성해주자.
     2가지 이상의 칼럼의 조합도 Unique로 설정할 수 있다.

  (2) Check Constraints
     비지니스 로직의 다양하고 복잡한 제약조건을 만들 때 Check를 사용하면 된다.
     내가 원하는 어떤 조건이든 넣을 수 있다.
     다만 한 테이블 내 데이터에 한해서 제약조건을 만들 수 있고 다른 테이블과의 Join은 불가능하다.

데이터베이스 테스트

위에서 소개한 DB Constraints로도 검증이 불가능한 것은 데이터베이스 테스트를 작성한다.

아래는 Jest를 사용해서 작성한 샘플 소스코드이다.
데이터 I/O를 주고받는 양쪽 DB의 row수 일치여부를 확인하는 테스트이다.

import { client1, client2 } from './setup'
import { formatDateNextMonth, formatNumberToDate } from '../lib/lambda/function/batch/hospital'

describe('mytable 데이터 확인', () => {
    let yyyymm: number
    beforeAll(async () => {
        // yyyymm 값 할당
        ...
    })

    test('1. row수 db별 비교', async () => {
        const db1Query = `
            select count(*)
            from db1.mytable
            where date=${yyyymm}
        `
        const db2Query = `
            select count(*)
            from db2.mytable
            where date=${yyyymm}
        `
        const db1Res = await client1.query(db1Query)
        const db2Res = await client2.query(db2Query)

        const db1Count = db1Res.rows[0]?.count
        const db2Count = db2Res.rows[0]?.count

        expect(db1Count).toBe(db2Count)
    }, 10000) // timeout: 10s
}

row수 비교뿐 아니라 추가할 수 있는 데이터베이스 테스트는 무궁무진하다.

데이터베이스 테스트를 잘 작성하기 위해선 비지니스 로직을 잘 알고 있어야 한다.
비지니스 의사 결정에 따라 데이터가 맞을수도 틀릴수도 있기 때문이다.

데이터베이스 테스트는 어디까지 해줘야하는가?

일반적인 테스트는 코드를 기준으로 하기에 코드 커버리지라는 기준이 있지만 데이터베이스 테스트에는 그런 기준이 없다.
그래서 데이터베이스 테스트를 100% 커버한다고 말하기는 어렵다. 다만 100%에 가깝기 위해 노력할 뿐이다.

최근에는 배치 시스템이 안정되면서 에러가 거의 생기지 않는다.
만약 데이터 오류가 발생하면 즉시 데이터베이스 테스트를 추가해주고 있다.
또한 다른 오류 가능성은 없는지 확장해서 검토해보고 이 또한 테스트에 반영해준다.


Part4에서 계속..

댓글남기기