인프런의 스프링 MVC 김영한님 강의를 정리
스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술 - 인프런 | 강의
원래는 웹 애플리케이션(톰캣)을 직접 설치하고 그 위에 서블릿 코드를 클래스 파일로 빌드해서 올린 다음 톰캣 서버를 실행해야 한다.
1️⃣ 서블릿으로 HTTP 요청, 응답 보내기
- 웹 브라우저가 url로 요청
- WAS 가 HTTP 요청에 맞춰서 request, response 객체 생성 > servlet 실행
- servlet 이 요청을 처리하고 response 객체 반환
- WAS 가 response 객체 정보로 HTTP 응답 생성 > 웹 브라우저에 전달
2️⃣ 서블릿 등록하기
서블릿을 직접 등록해서 사용하기 위해. Application 클래스에@ServletComponentScan을 적어주면 된다. (그럼 @WebServlet이 붙은 클래스를 탐색해서 등록해 준다.)
@ServletComponentScan //서블릿 자동 등록
@SpringBootApplication
public class ServletApplication {
public static void main(String[] args) {
SpringApplication.run(ServletApplication.class, args);
}
}
- @WebServlet: 서블릿을 등록하는 어노테이션
- name: 서블릿 이름
- urlPatterns: 매핑할 url
- @WebServlet 등록, HttpServlet을 상속, service()를 재정의 해주면 된다.
- 그러면 request, response 객체를 편리하게 사용할 수 있음
@WebServlet(name = "helloServlet", urlPatterns = "/hello")
public class HelloServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// request 파싱
String username = request.getParameter("username");
System.out.println("username = " + username);
// response 생성
response.setContentType("text/plain");
response.setCharacterEncoding("utf-8");
response.getWriter().write("hello " + username);
}
}
2️⃣ HttpServletRequest
서블릿이 HTTP 요청 메시지를 파싱 해서 HttpServletRequest 객체에 담아 제공한다.
요청 메시지 파싱 메서드
// start line 정보
request.getMethod();
request.getProtocol();
request.getScheme();
request.getRequestURL();
request.getRequestURI();
request.getQueryString();
request.isSecure();
// Header 정보
request.getHeaderNames().asIterator()
.forEachRemaining(headerName -> System.out.println(headerName + ": " + request.getHeader(headerName)));
request.getServerName();
request.getServerPort();
request.getLocales();
request.getContentType();
request.getContentLength();
request.getCharacterEncoding();
for (Cookie cookie: request.getCookies()) {
System.out.println(cookie.getName() + ": " + cookie.getValue());
}
// 기타 정보
request.getRemoteHost(); // IPv6 출력
request.getRemoteAddr();
request.getRemotePort();
request.getLocalName();
request.getLocalAddr();
request.getLocalPort();
HTTP 요청을 보내는 방식
- GET - 쿼리 파라미터
url에 쿼리 파라미터를 붙여서 전달 - POST - HTML Form
form에 값을 넣고 전송 → 웹 브라우저가 form에 맞춰서 HTTP 요청 메시지 생성
Content-Type:application/x-www-form-urlencoded - HTTP message body에 직접 데이터를 담는 방법
단순 텍스트, JSON, XML 등 거의 모든 데이터를 다 전달할 수 있음. POST / PUT / PATCH 방식 지원
1. GET - 쿼리 파라미터
// 전체 파라미터 조회
request.getParameterNames().asIterator()
.forEachRemaining(paramName -> System.out.println(paramName + "=" + request.getParameter(paramName)));
// 단일 파라미터 조회
request.getParameter("username");
// 이름이 같은 파라미터에 여러 값을 전달할 수 있음 > 여러 값 조회
String[] usernames = request.getParameterValues("username");
동일 파라미터에 여러 값을 전송하는 경우,
http://localhost:8080/request-param?username=hello&username=kim&age=20
- request.getParameter("username"); // 처음 전달된 값이 조회됨
- request.getParameterValues("username"); // 전달되는 여러 값을 한 번에 조회
2. POST HTML Form
HTML Form으로 데이터를 전송한 요청을 받는 방법
- Content-Type: application/x-www-form-urlencoded
- 메시지 바디에 쿼리 파라미터 형식으로 데이터를 전달한다.
POST - HTML Form은 메시지 바디에 쿼리 파라미터 형식으로 데이터를 전달하기 때문에 GET - 쿼리 파라미터 방식과 유사하다. → 따라서 request.getParameter()으로 두 가지 방식을 다 읽을 수 있다.
Content-Type
HTTP 메시지 바디의 데이터 형식을 지정
3. HTTP 메시지 바디 - 단순 텍스트
단순한 텍스트 메시지를 HTTP 바디에 담아서 전송하면 InputStream을 사용해서 읽을 수 있다.
@WebServlet(name = "requestBodyStringServlet", urlPatterns = "/request-bodystring")
public class RequestBodyStringServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletInputStream inputStream = request.getInputStream(); // request body의 텍스트를 읽음
String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8); // 바이트 스트림 -> String
System.out.println("messageBody = " + messageBody);
response.getWriter().write("ok");
}
}
InputStream은 request body의 텍스트를 읽어서 byte로 반환한다. → 문자열로 변경해줘야 한다.
4. HTTP 메시지 바디 - JSON
주로 HTTP API에서는 JSON 형태로 데이터를 전송한다.
- Content-Type: application/json
- message body: {"username": "hello", "age": 20}
JSON을 파싱 해서 그 값을 담을 객체 생성
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class HelloData {
private String username;
private int age;
}
JSON도 문자열로 HTTP Body로 전송되기 때문에 아래 과정을 수행해야 한다.
- HTTP Body를 InputStrema으로 텍스트 읽기
- ObjectMapper로 자바 객체로 변환
@WebServlet(name = "requestBodyJsonServlet", urlPatterns = "/request-bodyjson")
public class RequestBodyJsonServlet extends HttpServlet {
private ObjectMapper objectMapper = new ObjectMapper();
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletInputStream inputStream = request.getInputStream();
String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
HelloData helloData = objectMapper.readValue(messageBody, HelloData.class);
response.getWriter().write("ok");
}
}
3️⃣ HttpServletResponse
HTTP 응답 메시지 생성
- HTTP 응답 코드 지정
- 헤더 생성
- 바디 생성
@WebServlet(name = "responseHeaderServlet", urlPatterns = "/response-header")
public class ResponseHeaderServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// [response-headers]
response.setHeader("Content-Type", "text/plain;charset=utf-8");
response.setHeader("Cache-Control", "no-cache, no-store, mustrevalidate");
response.setHeader("Pragma", "no-cache");
response.setHeader("my-header","hello"); // 사용자 지정 헤더
// Content 메서드
response.setContentType("text/plain");
response.setCharacterEncoding("utf-8");
response.setContentLength(2); //(생략시 자동 생성)
// Cookie 메서드
Cookie cookie = new Cookie("myCookie", "good");
cookie.setMaxAge(600);
response.addCookie(cookie); // 쿠기 추가
// Redirect 메서드
response.setStatus(HttpServletResponse.SC_FOUND); // 302
//response.setHeader("Location", "/basic/hello-form.html");
response.sendRedirect("/basic/hello-form.html");
// [message body]
PrintWriter writer = response.getWriter();
writer.println("ok");
}
}
응답을 보내는 방식
- 단순 텍스트
response.getWriter().println("ok"); - HTML response.setContentType("text/html"); 설정 후 response 객체에 html 작성
- API JSON
response.setHeader("content-type", "application/json"); 헤더 설정 후 자바 객체를 JSON 형식 문자열로 변경한 뒤 response 객체에 작성
1. API JSON 형태로 보내기
- response의 Content-Type을 application/json으로 설정
- Jackson 라이브러리가 제공하는 objectMapper.writeValueAsString()를 사용해서 자바 객체를 JSON 형태 문자열로 변경
@WebServlet(name = "responseJsonServlet", urlPatterns = "/response-json")
public class ResponseJsonServlet extends HttpServlet {
private ObjectMapper objectMapper = new ObjectMapper();
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// Content-Type: application/json
response.setHeader("content-type", "application/json");
response.setCharacterEncoding("utf-8");
// 자바 객체
HelloData data = new HelloData();
data.setUsername("kim");
data.setAge(20);
// JSON 형태 문자열
// {"username":"kim","age":20}
String result = objectMapper.writeValueAsString(data);
response.getWriter().write(result);
}
}
JSON 형태 문자열 > 자바 객체
objectMapper.readValue(messageBody, HelloData.class);
자바 객체 > JSON 형태 문자열
objectMapper.writeValueAsString(data);
💡 참고
application/json은 스펙상 utf-8 형식을 사용하도록 정의되어 있다. 그래서 charset=utf-8과 같은 추가 파라미터를 지원하지 않는다. 따라서 application/json 형태로 사용하지 application/json;charset=utf-8 이라고 전달하는 것은 의미 없는 파라미터를 추가한 것이 된다.
response.getWriter()를 사용하면 추가 파라미터를 자동으로 추가해 버린다. 이때는 response.getOutputStream()으로 출력하면 그런 문제가 없다.
'CS > Backend' 카테고리의 다른 글
[Spring] JPA - 양방향 연관 관계 mappedBy, @JoinColumn (0) | 2023.07.18 |
---|---|
[Spring] @Valid와 @Validated를 이용한 유효성 검증 (0) | 2023.06.29 |
[Spring] Pagiantion, Pageable 인터페이스 (0) | 2023.06.27 |
[Java] 직렬화, 역직렬화 (0) | 2023.06.25 |
[Spring] REST, REST API (0) | 2023.06.23 |