회원가입 기능을 만들 때, 시간 제한이 있는 이메일 인증을 통과하도록 만들어보자. Redis를 사용하면 TTL(Time To Live)을 써서 시간 제한을 쉽게 구현할 수 있다.
로컬에 Redis 설치하기
homebrew로 간단하게 설치했다. 설치가 끝났다면 redis 서버를 실행시키자. 서버가 실행중이라면 redis-cli ping 명령어를 입력하면 PONG이라고 응답한다.
brew install redis
brew services start redis
redis-cli ping # PONG
Spring boot 의존성 및 설정 추가하기
build.gradle의 dependencies에 redis를 추가하고, application.yml에 호스트와 포트 설정을 추가하자.
// Redis 의존성 추가
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
spring:
data:
redis: # redis 설정
host: localhost
port: 6379
Redis Configuration 설정하기
Spring boot에서 redis를 사용하기 위해 필요한 Bean을 등록하자.
RedisConnectionFactory는 Spring Data Redis에서 Redis 데이터 저장소에 대한 연결을 추상화하는 인터페이스다. 주로 Jedis와 Lettuce 둘 중 하나를 구현체로 사용하는데 요즘은 성능 면에서 Lettuce가 우세해서 Lettuce를 사용한다고 한다.
RedisTemplate는 Redis 데이터 작업을 위한 고수준의 추상화를 제공한다.
- 다양한 데이터 타입 지원
- 직렬화/역직렬화 처리
- 트랜잭션 지원
- 연결 관리
@Configuration
@EnableRedisRepositories
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<?,?> redisTemplate() {
RedisTemplate<?,?> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory());
return redisTemplate;
}
}
인증 과정 및 Redis TTL 설정하기
Redis의 TTL(Time-To-Live) 기능을 설정해서 쉽게 처리할 수 있다. 회원가입 요청 이메일을 key, 인증 코드를 value로 설정하고 가입할 때 비교하자. key, value 모두 String으로 사용할 것이니까 RedisTemplate<String, String> redisTemplate를 선언해서 사용하자.
private final RedisTemplate<String, String> redisTemplate;
@Transactional
public UserResponse signUp(SignUpRequest request) {
// 이미 가입한 이메일 차단
if (userRepository.existsByEmail(request.getEmail())) {
throw new BaseException(ErrorCode.ALREADY_SIGNUP_EMAIL);
}
// 인증 코드 없거나 다르면 실패
ValueOperations<String, String> operations = redisTemplate.opsForValue();
String authCode = operations.get(request.getEmail());
if (authCode == null || !authCode.equals(request.getAuthCode())) {
throw new BaseException(ErrorCode.FAIL_TO_AUTH_EMAIL);
}
User user = new User(request, passwordEncoder);
User save = userRepository.save(user);
return new UserResponse(save);
}
@Transactional
public void sendAuthEmail(String email) {
Email authEmail = createAuthEmail(email);
ValueOperations<String, String> operations = redisTemplate.opsForValue();
operations.set(email, authEmail.getContent(), 5, TimeUnit.MINUTES); // TTL 5분
emailService.send(authEmail);
}
private Email createAuthEmail(String email) {
return new Email(
email,
"회원가입 인증 메일입니다.",
createAuthCode()
);
}
private String createAuthCode() {
Random random = new Random();
return Integer.toString(random.nextInt(100000, 999999));
}
이메일 보내기
의존성 추가
implementation 'org.springframework.boot:spring-boot-starter-mail
서비스 코드
간단한 텍스트만 전송하기 때문에 SimpleMailMessage를 사용했다. 텍스트 외의 것들을 전송하고 싶다면 MimeMailMessage를 사용하면 된다.
@Service
@RequiredArgsConstructor
public class EmailService {
private final JavaMailSender javaMailSender;
@Value("${spring.mail.from}")
private String from;
public void send(Email email) {
SimpleMailMessage mail = new SimpleMailMessage();
mail.setFrom(from);
mail.setTo(email.getTo());
mail.setSubject(email.getSubject());
mail.setText(email.getContent());
try {
javaMailSender.send(mail);
} catch (MailException e) {
throw new BaseException(ErrorCode.FAIL_TO_SEND_EMAIL);
}
}
}
'Tool > Spring' 카테고리의 다른 글
[Spring] Primary Key가 설정된 객체를 영속화 할 때 발생했던 문제 (0) | 2023.12.28 |
---|---|
[Spring] Spring Data JPA의 쿼리 메서드로 정렬 조건 여러 개 + 페이지네이션 조회하기 (0) | 2023.12.20 |
[Spring] 커스텀 어노테이션으로 DTO 유효성 검사하기 (0) | 2023.12.14 |
[Spring boot] 외부 파일 저장소를 사용할 때 트랜잭션은 어떻게 관리해야 할까? (with. AWS S3) (2) | 2023.12.11 |
[Spring boot] controller layer 테스트 코드 작성하기 (0) | 2023.12.04 |