[Spring MVC] 5. 스프링 MVC - 기본 기능(1)

2023. 6. 22. 13:49·🍃 Spring, Spring Boot/스프링 MVC

📝 로깅 간단히 알아보기

  • 앞으로 로그를 사용할 것이기 때문에, 로그에 대해서 간단히 알아보자.
  • 운영 시스템에서는 System.out.println() 같은 시스템 콘솔을 사용해서 필요한 정보를 출력하지 않고, 별도의 로깅 라이브러리를 사용해서 로그를 출력한다.
  • 참고로 로그 관련 라이브러리도 많고, 깊게 들어가면 끝이 없기 때문에, 여기서는 최소한의 사용 방법만 알아본다.

 

📜 로깅 라이브러리

  • 스프링 부트 라이브러리를 사용하면 스프링 부트 로깅 라이브러리(spring-boot-starter-logging)가 함께 포함된다.
  • 스프링 부트 로깅 라이브러리는 기본으로 다음 로깅 라이브러리를 사용한다.
    • SLF4J - http://www.slf4j.org
    • Logback - http://logback.qos.ch
  • 로그 라이브러리는 Logback, Log4J, Log4J2 등등 수 많은 라이브러리가 있는데, 그것을 통합해서 인터페이스로 제공하는 것이 바로 SLF4J 라이브러리다.
  • 쉽게 이야기해서 SLF4J는 인터페이스이고, 그 구현체로 Logback 같은 로그 라이브러리를 선택하면 된다.
  • 실무에서는 스프링 부트가 기본으로 제공하는 Logback을 대부분 사용한다.

 

📜 로그 선언 방법

  • private Logger log = LoggerFactory.getLogger(getClass());
  • private static final Logger log = LoggerFactory.getLogger(Xxx.class);
  • @Slf4j: 롬복 사용 가능

 

📜 로그 호출 방법

  • log.trace("trace log");
  • log.debug("debug log");
  • log.info("info log");
  • log.warn("warn log");
  • log.error("error log");

 

📜 테스트

//@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);

        return "ok";
    }
}
  • @RestController
    • @Controller는 반환 값이 String이면 뷰 이름으로 인식된다. 그래서 뷰를 찾고 뷰가 랜더링 된다.
    • @RestController는 반환 값으로 뷰를 찾는 것이 아니라, HTTP 메시지 바디에 바로 입력한다.
    • 따라서 실행 결과로 "ok" 메세지를 받을 수 있다. 이는 @ResponseBody와 관련이 있는데, 뒤에서 더 자세히 설명한다
  • 로그가 출력되는 포멧을 확인해보면 다음과 같다.
    • [시간] [로그 레벨] [프로세스 ID] [쓰레드 명] [클래스 명] [로그 메시지]
2023-06-22 12:32:53.026  INFO 12536 --- [nio-8080-exec-1] hello.springmvc.basic.LogTestController   :   info log=Spring

 

📜 로그 레벨 설정

  • 로그 레벨 설정을 변경해서 출력 결과를 확인해보자.
# 전체 로그 레벨 설정(기본 info)
logging.level.root=info

# hello.springmvc 패키지와 그 하위 로그 레벨 설정
logging.level.hello.springmvc=debug
  • 로그 레벨에는 다음 5가지가 존재한다.
    • TRACE > DEBUG > INFO > WARN > ERROR
    • 개발 서버는 주로 DEBUG를 출력하고
    • 운영 서버는 주로 INFO를 출력한다.
2023-06-22 12:32:53.026 DEBUG 12536 --- [nio-8080-exec-1] hello.springmvc.basic.LogTestController   :   debug log=Spring
2023-06-22 12:32:53.026  INFO 12536 --- [nio-8080-exec-1] hello.springmvc.basic.LogTestController   :   info log=Spring
2023-06-22 12:32:53.027  WARN 12536 --- [nio-8080-exec-1] hello.springmvc.basic.LogTestController   :   warn log=Spring
2023-06-22 12:32:53.028 ERROR 12536 --- [nio-8080-exec-1] hello.springmvc.basic.LogTestController   :   error log=Spring

 

📜 올바른 로그 사용법(❗)

log.trace("trace log=" + name);
log.info(" info log={}", name);
  • 로그 출력 레벨을 INFO로 설정한 상태에서 위처럼 로그를 사용하면, 당연히 TRACE 로그는 출력되지 않겠지만 해당 코드에 있는 "trace log=" + name 연산, 즉 문자 더하기 연산은 발생하게 된다.
  • 이는 다시말해 불필요한 연산이 발생하여 리소스가 낭비된다는 것을 의미한다.
  • 따라서 다음과 같이 사용하는 것이 바람직하다.
log.trace("trace log={}", name);
log.info(" info log={}", name);
  • 이렇게 하면 메서드에 파리미터만 넘김으로써 아무런 연산이 일어나지 않는다. 메서드 내부에서 TRACE임을 파악하고 그 시점에 바로 로직을 중단해버리기 때문이다.

 

📜 로그 사용시 장점

  • 쓰레드 정보, 클래스 이름 같은 부가 정보를 함께 볼 수 있고, 출력 모양을 조정할 수 있다.
  • 로그 레벨에 따라 개발 서버에서는 모든 로그를 출력하고, 운영 서버에서는 출력하지 않는 등 로그를 상황에 맞게 조절할 수 있다.
  • 시스템 아웃 콘솔에만 출력하는 것이 아니라, 파일이나 네트워크 등, 로그를 별도의 위치에 남길 수 있다. 특히 파일로 남길 때는 일별, 특정 용량에 따라 로그를 분할하는 것도 가능하다.
  • 성능도 일반 System.out보다 좋다. (내부 버퍼링, 멀티 쓰레드 등등) 그래서 실무에서는 꼭 로그를 사용해야 한다.

📝 요청 매핑

  • 요청 매핑이란 사용자 요청이 들어왔을 때 어떠한 컨트롤러가 호출되어야 하는지를 매핑하는 것을 말한다.
  • 간단한 컨트롤러를 작성해서 확인해보자.
@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";
    }
}
  • @RequestMapping("/hello-basic")
    • /hello-basic URL 호출이 오면 이 메서드가 실행되도록 매핑한다.
    • 대부분의 속성을 배열로 제공하므로 다중 설정이 가능하다. {"/hello-basic", "/hello-go"}
스프링 부트 3.0 이전에는 다음 두 가지 URL에 대해 서로 같은 요청으로 매핑하였다
/hello-basic, /hello-bacis/ → /hello/basic
스프링 부트 3.0 이후부터는 위의 두 가지 URL 모두 서로 다른 요청으로 매핑한다. 즉, 기존에는 마지막에 있는 slash(/)를 제거했지만, 스프링 부트 3.0 이후부터는 마지막의 slash(/)를 유지한다. 따라서 다음과 같이 다르게 매핑해서 사용해야 한다.
/hello-basic → /hello-basic
/hello-basic/ → /hello-basic/

 

📜 HTTP 메서드

  • @RequestMapping에 method 속성으로 HTTP 메서드를 지정하여 원하는 요청을 받을 수 있다.
  • 만약 method 속성을 따로 지정해주지 않는다면 HTTP 메서드와 무관하게 호출된다.
/**
* 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 메서드를 축약한 애노테이션을 사용하는 것이 더 직관적이다. 아래 코드를 보면 내부에서 @RequestMapping과 method를 지정해서 사용하는 것을 확인할 수 있다.
/**
* 편리한 축약 애노테이션 (코드보기)
* @GetMapping
* @PostMapping
* @PutMapping
* @DeleteMapping
* @PatchMapping
*/
@GetMapping(value = "/mapping-get-v2")
public String mappingGetV2() {
    log.info("mapping-get-v2");
    return "ok";
}

 

📜 PathVariable(❗)

  • 최근 HTTP API는 다음과 같이 리소스 경로에 식별자를 넣는 스타일을 선호한다.
    • /mapping/userA
    • /users/1
  • @RequestMapping은 URL 경로를 템플릿화 할 수 있는데, @PathVariable을 사용하면 매칭 되는 부분을 편리하게 조회할 수 있다.
/**
* PathVariable 사용
* 변수명이 같으면 생략 가능
* @PathVariable("userId") String userId -> @PathVariable String userId
*/
@GetMapping("/mapping/{userId}")
public String mappingPath(@PathVariable("userId") String data) {
    log.info("mappingPath userId={}", data);
    return "ok";
}
  • @PathVariable의 이름과 파라미터 이름이 같으면 생략할 수 있다.
@GetMapping("/mapping/{userId}")
public String mappingPath(@PathVariable String userId) {
    log.info("mappingPath userId={}", data);
    return "ok";
}
  • 다음과 같이 다중으로 사용할 수도 있다.
/**
* 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";
}

 

📜 특정 파라미터 조건 매핑

  • 특정 파라미터가 있거나 없는 조건을 추가할 수 있다. 잘 사용하지는 않는다.
/**
* 파라미터로 추가 매핑
* 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";
}
  • 위처럼 구현할 경우 파라미터로 "mode=debug"가 포함되어 있어야 해당 메서드가 호출된다.

 

📜 특정 헤더 조건 매핑

  • 특정 헤더가 있거나 없는 조건을 추가할 수 있다.
  • 파라미터 매핑과 비슷하지만, HTTP 헤더를 사용한다.
/**
* 특정 헤더로 추가 매핑
* 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

  • HTTP 요청의 Content-Type 헤더를 기반으로 미디어 타입으로 매핑한다.
  • 만약 맞지 않으면 HTTP 415 상태코드(Unsupported Media Type)를 반환한다.
/**
* 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";
}

 

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

  • HTTP 요청의 Accept 헤더를 기반으로 미디어 타입으로 매핑한다.
  • 만약 맞지 않으면 HTTP 406 상태코드(Not Acceptable)을 반환한다.
/**
* 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";
}

📝 요청 매핑 - API 예시

  • 회원 관리를 HTTP API로 만든다 생각하고 매핑을 어떻게 하는지 알아보자. (실제 데이터가 넘어가는 부분은 생략하고 URL 매핑만)

 

📜 회원 관리 API

  • 회원 목록 조회: GET /users
  • 회원 등록: POST /users
  • 회원 조회: GET /users/{userId}
  • 회원 수정: PATCH /users/{userId}
  • 회원 삭제: DELETE /users/{userId}
@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;
    }
}
  • @RequestMapping("/mapping/users")
    • 이전 시간에 설명했지만, 클래스 레벨에 매핑 정보를 두면 메서드 레벨에서 해당 정보를 조합해서 사용한다.
    • 따라서 요청은 다음과 같이 진행할 수 있다.
      • 회원 목록 조회: GET /mapping/users
      • 회원 등록: POST /mapping/users
      • 회원 조회: GET /mapping/users/id1
      • 회원 수정: PATCH /mapping/users/id1
      • 회원 삭제: DELETE /mapping/users/id1

📌 Reference

  • https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-1/dashboard
저작자표시

'🍃 Spring, Spring Boot > 스프링 MVC' 카테고리의 다른 글

[Spring MVC] 7. 스프링 MVC - 기본 기능(3)  (0) 2023.06.22
[Spring MVC] 6. 스프링 MVC - 기본 기능(2)  (0) 2023.06.22
[Spring MVC] 4. 스프링 MVC 구조 이해하기  (0) 2023.06.19
[Spring MVC] 3. MVC 프레임워크 만들기  (0) 2023.06.18
[Spring MVC] 2. MVC 패턴  (0) 2023.06.16
'🍃 Spring, Spring Boot/스프링 MVC' 카테고리의 다른 글
  • [Spring MVC] 7. 스프링 MVC - 기본 기능(3)
  • [Spring MVC] 6. 스프링 MVC - 기본 기능(2)
  • [Spring MVC] 4. 스프링 MVC 구조 이해하기
  • [Spring MVC] 3. MVC 프레임워크 만들기
Baeg-won
Baeg-won
  • Baeg-won
    좋았다면 추억이고 나빴다면 경험이다.
    Baeg-won
  • 전체
    오늘
    어제
    • 분류 전체보기
      • 🍃 Spring, Spring Boot
        • 스프링 프레임워크 기초
        • 스프링 핵심 원리 - 기본편
        • 자바 ORM 표준 JPA 프로그래밍 - 기본편
        • 스프링 MVC
        • 실전! 스프링 부트와 JPA 활용1 - 웹 애플리..
      • 🥑 Web Technoloy
      • 🚗 Backend Toy Project
        • 스프링 부트 게시판
        • Photogram
        • Baeg-won Clothing Gallery
      • 🥇 Problem Solving
        • Breadth-First Search
        • Depth-First Search
        • Backtracking
        • Simulation
        • Two-pointer
        • Binary Search
        • Greedy
        • Dynamic Programming
        • Minimum Spanning Tree
        • Dijkstra
        • Floyd warshall
      • ☕ Java
        • 명품 자바 에센셜
        • Applications
      • 🍦 JavaScript
        • JavaScript 기초
      • 🐧 Linux
        • 이것이 리눅스다(CentOS 8)
      • 📟 Database
        • 혼자 공부하는 SQL
      • 🧬 Data Structure
      • 🎬 HTML
      • 🎤 Tech Interview
      • 📌 etc
        • Unity 2D Raising Jelly Game
        • C++
        • 영어 쉐도잉
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
Baeg-won
[Spring MVC] 5. 스프링 MVC - 기본 기능(1)
상단으로

티스토리툴바