조컴퓨터

프로젝트 생성과 로깅, 요청 매핑 본문

공부/Spring

프로젝트 생성과 로깅, 요청 매핑

챠오위 2022. 1. 28. 17:05

프로젝트 생성

https://start.spring.io 

Packaging: War / Jar

1. JSP 를 사용하지 않는다면 Jar 를 사용하는 것이 좋다. 

2. Jar 를 사용하면 항상 내장 서버(톰캣 등)을 사용하고, webapp 경로도 사용하지 않는다. 내장 서버 사용에 최적화되어 있는 기능이다. 최근에는 이 방식을 주로 사용한다.

3. War 를 사용하면 내장 서버도 사용 가능하지만, 주로 외부 서버에 배포하는 목적으로 사용한다.

   // 할 일: Jar 파일 ec2 서버에 올려서 톰캣 돌아가는지 확인

 

 


Welcome 페이지 만들기

스프링 부트에 Jar 를 사용하면 /resources/static 위치에 index.html 파일을 두면 Welcome 페이지로 처리해준다.

해당 위치가 스프링 부트가 지원하는 정적 컨텐츠 위치이다. 

 

참고) 스프링 부트 Welcome 페이지 지원

Core Features (spring.io)

 

Core Features

Spring Boot lets you externalize your configuration so that you can work with the same application code in different environments. You can use a variety of external configuration sources, include Java properties files, YAML files, environment variables, an

docs.spring.io

 


로깅

운영 시스템에서는 System.out.println() 같은 시스템 콘솔을 사용해서 필요한 정보를 출력하지 않고, 별도의 로깅 라이브러리를 사용해서 로그를 출력한다.

 

 

로깅 라이브러리

스프링 부트 라이브러리에 스프링 부트 로깅(spring-boot-starter-logging) 라이브러리를 포함하고 있다.

스프링 부트 로깅 라이브러리는 기본으로 아래의 로깅 라이브러리를 사용한다.

 

- SLF4J: http://www.slf4j.org 

 

SLF4J

Simple Logging Facade for Java (SLF4J) The Simple Logging Facade for Java (SLF4J) serves as a simple facade or abstraction for various logging frameworks (e.g. java.util.logging, logback, log4j) allowing the end user to plug in the desired logging framewor

www.slf4j.org

- Logback: http://logback.qos.ch 

 

Logback Home

Logback Project Logback is intended as a successor to the popular log4j project, picking up where log4j 1.x leaves off. Logback's architecture is quite generic so as to apply under different circumstances. At present time, logback is divided into three mod

logback.qos.ch

 

로그 라이브러리는 Logback, Log4J, Log4J2 등 수 많은 라이브러리가 있는데, 그것을 통합해서 인터페이스로 제공하는 것이 바로 SLF4J 라이브러리이다.

쉽게 이야기하면 SLF4J 는 인터페이스이고, 그 구현체로 Logback 과 같은 로그 라이브러리를 선택하면 된다. 

 

 

로그 호출

//@Slf4j
@RestController
public class LogTestController {

    private final Logger log = LoggerFactory.getLogger(getClass());

    @RequestMapping("/log-test")
    public String LogTest() {
        String name = "Spring";

        System.out.println("name = " + name);
        
        log.trace("trace my log=" + name);

        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 메시지 바디에 바로 입력한다. 

 

 

테스트

- 로그가 출력되는 포멧 확인

  - 시간, 로그 레벨, 프로레스ID, 쓰레드명, 클래스명, 로그 메시지

- 로그 레벨 설정

  - Level: Trace > Debug > Info > Warn > Error

  - 개발 서버: Debug

  - 운영 서버: Info

 

*application.properties

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

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

 

 

로그 차이점

log.trace("trace my log=" + name);

- 연산이 일어나는 것이 핵심이다.

- 연산이 일어나면서 메모리와 CPU 를 사용하는 것이 문제가 된다.

- 출력하지도 않을 trace 로그를 위해 쓸모 없는 리소스를 사용하기 때문이다.

- 문자가 합쳐진 최종 로그 모양을 만들어 trace 에게 넘겼지만, trace 가 출력을 하지 않고 끝낸다.

 

log.trace("trace log={}", name);

- 로그 출력 레벨을 debug 이하로 잡으면 아무 일도 발생하지 않는다. 따라서 앞과 같은 의미없는 연산이 이루어지지 않는다. 

 

 

로그 사용의 장점

- 쓰레드 정보, 클래스 이름과 같은 부가 정보를 확인할 수 있고, 출력 모양을 조절할 수 있다.

- 로그 레벨에 따라 개발 서버에서는 모든 로그를 출력하고, 운영 서버에서는 출력하지 않는 등 로그를 상황에 맞게 구성할 수 있다.

- 콘솔에만 출력하는 것이 아니라, 파일이나 네트워크 등, 로그를 지정한 위치에 남길 수 있다. 

- 내부 버퍼링, 멀티 쓰레드 등과 같은 이슈 때문에 성능이 일반 System.out 보다 좋다. 그래서 실무에서는 꼭 로그를 사용해야 한다.

 

 

 


요청 매핑

각각의 요청 매핑은 Postman 으로 테스트한다.

@RestController
public class MappingController {

    private Logger log = LoggerFactory.getLogger(getClass());

    @RequestMapping("/hello-basic")
    public String helloBasic() {
        log.info("helloBasic");
        return "ok";
    }

    @RequestMapping(value = "/mapping-get-v1", method = RequestMethod.GET)
    public String mappingGetV1() {
        log.info("mappingGetV1");
        return "ok";
    }

    /**
     * 편리한 축약 애노테이션 (코드보기)
     * @GetMapping
     * @PostMapping
     * @PutMapping
     * @DeleteMapping
     * @PatchMapping
     */
    @GetMapping(value = "/mapping-get-v2")
    public String mappingGetV2() {
        log.info("mapping-get-v2");
        return "ok";
    }

    /**
     * PathVariable 사용
     * 변수명이 같으면 생략 가능
     * @PathVariable("userId") String userId -> @PathVariable userId
     */
    @GetMapping("/mapping/{userId}")
    public String mappingPath(@PathVariable("userId") String data) {
        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";
    }

    /**
     * 특정 헤더로 추가 매핑
     * 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";
    }

    /**
     * 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";
    }

    /**
     * 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 예시

실제 데이터가 넘어가는 부분은 생략하고 URL 매핑

@RestController
@RequestMapping("/mapping/users")
public class MappingClassController {

    /**
     * 회원 목록 조회: GET    /users
     * 회원 등록:     POST   /users
     * 회원 조회:     GET    /users/{userId}
     * 회원 수정:     PATCH  /users/{userId}
     * 회원 삭제:     DELETE /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;
    }

}

// 할 일: URL 매핑을 통해서 member 테이블에 대한 매핑 진행하고, order 에 대한 매핑 진행해서 리팩터링

 

 

 

참고)

1. 김영한 님의 '스프링 MVC 1편 - 백엔드 웹 개발 핵심'에서 스프링 MVC - 기본 기능

 

 

 

 

'공부 > Spring' 카테고리의 다른 글

ResponseEntity  (0) 2022.01.29
#스프링 MVC - 구조 이해  (0) 2022.01.27
#MVC 프레임워크 만들기 - 유연한 컨트롤러 - v5  (0) 2022.01.27
#MVC 프레임워크 만들기 - v1, v2, v3, v4  (0) 2022.01.27
MVC 패턴  (0) 2022.01.26