이메일 인증

2024. 6. 15. 14:00TIL

✔오늘 배운 중요한 🔑 point

  • 프로그래밍에서 이메일 인증을 구현할 때 SMTP(Simple Mail Transfer Protocol)를 사용하면 된다

🎯 오늘 배운 내용

이메일 인증

사용자 인증은 한명의 사람이 여러 계정을 만들수 있는 상황을 방지하기 위해서 꼭 필요한 기능이다.

사용자 인증을 하는 방법 중 이메일 인증을 시도하였다.

https://hyunmin1906.tistory.com/276

 

[Go] Google Gmail SMTP 설정 방법 및 메일 전송

■ SMTP 간이 우편 전송 프로토콜(Simple Mail Transfer Protocol)의 약자. 이메일 전송에 사용되는 네트워크 프로토콜이다. 인터넷에서 메일 전송에 사용되는 표준이다. 1982년 RFC821에서 표준화되어 현재

hyunmin1906.tistory.com

google Gmail 이메일 전송에 사용되는 네트워크 프로토콜을 설정하기 위해서 GOOGLE GMAIL 설정을 해야하는데 해당 블로그를 참조해서 설정을 완료하였다.

Google Gmail의 SMTP 서버를 이용해서 Gmail 계정을 통해서 이메일을 보낼수 있는 상태가 완료 되었다.

이제 이 설정 부분을 현재 작성 중인 api에 추가하고 관련 로직을 작성만 해주면 된다.

 

application.yml (프로그래밍 코드에서 이메일을 보내기 위한 설정)

spring:
  mail:
    host: smtp.gmail.com
    port: 587
    username: *********@gmail.com
    password: **** **** **** ****
    properties:
      mail:
        smtp:
          auth: true
          timeout: 5000
          starttls:
            enable: true
          ssl:
            trust: smtp.gmail.com

application.yml 파일 안의  spring하위에 작성하면 된다.

 

 

build.grade.kts( 의존성 추가 )

implementation ("org.springframework.boot:spring-boot-starter-mail")

 

EmailService( 이메일의 양식을 작성)

package spartacodingclub.nbcamp.kotlinspring.project.fco234.gameboard.infra.security.emailAuthentication.service

import org.springframework.beans.factory.annotation.Autowired
import org.springframework.mail.javamail.JavaMailSender
import org.springframework.mail.javamail.MimeMessageHelper
import org.springframework.stereotype.Service


@Service
class EmailService @Autowired constructor(
    private val mailSender: JavaMailSender
) {

    fun sendEmail(to: String, subject: String, text: String) {
        val message = mailSender.createMimeMessage()
        val helper = MimeMessageHelper(message, true)
        helper.setTo(to)
        helper.setSubject(subject)
        helper.setText(text, true) // true로 설정하여 HTML 내용을 허용

        mailSender.send(message)
    }
}

Spring Framework를 사용하여 애플리케이션에서 이메일을 보낼 수 있게 하기 위한 EmailService 작성

 

 

private val mailSender: JavaMailSender

Spring Framework에서 이메일 전송을 지원하는 인터페이스인 JavaMailSender를 변수  mailSender에 지정한다

 

fun sendEmail(to: String, subject: String, text: String) {
    val message = mailSender.createMimeMessage()
}

mailSender를 사용하여 새로운 MimeMessage 객체를 생성. (MimeMessage는 이메일 메시지를 표현한다.)

 

val helper = MimeMessageHelper(message, true)

MimeMessage를 보다 쉽게 설정할 수 있게 도와주는 MimeMessageHelper 객체를 생성

두 번째 인자인 true는 멀티파트 메시지를 허용하여 HTML 형식의 본문을 지원함

 

helper.setTo(to)
helper.setSubject(subject)
helper.setText(text, true) // true로 설정하여 HTML 내용을 허용

 

helper.setTo(to): 이메일의 수신자를 설정.

helper.setSubject(subject): 이메일의 제목 설정.

helper.setText(text, true): 이메일 본문 설정. 두 번째 인자인 true는 본문이 HTML 형식임을 나타냄.

 

 

mailSender.send(message)

구성된 이메일 메시지를 전송한다.

 

 

Service-  signup() (이메일 인증은 회원 가입시에 이루어져야 한다)

 private val validationCodes: MutableMap<String,String> = mutableMapOf()

fun signUp(signUpRequest: SignUpRequest): UserResponse? {

    if(userRepository.existsByEmail(signUpRequest.email)){
        throw IllegalArgumentException("ID is already exists")
    }

    val verificationCode = UUID.randomUUID().toString().substring(0, 6)
    val user = User(
        email = signUpRequest.email,
        password = passwordEncoder.encode(signUpRequest.password),
        profile = Profile(
            name = signUpRequest.name,
            birthday = signUpRequest.birthday
        ),
        role = UserRole.PLATFORM_USER,
        introduce = null
        
    )

    validationCodes.put(key= user.email, value = verificationCode)
    sendAuthenticationEmail(user.email, verificationCode)
    return userRepository.save(user).toResponse()
}

 

val verificationCode = UUID.randomUUID().toString().substring(0, 6)

인증코드로 무작위로 생성된 UUID에서 첫 6자리 문자열을 추출한다

 

private val validationCodes: MutableMap<String,String> = mutableMapOf()

사용자에게 인증 코드를 보냈을때 사용자가 입력한 인증코드가 맞는지 확인을 할려면 서버측에서도 사용자의 이메일과 인증코드를 알고있어야 하기때문에 validationCodes 변수를 생성

 

validationCodes.put(key= user.email, value = verificationCode)

validationCodes 안에 회원가입을 시도하는 유저의 이메일과 랜덤코드를 저장한다.

 

sendAuthenticationEmail(user.email, verificationCode)

회원가입시 입력한 이메일로 인증코드를 보낸다

 

Service - sendAuthenticationEmail()

private fun sendAuthenticationEmail(email: String, code: String) {
    val subject = "FC345의 프로젝트 서비스 회원가입 코드입니다"
    val text = "인증 코드: $code"
    emailService.sendEmail(email, subject, text)
}

이메일 인증시 보내는 내용을 작성한다

 

 

Service- verifyEmail() (사용자가 제대로 된 인증 코드를 입력했는지 확인해야한다)

fun verifyEmail(email: String, code: String): Boolean {
    if(!validationCodes.containsKey(key = email)) throw RuntimeException("해당 이메일로 진행된 인증 기록이 없음")
    else{
        return if (validationCodes[email]!! == code){
            validationCodes.remove(email)
            true
        }
        else false
    }
}

 

if(!validationCodes.containsKey(key = email)) throw RuntimeException("해당 이메일로 진행된 인증 기록이 없음")

validationCodes에 해당 이메일이 없다면 해당 이메일로 인증이 시도되지 않았음을 의미하므로 예외처리를 발생

 

else{
    return if (validationCodes[email]!! == code){
        validationCodes.remove(email)
        true
    }
    else false
}

validationCodes에 해당 이메일이 있다면 validationCodes의 value인 인증코드와 사용자가 입력한 code가 일치하는지 확인하고

일치한다면 validationCodes의 해당이메일 부분을 지우고 true를 반환한다

 

 

 

Controller - signup() (회원가입 로직 제어)

@PostMapping("/signup")
fun signup(@Valid @RequestBody signUpRequest: SignUpRequest): ResponseEntity<String> {
    val userResponse = authService.signUp(signUpRequest)
    return ResponseEntity.status(HttpStatus.CREATED).body("해당 이메일로 코드를 보냈으니 해당 코드를 verify-email에 입력해주세요")
}

 

 

Controller - verifyEmail() (이메일 인증 로직 제어)

@PostMapping("/verify-email")
fun verifyEmail(
    @RequestParam email: String,
    @RequestParam code: String
): ResponseEntity<String> {
    return if (authService.verifyEmail(email, code)) {
        ResponseEntity.ok("이메일 인증 완료")
    } else {
        ResponseEntity.status(HttpStatus.BAD_REQUEST).body("인증 코드가 유효하지 않습니다.")
    }
}

 

 

테스트 결과

 

이메일 인증 코드를 활용해 정상적으로 회원가입이 된것을 확인 할 수있다.

 

 

🤔 어떻게 활용할까?

인증 코드 뿐만 아니라 해당 이메일로 링크를 보내서 해당 링크로 접속을 하면 인증이 되는 식의 방법도 다음에 구현해 보면 좋을것 같다

📓 오늘의 한줄

"Blessed is he who has found his work; let him ask no other blessedness."

- Thomas Carlyle -