오늘은 스프링에서 자주 사용하는 Model 객체와 ModelMap 객체에 대해서 포스팅 해보겠다.

Model만 있는 줄 알았는데 ModelMap 이라는 객체도 있었다.


1. 스프링에서 Model 객체란?

스프링에서는 Controller 의 메소드들이 받는 파라미터(Argument)들을 관리하는 ArgumentResolver 가 존재한다. (정확하게는 HandlerMethodArgumentResolver 클래스에서 Argument들을 관리하는 역할을 한다.) 여기에 등록되어 있는, 자주 사용되는 파라미터 중 하나라고 할 수 있다. 주로 데이터를 관리하기 위한 파라미터이다. (MVC 패턴에서 M도 Model의 의미를 지니는 데, 그와 비슷하게 Model 객체도 Data를 전달하고 관리하기 위한 Argument 이다.)

 

Model과 ModelMap 두 객체는 기본적으로 상당히 비슷한 기능을 제공한다.

addAttribute(...) 메소드를 사용해서 다루고 싶은 데이터를 넣고,

getAttribute(...) 메소드를 이용해서 넣었던 데이터들을 빼서 사용할 수 있다.

 

 

2. Model  객체는 interface 이다.

public interface Model {
    
    Model addAttribute(String attributeName, @Nullable Object attributeValue);

	...
    
    @Nullable
    Object getAttribute(String attributeName);
}

Model 객체는 인터페이스로 구현되어 있기 때문에 map 인터페이스 구현체로의 변경이 쉬워서 코드의 유연성이 높다는 특징이 있다. 예를들어 TreeMap 이나 HashMap 으로도 호출할 수 있다.
(참고로, 페이지 redirect 시에 RUL parameter 정보들을 쉽게 관리하게 해주는 RedirectAttributes 인터페이스도 Model 인터페이스를 상속받는다.)

3. ModelMap 객체는 Class 이다. (LinkedHashMap)

public class ModelMap extends LinkedHashMap<String, Object> {

    public ModelMap addAttribute(String attributeName, @Nullable Object attributeValue) {
        Assert.notNull(attributeName, "Model attribute name must not be null");
        this.put(attributeName, attributeValue);
        return this;
    }

	...

    @Nullable
    public Object getAttribute(String attributeName) {
        return this.get(attributeName);
    }
}


구체적인 클래스로 구현되어 있기 때문에, LinkedHashMap 을 상속받은 클래스로 사용이 가능하다.

 

LinkedHashMap 클래스의 경우 HashMap 클래스를 상속받고 있는데, 이 둘(LinkedHashMap 과 HashMap)의 차이점은 순서의 유무이다. HashMap의 경우 순서 고려 없이 Entry에 Node 데이터를 집어 넣지만, LinkedHashMap의 경우 before, After Entry를 지정하여 put 한 순서데로 데이터를 저장한다는 차이점이 있다.

 

4. HandlerMethodArgumentResolver에 Model객체는 어떻게 등록되어 있을까?

public class ModelMethodProcessor implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler {
    public ModelMethodProcessor() {
    }

	// parameter 타입 특정하기 (간소화함)
    public boolean supportsParameter(MethodParameter parameter) {
        return Model.class;
    }

	// parameter 로써 역할 작성하기
    @Nullable
    public Object resolveArgument(...) throws Exception {
        ...
    }

	// return 타입 특정하기 (간소화함)
    public boolean supportsReturnType(MethodParameter returnType) {
        return Model.class;
    }

	// return 값으로써 역할 작성하기
    public void handleReturnValue(...) throws Exception {
        ...
    }
}

 

Model.class 의 경우 ModelMethodProcessor 클래스로 구현되어 있다. (참고로 함께 상속되어 있는 HandlerMethodReturnValueHandler 는 파라미터가 아니라 반환할 때의 데이터를 관리하기 위한 핸들러이다.)

 

기본적으로 스프링은, 컨트롤러의 파라미터들을 가져올 때 HandlerMethodArgumentResolver 를 상속받은 클래스들을 탐색한다. 마찬가지로 컨트롤러의 반환값들을 가져올 때는 HandlerMethodReturnValueHandler 인터페이스를 상속받은 클래스들을 탐색한다.

 

여기에 등록이 되어있어야 스프링이 파라미터나 반환값으로 인식할 수 있는 것이다.

+ Recent posts