티스토리 뷰

728x90
반응형

ㆍ싱글톤 패턴이든, 스프링 같은 싱글톤 컨테이너를 사용하든, 객체 인스턴스를 하나만 생성해서 공유하는 싱글톤 방식은 여러 클라이언트가 하나의 같은 객체 인스턴스를 공유하기 때문에 싱글톤 객체는 상태를 유지하게 설계하면 안됩니다.

 

무상태로 설계해야 한다.!

  특정 클라이언트에 의존적인 필드가 있으면 안됩니다.

  특정 클라이언트가 값을 변경할 수 있는 필드가 있으면 안됩니다.

  ㆍ가급적 조회만 할 수 있어야 합니다.

  ㆍ필드 대신 자바에서 공유되지 않는 지역변수, 파라미터, ThreadLocal 등을 사용해야 합니다.

 

문제가 발생하는 코드 예시

ㆍStatefulService Class는 싱글톤으로 선언된 클래스이며, getPrice 메서드를 사용하여 user들의 price를 반환

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.example.singletom;
 
public class StatefulService {
    
    // 싱글톤 선언 시작
    private static final StatefulService instance = new StatefulService();
    
    public static StatefulService getInstance() {
        return instance;
    }
    
    private StatefulService() {}
    // 싱글톤 선언 끝
    
    private int price; //상태를 유지하는 필드
    
    public void order(String name, int price) {
        this.price = price; // 문제 발생
    }
    
    public int getPrice() {
        return this.price;
    }
}
 
cs

 

ㆍuserA와 userB는 각각 1만원과 2만원을 주문하였지만 StatefulService Class에서 price 필드는 마지막 userB의 price로 값을 유지하기 때문에 아래와 같은 문제점이 발생합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class StatefulServiceTest {
    
    @Test
    void statefulServiceSingleton() {
        
        StatefulService statefulService1 = StatefulService.getInstance();
        StatefulService statefulService2 = StatefulService.getInstance();
 
        // A 사용자가 1만원 주문
        statefulService1.order("userA"10000);
        
        // B 사용자가 2만원 주문
        statefulService2.order("userB"20000);
        
        // 사용자A가 주문 금액 조회
        // 사용자 B가 중간에 끼어들어 price는 2만원이 나옴
        int price = statefulService1.getPrice();
        System.out.println(price); // 20000만원 출력
    }
}
 
cs

 

문제가 해결된 코드 예시 (스프링을 쓰지않는 코드)

ㆍ기존 StatefulService Class에 선언되어 있던 공유 필드인 price 필드를 삭제합니다.

ㆍ또한 getPrice 메서드 대신 order 메서드를 반환값으로 price를 반환합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.example.singletom;
 
public class StatefulService {
    
    // 싱글톤 선언 시작
    private static final StatefulService instance = new StatefulService();
    
    public static StatefulService getInstance() {
        return instance;
    }
    
    private StatefulService() {}
    // 싱글톤 선언 끝
    
    //private int price; //상태를 유지하는 필드 삭제
    
    public int order(String name, int price) {
        System.out.println("name : " + name + " price : "+  price);
        return price;
    }
}
 
cs

 

ㆍ order 매서드는 자신의 price를 반환하므로 userA와 userB는 각각 자신의 금액인 1만원과 2만원이 정상적으로 출력되는 것을 확인할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class StatefulServiceTest {
    
    @Test
    void statefulServiceSingleton() {
        
        StatefulService statefulService1 = StatefulService.getInstance();
        StatefulService statefulService2 = StatefulService.getInstance();
 
        // A 사용자가 1만원 주문
        int userA = statefulService1.order("userA"10000);
        
        // B 사용자가 2만원 주문
        int userB = statefulService2.order("userB"20000);
        
        // 사용자가 주문 금액 조회
        System.out.println(userA); // 10000만원 출력
        System.out.println(userB); // 20000만원 출력
    }
}
 
cs

 

 

문제가 해결된 코드 예시 (스프링 코드)

ㆍ스프링은 싱글톤을 알아서 해주므로 코드를 길게 적을 필요가 없어집니다.ㅎㅎ

1
2
3
4
5
6
7
8
public class StatefulService {
    
    public int order(String name, int price) {
        System.out.println("name : " + name + " price : "+  price);
        return price;
    }
}
 
cs

 

정상적으로 출력되는것을 볼 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class StatefulServiceTest {
    
    @Test
    void statefulServiceSingleton() {
        ApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);
        
        StatefulService statefulService1 = ac.getBean(StatefulService.class);
        StatefulService statefulService2 = ac.getBean(StatefulService.class);
            
        // A 사용자가 1만원 주문
        int userA = statefulService1.order("userA"10000);
        
        // B 사용자가 2만원 주문
        int userB = statefulService2.order("userB"20000);
        
        // 사용자 주문 금액 조회
        System.out.println(userA); // 10000만원 출력
        System.out.println(userB); // 20000만원 출력
    }
    
    static class TestConfig{
        
        @Bean
        public StatefulService statefulService() {
            return new StatefulService();
        }
    }
}
 
cs
728x90
반응형