본문 바로가기

웹 프로그래밍/스프링

스프링 MVC(1) 원리 정리

프론트 컨트롤러 패턴

프론트 컨트롤러(수문장)가 하나로 클라이언트 요청에 맞는 컨트롤러를 찾아서 호출

공통 처리를 전담(나머지 컨트롤러는 서블릿 사용하지 않아도 됨)

스프링 MVC의 DispatcherServlet이 프론트 컨트롤러 패턴으로 구현되어 있음

 

어댑터 패턴

프론트 컨트롤러가 다양한 방식의 컨트롤러를 처리할수 있음

핸들러 어탭터: 프론트 컨트롤러와 컨트롤러(핸들러) 중간에 위치해서 어탭터 역할을 함

핸들러(컨트롤러): 컨트롤러인데 어댑터 덕분에 더 범주가 다양해짐

 

로그 출력(@Slf4j)

SLF4J(인터페이스)의 구현체 Logback(구현체)를 사용

logging.level.hello.springmvc=debug (디폴트가 info)

 

@RestController

원래 Controller는 뷰를 리턴하지만 RestController는 ResponseBody(http 응답 메시지)에 리턴한 값을 넣어 버림

즉 @Controller + @ResponseBody의 효과

비동기 통신과 http api에 많이 쓰임

 

 

@PathVariable

리소스 경로의 식별자를 넣는 스타일(실무)

    @GetMapping("/mapping/{userId}")
        public String mappingPath(@PathVariable("userId") String data)

뷰로 이동할때도 url에 Path variable 사용 가능

return "/redirect:/basic/items/{itemId}";

 

@PostMapping

@PostMapping(value = "mapping-consume", consumes = "application/json")

-> 컨트롤러 입장에서 json만 받겠다(consumes으로 지정 가능)

 

@PostMapping(value = "mapping-produce", produces = "text/html")

-> 컨트롤러가 생산하는 데이터에 대해 클라이언트의 Accept가 "text/html" 이여야 함

 

MultiValueMap

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

-> 따라서 키로 찾게되면 리스트로 반환됨

 

@RequestParam

 

http://localhost:8080/request-param-v2?username=hello&age=20

public @ResponseBody String requestParamV2(
            @RequestParam("username") String memberName,
            @RequestParam("age") int memberAge)

-> 쿼리 스트링 이름으로 매칭해서 가져옴

 

    public @ResponseBody String requestParamV3(
            @RequestParam String username,
            @RequestParam int age)

-> 생략시 변수명이 파라미터 이름이랑 같아야함

 

@RequestParam(required = false) -> required가 true면 없으면 오류, false면 없어도 됨(null로 들어감)

 

defaultValue

    public @ResponseBody String requestParamDefault(
            @RequestParam(required = true, defaultValue = "guest") String username,
            @RequestParam(required = false, defaultValue = "-1") int age)

-> 값이 없으면 디폴트로 설정, 공백도 디폴트로 처리해줌(따라서 required가 true든 false든 상관이 없음)

 

@ModelAttribute

@RequestParam으로 일일이 파라미터를 받기보다

객체로 바로 받을수 있게 해줌(생략 가능)

public @ResponseBody String modelAttributeV1(@ModelAttribute HelloData helloData)

@ModelAttribute("item")

이름 속성을 정의하면 뷰 렌더링할때 필요한 Model model의 역할도 한번에 수행함

이름을 생략하면 디폴트로 클래스이름이 첫글자가 소문자로 바뀜

 

 

@RequestHeader

HTTP요청 헤더정보를 받을수 있음

@RequestHeader(value="Host") String host

 

요청 메시지 바디(String) 받기

- 1.스트림 방식

    @PostMapping("/request-body-string-v2")
    public void requestBodyString(InputStream inputStream, Writer responseWriter) throws IOException{
        String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
        log.info("messageBody={}", messageBody);

        responseWriter.write("ok");
    }

InputStream(Reader): HTTP 요청 메시지 바디의 내용을 직접 조회

OutputStream(Writer): HTTP 응답 메시지 바디에 직접 결과 출력

 

- 2.HttpEntity 방식

HttpEntity<>: Http header, body 정보를 편리하게 조회

    @PostMapping("/request-body-string-v3")
    public HttpEntity<String> requestBodyStringV3(HttpEntity<String> httpEntity) throws IOException {
        String messageBody = httpEntity.getBody();


        log.info("messageBody={}", messageBody);

        return new HttpEntity<>("ok");
    }

 

 

HttpEntity<>를 상속하고 더 기능을 구현한 RequestEntity<>, ResponseEntity<>도 있음

 

- 3.@RequestBody와 @ResponseBody

최종적으론 애노테이션으로 구현한 @RequestBody와 @ResponseBody를 실무에서 씀

 

    @PostMapping("/request-body-string-v4")
    public @ResponseBody String requestBodyStringV4(@RequestBody String messageBody) {
        log.info("messageBody={}", messageBody);

        return "hello!!!!!";
    }

 

정리

요청 파라미터를 조회하는 기능: @RequestParam, @ModelAttribute

HTTP 요청 메시지 바디를 직접 조회하는 기능: @RequestBody

HTTP 응답 메시지 바디를 직접 응답결과를 담아서 전달하는 기능(이경우도 view를 사용하지 않음): @ResponseBody

 

요청(content-type = "application/json" 인경우)

-> json -> http 메시지 컨버터 -> 객체

응답

-> 객체 -> http 메시지 컨버터 -> json 

 

ResponseEntity<>의 용도

 

동적으로 Http 상태를 바꿀때는 ResponseEntity 사용

@GetMapping("/response-body-string-v2")

public ResponseEntity<String> responseBodyV2(){

    return new ResponseEntity<>("ok", HttpStatus.OK);

}

 

일반적으로는 @ResponseStatus(HttpStatus.OK) + @ResponseBody를 실무에서 씀

 

@ResponseStatus(HttpStatus.OK)

@GetMapping("/response-body-json-v2")

public @ResponseBody HelloData responseBodyJsonV2(){

    HelloData helloData = new HelloData();

    helloData.setUsername("userA");

    helloData.setAge(20);

 

    return helloData;

    }

}

 

PRG (Post/Redirect/Get) 패턴

마지막 요청이 Post일 경우 새로고침하면 계속 Post 요청이 가서 에러가 남

이것을 막기위해서 컨트롤러에서 뷰 템플릿으로 이동하는것이 아니라

리다이렉트를 호출해서 마지막 요청이 Get이 되게끔 해야함

 

RedirectAttributes

    @PostMapping("/add")
    public String saveForm(@ModelAttribute("item") Item item, RedirectAttributes redirectAttributes){

        Item savedItem = itemRepository.save(item);
        redirectAttributes.addAttribute("itemId", savedItem.getId());
        redirectAttributes.addAttribute("status", true);

        return "redirect:/basic/items/{itemId}";
    }

 

리턴에 PathVariable처럼 사용 가능 치환이 없는 status는 쿼리 파라미터로 url로 넘어감(?status=true)

이런 쿼리 파라미터를 타임리프에서 <h2 th:if="${param.status}" th:text="'저장 완료'"></h2>

이렇게 param을 이용해서 꺼내서 사용할수 있고 if문의 조건에 따라 출력문을 생성할수 있음

'웹 프로그래밍 > 스프링' 카테고리의 다른 글

스프링 데이터 접근 핵심 원리  (0) 2023.05.08
스프링 MVC(2) 검증 정리  (0) 2023.04.14
웹 애플리케이션 이해  (0) 2023.04.05
빈 스코프  (0) 2023.02.20
빈 생명주기(라이프사이클) 콜백  (0) 2023.02.19