문제 상황
이전에 생성한 엔티티를 바탕으로 Repository Pattern을 구현할 때 생겨난 문제입니다.
(Repository Pattern은 우리가 entity를 통해 만들어준 테이블의 값 들이 어떠한 일련의 과정을 통해 서비스에서 접근 가능하게끔 해야 하는데 그때 repository가 매개체로서 이 과정을 수행한다. 즉, repository에서 entity로 정의해 놓은 DB의 값을 받고, service에 주입시켜 준다고 생각하면 이해하기 편합니다.)
제가 생성한 엔티티는 다음과 같습니다.
//order.entity.ts 일부
@CreateDateColumn({ type: 'datetime' })
created_at: Date;
@UpdateDateColumn({ type: 'datetime' })
updated_at: Date;
@DeleteDateColumn({ type: 'boolean', default: false })
deleted: boolean;
ingredients: { name: string; imageUrl: string }[];
//pizza.service.ts
async getPizzaByOrder(id: number): Promise<ResponsePizzaDto> {
const order = await this.orderRepository.findOne({
where: { id },
relations: ['connects', 'connects.ingredient'],
});
if (!order) {
throw new NotFoundException(`Can't find pizza with id ${id}`);
}
console.log(order.connects[0].ingredient);
const response: ResponsePizzaDto = {
pizzaId: order.id,
pizzaName: order.name,
pizzaScript: order.script,
ingredients: {
ingredientName: '',
},
};
위에서 만든 엔티티를 바탕으로 DB에 저장된 id값을 가진 피자이름을 조회해보려고 콘솔에 찍어보려고 하는데 문제의 ERROR [ExceptionsHandler] Cannot read properties of undefined (reading 'id')가 나왔습니다.
해결 과정
- 처음에는 커스텀해서 만든 레포지토리가 모듈에 장착이 안돼서 생기는 에러인 줄 알았습니다. (아님)
- 그리고 TypeORM 설정에서 logging: true 설정을 추가하여 어떤 쿼리가 실행되는지 로그에서 확인해보고자 하였습니다.
설정을 해주고 나서 if문에 걸리지않는걸 보아하니 orderRepository.findOne이 잘 주입된 것을 알았습니다. 그러나 조회 결과: null 이 나왔습니다 (분명히 DB안에 데이터들이 잘 들어있는데 말이죠…)
문제는 데이터 조회조건에 있었는데, TypeORM은 @DeletedDateColumn이 적용된 엔티티의 경우 소프트 삭제(Soft delete)를 위해 해당 칼럼이 null인 레코드만 조회하도록 자동으로 처리하고 있었습니다.
저의 경우, @DeletedDateColumn의 default값이 0으로 설정되어 있었기 때문에 조회결과가 null이었던 것입니다.
잠시 논리삭제(soft delete)와 물리삭제(hard delete)에 대해 알고가도록 해요
백엔드에서 삭제하는 방식은 크게 두가지가 있습니다.
- 논리 삭제: 저장된 데이터를 사용하지 않아서 논리적으로만 삭제하는 방법
- 물리 삭제: 저장된 데이터를 실제로 삭제해버리는 방법
논리 삭제를 사용하는 이유
- AI 학습 데이터로 이용
- 주고받은 채팅 내역일 때에, 법적인 문제가 생겼을 때 증거로 사용
- 관계(relation)가 맺어져 있을 때 문제가 생김
주로 물리 삭제는 위험하기 때문에 대부분의 서비스에 논리 삭제(soft delete)를 이용합니다.
저 또한 혹시 있을 인게임에서 레시피 업데이트 및 수정이 있을지도 모르기 때문에 소프트 삭제 기능을 사용하기로 했습니다.
그래서 결론은요
1. 소프트 삭제 기능을 사용할 경우
@DeletedDateColumn의 타입을 datetime(혹은 Date)로 지정하고 기본값을 제거
@DeleteDateColumn({ type: 'datetime', nullable: true })
deletedAt: Date;
2. 소프트 삭제 기능을 사용하지 않을 경우
삭제 여부를 Column으로 관리하지 않는다 !
해당 컬럼을 소프트 삭제 기능을 사용하고 워크벤치에서 반영이 잘되고 문제없이 잘 돌아가는 것을 알 수 있었습니다.
긴 글 읽어주셔서 감사합니다.
혹시라도 잘못 전달된 정보가 있을 경우에는 댓글 남겨주시면 감사하겠습니다. 😉
'포트폴리오' 카테고리의 다른 글
좋은 피자 위대한 피자 (2) (Type ORM 적용하여 entity 설정하기) (0) | 2025.01.16 |
---|---|
좋은 피자 위대한 피자 (1) (프로젝트 소개, ERD 정리) (0) | 2025.01.06 |