Spring MVC

byeol·2023년 3월 6일
0

어떻게 스프링이 등장하게 되었는지
그 내용을 순차적으로 한페이지로 정리해보려고 한다.

WAS

Web Application Server가 무엇인가
Web Server는 무엇인가

둘을 구분짓자면 Web Application Server는 동적인 페이지의 구현을 담당한다. Web Server는 정적인 페이지의 구현을 담당한다.

페이지를 담당한다라는 이 말을 서버에 저장된 html파일을 클라이언트에게 보낸다는 것이다.

정적인 페이지라는 것은 html 파일 그 자체를 가져와서 그대로 클라이언트에게 전달하는 것을 말한다. 예를 들어 회사의 비전처럼 잘 바뀌지 않는 페이지를 말한다.

동적인 페이지라는 것은 html 파일에 뭔가 동적인 비즈니스 로직이 섞여있는 페이지라고 볼 수 있다. 예를 들어 시간마다 달라지는 날씨, 내가 작성하면 바로바로 update되는 블로그 글처럼 html 파일 자체만으로는 구현될 수 없고 java를 통해서 결과를 도출하고 이 결과를 html파일에 함께 실어서 클라이언트에게 보여줘야 하는 페이지를 의미한다.

이 동적인 페이지를 서버에서 구현하는 여러가지 방법이 있는데
어떻게 발전되어 왔는지 그 순서대로 살펴보자.

Servlet

Servlet : "자바 서블릿은 자바를 사용하여 웹페이지를 동적으로 생성하는 서버측 프로그램 혹은 그 사양을 말하며, 흔히 "서블릿"이라 불린다. 자바 서블릿은 웹 서버의 성능을 향상하기 위해 사용되는 자바 클래스의 일종이다."

간단하게 설명하면 웹으로부터 요청을 받거나 응답을 보내는 것을 개발자 쉽게 구현할 수 있도록 도와주는, 관리해주는 역할을 하는 자바 웹 프로그래밍 기술이다.

@WebServlet(name="frontControllerServletV4", urlPatterns = "/front-controller")
public class FrontControllerServletV4 extends HttpServlet {

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    }
 }

클라이언트가 "http://기본도메인/front-controller"를 입력하며

JSP

MVC

컨트롤러 : Http 요청을 받아서 파라미터 검증하고, 비즈니스 로직을 실행 + 뷰에 전달할 결과 데이터를 조회해서 모델에 담는다.

모델

  • HttpServletRequest
  • request.setAttribute()


한계점 : viewPath 중복 , 포워드 중복, 사용하지 않는 코드 존재 (HttpServletResponse의 경우 사용되지 않는데 계속 존재)
또한 HttpServletResponse 및 HttpServletRequest를 사용하는 코드는 테스트케이스 작성이 어렵다 왜냐하면 Http에 대한 요청과 응답은 웹브라우저를 통해서 데이터를 주고 받는 환경이 구축되어야 가능하기 때문이다.

수문장 역할 : 포스트 컨트롤러

version 1

version 2

version 3

  • 서블릿 종속성 제거-> HttpServletRequest가 아닌 Model을 따로 만들어서 거기에 저장 , Controller 입장에서는 HttpServlet이 필요하지 않다.
  • 뷰 이름 중복 제거

version 4

파라미터 자체에 model을 담아 버린다.

version 5

컨트롤러가 달라질 때마다 포스트 컨트롤러 또한 수정해야 한다.
이를 해결하기 위한 어댑터 패턴이 존재한다.

어댑터 패턴에는 두가지가 등장한다.

  • 핸들러 : 컨트롤러
  • 핸들러어댑터 : 컨트롤러의 모양과 상관이 통일되게 포스트 컨트롤러에 똑같은 모양의 값을 전달해준다.

스프링 MVC

스프링 MVC도 프론트 컨트롤러 패턴으로 구현된다.
DispatcherServlet= 스프링의 프런트 컨트롤러

스프링은 핸들러 어댑터와 핸들러 매핑을 대부분 구현
개발자는 이미 구현된 핸들러 어댑터와 핸들러 매핑을 사용한다.

뷰리졸버: 뷰페이지의 이름을 물리적 주소로 변환해주는 역할을 담당하는데 스프링의 뷰리졸버는 InternalResourceViewResolver라는 뷰 리졸버를 자동으로 등록한다.
이때 application.properties에 있는 설정 정보를 사용해서 등록

version 1

version 2

version 3

Spring MVC 기능들

  • @RestController
  • 경로 변수
@RestController
public class MappingController {

    private Logger log = LoggerFactory.getLogger(getClass());


    @GetMapping("/mapping/{userId}")
    public String helloBasic(@PathVariable("userId") String data) {
        log.info("mappingPath userId={}",data);
        return "ok";
    }

}

  • 특정 파라미터가 있거나 없는 조건을 추가하여 url을 매핑할 수 있다. 잘 사용하지 않는다.
    
    @RestController
    ....
     @GetMapping(value="/mapping-param",params="mode=debug" )
       public String helloBasic() {
           log.info("mappingParam");
           return "ok";
       }

HTTP 요청 메세지

기본, 헤더 조회


@RestController
...

@RequestMapping("/headers")
    public String headers(HttpServletRequest request, HttpServletResponse response,
                          HttpMethod httpMethod,
                          Locale locale,
                          @RequestHeader MultiValueMap<String, String> headerMap,
                          @RequestHeader("host") String host,
                          @CookieValue(value = "myCookie", required = false) String cookie){

        log.info("request={}",request);
        log.info("response={}",response);
        log.info("httpMethod={}",httpMethod);
        log.info("locale={}",locale);
        log.info("headerMap={}",headerMap);
        log.info("header host={}",host);
        log.info("myCookie={}",cookie);

        return "ok";

    }


@RequestParam

  • 추가적으로 파라미터 이름과 변수 이름이 같으면 괄호 생략 가능
  • 추가적으로 String,int,Integer 등의 단순타입이면 @RequestParam도 생략 가능 -> 생략하면 이 파라미터는 필수값이 아니다. 저 RequestParam안에 required 속성이 있는데 이게 true이면 필수적으로 파리미터에 값이 들어가야 하고 false면 들어가든 들어가지 않든 상관없다.
  • required 속성

    age는 필수값이 아니다. 보면 자료형이 int가 아니 Integer다. 그 이유는 int는 null이 올 수 없는 기본값이 0인 자료형이기 때문에 Integer가 온다.
    또한 만약에 http://localhost:8080/request-param-required?username=를 입력했다고 하자. 그려면 username은 null이 아니라 빈문자열이 된다.
  • 기본 값 적용 속성 defaultValue

    http://localhost:8080/request-param-default?username= 이와 같이 빈문자열로 get요청을 하면

    기본값으로 받는다.
  • 파라미터를 Map으로 받기 @RequestParam Map

    +) 만약에 http://localhost:8080/request-param-map?username=kined&username=lim&age=14&age=25 이런 식으로 key에 대한 value값이 많을 때는 @RequestParam MultiValueMap

@ModelAttribute


HelloData라는 클래스가 있을 때 @ModelAttribute는 HelloData 객체를 생성하고 요청 파라미터의 이름(username, age)을 파라미터로 가지고 있는 setter(setUsername(), setAge())를 호출해서 값을 저장한다.

  • @ModelAttribute은 생략이 가능하다. @RequestParam도 생략이 가능하나 둘의 차이가 있다. @ModelAttribute는 String,int,Integer 외에서 생략 가능, 반대로 @RequestParam은 단순타입에서만 생략이 가능하다.

단순텍스트 - HTTP message body

파라미터가 아니다!


InputStream을 이용해서 읽음

  • HttpServletReqeust, HttpServletResponse가 아닌 InputStream과 Writer를 이용해서 직접 조회 및 출력
  • HttpEntity를 통해서 header,body정보 및 응답도 직접 조회 및 반환
  • @RequestBody : 단순하게 message body정보만을 가지고 오고 싶을 때, @ResponseBody: return값이 View를 조회하는 것이 아니라 message Body에 실을 내용이다.

JSON


텍스트와 마찬가지로
InputStream을 이용해서 메시지 바디에 있는 Json형태의 데이터를 읽어온다.
또한 Json-> java객체로 바꾸는 작업은 ObjectMapper의 readValue()메서드를 이용한다.

  • @RequestBody를 통해서 message body에 있는 내용 직접 조회하고 @ResponseBody로 내용 실어서 보내기

  • @RequestBody + 메세지 컨버터를 이용해서 message body에 있는 내용을 원하는 모양의 객체로 바로 넣어버리기

  • HttpEntity + 메세지 컨버터를 이용해서 message body에 있는 내용을 원하는 모양의 객체로 바로 넣어버리기

  • @ResponseBody + 메세지 컨버터를 이용해서 객체 데이터를 Json형태로 변환 없이 바로 message body에 넣어버리기 (객체->메서지 컨버터 -> Json)

HTTP 응답

정적 리소스는 그냥 html 파일 그대로 보내는 것
스프링 부트는 /static,/resources,/public,/META-INF/resources에 정적 리소스를 저장한다.

뷰템플릿은 동적인 리소스로 HTML을 동적으로 만들어서 클라어인트에게 전달한다.

HTTP 메세지는 HTML이 아닌 데이터를 전달하는 것, HTML 페이지를 클라이언트가 가지고 있다.


뷰 템플릿 호출


저기 형광펜으로 칠해진 파일을 호출한다고 가정한다.

위와 같이 사용하기 위해서는 application.properties에 아래와 같은 설정이 되어 있어야 한다.

spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html

HTTP API, 메세지 바디에 직접 입력


class에 @Controller가 있지만 반환값이 없기 때문에 view를 호출하지 않는다. 또한 HttpServletResponse 객체를 통해서 HTTP 메시지 바디에 "ok" 직접 입력

  • ResponseEntity는 HttpEntity를 상속받아 HTTP 메시지의 바디와 헤더 정보 모두를 가지고 있다. + 추가로 응답코드를 설정할 수 있다.
  • @ResponseBody는 return 값이 String이어도 view를 호출하지 않고 HTTP 메세지 바디에 응답을 반환한다.
  • 이제는 객체를 Json형태로 보내는 방법에 대해서 알아보자.
    일단 그냥 return 값으로 객체를 넣으면 알아서 메세지 컨버터가 Json으로 변환시켜 준다.
  • @RestController는 해당 컨트롤러 모두 @ResponseBody가 적용되는 효과가 있다.
profile
꾸준하게 Ready, Set, Go!

0개의 댓글