[스프링 MVC - 1편] 서블릿

69280 단어 스프링스프링

프로젝트 생성 할 때 Jar가 아닌 War로 선택 ! > JSP 사용 가능
Jar는 내장 톰캣을 사용
War도 내장 톰캣 사용 가능, 또한 톰캣 서버를 별도로 설치하고 War를 넣어 빌드할때도 사용
여기선 JSP를 공부해야하기 때문에 JSP를 사용하려면 War를 선택해야함

Hello 서블릿

@ServletComponentScan 애너테이션 추가

  • 자동으로 내 패키지를 포함해서 하위 패키지를 다 찾아 자동으로 서블릿을 등록
  • 서블릿을 직접 등록해서 사용
@WebServlet(name ="helloServlet",urlPatterns = "/hello")
public class HelloServlet extends HttpServlet {
    @Override
    //서블릿이 호출되면 서비스 메서드가 호출됨
    //url을 호출하면 웹 브라우저가 http 요청 메세지를 만들어서 서버에 던짐 > 서버는 request, response 객체를 만듦
    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"); //header 정보 content type에 들어가는 정보
        response.setCharacterEncoding("utf-8"); //header 정보 content type에 들어가는 정보
        response.getWriter().write("hello "+username);

    }
}
  • @WebServlet 서블릿 애노테이션

    • name: 서블릿 이름
    • urlPatterns: URL 매핑
    • name이나 urlPattern은 중복이 있으면 안됨
  • HTTP 요청을 통해 매핑된 URL이 호출되면 서블릿 컨테이너는 다음 메서드를 실행
    protected void service(HttpServletRequest request, HttpServletResponse response)


HttpServletRequest

개요

서블릿은 개발자가 HTTP 요청 메시지를 편리하게 사용할 수 있도록 개발자 대신 HTTP 요청 메시지를 파싱하고 결과를 HttpServletRequest 객체에 담아서 제공

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

기본 사용법

@WebServlet(name="requestHeaderService", 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

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

        //request.getHeader("host");
        // 헤더 하나만 조회하고 싶을 때 getHeader() 메서드 사용

        System.out.println("--- Headers - end ---");
        System.out.println();
    }

    //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());
        //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());
        //Http 메세지 Body에 무언가 담겨있어야 Content Type이 의미가 있음
        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 정보]");
        //요청이 온 것에 대한 정보
        // HTTP 메세지가 온 것은 아니고 내부에서 네트워크 커넥션이 맺어진 정보들 출력
        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 요청 데이터

개요

클라이언트에서 서버로 데이터를 전달하는 방법은 주로 3가지 방법을 사용

  • GET : 쿼리 파라미터
    • /url?key1=value1&key2=value2
    • 메시지 바디 없이 URL의 쿼리 파라미터에 데이터를 포함해서 전달
    • 검색, 필터, 페이징등에서 많이 사용하는 방식
  • POST : HTML Form
    • content-type: application/x-www-form-urlencoded > html 폼으로 데이터를 전송하는 형식이다!
      • content-type : body에 포함된 데이터가 어떤 형식인지 설명 ( HTTP 메시지 바디의 데이터 형식을 지정)
    • 메시지 바디에 쿼리 파리미터 형식으로 전달 key1=value1&key2=value2
    • 회원 가입, 상품 주문, HTML Form 사용
    • HTML Form 데이터를 보낼때는 무조건 POST만 사용 가능 !! > HTML 스펙 상 Form 데이터를 바디로 전송할 때는 POST 방식만 허용
  • HTTP message body에 데이터를 직접 담아서 요청
    • HTTP API에서 주로 사용
    • JSON, XML, TEXT정보를 그대로 담아서 전달
    • 데이터 형식은 주로 JSON 사용
    • POST, PUT, PATCH등 사용 가능

GET 쿼리 파라미터

  • 쿼리 파라미터는 URL에 다음과 같이 ? 를 시작으로 보내고 추가 파라미터는 & 로 구분
  • 서버에서는 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)));
        //  request.getParameterNames() > 모든 요청 파라미터를 다 꺼냄 (키(이름)를 꺼냄), 값을 꺼내려면 getParameter 메서드 사용
        System.out.println("[전체 파라미터 조회] - end");
        System.out.println();

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

        System.out.println("[이름이 같은 복수 파라미터 조회 파라미터 조회]");
        // 하나의 파라미터 이름에 여러 값을 넘길 수 있음(중복)
        // getParameter()를 사용하면 기본적으로 내부 우선순위에서 첫번째 값이 나옴
        String[] usernames = request.getParameterValues("username");
        //여러값이 있으면 getParameterValues()로 꺼내면 됨
        for(String name:usernames){
            System.out.println("username = " + name);
        }

    }
}

POST HTML Form

  • HTML의 Form을 사용해서 클라이언트에서 서버로 데이터를 전송
  • content-type: application/x-www-form-urlencoded
  • 메시지 바디에 쿼리 파리미터 형식으로 데이터를 전달
  • application/x-www-form-urlencoded 형식은 GET 쿼리 파라미터와 형식이 똑같기 때문에 request.getParameter()를 사용해서 다 꺼낼 수 있음 > 쿼리 파라미터 조회 메서드를 그대로 사용 가능
  • 클라이언트(웹 브라우저) 입장에서는 두 방식에 차이가 있지만, 서버 입장에서는 둘의 형식이 동일
  • request.getParameter()는 GET 쿼리 파라미터에 있는 데이터도 꺼낼 수 있고, HTML Form을 POST 방식으로 보낼때 내부에 있는 데이터도 꺼낼 수 있음
  • 쿼리 파라미터 형식으로 보내는 경우에는 다 동일하게 해결 가능

API 메시지 바디 - 단순 텍스트

  • HTTP message body에 데이터를 직접 담아서 요청
  • HTTP API에서 주로 사용, JSON, XML, TEXT
  • 데이터 형식은 주로 JSON 사용
  • POST, PUT, PATCH에서 주로 사용
  • 주로 앱 to 서버, 웹 클라이언트 to 서버, 서버 to 서버에서 사용
@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();
        // message body의 내용을 바이트 코드로 바로 얻을 수 있음

        String messageBody=StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
        // 인코딩 정보를 넘김
        System.out.println("messageBody = " + messageBody);

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


    }
}

API 메시지 바디 - JSON

@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);
        // JSON 결과를 파싱해서 사용할 수 있는 자바 객체로 변환

        System.out.println("helloData.getUsername = " + helloData.getUsername());
        System.out.println("helloData.getAge = " + helloData.getAge());

        response.getWriter().write("ok");
    }
}
  • JSON 결과를 파싱해서 사용할 수 있는 자바 객체로 변환하려면 Jackson, Gson 같은 JSON 변환 라이브러리를 추가해서 사용해야 함
  • 스프링 부트로 Spring MVC를 선택하면 기본으로 Jackson 라이브러리 ObjectMapper를 함께 제공

HttpServletResponse

기본 사용법

  • HTTP 응답 메시지 생성
    • start line 세팅(HTTP 응답코드 지정)
    • 헤더 생성
    • 바디 생성
  • 편의 기능 제공
    • Content-Type
    • 쿠키
    • Redirect
@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(response);
        redirect(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");
        //setHeader를 사용하는 방법 말고도 아래처럼 지정해 줄 수도 있음
        
        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 응답 데이터

HTTP 응답 메시지가 주로 전달 하는 것

  • 단순 텍스트 응답 writer.println("ok")
  • HTML 응답
  • HTTP API - MessageBody JSON 응답

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 로 지정
  • 서블릿으로 HTML을 렌더링 할 때는 직접 작성해야함
  • 동적으로도 HTML 생성 가능

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

        // 객체를 json 형식으로 변환 {"username":"kim", "age":20}
        String result=objectMapper.writeValueAsString(helloData);
        //객체의 값 문자로 바꿔라
        response.getWriter().write(result);

    }
}
  • HTTP 응답으로 JSON을 반환할 때는 content-type을 application/json 로 지정
  • Jackson 라이브러리가 제공하는 objectMapper.writeValueAsString()를 사용해 객체를 JSON 문자로 변경


출처
[인프런] 스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술

좋은 웹페이지 즐겨찾기