Spring Framework

모델의 일생 | MVC 아키텍처 중 Model의 생명주기

석이 2021. 12. 11. 15:19

일러두기

본 글은 자바 백앤드 국비과정 교육을 받고 있는 학생이 김영한님의 '스프링 MVC 1편'과 이일민님의 토비의 스프링을 읽고 공부한 내용을 정리하기 위해 쓴 글입니다.

학생이 쓴 글이기에 내용에 오류가 있을 수 있습니다!

본문

 

스프링 MVC를 구성하는 세 가지 컴포넌트(모델, 뷰, 컨트롤러) 중에서 컨트롤러와 뷰는 주로 DispatcherServlet의 기본 전략을 바꾸거나 재설정함으로써 결정할 수 있다. 반면에 모델은 컨트롤러와 뷰보다 훨씬 다이내믹한 생명주기를 갖고 있다. 따라서 모델이 준비되고 사용되는 흐름과 그 과정에 참여하는 여러 구성요소를 잘 파악하고 이를 관리할 수 있어야 한다. 모델과 관련된 이런 과정이 처음에는 복잡하게 보일지 모르겠지만 조금만 익숙해지면 이보다 편한 모델 관리는 없다고 느끼게 될 것이다. 초보적인 MVC 프로그래밍 모델이라면 컨트롤러 메소드 안에서 지저분하게 반복되는 코드로 처리해야 했던 부분을, 책임과 역할에 따라 깔끔하게 분리해서 뛰어난 확장과 재사용이 가능하도록 만든 것이다.

<토비의 스프링 3.1 vol2 284p>

 

모델의 생명주기에 대한 공부의 필요성

 

  1. MVC 패턴 중 컨트롤러나 뷰는 자바 클래스 혹은 JSP 스크립트 등으로 만들어졌기 때문에 동작 원리와 결과를 쉽게 예측하고 파악할 수 있다.
  2. 컨트롤러와 뷰와는 달리 모델은 보이지 않는 곳에서 만들어지기도 하고, 모델 오브젝트에 대한 다양한 조작이 자동적으로 이루어지기 때문에 그 동작 원리 혹은 결과를 예측하고 파악하는 것이 어렵다.
    • 모델은 코드 한 줄 작성하는 것 없이 단순히 애노테이션을 추가하는 것으로도 다양한 조작이 가능하다.
    • ex) 세션, 검증 등
  3. 모델은 MVC 아키텍처에서 정보를 담당하는 컴포넌트이기 때문에, 스프링 MVC 프레임워크를 잘 활용하기 위해서는 위와 같은 특징을 지닌 모델에 대한 이해와 지식이 중요하다.
  4. 모델의 생명주기를 공부하는 과정을 통해 모델의 생성에서부터 소멸까지의 전 과정을 이해하는 공부는 모델에 대한 이해를 돕는데 많은 역할을 할 수 있다.

 

모델의 일생 전반부 : HTTP 요청으로부터 컨트롤러 메소드까지

 

출처 / 토비의 스프링 3.1 vol2


위의 과정을 순서대로 살펴보자.

HTTP 파라미터

HTTP 파라미터가 브라우저 등의 웹 클라이언트에서 서버로 넘어오는 단계이다. 모든 웹통신은 HTTP 통신에 의해 이루어지고 HTTP 메시지에 담기는 모든 데이터는 텍스트이므로 이때 서버로 넘어오는 HTTP 파라미터 역시 텍스트, 즉 스트링의 형태로 넘어온다. 때문에 HTTP 파라미터의 타입을 서버에서 변환해주는 작업이 필요한데, 스프링에서는 이를 PropertyEditer와 ConversionService 등의 객체를 통해 해결한다. 이러한 작업은 모델의 일생 중 '프로퍼티 바인딩' 단계에서 수행된다.

모델 오브젝트 준비

스프링은 Controller 객체 내 메소드에 기입된 @ModelAttribute 파라미터 값을 조회하여 새로운 객체를 만든다. 예를 들어 @ModelAttribute User user라는 파라미터가 있다면, 스프링은 이를 조회하여 User 타입의 객체를 만드는 것이다. 이를 위해서는 해당 참조 객체에 디폴트 생성자가 반드시 필요하다. 이 때 만약 @SessionAttributes에 의해 세션에 저장되어 있는 모델 객체가 있다면 새로운 객체를 생성하지 않고 세션에 저장되어 있는 객체를 가져온다.

프로퍼티 바인딩

앞서 설명한 바와 같이 HTTP 파라미터는 텍스트의 형태로 넘어온다. 만약 '모델 오브젝트 준비' 단계에서 생성되거나 불러져 온 객체의 프로퍼티가 스트링 타입이 아니라면 HTTP 파라미터를 스프링이 이용할 수 있는 데이터 타입으로 변환해야 하는 작업이 필요하다. 스프링은 이를 PropertyEditer와 ConversionService 등의 객체를 통해 해결하며 전환이 불가한 경우에는 BindingResult 객체 안에 바인딩 오류를 전달하여 컨트롤러에 넘기거나 예외를 발생시킨다.

참고로 바인딩(Binding)이란 데이터의 변수 이름, 데이터 타입(자료형), 데이터의 값에 실제적이고 구체적인 값을 할당하는 작업을 의미한다.

모델 검증

데이터 타입에 대한 검증은 '프로퍼티 바인딩' 단계에서 끝났지만 비지니스 로직상 필요하거나 그 외에 검증할 내용이 있다면 적절한 검증기를 등록해서 모델의 내용을 검증 할 수 있다. 검증은 모델 파라미터에 지정할 수 있는 @Valid 애노테이션을 통해 자동으로 진행할 수도 있고, 원한다면 개별적인 로직을 만들어 직접 검증을 진행할 수도 있다. 검증 결과에서 검출된 예외는 BindingResult 객체에 등록되며 만약 로직상에 검증기능이 없다면 이 과정은 생략된다.

(참고 : 김영한님의 스프링 MVC 2편 검증 파트에 나온 수업 내용에 의하면 BindingResult 객체는 반드시 @ModelAttribute 애너테이션 다음에 와야 한다고 한다! 예를 들어 @ModelAttribute User user, BindingResult bindingResult는 가능하지만 BindingResult bindingResult, @ModelAttribute User user은 불가능 하다는 것!)

이후

'모델 검증' 단계까지 마친 모델 객체는 컨트롤러 메소드 내에서의 로직을 거친 후 추가로 등록된 모델 객체와 함께 ModelAndView 맵에 담겨 프론트 컨트롤러인 DispatcherServlet으로 전달된다. 만약 컨트롤러 메소드에 BindingResult 타입의 파라미터가 존재한다면 바인딩과 검증 작업의 결과가 담긴 BindingResult 객체 또한 ModelAndView 맵에 추가된다.

모델의 일생 후반부 : DispatcherServlet으로부터 뷰까지

 

출처 / 토비의 스프링 3.1 vol2


위의 과정을 에러메시지, 필드값, 뷰 이름 관점에서 각각 살펴보자.

Errors

ModelAndView를 타고 DispatcherServlet으로 넘어온 BindingResult 객체는 WebDataBinder에 등록된 MessageCodeResolver에 의해 특정 에러 코드로 변환된다. 이후 LocaleResolver와 MessageSource를 통해 뷰에 출력될 최종 에러 메시지를 할당받는다.

Model

DispatcherServlet 내에서의 ModelAndView 객체는 각각 Model 맵과 뷰의 이름을 가리키는 스트링 값으로 나뉜다. 이중 Model 맵은 뷰의 필드값으로 뿌려지게 되는데, 이때 만약 Model 맵에 @SessionAttribute로 지정한 이름과 일치하는 것이 있다면 HTTP 세션에 저장된다. 세션에 저장된 Model은 다음 요청에서 활용될 수 있다.

view

DispatcherServlet로 전송된 view는 만약 그것이 물리적인 뷰의 주소값을 저장하고 있다면 그대로 출력될 수 있지만, 만약 논리적인 주소값을 저장하고 있다면 ViewResolver 객체로 가 물리적인 주소값을 반환받을 수 있다. 이때 받환받은 view는 Errors와 Model의 값과 함께 View 객체에 의해 렌더링 되어 클라이언트에게 보여지게 된다. 그리고 이로써 모델의 생명주기도 끝이 난다.