Nest.JS를 처음으로 학습하고자, 코치님께 인프런 무료 강의를 추천받았다
강의평도 좋고 설명도 잘 되어있어서 초보자가 듣기에 매우 좋은 것 같다
다만, 한 가지 문제가 발생했다.
강의가 2021년에 게시되어서 그 사이에 TypeORM 부분의 버전이 달라졌다 !
강의에서 에러가 나는 부분은 구글링을 통해서 해결을 해야했다
우선 실습 중인 프로젝트는 '게시글 CRUD 구현'이다.
기존 강의 실습 코드
// board.repository.ts
import { Board } from './board.entity';
import { EntityRepository, Repository } from 'typeorm';
@EntityRepository(Board)
export class BoardRepository extends Repository<Board> {}
- 문제가 되는 부분이 바로 repository의 EntityRepository 모듈이다.
- 아래 사진처럼 코드를 작성하면 VScode에서 취소선이 그어진다.
※ Repository
: 엔티티 개체와 함께 작동하며 엔티티 찾기, 삽입, 업데이트, 삭제 등을 처리
DB에 관련된 일은 서비스에서 하는 것이 아닌 Repository에서 함 ⇒ Repository Pattern이라고도 부름.
< Req → Controller → Service → Repository → Service → Controller → Res >
@EntityRepository() : 클래스를 사용자 정의 저장소로 선언하는데 사용. 사용자 지정 저장소는 일부 특정 엔티티를 관리하거나 일반 저장소일 수 있음.
TypeORM 0.3.0 부터는 @EntityRepository가 deprecated 되어 사용하지 않는다.
해결 방법으로는 2가지가 있다고 한다.
- Typeorm 버전 downgrade 하여 사용하기
-> repository 패턴을 사용 - Typeorm 버전에 맞게 code 수정하기
-> repository 패턴 사용 방식
-> repository 패턴 미사용 방식
Typeorm을 계속 사용할 것이라면 버전을 downgrade하기보다는 현재 버전에 맞게 연습하는 것이 좋은 것 같아서
2번 방식으로 아래에서 해결을 시도했다.
// boards.service.ts
@Injectable()
export class BoardsService {
constructor(
@InjectRepository(BoardRepository)
private boardRepository: BoardRepository,
) {}
async createBoard(createBoardDto: CreateBoardDto): Promise<Board> {
const { title, description } = createBoardDto;
const board = this.boardRepository.create({
title,
description,
status: BoardStatus.PUBLIC,
});
await this.boardRepository.save(board);
}
async getBoardById(id: number): Promise<Board> {
const found = await this.boardRepository.findOne(id);
if (found) {
throw new NotFoundException(`Can't find Board with id ${id}`);
}
return found;
}
}
에러 해결
1차 시도
TypeORM 0.3.0 부터는 @EntityRepository를 사용하는 대신 DataSource의 createEntityMangager()를 이용한다.
DataSource: 데이터베이스와의 상호 작용은 데이터소스를 설정한 후에만 가능하다.
TypeORM의 DataSource는 데이터베이스 연결 설정을 보관하고, 사용하는 RDBMS에 따라 초기 데이터베이스를 연결 또는 연결 풀을 설정한다.
첫 번째 인수: target이 될 Entity, 두 번째 인수: EntityManager 타입의 manager(entity에 대한 operation을 수행할 수 있는 것)를 넣어줌
// board.repository.ts
import { Injectable } from '@nestjs/common';
import { Board } from './board.entity';
import { DataSource, Repository } from 'typeorm';
@Injectable()
export class BoardRepository extends Repository<Board> {
constructor(dataSource: DataSource) {
super(Board, dataSource.createEntityManager());
}
async getBoardById(id: number) {
return await this.findOneBy({ id: id });
}
}
- constructor로 DataSource를 설정하고 super로 첫 번째 인수에는 엔티티인 Board를 넣고 두 번째 인수에는 datasource.createEntityManager()를 넣어주었다. (다른 블로그 참고)
🚨 에러 발생
createBoard의 Promise<Board> 부분에서 "A function whose declared type is neither 'undefined', 'void', nor 'any' must return a value."
에러 발생 원인: return board를 안 해줘서 에러 발생. Promise<Board>면 Board 형태의 값을 return 한다는 의미인데 내 코드에는 return이 없었다.
에러 해결: return board 추가
🚨 에러 발생
getBoardById의 findOne(id) 부분에서 "Type 'number' has no properties in common with type 'FindOneOptions<Board>'."
에러 발생 원인: TypeORM의 버전이 올라가면서 findOne은 사용하지 않는 것 같았다.
에러 해결: findOne을 findOneBy로 변경하고 소괄호 안에는 id를 중괄호로 감싸서 넣어줌 findOneBy({id})
여기서부터가 EntityRepository를 사용하기 않음으로써 발생한 에러 해결을 위해 시도한 과정들이다.
🚨 에러 발생
ERROR [ExceptionsHandler] No metadata for "BoardRepository" was found.
EntityMetadataNotFoundError: No metadata for "BoardRepository" was found. at DataSource.getMetadata.
2차 시도
Copilot에게 물어봤다.
From Copilot. "board.entity.ts 파일의 export 위에 @Entity()를 추가해라"
🚨 동일 에러 발생
=> 실패
3차 시도
1) service 파일의 constructor(@InjectRepository(BoardRepository))를 constructor(@InjectRepository(Board))로 수정
2) module 파일에서 imports: [TypeOrmModule.forFeatrue([BoardRepository])]를 [TypeOrmModule.forFeatrue([Board])]로 수정
//// 기존 코드
// board.service.ts
@Injectable()
export class BoardsService {
constructor(
@InjectRepository(BoardRepository)
private boardRepository: BoardRepository,
) {}
...
}
// board.repository.ts
export class BoardRepository extends Repository<Board> {
constructor(@InjectRepository(BoardRepository) private dataSource: DataSource) {
super(Board, dataSource.manager);
}
..
}
//// 수정 코드
// board.service.ts
@Injectable()
export class BoardsService {
constructor(
@InjectRepository(Board)
private boardRepository: BoardRepository,
) {}
...
}
// board.repository.ts
export class BoardRepository extends Repository<Board> {
constructor(@InjectRepository(Board) private dataSource: DataSource) {
super(Board, dataSource.manager);
}
..
}
=> 실패
🚨 에러 발생
ERROR [ExceptionHandler] dataSource.createEntityManager is not a function
TypeError: dataSource.createEntityManager is not a function
에러 발생 원인: dataSource.createEntityManager가 함수가 아니라며 에러 발생
에러 해결: createEntityManager와 동일한 Type인 manager가 있다고 해서 dataSource.manager로 수정
4차 시도
🚨 에러 발생
ERROR [ExceptionsHandler] No metadata for "Board" was found.
EntityMetadataNotFoundError: No metadata for "Board" was found.
결국 처음과 동일한 문제가 다시 발생했고, BoardRepository에서 Board로만 수정된 것...
( 절망편...... )
5차 시도
지금까지의 코드에 처음에 Copilot 알려준 board.entity.ts 파일의 export 위에 @Entity() 추가
// board.entity.ts
@Entity()
export class Board extends BaseEntity {
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
@Column()
description: string;
@Column()
status: BoardStatus;
}
=> 성공
Postman으로 결과 확인
참고자료
https://velog.io/@sheoae12/NestJS-Custom-Repository-%EB%A7%8C%EB%93%A4%EA%B8%B0
https://velog.io/@ansunny1170/No-metadata-for-BoardRepository-was-found
'IT Study > JavaScript' 카테고리의 다른 글
[NestJS] Nest.js 시작하기 (0) | 2024.01.07 |
---|---|
[NestJS] Repository Pattern (0) | 2024.01.07 |
[JS] 비동기 처리 패턴 async, await (0) | 2023.09.06 |
[JS] setTimeout의 clearTimeout, 디바운싱, 쓰로틀링 (0) | 2023.09.06 |
[JavaScript] 핵심 개념(1) 동기·비동기 | 이벤트 루프 | Promise (0) | 2023.09.04 |