[새배내] level2 방학 스프링 공부 & 복습

Junseo Kim·2021년 6월 18일
0

[우아한테크코스3기]

목록 보기
23/27
post-thumbnail

2021.06.07 ~ 2021.06.21


Map이나 List로 의존성 주입 받기

의존성 주입을 받을 때, Map이나 List로 의존성 주입을 받게되면, 해당 타입의 빈들이 모두 Map이나 List에 주입된다.(해당 타입의 들을 주입해주는 것이기 때문에 꼭 빈으로 등록 되어 있어야한다.)

예를 들어 서비스에 나이 별로 할인 정책을 정해주는 부분이 있다고 하자. BabyDiscountPolicy, ChildDiscountPolicy, TeenagerDiscountPolicy, DefaultDiscountPolicy 이렇게 4가지의 DiscountPolicy 인터페이스의 구현체가 존재하고, 로그인 한 사용자의 나이에 따라 알맞는 정책을 찾기 위해 모든 DiscountPolicy를 찾아서 나이에 맞는 구현체를 찾아야한다고 하자.

이런 경우, List로 모든 정책을 주입받으면, 따로 DiscountPolicy 목록을 만들 필요가 없다.

@Component
public class Discount {

    private final List<DiscountPolicy> policies;

    // policies 내부에 BabyDiscountPolicy, ChildDiscountPolicy, TeenagerDiscountPolicy, DefaultDiscountPolicy 인스턴스를 주입해준다.
    public Discount(List<DiscountPolicy> policies) {
        this.policies = policies;
    }
    
    // ...
}

Provider & proxy

실제 사용 시점까지 지연시켜주는 것.

예를 들어, 빈의 스코프가 request인 경우, http 요청이 오기전에 다른 곳에서 해당 빈을 주입받아야 하는 경우(생성자 주입), 아직 해당 타입의 빈이 존재하지 않기 때문에 오류가 발생한다.

이런 경우 provider나 proxy를 사용하면 실제 사용 시점까지 의존성 주입을 지연시켜 문제를 해결할 수 있다.

Post와 Put의 차이

Post: 서버가 알아서 처리

Put: 클라이언트가 리소스 url를 알고 있는 경우 사용

3xx 상태코드

리다이렉션을 나타낸다.

  • 영구 리다이렉션(301, 308): 특정 리소스의 url이 영구적으로 바뀐 경우 사용.(원래의 url을 사용하지 않는 경우) ex. 원래 회원 정보를 /members로 사용하다가 /users로 바꾼 경우. /members로 오는 요청을 /users로 리다이렉트 시킴

  • 일시적 리다이렉션(302, 303, 307): 일시적인 변경. 실무에서 주로 사용한다. ex. 주문 완료 후 주문 내역 화면으로 이동하는 경우. PRG 패턴에서 사용됨.

원래는 301, 302 상태코드를 사용해 리다이렉션을 할 경우, 요청 메서드, 본문을 그대로 유지해서 리다이렉트 시키도록 설계했으나, 각 브라우저가 그렇게 구현해놓지 않았다. 이 때문에 307, 308 상태코드가 나오게 되었다. 현재 대부분의 브라우저가 302 요청을 GET으로 변경시키지만 명확한 것은 아니다 명확히 GET으로 요청 메서드를 변경하고 싶을땐 303을 사용하는 것을 권장한다.

HTTP 헤더 콘텐츠 협상

요청시만 사용하며, 클라이언트의 우선 순위를 표현하는 것이다.

  • Accept: 클라이언트가 선호하는 미디어 타입을 전달해준다.
  • Accept-Charset: 클라이언트가 선호하는 문자 인코딩
  • Accept-Encoding: 클라이언트가 선호하는 압축 인코딩
  • Accept-Language: 클라이언트가 선호하는 자연 언어.

예를 들어, 요청 헤더의 Accept-Language가 아래와 같다면,

accept-language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7

우선 순위는 ko-KR > ko > en-Us > en > 나머지 인 것이다.(1이 가장 높은 우선순위를 가지며 생략가능하다. ko-KR;q=1.0으로 보면된다.)

WEB-INF

webapp - WEB-INF 디렉토리 하위 자원들은 외부에서 직접 접근이 불가능하다. 컨트롤러를 거쳐서 접근해야한다.

Forward vs Redirect

forward : 서버 내부에서 일어난다. 클라이언트가 인지하지 못한다. url경로는 변경되지 않는다.

redirect : 응답값을 클라이언트가 받은 후, 다시 리다이렉트 요청하여 리다이렉트 된 페이지를 응답받는다. 따라서 클라이언트가 인지 할 수 있고, url 경로 또한 변경된다.

서블릿

[Java] 서블릿이란? 서블릿 컨테이너란? Servlet 감잡기

스프링 웹 MVC의 DispatcherServlet

  • 스프링 웹 MVC의 핵심이다.
  • FrontController 패턴으로 구현되어있다.(요청을 받는 서블릿을 하나로 두는 것)
  • 스프링 부트는 DispatcherServlet을 자동으로 등록하면서 모든 요청에 대해 매핑한다.(정적 리소스 요청도 포함)

동작 순서

1) 핸들러 조회 : 요청 url에 매핑되는 핸들러(컨트롤러)를 조회한다.
2) 핸들러 어댑터 조회 : 핸들러를 실행할 수 있는 어댑터를 조회한다.
3) 핸들러 어댑터 실행 : 핸들러 어댑터를 실행한다.
4) 핸들러 실행 : 핸들러 어댑터가 실제 핸들러를 실행한다.
5) ModelAndView 반환 : 핸들러 어댑터는 핸들러가 반환하는 정보를 ModelAndView로 변환해서 반환한다.
6) viewResolver 호출 : 뷰 리졸버를 찾고 실행한다.
7) view 반환 : 뷰 리졸버는 뷰의 논리 이름을 물리 이름으로 바꾸고, 렌더링 역할을 담당하는 view 객체를 반환한다.
8) view 렌더링 : view 객체를 통해 view를 렌더링한다.

로그 출력

  • 로그 레벨은 보통 개발 시 DEBUG, 운영 시 INFO를 사용한다.
  • 로그 출력시 String의 + 연산(ex. log.trace("trace : " + name))을 사용하면 안된다. 만약 trace 레벨로 로그를 출력하지 않아도 +연산은 실행되므로 리소스 낭비가 일어난다. log.trace("trace : {}", name)과 같은 식으로 사용하자

consume vs produce

consume : 해당 타입의 요청만 받을 수 있다는 뜻. Content-Type 헤더와 값을 비교한다.

produce : 반환 타입을 지정해주는 것. accept 헤더의 값과 비교한다.

@RequestHeader

요청에 포함된 헤더를 받아올 수 있다.

특정 헤더만 가져올 수도 있다.

@GetMapping
public String handler(@RequestHeader("host") String host) {
    // ...
}

MultiValueMap

하나의 키에 여러 값을 받을 수 있다.

@RequestBody를 @RequestParam이나 @ModelAttribute와 달리 생략할 수 없는 이유는?

스프링 내부 규칙상 String, int, integer 같은 단순 타입은 자동으로 @RequestParam을 붙여주고 나머지 객체 같은 타입은 @ModelAttribute를 붙여준다.

따라서 객체를 바인딩 받을 때 @RequestBody를 생략해버리면 @ModelAttribute가 자동으로 붙기때문에, @RequestBody는 생략할 수 없다.

RequestMappingHandlerAdapter

핸들러 어댑터 중 하나로 핸들러(컨트롤러)에 @RequestMapping이 붙어있으면 이 어댑터가 사용된다.

RequestMappingHandlerAdapter는 컨트롤러의 파라미터를 보고 각 타입에 맞는 ArgumentResolver를 호출해서 컨트롤러가 필요로하는 파라미터 값을 반환받는다.(ex. HttpServletRequest, Model, @RequestBody HelloData hellodata..)

그중 @RequestBody나 HttpEnitity를 처리하는 ArgumentResolver는 내부적으로 HttpMessageConverter를 호출한다.

ArgumentResolver를 통해 값을 반환받고 나면, RequestMappingHandlerAdapter가 반환받은 값을 컨트롤러에 주입해주는 것이다.

응답을 할 땐, ArgumentResolver가 아닌, ReturnValueHandler가 사용되는데, String으로 반환해도 view를 찾아 리턴하는 이유도 ReturnValueHandler가 처리해줘서 그런 것이다. 응답의 경우에도 @ResponseBody나 HttpEntity를 사용하여 응답을 할 땐, 내부적으로 HttpMessageConverter를 호출하여 처리한다.

메세지 컨버터

RequestMappingHandlerAdapter를 사용할 때 @RequestBody나 @ResponseBody를 사용하면 내부적으로 메세지 컨버터가 사용된다.

요청시 핸들러의 파라미터에 @RequestBody가 붙어있으면 httpMessageConverter를 통해 데이터를 적절한 타입으로 변환해서 받아온다. 파라미터의 타입과 요청 헤더의 Content-Type을 체크해서 어떤 httpMessageConverter를 쓸 것인지 결정한다.

응답시 @ResponseBody가 붙어있으면, viewResolver 대신 httpMessageConverter가 동작한다. 요청 시 들어온 Accept 헤더와, 핸들러(컨트롤러)의 반환 타입을 조합해서 어떤 HttpMessageConverter를 쓸건지 정해준다. 기본적으로 문자열을 반환할때는 StringHttpMessageConverter가 동작하고, 객체를 반환할때는 MappingJackson2HttpMessageConverter가 동작한다.

@ModelAttribute("")

""에 이름값을 주면 Model을 주입받아 model.addAttribute를 직접 사용하지 않아도 적어준 이름값을 통해 자동으로 Model에 담아준다. ""값을 주지 않으면 자동으로 바인딩할 객체 타입의 첫글자만 소문자로 바꿔 Model에 담아준다.

@PostMapping
public String modelAttribute(@ModelAttribute Test test) {
    // 자동으로 model.addAttribute("test", test); 처리가 된다.
    return "ok";
}

RedirectAttributes

리다이렉트시 사용할 수 있다. 파라미터로 주입받으면 된다.
자동으로 Url 인코딩도 해주고, {변수}로 치환해서 사용할 수도 있다.
{}로 값을 주지 않는다면 자동으로 쿼리 파라미터로 넘어간다.

@PostMapping("/add")
public String save(@ModelAttribute Item item, RedirectAttributes redirectAttributes) {
    Item savedItem = itemRepository.save(item);
    redirectAttributes.addAttribute("itemId", savedItem.getId());
    redirectAttributes.addAttribute("status", true);
    return "redirect:/basic/items/{itemId}"; // /basic/items/3?status=true
}

0개의 댓글