공부/Spring

서블릿(Servlet)

Stair 2025. 3. 11. 15:41
반응형

참고 : 서블릿 JSP 기술은 거의 사장되었다고 봐도 무방한 기술들이다. 하지만 MVC 패턴이 나오게 된 계기가 되는 것들이니 만큼 학습을 하고 넘어가려 한다.

+ 아직까지 가끔? JSP를 쓰는 기업들이 보이기는 하는거 같다.

 

우선 스프링 부트를 사용해 스프링 프로젝트를 생성하자.

https://start.spring.io/

 

Dependencies는 Spring Web, Lombok을 선택하여 만든다.

packaging은 war를 선택한다. JSP를 실행하기 위해 필요하다.

Hello 서블릿

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

 

*참고 : 서블릿은 톰캣 같은 웹 애플리케이션 서버를 직접 설치하고, 그 위에 서블릿 코드를 클래스 파일로 빌드해서 올린 다음, 톰캣 서버를 실행하면 된다. 하지만 이과정은 매우 번거롭기에 스프링 부트를 활용하여 톰캣 서버 설치 없이 서블릿 코드를 실행해보자.

 

처음 실행시키면 시간이 오래걸린다. 인내심을 가지고 기다리자.

우선 ServletApplication에

@ServletComponenetScan을 추가해주자.

@ServletComponentScan //서블릿 자동 등록
@SpringBootApplication
public class ServletApplication {

    public static void main(String[] args) {
       SpringApplication.run(ServletApplication.class, args);
    }

}

 

ServletComponentScan은 하위에 있는 컴포넌트들을 서블릿으로 자동 등록해주는 어노테이션이다.

이후 basic 패키지에 HelloServlet이라는 클래스를 만든다.

@WebServlet(name = "helloServlet", urlPatterns = "/hello") // /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);

    }
}

 

이전에 학습한 것처럼 서블릿은 request와 response를 받고 응답을 내려준다.

 

만약 파라미터를 넣은 URL을 검색하면 어떻게 될까?

 

http://localhost:8080/hello?username=kim

@WebServlet(name = "helloServlet", urlPatterns = "/hello") // /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);

    }
}

HelloServlet.service
request = org.apache.catalina.connector.RequestFacade@78415045
response = org.apache.catalina.connector.ResponseFacade@6ffbf683
username = kim

 

servlet을 통해 username이 kim임을 알 수 있다. request를 통해 username, kim을 받았기 때문이다.

 

응답 메시지를 보내려면 어떻게 해야할까?
response를 활용해야 한다.

response.setContentType("text/plain"); //헤더 정보에 들어감
response.setCharacterEncoding("utf-8"); //헤더 정보에 들어감
response.getWriter().write("hello " + username);

hello kim의 응답을 받은것을 알 수 있다.

 

정리

@WebServlet : 서블릿 애노테이션

  - name : 서블릿 이름

  - urlPatterns : URL 매핑

HTTP 요청을 통해 매핑된 URL이 호출되면 서블릿 컨테이너는 서비스 메서드를 실행한다.

 

log를 찍어보도록 하자. resources/application.properties에 log 관련 한줄을 추가해준다.

logging.level.org.apache.coyote.http11=trace

 

그러면 로그를 확인할 수 있다.

 

 

동작 방식을 확인해보자.

스프링 부트를 띄우게 되면 스프링 부트가 내장하고 있는 내장 톰캣 서버를 띄워주게 된다.

이 내장 톰캣 서버는 내부에 서블릿 컨테이너 기능이 있기 때문에 서블릿 컨테이너와 서블릿을 생성해준다.

 

이후에 localhost:8080으로 요청을 하면

HTTP가 응답을 하게 된다.

 

 

HTTP 요청이 오게 되면 요청 메시지를 기반으로 request와 response를 만들어서 helloServlet을 호출한다. 이후 응답을 위한 WAS서버가 HTTP응답을 만들어서 response를 반환한다.

 

서블릿 예제들을 배우기 위한 HTML파일들을 만들자.

 

index.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<ul>
    <li><a href="basic.html">서블릿 basic</a></li>
</ul>
</body>
</html>

 

basic.html

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<ul>
  <li>hello 서블릿
    <ul>
      <li><a href="/hello?username=servlet">hello 서블릿 호출</a></li>
    </ul>
  </li>
  <li>HttpServletRequest
    <ul>
      <li><a href="/request-header">기본 사용법, Header 조회</a></li>
      <li>HTTP 요청 메시지 바디 조회
        <ul>
          <li><a href="/request-param?username=hello&age=20">GET - 쿼리
            파라미터</a></li>
          <li><a href="/basic/hello-form.html">POST - HTML Form</a></
          li>
          <li>HTTP API - MessageBody -> Postman 테스트</li>
        </ul>
      </li> </ul>
  </li>
  <li>HttpServletResponse
    <ul>
      <li><a href="/response-header">기본 사용법, Header 조회</a></li>
      <li>HTTP 응답 메시지 바디 조회
        <ul>
          <li><a href="/response-html">HTML 응답</a></li>
          <li><a href="/response-json">HTTP API JSON 응답</a></li>
        </ul>
      </li>
    </ul>
  </li>
</ul>
</body>
</html>

 

 

HTTPServletRequest - 개요

HTTPServletRequest역할

HTTP 요청 메시지를 개발자가 직접 파싱해서 사용해도 되지만, 매우 불편하다. 서블릿은 개발자가 HTTP 요청 메시지를 편리하게 사용할 수 있도록 개발자 대신에 HTTP 요청 메시지를 파싱한다. 그리고 그 결과를 'httpServletRequest'객체에 담아서 제공한다.

 

HttpServletRequest를 사용하면 다음과 같은 HTTP 요청 메시지를 편리하게 조회할 수 있다.

 

요청메시지의 대략적인 구조는 다음과 같다.

 

-START LINE

    - HTTP 메소드

    - URL

    - 쿼리 스트링

    - 스키마, 프로토콜

- 헤더

    - 헤더 조회

- 바디

    - from 파라미터 형식 조회

    - message body 데이터 직접 조회

 

HttpServletRequest 객체는 추가로 여러가지 부가기능도 함께 제공한다.

1. 임시 저장소 기능 - 해당 HTTP 요청이 시작부터 끝날떄까지 유지되는 임시 저장소 기능

    - 저장 : request.setAttribuet(name, value)

    - 조회 : request.getAttribute(name)

2. 세션 관리 기능 - request.getSession(create : true);

 

HttpServletRequest, HttpServletResponse를 사용할 떄 가장 중요한 점은 이 객체들이 HTTP 요청 메시지, HTTP 응답 메시지를 편리하게 사용하도록 도화주는 객체라는 점이다. 따라서 이 기능에 대해서 이해하려면 HTTP 스펙이 제공하는 요청, 응답 메시지 자체를 이해해야 한다.

 

 

 

HttpServletRequest - 기본 사용법

 

HTTP의 헤더 정보를 받아보기 위해 RequestHeaderServlet클래스를 만들었다.

@WebServlet(name = "requestHeaderServlet", urlPatterns = "/request-header")
public class RequestHeaderServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        printStartLine(request);
    }

    private static 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
        // http://localhost:8080/request-header
        System.out.println("request.getRequestURL() = " + request.getRequestURL());
        // /request-header
        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();
    }
}

 

http://localhost:8080/request-header?username=hello

를 검색하게 되면 HTTP 요청의 헤더를 확인할 수 있다.

 

--- REQUEST-LINE - start ---
request.getMethod() = GET
request.getProtocol() = 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 ---

 

@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);
    }

    private static 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
        // http://localhost:8080/request-header
        System.out.println("request.getRequestURL() = " + request.getRequestURL());
        // /request-header
        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();
    }

    //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 + ": " + headerName);
        }*/

        request.getHeaderNames().asIterator()
                        .forEachRemaining(headerName -> System.out.println(headerName + ": " + headerName));


        System.out.println("--- Headers - 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();
    }

    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();
    }
}

 

다양한 헤더 정보 조회 방법을 확인해보자.

 

 

 

HTTP 요청 데이터 - 개요

HTTP 요청 메시지를 통해 클라이언트에서 서버로 데이터를 전달하는 방법을 알아보자.

 

주로 3가지 방법을 사용한다.

1. GET - 쿼리 파라미터

    - /url ?username=hello&age=20

    - 메시지 바디 없이, URL의 쿼리 파라미터에 데이터를 포함해서 전달한다.

    - 예) 검색, 필터, 페이징 등에서 많이 사용하는 방식이다.

2. POST - HTML Form

    - content-type : application/x-www-form-urlencoded

    - 메시지 바디에 쿼리 파라미터 형식으로 전달 username=hello&age=20

    - 예)회원 가입, 상품 주문, HTML Form 사용

3. HTTP message body에 데이터를 직접 담아서 요청

    - HTTP APi에서 주로 사용, JSON, XML, TEXT

    - 데이터 형식은 주로 JSON 사용

    - POST, PUT, PATCH

 

 

 

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

다음 데이터를 클라이언트에서 서버로 전송해보도록 하자.

 

전달 데이터

- username=hello

- age=20

 

메시지 바디 없이, URL의 쿼리 파라미터를 사용해서 데이터를 전달하자.

ex) 검색, 필터, 페이징등에서 많이 사용하는 방식

 

쿼리 파라미터는 URL에 다음과 같이 ?를 시작으로 보낼 수 있다. 추가 파라미터는 &로 구분하면 된다.

http://localhost:8080/request-header?username=hello&age=20

 

서버에서는 'HttpServletRequest'가 제공하는 다음 메서드를 통해 쿼리 파라미터를 편리하게 조회할 수 있다.

 

쿼리 파라미터 메서드 예제를 확인해보자.

/**
 * 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");


    }
}

 

파라미터를 받으면 파라미터를 조회하는 예제를 만들었다.

http://localhost:8080/request-param

그냥 빈 깡통일때는 

[��ü �Ķ���� ��ȸ] - start
[��ü �Ķ���� ��ȸ] - end

파라미터가 조회되지 않으나, 파라미터를 넣은 URL을 실행하면 

 

http://localhost:8080/request-param?username=hello&age=20

[��ü �Ķ���� ��ȸ] - start
username=hello
age=20
[��ü �Ķ���� ��ȸ] - end

 

username과 age 파라미터를 조회할 수 있게 된다.

 

** 참고 : 한글이 �로 깨지는 현상을 볼 수 있다. 해결방법은 나도 모르겠다...;;

이거저거 해봤는데 안되더라

++ 추가 

https://www.inflearn.com/community/questions/1099466/%EC%BD%98%EC%86%94-%ED%95%9C%EA%B8%80-%EA%B9%A8%EC%A7%90

 

콘솔 한글 깨짐 - 인프런 | 커뮤니티 질문&답변

누구나 함께하는 인프런 커뮤니티. 모르면 묻고, 해답을 찾아보세요.

www.inflearn.com

여기에 있는거 전부 적용시켜보자.

 

[전체 파라미터 조회] - start
username=hello
age=20
[전체 파라미터 조회] - end

 

드디어 한글이 안깨지고 조회된다.

 

본제로 돌아가서, 전체 파라미터 조회를 해봤으니 단일 파라미터도 조회해보자.

System.out.println("[단일 파라미터 조회]");
String username = request.getParameter("username");
String age = request.getParameter("age");

System.out.println("username = " + username);
System.out.println("age = " + age);

[단일 파라미터 조회]
username = hello
age = 20

 

단일 파라미터또한 정상적으로 나타나는 것을 볼 수 있다.

 

이름이 같은 파라미터가 여러개가 있을수도 있다.

http://localhost:8080/request-param?username=hello&age=20&username=hello2

이럴떄는

System.out.println("[이름이 같은 복수 파라미터 조회]");
String[] usernames = request.getParameterValues("username");
for (String name : usernames) {
    System.out.println("name = " + name);
}

[이름이 같은 복수 파라미터 조회]
name = hello
name = hello2

 

getParmeterValues를 사용하면 username을 배열로 나오게 만들 수 있다.

*참고 : getParameter는 단일값만 뽑는것이기 때문에 이름이 중복되는 여러 파라미터들에 대해서는 getParameterValues를 사용해야 한다. 하지만 이름이 중복되게 잘 사용하진 않는다.

 

 

 

HTTP 요청 데이터 - POST HTML Form

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

주로 회원가입, 상품 주문 등에서 사용하는 방식이다.

 

*특징*

- content-type : application/x-www-form-urlencoded

- 메시지 바디에 쿼리 파라미터 형식으로 데이터를 전달한다. username=hello&age=20

 

우선 html을 하나 생성해주자

src/main/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>

 

만약 username과 age를 hello와 20으로 주고 submit하면 어떻게 되는지 확인해보자.

 

[전체 파라미터 조회] - start
username=hello
age=20
[전체 파라미터 조회] - end

[단일 파라미터 조회]
username = hello
age = 20

[이름이 같은 복수 파라미터 조회]
name = hello

 

기존에 만들었던 코드의 print가 정확하게 출력되는 것을 볼 수 있다.

 

POST의 HTML Form을 전송하면 웹 브라우저는 다음 형식으로 HTTP 메시지를 만든다.

content-type: application/x-www-form-urlencoded

message body : username=hello&age=20

 

application/x-www-form-urlencoded의 형식은 GET에서 살펴본 쿼리 파라미터와 형식이 같다. 형식이 똑같기 때문에 request.getParameter의 쿼리 파라미터 조회 메서드를 그대로 사용해서 값을 다 꺼낸 것이다.

클라이언트 입장에서는 두 방식에 차이가 있지만, 서버 입장에서는 둘의 형식이 동일하므로, request.getParameter()로 편리하게 조회할 수 있다.

 

정리하면 request.getParameter()는 GET URL 쿼리 파라미터 형식도 지원하고, POST HTML Form 형식도 둘 다 지원한다.

 

*참고*

content-type은 HTTP 메시지 바디의 데이터 형식을 지정한다.

GET URL 쿼리 파라미터 형식으로 클라이언트에서 서버로 데이터를 전달할떄는 HTTP 메시지 바디를 사용하지 않기 때문에 content-type이 없다.

POST HTML Form 형식으로 데이터를 전달하면 HTTP 메시지 바디에 해당 데이터를 포함해서 보내기 때문에 바디에 포함된 데이터가 어떤 형식인지 content-type을 꼭 지정해야 한다. 이렇게 폼으로 데이터를 전송하는 형식을 application/x-www-form-urlencoded라고 한다.

 

 

하지만 이런 간단한 테스트에 HTML form을 일일히 만들어서 사용하기는 귀찮다. 이때는 Postman을 사용해서 편리하게 해결해보자.

POST 전송으로 하고, body -> x-www-form-urlencoded를 선택한다.

headers에서 content-type : application/x-www-form-urlencoded로 지정된 부분을 확인하자.

 

 

 

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

HTTP message body에 데이터를 직접 담아서 요청해보자.

    - HTTP API에서 주로 사용한다, JSON, XML, TEXT

    - 데이터 형식은 주로 JSON을 사용

    - POST, PUT, PATCH에서 주로 사용

 

 

- 먼저 가장 단순한 텍스트 메시지를 HTTP 메시지 바디에 담아서 전송하고, 읽어보자.

- HTTP 메시지 바디의 데이터를 InputStream을 사용해서 직접 읽을 수 있다.

@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");
    }
}

messageBody = hello!

 

메시지 바디의 내용이 그대로 콘솔에 출력되는 것을 확인할 수 있다.

 

*참고*

inputStream은 byte 코드를 반환한다. byte 코드를 우리가 읽을 수 있는 문자로 보려면 Charset을 지정해주어야 한다. 여기서는 UTF_8 Charset을 지정하였다.

 

POST http://localhost:8080/request-body-string

content-type: text/plain

message body: hello

결과 : messageBody = hello

 

--> 보통 요즘은 문자를 주고받지 않는다. JSON형식으로 된 값을 주고 받는다. JSON으로는 어떻게 주고 받는지 확인해보자.

 

 

 

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

이번에는 HTTP API에서 주로 사용하는 JSON 형식으로 데이터를 전달해보자.

 

*JSON 형식 전송*

POST http://localhost:8080/request-body-json

content-type : application/json

message body : {"username": "hello", "age": 20}

결과 : messageBody = {"username": "hello", "age": 20}

 

JSON 형식 파싱 추가

JSON형식으로 파싱할 수 있게 객체를 하나 생성해야 한다.

@Getter
@Setter
public class HelloData {

    private String username;
    private int age;

}

롬복의 Getter, Setter가 발린 HelloData클래스를 하나 생성하였다.

@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.getUsername() = " + helloData.getUsername());
        System.out.println("helloData.getAge() = " + helloData.getAge());

        response.getWriter().write("ok");

    }
}

getInputStream()을 통해 request를 바이트 코드로 변환하였고, 변환된 바이트 코드를 문자열로 변환하였다.

변환된 문자열은 ObjectMapper을 통해 JSON 문자열을 Java객체인 HelloData로 변환시켜주었다.

 

postman과 콘솔의 결과를 확인해보면 다음과 같다.

 

messageBody = {"username": "hello", "age": 20}
helloData.getUsername() = hello
helloData.getAge() = 20

 

*참고*
JSON 결과를 파싱해서 사용할 수 있는 자바 객체로 변환하려면 jackson, Gson같은 JSON 변환 라이브러리를 추가해서 사용해야 한다. 스프링 부트로 SpringMVC를 선택하면 기본으로 Jackson라이브러리('ObjectMapper')를 함께 제공한다.

 

*참고*

HTML form 데이터도 메시지 바디를 통해 전송되므로 직접 읽을 수 있다. 하지만 편리한 파라미터 조회 기능(request.getParameter())을 이미 제공하기 때문에 파라미터 조회 기능을 사용하면 된다.

 

 

 

HttpServletResponse - 기본 사용법

이전까지는 Request에 대해 알아보았다.

이번부터는 Response에 대해 알아보도록 하자.

 

HttpServletResponse는 다음과 같은 역할을 한다.

1. HTTP 응답 메시지 생성

    - HTTP 응답 코드 지정

    - 헤더 생성

    - 바디 생성

2. 편의 기능 제공

    - Content-Type, 쿠키, Redirect

 

HttpServletResponse - 기본 사용법

기본 사용법을 코드로 알아보자.

@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");
        response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
        response.setHeader("Pragma", "no-cache");
        response.setHeader("my-header", "hello");

        PrintWriter writer = response.getWriter();
        writer.println("ok");

    }
}

 

크롬의 개발자 모드를 오픈해서 header를 확인해보자.

 

setHeader를 통해 일일히 설정하는 것은 복잡하다. 편의 메서드를 사용하여 쉽게 해결해보자.

    @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(response);

        //[message body]
        PrintWriter writer = response.getWriter();
        writer.println("ok");

    }

    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); //(생략시 자동 생성)
    }

    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 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");}
}

 

content, cookie, redirect에 대한 편의 메서드를 추가하였다.

setHeader 대신에 다른 메서드를 통하여 간편하게 Header들을 설정하였다.

 

response는 Http 응답 스펙에 있는 헤더부분을 설정하고 bodyContent를 설정할 수 있는 것이다.

 

 

 

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

HTTP 응답 메시지는 주로 다음 내용을 담아서 전달한다.

1. 단순 텍스트 응답

    - 앞에서 확인해봄(writer.println("ok");)

2. HTML 응답

3. HTTP API - MessageBody JSON 응답

 

HttpServletResponse - HTML 응답

@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로 지정해야 한다.

 

 

 

HTTP 응답 데이터 - API 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.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 형식을 사용하도록 정의되어있다. 그래서 스펙에서 charset=utf-8과 같은 추가 파라미터를 지원하지 않는다. 따라서 application/json으로만 사용해야지

application/json;charset=utf-8이라고 전달하는 것은 의미 없는 파라미터를 추가한 것이 된다.

response.getWriter()를 사용하면 추가 파라미터를 자동으로 추가해버린다. 이때는 response.getOutputStream()으로 출력하면 그런 문제가 없다.

 

반응형

'공부 > Spring' 카테고리의 다른 글

MVC 프레임워크 만들기  (0) 2025.03.15
서블릿, JSP, MVC 패턴  (0) 2025.03.12
웹 애플리케이션의 이해  (0) 2025.03.10
빈 스코프  (0) 2024.11.08
bean 생명주기 콜백  (0) 2024.11.06