spring

싱글톤 Context에서 전략 패턴 사용 시 동시성 문제

devJK93 2025. 3. 27.

전략 패턴을 사용할 때 Context를 싱글톤으로 설계하면 동시성 문제가 발생할 수 있다. 특히 Context 내부에 Strategy를 필드로 가지고 있고, setStrategy()로 전략을 변경하는 구조일 경우 문제의 소지가 크다.

💥 문제 예시


@Slf4j
public class Context {
    private Strategy strategy;

    public void setStrategy(Strategy strategy) {
        this.strategy = strategy;
    }

    public void execute() {
        log.info("Context.execute() 호출");
        strategy.call(); // 문제 발생 가능 지점
    }
}

Context를 싱글톤으로 생성했다고 가정하면 아래와 같은 동시성 문제가 발생할 수 있다:


public void 동시성문제테스트() {
    Context context = new Context(); // 싱글톤처럼 공유

    Runnable userA = () -> {
        context.setStrategy(() -> log.info("유저A 전략 실행"));
        context.execute();
    };

    Runnable userB = () -> {
        context.setStrategy(() -> log.info("유저B 전략 실행"));
        context.execute();
    };

    Thread threadA = new Thread(userA);
    Thread threadB = new Thread(userB);

    threadA.start();
    threadB.start();
}

🧨 예상 출력


유저A 전략 실행
유저B 전략 실행

이론적으로는 위처럼 나와야 하지만, 실행 타이밍에 따라 다음처럼 유저B 전략이 두 번 실행되는 상황이 발생할 수 있다:


유저B 전략 실행
유저B 전략 실행

이유는 A가 실행하기 전에 B가 전략을 바꿔버리면, A도 B의 전략을 사용하게 되기 때문이다.


✅ 해결 방법 1: 상태 없는(stateless) 방식

전략을 Context 내부 상태로 두지 않고, execute() 호출 시에 매번 넘겨주는 방식으로 바꾸면 안전하다. 즉, 템플릿 콜백 패턴처럼 작성하면 된다.


@Slf4j
public class Context {
    public void execute(Strategy strategy) {
        log.info("Context.execute() 호출");
        strategy.call();
    }
}

이 구조는 공유된 싱글톤 Context를 여러 스레드가 동시에 사용해도 문제가 없다.

구조 동시성 안전 여부 설명
전략을 필드에 저장 (setStrategy()) ❌ 위험 전략이 바뀌는 동안 다른 스레드가 엉뚱한 전략 실행 가능
전략을 파라미터로 전달 (execute(strategy)) ✅ 안전 전략을 상태로 저장하지 않기 때문에 스레드 간 영향 없음

✅ 해결 방법 2: 불변(Immutable)한 의존성 주입

Context가 전략을 생성자 주입으로 받고 이후 절대 변경하지 않도록 설계하면, 동시성 문제를 원천 차단할 수 있다.


@Slf4j
public class Context {
    private final Strategy strategy;

    public Context(Strategy strategy) {
        this.strategy = strategy; // 한 번만 주입
    }

    public void execute() {
        log.info("Context.execute() 호출");
        strategy.call();
    }
}
  • strategyfinal이며,
  • setter가 존재하지 않고,
  • 한 번 박힌 전략은 바뀌지 않는다.

따라서 싱글톤으로 설계하더라도 동시성 문제가 전혀 발생하지 않는다. 이 방식은 Spring 프레임워크에서 생성자 주입이 권장되는 이유이기도 하다.


🧠 정리

한 번 의존성이 박히면 다신 다른 의존성을 받을 수 없게 설계하면 동시성 문제가 해결된다.

바로 이것이 불변 설계(Immutable Design)의 핵심이며, 전략 패턴을 안전하게 적용하는 가장 효과적인 방법이다.

댓글