진행중인 프로젝트에서 학교 이메일 인증이 필요하여 이메일 인증에 대한 코드를 작성하게 되었다.
🙋♀️ redis가 무엇이며 선택한 이유?
A. redis란 인메모리 데이터 저장소로 빠른 응답 시간, 데이터 만료 기능을 제공하기 때문에 제한된 시간 동안만 유효한 이메일 관련 데이터를 저장하기 적합하다고 판단하였다.
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
spring-boot-starter-mail, spring-boot-starter-data-redis 의존성을 추가해주었다.
application.yml
spring:
mail:
host: smtp.gmail.com
port: 587
username: teamwonpage@gmail.com
password: ${MAIL_PASSWORD}
properties:
mail.smtp.auth: true
mail.smtp.starttls.enable: true
redis:
host: localhost
port: 6379
나는 구글로 host email을 결정하였다. 구글 이메일 port는 587이다.
password는 github에 올라가면 절대 안되기때문에 환경변수 설정을 하는 것을 추천한다.
redis port는 6379이다.
구글 계정에 2단계 인증과 앱 비밀번호를 설정해야하는데, application.yml에 발급받은 앱 비밀번호를 적어주면 된다.
memberApiController
//이메일 전송
@PostMapping("/api/email")
public ResponseDto sendEmail(@RequestBody EmailSendDto emailDto) {
String subject = "회원가입 인증 메일입니다.";
Random random = new Random();
int code = random.nextInt(9000) + 1000;
String text = "인증 코드는 " + code + "입니다.";
memberService.send(emailDto.getEmail(), subject, text, code);
return new ResponseDto(HttpStatus.OK.value(), "이메일 전송 성공");
}
//이메일 인증
@PostMapping("/api/email/verify")
public ResponseDto verifyEmail(@RequestBody EmailVerificationDto emailVerificationDto) {
String email = emailVerificationDto.getEmail();
String code = emailVerificationDto.getCode(); //사용자가 입력한 코드
String savedCode = memberService.getVerificationCode(email); //redis에 저장된 코드
memberService.verificationEmail(code, savedCode);
return new ResponseDto(HttpStatus.OK.value(), "이메일 인증 성공");
}
memberService
private final JavaMailSender mailSender;
private final StringRedisTemplate redisTemplate;
//이메일 전송
public void send(String email, String subject, String text, int code) {
long count = getEmailRequestCount(email);
if (count == 5) {
throw new RuntimeException("이메일 인증 요청 5번 초과로 24시간 동안 이메일 인증 요청을 할 수 없습니다.");
}
SimpleMailMessage message = new SimpleMailMessage();
message.setTo(email);
message.setSubject(subject);
message.setText(text);
mailSender.send(message);
saveVerificationCode(email, String.valueOf(code)); //인증 코드 저장
increaseEmailRequestCount(email); // 이메일을 보낸 후 요청 횟수를 증가
}
//이메일 인증
public void verificationEmail(String code, String savedCode) {
if (!code.equals(savedCode)) {
throw new VerificationFailureException("이메일 인증 실패");
}
}
//redis에서 인증코드 가져오기
public String getVerificationCode(String email) {
return redisTemplate.opsForValue().get(email);
}
//redis에 인증코드 저장
public void saveVerificationCode(String email, String code) {
redisTemplate.opsForValue().set(email, code, 1, TimeUnit.MINUTES); //1분 타임아웃
}
//이메일 요청 카운트 증가
public void increaseEmailRequestCount(String email) {
String key = "email_request_count:" + email;
long count = redisTemplate.opsForValue().increment(key);
if (count == 5) {
redisTemplate.expire(key, 24, TimeUnit.HOURS);
}
}
//이메일 요청 카운트 가져오기
public long getEmailRequestCount(String email) {
String key = "email_request_count:" + email;
String value = redisTemplate.opsForValue().get(key);
return value != null ? Long.parseLong(value) : 0;
}
JavaMailSender 와 StringRedisTemplate 인터페이스를 사용하여 코드를 작성하였다. 인증코드를 radis에 저장하여 1분으로 제한을 두었고, 한 email 당 5번이 넘어가면 24시간동안 이메일 전송이 불가하도록 redis에 count를 저장해두었다.
redis 다운로드
https://github.com/microsoftarchive/redis/releases
msi파일을 다운받는다. -> redis-cli.exe 실행
성공..!