Spring

[Spring] Singleton 사용 시 필드 변수 사용 주의❗

lakelight 2022. 7. 11. 20:25
728x90
반응형
개요

싱글톤 방식은 객체 인스턴스를 하나만 생성하여 그 인스턴스를 공유하기 때문에
클라이언트 들이 하나의 인스턴스에 접근을 합니다.
그런데 이때 인스턴스에 있는 필드 변수를 사용하게 되면 문제가 발생할 수 있습니다.

해결방법

싱글톤 방식은 무상태(stateless)로 설계 해야합니다.
1.  클라이언트에게 의존적인 필드가 있으면 안됩니다.
2. 클라이언트가 변경할 수 있는 필드가 있으면 안됩니다.
3. 가급적 필드에 대해 읽기만 가능해야 합니다.
4. 필드 대신 지역변수, 파라미터, ThreadLocal을 사용해야 합니다.

 

문제점에 대한 예시
  • 필드를 사용하고 클라이언트가 직접 필드의 값을 변경하는 경우
//게임에서 점수를 측정하는 서비스
public class ScoreCheckService {
    private int score; //필드를 선언!
    
    public void game(String name, int score){
        this.score = score; //필드의 값을 변경하는 경우!
    }
    
    public int getScore(){
        return score; //필드에 score를 반환
    }
}
public class ScoreCheckServiceTest {
    public static void main(String[] args) {
        //설정 파일에서 빈 정보를 가져옵니다.
        ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
        
        //두 클라이언트가 빈을 호출해서 ScoreCheckService에 접근을 할 때
        ScoreCheckService service1 = ac.getBean("ScoreCheckService", ScoreCheckService.class);
        ScoreCheckService service2 = ac.getBean("ScoreCheckService", ScoreCheckService.class);
        
        //첫번째 사용자가 98점을 받고 두번째 사용자가 70점을 받았다고 가정하면
        service1.game("user1", 98);
        service2.game("user2", 70);

        //첫번째 사용자의 점수를 받아왔을 때
        int user1_score = service1.getScore();

	//출력 값은?
    }
}
user1_score의 값은 70으로 출력됩니다. 왜냐하면 ScoreCheckService는 싱글톤이기 때문에
인스턴스가 하나로 공유되고 있는 상황입니다. 그런데 service2.game()에서 ScoreCheckService에 score에 접근을 하고
그 값을 변경했기 때문에 user1이 98점을 받았음에도 user2의 게임에서 70점이 공유하는 ScoreCheckService의
score 필드값에 70점이 들어가서 service1.getScore(); 점수를 가져왔을 때 70으로 출력되는 것입니다.

 

문제점에 대한 해결방법
  • 지역변수를 사용하여 반환합니다.
//게임에서 점수를 측정하는 서비스
public class ScoreCheckService {
    //private int score; //필드 지우기!
    
    public void game(String name, int score){
        //this.score = score; //필드의 값에 접근하지 않기!
        return score; //점수를 바로 반환
    }
    
    //public int getScore(){
    //    return score; //필드에 score를 반환하는 메서드 지우기!
    //}
}
public class ScoreCheckServiceTest {
    public static void main(String[] args) {
        //설정 파일에서 빈 정보를 가져옵니다.
        ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);

        //두 클라이언트가 빈을 호출해서 ScoreCheckService에 접근을 할 때
        ScoreCheckService service1 = ac.getBean("ScoreCheckService", ScoreCheckService.class);
        ScoreCheckService service2 = ac.getBean("ScoreCheckService", ScoreCheckService.class);

        //사용자의 점수를 바로 지역변수로 받아서 사용합니다.
        int user1_score = service1.game("user1", 98);
        int user2_score = service2.game("user2", 70);

        //첫번째 사용자의 점수를 출력했을 때 98점이 나오게 됩니다.
        System.out.println(user1_score);
    }
}
728x90
반응형