강의를 들으며 생각 정리
이전 포스팅에서 다룬 서블릿부터 스프링MVC까지의 백엔드 기술들을 하나씩 차근차근 살펴보자.
프로젝트 생성
<사전 준비물>
Java 11 설치
IDE: IntelliJ
<스프링 프로젝트 생성>
스프링 부트 스타터 사이트로 이동해서 스프링 프로젝트 생성
-> 서블릿은 스프링이 필요하지 않지만 스프링 부트는 내부에 톰캣서버를 내장하고 있어서 테스트하기 편리하다.
실제로 스프링은 거의 사용하지 않을 것이다.
<프로젝트 설정>
Project : Gradle Project
Language : Java
Spring Boot : 2.4.5
Group : hello
Artifact : servlet
Packaging : War
-> Jar, War 모두 스프링 부트 내장 톰캣서버에서 빌드할 때 사용할 수 있다. 특히 War는 내장 서버가 아닌 별도의 톰캣서버에서 빌드할 때도 사용할 수 있다. 여기서는 나중 JSP 공부를 위해서 War를 사용한다.
Java : 11
Dependencies : Spring Web, Lombok
-> Spring Web을 추가하면 톰캣 서버(WAS)를 내장하게 된다.
<Gradle 대신에 자바 직접 실행>
최근 IntelliJ 버전은 Gradle을 통해서 실행하는 것이 기본 설정이다. 이렇게 하면 실행속도가 느리다. 자바로 바로 실행해서 실행속도를 더 빠르게 할 수 있다.
+) 주의사항 : IntelliJ 무료 버전의 경우 해당 설정은 IntelliJ IDEA가 아니라 Gradle로 설정해야 한다.
-> Jar 파일의 경우는 문제가 없는데, War의 경우 톰캣이 정상 시작되지 않는 문제가 발생한다. 유료 버전은 모두 정상 동작한다.
<롬복 적용>
getter, setter 등의 여러 기능들을 애노테이션 기반으로 편리하게 사용하기 위해 롬복을 사용한다.
<Postman>
API 테스트용으로 사용하기 위해 다음 사이트에서 Postman을 다운로드 받고 설치해두자
https://www.postman.com/downloads
Hello 서블릿
스프링 부트 환경에서 서블릿을 등록하고 사용해보자.
+) 서블릿은 톰캣 같은 WAS를 직접 설치하지 않고, 따로 설치한 WAS 위에 서블릿 코드를 클래스 파일로 빌드해서 올린 다음, 톰캣 서버를 실행한다. 하지만 이 과정은 매우 번거롭다.
스프링 부트는 톰캣 서버를 내장하고 있으므로, 톰캣 서버 설치 없이 편리하게 서블릿 코드를 실행할 수 있다.
스프링 부트는 서블릿을 직접 등록해서 사용할 수 있도록 @ServletComponentScan을 지원한다. 다음과 같이 추가하자.
<ServletApplication.java>
@ServletComponentScan //서블릿 자동 등록
@SpringBootApplication
public class ServletApplication {
public static void main(String[] args) {
SpringApplication.run(ServletApplication.class, args);
}
}
<서블릿 등록하기>
실제 동작하는 서블릿 코드를 등록해보자.
<basic/HelloServlet.java>
@WebServlet(name = "helloServlet", urlPatterns = "/hello")
public class HelloServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("HelloServlet.service");
System.out.println("request = " + request);
System.out.println("response = " + response);
String username = request.getParameter("username");
System.out.println("username = " + username);
response.setContentType("text/plain");
response.setCharacterEncoding("utf-8");
response.getWriter().write("hello " + username);
}
}
- @WebServlet : 서블릿 애노테이션 (name : 서블릿 이름, urlPatterns : URL 매핑 -> 이 옵션들은 중복되면 안된다.)
- HTTP 요청을 통해 매핑된 URL이 호출되면 서블릿 컨테이너는 service 메서드를 실행한다.
- 웹 브라우저 실행 -> http://localhost:8080/hello?username=kim
-> 결과
hello kim
-> 콘솔 실행결과
HelloServlet.service
request = org.apache.catalina.connector.RequestFacade@5e4e72
response = org.apache.catalina.connector.ResponseFacade@37d112b6
username = kim
HTTP 메시지를 직접 파싱할 필요 없이 request, response를 사용해서 편리하게 메시지에 필요한 부분을 작성할 수 있다.
<HTTP 요청 메시지 로그로 확인하기>
다음 설정을 추가하자.
<application.properties>
logging.level.org.apache.coyote.http11=debug
이제 서버에 요청해보면 서버가 받은 HTTP 요청 메시지를 콘솔에 출력하는 것을 확인할 수 있다.
+) 참고로 운영서버에 이렇게 모든 요청 정보를 다 남기면 성능저하가 발생할 수 있다. 개발 단계에서만 사용하자.
<서블릿 컨테이너 동작 방식>
- 웹 브라우저가 /hello URL 호출
- HTTP 요청 메시지가 생성되고 서버에 요청
- 톰캣 서버는 request, response 객체를 자동 생성하고 서블릿 컨테이너에 등록된 helloServlet 객체를 실행
- 개발자가 직접 짠 service 로직을 수행
- response 객체를 기반으로 HTTP 응답 메시지를 웹 브라우저에 전송
+) 참고 : HTTP 응답에서 Content-Length는 WAS가 자동으로 생성해준다.
<welcome 페이지 추가>
지금부터 개발할 내용을 편리하게 참고할 수 있도록 welcome 페이지를 만들어두자.
webapp 경로에 index.html을 두면 http://localhost:8080 호출시 index.html 페이지가 열린다.
+) 서블릿 기술에 집중하기 때문에 세부적인 html에 대한 사항은 앞으로 생략한다.
<welcome page>
HttpServletRequest - 개요
<HttpServletRequest 역할>
HTTP 요청 메시지를 개발자가 직접 파싱해서 사용해도 되지만, 매우 불편할 것이다. 서블릿은 개발자가 HTTP 요청 메시지를 편리하게 사용할 수 있도록 개발자 대신에 HTTP 요청 메시지를 파싱한다. 그리고 그 결과를 HttpServletRequest 객체에 담아서 제공한다.
- HttpServletRequest를 사용하면 다음과 같은 HTTP 요청 메시지를 편리하게 조회할 수 있다.
POST /save HTTP/1.1 -> START LINE
Host: localhost:8080
Content-Type: application/x-www-form-urlencoded -> 헤더
username=kim&age=20 -> 바디
- START LINE : HTTP 메소드, URL, 쿼리 스트링, 버전 등 조회
- 헤더 : 헤더 조회
- 바디 : HTML form 파라미터 형식 조회, JSON과 같은 데이터 직접 조회 (JSON을 직접 조회하려면 라이브러리가 추가로 필요한데 이는 뒤에서 다루겠다.)
- HttpServletRequest 객체는 추가로 여러가지 부가 기능도 함께 제공한다.
임시 저장소 기능 : 해당 HTTP 요청이 시작부터 끝날 때까지 유지되는 임시 저장소 기능
-> 저장 : request.setAttribute(name, value)
-> 요청 : : request.getAttribute(name)
세션 관리 기능
request.getSession(create: true)
+) HttpServletRequest, HttpServletResponse를 사용할 때 가장 중요한 점은 이 객체들이 HTTP 요청 메시지, HTTP 응답 메시지를 편리하게 사용하도록 도와주는 객체라는 점이다. 따라서 이 기능에 대해서 깊이있는 이해를 하려면 HTTP 스펙이 제공하는 요청, 응답 메시지 자체를 이해해야 한다.
+) HTTP에 대한 자세한 내용은 http 카테고리의 내용들을 참조
HttpServletRequest - 기본 사용법
HttpServletRequest가 제공하는 기본 기능들을 알아보자.
서블릿은 다음 코드를 기반으로 함수들을 작성한다.
<baisc/request/RequestHeaderServlet.java>
//http://localhost:8080/request-header?username=hello
@WebServlet(name = "requestHeaderServlet", urlPatterns = "/request-header")
public class RequestHeaderServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
printStartLine(request);
printHeaders(request);
printHeaderUtils(request);
printEtc(request);
}
}
<start-line 정보>
//start line 정보
private void printStartLine(HttpServletRequest request) {
System.out.println("--- REQUEST-LINE - start ---");
System.out.println("request.getMethod() = " + request.getMethod()); //GET
System.out.println("request.getProtocol() = " + request.getProtocol()); //HTTP/1.1
System.out.println("request.getScheme() = " + request.getScheme()); //http
System.out.println("request.getRequestURL() = " + request.getRequestURL()); // http://localhost:8080/request-header
System.out.println("request.getRequestURI() = " + request.getRequestURI()); // /request-header
System.out.println("request.getQueryString() = " + request.getQueryString()); //username=kim
System.out.println("request.isSecure() = " + request.isSecure()); //https 사용 유무
System.out.println("--- REQUEST-LINE - end ---");
System.out.println();
}
<결과>
--- REQUEST-LINE - start ---
request.getMethod() = GET
request.getProtocal() = HTTP/1.1
request.getScheme() = http
request.getRequestURL() = http://localhost:8080/request-header
request.getRequestURI() = /request-header
request.getQueryString() = username=hello
request.isSecure() = false
--- REQUEST-LINE - end ---
<헤더 정보>
헤더의 모든 정보를 출력하는 함수다.
//Header 모든 정보
private void printHeaders(HttpServletRequest request) {
System.out.println("--- Headers - start ---");
/*
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
System.out.println(headerName + ": " + request.getHeader(headerName));
}
*/
request.getHeaderNames().asIterator()
.forEachRemaining(headerName -> System.out.println(headerName + ": " + request.getHeader(headerName)));
System.out.println("--- Headers - end ---");
System.out.println();
}
-> 주석 처리한 방식으로도 헤더 모든 정보를 출력할 수 있다. 아래 코드는 더 간결한 표현이다.
<결과>
--- Headers - start ---
host: localhost:8080
connection: keep-alive
cache-control: max-age=0
sec-ch-ua: "Chromium";v="88", "Google Chrome";v="88", ";Not A Brand";v="99"
sec-ch-ua-mobile: ?0
upgrade-insecure-requests: 1
user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_0) AppleWebKit/537.36
(KHTML, like Gecko) Chrome/88.0.4324.150 Safari/537.36
accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/
webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
sec-fetch-site: none
sec-fetch-mode: navigate
sec-fetch-user: ?1
sec-fetch-dest: document
accept-encoding: gzip, deflate, br
accept-language: ko,en-US;q=0.9,en;q=0.8,ko-KR;q=0.7
--- Headers - end ---
<헤더 편리한 조회>
헤더에서 원하는 정보를 조회하는 함수다.
//Header 편리한 조회
private void printHeaderUtils(HttpServletRequest request) {
System.out.println("--- Header 편의 조회 start ---");
System.out.println("[Host 편의 조회]");
System.out.println("request.getServerName() = " +
request.getServerName()); //Host 헤더
System.out.println("request.getServerPort() = " +
request.getServerPort()); //Host 헤더
System.out.println();
System.out.println("[Accept-Language 편의 조회]");
request.getLocales().asIterator()
.forEachRemaining(locale -> System.out.println("locale = " +
locale));
System.out.println("request.getLocale() = " + request.getLocale());
System.out.println();
System.out.println("[cookie 편의 조회]");
if (request.getCookies() != null) {
for (Cookie cookie : request.getCookies()) {
System.out.println(cookie.getName() + ": " + cookie.getValue());
}
}
System.out.println();
System.out.println("[Content 편의 조회]");
System.out.println("request.getContentType() = " +
request.getContentType());
System.out.println("request.getContentLength() = " +
request.getContentLength());
System.out.println("request.getCharacterEncoding() = " +
request.getCharacterEncoding());
System.out.println("--- Header 편의 조회 end ---");
System.out.println();
}
<결과>
--- Header 편의 조회 start ---
[Host 편의 조회]
request.getServerName() = localhost
request.getServerPort() = 8080
[Accept-Language 편의 조회]
locale = ko
locale = en_US
locale = en
locale = ko_KR
request.getLocale() = ko
[cookie 편의 조회]
Idea-74cfe51e: bc07ef32-66e1-4bfe-b9eb-30fa9d50a128
[Content 편의 조회]
request.getContentType() = null
request.getContentLength() = -1
request.getCharacterEncoding() = UTF-8
--- Header 편의 조회 end ---
+) ContentType을 보면 null 값으로 되어 있는데 이는 쿼리 파라미터를 사용해 GET 방식으로 요청 메시지를 작성해서 바디 메시지가 따로 없기 때문이다.
앞서 설치한 Postman을 사용해 POST 메시지를 전송해보면 ContentType을 확인할 수 있다.
예시) ContentType : text/plain
<기타 정보>
//기타 정보
private void printEtc(HttpServletRequest request) {
System.out.println("--- 기타 조회 start ---");
System.out.println("[Remote 정보]");
System.out.println("request.getRemoteHost() = " +
request.getRemoteHost()); //
System.out.println("request.getRemoteAddr() = " +
request.getRemoteAddr()); //
System.out.println("request.getRemotePort() = " +
request.getRemotePort()); //
System.out.println();
System.out.println("[Local 정보]");
System.out.println("request.getLocalName() = " +
request.getLocalName()); //
System.out.println("request.getLocalAddr() = " +
request.getLocalAddr()); //
System.out.println("request.getLocalPort() = " +
request.getLocalPort()); //
System.out.println("--- 기타 조회 end ---");
System.out.println();
}
<결과>
--- 기타 조회 start ---
[Remote 정보]
request.getRemoteHost() = 0:0:0:0:0:0:0:1
request.getRemoteAddr() = 0:0:0:0:0:0:0:1
request.getRemotePort() = 54305
[Local 정보]
request.getLocalName() = localhost
request.getLocalAddr() = 0:0:0:0:0:0:0:1
request.getLocalPort() = 8080
--- 기타 조회 end ---
HTTP 요청 데이터 - 개요
지금까지 HttpServletRequest를 통해서 HTTP 메시지의 start-line, header 정보 조회 방법을 이해했다. 이제 본격적으로 HTTP 요청 데이터를 어떻게 조회하는지 알아보자.
먼저 HTTP 요청 메시지를 통해 클라이언트에서 서버로 데이터를 전달하는 방법을 알아보자. 주로 다음 3가지 방법을 사용한다.
<GET - 쿼리 파라미터>
/url?username=hello&age=20
메시지 바디 없이, URL의 쿼리 파라미터에 데이터를 포함해서 전달
ex) 검색, 필터 등에서 많이 사용하는 방식
<POST - HTML Form>
content-type: application/x-www-form-urlencoded
메시지 바디에 쿼리 파라미터 형식으로 전달 username=hello&age=20
ex) 회원 가입, 상품 주문, HTML Form 사용
<HTTP message body에 데이터를 직접 담아서 요청>
HTTP API에서 주로 사용, JSON, XML, TEXT
데이터 형식은 주로 JSON을 사용한다.
POST, PUT, PATCH 모두 지원
하나씩 알아보자.
HTTP 요청 데이터 - GET 쿼리 파라미터
메시지 바디 없이 URL의 쿼리 파라미터를 사용해서 데이터를 전달하자.
쿼리 파라미터는 URL에 다음과 같이 '?'를 시작으로 보낼 수 있다. 추가 파라미터는 '&'로 구분한다.
http://localhost:8080/request-param?username=hello&age=20
<RequestParamServlet.java>
/**
* 1. 파라미터 전송 기능
* http://localhost:8080/request-param?username=hello&age=20
*
* 2. 동일한 파라미터 전송 가능
* http://localhost:8080/request-param?username=hello&age=20&username=kim
*
*/
@WebServlet(name = "requestParamServlet", urlPatterns = "/request-param")
public class RequestParamServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("[전체 파라미터 조회] - start");
request.getParameterNames().asIterator()
.forEachRemaining(paramName -> System.out.println(paramName + "=" + request.getParameter(paramName)));
System.out.println("[전체 파라미터 조회] - end");
System.out.println();
System.out.println("[단일 파라미터 조회]");
String username = request.getParameter("username");
String age = request.getParameter("age");
System.out.println("username = " + username);
System.out.println("age = " + age);
System.out.println();
System.out.println("[이름이 같은 복수 파라미터 조회]");
String[] usernames = request.getParameterValues("username");
for (String name : usernames) {
System.out.println("username = " + name);
}
response.getWriter().write("ok");
}
}
<결과>
[전체 파라미터 조회] - start
username=hello
age=20
[전체 파라미터 조회] - end
[단일 파라미터 조회]
username = hello
age = 20
[이름이 같은 복수 파라미터 조회]
username = hello
username = hello2
[전체 파라미터 조회]의 경우 보통 getParameterNames, asIterator를 사용해서 모든 파라미터를 조회한다.
[단일 파라미터 조회]의 경우 getParameter(paramName)을 사용해서 조회한다.
[이름이 같은 복수 파라미터 조회]의 경우 만약 쿼리 파라미터에 중복된 파라미터 이름이 있다면 (ex. username)
getParameterValues() 메서드를 통해 중복된 파라미터를 전체 조회할 수 있다.
-> getParameter는 하나의 파라미터 이름에 대해서 단 하나의 값만 있을 때 사용해야 한다. 만약 중복일 때 getParameter를 사용하면 getParameterValues의 첫 번째 값을 반환한다.
+) 실제로 파라미터 이름을 중복으로 보내는 경우는 거의 없긴 하다.
HTTP 요청 데이터 - POST HTML Form
이번에는 HTML의 Form을 사용해서 클라이언트에서 서버로 데이터를 전송해보자.
<특징>
- content-type: application/x-www-form-urlencoded -> POST 방식은 메시지 바디가 있기 때문에 content-type이 있다. HTML Form의 경우 일반적으로 위 타입을 사용한다.
- 메시지 바디에 쿼리 파라미터 형식으로 데이터를 전달한다. -> username=hello&age=20
<webapp/basic/hello-form.html>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/request-param" method="post">
username: <input type="text" name="username" />
age: <input type="text" name="age" />
<button type="submit">전송</button>
</form>
</body>
</html>
실행해보자. -> http://localhost:8080/basic/hello-form.html
전송 버튼을 누르면 이전에 등록한 서블릿 URL인 /request-param으로 연결되게 설정했다.
POST의 HTML Form을 전송하면 웹 브라우저는 다음 형식으로 HTTP 메시지를 만든다.
POST /hello-form.html HTTP/1.1
Host: localhost:8080
Content-type: application/x-www-form-urlencoded
username=kim&age=20
application/x-www-form-urlencoded 형식은 앞서 GET에서 살펴본 쿼리 파라미터 형식과 같다. 따라서 쿼리 파라미터 조회 메서드를 그대로 사용하면 된다. (getParameter 등)
정리하면 request.getParameter()는 GET URL 쿼리 파라미터 형식도 지원하고, POST HTML Form 형식도 둘 다 지원한다.
+) Postman을 사용한 테스트
HTML Form을 테스트마다 일일이 만들기는 귀찮다. 이 때는 Postman을 사용하면 된다.
Body -> x-www-form-urlencoded 선택
HTTP 요청 데이터 - API 메시지 바디 - 단순 텍스트
이제 HTTP message body에 데이터를 직접 담아서 요청하는 방법을 알아보자.
HTTP API에서 사용하고 주로 JSON을 사용한다.
여기서는 단순 텍스트를 사용하면서 HTTP API를 익히자.
<RequestBodyStreamServlet.java>
@WebServlet(name = "requestBodyStreamServlet", urlPatterns = "/request-body-string")
public class RequestBodyStreamServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletInputStream inputStream = request.getInputStream();
String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
System.out.println("messageBody = " + messageBody);
response.getWriter().write("ok");
}
}
HTTP 메시지 바디의 데이터를 InputStream을 사용해서 직접 읽을 수 있다. inputStream은 byte 코드를 반환한다. byte 코드를 우리가 읽을 수 있는 문자(String)로 보려면 문자표(Charset)를 지정해주어야 한다. 여기서는 UTF_8 Charset을 지정해주었다.
이제 서버를 시작하고 Postman을 사용해 데이터를 전송해보자.
타입은 text/plain으로 단순 텍스트를 POST 형식으로 보내주었다.
<결과>
messageBody = hello
HTTP 요청 데이터 - API 메시지 바디 - JSON
이번에는 HTTP API에서 주로 사용하는 JSON 형식으로 데이터를 전달해보자.
<JSON 형식 전송>
POST http://localhost:8080/request-body-json
content-type: application/json
message body: {"username": "hello", "age": 20}
<RequestBodyJsonServlet.java>
@WebServlet(name = "requestBodyJsonServlet", urlPatterns = "/request-body-json")
public class RequestBodyJsonServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletInputStream inputStream = request.getInputStream();
String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
System.out.println("messageBody = " + messageBody);
response.getWriter().write("ok");
}
}
방식은 단순 텍스트 때와 동일하다.
Postman으로 실행해보자.
<결과>
messageBody={"username": "hello", "age": 20}
결과를 보면 messageBody가 그대로 출력되는 것을 볼 수 있다. JSON도 결국 문자이기 때문에 단순 텍스트 때와 동일한 결과가 출력된다.
그래서 이 JSON 형식을 파싱해서 사용할 수 있는 자바 객체로 변환하려면 JSON 변환 라이브러리를 추가해서 사용해야 한다. 여기서는 Jackson 라이브러리의 ObjectMapper를 사용하겠다.
<HelloDate.java>
@Getter @Setter
public class HelloData {
private String username;
private int age;
}
먼저 JSON 데이터 파싱용 객체를 하나 생성한다.
<RequestBodyJsonServlet.java - 수정>
@WebServlet(name = "requestBodyJsonServlet", urlPatterns = "/request-body-json")
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);
System.out.println("messageBody = " + messageBody);
HelloData helloData = objectMapper.readValue(messageBody, HelloData.class);
System.out.println("helloData.username = " + helloData.getUsername());
System.out.println("helloData.age = " + helloData.getAge());
response.getWriter().write("ok");
}
}
ObjectMapper를 추가해서 JSON messageBody를 HelloDate 객체 필드에 따라 파싱해서 출력한다.
<결과>
messageBody={"username": "hello", "age": 20}
helloData.username=hello
helloData.age=20
여기까지 HTTP 요청을 유형별로 나누어 request 객체의 데이터를 조회하는 방식을 배웠다.
이제 response의 사용법을 알아보자.
HttpServletResponse - 기본 사용법
HttpServletResponse의 역할
-> HTTP 응답 메시지 생성
- HTTP 응답코드 지정
- 헤더 생성
- 바디 생성
-> 편의 기능 제공
- Content-Type, 쿠키, Redirect
<HTTP 응답 메시지 생성>
<ReponseHeaderServlet.java>
@WebServlet(name = "responseHeaderServlet", urlPatterns = "/response-header")
public class ResponseHeaderServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//[status-line]
response.setStatus(HttpServletResponse.SC_OK);
//[response-header]
response.setHeader("Content-Type", "text/plain;charset=utf-8");
response.setHeader("Cache-Control", "no-cache,no-store,must-revalidate");
response.setHeader("Progma", "no-cache");
response.setHeader("my-header", "hello");
//[message body]
PrintWriter writer = response.getWriter();
writer.println("ok");
}
기본적인 response 메서드는 위와 같다.
[start-line] : setStatus 메서드를 사용한다. 인자로 200과 같은 상태코드를 직접 넣어도 되지만 SC_OK 같이 의미있는 표현으로 설정해주는 것이 좋다.
[response-header] : setHeader 메서드를 통해 헤더를 직접 넣어준다. 임의의 헤더를 만들어 넣어도 상관없다.
[message body] : getWriter 메서드를 통해 메시지 바디를 입력하면 된다.
<편의 기능 제공>
ReponseHeaderServlet.java에 다음 코드를 추가하고 각각의 함수들을 만들어보자.
[Header 편의 메서드]
content(response);
cookie(response);
redirect(response);
- Content 편의 메서드
private void content(HttpServletResponse response) {
//Content-Type: text/plain;charset=utf-8
//Content-Length: 2
//response.setHeader("Content-Type", "text/plain;charset=utf-8");
response.setContentType("text/plain");
response.setCharacterEncoding("utf-8");
//response.setContentLength(2); //(생략시 자동 생성)
}
앞서 setHeader 메서드를 통해 헤더를 직접 입력했는데, 서블릿은 setContentType과 같이 헤더 편의 메서드를 제공한다.
+) ContentLength의 경우 직접 입력해도 되지만 생략시 메시지 바디에 따라 자동 생성된다.
- 쿠키 편의 메서드
private void cookie(HttpServletResponse response) {
//Set-Cookie: myCookie=good; Max-Age=600;
//response.setHeader("Set-Cookie", "myCookie=good; Max-Age=600");
Cookie cookie = new Cookie("myCookie", "good");
cookie.setMaxAge(600); //600초
response.addCookie(cookie);
}
쿠키 역시 헤더로 문법 맞춰가면서 직접 입력하기 어렵기 때문에 서블릿은 Cookie 객체를 제공한다. 쿠키의 MaxAge 등의 정보를 설정할 수 있다.
- redirect 편의 메서드
private void redirect(HttpServletResponse response) throws IOException {
//Status Code 302
//Location: /basic/hello-form.html
//response.setStatus(HttpServletResponse.SC_FOUND); //302
//response.setHeader("Location", "/basic/hello-form.html");
response.sendRedirect("/basic/hello-form.html");
}
HTTP Redirect가 필요할 때 상태 코드(302)와 헤더(Location)를 따로 설정할 필요 없이 sendRedirect 메서드 하나로 한 번에 Redirect를 설정할 수 있다.
+) 참고로 HTTP 응답 메시지가 잘 작성되었나 확인하려면 서버 URL 페이지에서 F12(or 우클릭->검사) -> 네트워크 탭을 통해서 확인할 수 있다.
HTTP 응답 데이터 - 단순 텍스트, HTML
HTTP 응답 메시지는 요청 메시지 때와 마찬가지로 3가지 유형을 담아서 전달한다.
- 단순 텍스트 응답 : 앞에서 살펴봄(writer.println("ok"))
- HTML 응답
- HTTP API : MessageBody JSON 응답
여기서는 HTML 응답에 대해 알아보자.
<ResponseHtmlServlet.java>
@WebServlet(name = "responseHtmlServlet", urlPatterns = "/response-html")
public class ResponseHtmlServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//Content-Type: text/html;charset=utf-8
response.setContentType("text/html");
response.setCharacterEncoding("utf-8");
PrintWriter writer = response.getWriter();
writer.println("<html>");
writer.println("<body>");
writer.println(" <div>안녕</div>");
writer.println("</body");
writer.println("</html>");
}
}
HTTP 응답으로 HTML을 반환할 때는 content-type을 text/html로 지정해야 한다.
그리고 메시지 바디를 위 코드처럼 직접 다 작성해 주어야 한다.
+) 서버를 실행하고 페이지 소스를 보면 코드에서 작성한 html을 확인할 수 있다.
HTTP 응답 데이터 - API JSON
보통 실무에서는 응답 데이터로 JSON 형식을 많이 사용한다.
<ResponseJsonServlet.java>
@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.setContentType("application/json");
response.setCharacterEncoding("utf-8");
HelloData helloData = new HelloData();
helloData.setUsername("kim");
helloData.setAge(20);
//{"username": "kim", "age": 20}
String result = objectMapper.writeValueAsString(helloData);
response.getWriter().write(result);
}
}
HTTP 응답으로 JSON을 반환할 때는 content-type을 application/json 로 지정해야 한다.
Jackson 라이브러리가 제공하는 objectMapper.writeValueAsString() 를 사용하면 객체를 JSON 문자로 변경할 수 있다.
+) 참고 : application/json은 스펙상 utf-8 형식을 사용하도록 정의되어 있다. 따라서 application/json이라고만 사용해야지 application/json;charset=utf-8이라고 전달하는 것은 의미 없는 파라미터를 추가한 것이 된다.
-> response.getWriter()를 사용하면 이러한 추가 파라미터를 자동으로 추가해주기 때문에 JSON 사용시 utf-8 파라미터를 추가하지 말자.
'java > spring' 카테고리의 다른 글
[SpringMVC] MVC 프레임워크 만들기 (0) | 2021.04.23 |
---|---|
[SpringMVC] 서블릿, JSP, MVC 패턴 (0) | 2021.04.23 |
[SpringMVC] 웹 애플리케이션 이해 (0) | 2021.04.21 |
[Spring] devtools (0) | 2021.02.10 |
스프링 - 롬복(lombok) 라이브러리 설치 (0) | 2021.02.09 |