[Spring - MVC] MVC -기본 기능 part1 (7)

조대훈·2024년 2월 27일

김영한 Spring MVC -1

목록 보기
7/9
post-thumbnail

간단한 로깅 알아보기

로깅 라이브러리

스프링 부트 라이브러리를 사용하면 스프링 부트 로깅 라이브러리 spring-boot-stater-logging이 함께 포함 된다. 스프링 부트 로깅 라이브러리는 다음 로깅 라이브러리를 사용한다

@Slf4j
Logback

로그 라이브러리는 Logback Log4J 등 여러가지가 있는데 통합해 제공하는 인터페이스가 바로 SLF4J 라이브러리다.

SLF4J는 인터페이스 -> 구현체 Logback

@RestController  
public class LogTestController {  
    private final Logger logger = LoggerFactory.getLogger(LogTestController.class);  
  
    @RequestMapping("/log-test")  
    public String logtest() {  
        String name = "Spring";  
        System.out.println("name = " + name);  
        logger.info("info log={}", name);  
  
        return "ok";  
    }  
  
}
  • @RestContoller : RestAPI 의 Rest 이며 반환값이 HttpMessageBody에 담아서 그대로 return 된다.
    - 위의 예제에서는 ok 가 그대로 반환된다.
    - 반환 값이 뷰가 아닌 바디에 바로 입력한다

  • @Controller 는 반환 값이 String 이면 뷰 이름으로 인식된다. 그래서 뷰를 찾고 뷰가 랜더링 된다.

  • 뷰를 찾는 것이 아니라, HTTP 메시지 바디에 바로 입력한다. 따라서 실행 결과로 ok 메세지를 받을 수 있다

로깅 사용법

name = Spring //일반적인 sout
2024-02-27T11:42:13.112+09:00  INFO 43119 --- [nio-8080-exec-2] h.springmvc.basic.LogTestController 

로거를 찍은 부분은 시간, INFO 프로세스ID --- [현재실행한 쓰레드] 컨트롤러 이름: 메세지 전부 출력 된다

  • 시간
  • 로그 레벨
  • 프로세스 ID
  • 쓰레드 명
  • 클래스 명
  • 로그 메세지
logger.info("info log={}", name);  
logger.trace("trace log={}", name);  
logger.debug("debug log={}", name);  
logger.info("info log={}", name);  
logger.warn("warn log={}", name);  
logger.error("error log={}", name);

로그를 찍을때 레벨을 정할 수 있다. 가령 현재 정보는 운영시스템에서 봐야하는 중요한 정보야, 경고 메세지야, 상세야.

로그 레벨

레벨
1. TRACE
2. DEBUG
3. INFO
4. WARN
5. ERROR

패키지 별로 출력하는 로그 레벨 별도 설정

application.prorperties


#전체 로그 레벨 설정 (기본 info)
logging.level.root=info

#hello.springmvc 패키지와 그 하위 로그 레벨 설정  
logging.level.hello.springmvc=trace

이미 스프링은 기본 logging level이 info로 설정되어 있는데 임의로 debug로 바꿀 시에 수많은 log가 출력되는 것을 볼수 있다.

위와 같은 식으로 각 패키지 별로 log가 출력되는 레벨을 각기 따로 설정할 수 있다.
System.print.out 은 로그가 전부 남아서 실무에서는 절대 쓰지 않는다. 실무에서 적용시 수만건의 로그 폭탄을 맞을 수 있다.

올바른 로그 사용법

  • log.debug("data="+dataa)
    - 로그 출력 레벨을 ino로 설정해도 해당 코드에 있는 "data="+data가 실행이 되어 버린다. 결과적으로 문자 더하기 연산이 발생한다.
  • log.debug("data={}",data)
    - 로그 출력 레벨을 ino로 설정하면 아무일도 발생하지 않는다. 따라서 앞과 같은 의미없는 연산이 발생하지 않는다.

@Slf4j

lombok 어노테이션

private final Logger logger = LoggerFactory.getLogger(LogTestController.class);

위의 코드를 어노테이션 하나로 축약할 수 있다. 바로 로그를 쓰면 된다.

로그 사용시 장점

  • 쓰레드 정보, 클래스 이름 같은 부가정보 출력 설정과 출력 모양 또한 조정할 수 있다.
  • 로그 레벨에 따라 개발서버에서는 모든 로그를 출력하고, 운영서버에서는 출력하지 않는 등 상황에 맞게 조절이 가능하다.
  • 콘솔 출력 뿐만 아니라 로그 기록을 보관이 가능하다. 특히 파일로 남길 때엔 일별, 특정 용량에 따라 로그를 분할 할수 있다.
  • 성능도 Syste.out.println() 보다 우수하다.
    - 내부 버퍼링, 멀티 쓰레드 등

요청 매핑

HTTP 메서드 매핑

@RequestMapping(method = RequestMethod.GET)  
public String mappingGetV1() {  
    log.info("mappingGetV1");  
    return "ok";  
}

HTTP 메서드 매핑 축약

  
@GetMapping({"/hello-basic", "hello-go"})  
public String helloBasic() {  
  
    log.info("helloBasic");  
    return "ok";
    }
  1. @PostMapping
  2. @PutMapping
  3. @DeleteMapping
  4. @PatchMapping

PathVariable

  • @RequestMapping 은 URL 경로를 템플릿화 할 수 있는데, @PathVariable 을 사용하면 매칭 되는 부분을 편리하게 조회할 수 있다.

  • @PathVariable 의 이름과 파라미터 이름이 같으면 생략할 수 있다.

@GetMapping("/mapping/users/{userId}/orders/{orderId}")  
public String mappingPath(@PathVariable String userId, @PathVariable Long orderId) {  
    log.info("mappingPath userId={}, orderId={}", userId, orderId);  
  
    return "ok";  
}
### GET request with a header
GET http://localhost:8080/mapping/DAEHUN

2024-02-27T12:38:46.039+09:00  INFO 43442 --- [nio-8080-exec-1] h.s.b.requestmapping.MappingController   : mappingPath userId=DAEHUN

http://localhost:8080/mapping/userA

http://localhost:8080/mapping/users/userA/orders/100

특정 파라미터 조건 매핑


* 파라미터로 추가 매핑  
* params="mode",  
* params="!mode"  
* params="mode=debug"  
* params="mode!=debug" (! = )  
* params = {"mode=debug","data=good"} */

 @GetMapping(value = "/mapping-param", params = "mode=debug")
 public String mappingParam() {

     log.info("mappingParam");

     return "ok";
 }

실행
http://localhost:8080/mapping-param?mode=debug

특정 헤더 조건 매핑

* 특정 헤더로 추가 매핑  
* headers="mode",  
* headers="!mode"  
* headers="mode=debug"  
* headers="mode!=debug" (! = ) */

 @GetMapping(value = "/mapping-header", headers = "mode=debug")
 public String mappingHeader() {

     log.info("mappingHeader");

return "ok";

}

미디어 타입 조건 매핑 - 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")
 public String mappingConsumes() {

     log.info("mappingConsumes");

     return "ok";
 }

Postman으로 테스트 해야 한다.
HTTP 요청의 Content-Type 헤더를 기반으로 미디어 타입으로 매핑한다.

만약 맞지 않으면 HTTP 415 상태코드(Unsupported Media Type)을 반환한다. 예시) consumes

 consumes = "text/plain"

 consumes = {"text/plain", "application/*"}

 consumes = MediaType.TEXT_PLAIN_VALUE

미디어 타입 조건 매핑 - HTTP 요청 Accept, produce

  • Accept 헤더 기반 Media Type * produces = "text/html"
  • produces = "!text/html"
  • produces = "text/*"
  • produces = "\/"
 @PostMapping(value = "/mapping-produce", produces = "text/html")
  public String mappingProduces() {
     log.info("mappingProduces");
     return "ok";
}

HTTP 요청의 Accept 헤더를 기반으로 미디어 타입으로 매핑한다. 만약 맞지 않으면 HTTP 406 상태코드(Not Acceptable)을 반환한다.

예시)

 produces = "text/plain"

 produces = {"text/plain", "application/*"}

 produces = MediaType.TEXT_PLAIN_VALUE

 produces = "text/plain;charset=UTF-8"

요청 매핑 - API 예시

회원 관리를 HTTP API로 만드는 예시

회원 관리 API

  • 회원 목록 조회 GET /users
  • 회원 등록 POST /users
  • 회원 조회 GET /users/{userId}
  • 회원 수정 PATCH `/users/{userId}'
  • 회원 삭재 DELEETE /users/{userId}
@RestController  
@RequestMapping("/mapping/users")  
public class MappingClassControllrr {  
  
    @GetMapping  
    public String user() {  
        return "get users";  
    }  
  
    @PostMapping  
    public String addUSer() {  
        return "post user";  
    }  
  
    @GetMapping("/{userId}")  
    public String findUser(@PathVariable String id ) {  
        return "get userId=" + id;  
    }  
  
    @PatchMapping("/{userId}")  
    public String updateUser(@PathVariable String userId) {  
        return "get userId=" + userId;  
    }  
  
    @DeleteMapping("/{userId}")  
    public String deleteUser(@PathVariable String userId) {  
        return "delete userId=" + userId;  
    }  
}

HTTP 요청 - 기본, 헤더 조회

어노테이션 기반의 스프링 컨트롤러는 다양한 파라메터를 지원한다.

@GetMapping("/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";  
  
}
### GET request with a header  
GET http://localhost:8080/headers

  • HttpServletRequest
  • HttpServletResponse
  • HttpMethod : HTTP 메서드를 조회한다. org.springframework.http.HttpMethod Locale : Locale 정보를 조회한다.
  • @RequestHeader MultiValueMap<String, String> headerMap
    - 모든 HTTP 헤더를 MultiValueMap 형식으로 조회한다.
  • 특정 HTTP 헤더를 조회한다.
    - @RequestHeader("host") String host
  • 필수 값 여부: required
  • 기본 값 속성: defaultValue
  • 특정 쿠키를 조회한다. 속성
    - @CookieValue(value = "myCookie", required = false) String cookie
  • 필수 값 여부: required 기본 값: defaultValue
  • MAP과 유사한데, 하나의 키에 여러 값을 받을 수 있다.
  • HTTP header, HTTP 쿼리 파라미터와 같이 하나의 키에 여러 값을 받을 때 사용한다.
  • keyA=value1&keyA=value2
MultiValueMap<String,String> map = new LinkedMultiValueMap();

map.add("keyA","value1");
map.add("keyA","value2");

//[value1,value2]
List<String>value = map.get("keyA");

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

HTML 요청 데이터 조회 - 개요

HTTP 요청 메세지를 통해 클라이언트에서 서버로 데이터를 전달하는 방법 3가지

GET 쿼리 파라미터

  • /url**?username=hello&age=20
  • 메시지 바디 없이, URL의 쿼리 파라미터에 데이터를 포함해서 전달
  • 검색, 필터, 페이징에 적용
    POST-HTML Form
  • content-type:application/x-www-form-urlencoded
  • 메세지 바디에 쿼리파라미터 형식으로 전달
  • 회원가입, 상품주문에 적용
    HTTP message body에 직접 담아서 요청
  • HTTP API에서 주로 사용
    - JSON, XML, TEXT
  • 데이터 형식은 주로 JSON 사용
  • POST, PUT, PATCH

@RequestMapping("/request-param-v1")
	public void requestParamV1(HttpServlet request, HttpServletResponse response) throws IOException{

	String username= request.getParameter("username");
	int age= Integer.parseInt(request.getParameter("age"));
	log.info("username={}, name={}",username, name);


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

http://localhost:8080/request-param-v1?username=hello&age=20

Post Form 페이지 생성
먼저 테스트용 HTML Form을 만들어야 한다.

리소스는 /resources/static 아래에 두면 스프링 부트가 자동으로 인식한다. main/resources/static/basic/hello-form.html


 <!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> 

Post Form 실행 http://localhost:8080/basic/hello-form.html

참고
Jar 를 사용하면 webapp 경로를 사용할 수 없다. 이제부터 정적 리소스도 클래스 경로에 함께 포함해야 한다.

요약

  1. 로깅 라이브러리: 스프링 부트는 기본적으로 spring-boot-starter-logging을 포함하며, 이는 Logback을 사용한다. 로깅 라이브러리는 주로 SLF4J를 통해 통합되며, 로그 레벨에는 TRACE, DEBUG, INFO, WARN, ERROR가 있다.
  2. 로그 사용법: 로그 레벨에 따라 적절한 로그 메시지를 출력하고, 패키지별로 로그 레벨을 설정하여 관리할 수 있다. 올바른 로그 사용법으로 불필요한 문자열 연산을 피하고, 성능적인 이점을 얻을 수 있다.
  3. 요청 매핑: 스프링은 다양한 요청 매핑 기능을 제공한다. HTTP 메서드, URL 패턴, 특정 파라미터/헤더, 미디어 타입 등을 기반으로 매핑을 설정할 수 있다. 이를 통해 API를 설계하고 요청을 처리할 수 있다.
  4. HTTP 요청 데이터 조회: 클라이언트로부터의 HTTP 요청을 처리할 때, GET 방식의 쿼리 파라미터, POST 방식의 HTML Form 데이터, HTTP 메시지 바디를 통해 데이터를 전달받을 수 있다. 각각의 데이터 전달 방식에 따라 적절한 처리가 필요하다.
profile
백엔드 개발자를 꿈꾸고 있습니다.

0개의 댓글