- 해당 내용은, '인프런 - 스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술 (유료 - 69,300) 김영한님 강의'를 개인적으로 개념정리를 위해 정리한 내용입니다.
로깅 간단히 알아보기
- 로깅 라이브러리
- 스프링 부트 라이브러리를 사용하면 스프링 부트 로깅 라이브러리( 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")
- 시스템 콘솔로 직접 출력하는 것 보다 로그를 사용하면 다음과 같은 장점이 있다. 실무에서는 항상 로그를 사용해야 한다
- LEVEL: TRACE > DEBUG > INFO > WARN > ERROR
- 개발 서버는 debug 출력
- 운영 서버는 info 출력
- @Slf4j 로 변경
- 매핑 정보
- @RestController
- @Controller 는 반환 값이 String 이면 뷰 이름으로 인식된다. 그래서 뷰를 찾고 뷰가 랜더링 된다.
- @RestController 는 반환 값으로 뷰를 찾는 것이 아니라, HTTP 메시지 바디에 바로 입력한다.
- 따라서 실행 결과로 ok 메세지를 받을 수 있다. @ResponseBody 와 관련이 있는데, 뒤에서 더 자세히 설명한다.
- 로그 레벨 설정
- application.properties
#전체 로그 레벨 설정(기본 info)
logging.level.root=info
#hello.springmvc 패키지와 그 하위 로그 레벨 설정
logging.level.hello.springmvc=debug
- 로그 사용시 장점
- 쓰레드 정보, 클래스 이름 같은 부가 정보를 함께 볼 수 있고, 출력 모양을 조정할 수 있다.
- 로그 레벨에 따라 개발 서버에서는 모든 로그를 출력하고, 운영서버에서는 출력하지 않는 등 로그를 상황에 맞게 조절할 수 있다.
- 시스템 아웃 콘솔에만 출력하는 것이 아니라, 파일이나 네트워크 등, 로그를 별도의 위치에 남길 수 있다.
- 특히 파일로 남길 때는 일별, 특정 용량에 따라 로그를 분할하는 것도 가능하다.
- 성능도 일반 System.out보다 좋다. (내부 버퍼링, 멀티 쓰레드 등등) 그래서 실무에서는 꼭 로그를 사용해야 한다.
HTTP 요청 - 기본, 헤더 조회
- MultiValueMap
- 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> values = map.get("keyA");
HTTP 요청 파라미터 - @RequestParam
스프링이 제공하는 @RequestParam 을 사용하면 요청 파라미터를 매우 편리하게 사용할 수 있다.
/**
* @RequestParam 사용
* - 파라미터 이름으로 바인딩
* @ResponseBody 추가
* - View 조회를 무시하고, HTTP message body에 직접 해당 내용 입력
*/
@ResponseBody
@RequestMapping("/request-param-v2")
public String requestParamV2(
@RequestParam(required = true, defaultValue = "guest") String memberName,
@RequestParam("age") int memberAge)
{
log.info("username={}, age={}", memberName, memberAge);
return "ok";
}
- @RequestParam : 파라미터 이름으로 바인딩
- @ResponseBody : View 조회를 무시하고, HTTP message body에 직접 해당 내용 입력
- @RequestParam의 name(value) 속성이 파라미터 이름으로 사용
- @RequestParam("username") String memberName
- request.getParameter("username")
- 파라미터를 Map으로 조회하기 - requestParamMap
/**
* @RequestParam Map, MultiValueMap
* Map(key=value)
* MultiValueMap(key=[value1, value2, ...]) ex) (key=userIds, value=[id1, id2])
*/
@ResponseBody
@RequestMapping("/request-param-map")
public String requestParamMap(@RequestParam Map<String, Object> paramMap) {
log.info("username={}, age={}", paramMap.get("username"), paramMap.get("age"));
return "ok";
}
- 파라미터를 Map, MultiValueMap으로 조회할 수 있다
- @RequestParam Map ,
- Map(key=value)
- @RequestParam MultiValueMap
- MultiValueMap(key=[value1, value2, ...] ex) (key=userIds, value=[id1, id2])
- 파라미터의 값이 1개가 확실하다면 Map 을 사용해도 되지만, 그렇지 않다면 MultiValueMap 을 사용하자.
HTTP 요청 파라미터 - @ModelAttribute
- 실제 개발을 하면 요청 파라미터를 받아서 필요한 객체를 만들고 그 객체에 값을 넣어주어야 한다. 보통 다음과 같이 코드를 작성할 것이다.
@RequestParam String username;
@RequestParam int age;
HelloData data = new HelloData();
data.setUsername(username);
data.setAge(age);
- 스프링은 이 과정을 완전히 자동화해주는 @ModelAttribute 기능을 제공한다.
- 먼저 요청 파라미터를 바인딩 받을 객체를 만들자
- HelloData
package hello.springmvc.basic;
import lombok.Data;
@Data
public class HelloData {
private String username;
private int age;
}
- 롬복 @Data
- @Getter , @Setter , @ToString , @EqualsAndHashCode , @RequiredArgsConstructor 를 자동으로 적용해준다.
- @ModelAttribute 적용 - modelAttributeV1
/**
* @ModelAttribute 사용
* 참고: model.addAttribute(helloData) 코드도 함께 자동 적용됨, 뒤에 model을 설명할 때 자세히 설명
*/
@ResponseBody
@RequestMapping("/model-attribute-v1")
public String modelAttributeV1(@ModelAttribute HelloData helloData) {
log.info("username={}, age={}", helloData.getUsername(), helloData.getAge());
return "ok";
}
- 마치 마법처럼 HelloData 객체가 생성되고, 요청 파라미터의 값도 모두 들어가 있다
- 스프링MVC는 @ModelAttribute 가 있으면 다음을 실행한다.
- HelloData 객체를 생성한다.
- 요청 파라미터의 이름으로 HelloData 객체의 프로퍼티를 찾는다. 그리고 해당 프로퍼티의 setter를 호출해서 파라미터의 값을 입력(바인딩) 한다.
- 예) 파라미터 이름이 username 이면 setUsername() 메서드를 찾아서 호출하면서 값을 입력한다.
HTTP 응답 - 정적 리소스, 뷰 템플릿
- 스프링(서버)에서 응답 데이터를 만드는 방법은 크게 3가지이다.
- 정적 리소스
- 예) 웹 브라우저에 정적인 HTML, css, js를 제공할 때는, 정적 리소스를 사용한다.
- 뷰 템플릿 사용
- 예) 웹 브라우저에 동적인 HTML을 제공할 때는 뷰 템플릿을 사용한다.
- HTTP 메시지 사용
- HTTP API를 제공하는 경우에는 HTML이 아니라 데이터를 전달해야 하므로, HTTP 메시지 바디에 JSON 같은 형식으로 데이터를 실어 보낸다.
- 정적 리소스
- 스프링 부트는 클래스패스의 다음 디렉토리에 있는 정적 리소스를 제공한다.
- /static , /public , /resources , /META-INF/resources
- src/main/resources 는 리소스를 보관하는 곳이고, 또 클래스패스의 시작 경로이다. 따라서 다음 디렉토리에 리소스를 넣어두면 스프링 부트가 정적 리소스로 서비스를 제공한다.
- 정적 리소스 경로
- src/main/resources/static
- 다음 경로에 파일이 들어있으면
- src/main/resources/static/basic/hello-form.html
- 웹 브라우저에서 다음과 같이 실행하면 된다.
- http://localhost:8080/basic/hello-form.html
- 정적 리소스는 해당 파일을 변경 없이 그대로 서비스하는 것이다
- 스프링 부트는 클래스패스의 다음 디렉토리에 있는 정적 리소스를 제공한다.
- 뷰 템플릿
- 뷰 템플릿을 거쳐서 HTML이 생성되고, 뷰가 응답을 만들어서 전달한다. 일반적으로 HTML을 동적으로 생성하는 용도로 사용하지만, 다른 것들도 가능하다. 뷰 템플릿이 만들 수 있는 것이라면 뭐든지 가능하다.
- 스프링 부트는 기본 뷰 템플릿 경로를 제공한다
- 뷰 템플릿 경로
- src/main/resources/templates
- 뷰 템플릿 생성
- src/main/resources/templates/response/hello.html
- 뷰 템플릿 경로
- 정적 리소스
HTTP 메시지 컨버터
- HTTP 메시지 컨버터 위치
- HTTP 메시지 컨버터는 어디쯤 있을까? HTTP 메시지 컨버터를 사용하는 @RequestBody 도 컨트롤러가 필요로 하는 파라미터의 값에 사용된다.
- @ResponseBody 의 경우도 컨트롤러의 반환 값을 이용한다.
- 요청의 경우 @RequestBody 를 처리하는 ArgumentResolver 가 있고, HttpEntity 를 처리하는 ArgumentResolver 가 있다. 이 ArgumentResolver 들이 HTTP 메시지 컨버터를 사용해서 필요한 객체를 생성하는 것이다.
- 응답의 경우 @ResponseBody 와 HttpEntity 를 처리하는 ReturnValueHandler 가 있다. 그리고 여기에서 HTTP 메시지 컨버터를 호출해서 응답 결과를 만든다.
- 스프링 MVC는 @RequestBody @ResponseBody 가 있으면 RequestResponseBodyMethodProcessor (ArgumentResolver) HttpEntity 가 있으면 HttpEntityMethodProcessor (ArgumentResolver)를 사용한다.
궁금한 점
jar vs war
- JAR, WAR 모두 Java의 -jar 옵션 (java -jar)을 이용해 생성된 압축(아카이브 파일)로 애플리케이션을 쉽게 배포하고 동작시킬수 있도록 관련 파일을 패키징 한 것이다.
- JAR (JAVA Archive)
- JAVA 어플리케이션이 동작할 수 있도록 자바 프로젝트를 압축한 파일
- Class (JAVA리소스, 속성 파일), 라이브러리 파일을 포함함
- JRE(JAVA Runtime Environment)만 있어도 실행 가능함 (java -jar 프로젝트네임.jar)
- WAR (Web Application Archive)
- Servlet / Jsp 컨테이너에 배치할 수 있는 웹 애플리케이션(Web Application) 압축파일 포맷
- 웹 관련 자원을 포함함 (JSP, Servlet, JAR, Class, XML, HTML, Javascript)
- 사전 정의된 구조를 사용함 (WEB-INF, META-INF)
- 별도의 웹서버(WEB) or 웹 컨테이너(WAS) 필요
- 즉, JAR파일의 일종으로 웹 애플리케이션 전체를 패키징 하기 위한 JAR 파일이다.
JSON은 객체인가요 문자인가요?
- JSON은 규격화 한 문자열 입니다.
- 규격화 했다는 것은 일정한 규칙에 맞게 작성했다는 의미입니다. 이 문자열을 해석해서 객체로 변환, 사용하기 쉽게 만드는 것을 '파싱'한다고 표현합니다.
스프링 동작과정
- 스프링 부트 구동 시점에 스프링 컨테이너와 서블릿 컨테이너는 각각이 필요한 기본 빈들을 생성하고, 필요한 초기화 작업과 DI 작업을 수행
- 클라이언트로부터 URL 호출 시, 서블릿 컨테이너가 request를 파싱하고, HttpServletRequest와 Response 객체를 생성 후, 파싱된 메세지를 HttpServletRequest에 전달함. 이후, 쓰레드가 DispatcherServlet 객체의 service()를 호출(service()는 실제로 부모 클래스에서 오버라이딩 되어 있고 그 내부에서 중요한 메서드인 doDispatch()가 호출됨)
- 이후 DispatcherServlet은 미리 초기화 해둔 handlerMappings의 handlerMapping 구현 클래스를 순서대로 하나씩 꺼내와서 검사하는데, 1순위인 RequestMappingHandlerMapping 클래스의 조회 전략은 @Controller 클래스 중, 메서드 레벨에 @RequestMapping이 붙어 있고, request의 URL 정보를 토대로 일치하는지 검사. 있다면 해당 핸들러를 반환(컨트롤러) 하지만, 없다면 서블릿 예외를 던지고 애초에 @Controller 자체가 없다면 2순위로 검사 위임하지 않음
- 해당 핸들러를 getHandlerAdapter()를 통해서 해당 핸들러를 지원하는 어댑터가 있다면 해당 어댑터를 반환하여 HandlerAdapter ha에 할당하고, 지원하는 어댑터가 없는 경우 서블릿 예외를 던짐
- handlerAdapter의 handler()를 수행하는데 실제 컨트롤러 메서드를 호출하기 전에, 해당 메서드가 필요로 하는 매개변수 정보를 (@RequestBody인지, HttpEntity인지) ArgumentResolver에 제공하고, 각각에 특화된 Http메세지 컨버터를 사용해서 필요한 객체를 생성
- 컨트롤러가 결과값을 반환
- 메서드 레벨에 @ResponseBody가 붙어있다면 RetrunValueHandler가 HttpMessengerConverter를 이용해서 반환값을 응답 메세지 바디부에 실어서 ViewResolver등을 거치지 않고 즉시 요청 송신자에 반환
- 위의 경우가 아니면, ReturnValueHandler는 반환 값의 타입에 따라 적절한 HttpMessengerConverter를 통해 응답 메세지를 생성 후, ViewResolver가 동작하는데 이는 View 객체를 생성하고 DispatcherServlet가 이를 이용하여 render()하고 최종적으로 클라이언트에게 반환.
'Engineering WIKI > Spring Boot' 카테고리의 다른 글
[인프런 - 스프링 MVC 2편 - 백엔드 웹 개발 활용 기술] 예외 처리와 오류 페이지 (63) | 2023.11.02 |
---|---|
[인프런 - 스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술] 5장. 스프링 MVC - 구조 이해 (140) | 2023.09.24 |
[인프런 - 스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술] 4장. MVC 프레임워크 만들기 (80) | 2023.09.24 |
[인프런 - 스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술] 3장.서블릿, JSP, MVC 패턴 (67) | 2023.09.22 |
[인프런 - 스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술] 2장.서블릿 (105) | 2023.09.20 |
[인프런 - 스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술] 1장.웹 애플리케이션 이해 (81) | 2023.09.07 |