앞서 네이버에서 제공하는 SMTP서버를 통해 사용자에게 인증코드를 전송시켰다.
서비스 로직에서 사용자가 입력한 인증번호를 검증하려면, 서버는 메일로 발송한 인증번호를 저장하고 있어야 한다.
인증번호는 짧은 시간 동안만 유효하며 이후에는 필요없기 때문에, 이런 조건에 맞춰 효율적으로 관리할 수 있는 Redis와 같은 인메모리 데이터 저장소를 사용하는 것이 적합하다고 판단했다. Redis는 데이터 액세스 속도와 *TTL(Time To Live) 설정 기능을 제공해 단기 데이터를 처리하는데 많이 사용한다고 한다.(Redis의 특징이 결국 Redis를 사용하는 이유)
1) Redis 설치
- Redis 서버 실행을 위해, .msi 파일 다운 및 설치
- 기본 포트 번호 6379
- 현재 배포된 3.0.504 버전은 설치 완료와 동시에 서비스에 자동등록
https://github.com/microsoftarchive/redis/releases
Releases · microsoftarchive/redis
Redis is an in-memory database that persists on disk. The data model is key-value, but many different kind of values are supported: Strings, Lists, Sets, Sorted Sets, Hashes - microsoftarchive/redis
github.com
2) Redis 의존성 추가
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>3.4.0</version>
</dependency>
3) 연결 정보 설정
- application.properties 파일에 Redis 연결 정보 설정
(API 키, 비밀번호, 포트번호 등은 application.properties 파일에서 관리하도록 수정함)
#redis
spring.data.redis.host=localhost
spring.data.redis.port=6379
4) RedisConfig 설정
- @Value를 사용해 applications.properties에서 정의한 설정 값을 Spring Bean에 주입
package com.tofit.mvc.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig {
@Value("${spring.data.redis.host}")
private String host;
@Value("${spring.data.redis.port}")
private int port;
@Bean
public RedisConnectionFactory redisConnectionFactory() {
return new LettuceConnectionFactory(host, port);
}
@Bean
public RedisTemplate<String, Object> redisTemplate(){
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new StringRedisSerializer());
redisTemplate.setConnectionFactory(redisConnectionFactory());
return redisTemplate;
}
}
5) RedisService 구현
- setCode : 주어진 이메일을 키로, 인증 코드를 값으로 Redis에 저장, 만료시간 3분 설정
- getCode : 주어진 이메일에 해당하는 인증코드를 Redis에서 조회
package com.tofit.mvc.model.service;
import java.util.concurrent.TimeUnit;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
@Service
public class RedisService {
private final RedisTemplate<String, Object> redisTemplate;
public RedisService(RedisTemplate<String, Object> redisTemplate) {
super();
this.redisTemplate = redisTemplate;
}
public void setCode(String email, String code) {
ValueOperations<String, Object> valOperations = redisTemplate.opsForValue();
// 만료시간 3분
valOperations.set(email, code, 180, TimeUnit.SECONDS);
}
public String getCode(String email) {
ValueOperations<String, Object> valOperations = redisTemplate.opsForValue();
Object code = valOperations.get(email);
if(code == null) {
return null;
}
return code.toString();
}
}
6) UserRestController에 이메일 인증 요청 완성
@PostMapping("/mail")
public ResponseEntity<Boolean> mailConfirm(@RequestParam String email) throws Exception{
String code = UUID.randomUUID().toString().substring(0,6);
boolean result = mailService.sendMail(code, email);
// 메일 보내기 성공
if(result) {
// redis 저장
redisService.setCode(email, code);
return new ResponseEntity<Boolean>(true, HttpStatus.OK);
}
return new ResponseEntity<Boolean>(false, HttpStatus.BAD_REQUEST);
}
@PostMapping("/mail/confirm")
public ResponseEntity<?> codeConfirm(@RequestBody EmailInfo emailInfo){
String answerCode = redisService.getCode(emailInfo.getEmail());
System.out.println(answerCode);
if(answerCode == null)
return new ResponseEntity<String>("코드 만료", HttpStatus.UNAUTHORIZED);
if(answerCode.equals(emailInfo.getCode()))
return new ResponseEntity<Boolean>(true, HttpStatus.OK);
return new ResponseEntity<Boolean>(false, HttpStatus.NON_AUTHORITATIVE_INFORMATION);
}
구현을 마치며
단순한 작업처럼 보였지만, 보안, 성능, 유지보수 등 여러 측면을 고려해야 한다는 점을 다시 한번 느꼈다. 진행하면서 발생한 에러가 여러가지가 있었는데, Maven 빌드 과정에서 application.properties 파일을 처리하면서 발생한 문제였고, 원인 파악에 많은 시간이 걸렸다. 파일을 자세히 보니 한글이 깨져있는 것을 반견했다. 깃헙에 프로젝트를 합치고 다시 개인브랜치로 가져와 작업하면서 파일의 인코딩 설정이 어느순간 변경된 것 같다. 다시 UTF-8로 바꿔서 문제를 해결할 수 있었다. 처음에 pom.xml에 의존성 추가하면서 problems창에 에러가 떴는데 이 원인일줄은 몰랐다. .m2 폴더 삭제하고 플러그 다시 설치하고 난리..
'SPRING' 카테고리의 다른 글
이메일 인증 (1) - SMTP 활용한 본인확인 인증코드 전송 (6) | 2024.12.20 |
---|