인프런 커뮤니티 질문&답변

둘리콩콩님의 프로필 이미지
둘리콩콩

작성한 질문수

[코드팩토리] [초급] NestJS REST API 백엔드 완전 정복 마스터 클래스 - NestJS Core

QueryRunner 커스텀 데코레이터 만들기 & Transaction Interceptor 적용하기

createQueryRunner는 undefined속성을 읽을 수 없다

해결된 질문

작성

·

187

0

const qr = this.dataSource.createQueryRunner();

ERROR [ExceptionsHandler] Cannot read properties of undefined (reading 'createQueryRunner')

TypeError: Cannot read properties of undefined (reading 'createQueryRunner')

at TransactionInterceptor.intercept (C:\Users\Administrator\Desktop\cf_sns\src\common\interceptor\transaction.interceptor.ts:21:32)

이 에러가 떠서 postman으로 테스트를 진행하지 못하고있습니다.

dataSource도 typeorm에서 import 해서 사용했는데 컨트롤러에서 트랜잭션을 사용할때는 됐다가 인터셉터로 따로 빼서 사용하려고할 때 발생한 오류입니다.

답변 1

0

코드팩토리님의 프로필 이미지
코드팩토리
지식공유자

안녕하세요!

에러를 번역해보면 createQueryRunner를 undefined에 실행하려고하니 나는 에러네요.

즉, datasource가 undefined입니다.

정상적인 상황이라면 datasource가 undefined가 될 수 없습니다.

this.datasource가 왜 null인지 확인을 하셔야 할 것 같습니다.

interceptor로 따로 빼서 실행하려 할때 나는 에러라면 어떤 코드 변경이 있었는지, 실수한건 없는지 확인 해보셔야겠습니다.

도저히 모르겠다면 문제라고 생각되는 부분들을 더 넓게 캡쳐를 해주셔야합니다. 감사합니다!

감사합니다!

둘리콩콩님의 프로필 이미지
둘리콩콩
질문자

// posts.controller.ts
import {
  Body,
  Controller,
  Post,
  UseGuards,
  UseInterceptors,
} from '@nestjs/common';
import { QueryRunner as QR } from 'typeorm';
import { TransactionInterceptor } from 'src/common/interceptor/transaction.interceptor';
import { QueryRunner } from 'src/common/decorator/query-runner.decorator';

@Post()
  @UseGuards(AccessTokenGuard)
  @UseInterceptors(TransactionInterceptor)
  async postPosts(
    @User('id') userId: number,
    @Body() body: CreatePostDto,
    @QueryRunner() qr: QR,
  ) {
    const post = await this.postsService.createPost(userId, body, qr);

    // throw new InternalServerErrorException('TEST');

    for (let i = 0; i < body.images.length; i++) {
      await this.postsImagesService.createPostImage(
        {
          post,
          order: i,
          path: body.images[i],
          type: ImageModelType.POST_IMAGE,
        },
        qr,
      );
    }

    return this.postsService.getPostById(post.id, qr);
  }
// transaction.interceptor.ts
/* eslint-disable prettier/prettier */
import {
  CallHandler,
  ExecutionContext,
  InternalServerErrorException,
  NestInterceptor,
} from '@nestjs/common';
import { Observable, catchError, tap } from 'rxjs';
import { DataSource } from 'typeorm';

export class TransactionInterceptor implements NestInterceptor {
  constructor(private readonly dataSource: DataSource) {}
  async intercept(
    context: ExecutionContext,
    next: CallHandler<any>,
  ): Promise<Observable<any>> {
    const req = context.switchToHttp().getRequest();

    // 트랜잭션과 관련된 모든 쿼리를 담당할
    // 쿼리 러너를 생성한다.
    const qr = this.dataSource.createQueryRunner();

    // 쿼리 러너에 연결한다.
    await qr.connect();

    // 쿼리 러너에서 트랜잭션을 시작한다.
    // 이 시점부터 같은 쿼리러너를 사용하면
    // 트랜잭션 안에서 데이터베이스 액션을 실행할 수 있다.
    await qr.startTransaction();

    req.queryRunner = qr;

    return next.handle().pipe(
      catchError(async (e) => {
        await qr.rollbackTransaction();
        await qr.release();

        throw new InternalServerErrorException(e.message);
      }),
      tap(async () => {
        await qr.commitTransaction();
        await qr.release();
      }),
    );
  }
}

해당 강의에서 사용된 코드 부분입니다.

코드팩토리님의 프로필 이미지
코드팩토리
지식공유자

dataSource를 직접 생성하고 있지 않으니 IoC로부터 인젝트를 받아야하는데 그러려면 어떻게 해야할까요? (inject 받지 못하니 null이 나오는겁니다)

둘리콩콩님의 프로필 이미지
둘리콩콩
질문자

@Injectable()

이걸 빠뜨렸네용.. 정답을 바로 알려주시지 않고 스스로 찾을 수 있도록 도와주셔서 정말 감사합니다!!

코드팩토리 짱

╰(*°▽°*)╯

둘리콩콩님의 프로필 이미지
둘리콩콩

작성한 질문수

질문하기