Search
Duplicate

서블릿

목차

프로젝트 생성

사전 준비물

JDK 11 +
IDE: IntelliJ or Eclipse( or else ...)

프로젝트 생성

스프링 공식 홈페이지에서 프로젝트 구성 (https://start.spring.io)
IntelliJ Ultimate 이상이면 자체적으로 생성가능

프로젝트 구성

Progject: Gradle Project
Language: Java
Spring Boot: 2.4.x
Project Metadata
Group: hello
Artifact:servlet
Name:servlet
Package name: hello.sevlet
Packaging: War(주의!)
Java: 11

의존성 관리

Spring Web
Lombok
Lombok 적용
1.
Preferences → plugin → lombok 검색 실행(재시작)
2.
Preferences → Annotation Processors 검색 → Enable annotation processing 체크(재시작)

실행및 확인

1.
ServletApplication.main() 실행 (해당 위치에서 우클릭해서 run을 하거나 IDE 우측상단에서 해등 어플리케이션 실행을하거나 한다.)
2.
http://localhost:8080 으로 접속해서 Whitelabel Error Page가 나올경우 정상 동작이다.
⇒ 현재 매핑될 페이지가 구현되지 않았기에 에러페이지가 나오는게 정상.

Postman 설치

url 테스트를 위해 postman을 설치해두자.

Address already in use (Bind failed) 해결하기

해당 포트를 이미 다른 프로젝트에서 실행중이거나 IDE를 재시작하면서 의도치않게 이전에 실행중이던 프로세스가 종료되지 못한 경우 해당 에러가 발생하는데 아래와 같이 해결할 수 있다.
터미널(or CMD) 에서 이미 실행중인 포트를 찾아야한다.
맥OS
lsof -i :포트번호 kill -9 찾은PID
Bash
복사
윈도우
netstat -ano | findstr :포트번호 taskkill /f /pid 찾으PID
Bash
복사
위 명령어로 이미 실행중이라고 나오는 프로세스를 종료 후 다시 프로젝트를 실행해보자.

사전준비: view 페이지 작성

프론트 학습이 아니기에 HTML 파일은 제공해주는 것을 사용하도록 하자.
경로는 src/main 디렉토리 하위에 webapp디렉토리를 생성해주자.
index.html
basic.html
basic/hello-form.html
작성 완료 후 패키지 구조
Wellcome Page: Index.html은 웰컴페이지인데 따로 경로를 지정해주지않아도 루트 경로로 실행되는 기분 페이지

참고: 위와같은 설정이 귀찮거나 이미 익숙해서 학습할 필요가 없는 경우

준비해둔 리포지토리를 클로닝해 챕터는 브랜치별로 나뉘어져있고 챕터는 커밋별로 구분되있어서 사용하면 된다.

Hello 서블릿

이제 스프링 부트 환경에서 서블릿을 등록하고 사용해보자.

1. @ServletComponentScan 추가

:프로젝트의 ServletApplication 클래스에 가서 @ServletComponentScan 애노테이션을 추가한다
이 애노테이션은 프로젝트내의 애노테이션이 선언된 클래스가 포함된 패키지부터 그 하위 패키지까지 작성된 서블릿들을 등록하고 사용할수 있도록 해준다.
package hello.servlet; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.servlet.ServletComponentScan; @ServletComponentScan //servlet auto register @SpringBootApplication public class ServletApplication { public static void main(String[] args) { SpringApplication.run(ServletApplication.class, args); } }
Java
복사

2. HelloServlet 생성

이제 실제로 서블릿을 생성해서 테스트 해 볼 필요가 있다. 패키지 경로 hello.servlet.basic에 HelloServlet을 만들어보자.
package hello.servlet.basic; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @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); } }
Java
복사
Servlet을 사용하기위해서는 HttpServlet을 상속받아야한다.
더하여 service 메서드를 오버라이딩해서 사용해야 한다.(접근제어자가 protected)
@WebServlet은 해당 클래스를 서블릿으로 사용할 수 있게 해주는 애노테이션
name속성으로 서블릿 이름을 지정
urlPatterns 속성으로 서블릿에 매핑 될 request url 을 지정해준다.
주의! 모든 속성이 중복되서는 안된다.
HTTP 요청을 통해 매핑된 URL이 호출되면 서블릿 컨테이너는 service 메서드를 실행한다.
실행 및 동작 확인
웹브라우저 혹은 Postman을 이용해 url을 작성해서 요청
http://localhost:8080/hello?username=catsbi
URL 호출시 프로젝트 콘솔창 출력화면

참고: HTTP 요청 메세지를 로그 출력하기

웹 브라우저 혹은 Postman을 통해 요청하는 HTTP 요청 메세지 혹은 응답 메세지를 직접 확인해보고 싶다면, 설정파일에 로깅 옵션을 넣어서 확인하자.
logging.level.org.apache.coyote.http11=debug
YAML
복사
⇒ 운영 서버에서 사용했다간 성능저하가 발생할 수 있으니 개발단계에서만 사용하자.
⇒ 요청했을 경우 출력되는 로그 내역
주의 IntelliJ 무료 버전에서 정상 동작하지 않는다면 자바로 직접실행해서 주의사항을 참고하자

3. 응답 메세지 작성및 확인

작성한 HelloServlet의 service 메서드에 다음 코드를 추가하자.
response.setContentType("text/plain"); response.setCharacterEncoding("utf-8"); response.getWriter().write("hello "+username);
Java
복사
⇒ response.setHeader("Content-Type", "text/plain;charset=utf-8") 로 작성해도 되지만, 매번 key/value를 모두 작성하는것은 힘들고 오타가 날 확률도 높아지기에 위와같은 편의메서드를 제공한다.
이제 다시 http://localhost:8080/hello?username=catsbi URL로 요청을 보낸 다음 응답 메세지를 확인해보자. 여기서는 Postman을 통해 확인한다.
Postman의 요청 응답 메세지 화면
응답 메세지로 hello + username(catsbi)가 정상적으로 출력되는것을 확인할 수 있다.

서블릿 컨테이너의 동작 방식

코드를 통해 프로젝트에서 서블릿을 생성/등록 후 HTTP 요청을 통해 동작이 되는것도 확인을 해보았다.
그럼 이 서블릿컨테이너와 등록된 서블릿은 어떻게 동작을하길래 요청을받고 응답을 해주는 것일까?
여기서는 스프링부트를 기반으로 설명한다.

1. 내장 톰캣 생성

스프링 부트 내부에 포함된 내장톰캣이 띄워지면서 톰캣이 가지고있는 서블릿 컨테이너를 통해 서블릿을 생성하고 등록해준다.

2. HTTP request, HTTP response

HTTP request
⇒ 웹 브라우저에서 자료 좌측의 HTTP 요청 포맷을 만들어서 서버에 요청한다.
HTTP response
⇒ 서버에서 로직 수행이 끝나면 자료 우측과같은 HTTP 응답 메세지를 만들어 웹 브라우저에 전달한다.

3. 웹 애플리케이션 서버의 요청 응답 구조

서버에서는 요청받은 HTTP request를 가지고 request, response 객체를 생성
서블릿 컨테이너에 싱글톤으로 등록된 서블릿 중 매핑되는 서블릿(HelloServlet)을 호출하며 생성한 request, response 객체 전달
해당 서블릿에선 자신이 작성한 비즈니스로직 수행
서블릿에서 작성한 응답객체(response)를 기반으로 HTTP 응답 메세지를 만들어서 웹 브라우저로 반환
참고: HTTP 응답에서 Content-Length는 웹 애플리케이션 서버가 자동으로 생성해준다.

HttpServletRequest - 개요

역할

HTTP 요청 메세지
웹 브라우저에서는 위와같이 HTTP 요청 메세지를 서버에 전달한다. 결국 텍스트구조일텐데 이걸 개발자가 서블릿내에서 사용하려면 직접 하나하나 파싱해줘야 한다.
HttpServletRequest는 이런 파싱작업을 개발자 대신 진행해서 담아 놓은 객체로 개발자가 HTTP 요청 메세지를 편리하게 사용할 수 있도록 해주는 객체이다.

요청 메세지 분석

위의 HTTP 요청 메세지를 HttpServletRequest 객체를 사용하면 편리하게 사용가능하다.
START LINE
⇒ HTTP 메소드
⇒ URL
⇒ Query String
⇒ scheme, protocol
Header
⇒ 헤더 조회
Body
⇒ form 파라미터 형식 조회
⇒ message body 데이터 직접 조회
추가적으로 HttpServletRequest는 부가기능도 함께 제공한다.
1.
임시 저장소 기능
해당 HTTP 요청이 시작부터 끝날 때까지 유지되는 임시 저장소 기능
⇒ 저장: reuqest.setAttribute(name, value)
⇒ 조회: request.getAttribute(name)
2.
세션 관리 기능
request.getSession(create:true)

중요

⇒ HttpServletRequest, HttpServletResponse를 사용할 때 중요한 점은 HTTP 요청및 응답 메세지를 편리하게 사용해주는 객체이기 때문에 이런 기능을 제대로 사용하려면 HTTP 스펙을 학습해야한다.

HttpServletRequest - 기본 사용법

위에서 살펴보면 HttpServletRequest가 제공하는 기본 기능및 사용법을 알아보자.

RequestHeaderServlet 작성

package hello.servlet.basic.request; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @WebServlet(name = "requestHeaderServlet", urlPatterns = "/request-header") public class RequestHeaderServlet extends HttpServlet { @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { printStartLine(request); printHeader(request); printHeaderUtils(request); printEtc(request); response.getWriter().write("ok"); } 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(); } private void printHeader(HttpServletRequest request) { System.out.println("--- Headers - start ---"); request.getHeaderNames().asIterator() .forEachRemaining(headerName -> System.out.println(headerName + ":" + request.getHeader(headerName))); System.out.println("--- Headers - end ---"); System.out.println(); } private void printStartLine(HttpServletRequest request) { System.out.println("--- REQUEST-LINE - start ---"); System.out.println("request.getMethod() = " + request.getMethod()); //GET System.out.println("request.getProtocal() = " + request.getProtocol()); //HTTP/1.1 System.out.println("request.getScheme() = " + request.getScheme()); //http http://localhost:8080/request-header System.out.println("request.getRequestURL() = " + request.getRequestURL());// /request-test System.out.println("request.getRequestURI() = " + request.getRequestURI());//username=hi System.out.println("request.getQueryString() = " + request.getQueryString()); System.out.println("request.isSecure() = " + request.isSecure()); //https사용 유무 System.out.println("--- REQUEST-LINE - end ---"); System.out.println(); } 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(); } }
Java
복사

웹브라우저 (or Postman)으로 HTTP 요청 메세지 전달

http://localhost:8080/request-header?username=catsbi
Plain Text
복사

START-LINE 정보 조회

HTTP 요청 메세지의 필요한 정보들을 메서드를 통해 추출이 가능하다.
Method, Protocol, scheme, URL, URI, querystringhttps인지 구분하는 isSecure까지 제공한다.

Header 정보 조회

Header 편의 정보 조회

HTTP 요청 메세지의 Header 정보를 좀 더 편리하게 꺼내서 사용할수도 있다.
Accept-Language 조회는 getLocale을 통해 조회하면 가장 우선순위가 높은 최상단의 locale 조회되고, iterator를 통해 전체 조회를 하면 모든 locale을 볼수도 있다.
Content-Type은 null이 나오는데 이는 요청메세지에 내용이 담겨있지 않기 때문인데, Postman을 통해 테스트가 가능하다.
⇒ Postman에서 Content 작성
⇒ 요청 콘솔 출력 확인

기타 조회

기타 부가적인 정보(Remote(요청을 보낸 곳), Local(내 컴퓨터))를 조회할수도 있다.
로컬에서 테스트할 때는 IPv6정보가 나오는데, IPv4정보를 보고싶은 경우, VM options에 다음 파라미터를 작성해주면 된다.
-Djava.net.preferIPv4Stack=true

HTTP 요청 데이터 - 개요

HTTP 요청 메세지를 통해 클라이언트에서 서버로 데이터를 전달하는데, 그 방법은 주로 다음 3가지를 사용한다.

GET- Query Parameter

/url?keyA=valueA&keyB=valueB
메세지 바디 없이 URL의 쿼리 파라미터에 데이터를 포함해서 전달하는 방식
⇒ 검색, 필터, 페이징등에서 많이 사용하는 방식이다.

POST - HTML Form

content-type: application/x-www-form-urlencoded
메세지 바디에 쿼리 파라미터 형식으로 전달한다.
⇒ keyA=valueA&keyB=valueB
⇒ 회원 가입, 상품 주문, HTML Form 사용
POST - HTML Form 전송

HTTP message body에 데이터를 담아 요청

HTTP API에서 주로 사용하며 JSON, XML, TEXT 사용
데이터 형식은 주로 JSON이 사용된다.
⇒ POST, PUT, PATCH

HTTP 요청 데이터 - GET 쿼리 파라미터

우선 위에서 살펴본 HTTP 요청 데이터 전달 방식중 GET Query Parameter를 사용해보자.
전달 데이터는 username, age 두개이고
username = catsbi
age = 20
메세지 바디 없이 URL Query Parameter를 사용해서 전달한다.
쿼리파라미터는 URL에 ? 를 시작으로 작성하며 하나 이상의 파라미터는 & 구분자를 통해 추가한다.
http://localhost:8080/request-param?username=catsbi&age=20
위 쿼리파라미터가 포함된 URL을 통해 요청을 할 것이고 이를 받아줄 서블릿을 작성해보자.

RequestParamServlet

package hello.servlet.basic.request; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * 1. 파라미터 전송 기능 * http://localhost:8080/request-param?username=hello&age=20 */ @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("[단일 파라미터 조회] - start"); String username = request.getParameter("username"); String age = request.getParameter("age"); System.out.println("username = " + username); System.out.println("age = " + age); System.out.println("[단일 파라미터 조회] - end"); System.out.println(); response.getWriter().write("ok"); } }
Java
복사
해당 서블릿을 만들어줬으면 이제 실행해서 확인해보자.
실행 결과
getParameter()메서드를 통해 단일 파라미터 조회가 가능하다.
getParameterNames() 메서드는 전달받은 쿼리스트링의 모든 파라미터의 key값을 이터레이터로 반환한다.

복수 파라미터 조회

username은 catsbi라는 결과가 출력되었다. 그런데 만약 username을 한번 더 작성한다면 어떻게 될까?
에러는 발생하지 않고 하나의 파라미터의 여러 값을 전달할수도 있는데, 기본적으로 우선 작성된 파라미터값이 나오며 전체를 다 꺼내서 확인할수도 있다.
service 메서드에 다음 코드를 작성해서 확인해보자.
System.out.println("[복수 파라미터 조회]"); String[] usernames = request.getParameterValues("username"); for (String name : usernames) { System.out.println("username="+name); }
Java
복사
http://localhost:8080/request-param?username=catsbi&age=20&username=world
[실행 결과]
getParameterValues() 메서드를 통해 해당 key값의 value를 모두 꺼낼 수 있다.

HTTP 요청 데이터 - POST HTML Form

이번에는 HTML의 Form을 상요해 클라이언트에서 서버로 데이터를 전송해보자.

특징

content-type: application/x-www-form-urlencoded
메세지 바디에 쿼리 파라미터 형식으로 데이터를 전달한다.

1. 웹 브라우저에서 hello-form 페이지 접속

기존 프로젝트 준비과정에서 작성한 webapp/basic/hello-form.html 페이지에 접속하도록 한다.
경로는 http://localhost:8080/basic/hello-form.html이다.
이제 form에 username과 age를 작성한 뒤 전송버튼을 눌러서 확인해보자. 이 HTML 파일은 위에서 작성한 서블릿 RequestParamServlet인 /request-param경로로 요청을 하게되는데 실행결과를 보면 다음과 같이 정상적으로 출력된다.
이 결과를 통해 해당 서블릿에서는 GET 쿼리스트링, POST Form방식 둘 다 쿼리스트링의 형태로 요청을 하기 때문에 getParameter로 파라미터를 추출할 수 있다.
정리하면, getParameter()메서드는 GET URL 쿼리 파라미터POST HTML Form 형식 모두 지원한다.

참고 : Postman을 이용한 테스트

이번 POST - HTML Form 테스트를 위해 HTML 페이지(hello-form.html)을 만들었다. 그럼 매번 이렇게 HTML 페이지를 만들어야할까? 이를 Postman을 사용하면 손쉽게 테스트를 할 수 있다.
Postman을 이용한 POST - Form 방식 전송
위와같이 정상적으로 수행이 안된다면 내가 Body를 x-www-form-urlencoded로 선택이 잘 되었는지, Header의 content-type이 application/x-www-form-urlencoded로 설정되있는지 확인하자.

HTTP 요청 데이터 - API 메시지 바디 - 단순 텍스트

HTTP API에서 주로 사용하는 방식으로 HTTP message body에 개발자가 직접 데이터를 담아서 요청하는 방식이다. JSON, XML, TEXT가 주로 사용되는 포맷 형식인데 최근에 들어서는 거진 JSON이 사용된다.
POST, PUT, PATCH사용에 주로 사용된다.
이번 챕터에서는 단순한 텍스트 메세지를 HTTP message body에 담아 전송하고 읽어보자.

RequestBodyStringServlet

package hello.servlet.basic.request; import org.springframework.util.StreamUtils; import javax.servlet.ServletException; import javax.servlet.ServletInputStream; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.nio.charset.StandardCharsets; @WebServlet(name = "requestBodyStringServlet", urlPatterns = "/request-body-string") public class RequestBodyStringServlet 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"); } }
Java
복사
HTTP message body는 InputStream을 사용해서 직접 읽을 수 있다.
StreamUtil 유틸 클래스를 통해 InputStream을 String 으로 편하게 바꿀수 있는데 파라미터에 문자표(Charset)을 지정해줘야한다. 여기서는 UTF-8을 지정해줬다.

Postman을 이용해 동작 확인

URL은 POST이고 http://localhost:8080/request-body-string
content-type은 text/plain
message body는 raw의 text로 설정한 뒤 hello 작성
실행 결과

HTTP 요청 데이터 - API 메시지 바디 - JSON

이제 주로 쓰이는 JSON으로 HTTP API를 작성해 요청해보자.

HelloData

JSON형식을 사용할때는 해당 데이터를 파싱해서 객체화 해줄 객체를 만들어줄 필요가 있다.
username과 age를 담을 수있는 JSON parse 객체를 만들어주자
package hello.servlet.basic; import lombok.Getter; import lombok.Setter; import lombok.ToString; @Getter @Setter @ToString public class HelloData { private String username; private int age; }
Java
복사
롬복(Lombok)을 사용해서 getter, setter toString 메서드를 생략할 수 있다.

RequestBodyJsonServlet

JSON 방식의 API와 매핑해줄 서블릿 RequestBodyJsonServlet을 생성해준다.
package hello.servlet.basic.request; import com.fasterxml.jackson.databind.ObjectMapper; import hello.servlet.basic.HelloData; import org.springframework.util.StreamUtils; import javax.servlet.ServletException; import javax.servlet.ServletInputStream; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.nio.charset.StandardCharsets; @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 = " + helloData); } }
Java
복사
StreamUtil 유틸 클래스로 InputStream을 읽어와서 이를 스프링부트에서 기본으로 제공하는 Jackson 이라는 json 라이브러리를 사용해서 이 값을 객체로 읽어올 수 있다.
objectMapper.readValue(messageBody, HelloData.class);

Postman 전송 양식

URL: POST http://localhost:8080/request-body-json
content-type은 application/json
message body는 다음과 같이 작성한다.
{ "username":"catsbi", "age":20 }
JSON
복사
실행 결과

HttpServletResponse - 기본 사용법

지금까지 요청에 대해 알아보았고 이제 응답메세지에 대해 알아보자.
HttpServletResponse 객체 역시 요청객체인 HttpServletRequest와 같은 목적으로 개발자가 HTTP 응답 메세지를 직접 작성하려면 너무 번거롭다보니 이를 자동으로 작성해주기위한 편의 객체이다.
Response 객체를 통해 응답 메세지 생성을 하기위해서는
HTTP 응답 코드 지정
헤더 생성
바디 생성
을 해주면 되며, 모두 지원을 해주고 편의 기능도 제공해준다.
Content-Type, 쿠키, Redirect

학습해보기

1. HttpServletResponse

package hello.servlet.basic.response; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; @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-headers] // response.setHeader("Content-Type", "text/plain;charset=utf-8"); response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); response.setHeader("Pragma", "no-cache"); response.setHeader("my-header", "hello"); //[Header 편의 메서드] content(response); //[Cookie 편의 메서드] cookie(response); //[redirect 편의 메서드] redirect(response); //[message body] PrintWriter writer = response.getWriter(); writer.println("성공"); } 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"); } 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); } 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); } }
Java
복사
HttpServletResponse.SC_OK
⇒ 서블릿에서는 HttpServletResponse에서 상태코드를 의미있는 상수로 작성해놔서 이를 사용하는 것이 혹시 모를 오타를 방지하고 가독성을 높힐 수 있다.
response.setHeader(...)
⇒ 예제 코드에 작성된 헤더 내용은 캐시를 완전히 무효화 하겠다는 내용이고, 주석된 내용은 ContentType 편의 메서드인 content(response)에서 작성을 해줬기에 주석처리 해준다.

1-1. Content 편의 메소드:: content(response)

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); }
Java
복사
setHeader를 통해 name/value로 값을 넣어줘도 되지만, 좀 더 편하게 작성할 수 있도록 contentType과 charset을 바로 세팅할 수 있는 편의 메서드를 제공한다. 그리고 content-length역시 직접 지정해줄 수 있지만, 자동으로 서블릿에서 계산해서 설정되도록 하는것이 좋다.

1-2 Cookie 편의 메소드:: cookie(response)

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); }
Java
복사
쿠키 편의 메소드로 편의 메소드를 사용하지 않으면 setHeader메소드에 Set-Cookie 라는 name으로 value를 직접 입력해줘야 하지만, 편의 메서드를 통해 쿠키와 쿠키의 유효시간을 모두 손쉽게 설정할 수 있다.

1-3 redirect편의 메소드:: redirect(response)

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"); }
Java
복사
리다이렉트 편의 메서드를 사용하지 않고 리다이렉트를 유도하려면 response의 상태코드를 SC_FOUND(302)를 설정한 뒤 Header에 Location의 값으로 리다이렉션 해야하는 경로를 입력해줘야한다.
하지만, 편의메서드인 sendRedirect를 사용하며 리다이렉션할 경로만 파라미터로 넣어주면, 자동으로 302 상태코드와 리다이렉션할 경로를 응답메세지에 설정해준다.

HTTP 응답 데이터 - 단순 텍스트, HTML

응답 데이터도 요청과 비슷하지만, 응답 내용을 분류하면 단순하게 텍스트 응답, HTML응답, HTTP API 응답이 있다.
그래서 이 응답 데이터를 이용해 사용자에게 HTML 페이지를 보여주기도하고 데이터를 가공해서 DOM 조작을 통해 페이지의 내용을 변경시킬수도 있다.

학습하기

1. ResponseHtmlServlet

package hello.servlet.basic.response; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; @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>"); } }
Java
복사
HTML 으로 반환한다는 내용을 setContentType으로 content-type을 지정해줘야 웹 브라우저에서는 정상적으로 해당 응답 결과 message body를 html으로 렌더링해서 사용자에게 보여준다.
서블릿을 통해 html 소스를 작성해주었으며, 동적으로 내용이 추가되거나 분기를 해야하는 경우 해당 메서드에서 작업을 수행하면 된다.
⇒ EX: writer.println("<div>안녕하세요."+ username +"님</div>");

2. 웹 브라우저에서 해당 서블릿으로 접근

http://localhost:8080/response-html
위 URL을 서버에 요청하면 다음과 같은 페이지가 나온다.
이를 페이지 소스보기를 통해 소스를 보면, 다음과 같이 나온다.

HTTP 응답 데이터 - API JSON

응답 메세지의 분류중 API JSON 타입에 대해서 알아보자.

학습하기

1. ResponseJsonServlet

package hello.servlet.basic.response; import com.fasterxml.jackson.databind.ObjectMapper; import hello.servlet.basic.HelloData; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @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("catsbi"); helloData.setAge(20); String result = objectMapper.writeValueAsString(helloData); response.getWriter().write(result); } }
Java
복사
ObjectMapper를 사용해서 HelloData 객체를 json으로 만들어주고있다.
content-type을 application/json으로 지정해야 한다.
application/json은 기본이 utf-8로 charset이 설정되있기에 setCharacterEncoding을 해주지 않아도 된다.
objectMapper.writeValueAsString() 메서드를 사용하면 객체를 JSON 문자로 변경할 수 있다.

2. 웹브라우저에서 URL 요청

http://localhost:8080/response-json
JSON 포맷으로 데이터가 정상적으로 응답되는 것을 확인할 수 있다.
(위 포맷은 크롬 익스텐션을 사용했기에 나오는 형식)

이전 챕터로

다음 챕터로