NestJS@9.x.x & TypeORM@0.3.x에서 customRepository 쉽게 사용하기

wonjun.Aden·2022년 10월 19일
3

nestJs

목록 보기
3/5
post-thumbnail

TypeORM의 버전 변경으로 인한 혼란.

typeorm의 버전이 0.3.x 로 올라가면서 0.2와는 너무나도 다른 많은 변경들이 생겼다. 이것은 혼란 그 잡채였다.
기존 0.2 버전에서는 @EntityRepository를 이용해서 커스텀 레포지토리를 만들어서 쓸 수 있었다면 0.3버전에서는 @EntityRepository가 deprecated되어서 사용할 수 없었다.
커스텀 레포지토리는 필수적 요소라고 생각하기 때문에 typeorm 버전 up이 되더라도 무조건 쓸 수 있게 설정 해놔야 한다고 생각했다. 그래서 여러가지 방법들을 찾아보려고 노력하였다. 두가지 방법에 대하여 찾아봤는데 그 전에 레포지토리가 무엇인지 먼저 살펴보도록 하겠다.

Repository 패턴이란?

정의

리포지토리는 데이터 원본에 액세스하는 데 필요한 논리를 캡슐화하는 클래스 또는 구성 요소입니다. 리포지토리는 공통 데이터 액세스 기능에 집중해 더 나은 유지관리를 제공하고 도메인 모델 계층에서 데이터베이스에 액세스하는 데 사용되는 기술이나 인프라를 분리합니다. Entity Framework와 같은 ORM(개체 관계 매핑)을 사용하는 경우 LINQ 및 강력한 형식화 덕분에 구현해야 할 코드가 간소화됩니다. 이렇게 하면 데이터 액세스 내부 작업보다 데이터 지 속성 논리에 더 집중하게 합니다.

참고자료: https://learn.microsoft.com/ko-kr/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/infrastructure-persistence-layer-design

간략하게 db와 관련된 일을 서비스단에서 하는것이 아닌 repository에서 다루어서 서비스단에서는 db의 쿼리들이 노출되지 않는다라는 것이다.

Custom Repository 찾은 방법

첫번째 방법

간단한 방법으로 다른 provider들과 똑같이 쓰는 것이다.

user.module.ts

@Module({
  imports: [TypeOrmModule.forFeature([User])],
  controllers: [UserController],
  providers: [UserService, UserRepository],
})
export class UserModule {}

user.repository.ts

@Injectable()
export class UserRepository extends Repository<User> {
  constructor(private dataSource: DataSource) {
    super(User, dataSource.createEntityManager());
  }

  async findByUsername(username: string): Promise<User> {
    const result = await this.createQueryBuilder('user')
      .where('user.username = :username', { username: username })
      .getOne();
    return result;
  }

}

user.service.ts

@Injectable()
export class UserService {
  constructor(
    private userRepository: UserRepository,
  ) {}
  
  async findByUsername(username: string): Promise<User> {
    return this.userRepository.findByUsername(username);
  }
}

Repository에서 dataSource를 injection 받는 방법인 것이다.
constructor의 기본 짜임새는 아래와 같다.

constructor(target: EntityTarget<Entity>, manager: EntityManager, queryRunner?: QueryRunner);

두번째 방법

두번째 방법은 현재 나는 에러가 나는데 다른 분들은 잘 되신건지 공유를 하신 방법이다. 나는 해결을 못해서 일단 공유는 하는데 혹시나 빠진 부분이 있거나 실수한 부분이 있다면 댓글 부탁드립니다.

두번째 방법으로는 @EntityRepository와 유사하게 만든 @CustomRepository를 사용하는 방법이다.
우선 소스에서 @CustomRepository 데코레이터와 module을 만들어준다.

src/db/typeorm.decorator.ts

import { SetMetadata } from '@nestjs/common';

export const TYPEORM_CUSTOM_REPOSITORY = 'TYPEORM_CUSTOM_REPOSITORY';

// eslint-disable-next-line @typescript-eslint/ban-types
export function CustomRepository(entity: Function): ClassDecorator {
  return SetMetadata(TYPEORM_CUSTOM_REPOSITORY, entity);
}

src/db/typeorm-custom.module.ts

import { DynamicModule, Provider } from '@nestjs/common';
import { getDataSourceToken } from '@nestjs/typeorm';
import { DataSource } from 'typeorm';
import { TYPEORM_CUSTOM_REPOSITORY } from './typeorm.decorator';

export class TypeOrmCustomModule {
  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_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: TypeOrmCustomModule,
      providers,
    };
  }
}

user.repository.ts

@CustomRepository(User)
export class UserRepository extends Repository<User> {}

user.module.ts

@Module({
  imports: [TypeOrmCustomModule.forCustomRepository([UserRepository])],
  controllers: [UserController],
  providers: [UserService],
})
export class UserModule {}

이렇게 해주면 0.2.x 버전의 @EntityRepository와 유사하게 사용할 수 있다.
하지만 난 되지 않아서 첫번째 방법으로 해줬다.나의 두번째 방법 에러는 아래와 같다.

[Nest] 160316  - 2022. 10. 19. 오후 12:21:50   ERROR [ExceptionHandler] Nest can't resolve dependencies of the UserService (?). Please make sure that the argument UserRepository at index [0] is available in the UserModule context.

Potential solutions:
- If UserRepository is a provider, is it part of the current UserModule?
- If UserRepository is exported from a separate @Module, is that module imported within UserModule?
  @Module({
    imports: [ /* the Module containing UserRepository */ ]
  })

Error: Nest can't resolve dependencies of the UserService (?). Please make sure that the argument UserRepository at index [0] is available in the UserModule context.

Potential solutions:
- If UserRepository is a provider, is it part of the current UserModule?
- If UserRepository is exported from a separate @Module, is that module imported within UserModule?
  @Module({
    imports: [ /* the Module containing UserRepository */ ]
  })

    at Injector.lookupComponentInParentModules (D:\workspace\nestjs-without-typeorm\nestjs9\node_modules\@nestjs\core\injector\injector.js:241:19)
    at Injector.resolveComponentInstance (D:\workspace\nestjs-without-typeorm\nestjs9\node_modules\@nestjs\core\injector\injector.js:194:33)
    at resolveParam (D:\workspace\nestjs-without-typeorm\nestjs9\node_modules\@nestjs\core\injector\injector.js:116:38)        
    at async Promise.all (index 0)
    at Injector.resolveConstructorParams (D:\workspace\nestjs-without-typeorm\nestjs9\node_modules\@nestjs\core\injector\injector.js:131:27)
    at Injector.loadInstance (D:\workspace\nestjs-without-typeorm\nestjs9\node_modules\@nestjs\core\injector\injector.js:57:13)    at Injector.loadProvider (D:\workspace\nestjs-without-typeorm\nestjs9\node_modules\@nestjs\core\injector\injector.js:84:9) 
    at async Promise.all (index 3)
    at InstanceLoader.createInstancesOfProviders (D:\workspace\nestjs-without-typeorm\nestjs9\node_modules\@nestjs\core\injector\instance-loader.js:47:9)
    at D:\workspace\nestjs-without-typeorm\nestjs9\node_modules\@nestjs\core\injector\instance-loader.js:32:13

이렇게 NestJS@9.x.x & TypeORM@0.3.x에서 customRepository를 사용하는 방법에 대하여 알아보았다.

참고자료:
https://stackoverflow.com/questions/71557301/how-to-workraound-this-typeorm-error-entityrepository-is-deprecated-use-repo

profile
아프리카의 BackEnd Developer 장원준입니다.

0개의 댓글