HTTP,MVC

마동찬·2023년 4월 16일
0
post-thumbnail

Reference <- https://ittrue.tistory.com/234

Intro

스프링 프레임워크의 모듈 중에는 웹 계층을 담당하는 몇 가지 모듈이 있다.
웹 계층에 서블릿(Servlet) API를 기반으로 클라이언트의 요청을 처리하는 모듈이 있는데 이를 스프링 웹 MVC(spring-web-mvc) 또는 스프링 MVC라고 한다.
Spring MVC는 클라이언트의 요청을 편리하게 해주는 기능을 제공한다.

서블릿(Servlet)

서블릿은 클라이언트의 요청을 처리하도록 특정 규약에 맞춰 Java 코드로 작성하는 클래스 파일이다.
아파치 톰캣(Apache Tomcat)은 이러한 서블릿들이 웹 애플리케이션으로 실행할 수 있도록 해주는 서블릿 컨테이너(Servlet Container) 중 하나이다.
Spring MVC 내부에서는 서블릿을 기반으로 웹 애플리케이션을 동작하며, 스프링 부트는 기본적으로 아파치 톰캣이 내장되어 있다.

MVC ( Model, View, Controller)

MVC 패턴은 애플리케이션을 개발할 때 사용하는 디자인 패턴이다.
애플리케이션의 개발 영역을 MVC(Model, View, Controller)로 구분하여 각 역할에 맞게 코드를 작성하는 개발 방식이다.
MVC 패턴을 도입하면서 UI 영역과 도메인(비즈니스 로직) 영역으로 구분되어 서로에게 영향을 주지 않으면서 개발과 유지보수를 가능하게 되었다.

  • Model
    Spring MVC 기반의 웹 애플리케이션이 클라이언트의 요청을 전달받으면 요청 사항을 처리하기 위한 작업을 한다.
    처리한 작업의 결과 데이터를 클라이언트에게 응답을 돌려주어야 하는데, 이때 클라이언트에게 응답으로 돌려주는 작업의 처리 결과 데이터를 'Model' 이라 한다.
    클라이언트의 요청 사항을 구체적으로 처리하는 영역을 서비스 계층(Service layer)라고 하며,
    요청 사항을 처리하기 위해 Java 코드로 구현한 것을 비즈니스 로직(Business Logic)이라 한다.
  • View
    View는 Model을 이용하여 웹 브라우저와 같은 애플리케이션의 화면에 보이는 리소스(Resource)를 제공하는 역할을 한다.
    Spring MVC에는 다양한 View 기술이 포함되어 있다.
    • HTML 페이지 출력
    • PDF, Excel 등의 문서 형태로 출력
    • XML, JSON 등 특정 형식의 포맷으로 변환
  • Controller
    컨트롤러는 클라이언트 측의 요청을 직접적으로 전달받는 엔드포인트(Endpoint)로써 Model과 View의 중간에서 상호작용을 해주는 역할을 한다. 클라이언트 측의 요청을 전달받아 비즈니스 로직을 거친 후, Model 데이터가 만들어지면, 이 Model 데이터를 View로 전달하는 역할을 한다.

Spring MVC의 동작 방식과 구성 요소

  • DispatcherServlet의 역할
    • DIspatcherServlet은 바빠보이지만 직접적으로 요청을 처리하지 않고 다른 구성요소에게 요청을 위임(Delegate)합니다.
    • 이처럼 애플리케이션 가장 앞단 배치되어 다른 구성요소들과 상호작용하면서 클라이언트 요청을 처리하는 패턴을 Front Controller Pattern이라고 합니다
    • DispatcherServlet은 HttpServlet을 상속받아 사용하고, 서블릿으로 동작한다.
      DispatcherServlet → FrameworkServlet → HttpServletBean → HttpServlet
      DispacherServlet을 사용하면 서블릿으로 등록하면서 모든 경로에 (urlPatterns=”/”)에 대해 매핑한다.

요청 흐름

서블릿이 호출되면 HttpServlet이 제공하는 service()가 호출된다.
스프링 MVC는 FramworkServlet.service()를 시작으로 여러 메서드가 호출되면서 DispacherServlet.doDispatch()가 최종적으로 호출된다.

동작 순서

핸들러 조회 : 핸들러 매핑을 통해 URL에 매핑된 핸들러(컨트롤러)를 조회한다.
핸들러 어댑터 조회 : 핸들러를 실행할 수 있는 핸들러 어댑터를 조회한다.
핸들러 어댑터 실행 : 핸들러 어댑터를 실행한다.
핸들러 실행 : 핸들러 어댑터가 실제 핸들러를 실행한다.
ModelAndView 반환 : 핸들러 어댑터는 핸들러가 반환하는 정보를 ModelAndView로 변환하여 반환한다.
viewResolver 호출 : 뷰 리졸버를 찾고 실행한다.
View 반환 : 뷰 리졸버는 뷰의 논리 이름을 물리 이름으로 바꾸고, 렌더링 역할을 담당하는 뷰 객체를 반환한다.
뷰 렌더링 : 뷰를 통해 뷰를 렌더링한다.

HTTP

  • HTTP (HyperText Transfer Protocol)
    • HTML과 같은 문서를 전송하기 위한 Application Layer 프로토콜
    • Header와 본문 내용으로 구성
    • Annotation을 통해 값을 가져올 수 있다.
  • HTTP 헤더(Header)
    클라이언트의 요청이나 서버의 응답에 포함되어 부가적인 정보를 담고 있음
    • Content-Type
      • 서버가 주고 받는 HTTP 메시지 바디(body, 본문)의 데이터 형식이 무엇인지를 알려주는 역할
      • 화면 정보가 아닌 데이터를 주고 받을때는 주로 'application/json'을 사용
    • authorization
      • 클라이언트가 적절한 자격 증명을 가지고 있는지를 확인하기 위한 정보를 담음
      • 일반적으로 REST API 기반 애플리케이션의 경우 클라이언트와 서버 간의 로그인 인증에 통과한 클라이언트들은 ‘Authorization’ 헤더 정보를 기준으로 인증에 통과한 클라이언트가 맞는지 확인하는 절차를 거친다.
    • User-Agent
      • 모바일 에이전트에서 들어오는 요청인지 모바일 이외에 다른 에이전트에서 들어오는 요청인지를 구분해서 처리 가능

◎ Spring, HTTP Header 정보 얻기

  • 개별 헤더 정보 받기

    @RestController
    @RequestMapping(path = "/coffees")
    public class CoffeeController {
      @PostMapping
      public ResponseEntity postCoffee(@RequestHeader("user-agent") String userAgent,
                                       @RequestParam("name") String name,
                                       @RequestParam("price") int price) {
    
          System.out.println("user-agent: " + userAgent);
          return new ResponseEntity<>(new Coffee(name, price), HttpStatus.CREATED);
      }
    }
    
  • 전체 헤더 정보받기

    @RestController
    @RequestMapping(path = "/members")
    public class MemberController {
      @PostMapping
      public ResponseEntity postMember(@RequestHeader Map<String, String> headers,
                                       @RequestParam("email") String email,
                                       @RequestParam("name") String name,
                                       @RequestParam("phone") String phone) {
    
          for (Map.Entry<String, String> entry : headers.entrySet()) {
              System.out.println("key: " + entry.getKey() +
                      ", value: " + entry.getValue());
          }
          return new ResponseEntity<>(new Member(email, name, phone),
                  HttpStatus.CREATED);
      }
    }
  • HttpServletRequest 이용
    HttpServletRequest 이용시, Request 헤더 정보에 다양한 방법으로 접근 가능
    HttpServletRequest는 다양한 API를 지원함
    단순히 특정 헤더 정보에 접근시에는 @RequestHeader를 사용하는 것을 추천
    저수준(Low Level)의 서블릿 API를 사용할 수 있기때문에 복잡한 HTTP Request/Response를 처리하는데 사용

    @RestController
    @RequestMapping(path = "/v1/orders")
    public class OrderController {
      @PostMapping
      public ResponseEntity postOrder(HttpServletRequest httpServletRequest,
                                      @RequestParam("memberId") long memberId,
                                      @RequestParam("coffeeId") long coffeeId) {
    
          String userAgent = httpServletRequest.getHeader("user-agent");
          System.out.println("user-agent: " + userAgent);
    
          return new ResponseEntity<>(new Order(memberId, coffeeId),
                  HttpStatus.CREATED);
      }
    }	
  • HttpEntity 이용
    - Spring MVC에서는 HttpEntity 객체를 통해서도 헤더 정보를 읽을 수 있다.
    - HttpEntity는 Request 헤더와 바디 정보를 래핑하고 있음
    - 조금 더 쉽게 헤더와 바디에 접근할 수 있는 다양한 API를 지원

    @RestController
    @RequestMapping(path = "/v1/coffees")
    public class CoffeeController{
    
      @GetMapping
      public ResponseEntity getCoffees(HttpEntity httpEntity) {
    
          for(Map.Entry<String, List<String>> entry : httpEntity.getHeaders().entrySet()){
              System.out.println("key: " + entry.getKey()
                      + ", " + "value: " + entry.getValue());
          }
    
          // Host 정보도 가져올 수 있다.
          System.out.println("host: " + httpEntity.getHeaders().getHost());
          // host: localhost:8080
          return null;
      }
    }

    Reference
    https://velog.io/@gwichanlee/Spring-HTTP-Header
    https://ittrue.tistory.com/234

profile
새내기개발자 성장기록

0개의 댓글