spring_sunshine
Coding Blog
spring_sunshine
전체 방문자
오늘
어제
  • 분류 전체보기 (144)
    • 개발일지 (2)
    • Java (12)
      • 개념 (12)
      • 코테 (0)
    • Spring (5)
      • 개념 (5)
      • 프로젝트 (0)
    • CS (19)
      • 네트워크 (9)
      • 스터디 (6)
      • 정처기 (3)
    • SQLD (3)
    • React (27)
      • [책] 리액트를 다루는 기술 (13)
      • [강의] 만들면서 배우는 리액트 (2)
      • 개념 (11)
    • React Native (10)
      • [책] 리액트 네이티브 앱프로그래밍 (4)
      • [강의] 처음 배우는 리액트 네이티브 (4)
      • 프로젝트 (1)
    • Python (37)
      • [책] 이코테 (9)
      • [강의] 파이썬 알고리즘 문풀 (6)
      • [강의] 파이썬입문과 크롤링기초 (6)
      • 개념 (7)
      • 백준 (9)
    • Flutter (4)
      • Dart (4)
    • ML (14)
      • [책] 혼공머신 (6)
      • [강의] 딥러닝 CNN (4)
      • NumPy (4)
    • JavaScript (3)
    • 기타 (1)

블로그 메뉴

  • 홈
  • 글쓰기
  • 관리

공지사항

인기 글

태그

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
spring_sunshine

Coding Blog

[SnowTaxi] 이메일 인증 로직과 비동기 처리
개발일지

[SnowTaxi] 이메일 인증 로직과 비동기 처리

2024. 1. 28. 15:16

학교 프로젝트에서 학우 전용 인증/인가 시스템을 개발하던 중이었습니다. 회원가입 플로우를 구현하고 테스트를 진행하는데, 한 가지 큰 문제점을 발견했습니다.

"이메일 인증 버튼 누르고 5초나 기다려야 하네?"

이메일 전송에 5초나 걸리는 동안 사용자는 아무것도 하지 못한 채 기다려야 했습니다. 개발자인 저도 테스트하면서 답답함을 느꼈는데, 실제 사용자라면 더 큰 불편을 겪을 것이 분명했습니다.

 

코드를 살펴보니 원인을 찾을 수 있었습니다. 회원가입 로직이 하나의 스레드에서 순차적으로 처리되고 있었습니다.

@Service
public class UserService {
    public void registerUser(UserDto userDto) {
        // 1. 회원 정보 저장
        User user = new User(userDto);
        userRepository.save(user);
        
        // 2. 이메일 전송 (여기서 5초 대기)
        emailService.sendVerificationEmail(user);
        // 3. 이메일 전송이 완료될 때까지 다음 단계로 진행 불가
    }
}

"꼭 이메일이 발송될 때까지 기다려야 할까? 사용자가 다른 정보를 먼저 입력할 수는 없을까?"

 

해결 방안: 멀티스레드와 화면 분리

문제를 해결하기 위해 두 가지 방향으로 접근했습니다.

  1. 백엔드: 이메일 전송을 별도 스레드로 분리하자.
  2. 프론트엔드: 회원가입 화면을 두 단계로 나누어 자연스러운 흐름을 구성하자.

Spring의 비동기 처리 기능을 찾아보던 중 @Async 어노테이션을 발견했습니다. 회원가입 처리와 이메일 전송을 별도의 스레드로 나눌 수 있겠다는 생각이 들었습니다.

1. Thread Pool 구성

먼저 이메일 전송을 담당할 스레드 풀을 설정했습니다.

@Configuration
@EnableAsync
public class AsyncConfig {
    @Bean(name = "taskExecutor")
    public Executor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(2);    // 기본 작업 스레드 수
        executor.setMaxPoolSize(5);     // 최대 스레드 수
        executor.setQueueCapacity(500); // 작업 대기 큐 크기
        executor.setThreadNamePrefix("EmailThread-");
        executor.initialize();
        return executor;
    }
}

 

2. 비동기 이메일 서비스 구현

이메일 전송 로직을 별도 스레드에서 처리하도록 변경했습니다.

@Service
public class EmailService {
    @Async
    public void sendVerificationEmailAsync(User user) {
        // 이메일 전송 로직 (5초 소요)
        // 메인 스레드는 차단되지 않고 즉시 다음 작업 진행
    }
}

 

2. 회원가입 UI 개선

기존의 단일 페이지 회원가입을 두 단계로 나누어 재설계했습니다.

 

Step 1: 회원정보 입력 페이지

  • 이메일 입력 후 "인증하기" 버튼 클릭 → 즉시 "이메일이 발송되었습니다"라는 알림 표시
  • 사용자는 이메일 발송까지 기다리지 않고, 나머지 회원 정보를 입력하고 다음 단계로 진행

Step 2: 인증번호 확인 페이지

  • 인증번호 입력 필드만 있는 간단한 화면
  • 필요시 인증번호 재발송 버튼도 제공됨

 

리팩토링 후 느낀 점

리팩토링 후 회원가입 플로우가 훨씬 자연스러워졌습니다.

  • 사용자가 이메일 인증 버튼을 클릭하면 즉시 “인증번호가 발송되었습니다” 메시지가 표시됩니다.
  • 이메일 전송이 별도의 비동기 스레드에서 처리되므로, 사용자는 다른 회원정보를 입력할 수 있습니다.
  • 모든 정보 입력 후 인증 페이지로 이동하면, 그때쯤 이메일이 도착해 있어 바로 인증이 가능합니다.

테스트 과정에서 “어, 이제 기다리는 느낌이 안 드네?“라는 생각이 들었을 때, 정말 기분이 좋았습니다.

 

@Async의 동작 방식과 주의점

리팩토링 과정에서 @Async의 내부 동작 방식에 대해서도 깊이 이해하게 되었습니다.

@Async는 Spring AOP(Aspect Oriented Programming)의 프록시 방식으로 동작합니다. 쉽게 말해, Spring이 해당 클래스를 감싸는 프록시 클래스를 생성하고, 이 프록시가 비동기 처리를 담당하는 방식입니다.

// 실제 코드
@Service
public class EmailService {
    @Async
    public void sendEmail() {
        // 이메일 전송 로직
    }
}

// Spring이 내부적으로 생성하는 프록시 형태
public class EmailServiceProxy extends EmailService {
    public void sendEmail() {
        // 별도 스레드에서 실행
        executor.submit(() -> {
            super.sendEmail();
        });
    }
}

 

이러한 동작 방식 때문에 두 가지 중요한 제약사항이 있었습니다.

  1. private 메소드에는 @Async를 사용할 수 없습니다.
    • 프록시는 원본 클래스를 상속하므로, private 메소드는 접근이 불가능합니다.
  2. 같은 클래스 내의 메소드 호출(self-invocation)에는 @Async가 적용되지 않습니다.
    • 내부 호출은 프록시를 거치지 않고 직접 호출되기 때문입니다.
 

'개발일지' 카테고리의 다른 글

[SnowTaxi] WebSocket + STOMP로 실시간 채팅 구현  (0) 2024.02.10
    '개발일지' 카테고리의 다른 글
    • [SnowTaxi] WebSocket + STOMP로 실시간 채팅 구현
    spring_sunshine
    spring_sunshine

    티스토리툴바