스프링 MVC-서블렛과 jsp
서블릿
오래 전 사람들은 서블릿을 가지고 HTTP 환경에서 요청과 응답을 처리하는 코드를 만들었다. 서블릿과 html을 사용하여 웹을 구축했는데 이후 html과 함께 자바 코드를 넣어 동적인 웹페이지를 쉽게 만들 수 있는 jsp 형식이 많이 사용되었다.
코드 간의 반복과 역할의 분리를 위해 MVC 패턴이 고안되고 ‘패턴’이라는 것은 곧 반복이란 뜻이므로 MVC 패턴을 쉽게 구축할 수 있게 해주는 여러 MVC 프레임워크들이 제작되었다.
후에 스프링 MVC로 통일되지만 미래의 이야기이고, 일단 서블릿으로 옛날 방식대로 코드를 짜보며 어떤 문제가 있는지, 그러한 문제를 해결하기 위해 어떤 방식이 채택됬는지를 알아보는 강의의 커리큘럼에 따라 서블릿부터 먼저 알아보도록 하겠다.
서블릿의 사용
서블릿은 위 그림과 같이 작동한다. WAS 서버에서 HTTP 요청을 기반으로 request, response를 만들면 서블릿 컨테이너에서 서블릿들이 request의 정보를 받아 여러 비즈니스 로직을 처리하고, response에 정보를 담아 반환하면 WAS에서 response를 바탕으로 HTTP 응답을 만들어 클라이언트에게 응답한다.
따라서 서블릿은 HTTP 요청 메세지를 파싱하고, response에 여러 정보를 담는 기능들을 제공한다.
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;
@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("[단일 파라미터 조회]");
String username = request.getParameter("username");
System.out.println("username = " + username);
String age = request.getParameter("age");
System.out.println("age = " + age);
System.out.println("[이름이 같은 복수 파라미터 조회]");
String[] usernames = request.getParameterValues("username");
for (String name : usernames) {
System.out.println("username = " + name);
}
response.getWriter().write("ok");
}
}
위 코드는 서블릿을 통해 request의 여러 파라미터들을 파싱하는 코드이다. request의 getParameter()과 같은 메서드를 통해 HTTP 요청으로 들어온 파라미터들을 손쉽게 파싱하는 것이 가능하다. 이 외에도 getHeader(), getCookies()와 같이 헤더와 쿠키들도 파싱할 수 있는 메서드들을 제공한다.
HTTP의 요청과 응답
HTTP의 요청은 주로 다음과 같은 방식으로 전달된다.
- GET-쿼리 파라미터
- POST-HTML Form
- HTTP 바디에 직접 메세지를 담아 전달
이 중 GET은 바디 없이 쿼리 파라미터만을 전달하게 되는데, 주로 검색, 필터 등 원하는 데이터를 간단하게 가져오기 위한 방법으로 사용된다.
POST는 메세지 바디에 쿼리 파라미터를 담아 전달하는데, 주로 회원 가입, 상품 주문 등 서버에 데이터를 전달하기 위한 방법으로 사용된다. 위의 GET과 같이 둘 다 쿼리 파라미터를 사용하기 때문에 request의 getParameter()로 쉽게 파싱해올 수 있다.
HTTP 바디에 메세지를 담는 방식은 주로 HTTP API에서 사용되는데, 각 서비스 사이에서 데이터만을 전달하기 위한 방법으로 사용된다. 데이터 형식은 JSON, XML 등 여러가지가 있지만 주로 JSON이 사용된다. 바디의 데이터는 request의 getInputStream() 메서드를 사용하여 읽어올 수 있다.
실 사용 코드는 위의 파라미터 조회 코드와 매우 유사하므로 따로 기재하지 않았다.
HTTP의 응답은 주로 다음과 같은 방식으로 전달된다.
- 단순 텍스트 응답
- HTML 응답
- HTTP API-JSON 응답
기본적으로 서블릿에서는 response에 HTTP 응답코드, 헤더, 바디를 지정하여 데이터를 전달할 수 있다. 이 외에도 컨텐츠 타입, 쿠키, 리다이렉트 등의 편의기능도 제공한다.
바디에 무엇을 담을 것인지는 response에서 setContentType()으로 컨텐츠 타입을 html, json 등으로 지정하여 결정할 수 있고, getWriter로 PrintWriter 객체를 받아 해당 타입의 데이터를 write() 메서드로 전달함으로써 HTTP 응답에 넣을 수 있다.
package hello.servlet.basic.response;
import com.fasterxml.jackson.databind.ObjectMapper;
import hello.servlet.basic.HelloData;
import hello.servlet.basic.HelloServlet;
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 {
ObjectMapper objectMapper = new ObjectMapper();
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("application/json");
response.setCharacterEncoding("utf-8");
HelloData helloData=new HelloData();
helloData.setAge(20);
helloData.setUsername("kim");
String result = objectMapper.writeValueAsString(helloData);
response.getWriter().write(result);
}
}
위 코드는 HTTP 응답 메세지의 바디에 JSON 데이터를 담아 전달하는 코드이다. ObjectMapper 클래스를 사용하면 다른 클래스의 파라미터와 밸류를 String 값으로 변환하여 json 형식으로 전달할 수 있다.
이후 서블릿을 통해 간단한 회원 관리 도메인을 만들고, html만을 사용하면 코드에서 html form을 제작하는 것이 너무 번거로웠기 때문에 jsp를 사용하여 html 사이에 자바 코드를 입력해 동적인 웹페이지를 만들었다.
jsp를 통해 form을 만드는 것은 더 간단해졌지만 아직 문제가 남았다. 서블릿을 사용할 때에는 자바 코드에 html이 섞여 지저분했었고, jsp를 사용할 때엔 html 사이에 자바 코드들이 섞여 지저분해졌다. 또한 뷰와 비즈니스 로직이 분리되어 있지 않아 뷰를 렌더링하는 jsp에서 리포지토리 등을 참조하여 데이터를 가져오는 역할까지 해야 했기에 코드가 무거워졌다.
그로 인해 mvc 패턴이 등장한다. 다음 포스팅에서는 mvc 패턴을 사용하여 코드를 리팩토링 해보자.