🧑🏫 목표
- Spring이 지원하는 다양한 기능과 파라미터 매핑, 애노테이션 활용 방법을 학습한다
Slf4j는 인터페이스이고 그 구현체로 Logback같은 라이브러리를 선택한다. 실제 개발에서는 Spring Boot가 기본으로 제공하는 Logback을 대부분 사용
System.out.println(); 대신 별도의 로깅 라이브러리를 사용하여 로그를 출력TRACE > DEBUG > INFO > WARN > ERRORapplication.properties# com.example.springbasicannotation 하위 경로들의 로그 레벨을 설정한다.
logging.level.com.example.springbasicannotation=TRACE
설정하지 않으면 Default 레벨인 INFO 로 설정된다.
→ 본인 포함 하위 레벨인 INFO > WARN > ERROR 만 출력
➕) log는 코드가 실행되는 시점에 맞춰서 문자 연산을 실행한다.
🤨 문제가 되는 이유?
로그 레벨이 낮아서 출력되지 않는 로그의 문자 연산도 실행하여 불필요한 동작을 수행할 수 있다.
log.info("문자 info={}", "info"); // 문자 연산을 진행하지 않는다.
log.trace("문자 trace " + "trace"); // 문자 연산을 먼저 해버린다.
{})를 포함한 문자열👉 위와 같이 매개변수로 치환하는 형태로 로그를 작성하자!
Annotation 기반의 Spring에서 Controller(Handler)를 만들 때 사용하는 어노테이션
package com.example.springbasicannotation.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class ViewController {
@RequestMapping("/view")
public String example() {
// logic
return "sparta"; // ViewName이 return
}
}
여기서 반환된 View는 Thymeleaf로 작성되어 View와 Resolver가 이미 존재한다. → 의존성만 추가하면 Spring Boot가 알아서 View Rendering 해준다.
build.gradle 의존성 추가dependencies {
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
...
}
👉 main/resources/templates 가 기본 경로로 설정된다.
↳ 해당 경로에 존재하는 Thymeleaf 코드
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Hello</title>
</head>
<body>
<h2>Thymeleaf Template Sample</h2>
</body>
</html>

ThymeleafViewResolver에 의해 View Name으로 인식
package com.example.springbasicannotation.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ResponseController {
@RequestMapping("/string")
public String example() {
// logic
return "sparta"; // ViewName이 return 되는게 아니라, String Data가 반환된다.
}
}
💡 View가 아닌 HTTP Message Body에 Data가 들어가는 이유는 아래에서 배울
@Responsebody와 관련이 있다.

@IndexedTYPE(Class, Interface, annotation, enum, recode),FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, etc@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface Component {
String value() default "";
}
= 클래스 상위에 @Component 어노테이션을 사용하면 그 하위의 클래스는 Component로 등록된다.
SOURCE: 소스코드(.java)에서만 유지 컴파일러에 의해서 클래스 파일로 저장되지 않는다,CLASS: 컴파일된 클래스 파일(.class)에 저장되지만, JVM이 실행 시 읽지 않는다.(주석과 같음),RUNTIME: 클래스 파일(.class)에 저장되고, JVM에 의해 런타임 시점에 읽을 수 있다. 실제 런타임 시점 코드에 반영되어 영향을 준다.👉 @Component을 그대로 읽어들이는 것이 아니라 @Retention을 확인하고 Spring Bean으로 등록할지 결정
개발에서 우선순위는 항상 자세히 선언된것이 우선순위가 높다.
(RestController 는 Controller를 구체화한 것이다.)

@Target(ElementType.Type)@Retention(RetentionPolicy.RUNTIME)@Document@Component

특정 URL로 Request를 보내면 들어온 요청을 Controller 내부의 특정 Method와 Mapping 하기 위해 사용
💡 Client로부터 요청이 왔을 때 어떤 Controller가 호출될지 Mapping하는것은 단순히 URL로 Mapping 하는것이 아니라 여러가지 요소(URL, Method 등)를 조합하여 Mapping
Spring Boot 3.0 버전 이하
/example, /example**/** 모두 허용(Mapping)한다.Spring Boot 3.0 버전 이상(현재 버전)
/example 만 허용(Mapping)한다.속성값들을 설정할 때 배열 형태로 다중 설정이 가능하다
ex) @RequestMapping({”/example”, “/example2”, “/example3”})
HTTP Method POST, GET, PUT, PATCH, DELETE, HEAD 모두 허용한다
method 속성으로 HTTP 메서드를 지정하면 지정된것만 허용한다.
package com.example.springbasicannotation.controller;
import org.springframework.web.bind.annotation.*;
// 응답 데이터를 반환한다.
//@RequestMapping("/prefix")
@RestController
public class RequestMappingController {
// HTTP Method 는 GET만 허용한다.
@RequestMapping(value = "/v1", method = RequestMethod.GET)
public String exampleV1() {
// logic
return "this is sparta!";
}
// Post, GET, Put, Patch, Delete 모두 가능
@GetMapping(value = "/v2")
public String exampleV2() {
// logic
return "this is sparta!";
}
// @PostMapping(value = "/v2")
// public String exampleV2() {
// // logic
// return "this is sparta!";
// }
//
// @PutMapping(value = "/v2")
// public String exampleV2() {
// // logic
// return "this is sparta!";
// }
//
// @PatchMapping(value = "/v2")
// public String exampleV2() {
// // logic
// return "this is sparta!";
// }
//
// @DeleteMapping(value = "/v2")
// public String exampleV2() {
// // logic
// return "this is sparta!";
// }
// // Post, GET, Put, Patch, Delete 모두 가능
// @GetMapping(value = "/v3")
// public String exampleV3() {
// // logic
// return "this is sparta!";
// }
}
👉 속성으로 설정된 HTTP Method로 요청이 아닌 다른 Method로 요청하면?

405 Method Not Allowed 클라이언트 오류 발생
@RequestMapping을 Method 속성 지정을 편리하게 사용하기 위해 만듦 → 직관적이고 축약됨
@GetMapping()@PostMapping()@PutMapping()@PatchMapping()@DeleteMapping()예) @GetMapping

👉 매개변수로 URL 값만 넘기는 경우 value= 생략 가능
@GetMapping(value = "/v2")
@GetMapping("/v2")
🤨 그럼 @RequestMapping 은 언제 쓰나요?

@RequestMapping("/prefix")
@RestController
public class RequestMappingController {
// Post, GET, Put, Patch, Delete 모두 가능
@GetMapping(value = "/v3")
public String exampleV3() {
// logic
return "this is sparta!";
}
}

HTTP 특성 중 하나인 비연결성을 극복하여 데이터를 전달하기 위한 방법 중 하나이다. URL로 전달된 값을 파라미터로 받아오는 역할을 수행한다.
경로 변수를 중괄호에 둘러싸인 값으로 사용할 수 있다.
ex) user/{id}
기본적으로 @PathVariable로 설정된 경로 변수는 반드시 값을 가져야 하며 값이 없으면 응답 상태코드 404 Not Found Error가 발생한다.
최근 Restful API를 설계하는 것이 API의 기준이 되며 해당 어노테이션의 사용 빈도가 높아졌다.
💡 Restful API를 설계하게 되면 URL path 만으로 어떤 Resource을 사용하는지, HTTP Method 만으로 어떤 기능이 동작되는지 쉽게 알아볼 수 있다.
posts/{postId}/commentsposts/{postId}/commentsposts/{postId}/comments/{commentId}posts/{postId}/comments/{commentId}posts/{postId}/comments/{commentId}파라미터 변수명과 PathVariable 변수명이 같으면 속성 값 생략 가능
@RequestMapping("/posts")
@RestController
public class PathVariableController {
// postId로 된 post 단건 조회
@GetMapping("/{postId}")
public String pathVariableV1(@PathVariable("postId") Long data) {
// logic
String result = "PathvariableV1 결과입니다 : " + data;
return result;
}
}
// 변수명과 같다면 속성값 생략가능
@GetMapping("/{postId}")
public String pathVariableV2(@PathVariable Long postId) {
// logic
String result = "PathvariableV2 결과입니다 : " + postId;
return result;
}
@PathVariable 다중 사용 가능
@GetMapping("/{postId}/comments/{commentId}")
public String pathVariableV3(
@PathVariable Long postId,
@PathVariable Long commentId
) {
// logic
String result = "PathvariableV3 결과입니다 postId : " + postId + "commentsId : " + commentId;
return result;
}
@RequestMapping + @GetMapping 에 @PathVariable 다중 사용
@RequestMapping("/posts/{postId}")
@RestController
public class PathVariableController {
@GetMapping("/comments/{commentId}")
public String pathVariableV4(
@PathVariable Long postId,
@PathVariable Long commentId
) {
// logic
String result = "PathvariableV4 결과입니다 postId : " + postId + "commentsId : " + commentId;
return result;
}
}
속성 설정을 통하여 특정 헤더, 특정 파라미터와 Mapping 할 수 있다.
@RestController
public class ParameterController {
// parms 속성값 추가
@GetMapping(value = "/users", params = "gender=man")
public String params() {
// logic
String result = "params API가 호출 되었습니다.";
return result;
}
Query String (Query Parameter)의 값과 params의 값 비교
params = "gender"params = "!gender"params = "gender=man"params = "gender!=man" params = {"gender=man", "gender=woman"}속성 작성 규칙에 위배되는 요청이 들어오면 서버는 400 Bad Request 반환
@RestController
public class ParameterController {
// headers 속성값 추가
@PostMapping(value = "/users", headers = "Content-Type=application/json")
public String headers() {
// logic
String result = "headers API가 호출 되었습니다.";
return result;
}
}
HTTP Header Content-Type(요청)과 매핑 된다.
// consumes 속성값 추가
@PostMapping(value = "/users", consumes = "application/json") // MediaType.APPLICATION_JSON_VALUE
public String consumes() {
// logic
String result = "consumes API가 호출 되었습니다.";
return result;
}
consumes 속성 value값으로는 이미 Spring에서 제공되는 Enum인 MediaType.APPLICATION_JSON_VALUE 형태로 사용

파라미터가 없거나 다르다면?

consumes=”application/json”consumes=”!application/json”consumes=”application/*” consumes=”*\/*”요청 헤더의 Accept 값에 따라서 produces 하는 값이 변한다.
@RestController
public class ParameterController {
// produces 속성값 추가
@GetMapping(value = "/users", produces = "text/plain")
public String produces() {
// logic
String result = "text/plain 데이터 응답";
return result;
}
}

@Controller 의 사용 가능한 파라미터 목록
Method Arguments
@Controller 의 사용 가능한 Response 목록
Return Values
// 로깅
@Slf4j
@RestController
public class RequestHeaderController {
@GetMapping("/request/headers")
public String headers(
HttpServletRequest request, // Servlet에서 사용한것과 같음
HttpServletResponse response, // Servlet에서 사용한것과 같음
@RequestHeader MultiValueMap<String, String> headerMap,
@RequestHeader("host") String host,
@CookieValue(value = "cookie", required = false) String cookie,
HttpMethod httpMethod,
Locale locale
) {
// Servlet
log.info("request={}", request);
log.info("response={}", response);
// @RequestHeader
log.info("headerMap={}", headerMap);
log.info("host={}", host);
// @CookieValue
log.info("cookie={}", cookie);
// HttpMethod
log.info("httpMethod={}", httpMethod);
// Locale
log.info("Locale={}", locale);
return "success";
}
}

💡 Map과 유사하게 Key, Value 형식으로 구현되어 있지만 하나의 Key가 여러 Value를 가질 수 있다 HTTP Header, Reqeust Parameter와 같이 하나의 Key에 여러 값을 받을 때 사용한다.
ex) key1=value1&key1=value2