항해플러스 - 3주차

CH_Hwang·2023년 12월 23일
0

항해플러스

목록 보기
4/8

이번주에 한 것

  • transaction injection 함수 리팩토링
  • 기능 전부 구현
  • 통합테스트 세팅
    • 테스트 db (실제 db와 같은 환경)을 container방식으로 띄움
    • 간단한 동시성 테스트

transaction injection 함수 리팩토링

또다시 transaction 관련된 함수가 문제였다... ㅎㅎ (망할..)
기능을 구현하면서 event sourcing 방식의 개발을 적용해보면서 문제가 되는 부분이 있었다.

import type { EntityManager } from 'typeorm';

export function injectTransactionalEntityManager(transactionalEntityManager: EntityManager) {
  return <T extends (...args: any[]) => any>(f: T) =>
    async <U extends Parameters<T>>(...args: U): Promise<ReturnType<T>> =>
      f({ ...args[0], transactionalEntityManager });
}

repository에서 domain event를 save할 때 this를 불러서 하도록 구현해놨는데, 알다시피 나의 트랜잭션 Injector 함수는 콜백 지옥속에서 실행된다. 콜백 지옥속에서 클래스 method를 실행하게 되면 context를 잃어버려서 this를 잃어버리는 대참사가 일어났다.

처음에는 잘 몰랐기 때문에 바로 나의 개발메이트 copilot에게 물어봤다.

여기서 그러면 나의 머릿속에 생각이 든다.
1. 그렇군... 그렇다면 binding을 해주려면 repository를 저 injection함수에서 알아야하네..?
2. 그럼 레포지토리를 넘겨야하네...?
3. 그럼 굳이 method를 따로 또 넘길 필요가 없겠다!

그래서 나는 이런식으로 구현하게 된다.
기존에 타입이 조금 이상한 것도 있었기 때문에 (마지막 함수 제너릭 부분, U를 쓰면 오히려 이상하게 타이핑이 되기 때문에 안된다...)

// transaction/index.ts
export function injectTransactionalEntityManager(transactionalEntityManager: EntityManager) {
  return <T, M extends FunctionKeys<T>>(repository: T, methodName: M) =>
    async (
      ...args: T[M] extends (...args: infer U) => any ? U : never
    ): Promise<Awaited<T[M] extends (...args: any[]) => infer U ? U : never>> =>
      (repository[methodName] as (...args: unknown[]) => any)({
        // @ts-expect-error NOTE: 현재 repository method의 파라미터가 다양하여 transactionalEntityManager를 받으려면 객체 하나만 받아야한다.
        ...args[0],
        transactionalEntityManager,
      });
}

// @types/index.d.ts
type FunctionKeys<T> = {
  [K in keyof T]: T[K] extends (...args: any[]) => any ? K : never;
}[keyof T];

이런식으로 1번째 파라미터로는 repository 자체를 넘겨버리고 2번째 파라미터로 해당 레포지토리에서 실행할 메서드 이름을 넣는다. 그러면 그 뒤의 동작은 알아서 똑같이 되도록 만들어주었다.

통합 테스트 세팅

통합테스트(e2e)를 위해 실제 db를 띄우려고 했다. 그래서 docker compose를 이용해서 띄우고 migration code로 테이블을 세팅해준다.

그리고 미리 들어가야하는 데이터들은 따로 처리하지 않았다. 해당 데이터들은 내생각엔 test suite별로 관리되는 것이 좋을 것 같아서 그렇게했다. 세팅하는 부분에서 굉장히 애를 먹기는 했다. db migration도 처음해보기도 하고 migration하면서 orm config를 로컬에서 잘 돌아가게끔 해주기도 해야하고 해서...

조금 더 자세한 내용을 알고싶다면 https://github.com/EnhancementPlayGround/nest_playground/pull/32/files 여기를 참고하자

발표

항해 플러스에서 매니저님이 발표를 요청해서 발표도 했다. 주제는 일단 DDD의 전술적 설계를 내가 어떻게 적용을 했는가에 대해서 발표했다. 전략적 설계부분이 물론 중요하지만 현재 내가 하고있는 것은 그렇게 요구사항이 많지 않아서 전술적 설계에 조금 더 초점을 맞췄다.
보고싶다면 아래 링크를 보도록 하자..
https://docs.google.com/presentation/d/1ifTq7wsDUjscXVKnwMgMMcc_smNpsPLeI96M95rwOrk/edit?usp=sharing

0개의 댓글