ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 테스트 코드로 알아보는 DI
    TIL 2024. 6. 10. 22:04

     

     

     

    서비스를 개발하던 도중 테스트 코드를 작성해야할 필요가 있었고, 

     

    bcrypt와 같은 외부라이브러리를 사용할때, 함수 모킹이 되지않고, 테스트속도가 오래걸린다는 문제가 있었습니다

     

    (bcrypt와 같은 암호화하는 작업들은 cpu 작업량을 많이요구하고 시간도 오래걸립니다) 

     

     

     

     

     

    문제가 생긴 메서드

    import * as bcrypt from 'bcryptjs';
    
    async signIn(loginDto: LoginDto): Promise<{ accessToken: string }> {
        const channel = await this.prisma.channel.findFirst({
          where: { id: loginDto.id },
        });
    
        if (!channel) {
          throw new UnauthorizedException('login failed');
        }
    
        if (!(await bcrypt.compare(loginDto.pw, channel.pw))) {
          throw new UnauthorizedException('login failed');
        }
    
        const accessToken = await this.jwtService.signAsync({ idx: channel.idx });
        return { accessToken: accessToken };
      }

     

     

     

    bcrypt를 직접 import해서 쓰는 방식인데 이 방식은

     

    1. 테스트 코드작성시 함수가 모킹이되지 않는다
    2. 테스트 시 시간이 오래걸린다

    는 단점이 있었습니다

     

     

     

     

     bcrypt를 import해서 쓰기)

    import * as bcrypt from 'bcrypt';
    
    if (!(await bcrypt.compare(loginDto.pw, channel.pw))) { 
      throw new UnauthorizedException('login failed');
    }

     

     

    테스트결과)

     

     

     

    실제로 이 방법은 1개의 유닛테스트를 하는데 1초가 걸렸습니다. 

     

    테스트할 유닛의 수가 훨씬많아지면 테스트시간이 오래걸리게될 것입니다

     

     

     

     

    그래서 테스트할때는 실제처럼 bcrypt 암호화할 필요가 없다고 생각했고, 

     

    의존성 주입을 통해 모킹이 가능하게 만들어서 일정한 값을 준다고 반환한다고 가정한뒤에 테스트하면 

     

    1. 테스트시간이 더 빨라진다

     

    2. 암호화시 매번 값이 달라지는 번거로움을 테스트에 적용할 필요없다

     

    는 장점을 가지게 될것입니다.

     

     

     

     

    고치는 과정)

     

     

     BcryptService 만들기 

     

    필요한 bcrypt 함수들을 BcryptService내에서 재정의해주었습니다

     

    그리고 bcrypt와 같은 암호화 서비스는 인증 기능 일부라 생각해서  auth모듈에 위치시켰습니다.

    import * as bcrypt from 'bcryptjs';
    
    @Injectable()
    export class BcryptService {
      constructor() {}
    
      async compare(s: string, hash: string): Promise<boolean> {
        return bcrypt.compare(s, hash);
      }
    }

     

     

    auth.service.ts에서 의존성 주입받아서 쓰기

    export class AuthService {
      constructor(
        private readonly bcryptService: BcryptService,
      ) {}
    
    ~~~~
    
        if (!(await this.bcryptService.compare(loginDto.pw, channel.pw))) {
          throw new UnauthorizedException('login failed');
        }

     

     

     

    테스트결과)

     

    1초 -> 0.5초로 단축되었습니다

     

     

     

     

     

     

     

     

     

    의존성주입(DI)를 하는 이유 

     

     

    1. 테스트 하기 용이하다

     

    > 외부 라이브러리 함수들도 쉽게 모킹이 가능해서 테스트를 쉽게 작성할 수 있다

     

     

    2. 코드 재사용성 증가

     

    > 다른 클래스에서도 쉽게 서비스 재사용 가능하다

     

     

    3. 유연성과 확장성을 높여준다

     

     

    4. 정해진형식으로 다른 개발자들도 이해하기쉽다

     

    > 어떤 의존성을 가지는 지 이해하기쉽다

     

     

     

     

    의존성 주입 단점

     

    1. 코드 작성 난이도가 올라간다

     

    > 객체지향프로그래밍이 익숙하지 않다면 어렵다고 느껴진다..

     

     

    2. 코드가 길어진다

     

     

     

     

     

     

    언제사용하는가?

     

    > 해당 클래스가 비즈니스 로직을 담고있는 경우에 사용한다

     

    > dto, entity와 같이 타입만 정의되어있는 클래스의 경우 쓸 필요가 없다

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

Designed by Tistory.