트랜잭션을 사용해보기 위해서 이것저것 포스팅과 공식문서를 참고하면서 로직을 짯는데 서버를 실행할 때 아래와 같은 에러가 발생했다.
"Cannot save, given value must be instance of entity class, instead object literal is given. Or you must specify an entity target to method call."
위 오류는 객체 리터럴(object literal: 중괄호({})
로 둘러싸인 key-value 쌍으로 이루어진 객체)이 아닌 엔티티 클래스의 인스턴스를 저장해야 한다는 말인데, 내 코드는 원래 있던 코드에서 트랜잭션을 위해 살짝 변형한 코드여서 그런지 save()
함수에 객체리터럴로 인자를 전달해 주었었다.
//기존코드
async saveUserInfo(userInfo: SignUpDto): Promise<SignUpDto> {
await this.transformPassword(userInfo);
return await this.userRepository.save(userInfo); //<< save할때 entity가 아닌 userInfo의 객체리터럴을 사용
}
async transformPassword(userDto: SignUpDto): Promise<void> {
userDto.password = await bcrypt.hash(userDto.password, 10);
}
//transaction사용 연습을 위한 변경코드
async saveUserInfo(userInfo: SignUpDto): Promise<SignUpDto> {
// 주입한 dataSource객체로 QueryRunner생성
const queryRunner = this.dataSource.createQueryRunner();
//QueryRunner로 DB연결
await queryRunner.connect();
//트랜잭션 시작
await queryRunner.startTransaction();
try {
await this.transformPassword(userInfo);
const result = await queryRunner.manager.save(userInfo);//<< userInfo의 객체리터럴을 그대로 사용
//트랜잭션 성공 후 적용
await queryRunner.commitTransaction();
return result;
} catch (err) {
console.error(err);
//트랜잭션중 실패하면 롤백
await queryRunner.rollbackTransaction();
throw new HttpException('회원가입 트랜잭션 롤백 에러', 500);
} finally {
//트랜잭션 완료 후 연결끊기
await queryRunner.release();
}
// return await this.userRepository.save(userInfo);
}
위와 같은 에러를 해결하기 위한 방법에 대해서 알아보니 여러가지가 존재했다.
const entity = new Entity();
entity.property = value;
await entityManager.save(entity);
const entity = Entity.create({ property: value });
await entityManager.save(entity);
@Entity({ target: Entity })
class Entity {
// ...
}
가장 처음 이미지와 같이 에러를 반출하는 것으로 보아서 트랜잭션은 잘 동작하는것 같고, 따라서 위의 첫번째 방법을 통해 이를 해결해 주니 정상동작하는 것을 확인할 수 있었다.
const user = new User();
user.email = userInfo.email;
user.nickname = userInfo.nickname;
user.password = userInfo.password;
const result = await queryRunner.manager.save(user);