Go, Vantage point
가까운 곳을 걷지 않고 서는 먼 곳을 갈 수 없다.
Github | https://github.com/overnew/
Blog | https://everenew.tistory.com/
티스토리 뷰
* 김영한님의 스프링 MVC 1편 강좌를 수강하며 정리한 글입니다. *
이전 글에서는 서버와 쓰레드 풀에 대해서 살펴보았다.
@WebServlet
스프링은 서블릿을 위해 @ServletComponentScan 어노테이션을 지원한다.
적용된 클래스의 하위 패키지를 모두 탐색해 서블릿을 자동 등록해준다
따라서 스프링 프로젝트의 루트 패키지의 애플리케이션 클래스에 적용하면 모든 서블릿이 자동 등록될 것이다.
서블릿 스캔의 대상이 되는 것은 @WebServlet이 적용된 클래스이다.
@WebServlet(name = "서블릿 이름", urlPatterns = "/URL")를 적용하면 해당 URL을 접속 시 서블릿 컨테이너가 해당 서블릿의 service() 함수를 실행해 준다.
이때 service(HttpServletRequest request, HttpServletResponse response)의 request와 response 객체는 WAS(스프링에서는 톰캣)이 자동으로 생성해 넘겨준다.
간단히 브라우저의 url 검색창에 [URL?파라미터 이름=내용]으로 접근하면 해당 파라미터가 HTTP 메시지로 전달된다.
WAS에서 생성한 객체인 request의 getParameter(파라미터 이름)을 호출하면, 굉장히 편리하게 파라미터 조회가 가능하다.
만약 서블릿의 지원이 없다면 직접 HTTP 메시지를 파싱해서 추출해야 할 것이다.
서버가 클라이언트에게 전달하는 response 객체는 헤더의 정보를 setting하고 response.getWriter().write() 본문을 작성하면 HTTP로 만들어 전달해준다.
실제로 브라우저가 응답받은 HTTP헤더를 살펴보면 세팅한 값이 저장되어 있다.
세팅하지 않은 이외의 값들은 WAS가 자동으로 세팅하여 보내준다.
스프링 부트에서 동작하는 순서를 정리해보자.
1. 스프링 부트가 실행됨.
2. WAS(내장 톰캣 서버)가 서블릿 컨테이너를 생성.
3. 서블릿 객체가 등록됨.
4. WAS가 웹 브라우저(클라이언트)로부터 HTTP 요청 메시지 수신.
5. 해당 메시지로 request와 response 객체를 만들어 서블릿 객체의 service메서드를 호출.
6. service 메서드가 작업을 하여 response 객체에 데이터를 주입하고 종료.
7. WAS가 response 객체의 정보로 HTTP 응답 메시지를 만들어 반환
8. 브라우저가 수신
HttpServletRequest
서블릿이 파싱한 HTTP 요청 메시지의 내용을 담는 객체이다.
서블릿 객체는 요청이 처리되고 응답이 완료되면 소멸하는 생존 주기를 가진다.
소멸 전까지 임시로 HttpServletRequest객체에 데이터를 저장 setAttribute(name, value), 조회 getAttribute(name)할 수 있다.
또한 getSession()으로 세션 관리 기능까지 제공한다.
getHeaderNames()를 사용하면 http의 모든 헤더 이름들이 출력되는데, 실제로 브라우저의 개발 도구를 들어가 확인해 보면 헤더가 굉장히 많다.
특정 헤더 값을 원한다면 getHeader("헤더 이름")으로 꺼내올 수 있다.
대응하는 헤더마다 값을 꺼낼 수 있는 메서드도 HttpServletRequest로부터 제공되므로 원하는 값은 꺼내서 확인할 수 있다.
PostMan이라는 프로그램을 사용하면 서버에 원하는 요청 데이터를 간단히 만들어서 보낼 수 있다.
HTTP 본문에 데이터를 담아 요청을 보내고 확인해보자.
HTTP의 3가지 요청 방식
1. GET Method의 쿼리 파라미터
url 뒤에 ?key1=value1&key2=value2 로 쿼리 파라미터를 붙여서 전달하는 방식
검색 사이트에서 많이 사용되는 방식이다.
url 끝에 ?로 시작하고 추가적인 파라미터는 &로 붙여 준다.
url?name=EVEerNew&age=100
위와 같이 요청한다면 request 객체에서는 getParameter(paramName)를 통해
name=EVEerNew, age=100를 꺼낼 수 있다.
이때 클라이언트가 어떤 파라미터 이름을 사용했는지 모르기 때문에 getParameterNames()로 접근할 수 있다.
request.getParameterNames().asIterator().forEachRemaining(
paramName -> System.out.println(paramName + " = " + request.getParameter(paramName))
);
만약 이름이 같은 파라미터 값을 여러개 보낸다면 어떻게 될까? (url?name=EVEerNew&name=100)
이때는 값이 덮어씌워 지는 것이 아니라 배열로 데이터가 저장이 된다.
getParameterValues("name") 로 배열 값을 받아올 수 있고 getParameter() 사용 시, 첫번째 값이 나온다.
2. HTML Form를 통한 POST Method
헤더에는 메시지 바디에 담은 타입(Content-Type)을 나타내고 메시지의 바디에 쿼리 파라미터를 담는다.
HTML의 form을 통해 전달하면 Content-Type은 application/x-www-form-urlencoded로 전달된다.(브라우저가 만들어 보내줌)
전달되는 형식은 GET의 쿼리 파라미터와 동일하기 때문에, 서버 입장에서는 같은 함수로 파라미터의 추출이 가능하다.
(GET Method의 경우 메시지의 바디가 없기 때문에 Content-Type도 없다.)
3. HTTP message body로 전달 (HTTP API)
메시지 바디에 원하는 데이터 정보를 그대로 담아 전송한다.
서버-서버, 클라이언트-서버가 통신할 때 html이 필요 없다면, 이 방식을 사용한다.
대표적으로 JSON 데이터가 이런 방식으로 전달되고 REST API가 사용하는 방식이다.
단순 텍스트라면 다음과 같이 제공되는 ServletInputStream으로 request의 본문 데이터를 받아올 수 있다.
@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("message 내용: " + messageBody);
}
Json 타입으로 보낸다면 Content-Type은 application/json 이 되고 본문에 {"key" : value...}를 담아 주면 된다.
사실 json도 그냥 text메시지를 보낸 것이기 때문에 사용하기 위해서는 파싱하고 객체로 변환할 필요가 있다.
스프링에서는 파싱과 변환을 자동으로 도와주는 Json 라이브러리(Jackson)가 포함되어 있다.
ServletInputStream inputStream = request.getInputStream();
String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.readValue(messageBody, 보낸.class);
//message의 데이터로 객체를 만듬, 이 객체는 setter가 필요
HttpServletResponse
HTTP 응답 메시지를 생성해주는 객체이다.
헤더와 바디 생성을 도와주고 응답 코드(404 Not Found, 300대 redirect)를 지정할 수 있다.
쿠키도 직접 넣어주려면 번거롭지만, 메서드로 손쉽게 넣어줄 수 있다.
헤더 세팅
response 객체에 헤더를 세팅하면 실제로 브라우저의 응답 헤더에 같은 값이넘어온다.
//각 헤더마다 setter도 존재하므로 호출해 사용할 수 있다.
response.setContentType("text/plain");
response.setCharacterEncoding("utf-8");
//쿠키도 서버에서 직접 만들어서 넣어줄 수 있다.
Cookie cookie = new Cookie("myCookie", "good");
cookie.setMaxAge(600);
response.addCookie(cookie);
redirect의 경우 상태 코드는 302로 만들고 이동할 위치를 Location 헤더에 설정해주면 된다.
웹 브라우저가 302 상태 코드인 http를 수신하면 Location위치로 자동으로 넘어가게 된다.
//상태코드와 헤더를 직접 설정
response.setStatus(HttpServletResponse.SC_FOUND); //302
response.setHeader("Location", "/redirect.html");
// 위의 두 작업을 한번에 처리하는 메서드
response.sendRedirect("/redirect.html");
응답 데이터 세팅
1. 단순 텍스트 응답
단순 텍스트 응답은 writer 객체를 가져와서 세팅해줄 수 있다.
PrintWriter writer = response.getWriter();
writer.println("원하는 텍스트 삽입");
2. HTML 응답
HTML응답의 경우 Content-Type을 text/html로 세팅을 하고 직접 html 태그로 이뤄진 text를 작성해주면 된다.
이러한 응답을 받은 웹 브라우저는 html로 인식하여 화면에 출력한다.
3. HTTP API 응답 (Json)
Content-Type을 application/json으로 세팅을 하고 json형식의 텍스트를 보내주면 된다.
이때 ObjectMapper를 사용하면, 텍스트를 객체로 바꾼 것과 반대로 객체를 json형식의 텍스트로 바꾸어 줄 수 있다.
'개발 > Spring' 카테고리의 다른 글
[Spring] 15. 프런트 컨트롤러 패턴, 어댑터 패턴 (0) | 2022.07.10 |
---|---|
[Spring] 14. 템플릿 엔진과 MVC 패턴 (0) | 2022.07.08 |
[Spring] 12. 웹 어플리케이션 서버와 쓰레드 풀 (0) | 2022.07.04 |
[Spring] 11. 스프링 빈의 스코프(Scope) (0) | 2022.07.02 |
[Spring] 10. 스프링 빈의 라이프 사이클 (0) | 2022.06.30 |