Nest - Next

Nest - Next | n2server | Signup | Email Send(2)

개발자티포 2021. 10. 20. 14:31
728x90
반응형

 

전 포스터에서 이메일 중복여부를 확인하는 로직까지 완성했다.

이번엔 백엔드에서 클라이언트가 원하는 데이터를 전송해보도록 하겠다.

 

이메일 리소스를 만든다.

$ nest g res email

 

이메일 중복 여부와 이메일 보내기 기능을 같이 구현해보겠다.

먼저 EmailService에서 UserModel을 써야하기 때문에 EmailModule부분을 다음과 같이 바꿔준다.

email.module.ts

import { Module } from '@nestjs/common';
import { EmailService } from './email.service';
import { EmailController } from './email.controller';
//mongose
import { MongooseModule } from '@nestjs/mongoose';
//userModel
import { User, UserSchema } from 'src/user/schemas/user.schema';


@Module({
  imports: [MongooseModule.forFeature([{ name: User.name, schema: UserSchema }])],
  controllers: [EmailController],
  providers: [EmailService]
})
export class EmailModule {}

 

또한 nest에서 지원해주는 email모듈을 써야하기 때문에 다음 모듈을 설치하고 import 해준다.

 

 $ npm install @nestjs-modules/mailer nodemailer handlebars --save

 

그다음 emailModule을 다음과 같이 바꿔준다. 네이버 아이디와 비밀번호는 개인용으로 쓰면 된다.

email.module.ts

import { Module } from '@nestjs/common';
import { EmailService } from './email.service';
import { EmailController } from './email.controller';
//mongose
import { MongooseModule } from '@nestjs/mongoose';
//userModel
import { User, UserSchema } from 'src/user/schemas/user.schema';
//Email
import { MailerModule } from '@nestjs-modules/mailer';
import { HandlebarsAdapter} from '@nestjs-modules/mailer/dist/adapters/handlebars.adapter';

@Module({
  imports: [
  MailerModule.forRoot({
    transport: {
      service: 'Naver',
      host: 'smtp.naver.com',
      port: 587, 
      auth: {
        user: process.env.NAVER_ID, // 네이버 아이디
        pass: process.env.NAVER_PW, // 네이버 비밀번호
      },
    },
    template: {
      dir: process.cwd() + '/template/',
      adapter: new HandlebarsAdapter(), // or new PugAdapter()
      options: {
        strict: true,
      },
    },
  }),
  MongooseModule.forFeature([{ name: User.name, schema: UserSchema }])],
  controllers: [EmailController],
  providers: [EmailService]
})
export class EmailModule {}

 


Nest Configuration에 관한 내용은 아래 링크에서 확인하면 된다.

 

https://docs.nestjs.kr/techniques/configuration

 

네스트JS 한국어 매뉴얼 사이트

네스트JS 한국, 네스트JS Korea 한국어 매뉴얼

docs.nestjs.kr


 

Naver smtp 서비스를 이용하려면 Naver 메일로 가서 설정을 해주어야한다.

 

 

자세한 내용은 아래 링크

https://velog.io/@jiwon/-Nodemailer%EB%A1%9C-%EC%9D%B8%EC%A6%9D-%EA%B4%80%EB%A0%A8-%EC%9D%B4%EB%A9%94%EC%9D%BC-%EB%B3%B4%EB%82%B4%EA%B8%B0-d4k4pqoot4

 

Nodemailer로 인증 관련 이메일 보내기

사용자가 회원가입 시, 입력한 이메일이 유효한지 검증하는 상황 혹은 사용자에게 임시 비밀번호를 전달하는 경우 등등 서버 측에서 사용자에게 이메일을 보내야하는 경우가 존재합니다! 이때,

velog.io

 

 

EmailService 부분에 Usermodel을 가져오고 다음과같이 수정해준다.

email.service.ts

import { Injectable } from '@nestjs/common';
//mongoose
import { Model } from 'mongoose';
import { InjectModel } from '@nestjs/mongoose';
import { User, UserDocument } from '../user/schemas/user.schema';
//dtd
import { CreateEmailDto } from './dto/create-email.dto';
import { UpdateEmailDto } from './dto/update-email.dto';

@Injectable()
export class EmailService {
  constructor(@InjectModel(User.name) private userModel: Model<UserDocument>) {}
  
  create(createEmailDto: CreateEmailDto) { 
    return 'This action adds a new email';
  }

  findAll() {
    return `This action returns all email`;
  }

  async findOne(email: string) {
    const userOne = await this.userModel.findOne({email});
    if(userOne)
      return {result : false, user : userOne};
    else
      return {result : true};
  }

  update(id: number, updateEmailDto: UpdateEmailDto) {
    return `This action updates a #${id} email`;
  }

  remove(id: number) {
    return `This action removes a #${id} email`;
  }
}

 

 

이제 이메일을 보내는 함수를 작성해보자.

email.service.ts

import { Injectable } from '@nestjs/common';
//mongoose
import { Model } from 'mongoose';
import { InjectModel } from '@nestjs/mongoose';
import { User, UserDocument } from '../user/schemas/user.schema';
//dto
import { CreateEmailDto } from './dto/create-email.dto';
import { UpdateEmailDto } from './dto/update-email.dto';
//mailer
import { MailerService } from '@nestjs-modules/mailer';

@Injectable()
export class EmailService {
  constructor(
    @InjectModel(User.name) private userModel: Model<UserDocument>,
    private mailerService: MailerService
    ) {}

  async findOne(email: string) {
    const userOne = await this.userModel.findOne({email});
    if(userOne)
      return {result : false, user : userOne};
    else
      return {result : true};
  }

  async emailSend(email : string) {
    try {
      const number: number = Math.floor(100000 + Math.random() * 900000);
      // 메일보내기
      await this.mailerService.sendMail({
        to: email, // list of receivers
        from: 'user@email', // sender address
        subject: '이메일 인증 요청 메일입니다.', // Subject line
        html: '6자리 인증 코드 : ' + `<b> ${number}</b>`, // HTML body content
      });
      return {result : true, authNum : number}
    } catch (err) {
      return {result : false}
    }
  }
}

 

async emailSend 함수에서 파라미터로 email을 받아 메일을 받을 사람을 정하고, 보낼 사람과 내용까지 정의해서

메일을 보내게끔 한다.

 

반환값으로 인증번호를 그대로 보내기엔 보안위험이 있을 수 있으니 bcrypt로 암호화 해서 보내자.

 

$ npm i bcrypt
$ npm i @types/bcrypt -D

 

email.service.ts

import { Injectable } from '@nestjs/common';
//mongoose
import { Model } from 'mongoose';
import { InjectModel } from '@nestjs/mongoose';
import { User, UserDocument } from '../user/schemas/user.schema';
//dto
import { CreateEmailDto } from './dto/create-email.dto';
import { UpdateEmailDto } from './dto/update-email.dto';
//mailer
import { MailerService } from '@nestjs-modules/mailer';
//bcrypt
import * as bcrypt from 'bcrypt'

@Injectable()
export class EmailService {
  constructor(
    @InjectModel(User.name) private userModel: Model<UserDocument>,
    private mailerService: MailerService,
    ) {}

  async findOne(email: string) {
    const userOne = await this.userModel.findOne({email});
    if(userOne)
      return {result : false, user : userOne};
    else
      return {result : true};
  }

  async emailSend(email : string) {
    try {
      const number: string = Math.floor(100000 + Math.random() * 900000).toString();
      console.log(number)
      // 메일보내기
      await this.mailerService.sendMail({
        to: email, // list of receivers
        from: 'user@email.com', // sender address
        subject: '이메일 인증 요청 메일입니다.', // Subject line
        html: '6자리 인증 코드 : ' + `<b> ${number}</b>`, // HTML body content
      });
      const authNum = await bcrypt.hash(number,parseInt(process.env.saltOrRounds));
      return {result : true, authNum : authNum};
    } catch (err) {
      return {result : false, authNum : ''};
      console.log(err)
    }
  }
}

bcryptimport하고 class 내부에 salt 문자열을 선언했으며 bcrypt로 number를 암호화해서 반환했다.

 

이제 생성한 authNum을 쿠키에 넣어주기 위해 res를 추가하고 수정하자.

 

email.service.ts

  async emailSend(email : string, res : any) {
    try {
      const number: string = Math.floor(100000 + Math.random() * 900000).toString();
      console.log(number)
      // 메일보내기
      await this.mailerService.sendMail({
        to: email, // list of receivers
        from: process.env.EMAIL, // sender address
        subject: '이메일 인증 요청 메일입니다.', // Subject line
        html: '6자리 인증 코드 : ' + `<b> ${number}</b>`, // HTML body content
      });
      const authNum = await bcrypt.hash(number,parseInt(process.env.saltOrRounds));
      
      res.cookie('authNum', authNum, {path: '/', expires: new Date(Date.now()+300000)}); // 쿠키생성
    
      return {result : true, authNum : authNum};
    } catch (err) {
      console.log(err)
      return {result : false, authNum : ''};
      
    }
  }

 


쿠키를 사용하기 위해 먼저 다음 명령어를 실행한다.

$ npm i cookie-parser
$ npm i -D @types/cookie-parser

 

그다음 main.ts 파일을 다음과 같이 수정한다.

 

main.ts

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import * as cookieParser from 'cookie-parser';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.use(cookieParser());
  await app.listen(3001);
}
bootstrap();

 


 

 

 

이메일을 성공적으로 보냈으면 cookie에 authNum을 시간을 설정해서 담아주고 result를 true로 보냈다.

이제 다시 프론트로 넘어가 result가 true로 반환되었을 때 로직을 구현해보자.

 

남은시간과 이메일 확인 입력칸 state를 만들어준다.

    const [emailCount,setEmailCount] = useState<number>(0); // email counter
    const [emailAuthText,setEmailAuthText] = useState<string>('emailAuthText'); // email auth text

 

 

emailSendOK state를 이용해 재전송으로 바꾸고 하단에 인증번호 입력칸과 확인버튼, 남은시간을 보여주는 곳을 만든다. 

 

<div>
    <input 
        name="email"
        type="email"
        placeholder="Enter Email"
        onChange={userChangeHandler}
        disabled={userState.emailAuth}/>
    <button
    	type="button"
        onClick={emailSend}
        disabled={userState.emailAuth}>
    {emailsendOK ? '재전송' : '보내기'}    
    </button>
</div>
{ emailsendOK ?
<div>
    <p style={{color : 'red', fontSize: 5}}>남은 시간 : {Math.floor(emailCount / 60)} 분 {Math.floor(emailCount % 60)} 초</p>
    <input 
        name="emailAuthText"
        type="text"
        placeholder="Enter Email"
        onChange={(event) => { setEmailAuthText(event.target.value)}}/>
    <button
    	type="button"
        onClick={emailSend}>
    확인  
    </button>
</div>
: ''}

 

보내기 버튼을 눌러보자. 이메일이 정상적으로 보내졌고 하단에 추가되었다.

 

 

 

 

 

 

728x90
반응형