Programming/Spring

[Spring] 스프링 MVC 1편(서블릿)

kmindev 2024. 2. 2. 18:20

김영한님의 스프링 MVC 1편 강의를 듣고 정리하는 글입니다

 

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-1/dashboard

 

스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술 강의 - 인프런

웹 애플리케이션을 개발할 때 필요한 모든 웹 기술을 기초부터 이해하고, 완성할 수 있습니다. 스프링 MVC의 핵심 원리와 구조를 이해하고, 더 깊이있는 백엔드 개발자로 성장할 수 있습니다., 원

www.inflearn.com

 

1. 스프링부트 서블릿 환경 구성

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

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

}

스프링 부트에서 서블릿을 등록해서 사용할 수 있도록 @ServletComponentScan을 지원해준다. @CoponentScan과 마찬가지로 탐색 패키지를 지정해줄 수 있으며 해당되는 패키지 하위의 모든 컴포넌트로 등록된 Servlet을 스캔하는 역할을 한다.  

 

2. 서블릿 등록

@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://localhost:8080/hello" 를 입력하면 service 메서드가 호출된다.
  • HttpServletReponse는 HTTP 응답메시지를 만들 수 있도록 다양한 메서드를 제공한다.

 

3. HttpServletRequest 사용법

HttpServletRequest는 서블릿에서 HTTP 메시지를 파싱해서 객체로 제공하여 HTTP 메시지를 다룰 수 있는 다양한 메서드를 제공한다.

 

HTTP 에 요청할 때 주로 3가지 방법을 사용한다.

1. GET - 쿼리 파라미터

2.  POST - HTML  Form 사용

3. HTTP message body

 

3가지 request에 대해 처리하는 방법을 예제를 통해 정리하겠다.

a. 헤더 및 기타 정보 다루기

HTTP 메시지의 헤더 및 기타 정보를 다루기 위한 다양한 메서드를 제공한다.

메서드 설명
getMethod() HTTP 요청 메서드 반환(GET, POST ..)
getProtool() 요청 프로토콜 반환(HTTP/1.1)
getRequestURL() 요청 URL 반환(http://localhost:8080/request-header)
getRequestURI() 요청 URI 반환(/request-header)
getQueryString() 요청 쿼리파라미터 반환(username=강&age=10&username=김)
isSecure() https 적용 여부 반환(true / false)
getHeaderNames() 요청 메시지의 모든 헤더 반한(Enumeration 반환하므로 반복자로 출력)
getServerName() 서버 host 정보(localhost)
getServerPort() 서버 port 정보(8080)
getLocals() 모든 Accept-Language 반환(Enumeration 반환하므로 반복자로 출력)
getLocal() Accept-Language 반환(우선순위 높은 language 반환)
getCookies() 모든 쿠기 반환(배열 형태로 반환)
getContentType() Content-Type 반환( application/json)
getContentLength() Content-Length 반환 (5)
getCharacterEncoding() 인코딩 정보 반환 (UTF-8)
getRemoteHost() 요청자 host 반환(0:0:0:0:0:0:0:1)
getRemoteAddr() 요청자 주소 반환(0:0:0:0:0:0:0:1)
getRemotePort() 요청자 port 반환(60408)
getLocalName() 로컬 이름 반환(0:0:0:0:0:0:0:1)
getLocalAddr() 로컬 주소 반환(0:0:0:0:0:0:0:1)
getLocalPort() 로컬 port 반환(8080)

 

 

메서드 사용 예시

더보기
@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
    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
//        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 ---");

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

 

 

b. GET 쿼리파라미터 / POST Form 요청 다루기

GET 요청의 쿼리파라미터, HTML Form으로 POST 요청을 보낼 때 모두 getParameter()를 통해 값을 조회할 수 있다.

@WebServlet(name = "requestParamServlet", urlPatterns = "/request-param")
public class RequestParamServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // getParameter() 는 쿼리스트링(get), 메시지바디 내용을 꺼낼 때 모두 사용
        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");
    }
}

 

 

c. 단순 텍스트 요청 다루기

HttpServletRequest의 getInputStream()으로 HTTP 메시지 바디의 바이너리 데이터를 읽어와서 String으로 변환해서 문자열로 다룰 수 있다.

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

 

d .JSON 형식 요청 다루기

API 통신할 때 주로 JSON 형식으로 주고 받는다. JSON도 단순 바이너리 데이터이기 때문에 String으로 변환 후 JSON 변환 라이브러리인 Jackson에서 제공해주는 ObjectMapper를 사용하여 JSON형식의 데이터를 객체로 변환할 수 있다.

@Getter
@Setter
public class HelloData {
    private String username;
    private int age;
}

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

 

 

 

4. HttpServletResponse 사용

HttpServletResponse는 HTTP 응답 메시지를 생성에 필요한 다양한 메서드를 제공한다.

 

a. 응답코드 지정

 

setStatus로 응답코드를 지정할 수 있다. 

 @Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     // [status-line]
     response.setStatus(HttpServletResponse.SC_OK); // 200
     response.setStatus(HttpServletResponse.SC_BAD_REQUEST); // 400
     response.setStatus(HttpServletResponse.SC_FOUND); // 302
}

 

 

b. 헤더 생성

 @Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     // 헤더 설정 방법
     response.setHeader("Content-Type", "text/plain;charset=utf-8");
     response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
     response.setHeader("Pargma", "no-cache");
     response.setHeader("my-header", "hello");
     
     // content 정보, 인코딩 정보 - 헤더
     response.setContentType("text/plain");
     response.setCharacterEncoding("utf-8");
        
     // 쿠키 설정 - 헤더
     Cookie cookie = new Cookie("myCookie", "good");
     cookie.setMaxAge(600); //600초
     response.addCookie(cookie);
     
     // 리다이엑트 설정 - 헤더
     response.sendRedirect("/basic/hello-form.html");
}

 

 

 

c. 바디 생성

1. 단순 텍스트 응답 메시지

 @Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     // [message body]
     PrintWriter writer = response.getWriter();
     writer.println("ok");
}

 

 

2. HTML 응답 메시지

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

 

 

3. JSON 응답 메시지

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