포시코딩

[Nest.js] 이메일 인증 시스템 (2). cache-manager 본문

Node.js

[Nest.js] 이메일 인증 시스템 (2). cache-manager

포시 2023. 2. 23. 22:01
728x90

개요

https://4sii.tistory.com/436

 

[Nest.js] 이메일 인증 시스템 (1). nodemailer

개요 위와 같은 회원가입 폼에서 이메일 입력 후 인증번호 전송을 누르면 해당 이메일로 랜덤한 6자리 숫자의 인증번호가 보내지고 사용자는 이메일에서 인증번호를 확인 후 인증번호 란에 입

4sii.tistory.com

이전 글에서 전달받은 이메일에 대해 생성한 인증 번호를 따로 저장하는 코드는 생략했었는데

이번 포스팅을 통해 구현해보고자 한다.

 

저장할 공간은 cache-manager를 활용할 것이다. 

자세한 사용 방법은 아래 글 참고

https://4sii.tistory.com/433

 

[Nest.js] 캐싱 사용해보기

개요 Nest.js에서는 cache-manager 와 연계하여 캐싱 기능을 사용할 수 있는데 이에 대해 공부한 것을 정리해보았다. 설치 npm i cache-manager npm i -D @types/cache-manager 세팅 app.module.ts // ...import 생략 @Module({ i

4sii.tistory.com

 

코드

app.module.ts

@Module({
  imports: [
    ConfigModule.forRoot({ isGlobal: true }),
    TypeOrmModule.forRootAsync({ useClass: TypeOrmConfigService }),
    CacheModule.register({
      ttl: 300000,  // 데이터 캐싱 시간(밀리 초 단위)
      max: 100, // 최대 캐싱 개수
      isGlobal: true,
    }),
    UsersModule,
  ],
  controllers: [AppController],
  providers: [AppService],
})

CacheModule.register() 부분만 참고하면 된다. 

ttl (Time-To-Live)은 밀리 초 단위라 1000이 1초인데 테스트라 일단

인증 시간은 5분 정도로 설정했다.

 

users.service.ts

import _ from 'lodash';
import { CACHE_MANAGER, Inject, Injectable, NotFoundException, UnauthorizedException } from '@nestjs/common';
import { Cache } from 'cache-manager';
import { EmailService } from './email.service';

@Injectable()
export class UsersService {
  constructor(
    private readonly emailService: EmailService,
    @Inject(CACHE_MANAGER) private readonly cacheManager: Cache  // cache-manager를 inject 해준다.
  ) {}

  async sendVerification(email: string) {
    const verifyToken = this.generateRandomNumber();
    await this.cacheManager.set(email, verifyToken);  // cache-manager를 통해 저장
    await this.sendVerifyToken(email, verifyToken);
  }
  
  async sendVerifyToken(email: string, verifyToken: number) {
    await this.emailService.sendVerifyToken(email, verifyToken);
  }

  async verifyEmail(email:string, verifyToken: number) {
    const cache_verifyToken = await this.cacheManager.get(email);  // cache-manager를 통해 확인
    if (_.isNil(cache_verifyToken)) {
      throw new NotFoundException('해당 메일로 전송된 인증번호가 없습니다.');
    } else if (cache_verifyToken !== verifyToken) {
      throw new UnauthorizedException('인증번호가 일치하지 않습니다.');
    } else {
      await this.cacheManager.del(email);  // 인증이 완료되면 del을 통해 삭제
    }
  }

  private generateRandomNumber(): number {
    var minm = 100000;
    var maxm = 999999;
    return Math.floor(Math.random() * (maxm - minm + 1)) + minm;
  }
}

추가된 부분은 주석을 통해 확인할 수 있다.

먼저 생성자에서 cache-manager를 inject 해주었으며

sendVerification() 메서드에서 생성된 인증 번호와 이메일을 저장하는 모습을 볼 수 있다.

 

이후 verifyEmail() 메서드에서 

전달받은 email과 token에 대해 검증 과정을 거치는데 

해당하는 이메일이 key인 값이 없거나 가져왔는데 인증번호가 다를 경우 throw new Exception을 통해 

각각 4xx status code를 반환하게 된다.

 

만약 두 과정 모두 통과된다면 del을 통해 해당 email에 대한 정보를 삭제해준 뒤에 

200 status code를 반환해 인증을 완료시킨다.

 

테스트

직접 Insomnia를 통해 테스트 해보자

 

내 이메일로 인증번호를 요청한다.

 

메일로 인증번호 오는거 확인 (cchoseonghun2 가 서버에 세팅한 관리자 이메일이다.)

 

예외상황 1. 잘못된 이메일로 인증 요청할 경우

 

예외상황 2. 잘못된 인증번호로 요청할 경우

 

올바른 이메일로 전달받은 인증번호를 입력했을 때 200 status code를 받는 것을 볼 수 있다.

 

정리

추후에 cache-manager는 서버에 부담이 되기 때문에 Redis로 변경할 생각이며

Insomnia 대신 React를 통해 요청해서 실제 사용자로서의 화면을 구현해볼 생각이다.

728x90