우리는 Spring Boot에서 ExecutorService를 사용하여 Payment API 지연 시간을 60% 감소시켰습니다

발행: (2025년 12월 29일 오전 12:02 GMT+9)
4 min read
원문: Dev.to

Source: Dev.to

위 링크에 포함된 전체 텍스트를 제공해 주시면, 해당 내용을 한국어로 번역해 드리겠습니다. 코드 블록, URL 및 마크다운 형식은 그대로 유지하면서 번역을 진행합니다.

🏦 Business Scenario (Very Common in FinTech)

결제 처리 서비스를 상상해 보세요.

결제를 처리하기 전에 시스템은 다음을 검증해야 합니다:

  • 계정 상태 (활성 / 차단)
  • 잔액 확인
  • 사기 위험 검사

각 검증은:

  • 다른 내부 서비스를 호출하고
  • 300–800 ms가 소요되며
  • 독립적입니다.

잘못된 접근 방식 (순차 처리)

  • 총 시간 ≈ 2 seconds
  • 높은 지연 → SLA 위반

올바른 접근 방식 (ExecutorService를 이용한 병렬 처리)

  • 모든 검증을 동시에 실행
  • 총 시간 ≈ max(800 ms)

🧠 Why ExecutorService Here?

  • Controlled thread pool (avoid thread explosion)
  • Parallel execution
  • Better SLA
  • Clean error handling

This is exactly where ExecutorService shines.

아키텍처 흐름

Client
  |
  v
Payment API
  |
  +-- Account Validation (Thread-1)
  +-- Balance Check     (Thread-2)
  +-- Fraud Check       (Thread-3)
  |
  v
Final Decision

1️⃣ ExecutorService 구성

package com.example.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@Configuration
public class ExecutorConfig {

    @Bean
    public ExecutorService executorService() {
        // Controlled pool for validation tasks
        return Executors.newFixedThreadPool(3);
    }
}

2️⃣ 검증 서비스 (병렬 작업)

package com.example.service;

import org.springframework.stereotype.Service;

import java.util.concurrent.Callable;

@Service
public class ValidationTasks {

    public Callable accountCheck() {
        return () -> {
            Thread.sleep(500); // simulate service call
            return true; // account is active
        };
    }

    public Callable balanceCheck() {
        return () -> {
            Thread.sleep(700); // simulate service call
            return true; // sufficient balance
        };
    }

    public Callable fraudCheck() {
        return () -> {
            Thread.sleep(800); // simulate service call
            return true; // low risk
        };
    }
}

3️⃣ 결제 오케스트레이터 서비스

This is where ExecutorService is actually used.

package com.example.service;

import org.springframework.stereotype.Service;

import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;

@Service
public class PaymentValidationService {

    private final ExecutorService executorService;
    private final ValidationTasks tasks;

    public PaymentValidationService(
            ExecutorService executorService,
            ValidationTasks tasks) {
        this.executorService = executorService;
        this.tasks = tasks;
    }

    public boolean validatePayment() throws Exception {

        List> results = executorService.invokeAll(
                List.of(
                        tasks.accountCheck(),
                        tasks.balanceCheck(),
                        tasks.fraudCheck()
                )
        );

        // If any validation fails → reject payment
        for (Future result : results) {
            if (!result.get()) {
                return false;
            }
        }
        return true;
    }
}

🔍 invokeAll()을 사용하는 이유

  • 여러 작업을 한 번에 제출합니다
  • 모든 작업이 완료될 때까지 대기합니다
  • 깔끔하고 가독성 높은 오케스트레이션 로직을 제공합니다

4️⃣ REST 컨트롤러

package com.example.controller;

import com.example.service.PaymentValidationService;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class PaymentController {

    private final PaymentValidationService service;

    public PaymentController(PaymentValidationService service) {
        this.service = service;
    }

    @PostMapping("/validate-payment")
    public String validatePayment() throws Exception {

        boolean valid = service.validatePayment();

        return valid
                ? "Payment validation successful"
                : "Payment validation failed";
    }
}

5️⃣ curl 요청

curl -X POST http://localhost:8080/validate-payment

6️⃣ 응답

Payment validation successful

⏱ Performance Comparison

ApproachApprox. Time
Sequential~2.0 sec
ExecutorService (parallel)~0.8 sec

60 %+ latency reduction

  • Real business problem
  • Parallelism (not just async hype)
  • Controlled concurrency
  • Best usage of ExecutorService
  • SLA‑driven design

⚠ 일반적인 실수 (면접에서 이렇게 말하세요)

  • 요청당 새로운 ExecutorService 생성
  • 무제한 스레드 풀 사용
  • 모든 것을 무차별적으로 블로킹
  • 타임아웃 무시
  • 정상적인 종료를 수행하지 않음
Back to Blog

관련 글

더 보기 »