반응형
# 스프링 MVC 기본 기능
## 프로젝트 생성.
- 스프링 부트 스타터 사이트로 이동해서 스프링 프로젝트 생성 https://start.spring.io
- 아래와 같이 설정 후 GENERATE 진행.
Packaging : War가 아닌 Jar 선택 이유.
- JSP를 사용하지 않기 때문에 Jar를 사용하는 것이 좋음.
- 스프링 부트를 사용하면 이 방식을 주로 사용.
- Jar를 사용하면 항상 내장 서버(톰캣등)을 사용, webapp 경로도 사용하지 않는니다. 내장 서버 사용에 최적화 되어 있는 기능으로 최근에는 주로 이 방식을 사용.
- War를 사용하면 내장 서버도 사용가능 하지만, 주로 외부 서버에 배포하는 목적으로 사용.
build.gradle
plugins {
id 'org.springframework.boot' version '2.6.4'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
}
group = 'hello'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
tasks.named('test') {
useJUnitPlatform()
}
lombok 세팅
- File > Settings 에서 annotation processors 검색 후 Enable annotation processing 체크.
Port 변경.
- 8080 사용 중일 경우, run > edit configurations > Environment variable 에 아래와 같이 입력하여 port 변경.
server.port=8082
작동확인
설정 후 아래 클래스 진입하여 실행. (http://localhost:8080)
SpringmvcApplication
Welcome 페이지 생성.
- 스프링 부트에 Jar 사용 시 /resources/static/index.hml 위치에 index.html 파일을 두게되면 Welcome 페이지로 처리. (스프링 부트가 지원하는 정적 컨텐츠 위치에 /index.html 이 있으면 된다.)
## 로깅 간단히 알아보기 (최소한의 기능)
- 운영 시스템에서는 System.out.println() 같은 시스템 콘솔을 사용해서 필요한 정보를 출력하지 않고, 별도의 로깅 라이브러리를 사용해 로그를 출력.
- 로그 관련 라이브러리도 많고, 깊게 들어가면 끝이 없음. (Logback, Log4J, Log4J2 등 수 많은 라이브러리 존재)
로깅 라이브러리
- 스프링 부트 라이브러리를 사용하면 스프링 부트 로깅 라이브러리( 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.info("hello")
- System.out.println("hello") 시스템 콘솔로 직접 출력하는 것 보다 로그를 사용하면 다음과 같은 장점이 있다.
- 실무에서는 항상 로그를 사용해야 한다.
LogTestController.
package hello.springmvc.basic;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@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";
}
}
- System.out.println("hello") 의 경우 운영/개발 할것없이 항상 출력되므로 사용 하지 않는다. (로그 레벨 설정하여 로그 사용!!!!)
매핑 정보
- @RestController @Controller 는 반환 값이 String 이면 뷰 이름으로 인식. 그래서 뷰를 찾고 뷰가 랜더링 된다.
- @RestController 는 반환 값으로 뷰를 찾는 것이 아닌, HTTP 메시지 바디에 바로 입력. 따라서 실행 결과로 ok 메세지를 받을 수 있다.
로그 레벨 설정 (application.properties 에 입력) -> 기본 : info
- 모든 로그를 출력 할 경우 trace
# hello.springmvc 패키지와 그 하위 로그 레벨 설정
logging.level.hello.springmvc=trace
- trace 제외 debug 하위 레벨 출력 할 경우 debug
# hello.springmvc 패키지와 그 하위 로그 레벨 설정
logging.level.hello.springmvc=debug
- info 하위 레벨 출력 할 경우 info (기본)
# hello.springmvc 패키지와 그 하위 로그 레벨 설정
logging.level.hello.springmvc=info
올바른 로그 사용법
- log.debug("data="+data) 로그 출력 레벨을 info로 설정해도 해당 코드에 있는 "data="+data가 실제 실행이 되어 버린다. 결과적으로 문자 더하기 연산이 발생.
- log.debug("data={}", data) 로그 출력 레벨을 info로 설정하면 아무일도 발생하지 않는다. 따라서 앞과 같은 의미없는 연산이 발생하지 않는다.
// 자바언어는 아래의 경우 "trace my log = " + "Spring"로 치환 후 더함(연산 발생)
// 로그 레벨을 debug로 할 경우 trace 사용하지 않아도 연산 일어남으로써
// -> 메모리, cpu 사용. (쓸모없는 리소스 사용 / 의미없는 연산 발생.)
log.trace("trace my log = " + name);
로그가 출력되는 포멧 확인
- 시간, 로그 레벨, 프로세스 ID, 쓰레드 명, 클래스명, 로그 메시지
로그 레벨 설정을 변경에 따른 출력 결과
- LEVEL : TRACE > DEBUG > INFO > WARN > ERROR
- 개발 서버 : debug 출력
- 운영 서버 : info 출력
@Slf4j
- lombok이 제공하는 것.
- @Slf4j를 넣을 경우 아래 코드를 자동으로 해줌.
private final Logger log = LoggerFactory.getLogger(getClass());
로그 사용 장점
- 시스템 아웃 콘솔에만 출력하는 것이 아니라, 파일이나 네트워크 등, 로그를 별도의 위치에 남길 수 있다.
- 특히 파일로 남길 때는 일별, 특정 용량에 따라 로그를 분할하는 것도 가능하다.
- 성능도 System.out보다 좋음. (내부 버퍼링, 멀티 쓰레드 등)
- 실무에서는 꼭 로그 사용.
로그 추가 학습 시 참고.
로그에 대해서 더 자세한 내용은
- slf4j, logback 검색.
- SLF4J - http://www.slf4j.org
- Logback - http://logback.qos.ch
스프링 부트가 제공하는 로그 기능
## 요청 매핑
MappingController.
package hello.springmvc.basic.requestmapping;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MappingController {
private Logger log = LoggerFactory.getLogger(getClass());
@RequestMapping("/hello-basic")
public String helloBasic() {
log.info("basic");
return "OK";
}
}
@RestController
- @Controller 는 반환 값이 String 이면 뷰 이름으로 인식. 그래서 뷰를 찾고 뷰가 랜더링 된다.
- @RestController 는 반환 값으로 뷰를 찾는 것이 아난, HTTP 메시지 바디에 바로 입력. 따라서 실행 결과로 ok 메세지를 받을 수 있다.
@RequestMapping("/hello-basic")
- /hello-basic URL 호출이 오면 이 메서드가 실행되도록 매핑.
- 대부분의 속성을 배열[] 로 제공하므로 다중 설정이 가능하다.
@RequestMapping({"/hello-basic", "/hello-go"})
두가지 모두 허용.
- 다음 두가지 요청은 다른 URL이지만, 스프링은 다음 URL 요청들을 같은 요청으로 매핑.
- 매핑 : /hello-basic
- URL 요청 : /hello-basic , /hello-basic/
HTTP 메서드
- @RequestMapping 에 method 속성으로 HTTP 메서드를 지정하지 않으면, HTTP 메서드와 무관하게 호출.
- 모두 허용 GET, HEAD, POST, PUT, PATCH, DELETE
- 아래와 같이 설정해줘야 함.
@RequestMapping(value = "/hello-basic", method = RequestMethod.GET)
- 아래와 같이 축약 가능.
@GetMapping("/hello-basic")
package hello.springmvc.basic.requestmapping;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MappingController {
private Logger log = LoggerFactory.getLogger(getClass());
@RequestMapping(value = "/hello-basic")
public String helloBasic() {
log.info("basic");
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("mappingGetV2");
return "OK";
}
}
PathVariable(경로 변수) 사용 (자주 사용!!!!!)
/**
* PathVariable 사용
* 변수명이 같으면 생략 가능.
* @PathVariable("userId") String userId -> @PathVariable userId
* url 자체에 /mapping/userA 이런식으로 값이 들어감.
*/
@GetMapping("/mapping/{userId}")
public String mappingPath(@PathVariable("userId") String data) {
log.info("mappingPath userId = {} ", data);
return "OK";
}
- 최근 HTTP API는 아래와 같이 리소스 경로에 식별자를 넣는 스타일을 선호.
/mapping/userA
/users/1
- @RequestMapping 은 URL 경로를 템플릿화 할 수 있는데, @PathVariable 을 사용하면 매칭 되는 부분을 편리하게 조회할 수 있다.
- @PathVariable 의 이름과 파라미터 이름이 같으면 생략 가능 (아래 코드 참고) (단, @PathVariable 자체를 생략하는건 안됨.)
@GetMapping("/mapping/{userId}")
public String mappingPath(@PathVariable String userId) {
log.info("mappingPath userId = {} ", userId);
return "OK";
}
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";
}
특정 파라미터 조건 매핑 (자주 사용하진 않음)
- 특정 파라미터가 있거나 없는 조건을 추가할 수 있다.
/**
* 파라미터로 추가 매핑
* 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";
}
미디어 타입 조건 매핑 - 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";
}
- HTTP 요청의 Content-Type 헤더를 기반으로 미디어 타입으로 매핑.
- 만약 맞지 않으면 HTTP 415 상태코드(Unsupported Media Type)을 반환.
- consume 예시.
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}
- 회원 삭제 : DELETE /users/{userId}
package hello.springmvc.basic.requestmapping;
import org.springframework.web.bind.annotation.*;
@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";
}
@PatchMapping("/{userId}")
public String updateUser(@PathVariable String userId) {
return "update userId=" + userId;
}
@DeleteMapping("/{userId}")
public String delete(@PathVariable String userId) {
return "update userId=" + userId;
}
}
- @RequestMapping("/mapping/users") 클래스 레벨에 매핑 정보를 두면 메서드 레벨에서 해당 정보를 조합해서 사용.
반응형
'인프런 강의 학습 > 스프링 MVC 1' 카테고리의 다른 글
스프링 MVC 10일차_스프링 MVC_기본 기능_3 (0) | 2022.03.01 |
---|---|
스프링 MVC 9일차_스프링 MVC_기본 기능_2 (0) | 2022.02.28 |
스프링 MVC 7일차_스프링 MVC 구조이해. (0) | 2022.02.26 |
스프링 MVC 6일차_MVC 프레임워크 만들기_2 (0) | 2022.02.25 |
스프링 MVC 5일차_MVC 프레임워크 만들기_1 (0) | 2022.02.24 |