스프링 mvc - 기본 기능

meluu_·2024년 1월 9일
0

스프링

목록 보기
14/27
post-thumbnail

🌿 Spring MVC 구조


스프링 부트 3.2.1 버전을 기준으로 작성됨

주의 : 스프링 부트 3 이상부터
URL 요청의 경우 /home/home/는 서로 다르게 매핑된다.

✔️ 요청 매핑


@RestController : 반환 값을 HTTP 메시지 바디에 바로 입력 (뷰 x)
@ResponseBody : View 조회 무시 및 HTTP 메시지 바디에 직접 해당 내용 입력
@RequestMapping() : 기본적인 요청 매핑 , (배열 제공으로 다중 설정 가능)
ex) @RequestMapping({"/home", "/category"})

편리한 축약 애노테이션

@GetMapping
@PostMapping
@PutMapping
@DeleteMapping
@PatchMapping

error : 405 code

PathVariable (경로변수)

// 변수명이 같으면 생략 가능
// @PathVariable String userId 

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

// 다중 사용
@GetMapping("/order/{userId}/{oderId}")
@PathVariable String userId, @PathVariable Long orderId

특정 조건 매핑

// 특정 파라미터 조건 매핑
// 파라미터가 맞아야 매핑됨 
 * params="mode",
 * params="!mode"
 * params="mode=debug"
 * params="mode!=debug" (! = )
 * params = {"mode=debug","data=good"}

@GetMapping(value = "/mapping-param", params = "mode=debug")


// 특정 헤더 조건 매핑
 * headers="mode",
 * headers="!mode"
 * headers="mode=debug"
 * headers="mode!=debug" (! = )
 
@GetMapping(value = "/mapping-header", headers = "mode=debug")


// 미디어 타입 조건 매핑 - HTTP 요청 Content-Type, consume

 * Content-Type 헤더 기반 추가 매핑 Media Type
 * consumes="application/json"
 * consumes="!application/json"
 * consumes="application/*"
 * consumes="*\/*"
 * MediaType.APPLICATION_JSON_VALUE
 
@PostMapping(value = "/mapping-consume", consumes = "application/json")

error : 415 code


// 미디어 타입 조건 매핑 - HTTP 요청 Aceept, produce
 
 * Accept 헤더 기반 Media Type
 * produces = "text/html"
 * produces = "!text/html"
 * produces = "text/*"
 * produces = "*\/*"
 
@PostMapping(value = "/mapping-produce", produces = "text/html")

error : 406 code

같은 url에 method만 다르게 하여 요청을 처리하는 방식을 택하자


✔️ HTTP 요청 - 기본, 헤더 조회


@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로 찍어보면 알 수 있다. 
host 
* 특정 HTTP 헤더를 조회
* 속성
  * 필수 값 여부: required
  * 기본 값 속성: defaultValue
  
cookie
* 특정 쿠키를 조회
* 속성
  * 필수 값 여부: required
  * 기본 값: defaultValue
  
MultiValueMap
* 하나의 키에 여러 값을 받기 가능
* HTTP header, HTTP 쿼리 파라미터와 같이 하나의 키에 여러 값을 받을 때 사용
* ex) keyA=value1&keyA=value2

/*
MultiValueMap<String, String> map = new LinkedMultiValueMap();
map.add("keyA", "value1");
map.add("keyA", "value2");
*/

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


쿼리 파라미터

  • ex) /home?name=kim&age=10
  • @PathVariable 사용

HTML Form

V1 : HttpServletRequest or Response : getParameter()
V2 : @RequestParam("파라미터 명") 자료형 변수
V3 : @RequestParam 자료형 변수 (파라미터 이름 == 변수 이름)
V4 : 자료형 변수 (String, int, Integer 등 단순 타입이면 @RequestParam 생략 가능)

주의
@RequestParam 애노테이션을 생략하면 스프링 MVC는 내부에서 required=false 를 적용

V3을 사용하도록 하자

파라미터 필수 여부
@RequestParam(required = true) String username

  • 기본값 : true (필수)

주의

  • 파라미터 이름은 있고 값이 없는 경우 : 빈문자로 통과
  • int형은 null 불가능 Integer or defaultValue 사용

기본 값 적용

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

파라미터 Map으로 조회

@RequestParam Map<String, Object> paramMap 
              MultiValueMap<String, Object> paramMap

paramMap.get("파라미터 이름")

✔️ HTTP 요청 파라미터 - @ModelAttribute


요청 파라미터를 받아서 필요한 객체를 만들고 그 객체에 바로 값을 넣는 방법

@Data // lombok (@getter,setter,toString... 등등 자동으로 적용)
public class Student {
 private String name;
 private int age;
}


public String modelAttrV1(@ModelAttribute Student student) {
	student.getName(); // 프로퍼티 접근
}
* @ModelAttribute 생략 가능 
* String, int 같은 단순 타입 : @RequestParam
* argument resolver 지정 타입 외 : @ModelAttribute

public String modelAttrV2(Student student) {
	student.getName(); // 프로퍼티 접근
}

바인딩 오류
숫자를 받는 곳에 문자를 넣으면 BindException 이 발생 -> 검증

✔️ HTTP 요청 메시지 - 단순 텍스트

  • HTTP message body에 데이터를 직접 담아서 요청
    • HTTP API에서 주로 사용, JSON, XML, TEXT
    • 데이터 형식은 주로 JSON 사용

여기서는 우선 단순 텍스트

HTTP 메시지 바디를 통해 데이터가 직접 넘어오는 경우는 @RequestParam ,@ModelAttribute 를 사용 불가

V1 : InputStream 으로 직접 읽기
V2 : Input, Output 스트림, Reader 사용

V3


 * HttpEntity: HTTP header, body 정보를 편리하게 조회
 * - HttpMessageConverter 사용 -> StringHttpMessageConverter 적용
 * - 응답에서도 HttpEntity 사용 가능
 * - 메시지 바디 정보 직접 반환(view 조회X)
 * - HttpMessageConverter 사용 -> StringHttpMessageConverter 적용
 
@PostMapping("/request-body-string-v3")
public HttpEntity<String> requestBodyStringV3(HttpEntity<String> httpEntity) {
 String messageBody = httpEntity.getBody();
 log.info("messageBody={}", messageBody);
 return new HttpEntity<>("ok");
}

// HTTP 상태 코드 설정 가능, 응답에서 사용
// return new ResponseEntity<String>("Hello World", responseHeaders, HttpStatus.CREATED)

V4

* @RequestBody
 * - 메시지 바디 정보를 직접 조회(@RequestParam X, @ModelAttribute X)
 * - HttpMessageConverter 사용 -> StringHttpMessageConverter 적용
 *
 * @ResponseBody
 * - 메시지 바디 정보 직접 반환(view 조회X)
 * - HttpMessageConverter 사용 -> StringHttpMessageConverter 적용

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

// 헤더 정보가 필요하다면 
// HttpEntity || @RequestHeader 를 사용

✔️ HTTP 요청 메시지 - JSON


V1

 private ObjectMapper objectMapper = new ObjectMapper();
 
 @PostMapping("/request-body-json-v1")
 public void requestBodyJsonV1(HttpServletRequest request, HttpServletResponse response) throws IOException {
    // 메시지 바디에서 데이터 읽고 문자로 변환
 	ServletInputStream inputStream = request.getInputStream();
 	String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
 	log.info("messageBody={}", messageBody);
    
    // 문자로 된 json 데이터를 Jackson 라이브러리인 objectMapper 사용하여 json을 객체로 변환
 	HelloData data = objectMapper.readValue(messageBody, HelloData.class);
 	log.info("username={}, age={}", data.getUsername(), data.getAge());

V2

// 메시지에서 데이터 꺼내고 messageBody에 저장
public String requestBodyJsonV2(@RequestBody String messageBody) throws IOException {

// 마찬가지로 objectMapper로 자바 객체 변환
 HelloData data = objectMapper.readValue(messageBody, HelloData.class);
 log.info("username={}, age={}", data.getUsername(), data.getAge());

V3


 /* @RequestBody 생략 불가능(@ModelAttribute 가 적용되어 버림)
 *  HttpMessageConverter 사용 -> MappingJackson2HttpMessageConverter (content-type: application/json)
 */
public String requestBodyJsonV3(@RequestBody HelloData data) {

 log.info("username={}, age={}", data.getUsername(), data.getAge());

V4

public String requestBodyJsonV4(HttpEntity<HelloData> httpEntity) {
 HelloData data = httpEntity.getBody();
 log.info("username={}, age={}", data.getUsername(), data.getAge());

V5

// 객체 반환 -> JSON 응답
public HelloData requestBodyJsonV5(@RequestBody HelloData data) {
 return data;

주의
HTTP 요청시에 content-type이 application/json인지 확인 -> JSON을 처리할 수 있는 HTTP 메시지 컨버터가 실행


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


서버 응답 데이터

  • 정적 리소스 (HTML, css, js)
  • 뷰 템플릿 (웹 브라어주에 동적인 HTML 제공)
  • HTTP 메시지 (REST API JSON)

정적 리소스
/static , /public , /resources , /META-INF/resources

뷰 템플릿
src/main/resources/templates

String 반환

  • @ResponseBody x : 뷰 리졸버
  • @ResponseBody o : http 메시지 바디에 직접 문자 반환

void 반환

  • Http 메시지 바디를 처리하는 파라미터 없으면 요청 URL 참고해서 논리 뷰 이름으로 사용
    • 요청 url : /home/hello
    • 실행 : templates/home/hello.html

Thymeleaf 스프링 부트 설정

application.properties

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

✔️ HTTP 응답 - HTTP API, 메시지 바디에 직접 입력


String (단순 텍스트)

// V1
public void v1(HttpServletResponse response) throws IOEception {

	response.getWriter().write("ok);
}

// V2
public ResponseEntity<String> v2() {
	return new ResponseEntity<>("ok", HttpStatus.OK);
}
// V3
@ResponseBody
public String responseBodyV3() {
	return "ok";
}

Json

// V1
public ResponseEntity<HelloData> V1() {
 HelloData helloData = new HelloData();
 helloData.setUsername("userA");
 helloData.setAge(20);
 return new ResponseEntity<>(helloData, HttpStatus.OK);
 }

// V2
 @ResponseStatus(HttpStatus.OK)
 @ResponseBody
 public HelloData V2() {
 	HelloData helloData = new HelloData();
 	helloData.setUsername("userA");
 	helloData.setAge(20);
    
 	return helloData;
 }

✔️ HTTP 메시지 컨버터


  • @ResponseBody 를 사용
    • HTTP의 BODY에 문자 내용을 직접 반환
    • viewResolver 대신에 HttpMessageConverter 가 동작
    • 기본 문자처리: StringHttpMessageConverter
    • 기본 객체처리: MappingJackson2HttpMessageConverter
    • byte 처리 등등 기타 여러 HttpMessageConverter가 기본으로 등록되어 있음

스프링 MVC는 다음의 경우에 HTTP 메시지 컨버터를 적용한다.

  • HTTP 요청: @RequestBody , HttpEntity(RequestEntity)
  • HTTP 응답: @ResponseBody , HttpEntity(ResponseEntity)

RequestMappingHandlerAdapter 동작 방식

HTTP 메시지 컨버터 위치

더 자세한 내용은 강의와 교재를 참고


🔖 학습내용 출처

스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술

profile
열심히 살자

0개의 댓글