컨트롤러는 상태를 가지면 안된다.
Programming/Java

컨트롤러는 상태를 가지면 안된다.

이번 글에서는 컨트롤러가 상태를 가지면 안되는 이유에 대해서 이야기해보고자 합니다.

 

서론

로또 미션을 진행하면서 다음과 같이 컨트롤러 내 인스턴스 변수로 발행한 로또와 당첨번호를 저장했었습니다. 피드백을 통해 이러한 방식에 문제가 있다는 사실을 알게 되었습니다. 

public class LottoController {

    private Lottos lottos;
    private LottoWinningNumbers lottoWinningNumbers;
    
    // ...
 }

 

컨트롤러와 모델의 차이점

위 방식에 문제가 있다는 사실을 알고 나서 문득 한가지 의문이 들었습니다. '컨트롤러가 왜 상태를 가지면 안될까? 그럼 모델이 상태를 가져져도 되는 이유는 뭐지?'

 

잠시 MVC 구조를 짚고 넘어가겠습니다. MVC에서 Model, View, Controller는 각각 다음과 같은 역할을 한다고 정의내릴 수 있습니다.

  • Model : 데이터와 관련된 부분
  • View : 사용자한테 보여지는 부분
  • Controller : Model과 View를 이어주는 부분

맡은 역할 뿐만 아니라 모델과 컨트롤러가 가지는 중요한 차이점이 있습니다. 바로 컨트롤러는 1개, 모델은 여러개가 생성될 수 있다는 것입니다. 컨트롤러 객체는 1개가 생성되어 요청이 들어올 때마다 쓰레드가 생성됩니다. 현재는 싱글 스레드로 동작하지만, 나중에 멀티쓰레드 방식으로 수정하거나 웹서비스로 발전할 경우 문제가 발생할 수 있습니다. 인스턴스를 여러 스레드에서 동시에 사용하면서 원하지 않는 값으로 변경될 위험이 있기 때문입니다.

 

그럼 컨트롤러에서 접근하는 모델 객체는 멀티쓰레드 방식이어도 괜찮을 걸까요? 네 그렇습니다.

 

자바의 메모리구조

해답은 자바의 메모리구조에 있습니다. 클래스변수와 인스턴스변수는 스레드끼리 공유하고, 메소드 내 지역변수는 공유되지 않습니다. 좀 더 자세히 이야기하면 JVM 메모리영역은 Method Area, Heap Area, Stack Area, PC Register, Native Method Stack으로 나눌 수 있습니다. 이 중 메소드 영역과 힙 영역은 모든 쓰레드가 공유합니다.

 

JVM 메모리 구조

 

클래스 변수는 메소드 영역에, 인스턴스 변수는 힙 영역에 저장됩니다. 그러나 메소드 내 지역 변수는 스택에 저장되기에 쓰레드끼리 공유되지 않습니다. 우리는 컨트롤러 내 메소드로 모델 객체를 생성하고 접근하기 때문에, 멀티쓰레드 환경에서도 모델에 저장한 데이터가 공유되지 않는다는 것을 보장할 수 있습니다.

 

synchronized 를 이용해 thread-safe 하도록 만들면 어떨까?

컨트롤러 내 인스턴스 변수를 synchronized 처리하면 되지 않을까? 라는 생각도 했었습니다. 그럼 적어도 여러 스레드가 인스턴스 변수에 동시에 접근하는 일은 없을테니까요. 그러나 하나의 스레드가 변수에 접근할 때, 다른 스레드는 기다려야 하기에 성능저하가 심하게 발생합니다.

 

또한 동시성 버그를 고치는 일은 정말 어렵기 때문에 synchronized는 사용하지 않는 것이 좋습니다. 이에 관해 <모던 자바 인 액션> 18장에는 이런 말이 적혀있습니다. '누군가 대규모 프로그램 유지보수를 제안했다고 가정해보자. 이 때 프로그램 내에 synchronized 키워드가 발견된다면 제안을 거절하라.'

 

요약

컨트롤러는 상태를 가지면 안됩니다. 여러 스레드가 상태를 공유하게 된다면 예상치 못한 부작용이 발생할 수 있기 때문입니다.

 

참고 자료

  • https://jeong-pro.tistory.com/204
  • https://kdhyo98.tistory.com/m/67
  • https://steady-coding.tistory.com/305

'Programming > Java' 카테고리의 다른 글

package-info.java란  (0) 2023.07.02
JPA Entity의 equals와 hashCode  (0) 2022.08.08
Optional  (0) 2022.07.01
자바 추상클래스와 인터페이스  (0) 2021.10.02