스프링 MVC 1편 - 서블릿

서은경·2022년 12월 27일
0

Spring

목록 보기
34/43

프로젝트 생성

  • 환경
    • Gradle - Groovy
    • Java 11
    • spring web, lombok
    • Spring boot 3.0.1
    • War

🚫 war로 프로젝트 생성 시

No active profile set, falling back to 1 default profile: "default"

해당 에러를 만날 수 있는데 이 때 해결법은 preference > gradle 검색 > Build and run using 옵션을 Gradle로 바꾸면 된다.

Hello 서블릿

🙋‍♀️ 참고
서블릿은 톰캣 같은 웹 애플리케이션 서버를 직접 설치하고, 그 위에 서블릿 코드를 클래스 파일로 빌드해서 올린 다음 톰캣 서버를 실행하면 된다. 하지만 스프링 부트는 톰캣 서버를 내장하고 있으므로 톰캣 서버 설치 없이 편리하게 서블릿 코드를 실행할 수 있다!

@ServletComponentScan
현재 내 패키지 하위의 모든 패키지들을 내에 서블릿을 찾아 자동으로 등록하여 실행할 수 있도록 해줌

@WebServlet
name : 서블릿 이름
urlPatterns : URL 매핑
HTTP 요청을 통해 매핑된 URL이 호출되면 서블릿 컨테이너가 service 메서드를 자동 실행

서블릿 컨테이너 동작 방식

  1. 스프링 부트 실행
  2. 내장 톰캣 서버 띄움
  3. 서블릿 컨테이너 생성, helloServlet 생성
  4. HTTP 요청이 오면 request, response 객체를 만들어 helloServlet 호출
  5. response 객체 정보로 HTTP 응답 생성

HttpServletRequest 개요

HttpServletRequest 역할

HTTP 요청 메시지를 개발자가 편리하게 사용할 수 있도록 개발자 대신 HTTP 요청 메시지 파싱 후 객체에 담아 제공

HTTP 요청 메시지

  • START LINE
    • HTTP 메소드
    • URL
    • 쿼리 스트링
    • 스키마, 프로토콜
  • 헤더
    • 헤더 조회
  • 바디
    • form 파라미터 형식 조회
    • message body 데이터 직접 조회
  • 임시 저장소 기능
    • 해당 HTTP 요청이 시작부터 끝날 때까지 유지되는 임시 저장소 기능
      저장 : request.setAttribute(name, value)
      조회 : request.getAttribute(name)
  • 세션 관리 기능
    • request.getSEssion(create: true)

HttpServletRequest 기본 사용법

package hello.servlet.basic.request;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.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);
        printHeaders(request);
        printHeaderUtils(request);
        printEtc(request);
    }

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

    //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 요청 데이터 개요

  • GET 쿼리 파라미터
    • /url?username=hello&age=100
    • 메시지 바디 없이 URL의 쿼리 파라미터에 데이터를 포함해서 전달
    • 검색, 필터, 페이징 등
  • POST HTML form
    • content-type: application/x-www-form-urlencoded
    • 메시지 바디에 쿼리 파라미터 형식으로 전달 username=hello&age=100
      회원가입, 상품 주문 등 html form 사용
  • HTTP message body
    • HTTP API에서 주로 사용, JSON, XML, TEXT
    • 데이터 형식은 주로 JSON
    • POST, PUT PATCH

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

package hello.servlet.basic.request;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.util.Enumeration;

/*
* 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+" "+age);
        System.out.println();

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

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

HTTP 요청 데이터 - POST HTML Form

특징

  • content-type:application/x-www-form-urlencode
  • 메시디 바디에 쿼리 파라미터 형식으로 데이터를 전달한다
  • request.getParameter() 파라미터 조회 메서드 그대로 사용하면 됨

🙋‍♀️참고

  • content-type은 HTTP 메시지 바디의 데이터 형식을 지정한다
  • GET URL 쿼리 파라미터 형식은 null
  • POST HTML Form 형식은 메시지 바디에 데이터를 포함해서 보내기 때문에 content-type 꼭 지정

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

단순 텍스트

  • HTTP message body에 데이터를 직접 담아서 요청
  • 데이터 형식은 주로 JSON 사용
package hello.servlet.basic.request;

import jakarta.servlet.ServletException;
import jakarta.servlet.ServletInputStream;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.util.StreamUtils;

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

JSON

package hello.servlet.basic.request;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.json.JsonMapper;
import hello.servlet.basic.HelloData;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletInputStream;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.util.StreamUtils;

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);
        HelloData helloData = objectMapper.readValue(messageBody, HelloData.class);
        System.out.println(helloData.getUsername() + " " + helloData.getAge());

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

    }
}

HttpServletResponse 기본 사용법

역할

  • HTTP 응답 메시지 생성
    • HTTP 응답 코드 지정
    • 헤더 생성
    • 바디 생성
  • 편의 기능 제공
    • Content-Type, 쿠키, Redirect
package hello.servlet.basic.response;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.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-header
        // response.setHeader("Content-Type", "text/plain;charset=utf-8"); 적용안됨
        response.setContentType("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);
        redirect(response);

        response.getWriter().write("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");
    }
}

HTTP 응답 데이터

단순 텍스트, html

  • 단순 텍스트 : wirter.println("ok")

  • html

package hello.servlet.basic.response;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.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;charset=utf-8");
        PrintWriter writer = response.getWriter();
        writer.println("<html>");
        writer.println("<body>");
        writer.println("<div style='color:pink'>안녕!</div>");
        writer.println("</body>");
        writer.println("</html>");
    }
}

API

  • content-type을 application/json으로 지정
  • jackson 라이브러리가 제공하는 objectMapper를 사용하면 객체를 json 문자로 변경 가능
package hello.servlet.basic.response;

import com.fasterxml.jackson.databind.ObjectMapper;
import hello.servlet.basic.HelloData;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.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
        response.setContentType("text/plain;applicaiton/json;charset=utf-8");

        HelloData helloData = new HelloData();
        helloData.setUsername("sek");
        helloData.setAge(28);

        // {"username":"sek", "age":28}
        String result = objectMapper.writeValueAsString(helloData);
        response.getWriter().write(result);
    }
}

🙋‍♀️참고
application/json 은 스펙살 utf-8 형식을 사용하도록 정의되어 있기 때문에 charset=utf-8과 같은 추가 파라미터를 지원하지 않음
response.getWriter() 를 사용하면 추가 파라미터를 자동으로 추가해버리는데 이때 response.getOutputStream() 으로 출력하면 그런 문제가 없다

0개의 댓글