스프링 웹 MVC 1편 - 서블릿

링딩·2022년 7월 22일
0

스프링 MVC

목록 보기
2/18

이 글은 김영한 강사님의 강의를 참고하여 작성하였습니다.

Chap 2. 서블릿

1. 서블릿 등록과 동작방식

First. 먼저 꼬옥 서블릿 환경을 구성 해주어야 한다..!

@SpringBootApplication 에서 @ServletComponentScan 서블릿 자동 등록 해주기..!



hello.servlet.basic.HelloServlet

@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 = "/경로")

  • 서블릿 등록
    -> urlPatterns: HTTP 요청에 매핑된 url임
    -> 해당 url이 호출되면 서블릿의 service() 실행됨

서블릿 컨테이너 동작방식

[처음]에는 스프링 부트에서 WAS가 서블릿을 서블릿 컨테이너에 미리 객체를 생성해 놓는다.

http://localhost:8080/hello?username=world 가 호출되면...

  1. HTTP 요청 메시지를 '기반'으로 request와 response 객체 를 생성
  2. service() 메소드를 호출하여 싱글톤 객체 'helloServlet'에서 값을 세팅
  3. 서블릿이 종료된 상태에서 우리가 값을 세팅한 response 객체를 불러와 'HTTP 응답'을 생성해서 보내줌.

HTTP 응답에 'Content-Length'는 WAS가 자동으로 생성해준다.




+) HTTP 요청 메시지 '로그'로 확인 법

application.properties에서 logging.level.org.apache.coyote.http11=debug 으로 세팅해주고 다시 서버 시작!




2. HttpServletRequest

역할

  • HTTP 요청 메시지를 편리하게 사용할 수 있도록 개발자 대신에 'HTTP 요청 메시지'를 파싱'결과'를 객체에 담아서 제공
  • 곧 HttpServletRequest 덕에 'HTTP 요청 메시지'를 편리하게 조회가 가능

🎉기능

  • HTTP 요청 메시지 제공 -> 조회가능
  • 임시 저장소
    ->언제? => HTTP 요청 시작 - 끝날 때까쥐
    - 저장: request.setAttribute(name, value)
    - 조회: request.getAttribute(name)
  • 세션 관리 기능
    - request.getSession(create: true)
  • 헤더,바디(form이나 message body 데이터 등) 조회

기본 사용법

[기본기능]

start-line 정보

//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.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 정보

[Host 조회]

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

[Accept-Language]

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

[Accept-Language]

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

[Accept-Language]

System.out.println("[cookie 편의 조회]");
 
if (request.getCookies() != null) {
 
for (Cookie cookie : request.getCookies()) {
 System.out.println(cookie.getName() + ": " + cookie.getValue());
 
}
 
}
 System.out.println();


[Content]

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




HTTP 요청 데이터

클라이언트에서 서버로 '데이터를 전달'하는 방법

1. GET 쿼리 파라미터

  • 메시지 바디 없이, 'UR'L의 쿼리 파라미터를 사용해서 데이터를 전달
    예) 검색, 필터, 페이징등에서 많이 사용하는 방식
  • '쿼리 파라미터'URL에 다음과 같이 ? 를 시작으로 보낼 수 있다. 추가 파라미터는 & 로 구분
  • 예시http://localhost:8080/request-param?username=hello&age=20
    => 전달할 데이터
    1) username=hello 2) age = 20

[쿼리 파라미터 '조회' 메서드 종류]

  • 단일 파라미터 조회
    String username = **request.getParameter("username")**;

  • 파라미터 이름들 모두 조회
    Enumeration<String> parameterNames = request.getParameterNames();

  • 파라미터를 Map으로 조회
    Map<String, String[]> parameterMap = request.getParameterMap();

  • 복수 파라미터 조회
    String[] usernames = request.getParameterValues("username");


그러면 파라미터가 같은 이름으로, 값이 '중복' 되면?? 🤷‍♂️

if) 중복이고 모든 값을 내보내고 싶다
request.getParameterValues("username")

if) 그냥 맨 앞에 값 하나만 내보내고 싶다.
request.getParameter()
=> 단일 조회 메소드 이기 때문



2. [POST] HTML Form

  • '메시지 바디'쿼리 파리미터 형식으로 데이터를 전달
    -> [메시지 바디] username=hello&age=20
    예) 주로 회원 가입, 상품 v주문
  • content-type : application/x-www-form-urlencoded

[HTTP 요청 메시지를 이렇게 만든다.]

  • 요청 URL: http://localhost:8080/request-param
  • content-type: application/x-www-form-urlencoded
  • message body: username=hello&age=20

아까 위에서 쓴 'GET 쿼리 파라미터'처럼 얘도 '쿼리 파라미터'형식이네 ?🤔

request.getParameter()2가지 모두(GET URL 쿼리 파라미터 형식,POST HTML Form
형식 )를 지원
한다.


+) content type이 생겼다? 🤔🤔

* content type은 HTTP 메시지 바디의 '데이터 형식'을 지정하는 곳

  • POST HTML FORM 형식은 '메시지 바디' 부분에 데이터를 포함해 보낸다.
    => GET url 쿼리 파라미터와 달리 얘는 반드시 '어떤 데이터 형식을 쓰는지' 지정이 필요


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

'HTTP message body'에 데이터를 직접 담아서 요청
HTTP API에서 주로 사용, JSON, XML, TEXT
데이터 형식은 주로 JSON 사용
* [POST, PUT, PATCH]

[요청 메시지]

문자 전송
• [POST] http://localhost:8080/request-body-string
content-type: text/plain
message body: hello
결과: messageBody = hello

inputStream 과 같은 코드의 뜻?

ServletInputStream inputStream = request.getInputStream();
String messageBody = StreamUtils.copyToString(inputStream,
StandardCharsets.UTF_8);
  • request.getInputStream() : request의 '메시지 바디'의 내용을 byte 타입으로 가져온다.
  • String messageBody = StreamUtils.copyToString(inputStream,StandardCharsets.UTF_8);
    은 앞의 byte타입으로 가져온 '메시지 바디'내용을 문자열 중 'UTF-8'로 가져오겠다는 뜻.

4. 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}

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

Postman에서 Json 쓰는법

  1. content-type: application/json (Body raw, 가장 오른쪽에서 JSON 선택
  2. message body: {"username": "hello", "age": 20} 입력

Json 결과를 어떻게 '자바 객체'로 변환한 것일까?

정답은 'Jackson'

  • 스프링 부트를 선택시 '기본'으로 'Jackson' 라이브러리를 함꼐 제공함

+) HTML form 데이터도 '메시지 바디'를 통해 데이터가 직접 전송 => 곧 데이터를 '직접' 읽을 수 있음

  • 그러나 편한 request.getParameter(..)을 두고 굳이 ? 싶으니 참고만 하자^^




HttpServletResponse

HttpServletResponse 역할

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

편의 기능

Content 편의 메서드

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

redirect 편의 메서드

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

redirect 경로를 제시해줌.



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

[전체적으로 HTTP 응답 데이터는 이렇게 있다.]

1) 단순 텍스트 응답
- 앞에서 살펴봄 ( writer.println("ok"); )
2) HTML 응답
3) HTTP API - MessageBody JSON 응답

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

일일히 써줘야 한다는 단점이 있다.

  • 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.setHeader("content-type", "application/json");
 response.setCharacterEncoding("utf-8");

HelloData data = new HelloData();
 data.setUsername("kim");
 data.setAge(20);

//{"username":"kim","age":20}
String result = objectMapper.writeValueAsString(data);

response.getWriter().write(result);
 
}
}
  • JSON을 반환할 때는 content-typeapplication/json 로 지정
  • objectMapper.writeValueAsString() : '객체'를 JSON 문자로 변경할 수 있다.

+) 참고

  • application/json기본 스펙상 utf-8 형식을 사용함
    -> 굳이 charset=utf-8 쓰는거 무의미
    -> application/json;charset=utf-8 (x)
  • response.getWriter()를 사용 하면 위와 같은 일이 생김
    => 그러니 response.getOutputStream() 를 쓰자

profile
초짜 백엔드 개린이

0개의 댓글