TIL

2021.08.16 기록장

Gisungcu 2021. 8. 15. 22:59

ToDo

  • 알고리즘 문제
  • 토비의 스프링

Done

  • 토비의 스프링

Weekly goal

  • 책 읽기
  • 토비 스프링 코딩
  • 7월 회고 쓰기

토비의 스프링

 

466p.

@RequestMapping이 나오기 전에는 핸들러 매핑과 핸들러 어댑터의 대상이 오브젝트였다. 하지만 이제는 DefaultAnnotationHandlerMapping 등으로 검색 대상이 오브젝트를 넘어 메서드가 됐다. 어노테이션은 메서드 레벨까지 내려갈 수 있기 때문이다. 

 

RequestMapping은 value를 가지고 있다. 이것은 default suffix pattern으로 인해 url 뒤쪽에 /와 /.*이 생략되어있다.

 

우리가 어노테이션을 메서드 레벨까지 사용한다 해도 기본적으로 spring여태까지 컨트롤러를 정해왔던 방법, 핸들러 매핑은 DefaultAnnotationHandlerMapping이라고 해서 달라지진 않는다. 

DefaultAnnotationHandlerMapping은 오브젝트까지만 매핑을 하고 실행은 핸들러 어뎁터가 한다.

 

어댑터는 매핑된 실제 메서드를 호출하는데 파라미터나 리턴 값을 모르는데 어떡하나?

어댑터에서 값들을 준비한다. 

먼저 객체가 있을 경우는 객체를 new로 만들고 값들을 프로퍼티 에디터를 통해 집어넣고 

valid검사를 한다.

 

494p.

예로 @ModelAttribut가 붙은 객체를 만들어 주는 역할은 어댑터에서 실행된다.

string 문자로 들어온 것들을 프로퍼티 or컨버터를 통해 변환하고 집어넣는다. 

근데 이때 프로퍼티에 맞지 않는 타입이면 BindingException이 BindingResult에 담긴다.

spring은 값 타입이 다른 것을 이미 예상하고 있었기 때문이다.

 

만약 @ModelAttribut와 BindingResult or Error를 같이 쓰지 않는다면 spring은 애플리케이션이 값을 보장해준다고 믿는다. 근데 맞지 않으니까 BindingException이 바로 던져진다.

 

499p.

@RequestBody가 붙어있는 파라미터는 어댑터에서 컨버터 중에 http의 미디어 타입과 파라미터 타입 처리 가능한 것을 찾아서 본문을 전부 전달해준다. json or xml 등도 가능하다.

 

506p. 

만약 controller의 return값이 void이면 리졸버는 RequestToViewNameResolver가 동작한다. url을 통해 뷰를 생성한다.

 

만약 @ResponseBody가 붙어있다면 뷰 이름으로 동작하는 것이 아니라 아무것도 안 준다. http응답 만주고.

근데 만약 리턴 값이 존재하고 ResponseBody가 붙어있다면 메시지 컨버터가 뷰 리졸버와 같이 동작한다.

 

511p.

하나의 상황을 생각해보자. 누군가 로그인을 하고 자신의 정보를 수정할 때 그 정보는 처음에는 DB에서 긁어온 다음 form에 뿌려줄 것이다. 그리고 수정을 하고 갔으면 완벽한데, 만약 오류가 있어서 다시 form을 보여줘야 한다면?

form에 뿌릴 것을 다시 DB에서 긁어와야 하는가? 이는 성능에는 미비하지만 부담을 준다.

해법으로는 세션을 이용하는 것이다. 

spring에서는 @SessionAttributes를 지원한다. class레벨에 달고 원하는 모델의 이름을 넣으면 해당 컨트롤러의 모델 중에 이름이 같은 놈을 넣는다. 

 

위의 수정 상황으로 생각해보면 처음에 DB에 긁어올 때 user는 SessionAttributes에 의해 세션에 저장된다. 

후에 수정이 오류가 나서 다시 form에 보내줄 때는 세션에 저장된 user를 다시 가지고 오면 되는 것이다.

또 

근데 세션에서 사용이 다된 객체는 해제를 해줘야 한다. 

그래서 SessionStatus가 존재한다. 파라미터로 사용되는데 setComplete() 메서드를 실행시켜줘야 한다.

수정form은 SessionAttributes를 사용하자

모두 같은 세션에서 같은 user를 이용하게 될 것이다.

 

세션어트리부트의 한계

https://stackoverflow.com/questions/9491504/spring-mvc-sessionattribute-and-scalability

 

Spring MVC, @SessionAttribute and scalability

We are building a Spring-MVC web application for 80 000 users. I see a lot of controllers in the petclinic example using : @SessionAttribute annotation and SessionStatus status ... status.

stackoverflow.com

 

521p.

위는 수정 폼에 관한 이야기였고 등록 폼은 처음에는 DB에서 가져올  user가 없다. 그럼 그냥 화면을 보여주는 것보다는 위의 form메서드처럼 빈 user오브젝트를 주는 것이 좋다. 이러면 처음 form과 오류 form을 구분하지 않아도 된다. 꿀팁이다.

 

 

525p.

중간에 세션으로 세긴 했는데 다시 모델을 바인딩하는 과정에 대해 알아보자.

핸들러 매핑으로 컨트롤러를 가지고 오고 어댑터가 메서드를 찾아서 실행시키는데 

그중에 파라미터가 객체일 때의 어댑터가 행하는 과정이다. 나머지 리퀘스트 파람은 바인딩을 통해 변환한다.

 

@ModelAttribute는 

1. 오브젝트를 만들고 sessionAttribute에 있으면 그거 가지고 오고

2. 모델 오브젝트에 가지고 온 파라미터들을 바인딩시켜준다. 가지고 올 때는 문자열 타입으로 가지고 오니까 적절한 바인딩이 필요하다. 이때 사용되는 것이 프로퍼티 에디터나 컨버터이다.

3. 모델의 값을 검증한다. 값이 없다거나 숫자의 경우 비즈니스 범위를 넘어서는지 등

 

536p.

ModelAttribute나 RequestParam, PathVariable은 바인딩을 해줘야 한다.

바인딩은 어댑터가 실행될 때 webDataBinder를 만들고 여기서 프로퍼티 에디터를 사용한다.

커스텀 프로퍼티 에디터는 해당 컨트롤러에 @initBinder를 통해 사용하거나(해당 class에만 적용)

WebBindingInitialze를 직접 등록해서 전역으로 사용할 수 있다.

 

539p.

위에서 봤듯이 프로퍼티 에디터는 항상 사용될 때마다 new를 통해 생성된다. 그래서 converter를 사용하는 것이 더 좋다.

converter는 프로퍼티 에디터(상태를 가지고 있다.)와 달리 메서드를 실행시키는 것이라 스레드 세잎 하고 빈으로 등록해서 쉽게 사용할 수 있다.

 

 

우리가 쉽게 하는 방법 중에 하나가 service에 id를 전달해서 나머지 기능은 service에 맡기는 것이다.

근데 애초에 controller에서 바인딩될 때 프로토타입 빈으로 Dao를 DI 받아서 가지고 오고 바로 객체로 파라미터에 전달된다면?

나는 뭐가 더 좋은지 모르겠다. 

 

549p.

converter인터페이스는 프로퍼티 에디터를 대신하기 위해 등장했다. 위에서 말한 것처럼 빈으로 등록해서 메서드를 호출하는 방법으로 사용할 수 있다.