본문 바로가기
Category/Project

JAVA TCP 소켓을 사용하여 HTTP통신이 가능한 WAS 직접 구현하기 -1

by developer__Y 2024. 9. 12.

 

Spring 프레임워크를 사용하여 웹개발을 하면서 

요청을 꺼내쓸때는 request 객체를 사용해야한다는 '최종적인' 수단만 공부해오다보니,

정작 서블릿 컨테이너가 정확히 무얼하는지도 모를만큼 Web에 대한 기본기가 부족한것이었다.

그동안 당연하게 사용해왔던 WAS인 tomcat에 대해 알아보고

tomcat없이도 내가만든 Web Application이 클라이언트와 원활하게 HTTP 통신을 주고받을수있는 WAS를 직접 구현해보기로 하였다.

 

 

Web Service의 전반적인 처리과정

 

 

웹 서비스의 전반적인 처리과정은 다음의 아키텍처와 같다.

 

 

위의 처리과정에서 WAS가 담당하고있는 부분은 아래의 과정이다.

 

1. 브라우저를 통해 사용자가 HTTP 요청을 보낸다.

2. WAS(Tomcat)의 Connector에서 해당 요청을 수신하여 Engine으로 전달한다.

3. Engine에서 해당 요청을 처리할 Host와 Context를 찾아 전달한다.

 

Spring 프레임워크를 사용하면서 웹 개발을 한다는것은 

위의 Host에 설정된 webapp인 Context에 들어가는 Web Application을 Spring을 통해 구현한다는 것이다.

즉, WAS 클라이언트의 HTTP 요청을 받아서 설정된 Context에 전달해주면,

우리가 Spring 프레임워크의 Controller를 사용하여 구현한  URL 매핑과 동작들을 서블릿 객체가 실행하여 해당 Response를 반환하는것이다.

 

 

Tomcat의 동작구조

 

Tomcat의 아키텍처에 대해 알아보자.

Tomcat's Architecture

 

  • The Server : 서버는 우리가 실행하는 톰캣 그 자체로 JVM위에서 하나의 Process로 실행되는 하나의 인스턴스이다. 즉, 톰캣을 실행한다는것은 하나의 위 아키텍처 구조가 실행된다고 볼수있다.
  • Service : 최상위 구성요소인 Server안에서 여러개의 service를 가질수있다.
  • Connector : TCP Port에서 클라이언트가 보낸 요청을 listen하여 연결된 Engine으로 보내준다.
  • Engine : Service는 하나의 Engine을 가지며, Connector에서 보낸 요청을 해당 엔진 하위에 있는 Host에게 보내준다.
  • Host : 실행한 tomcat  서버에 대한 네트워크 이름을 의미하며, default로 localhost를 사용한다. 

 

즉, 톰캣을 실행하면 하나의 Server이 생성되고, 그 하위에 여러개의 Service가 있는데 각 Service들은 여러 Connector와 연결되어있으며 해당 요청을 처리하기위한 Engine이 존재한다.

 

추상적인 개념을 파악한뒤, 좀더 실제적인 구조를 살펴보자.

 

 

 

설치된 톰캣의 폴더에 들어가보면 다음과 같은 디렉토리 구조로 되어있는것을 볼수있다.

위 디렉토리중 눈여겨볼 것은 conf 와 webapps이다.

톰캣 서버에 관한 설정파일인 server.xml을 보자.

 

<?xml version='1.0' encoding='utf-8'?>
<Server port="8005" shutdown="SHUTDOWN">
   <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
   <Listener className="org.apache.catalina.core.JasperListener" />
   <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
   <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
   <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
   <GlobalNamingResources>
     <Resource name="UserDatabase" auth="Container"
               type="org.apache.catalina.UserDatabase"
               description="User database that can be updated and saved"
               factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
               pathname="conf/tomcat-users.xml" />
   </GlobalNamingResources>
   <Service name="Catalina">
     <Connector port="8080" protocol="HTTP/1.1"
                connectionTimeout="20000"
                redirectPort="8443" />
     <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
     <Engine name="Catalina" defaultHost="localhost">
       <Realm className="org.apache.catalina.realm.LockOutRealm">
         <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
                resourceName="UserDatabase"/>
       </Realm>
       <Host name="localhost"  appBase="webapps"
             unpackWARs="true" autoDeploy="true">
         <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
                prefix="localhost_access_log." suffix=".txt"
                pattern="%h %l %u %t &quot;%r&quot; %s %b" />
       </Host>
     </Engine>
   </Service>
</Server>

 

위 Server.xml은 톰캣의 아키텍처구조와 유사한것을 볼수있다.

하나의 <Server>안에  "Catalina"라는 name의 <Service>가 있다.

해당 <Service>는 8080 포트에서 HTTP/1.1요청을 받는 커넥터와 8009 포트에서 AJP/1.3 프로토콜을 수신하는 커텍터가 연결되어있는것을 볼수있다.

 

또한 <Engine>의 defaultHost가 "localhost"로 되어있어, 정의되어있지않은 Host의 요청들은 모두 localhost로 받는다.

 

위의 서버 설정내용을 토대로 전반적인 톰캣의 실행과정을 요약해보면 다음과 같다.

 

1. 브라우저에서 localhost:8080/ 으로 HTTP/1.1 요청을 보낸다.

2.  TCP PORT를 타고 넘어온 브라우저의 요청을 8080 포트에 HTTP/1.1 요청이 바인딩된 커넥터가 수신하여 name="Catalina" 인 Service에 넘겨준다.

3.  Service(=Engine)는 해당 요청을  localhost라는 Host에 전달하여 해당 Host의 Context를 찾는데, 위 설정상으로 해당 Context는 appBase="webapps"에 존재한다.

4. 톰캣의 Webapps에는 우리가 Spring 프레임워크를 통해 만든 Web Application를 패키징한 war이 있는데, 여기서 우리가 설정했던 web.xml을 읽어 서블릿을 통해 처리한다.

 

 

Java Dynamic Web Project를 통해 WAS와의 구조 확인하기

 

이제 Tomcat의 전반적인 과정에 대해 알아보았으니, Spring 프레임워크를 사용하지않고

Eclipse에서 제공하는 Java Dynamic Web Project를 통해 WAS와의 연동구조에 대해 파악해보자.

 

 

 

생성한 프로젝트에서 WebContent - WEB-INF - lib에 jsp 파일을 생성하면 javax.servlet.http.HttpServlet 이 클래스패스에 없다는 오류를 발생시킨다.

 

 

또한, src폴더에 Servlet 파일을 생성해주면, javax.servlet이 없다는 오류를 발생시킬것이다.

 

위의 두 파일을 통해 Web 프로젝트를 구동하기위해서는 Java의 Servlet 이라는 라이브러리가 필요하다는 사실을 알수있다.

당연한 사실이다. SpringBoot는 Tomcat이 내장되어있지만 Spring Framework나, 기본 Java Dynamic Project는 해당 Application을 구동시킬 WAS가 없기때문이다.

따라서 이클립스를 통해 설치한 Tomcat을 서버로 사용하도록 설정을 해준뒤,

해당 프로젝트의 Build Path에서 Add library를 통해 Server Runtime에서 Tomcat을 설정해준다.

 

 

Tomcat의 lib폴더에 WAS구동을 위한 다양한 jar 라이브러리가 있고 Servlet 라이브러리또한 포함되어있기때문에,

Tomcat의 런타임 라이브러리를 빌드패스에 추가해줌으로써 프로젝트에서 Servlet을 사용할수있게 되었다.

 

 

 

그리고, 프로젝트 익스플로러에서 좌측 Servers를 보면 WAS로 등록한 Tomcat이 보인다.

 

 

하단 콘솔창부분에서 Servers를 더블클릭하면 위처럼 전반적인 Server 설정을 할수있는데, Ports에서 HTTP/1.1 요청을 받는 포트를 8070으로 변경해주었다.

 

 

그에따라 Servers의 tomcat - server.xml에서 Connector이 다음과같이 8070 포트로 변경된것을 볼수있다.

 

이제, Tomcat WAS안에서 Servlet을 통해 클라이언트와 HTTP통신을 주고받는 예제를 만들어보자.

 

 

위의 @WebServlet("/TestServlet") 어노테이션을 통해  localhost:8070/WebProject 라는 Host의 Context중에서,

/TestServlet 에 매핑되는 서블릿을 생성하였다.

 

그리고 localhost:8070/WebProject/TestServlet?name=Tom 이라는 HTTP 요청을 보내면,

 

생성한 TestServlet 파일의 코드가 실행되어 응답을 보내주게된다.

 

 

이제, 위의 과정들을 WAS와 내가 만든 JAVA Project의 관점에서 살펴보자.

 

WAS안에서 Java Project

이클립스에 Tomcat 서버와 포트를 설정(localhost, 8070)하고, 생성한 WebProject를 tomcat에 올려 실행한다는것은

Tomcat의 server.xml에서 <Host>와 <Context>를 다음과같이 정의함으로써 설정되었다.

실제로, Tomcat의 conf/server.xml을 찾아보면 확인할수있다.

 

 

 

지금까지 만든 WebProject와 Servlet을 통해

localhost:8070/WebProject/TestServlet?name=Tom 요청을 보내고, 해당 서블릿이 동작하여

화면상에 Response한 과정들을

 

Tomcat의 아키텍처속에서 표현하면 다음과 같다.

 

 

 

1. localhost:8070/WebProject/TestServlet?name=Tom 이라는 HTTP 요청을 Connector가 수신하였다.

 

2. PORT : 8070에 매핑된 Connector가 name= localhost인 Service로 HTTP요청을 전달하였다 .

 

3. name = localhost Engine은 이를 처리할 Host를 찾아보고, name= localhost인 Host에게 전달하였다.

 

4. localhost Host는 WebProject 인 Context에게 해당 요청을 전달하였다.

 

5. WebProject는 비로소 해당 요청 처리하기위해 Servlet 객체를 통해 Request , Reponse 객체를 사용하여 처리하였다.

 

 

결론

 

즉, 지금까지 웹 개발을 하면서 Controller에서 Request, Reponse 객체를 통해 View로 반환하던 과정들속에서

WAS가 해주었던 HTTP요청을 수신하여 Request,Response 객체로 변환시켜 Connector에 던지고...등등등의

일련의 과정들을

우리가 Spring Framework, SpringBoot를 사용하면서 신경쓰지않고 Controller URL매핑 어노테이션 하나와 

DispatcherServlet과 같은 편리한 기능으로 처리하고있었던것이다.

 

이제 WAS가 해주던 역할들에 대해 배웠으니, 다음편부터 WAS를 직접 JAVA를 통해 구현해보도록 하자.

 

 

 

 

 

 

 

 

 

 

 

자료출처

https://kbss27.github.io/2017/11/16/tomcatarchitecture/?source=post_page-----91fbebf0eb67--------------------------------

 

tomcat architecture

tomcat architecture Nov 16, 2017 tomcat의 구조를 아는 것은 매우 중요하다. Spring Boot를 자주 사용하면서, Embedded tomcat을 사용하는 경우가 많아졌다. 이러한 경우에 어떠한 이슈가 발생했을때, tomcat의 구조

kbss27.github.io

https://velog.io/@hyunjae-lee/Tomcat-2-%EA%B5%AC%EC%A1%B0

 

Tomcat - (2) 구조

Tomcat의 구조에 대해 자세히 알아봅니다.

velog.io

 

https://carrotweb.tistory.com/17

 

이클립스 다이나믹 웹 프로젝트 만들기 3 - Eclipse Dynamic Web Project

1. 웹 실행시 내용이 없으면 구동되었는지 확인 할 수 없으니 "index.jsp"파일에 내용을 입력하여 저장합니다. 간단하게 "Hello world"를 입력하였습니다. 2. 하단에 "Servers"탭을 클릭합니다. "Tomcat8"를

carrotweb.tistory.com

https://medium.com/@potato013068/%ED%86%B0%EC%BA%A3%EC%9D%98-%EA%B5%AC%EC%A1%B0%EC%99%80-%EB%8F%99%EC%9E%91-%EB%A9%94%EC%BB%A4%EB%8B%88%EC%A6%98-91fbebf0eb67