Skip to content

게시물 리스트 API 개선

yanggwangseong edited this page Jan 1, 2025 · 17 revisions

📈 Bubbleprof 분석 결과

[GET] /articles

🔗 Bubbleprof HTML

🔥 Bubbleprof 보기

Bubbleprof 갤러리

구간 캡처 이미지
findAllV2 1구간
canActivate 2구간

📝 분석 요약

1. 쿼리 성능 문제 (findAllV2)

  • 문제:
    • ArticlesService.findAll2에서 **비동기 지연 59,295ms (70%)**가 발생.
    • 데이터베이스 쿼리 처리에서 병목 발생.

2. BearerTokenGuard 문제

  • 문제:
    • 약 58.501ms의 비동기 지연
    • findOneByEmail에서 **비동기 지연 58,501ms (79%)**가 발생.
    • accessToken 검증 guard 부분에서 지연이 발생 하였습니다.

code

findOneByEmail(email: string) {
        return this.membersRepository.findOne({
            select: {
                id: true,
                email: true,
                refreshToken: {
                    token: true,
                },
            },
            where: {
                email,
            },
            relations: {
                refreshToken: true,
            },
        });
    }

query

SELECT DISTINCT `distinctalias`.`memberentity_id` AS `ids_memberentity_id`
FROM (
    SELECT `memberentity`.`id` AS `memberentity_id`,
           `memberentity`.`email` AS `memberentity_email`,
           `memberentity__memberentity_refreshtoken`.`token` AS `memberentity__memberentity_refreshtoken_token`
    FROM `member` `memberentity`
    LEFT JOIN `refresh_token` `memberentity__memberentity_refreshtoken`
    ON `memberentity__memberentity_refreshtoken`.`id` = `memberentity`.`refreshtokenid`
    WHERE `memberentity`.`email` = 'user_1734104474333_i7ppqw@unwilling-attic.name'
) `distinctalias`
ORDER BY `memberentity_id` ASC 
LIMIT 1;

SELECT `memberentity`.`id` AS `memberentity_id`,
       `memberentity`.`email` AS `memberentity_email`,
       `memberentity__memberentity_refreshtoken`.`token` AS `memberentity__memberentity_refreshtoken_token`
FROM `member` `memberentity`
LEFT JOIN `refresh_token` `memberentity__memberentity_refreshtoken`
ON `memberentity__memberentity_refreshtoken`.`id` = `memberentity`.`refreshtokenid`
WHERE `memberentity`.`email` = 'user_1734104474333_i7ppqw@unwilling-attic.name'
AND `memberentity`.`id` IN (1);

개선 결과

  1. 쿼리 성능 개선:
    • Raw Query 사용 후 쿼리 실행 시간 50% 감소 (1초 → 500ms).
    • (이전 쿼리빌더의 쿼리는 1s가 소요됨)
  2. findOne relations 옵션 사용시 고정 쿼리 2개를 보내는 ORM문제:

code

findOneByEmail(email: string) {
        return this.membersRepository
            .createQueryBuilder("member")
            .leftJoinAndSelect("member.refreshToken", "refreshToken")
            .select(["member.id", "member.email", "refreshToken.token"])
            .where("member.email = :email", { email })
            .getOne();
    }

query

SELECT `member`.`id`          AS `member_id`,
       `member`.`email`       AS `member_email`,
       `refreshToken`.`token` AS `refreshToken_token`,
       `refreshToken`.`id`    AS `refreshToken_id`
FROM   `member` `member`
       LEFT JOIN `refresh_token` `refreshToken`
              ON `refreshToken`.`id` = `member`.`refreshtokenid`
WHERE  `member`.`email` = ? 

Comment

  • 백엔드에서 쿼리 분리 요청 후 애플리케이션에서 그룹화로 개선하기
  • TypeORM에서 findOne 메서드 지양하기
    • findOne과 relations 옵션을 사용 했을때 항상 2개의 고정쿼리를 날리는것은 굉장히 비효율적이고 해당 이슈에서도 사람들은 join으로 문제를 해결 해야한다고 주장하지만 아직 수정 되지 않았습니다.
  • ‼️ fineOne에서의 성능 문제는 크게 없었고 BearerTokenGuard에서 문제가 발생 합니다. 즉, jwt 문제와 쿼리 문제 2가지가 성능 저하를 발생 시켰습니다.
Clone this wiki locally