ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Project] Nest 넘어져보기 (Service, Repository 분리)
    Project 2023. 4. 18. 09:19


    시작하며

    nest 프로젝트를 하면서 Service와 Repositoty를 분리하는 것이 좋다는 조언을 얻었습니다. 저는 Service계층에서 직접 DB에 접근하는 방법을 쓰고 있었고, 구글링을 통해 @EntityRepository()를 사용해서 Service와 Repository를 분리하는 방법을 알아냈습니다. (현재 작업 중인 프로젝트에서 typeORM을 쓰고 있습니다)

     

     


     

    마주친 문제

    typeorm 0.3.x 버전 이후 해당 @EntityRepository()를 사용하게 되면 deprecated라는 문구를 보게 되며, 사용이 불가하다는 사실을 알게 됩니다. 이에 대한 문제 해결을 다루어 보려 합니다.

     

    문제 개선 전)

    @Injectable()
    export class UserService {
      constructor(
        @InjectRepository(UserRepository)
        private userRepository: Repository<User>   <- 이 부분
      ){}
    }

    이렇게 Service에서 typeORM이 제공하는 Repository<Entity>를 통해 Entity에 접근할 수 있지만, 이 부분을 분리하여 문제를 해결해 보려 합니다. 이 문제를 해결하기 위해 Custom Repository를 사용해야 합니다.

    Custom Repository 생성 

    커스텀 데커레이터 생성

    // typeorm-ex-decotator.ts
    
    import { SetMetadata } from "@nestjs/common";
    
    export const TYPEORM_EX_CUSTOM_REPOSITORY = "TYPEORM_EX_CUSTOM_REPOSITORY";
    
    export function CustomRepository(entity: Function): ClassDecorator {
      return SetMetadata(TYPEORM_EX_CUSTOM_REPOSITORY, entity);
    }

    이 코드는 @EntityRepository()를 대체할 코드입니다. 벌써부터 어질어질합니다......

    vsCode 정의 기능으로 SetMetadata를 읽어 봅시다.

    /**
     * Decorator that assigns metadata to the class/function using the
     * specified `key`.
     *
     * Requires two parameters:
     * - `key` - a value defining the key under which the metadata is stored
     * - `value` - metadata to be associated with `key`
     *
     * This metadata can be reflected using the `Reflector` class.
     *
     * Example: `@SetMetadata('roles', ['admin'])`
     *
     * @see [Reflection](https://docs.nestjs.com/guards#reflection)
     *
     * @publicApi
     */
    export declare const SetMetadata: <K = string, V = any>(metadataKey: K, metadataValue: V) => CustomDecorator<K>;

    이 외계어를 최대한 해석해 보겠습니다. 위 코드는 key : value형태를 인자로 받고 있습니다. 여기서 key는 TYPEORM_EX_CUSTOM_REPOSITORY 되고, entityvalue값이 되는 것입니다.

    다이나믹 모듈 생성

    아래 동적 모듈은 위에서 작성한 @CustomRepository가 적용된 레파지토리를 받아줄 모듈입니다.

    // typeorm-ex.module.ts
    
    import { DynamicModule, Provider } from '@nestjs/common'
    import { getDataSourceToken } from '@nestjs/typeorm'
    import { DataSource } from 'typeorm'
    import { TYPEORM_EX_CUSTOM_REPOSITORY } from './typeorm-ex.decorator'
    
    export class TypeOrmExModule {
      public static forCustomRepository<T extends new (...args: any[]) => any>(
        repositories: T[],
      ): DynamicModule {
        const providers: Provider[] = []
    
        for (const repository of repositories) {
          const entity = Reflect.getMetadata(
            TYPEORM_EX_CUSTOM_REPOSITORY,
            repository,
          )
    
          if (!entity) {
            continue
          }
    
          providers.push({
            inject: [getDataSourceToken()],
            provide: repository,
            useFactory: (dataSource: DataSource): typeof repository => {
              const baseRepository = dataSource.getRepository<any>(entity)
              return new repository(
                baseRepository.target,
                baseRepository.manager,
                baseRepository.queryRunner,
              )
            },
          })
        }
    
        return {
          exports: providers,
          module: TypeOrmExModule,
          providers,
        }
      }
    }

     

    아직까지 위 코드를 정확히 이해하고 내 코드로 습득하지는 못했다.......

     

    모듈에 적용시키기

    -> user.repository.ts

    -> user.service.ts

    보다시피 서비스에서 @injectRepository를 사용하지 않았다.

     

    -> auth.module에 imports 부분을 보면 forCustomRepository로 불러온다.

    -> app.module에 entities에 User를 추가해 주면 된다

     


    출처:  

Designed by Tistory.