웹 개발자라면 톰캣을 한 번쯤은 들어봤거나 공부해 봤을 거라 생각합니다.
하지만 만약 누군가가 톰캣이 정확히 무엇이며, 어떤 매커니즘으로 동작하는지 물어본다면 대답하실 수 있나요? 저는 그러지 못했습니다. 돌이켜보면, 톰캣의 내부 구조나 동작 방식에 대해 크게 관심을 두지 않았던 것 같습니다. 특히 스프링 부트를 사용해 개발을 하다 보면 톰캣이 내장되어 있어 굳이 신경 쓸 필요가 없다고 느꼈기 때문인 것 같은데요.
그래서 이번 기회에 톰캣의 뭔지부터 시작해 구조 및 매커니즘까지 차근차근 정리해 보려 합니다.
Apache Tomcat은 Java Servlet, JavaServer Pages, Java Expression Language 및 WebSocket 기술의 오픈 소스 구현으로 Java 코드가 실행될 수 있는 "Pure Java" HTTP 웹 서버 환경을 제공합니다
위에 문구는 Apache 공식 문서에 적혀있는 Apache Tomcat에 대한 설명입니다. 말이 어렵게 되어있지만, 간단히 정리하면 톰캣은 Java로 만든 동적 웹사이트나 웹 애플리케이션을 쉽게 실행하고 관리할 수 있게 해주는 도구라고 볼 수 있습니다.
아파치 톰캣을 Apache(Apache HTTP) + Tomcat으로 생각하는 경우가 있는데요. 이는 엄연히 틀린 표현입니다.
결론부터 말하자면 톰캣과 아파치 톰캣은 같은 말입니다.
"아파치 톰캣(Apache Tomcat)" 이라는 이름은 아파치 소프트웨어 재단(Apache Software Foundation)과 톰캣 프로젝트의 결합일 뿐 우리가 보통 아파치라고 부르는 아파치 HTTP 서버와는 관련이 없습니다.
그렇기에 "톰캣 자체에 아파치 역할을 하는 웹서버가 존재한다", 또는 "톰캣이 아파치의 기능 일부를 가져와서 제공한다"와 같은 표현들은 다소 잘못된 표현인 것이죠.
또한 톰캣을 서블릿 컨테이너라고 부르는 경우도 있고, 웹 애플리케이션 서버라고 부르는 경우도 있습니다. 둘 중 어느 표현이 올바른 표현일까요? 이를 알아보기 위해 관련된 용어들을 먼저 이해해 봅시다.
웹 서버란 웹 브라우저 클라이언트로부터 HTTP 요청을 받아서 html, css, 이미지 등의 정적인 컨텐츠를 제공해 주는 서버를 칭합니다.
정적인 컨텐츠만을 제공하기 때문에 요청에 따른 비즈니스 로직 처리나 DB 연동 문제를 해결하기 위해 WAS가 등장하게 되었습니다.
WAS는 웹 애플리케이션을 실행하고 제공하는 서버 소프트웨어로 분산 트랜잭션, 보안, 스레드 처리 등의 기능을 처리하는 분산 환경에서 사용되는 미들웨어입니다.
또한 웹 서버와 웹 컨테이너의 기능을 결합하여, 웹 기반 애플리케이션이 동작할 수 있는 환경을 제공합니다.
WAS는 웹 서버와 웹 컨테이너가 합쳐진 형태지만, 부하를 줄이기 위해 웹 서버와 WAS를 분리해 각각 정적, 동적 컨텐츠를 다루게끔 하는 방식이 좋다고 합니다.
먼저 Servlet에 대해 알아봅시다. Servlet이 뭘까요? Servlet은 사용자의 요청을 처리해서 동적인 컨텐츠를 응답으로 주기 위한 프로그램이며 Java EE 의 표준 API 중 하나로, 웹 요청과 응답의 흐름을 간단한 메서드 호출만으로 체계적으로 다룰 수 있게 해줍니다.
서블릿 컨테이너는 이 서블릿들을 관리하고, 클라이언트로부터의 요청을 적절한 서블릿으로 라우팅하며, 서블릿의 생명 주기(lifecycle)를 관리합니다.
웹 서버는 정적인 컨텐츠를 다루고, WAS와 Servlet Container는 동적인 컨텐츠를 다룹니다.
그 중 Servlet Container는 Servlet과 JSP 실행에 필요한 최소한의 기능만 제공하는 것이고, WAS는 서블릿 컨테이너 외에도 트랜잭션 관리자, 메시징 큐, 보안 관리자 등의 역할까지 수행을 합니다.
그럼 톰캣은 WAS일까요, Servlet Container일까요? 결론은 Servlet Container입니다. 톰캣은 자바 Servlet과 JSP를 실행하는 데 최적화되어 있으며, 웹 서버로도 사용할 수 있습니다. 하지만, 톰캣 자체는 완전한 WAS가 아닙니다. 일부 WAS 기능을 제공하기는 하지만, 기본적으로 서블릿과 JSP 실행에 초점을 맞춘 경량 컨테이너입니다. (실제로 톰캣은 J2EE 스펙 중 EJB 기술이 적용되어 있지 않습니다)
<?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 "%r" %s %b" />
</Host>
</Engine>
</Service>
</Server>
위에 파일은 서버에 관한 설정 파일인 server.xml입니다. 관련 용어들을 하나씩 이해해 보겠습니다.
커넥터는 클라이언트와 통신을 담당하여 HTTP 요청을 받아들이고 처리하는 역할을 합니다. 톰캣의 기본 커넥터는 HTTP/1.1 표준에 따라 클라이언트로부터의 요청을 처리하며, 특정 포트(Default는 8080)에서 요청을 기다립니다. redirectPort 속성은 SSL(HTTPS) 연결이 필요한 경우, 요청을 안전한 포트(Default는 8443)로 리디렉션을 하기 위한 속성입니다.
호스트(Host)는 Tomcat 서버에서 특정 도메인 이름(예: www.velog.com)에 대한 설정을 의미하며, Tomcat 서버가 해당 도메인 이름에 대해 요청을 처리할 수 있도록 연결하는 역할을 합니다.
또한 호스트는 여러 개의 컨텍스트(Context, 즉 개별 웹 애플리케이션)를 포함할 수 있습니다. 예를 들어, www.velog.com이라는 호스트 아래 www.velog.com/save와 www.velog.com/write라는 두 개의 컨텍스트를 설정할 수 있습니다. 각 컨텍스트는 호스트 내에서 개별적인 웹 애플리케이션이나 모듈을 나타냅니다.
기본적으로 Tomcat에는 localhost라는 이름의 호스트가 포함되며, 이 호스트는 일반적으로 개발 환경에서 많이 사용됩니다.
엔진은 Tomcat의 핵심 역할을 수행합니다. 서비스에는 여러 개의 커넥터가 있을 수 있으며, 엔진은 이들 커넥터로부터 모든 요청을 받아 처리한 후, 클라이언트에게 응답을 전달할 적절한 커넥터로 다시 전달합니다. 엔진은 하나 이상의 호스트를 포함해야 하며, 그중 하나는 기본 호스트로 지정됩니다. 기본 Tomcat 구성에는 localhost 호스트를 포함하는 Catalina 엔진이 포함되어 있습니다.
리스너는 org.apache.catalina.LifecycleListener 인터페이스를 구현하여 특정 이벤트에 응답할 수 있는 Java 객체입니다.
렐름(Realm) 인증과 관련된 역할로 컨테이너 기반 인증을 지원합니다. 그렇기에 모든 컨테이너 구성 요소(엔진, 호스트, 컨텍스트)에 설정될 수 있으며, 사용자, 비밀번호, 사용자 역할에 대한 데이터베이스를 관리합니다.
밸브(Valve)는 컨테이너(컨텍스트, 호스트, 또는 엔진)에 추가되어 애플리케이션에 도달하기 전에 모든 들어오는 HTTP 요청을 가로채는 역할을 합니다. 마치 인터셉터처럼 작동하며, 이를 통해 특정 애플리케이션, 가상 호스트, 또는 엔진 내에서 실행되는 모든 애플리케이션으로 향하는 요청을 사전에 처리할 수 있습니다.

톰캣 아키텍처를 보며 마지막으로 전체적인 흐름을 이해해 보도록하겠습니다. 서버는 최상위 구성요소이며 톰캣의 인스턴스를 나타내는 것을 볼 수 있습니다. 즉, 서버는 톰캣 그 자체라고 볼 수 있겠습니다.
Server 안에는 Connector 가 있는 하나 이상의 Service가 포함됩니다. Service는 클라이언트의 요청을 처리하는 데 필요한 Connector와 Engine 간의 연결을 관리하는 역할을 합니다.
Service가 요청을 처리하는데 필요한 Connector와 Engine를 찾아 전달해주면, Connector는 특정 포트(예: 8080, 443, 80)에서 클라이언트 요청을 받아들이고, 이 요청을 Engine에 전달합니다. Engine은 톰캣 내에서 주요 요청 처리 컴포넌트로, 여러 Host와 Context를 관리합니다. Host는 가상 호스트를 의미하며, Context는 특정 웹 애플리케이션을 나타냅니다.
Engine은 클라이언트 요청을 적절한 Host와 Context로 라우팅하여, Servlet이나 JSP와 같은 웹 애플리케이션에서 처리되도록 합니다. 이 과정에서 Valve와 Logger는 보안, 요청 필터링, 로깅 등의 기능을 지원하며, Realm은 사용자 인증과 권한 부여를 담당하여 웹 애플리케이션의 보안을 관리합니다.
이렇게 톰캣은 무엇이고, 안에는 어떤 요소들이 있으며 각 요소들이 어떻게 상호작용을 하는지 알아보았습니다. 이번 블로그 글을 작성하며 톰캣이 무엇인지, WAS와 Servlet Container이 무엇인지, 톰캣 안에는 어떤 요소들이 있는지 등등 다양한 지식을 얻을 수 있어서 좋았습니다.
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
https://kadensungbincho.tistory.com/62