해결된 질문
작성
·
264
0
## 트랜잭션 서비스
@Injectable()
export class TransactionService {
constructor(private readonly dataSource: DataSource) {}
async transaction(callback) {
const queryRunner = this.dataSource.createQueryRunner();
await queryRunner.connect();
await queryRunner.startTransaction();
try {
await callback(queryRunner);
await queryRunner.commitTransaction();
return true;
} catch (error) {
console.error(error);
await queryRunner.rollbackTransaction();
throw error;
} finally {
await queryRunner.release();
}
}
}
## 트랜잭션 모듈 (글로벌)
@Global()
@Module({
providers: [TransactionService],
exports: [TransactionService],
})
export class TransactionModule {}
## 사용예제
async join(email: string, nickname: string, password: string) {
const user = await this.usersRepository.findOne({ where: { email } });
if (user) {
throw new HttpException('이미 존재하는 사용자입니다.', 401);
}
const hashedPassword = await bcrypt.hash(password, 12);
await this.transactionService.transaction(async (queryRunner) => {
const result = await queryRunner.manager.getRepository(Users).save({ email, nickname, password: hashedPassword });
const sleact = await queryRunner.manager.getRepository(Workspaces).findOne({ where: { name: 'Sleact' } });
await queryRunner.manager.getRepository(WorkspaceMembers).save({ UserId: result.id, WorkspaceId: sleact.id });
const channel = await queryRunner.manager
.getRepository(Channels)
.findOne({ where: { name: '일반', WorkspaceId: sleact.id } });
await queryRunner.manager.getRepository(ChannelMembers).save({ UserId: result.id, ChannelId: channel.id });
});
}
안녕하세요 제로초님.
트랜잭션 관련해서 로직 작성하다가 실행부를 제외하고는 너무 공통되는 것 같아서 방법을 고민하다가 위 소스처럼 처리하면 어떨까 싶어 작성해보았습니다.
트랜잭션 서비스를 생성해서 공통이되는 트랜잭션 로직을 모아두고, 콜백함수를 받아서 처리하는 형태로 해보았는데요!
일단 동작은 정상적으로 하는데, 이렇게 했을 때 발생할만한 문제가 있을지 짐작이 가지 않아서 문의 남깁니다!
위와같은 로직으로 트랜잭션을 공통처리 했을 때 생길만한 사이드이펙트가 있을까요?
답변 1
1
일단 코드 상 문제는 없어 보입니다. 다만 실제 서비스에서는 트랜잭션을 여러 개 여는 경우도 있고, 하나의 트랜잭션 안에서 다른 트랜잭션을 여는 경우도 있어 이런 것들은 대응이 어려울 것 같네요.
https://www.npmjs.com/package/typeorm-transactional
이 라이브러리 적용이 제일 깔끔합니다.