본문 바로가기
Spring Study/MVC 패턴

[MVC 패턴] MVC 기본 기능

by 정재인 2023. 8. 15.

로깅 (Logging)

운영 시스템에서는 System.out.println() 보다는 별도의 로깅 라이브러리를 사용해 로그를 출력한다.

 

로그 선언 방법 (3가지)

private Logger log = LoggerFactory.getLogger(getClass());
private static final Logger log = LoggerFactory.getLogger(Xxx.class)
@Slf4j //Lombok 사용

 

로그 호출 방법

log.info("hello")
log.info("info log={}", name);

 

로그 사용시 장점

- 쓰레드 정보, 클래스 이름 같은 부가 정보를 함께 볼 수 있고, 출력 모양을 조정할 수 있다.

- 로그 레벨에 따라 개발 서버에서는 모든 로그를 출력하고, 운영서버에서는 출력하지 않는 등 로그를 상황에 맞게 조절할 수 있다.

- 시스템 아웃 콘솔에만 출력하는 것이 아니라, 파일이나 네트워크 등 로그를 별도의 위치에 남길 수 있다.

     * 특히 파일로 남길 때는 일별, 특정 용량에 따라 로그를 분할하는 것도 가능하다.

- 성능도 일반 System.out보다 좋다. (내부 버퍼링, 멀티 쓰레드 등) / 실무에서는 꼭 로그를 사용해야 한다.

 


요청 매핑 (RequestMapping)

package hello.springmvc.basic.requestmapping;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.*;
    @RestController
     public class MappingController {
        private Logger log = LoggerFactory.getLogger(getClass());
/**
  * 기본 요청
  * 둘다 허용 /hello-basic, /hello-basic/
  * HTTP 메서드 모두 허용 GET, HEAD, POST, PUT, PATCH, DELETE 
  */
    @RequestMapping("/hello-basic")
     public String helloBasic() {
        log.info("helloBasic");
        return "ok";
    }
}

 

@RestController

- @Controller는 반환 값이 String이면 뷰 이름으로 인식한다. 따라서 뷰를 찾고 뷰가 랜더링 된다.

- @RestController는 반환 값으로 뷰를 찾는 것이 아니라, HTTP 메시지 바디에 바로 입력한다.

☞ 따라서 실행 결과 'ok' 메시지를 받을 수 있다.

 

@RequestMapping("/hello-basic")

- /hello-basic URL 호출이 오면 이 메서드가 실행되도록 매핑한다.

- 대부분의 속성을 배열[]로 제공하므로 다중 설정이 가능하다. ex) {"/hello-basic", "/hello-go"}

 

HTTP 메서드

- @RequestMapping에 method 속성으로 HTTP 메서드를 지정하지 않으면 HTTP 메서드와 무관하게 호출된다.

- GET, HEAD, POST, PUT, PATCH, DELETE 모두 허용

/**
  * method 특정 HTTP 메서드 요청만 허용
  * GET, HEAD, POST, PUT, PATCH, DELETE
  */
  @RequestMapping(value = "/mapping-get-v1", method = RequestMethod.GET)
  public String mappingGetV1() {
      log.info("mappingGetV1");
      return "ok";
  }

 

HTTP 메서드 매핑 축약 (@GetMapping을 이용한 모습)

/**
  * 편리한 축약 애노테이션 (코드보기) * @GetMapping
  * @PostMapping
  * @PutMapping
  * @DeleteMapping
  * @PatchMapping
  */
  @GetMapping(value = "/mapping-get-v2")
  public String mappingGetV2() {
      log.info("mapping-get-v2");
      return "ok";
  }

 

PathVariable(경로 변수) 사용

/**
  * PathVariable 사용
  * 변수명이 같으면 생략 가능
  * @PathVariable("userId") String userId -> @PathVariable userId
  */
  @GetMapping("/mapping/{userId}")
  public String mappingPath(@PathVariable("userId") String data) {
      log.info("mappingPath userId={}", data);
      return "ok";
  }

 

요청 매핑 - API 예시

ex) 회원 관리를 HTTP API로 만든다 생각하고 매핑

- 회원 목록 조회: GET /users

- 회원 등록: POST /users

- 회원 조회: GET /users/{userId}

- 회원 수정: PATCH /users/{userId}

- 회원 삭제: DELETE /users/{userId}

package hello.springmvc.basic.requestmapping;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/mapping/users")
public class MappingClassController {
   
   /**
     * GET /mapping/users
     */
    @GetMapping
    public String users() {
        return "get users";
    }
   
   /**
     * POST /mapping/users
     */
    @PostMapping
    public String addUser() {
        return "post user";
    }
    
   /**
     * GET /mapping/users/{userId}
     */
    @GetMapping("/{userId}")
    public String findUser(@PathVariable String userId) {
        return "get userId=" + userId;
    }
    
   /**
     * PATCH /mapping/users/{userId}
     */
    @PatchMapping("/{userId}")
    public String updateUser(@PathVariable String userId) {
        return "update userId=" + userId;
    }
    
    /**
      * DELETE /mapping/users/{userId}
      */
      @DeleteMapping("/{userId}")
      public String deleteUser(@PathVariable String userId) {
          return "delete userId=" + userId;
      }
}

 


HTTP 요청 파라미터 - 쿼리 파라미터, HTML Form

클라이언트에서 서버로 요청 데이터를 전달할 때 사용하는 방법

 

GET 쿼리 파라미터

- /url?username=hello&age=20

- 메시지 바디 없이, URL의 쿼리 파라미터에 데이터를 포함해서 전달

- ex) 검색, 필터, 페이징 등에서 많이 사용하는 방식

package hello.springmvc.basic.request;
import hello.springmvc.basic.HelloData;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;
  
@Slf4j
@Controller
    public class RequestParamController {
      /**
        * 반환 타입이 없으면서 이렇게 응답에 값을 직접 집어넣으면, view 조회X
        */
      @RequestMapping("/request-param-v1")
      public void requestParamV1(HttpServletRequest request, HttpServletResponse response) throws IOException {
          String username = request.getParameter("username");
          int age = Integer.parseInt(request.getParameter("age"));
          log.info("username={}, age={}", username, age);
          response.getWriter().write("ok");
      }
}

request.getParameter()를 이용하여 요청 파라미터 조회

 

POST - HTML Form

- content-type: application/x-www-form-urlencoded

- 메시지 바디에 쿼리 파라미터 형식으로 전달 username=hello&age=20

- ex) 회원 가입, 상품 주문, HTML Form 사용

<!DOCTYPE html>
  <html>
  <head>
      <meta charset="UTF-8">
      <title>Title</title>
  </head>
  <body>
        <form action="/request-param-v1" method="post">
		username: <input type="text" name="username" /> 
		age: <input type="text" name="age" /> <button type="submit">전송</button>
        </form>
    </body>
</html>

 

 

HTTP message body에 데이터를 직접 담아서 요청

- HTTP API에서 주로 사용 JSON, XML, TEXT

- 데이터 형식은 주로 JSON 사용

- POST, PUT, PATCH

/**
  * @RequestParam 사용
  * - 파라미터 이름으로 바인딩
  * @ResponseBody 추가
  * - View 조회를 무시하고, HTTP message body에 직접 해당 내용 입력 */
    @ResponseBody
    @RequestMapping("/request-param-v2")
    public String requestParamV2(@RequestParam("username") String memberName, @RequestParam("age") int memberAge) {
        log.info("username={}, age={}", memberName, memberAge);
        return "ok";
}

- @RequestParam: 파라미터 이름으로 바인딩

- @ResponseBody: View 조회를 무시하고, HTTP message body에 직접 해당 내용 입력

@RequestParam("username") String memberName = request.getParameter("username)

- String, int, Integer 등의 단순 타입이라면 @RequestParam 생략 가능 (웬만하면 그냥 작성하는 편이 나음)

 


 

HTTP 요청 파라미터 - @ModelAttribute

/**
  * @ModelAttribute 사용
  */
  @ResponseBody
  @RequestMapping("/model-attribute-v1")
  public String modelAttributeV1(@ModelAttribute HelloData helloData) {
      log.info("username={}, age={}", helloData.getUsername(),
      helloData.getAge());
      return "ok";
  }

- HelloData 객체를 생성한다.

- 요청 파라미터의 이름으로 HelloData 객체의 프로퍼티를 찾고, 해당 프로퍼티의 setter를 호출해서 파라미터의 값을 입력(바인딩)한다.

* 프로퍼티: 객체에 getUsername(), setUsername() 메서드가 있으면, 이 객체는 username이라는 프로퍼티를 가지고 있다.

 

HTTP 요청 메시지 - 단순 텍스트 / JSON

HTTP message body에 데이터를 직접 담아서 요청

- HTTP API에서 주로 사용 JSON, XML, TEXT

- 데이터 형식은 주로 JSON 사용

- POST, PUT, PATCH

 

스프링 MVC가 지원하는 파라미터

- HttpEntity: HTTP header, body 정보를 편리하게 조회

 1. 메시지 바디를 직접 조회

 2. 요청 파라미터를 조회하는 기능과 관계 없음 @RequestParam X, @ModelAttribute X

 

- HttpEntity는 응답에도 사용 가능

 1. 메시지 바디 정보 직접 반환

 2. 헤더 정보 포함 가능

 3. view 조회 X

 


 

HTTP 응답 - 정적 리소스, 뷰 템플릿

정적 리소스: 웹 브라우저에 정적인 HTML, css, js를 제공할 때 사용한다

- 정적 리소스 경로: src/main/resources/static

- src/main/resources/static/basic/hello-form.html 이라는 파일이 들어있을 경우

    → localhost:8080/basic/hello-form.html

- 정적 리소스는 해당 파일을 변경 없이 그대로 서비스 하는 것이다.

 

뷰 템플릿 사용: 웹 브라우저에 동적인 HTML을 제공할 때 사용한다.

- 뷰 템플릿 경로: src/main/resources/templates

 

<src/main/resources/templates/response/hello.html 생성>

<!DOCTYPE html>
  <html xmlns:th="http://www.thymeleaf.org">
  <head>
      <meta charset="UTF-8">
      <title>Title</title>
  </head>
  <body>
  <p th:text="${data}">empty</p>
  </body>
  </html>

<뷰 템플릿을 호출하는 컨트롤러>

package hello.springmvc.basic.response;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
    public class ResponseViewController {
    
    @RequestMapping("/response-view-v1")
    public ModelAndView responseViewV1() {
        ModelAndView mav = new ModelAndView("response/hello").addObject("data", "hello!");
        return mav; 
     }

    @RequestMapping("/response-view-v2")
    public String responseViewV2(Model model) {
        model.addAttribute("data", "hello!!");
        return "response/hello";
    }
    @RequestMapping("/response/hello")
    public void responseViewV3(Model model) {
        model.addAttribute("data", "hello!!");
      }
}

- String을 반환하는 경우 - View or HTTP 메시지

1. @ResponseBody가 없으면 response/hello로 뷰 리졸버가 실행되어 뷰를 찾고, 렌더링 한다.

2. @ResponseBody가 있으면 뷰 리졸버를 실행하지 않고, HTTP 메시지 바디에 직접 문자가 입력된다.

 

- Void를 반환하는 경우

@Controller를 사용하고 HttpServletResponse, OutputStream(Writer) 같은 HTTP 메시지 바디를 처리하는 파라미터가 없으면 요청 URL을 참고해서 논리 뷰 이름으로 사용

 

- HTTP 메시지 사용

HTTP API를 제공하는 경우 HTML이 아닌 데이터를 전달해야 하므로, HTTP 메시지 바디에 JSON 같은 형식으로 데이터를 실어 보낸다.

 

 

댓글