Perl script로 작성되었음(not java).
C, Python, PHP등등 많은 언어에서 사용할 수 있었음.
CGI를 사용하면 동적 웹페이지를 사용할 수 있었다.
하지만 문제는 CGI는 모든 request마다 프로세스를 만드는 문제가 존재.
너무 무거웠다.
그래서 서블릿/JSP 와 이를 관리해주는 서블릿 컨테이너가 등장.
Http Request가 client로부터 날아온다.
Container가 이를 받아 HttpServletRequest
와 HttpServletResponse
생성
URL에 맞는 Servlet을 고른 뒤에 쓰레드를 생성한다. 그리고 그 서블릿 쓰레드에 Request object와 Response Object를 전달한다.
여기서 servlet을 고르는 과정은 차후 설명하겠음.
개별 쓰레드에서 service()
method를 호출한다.
service method는 doGet()
, doPost()
와 같은 method를 다시 호출
doGet() method는 동적 페이지를 생성하고 이를 response 객체에 붙여준다. 여기서 기억할 것은 container는 여전히 response 객체를 참조하고 있다는 것!
thread가 수행을 완료하면 컨테이너는 response 객체를 HTTP response로 변경하고 이를 클라이언트에게 반환한다. 이후 request, response object를 제거한다.
코드로 동작방식을 대충 보면 다음과 같다.
public class MyServlet extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse res){
PrintWriter out = res.getWriter();
java.util.Date today = new java.util.Date();
out.println("<html> " +
"<body>" +
"<br>" + today +
"</body>" +
"</html>");
}
}
service()
method에 대해서는 차후 살펴보도록 하겠음.service()
method가 doGet()
을 호출한다는 것 정도만 알아두기.main/src/MyClass.java
근데 왜 3개 이름 다 가지고 있을까. 그냥 실제 경로 이름하나만 가져도 되지 않나?
이유 : flexibility와 security
이제 그럼 Deployment Descriptor로 url - servlet 매핑을 해보자.
<servlet>
<servlet-mapping>
<web-app ...>
<servlet>
<servlet-name>Internal name 1</servlet-name>
<servlet-class>foo.Servlet1</servlet-class>
</servlet>
<servlet>
<servlet-name>Internal name 2</servlet-name>
<servlet-class>foo.Servlet2</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Internal name 1</servlet-name>
<url-pattern>/public1</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Internal name 2</servlet-name>
<url-pattern>/public2</url-pattern>
</servlet-mapping>
시도 1.
처음부터 생각해보자. 우리는 single servlet을 통해 모든 기능을 실행하고 있다.
이때 servlet을 기능에 따라 구분하면 더 객체지향적인 디자인을 할 수 있지 않을까?
그래서 servlet을 각각 나눠보았다.
결과 1.
서블릿을 나누니 OO에 맞는 디자인을 할 수 있었다. 이렇게 서블릿을 나누고 나면 각 servlet에는 기능마다 필요한 데이터베이스 수정 및 읽기 기능, HTML을 반환하여 보여주는 stream등이 필요하게 될 것이다. 아래 코드를 보자.
public class MyAppServlet1 extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse res){
//이 서블릿에서 수행할 비즈니스 로직
//데이터베이스에 저장하고, 읽는 등의 코드가 있음.
//여기는 프레젠테이션
out.println("어쩌구 저쩌구...");
}
}
문제점이 보이는가? 위 코드는 서블릿 내부에 비즈니스 로직과 presentation이 짬뽕되어있다.
시도2.
그래서 이제 JSP를 사용하기로 했다.
JSP를 통해 비즈니스 로직과 presentation을 구분해서 SoC(Separation of Concerns)를 고려한 코드를 짜보았다.
결과2.
public class MyAppServlet1 extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse res){
PrintWriter out = res.getWriter();
java.util.Date today = new java.util.Date();
//이 서블릿에서 수행할 비즈니스 로직
//데이터베이스에 저장하고, 읽는 등의 코드가 있음.
//이젠 JSP page로 request를 forwarding 해주는 코드를 삽입
//output stream에 HTML을 더이상 프린트하지 않는다.
}
}
이제 코드가 상당히 나아졌다. 하지만 여전히 문제점이 존재한다.
서블릿에서 비즈니스 로직과 presentation을 분리했으나 여전히 서블릿에는 비즈니스 로직이 존재한다.
이렇게 되면 다른 서블릿에도 반복되는 비즈니스 로직을 적용시킬 수 없게 된다.
시도 3.
그래서 이젠 MVC 패턴을 사용하기로 했다.
MVC은 Model-Controller-View를 의미
Controller와 Model, View를 분리.
비즈니스 로직은 Model에 담겨있다.
결과3.
public class MyAppServlet1 extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse res){
//비즈니스 로직을 수행할 모델로 요청 전달.
//비즈니스 로직 수행한 것을 JSP 페이지로 포워딩할때 같이 전달.
}
}
이렇게 하게 되면 서블릿 내부에 비즈니스 로직이 담겨있지 않기 때문에 책임의 구분이 더 깔끔해진다.
여기까지 왔으면 다시 위에 있던 질문을 해보자.