server.xml 구조 분석
server.xml은 Tomcat의 핵심 설정 파일로, 서버의 전체 계층 구조를 정의합니다. 이 파일을 이해하면 포트 설정, 가상 호스트, 애플리케이션 컨텍스트를 자유롭게 제어할 수 있습니다.
전체 계층 구조
server.xml은 XML 트리 구조로 Tomcat의 컴포넌트 계층을 표현합니다.
<Server> <!-- 최상위: JVM 프로세스 하나 -->
<Service> <!-- 커넥터 묶음 + 엔진 -->
<Connector port="8080" .../> <!-- 요청 수신 포트 -->
<Connector port="8009" .../> <!-- AJP 포트 (선택) -->
<Engine name="Catalina"> <!-- 요청 처리 엔진 -->
<Host name="localhost"> <!-- 가상 호스트 -->
<Context path="/" /> <!-- 웹 애플리케이션 -->
</Host>
</Engine>
</Service>
</Server>
각 컴포넌트 역할
| 컴포넌트 | 역할 | 핵심 속성 |
|---|---|---|
| Server | JVM 인스턴스 전체를 대표 | port (종료 명령 포트), shutdown |
| Service | 커넥터 집합 + 엔진 | name |
| Connector | 네트워크 요청 수신 (HTTP/AJP) | port, protocol, maxThreads |
| Engine | 요청을 Host로 라우팅 | name, defaultHost |
| Host | 가상 호스트 (도메인 기반 분기) | name, appBase |
| Context | 웹 애플리케이션 경로 매핑 | path, docBase |
Server 엘리먼트
<Server port="8005" shutdown="SHUTDOWN">
| 속성 | 설명 | 기본값 |
|---|---|---|
port | 종료 명령 수신 포트 | 8005 |
shutdown | 종료 명령 문자열 | SHUTDOWN |
보안 권고:
port="-1"로 설정하면 종료 포트를 비활성화할 수 있습니다. 원격 종료 명령 취약점 방지에 유용합니다.
<!-- 종료 포트 비활성화 (프로덕션 권장) -->
<Server port="-1" shutdown="SHUTDOWN">
Service 엘리먼트
<Service name="Catalina">
<!-- Connector들과 Engine이 여기에 포함됨 -->
</Service>
하나의 Server 안에 여러 Service를 등록할 수 있지만, 일반적으로 하나의 "Catalina" Service를 사용합니다.
Engine 엘리먼트
<Engine name="Catalina" defaultHost="localhost">
| 속성 | 설명 |
|---|---|
name | 엔진 식별자 (로그에 사용) |
defaultHost | 매핑되는 Host가 없을 때 사용할 기본 호스트 |
jvmRoute | 로드밸런서 클러스터 환경에서 세션 끈기(Sticky Session) 식별자 |
<!-- 클러스터 환경에서 jvmRoute 설정 -->
<Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat1">
Host 엘리먼트
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
| 속성 | 설명 | 기본값 |
|---|---|---|
name | 가상 호스트 이름 (도메인 매칭) | — |
appBase | 애플리케이션 기본 디렉터리 | webapps |
unpackWARs | WAR 파일 자동 압축 해제 | true |
autoDeploy | 런타임 중 WAR 자동 배포 | true |
deployOnStartup | 시작 시 appBase 자동 스캔 | true |
다중 가상 호스트 설정
<Engine name="Catalina" defaultHost="localhost">
<!-- 기본 호스트 -->
<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>
<!-- 추가 가상 호스트 -->
<Host name="api.example.com" appBase="/var/www/api"
unpackWARs="true" autoDeploy="false">
<Alias>api2.example.com</Alias> <!-- 별칭 -->
</Host>
<Host name="admin.example.com" appBase="/var/www/admin"
unpackWARs="true" autoDeploy="false">
</Host>
</Engine>
Context 엘리먼트
Context는 특정 웹 애플리케이션을 URL 경로에 매핑합니다. server.xml보다는 conf/Catalina/localhost/앱이름.xml 파일로 분리하는 것이 권장됩니다.
<!-- server.xml에 직접 등록 (비권장) -->
<Context path="/myapp" docBase="/var/www/myapp" reloadable="true"/>
<!-- 권장: conf/Catalina/localhost/myapp.xml -->
별도 Context 파일 방식 (권장)
<!-- conf/Catalina/localhost/myapp.xml -->
<Context docBase="/var/www/myapp"
path="/myapp"
reloadable="false"
crossContext="false">
<!-- JNDI 데이터소스 -->
<Resource name="jdbc/myDB"
auth="Container"
type="javax.sql.DataSource"
maxTotal="100"
maxIdle="30"
maxWaitMillis="10000"
username="dbuser"
password="dbpass"
driverClassName="com.mysql.cj.jdbc.Driver"
url="jdbc:mysql://localhost:3306/mydb?useSSL=false"/>
</Context>
| 속성 | 설명 | 기본값 |
|---|---|---|
path | URL 컨텍스트 경로 | "" (ROOT) |
docBase | 실제 파일 경로 | — |
reloadable | 클래스 변경 시 자동 리로드 | false |
crossContext | 다른 컨텍스트 접근 허용 | false |
프로덕션 주의:
reloadable="true"는 개발 환경에서만 사용합니다. 운영 환경에서는 성능 저하를 유발합니다.
GlobalNamingResources
전역 JNDI 리소스(데이터소스, 메일 세션 등)를 정의합니다.
<GlobalNamingResources>
<!-- 사용자 인증 DB (기본 제공) -->
<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"/>
<!-- 글로벌 JDBC 데이터소스 -->
<Resource name="jdbc/globalDB"
auth="Container"
type="javax.sql.DataSource"
driverClassName="com.mysql.cj.jdbc.Driver"
url="jdbc:mysql://db-host:3306/mydb"
username="appuser"
password="secret"
maxTotal="50"
maxIdle="10"/>
</GlobalNamingResources>
Realm — 인증 설정
Realm은 사용자 인증 방법을 정의합니다.
<!-- 파일 기반 인증 (tomcat-users.xml) -->
<Realm className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Realm>
<!-- JDBC 기반 인증 -->
<Realm className="org.apache.catalina.realm.JDBCRealm"
driverName="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/auth_db"
connectionName="auth_user"
connectionPassword="auth_pass"
userTable="users"
userNameCol="user_name"
userCredCol="user_pass"
userRoleTable="user_roles"
roleNameCol="role_name"/>
완성된 server.xml 예시 (프로덕션)
<?xml version="1.0" encoding="UTF-8"?>
<Server port="-1" shutdown="SHUTDOWN">
<Listener className="org.apache.catalina.startup.VersionLoggerListener"/>
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on"/>
<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">
<!-- HTTP/1.1 커넥터 -->
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
maxThreads="200"
minSpareThreads="10"
acceptCount="100"
enableLookups="false"
compression="on"
compressionMinSize="2048"
noCompressionUserAgents="gozilla, traviata"
compressibleMimeType="text/html,text/xml,text/plain,text/css,application/json"/>
<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="false">
<Valve className="org.apache.catalina.valves.AccessLogValve"
directory="logs"
prefix="localhost_access_log"
suffix=".txt"
pattern="%h %l %u %t "%r" %s %b %D"/>
<!-- 에러 처리 밸브 -->
<Valve className="org.apache.catalina.valves.ErrorReportValve"
showReport="false"
showServerInfo="false"/>
</Host>
</Engine>
</Service>
</Server>
Summary
| 컴포넌트 | 핵심 설정 | 주의사항 |
|---|---|---|
| Server | port="-1" (보안) | 종료 포트 비활성화 권장 |
| Host | autoDeploy="false" | 프로덕션에서 자동 배포 비활성화 |
| Context | 별도 XML 파일 권장 | reloadable="false" (운영) |
| Engine | jvmRoute | 클러스터 Sticky Session 필수 |
| Realm | LockOutRealm 래핑 | 무차별 대입 공격 방어 |