새로운 Welcome 페이지 완성!
지금까지 실행 결과를 확인하거나 테스트를 하기 위해서 System.out.println()
을 이용해왔다.
하지만 이 명령어는 IO 리소스를 많이 사용해서 시스템이 느려질 수 있는 등 여러 단점이 있어서, 실무에서는 로깅 라이브러리를 이용해서 로그를 출력한다!
지금부터 로깅에 대해 더 자세히 알아보자!
스프링 부트 로깅 라이브러리(spring-boot-starter-loggin
)는 기본적으로 SLF4J
, Logback
을 사용한다.
SLF4J
는 Logback
, Log4J
, Log4J2
등 수많은 라이브러리를 통합해서 인터페이스를 제공하는 것으로, 이 인터페이스의 구현체로 Logback
을 대부분 사용한다.
//getClass() 메서드를 통해 사용되는 클래스 타입 반환하여 삽입한다.
private Logger log = LoggerFactory.getLogger(getClass());
//직접적으로 해당 클래스 타입을 입력해줘도 된다.
private static final Logger log = LoggerFactory.getLogger(Xxx.class);
간단하게 @Slf4j
애노테이션을 사용해도 된다.
LogTestController
package hello.springmvc.basic;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
//@Slf4j
@RestController
public class LogTestController {
private final Logger log = LoggerFactory.getLogger(getClass());
@RequestMapping("/log-test")
public String logTest() {
String name = "Spring";
log.trace("trace log={}", name);
log.debug("debug log={}", name);
log.info(" info log={}", name);
log.warn(" warn log={}", name);
log.error("error log={}", name);
//로그를 사용하지 않아도 a+b 계산 로직이 먼저 실행됨, 이런 방식으로 사용하면 X
log.debug("String concat log=" + name);
return "ok";
}
}
💡
@RestController
를 사용하는 이유!
@Controller
는 반환값이String
일 때, 뷰 이름으로 인식하기 때문에 뷰를 찾고 뷰가 렌더링된다.@RestController
는 반환값으로 뷰를 찾지 않고, HTTP 메세지 바디에 바로 입력한다.
시간, 로그 레벨, 프로세스 ID, 쓰레드 명, 클래스명, 로그 메시지가 출력된다.
📌 참고
- 로그 레벨을 설정하면 해당 로그보다 우선순위가 높은 것만 출력된다.
LEVEL
:TRACE
<DEBUG
<INFO
<WARN
<ERROR
- 스프링 부트에서
default
값은info
!
→info
보다 낮은trace
,debug
는 출력되지 않는다.
(로그 레벨을 바꾸고 싶다면application.propertires
에서 변경하면 된다.- 개발 서버는
debug
출력- 운영 서버는
info
출력
💡 요청 매핑
요청에 대해 어떤 컨트롤러에서 처리할지 매핑을 진행하는 것.
@RestController
public class MappingController {
private Logger log = LoggerFactory.getLogger(getClass());
@RequestMapping("/hello-basic")
public String helloBasic() {
log.info("helloBasic");
return "ok";
}
}
@RequestMapping("/hello-basic")
/hello-basic
URL 호출이 오면 이 메서드가 실행되도록 매핑한다.[ ]
로 제공하기 때문에 URL을 여러 개 설정할 수 있다. (Ex. {"/hello-basic", "/hello-go"}
)@RequestMapping(value = "/mapping-get-v1", method = RequestMethod.GET)
public String mappingGetV1() {
log.info("mappingGetV1");
return "ok";
}
@RequestMapping
에 method
속성으로 HTTP 메서드를 지정하지 않으면 메서드와 무관하게 호출된다.GET
으로만 호출 가능하다.@GetMapping(value = "/mapping-get-v2")
public String mappingGetV2() {
log.info("mapping-get-v2");
return "ok";
}
@RequestMapping
의 method
속성을 지정하지 않고 HTTP 메서드를 축약한 메서드도 있다. 훨씬 간편하고 직관적이다!
→ @GetMapping
, @PostMapping
, @PutMapping
, @DeleteMapping
, @PatchMapping
등
PathVariable
(경로 변수) 사용@GetMapping("/mapping/{userId}")
public String mappingPath(@PathVariable("userId") String data) {
log.info("mappingPath userId={}", data);
return "ok";
}
@RequestMapping
은 URL 경로를 템플릿화 할 수 있는데, @PathVariable
을 사용하면 매칭되는 부분을 편리하게 조회할 수 있다.@PathVariable
의 이름과 파라미터 이름이 같으면 생략할 수 있다.@PathVariable("username") String username
→ PathVariable String username
@GetMapping(value = "/mapping-param", params = "mode=debug")
public String mappingParam() {
log.info("mappingParam");
return "ok";
}
@GetMapping(value = "/mapping-header", headers = "mode=debug")
public String mappingHeader() {
log.info("mappingHeader");
return "ok";
}
HTTP
요청 Content-Type, consume
@PostMapping(value = "/mapping-consume", consumes = "application/json")
public String mappingConsumes() {
log.info("mappingConsumes");
return "ok";
}
HTTP
요청 Accept, produce
@PostMapping(value = "/mapping-produce", produces = "text/html")
public String mappingProduces() {
log.info("mappingProduces");
return "ok";
}
API
예시지금부터 회원 관리 웹 애플리케이션에 필요한 HTTP API 구조를 만들어보자!
package hello.springmvc.basic.requestmapping;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/mapping/users")
public class MappingClassController {
/**
* - 회원 목록 조회 : GET `/mapping/users`
* - 회원 등록 : POST `/mapping/users`
* - 회원 조회 : GET `/mapping/users/{userId}`
* - 회원 수정 : PATCH `/mapping/users/{userId}`
* - 회원 삭제 : DELETE `/mapping/users/{userId}`
* */
@GetMapping
public String user() {
return "get users";
}
@PostMapping
public String addUser() {
return "post user";
}
@GetMapping("/{userId}")
public String findUser(@PathVariable String userId) {
return "get userId=" + userId;
}
@PatchMapping("/{userId}")
public String updateUser(@PathVariable String userId) {
return "update userId=" + userId;
}
@DeleteMapping("/{userId}")
public String deleteUser(@PathVariable String userId) {
return "delete userId=" + userId;
}
}
모두 다 잘 된다~
HTTP
요청 - 기본, 헤더 조회package hello.springmvc.basic.requestmapping.request;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpMethod;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Locale;
@Slf4j
@RestController
public class RequestHeaderController {
@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";
}
}
HttpMethod
: HTTP 메서드를 조회한다.Locale
: Locale 정보를 조회한다.@RequestHeader MultiValueMap<String, String> headerMap
: 모든 HTTP 헤더를 MultiValueMap
형식으로 조회한다.RequestHeader("host") String host
: 특정 HTTP 헤더를 조회한다.@CookieValue(value = "myCookie", required = false) String cookie
: 특정 쿠키를 조회한다.