아파치 톰캣(Apache Tomcat)은 Apache Software Foundation에서 개발한 오픈 소스 웹 애플리케이션 서버이다.
아파치는 클라이언트에서 요청하는 HTTP요청을 처리하는 웹서버(Webserver) 이고, 톰캣은 자바 서블릿(Servlet)과 JavaServer Pages(JSP)를 실행하기 위한 컨테이너 역할을 하며, 자바 웹 어플리케이션을 실행하는 데 사용된다.
1. 아파치 톰캣 구조
우선 그림을 보자. 아파치 톰캣의 대략적인 구조를 그려보았다.
① 클라이언트가 URL 을 통해 데이터를 요청한다. 아파치 톰캣은 포트:8080 으로 연결되며, 연결 후 새로운 소켓과 연결 후 8080 포트는 연결 종료된다. 연결 후 아파치는 클라이언트에게 받은 URL 을 파싱 후 오브젝트에 저장한다.
② 데이터를 아파치가 데이터를 받게 되면 우선 필터(리스너) 를 거치게 된다. 리스너는 아파치 톰캣 내의 web.xml 파일을 실행 시키는데, 이 파일에 도메인/ 뒤의 값을 입력하지 않으면 index.html 로 연결되는 내용이 포함되어 있다.
또한 필터의 기능이 있어, 받은 데이터를 돌려보낼지 지속할지를 결정한다.
③ 그 다음은 조건문이 있다. 조건문은 입력된 URL 이 파일명인지, 식별자인지를 구분한다.
만약 파일명이라면 아파치는 아파치가 소유하고 있는 webapp 으로 가서 데이터 스캔을, 식별자라면 톰캣에게 위임한다.
④ 데이터가 hello.html 같은 파일명이라면 아파치가 소유하고 있는 웹서버의 webapp 폴더에서 데이터를 스캔한다. 데이터가 있다면 클라이언트에게 파일을 응답한다.
아파치는 파일 형태를 응답하기 때문에 정적 페이지를 응답한다.
⑤ 요청받은 URL 이 식별자라면 아파치는 톰캣에게 데이터를 위임한다. 톰캣은 전달 받은 데이터를 다시 파싱, 오브젝트에 담는다.
⑥ 오브젝트에는 두 가지로 나뉘는데, HttpServletRequest 은 요청받은 데이터를 담을 객체를 생성하고, HttpServletResponse 는 클라이언트에게 응답할 데이터를 담을 객체를 생성한다.
동적인 웹 애플리케이션을 작성하는 데 필수적인 요소이다. 웹 애플리케이션은 클라이언트의 요청을 받아 서버에서 데이터를 처리하고 그 결과를 클라이언트에게 응답하는 과정에서 이 인터페이스들이 사용된다.
2. 톰캣과 데이터베이스 정보 요청 과정
① 톰캣의 오브젝트는 FrontController 로 전달된다. FrontController 는 Servlet 을 기반으로 만들어진다. Servlet 이란 웹 애플리케이션에서 동적인 콘텐츠를 생성하고 HTTP 요청 및 응답을 처리하는 데 사용된다. FrontController 의 service() 메서드에 HTTPServletRequest 와 HTTPServletResponse 를 담고 디스패쳐를 호출한다.
클라이언트가 요청 시마다 Servlet이 생성된다. 요청이 많아지면 메모리 부하가 커지게 된다. 그래서 하나의 Servlet을 사용하는데 이를 FrontController 라고 한다.
FrontController 장단점
장점 : 공통 로직을 처리할 수 있음
단점 : 라우터를 설계해야 됨
② FrontController 의 service() 메서드를 실행하면 디스패쳐가 호출된다. 디스패쳐는 적절한 컨트롤러로 연결하는 라우팅의 역할을 수행한다.
FrontController 를 사용하게 되면, 수많은 클라이언트의 데이터를 FrontController 하나로 처리하기 때문에 디스패쳐가 라우터를 하기 위해선 리플렉션을 사용해야 한다. 그래서 디스패쳐는 리플렉션의 영역이기 때문에 개발자가 따로 구현할 필요는 없다.
③ 이후의 과정은 이전 블로그를 참고하면 된다.
3. 실행해보기
FrontController
@WebServlet("/*") // public class FrontController extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException { resp.setHeader("Content-Type", "text/html; charset=utf-8"); // Contant-Type 알려주기 String cmd = req.getParameter("cmd"); // HTTP 요청에서 "cmd"라는 이름의 파라미터 값을 가져오는 부분 String action = req.getParameter("action"); // action 을 파라미터 값으로 가져옴 Dispatcher.route(cmd,action,resp); } }
FrontController 에는 service() 메서드를 가지고 있다. 이 메서드는 service( HttpServletRequest , HttpServletResponse ) 를 매개변수로 받는다.
@WebServlet("/*") 어노테이션은 해당 서블릿이 어떤 URL 패턴에 매핑되는지를 지정하는 역할을 한다
Dispatcher
public class Dispatcher { public static void route(String cmd, String action, HttpServletResponse resp) throws IOException { if(cmd.equals("user")){ UserController con = new UserController(); if(action.equals("login")){ String html = con.login(); resp.getWriter().println(html); }else if(action.equals("join")){ String html = con.join(); resp.getWriter().println(html); } }else if(cmd.equals("board")){ resp.getWriter().println("<h1>board</h1>"); } } }
디스패쳐 클래스에에는 route 메서드가 있다. 라우터는 반복문을 통해 맞는 컨트롤러에 전달하는 역할을 한다.
URL에 cmd 파라미터의 값이 user 라면 UserController 객체 con이 생성된다. 그리고 action 파라미터의 login 과 join 중 맞는 컨트롤러의 메서드를 호출한다. 호출된 데이터는 html 변수에 저장되고, HttpServletResponse 의 버퍼에 담겨 전송된다.
UserController
public class UserController { public String login(){ return "<h1>login</h1>"; } public String join(){ return "<h1>join</h1>"; } }
4. 실행 결과
URL
http://localhost:8080/?cmd=user&action=login
URL 을 알아서 파싱 후 데이터를 응답해준다.
Share article