동적/정적 페이지 그리고 아파치/톰캣

#JAVA_022
Jan 25, 2024
동적/정적 페이지 그리고 아파치/톰캣
 

🍒 동적 페이지 vs 정적 페이지

웹 페이지는 크게 두가지 정적 페이지(Static Page)와 동적 페이지(Dynamic Page)로 구분이 된다.
 
기본 구조 시각화
notion image
 
 

🥭 정적 페이지 (Static Pages)

정적 페이지는 서버에 미리 저장되어 있는 파일로, 서버에 요청이 들어올 때마다 파일 내용이 변경되지 않고 그대로 전송된다. 때문에 빠르고 효율적으로 제공될 수 있고, 간단한 웹 사이트에서 주로 사용된다. 하지만 사용자의 입력이나 데이터에 따라 내용이 바뀌지 않고, 모든 사용자에게 동일한 내용이 표시된다. 여기서는 이러한 웹 구조에서 널리 사용되는 Apache서버 소프트웨어로 설명하겠다.
 
정적 페이지 프로세스 구조
notion image
 
1️⃣ 클라이언트의 요청
  1. 웹 서버 구조에서 클라이언트의 요청 전에 서버 쪽에서는 클라이언트가 접속할 수 있는 리스너 포트를 열어둔다. 접속이 되면 서버는 각 클라이언트를 위한 소켓을 생성하고 통신을 하게 된다.
 
2️⃣ 필터링
  1. 소켓이 생성되고 통신이 시작되면 제일 먼저 필터링을 거치게 된다. 이 필터링 과정에서 문지기는 web.xml 에 명시된 index.html을 호출하며 맞이한다.
  1. 그리고 문지기는 조건문을 통해서 어떤 요청이 들어왔는 지 검사를 하게되고, 만약 html같은 파일에 대한 요청이 들어왔다면, 이 연결을 톰캣으로 보내지 않고 내부적으로 처리한다.
 
3️⃣ 응답
  1. 내부적으로 처리를 할 때, 문지기는 webapp 폴더 내부에 있는 요청 문서를 찾아서 응답을 해준다.
  1. 여기서 프로토콜에 따라 헤더에 동적페이지/ 정적페이지에 대한 정보를 넣고 브라우저가 캐싱해야될지 결정하게 해준다.
    1. 👉 Header에 포함되는 정보
      🟡 Cache-Control: public
      이 헤더 정보는 응답이 공개적으로 캐시가 될 수 있음을 나타낸다.
      🟡 Cache-Control: private
      이 헤더 정보는 응답이 개인적으로만 캐시가 될 수 있음을 나타낸다.
  1. 여기까지가 정적페이지 요청시에 Apache 서버가 처리하는 역할이다.
 
💪 정적페이지의 그리고 아파치의 장점
  • 정적 파일을 매우 효율적으로 처리하고 전달할 수있어서 빠르고 안전하다.
  • 아파치 덕분에 개발자가 직접 파싱하지 않아도 자신의 객체를 만들어 스트림을 통역한다.
 
 

🍋 동적 페이지 (Dynamic Pages)

아파치 소프트웨어서 조건문으로 어떤 요청인지 분석후 식별자 (URI : “/요청”)를 넘겨 받으면 톰캣으로 전달해서 동적페이지 요청을 관리한다.
 
동적 페이지는 서버에 요청이 들어올 때마다 그 내용이 바뀔 수 있는 페이지이다. 서버 사이드 스크립트 (Java Servlet, JSP, ASP.NET 등)를 통해 생성되며, 데이터 베이스 쿼리 결과나 사용자의 입력에 따라 내용이 달라질 수 있다.
 
동적페이지 프로세스 구조
notion image
 
1️⃣ 파싱
  1. 아파치로부터 넘어온 요청은 모두 동적인 컨텐츠를 처리하기 위함이다. 톰캣은 전달받은 요청을 파싱한다. 이 과정에서 톰캣은 URL, HTTP 메소드 (GET,POST등), 헤더, 쿠키, 바디 데이터등을 분석한다.
  1. 요청 정보를 바탕으로 톰캣은 해당 요청을 처리할 서블릿을 찾는다. 이는 web.xml 파일 또는 어노테이션을 통해 정의된 URL패턴과 서블릿 매핑을 기반으로 한다.
 
2️⃣ 서블릿 처리
매핑된 서블릿이 객체화 (인스턴스화) 되고, 톰캣은 요청에 따라 Get 또는 Post같은 메소드를 Dispatcher을 호출하여 초기 요청을 전달하는 책임을 진다. 톰캣은 여기까지 관리하며 MVC패턴이후의 반환된 자료를 다시 아파치에게 돌려준다.
 
3️⃣ Dispatcher
  • 톰캣으로 부터 받은 모든 요청을 라우팅하는 역할을 한다. 종종 FrontController라고 불리며, 이 한 곳에서 모든 진입점을 관리하는 방식을 Front Controller패턴이라고 한다.
  • Dispatcher에서 요청을 분석하여서 어떤 Controller가 해당 요청을 처리해야하는 지 결정한다. 라우팅한다하고 표현한다.
 
4️⃣ User Controller
사용자의 요청에 대한 구체적인 처리로직을 담당한다. 사용자의 입력을 처리하고 필요한 데이터를 DAO를 통해 비지니스 로직을 실행한다.
 
5️⃣ DAO (Data Access Object)
데이터 베이스와의 모든 상호작용을 처리한다. 데이터 베이스에서 데이터를 조회하거나 데이터를 삽입,수정,삭제하는 역할을 한다.
 
6️⃣ DB (DataBase)
데이터를 저장하고 관리하는 시스템이다. 구조화된 데이터를 보관하며 필요에 따라 이 데이터를 조회 수정 삭제 할 수 있다.
notion image
 
 

😋 Servlet Container (Front Controller) 코드 구조

지금부터는 실제 코드예시들을 살펴보자. 여기서는 JavaServlet을 사용한 간단한 프론트 컨트롤러 패턴의 예시이다. 프론트 컨트롤러는 모든 크라이언트 요청을 처음으로 받는 중앙 진입점으로, 이를 통해 요청을 컨트롤러로 라우팅하는 역할을 한다.
 
Front Controller 예시 코드
@WebServlet("/*") public class FrontController extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setHeader("Content-Type", "text/html; charset=utf-8"); String cmd = req.getParameter("cmd"); String action = req.getParameter("action"); Dispatcher.route(cmd, action, resp); } }
notion image
  • @WebServlet("/*") 어노테이션은 이 서블릿(FrontController)이 모든 URL 패턴(/*)에 대해 요청을 처리하도록 지정한다. 즉, 이 서블릿은 웹 애플리케이션의 모든 요청을 받는다.
  • HttpServlet 클래스를 확장(extends)하는 이유는 Java Servlet API를 사용하여 웹 서버에서 HTTP 요청을 처리하기 위한 기본적인 기능과 구조를 제공받기 위함이다.
 
 
notion image
  • Service메소드를 오버라이딩해서 HTTP 요청이 들어올 때마다 톰캣과 같은 서블릿 컨테이너에 의해 호출도니다.
  • HttpServletRequestHttpServletResponse 를 매개 변수를 받아서 요청을 받고 응답해준다.
 
 
notion image
  • resp.setHeader("Content-Type", "text/html; charset=utf-8"); 에서 응답을 반환할 때, 헤더에 텍스트타입이 지정해줘야 전달 하고싶은 텍스트가 깨지지 않는다.
  • cmdaction값을 Dispatcher에 전달한다.
 
 

🤗 Dispatcher 구조

이 구조는 FrontController에서 넘겨받은 변수에 따라 그와 연동된 작업을 호출하여 값을 받아오는 역할을 수행한다. 따라서 내부에 route 메소드를 통해 입력된 cmdaction 값에 따라 다른 처리를 호출하고, HTTP 응답을 생성한다.
 
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>"); } } }
notion image
  • Dispatcher.route 가 정적 메소드로 선언된 것은, 요청이 들어올 때마다 매번 객체를 생성하지 않고 해당 기능을 사용하기 위함이다.
  • 매개 변수로서 cmd값과 action값을 받아서 전달하며 HttpServletResponse에 정보를 담을 그릇을 준비한다.
 
 
notion image
  • 유효성 검사와 응답을 처리하는 로직이 구현되어 있다.
  • 1차적으로 cmd값이 user인지 검사한다.
  • 2차적으로 action값이 내부에 구현된 login join이 맞는 지 검사하고 해당 응답을 HttpServletResponse객체에 담는다.
 
 

😎 User Controller 구조

아래 코드의 구조는 간단한 웹 어플리케이션으로 Dispatcher에 의해서 호출된다. 예를 들어, 사용자가 로그인 페이지를 요청하면, Dispatcher.route메소드는 UserControllerlogin 메소드를 호출하고 반환된 HTML 문자열을 클라이언트에게 응답으로 전송한다.
 
User Controller 예시 코드
public class UserController { public String login() { return "<h1>login</h1>"; } public String join() { return "<h1>join</h1>"; } }
notion image
  • Dispatcher에서 해당 객체가 생성되고 메소드가 호출되면 return값을 반환한다.
 
 
 
 
 
Share article

AI_Nomads