No metadata for "BoardRepository" was found. - Typeorm repository 패턴 사용하기

ansunny1170·2023년 2월 8일
4

오답노트

목록 보기
1/1

🎪시작에 앞서

본 포스팅은 Nest.js를 처음 접하는 개발자가 Typeormrepository 패턴을 적용하며 마주한 문제를 해결 하고 있습니다.

TL;DR

  1. Typeorm 버전에 따라 사용 가능한 method가 있고, 또 없습니다.
    버전에 따라 적용 방법이 나뉩니다.
  2. repository 패턴사용하는 방식 & 사용 안하는 방식 이 있습니다.
  3. 여러가지 방법을 소개하고 있습니다.
    목차에서 방법을 보고 선택하여 시도해보시면 시간을 벌 수 있습니다... ㅠㅠ

참고자료

실습 참고 콘텐츠 : Youtube 따라하면서 배우는 NestJS

DAO(Data Access Object) 정도로 이해 하시면 됩니다.


📚문제의 시작

Nest.js 튜토리얼을 따라 실습 하던 중에 아래와 같은 error가 발생 했습니다.
repository 패턴을 적용하려하자 문제가 나왔습니다.

[Nest] 11926  - 02/08/2023, 6:24:48 PM   ERROR [ExceptionsHandler] No metadata for "BoardRepository" was found.
EntityMetadataNotFoundError: No metadata for "BoardRepository" was found.
    at DataSource.getMetadata (/home/kyc/study/nestjs-board-app/src/data-source/DataSource.ts:438:30)
    at Repository.get metadata [as metadata] (/home/kyc/study/nestjs-board-app/src/repository/Repository.ts:53:40)
    at Repository.create (/home/kyc/study/nestjs-board-app/src/repository/Repository.ts:130:18)
    at BoardsService.createBoard (/home/kyc/study/nestjs-board-app/src/boards/boards.service.ts:19:44)
    at BoardsController.createBoard (/home/kyc/study/nestjs-board-app/src/boards/boards.controller.ts:18:35)
    at /home/kyc/study/nestjs-board-app/node_modules/@nestjs/core/router/router-execution-context.js:38:29
    at processTicksAndRejections (node:internal/process/task_queues:95:5)
    at /home/kyc/study/nestjs-board-app/node_modules/@nestjs/core/router/router-execution-context.js:46:28
    at /home/kyc/study/nestjs-board-app/node_modules/@nestjs/core/router/router-proxy.js:9:17

튜토리얼은 되는데 나는 왜 안되냐? 라고 묻는다면 원인은 Typeorm package version입니다.
해결하는 방법은 2가지입니다.

  1. Typeorm 버전 downgrade 하여 사용하기
    -> 그리고 repository 패턴을 사용합니다.
  2. Typeorm 버전에 맞게 code 수정하기
    -> repository 패턴 사용 방식
    -> repository 패턴 미사용 방식

🍩원본 코드

service.ts에서 create board 되는 것을 확인한 상태입니다. 이제 여기서 repository 패턴으로 넘어가야 합니다.
넘어가는 방법은 verison을 downgrade하는 방식을 참조하시기 바랍니다.

board.repository.ts

import { DataSource, Repository } from "typeorm";
import { Board } from "./board.entity";

@EntityRepository(Board)
export class BoardRepository extends Repository<Board> {

}

boards.service.ts

import { Injectable, NotFoundException } from '@nestjs/common';
import { BoardStatus } from './board-status.enum'
import { CreateBoardDto } from './dto/create-board.dto';
import { BoardRepository } from './board.repository';
import { InjectRepository } from '@nestjs/typeorm';
import { Board } from './board.entity';

@Injectable()
export class BoardsService {
    constructor(
        @InjectRepository(BoardRepository)
        private boardRepository: BoardRepository,
    ) {}

    createBoard(createBoardDto: CreateBoardDto): Promise<Board> {
        const { title, description } = createBoardDto;

        const board = this.boardRepository.create({
            title,
            description,
            status: BoardStatus.PUBLIC
        });

        await this.boardRepository.save(board)
        return board;
    }
}

boards.module.ts

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { BoardRepository } from './board.repository';
import { BoardsController } from './boards.controller';
import { BoardsService } from './boards.service';

@Module({
  imports: [
    TypeOrmModule.forFeature([BoardRepository])
  ],
  controllers: [BoardsController],
  providers: [BoardsService]
})
export class BoardsModule {}

📚Typeorm 버전 downgrade 하고 repository 패턴 사용하기

📖버전 수정

제가 실습에 사용하던 버전은

    "@nestjs/typeorm": "^9.0.1",
    "typeorm": "^0.3.12",

튜토리얼에서 사용하는 버전은

	"@nestjs/typeorm": "^8.0.1",
    "typeorm": "^0.2.34",

튜토리얼 혹은 아래 사이트의 글을 참조하여 version을 낮춥니다.

버전 관련 비슷한 사례

  1. 인프런 지식공유 참여
    @nestjs/typeorm': '^8.0.3' 에서 @nestjs/typeorm': '^8.0.1' 로 변경하니 가능했다는 내용.
  2. stack overflow - 댓글 읽기
    역시 버전을 변경하니 가능하다는 내용.

🍩코드 수정

**.repository.ts 을 사용해 @InjectRepository(~~)~~ 부분에 class Repository 를 주입하는 방식입니다.
board.repository.ts

import { DataSource, Repository } from "typeorm";
import { Board } from "./board.entity";

@EntityRepository(Board)
export class BoardRepository extends Repository<Board> {
	const { title, description } = createBoardDto;

        const board = this.boardRepository.create({
            title,
            description,
            status: BoardStatus.PUBLIC
        });

        await this.boardRepository.save(board)
        return board;
    }
}

boards.service.ts

import { Injectable, NotFoundException } from '@nestjs/common';
import { BoardStatus } from './board-status.enum'
import { CreateBoardDto } from './dto/create-board.dto';
import { BoardRepository } from './board.repository';
import { InjectRepository } from '@nestjs/typeorm';
import { Board } from './board.entity';

@Injectable()
export class BoardsService {
    constructor(
        @InjectRepository(BoardRepository)
        private boardRepository: BoardRepository,
    ) {}

    createBoard(createBoardDto: CreateBoardDto): Promise<Board> {
      return this.boardRepository.createBoard(createBoardDto);
        
}

boards.module.ts

# 수정 없습니다.

📚Typeorm 버전에 맞게 코드만 수정하기

📖Repository 패턴 사용하지 않는 방식

아래 사이트를 참고하여 수정하면 됩니다.
Nestjs 공식 사이트 - entity

이렇게 하면 service 단에서 DB 접근을 직접 하기 때문에 비즈니스 로직에 집중하기 어렵고 코드도 많이 길어지는 단점이 있습니다.

boards.service.ts 수정 후

import { BoardRepository } from "typeorm"; // 그대로 사용
import { Board } from "./board.entity"; // 추가

    constructor(
        // @InjectRepository(BoardRepository) // 삭제
        @InjectRepository(Board)
        private boardRepository: BoardRepository, // 여기서 사용
    ) {}

boards.module.ts 수정 후

// import { BoardRepository } from './board.repository'; // 삭제
import { Board } from './board.entity'; // 추가

@Module({
  imports: [
    // TypeOrmModule.forFeature([BoardRepository]) // 삭제
    TypeOrmModule.forFeature([Board]) // 추가
  ],
  controllers: [BoardsController],
  providers: [BoardsService]
})

📖Repository 패턴 사용 방식

첫 번째 방법

블로그 Custom Repository 찾은 방법

버전이 변경 된 후, 많은 혼란한 상황을 함께 겪은 듯한 동지애(?)를 느꼈습니다. 하지만 정말 아쉽게도 제가 사용하는 버전에 완벽하게 적용하지는 못했습니다.
그래도 많은 도움을 받아 소개합니다.

board.repository.ts 수정 후

import { InjectRepository } from "@nestjs/typeorm";
import { DataSource, Repository } from "typeorm";
import { BoardStatus } from "./board-status.enum";
import { Board } from "./board.entity";
import { CreateBoardDto } from "./dto/create-board.dto";

export class BoardRepository extends Repository<Board> {
    // constructor 추가
	constructor(@InjectRepository(Board) private dataSource: DataSource) {
        super(Board, dataSource.createEntityManager())
    }
  
    async createBoard(createBoardDto: CreateBoardDto) : Promise<Board> {

    const { title, description } = createBoardDto;

    const board = this.create({
        title,
        description,
        status: BoardStatus.PUBLIC
    });

    await this.save(board)
    return board;
    }
}

boards.service.ts 수정 후

import { Injectable, NotFoundException } from '@nestjs/common';
import { BoardStatus } from './board-status.enum'
import { CreateBoardDto } from './dto/create-board.dto';
import { BoardRepository } from './board.repository';
import { InjectRepository } from '@nestjs/typeorm';
import { Board } from './board.entity';

@Injectable()
export class BoardsService {
    constructor(
        // @InjectRepository(Board) // 삭제
        // @InjectRepository(BoardRepository) // 삭제
        private boardRepository: BoardRepository,
    ) {}

    createBoard(createBoardDto: CreateBoardDto): Promise<Board> {
        return this.boardRepository.createBoard(createBoardDto);
    }

boards.module.ts 수정 후

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Board } from './board.entity';
import { BoardRepository } from './board.repository'; // 추가
import { BoardsController } from './boards.controller';
import { BoardsService } from './boards.service';

@Module({
  imports: [
    TypeOrmModule.forFeature([Board])
  ],
  controllers: [BoardsController],
  providers: [BoardsService, BoardRepository] // BoardRepository 추가
})
export class BoardsModule {}

발생했었던 문제

[Nest] 5032  - 02/09/2023, 6:43:34 PM   ERROR [ExceptionHandler] dataSource.createEntityManager is not a function
TypeError: dataSource.createEntityManager is not a function
    at new BoardRepository (/home/kyc/study/nestjs-board-app/src/boards/board.repository.ts:13:33)
    at Injector.instantiateClass (/home/kyc/study/nestjs-board-app/node_modules/@nestjs/core/injector/injector.js:348:19)
    at callback (/home/kyc/study/nestjs-board-app/node_modules/@nestjs/core/injector/injector.js:54:45)
    at processTicksAndRejections (node:internal/process/task_queues:95:5)
    at Injector.resolveConstructorParams (/home/kyc/study/nestjs-board-app/node_modules/@nestjs/core/injector/injector.js:133:24)
    at Injector.loadInstance (/home/kyc/study/nestjs-board-app/node_modules/@nestjs/core/injector/injector.js:58:13)
    at Injector.loadProvider (/home/kyc/study/nestjs-board-app/node_modules/@nestjs/core/injector/injector.js:85:9)
    at /home/kyc/study/nestjs-board-app/node_modules/@nestjs/core/injector/instance-loader.js:49:13
    at async Promise.all (index 4)
    at InstanceLoader.createInstancesOfProviders (/home/kyc/study/nestjs-board-app/node_modules/@nestjs/core/injector/instance-loader.js:48:9)

두 번째 방법

위 첫 번째 방법과 다른 부분은 repository.ts 파일입니다.

board.repository.ts 수정 후

import { InjectRepository } from "@nestjs/typeorm";
import { DataSource, Repository } from "typeorm";
import { BoardStatus } from "./board-status.enum";
import { Board } from "./board.entity";
import { CreateBoardDto } from "./dto/create-board.dto";

export class BoardRepository extends Repository<Board> {
    constructor(@InjectRepository(Board) private dataSource: DataSource) {
        super(Board, dataSource.manager) // 변경
        // super(Board, dataSource.createEntityManager()) // 삭제
    }
    async createBoard(createBoardDto: CreateBoardDto) : Promise<Board> {

    const { title, description } = createBoardDto;

    const board = this.create({
        title,
        description,
        status: BoardStatus.PUBLIC
    });

    await this.save(board)
    return board;
    }
}

답답해서 DataSource class를 뒤적거리다가 createEntityManager와 동일한 Type의 manager를 발견했습니다. 적용했더니.. 다행히도 정상 작동됨을 확인했습니다...


🖐️마치며

EntityRepository deprecated

board.repository.ts

import { EntityRepository, Repository } from "typeorm";
import { Board } from "./board.entity";

@EntityRepository(Board)
export class BoardRepository extends Repository<Board> {

}

어쩐지.. 튜토리얼 과는 다르게 EntityRepository 이렇게 사용안함으로 되어있던데... 느낌이 싸했습니다.
(친절히 알려주고 있었는데 가뿐히 무시한 대가는 소중한 시간이었습니다. ㅠㅠ)

Nest.js Docs에서 안내하는 Repository pattern..??

https://docs.nestjs.com/techniques/database#repository-pattern
https://docs.nestjs.com/recipes/sql-typeorm

분명 Repository pattern이라고 안내하고 있는데, 눈을 씻고 찾아봐도 repository.ts 파일에 대한 설명이 안보입니다.. ㅠㅠㅠ

제 생각에 service.ts 에서 사진과 같이 usersRepository를 선언 할 때, Repository<User> 형식으로 선언하면 이게 과연 repository pattern인가... 에서 뇌정지가 왔습니다. 제가 repository pattern을 제대로 이해 못한 거겠죠?...😂

privaite usersRepository: Repository<User>

TypeORM Transactions

이어서 공식 문서를 보던 중에 트랜젝션을 적용하는 방법도 있었습니다.

추후 프로젝트를 진행할 떄 트랜젝션 적용은 필수 요소입니다. 나중에.. 나중에 필요할 때 꺼내 봐야 겠습니다.

https://docs.nestjs.com/techniques/database#typeorm-transactions

끗! 감사합니다.

profile
공정 설비 개발/연구원에서 웹 서비스 개발자로 경력 이전하였습니다. Node.js 백엔드 기반 풀스택 개발자를 목표로 하고 있습니다.

1개의 댓글

comment-user-thumbnail
2023년 7월 31일

좋은 글 너무 감사드립니다 ㅠㅠ

답글 달기