WAR 파일은 Java 웹 애플리케이션을 배포하기 위해 사용되는 형식으로, JSP나 Servlet과 같은 웹 애플리케이션 컴포넌트들이 WAS(Web Application Server) 컨테이너 위에서 실행될 수 있도록 미리 빌드된 형태이다.
WAR 파일은 WAS가 실행되는 시점에 웹 어플리케이션 설정과 리소스를 로드하며, War 파일을 실행하기 위해서는 별도의 웹 서버나 WAS가 필요하다는 특징이 있다.
Spring으로 개발된 웹 애플리케이션을 WAR 파일로 빌드하고, AWS와 MobaXterm을 활용하여 Tomcat에 배포하는 방법을 살펴보도록 하자!
프로젝트 목표
- Spring Legacy Project(Legacy Java) 구조를 학습한다.
- Spring 웹 애플리케이션을 War 파일로 빌드하는 과정을 이해한다.
- AWS를 사용하여 웹 애플리케이션을 호스팅하는 방법을 익힌다.
- MobaXterm을 통해 원격 서버에 접속하고, Tomcat에 War 파일을 배포하는 과정을 숙지한다.
- 리눅스에서의 프로세스 관리 등 기본 시스템 관리 작업을 익힌다.
💡Apache Tomcat
https://tomcat.apache.org/download-90.cgi
Apache Tomcat은 아파치 소프트웨어 재단에서 개발한 서블릿 컨테이너(또는 웹 컨테이너)만 있는 웹 애플리케이션 서버이다.
Apache는 웹서버 전용 기능이고 Apache Tomcat은 WAS 기능을 한다. Tomcat에도 기본적인 웹서버 기능이 있어서 공부할 때는 톰캣만 사용해서 웹서버를 구성할 수 있다.
즉, Apache Tomcat은 경량 Apache와 Tomcat을 합친 것이라고 생각하면 된다. 다만 Apache 웹서버에 비해 기능이 한정적이라 규모가 있는 웹서비스는 둘을 분리해서 사용하는 편이다.
Tomcat은 WEB/WAS의 기능을 가진 자바 애플리케이션이므로,Java Servlet, JavaServer Pages(JSP), Java Expression Language(EL) 및 WebSocket을 실행할 수 있다. 그중에서도 주로 Java 웹 애플리케이션의 배포를 담당하며, Java EE(Enterprise Edition) 기술 스택의 일부인 서블릿 컨테이너 역할을 한다.
Tomcat은 클라이언트로부터 HTTP 요청을 받아들이고, 서블릿 및 JSP와 같은 동적인 콘텐츠를 생성하기 위한 서비스를 제공하며, 정적인 콘텐츠를 제공하기 위한 기능도 제공한다.
웹 애플리케이션을 Tomcat에 배포하면 Tomcat은 해당 애플리케이션을 실행하고 관리한다. 이를 통해 개발한 웹 애플리케이션을 인터넷상에 게시할 수 있다.
Tomcat과 JDK 버전 호환
https://tomcat.apache.org/whichversion.html
사용하는 Tomcat 버전과 JDK 버전이 호환이 되는지 확인하도록 한다.
본 글에서는 apache-tomcat-9.0.86과 JDK 17을 사용한다.
아파치 톰캣 9.0.x 시리즈는 Java 8 이상의 버전을 지원하므로 JDK 17 또한 호환될 것으로 보인다.
Apache Tomcat 설치
$ wget https://dlcdn.apache.org/tomcat/tomcat-9/v9.0.86/bin/apache-tomcat-9.0.86.tar.gz
$ tar -xvf apache-tomcat-9.0.86.tar.gz
$ sudo mkdir -p /opt/tomcat
$ sudo mv apache-tomcat-9.0.86 /opt/tomcat
1. Apache Tomcat 9.0.86을 다운로드한다. 만약 다운로드 되지 않고 오류가 발생한다면 새로운 버전이 출시된 것이니 수정해야 한다.
2. 다운로드한 파일을 압축 해제한다.
3. /opt/tomcat 디렉터리를 생성한다. -p 옵션은 하위 디렉터리를 생성할 때 상위 디렉터리가 없으면 함께 생성한다.
5. 압축 해제한 Tomcat 디렉터리의 이름을 변경하면서 /opt/tomcat로 이동한다.
tomcat servcice 파일 수정
$ sudo vim /etc/systemd/system/tomcat.service
[Unit]
Description=Tomcat 9.0.86 servlet container
After=network.target
[Service]
Type=forking
User=root
Group=root
Environment="JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64"
Environment="JAVA_OPTS=-Djava.security.egd=file:///dev/urandom"
Environment="CATALINA_BASE=/opt/tomcat/apache-tomcat-9.0.86"
Environment="CATALINA_HOME=/opt/tomcat/apache-tomcat-9.0.86"
Environment="CATALINA_PID=/opt/tomcat/apache-tomcat-9.0.86/temp/tomcat.pid"
Environment="CATALINA_OPTS=-Xms512M -Xmx1024M -server -XX:+UseParallelGC"
ExecStart=/opt/tomcat/apache-tomcat-9.0.86/bin/startup.sh
ExecStop=/opt/tomcat/apache-tomcat-9.0.86/bin/shutdown.sh
[Install]
WantedBy=multi-user.target
1. 시스템 서비스 파일을 편집기로 연다. vim이 아니라 nano로 열어도 된다.
2. 해당 파일에는 Tomcat 서비스를 설명하고, 실행 순서 등을 정의한다. 예를 들어, 서비스가 네트워크 로드 이후에 시작되도록 지정한다.
Tomcat 서비스 변경 후 로드
$ sudo systemctl daemon-reload
$ sudo systemctl restart tomcat
$ systemctl status tomcat.service
1. 시스템 서비스 관련 설정을 다시 로드한다. 위와 같이 Tomcat 서비스 파일을 수정했을 경우 Tomcat을 재시작하기 전에 설정을 리로드 하는 것을 권장한다.
2. Tomcat 서비스를 다시 시작한다.
3. Tomcat 서비스의 상태를 확인한다.
로그 모니터링
$ cd /home/ubuntu/apache-tomcat-9.0.83/bin/
$ ./startup.sh
$ tail -f /opt/tomcat/apache-tomcat-9.0.86/logs/catalina.out
$ sudo less /opt/tomcat/apache-tomcat-9.0.86/logs/catalina.out
1. Tomcat 디렉터리로 이동한다.
2. Tomcat을 시작한다. ./는 현재 폴더를 의미한다. 따라서 cd로 startup.sh 명령어를 실행시킬 수 있는 위치로 이동한 것이다.
3. Tomcat 로그를 실시간으로 모니터링한다. tail -f 명령어는 파일의 끝에서 시작하여 실시간으로 파일의 새로운 내용을 표시한다. 주로 로그 파일을 모니터링하는 데 사용된다. -f 옵션은 "follow"의 약자로, 파일의 끝이 아니라 실시간으로 변하는 파일 내용을 계속해서 표시한다.
4. Tomcat 로그를 읽는다. less 명령어는 파일을 페이지 단위로 보여주는 텍스트 뷰어이다. 파일 내용을 위아래로 스크롤하고 검색할 수 있다. less 명령을 실행하면 파일의 끝에 도달하면 추가적인 내용을 로드하지 않는다. 종료하려면 'q' 키를 누르면 된다.
권한 부여
$ sudo chmod -R 777 /opt/tomcat/apache-tomcat-9.0.86
$ sudo chmod -R 777 /opt/tomcat/apache-tomcat-9.0.86/webapps
1. Tomcat 디렉터리 및 하위 파일 및 디렉터리의 모든 사용 권한을 변경하여 모든 사용자에게 읽기, 쓰기 및 실행 권한을 부여한다.
2. Tomcat 웹 애플리케이션 디렉터리의 모든 사용 권한을 변경하여 모든 사용자에게 읽기, 쓰기 및 실행 권한을 부여한다.
프로세스 확인
$ cd /opt/tomcat/apache-tomcat-9.0.86/bin
$ ps aux | grep tomcat
$ sudo ss -tuln
1. Tomcat bin 디렉토리로 이동한다.
2. 현재 시스템에서 열린 TCP 및 UDP 포트와 해당 프로세스를 확인한다. 시스템에서 현재 실행 중인 프로세스 중 "tomcat"이라는 문자열을 포함하는 모든 프로세스를 보여준다.
3. Tomcat 프로세스를 확인한다. 또한, 현재 네트워크 소켓 연결 상태를 보여준다.
- -t: TCP 소켓을 보여준다.
- -u: UDP 소켓을 보여준다.
- -l: 리스닝 소켓만 표시한다.
- -n: IP 주소와 포트 번호를 숫자로 표시한다(네트워크 주소 변환 없음).
즉, 이 명령어는 시스템에서 현재 활성화된 TCP 및 UDP 포트를 보여주고, 각각이 무엇에 바인딩되어 있는지를 나열한다. 네트워크 연결을 확인하거나 특정 포트를 사용하는 프로세스를 파악하는 데 사용된다.
💡JAR와 WAR
JAR (Java Archive)
1. JAR 파일은 Java 애플리케이션과 라이브러리를 패키징 하는 데 사용된다.
2. Java 클래스 파일, 리소스, 메타데이터 및 라이브러리들을 포함한다.
3. 주로 독립 실행형 애플리케이션과 라이브러리를 패키징 하는 데 사용된다. 예를 들어, 단일 JAR 파일로 실행 가능한 Java 프로그램을 생성할 수 있다.
4. JAR 파일은 JVM(Java Virtual Machine)에서 실행된다. 따라서 Java Runtime Environment(JRE)가 필요하다.
WAR (Web Application Archive)
1. WAR 파일은 Java 웹 애플리케이션을 패키징하는 데 사용된다.
2. HTML, CSS, JavaScript, JSP(JavaServer Pages), 서블릿 클래스, 리소스 등을 포함한다.
3. 주로 웹 애플리케이션 서버(예: Apache Tomcat, Jetty 등)에서 실행하는 데 사용된다. WAR 파일을 통해 웹 애플리케이션을 배포하고 실행할 수 있다.
4. WAR 파일은 웹 애플리케이션의 구조를 정의하는 규칙이 있다. 일반적으로 WEB-INF 디렉터리 아래에 설정 파일, 서블릿 클래스, JSP 파일 등이 포함된다.
요약하자면 JAR와 WAR는 용도, 구조, 실행 환경에서 차이가 있다.
JAR 파일은 독립 실행형 Java 애플리케이션과 라이브러리를 패키징 하는 데 사용되며, WAR 파일은 Java 웹 애플리케이션을 패키징하여 웹 애플리케이션 서버에서 실행하는 데 사용된다.
JAR 파일은 JVM에서 직접 실행되지만 WAR 파일은 웹 애플리케이션 서버를 통해 실행된다.
2024.02.10 - [Programming/AWS] - [AWS] Spring Boot 웹 애플리케이션 AWS와 Mobaxterm으로 서버에 배포하기
JAR 파일로 AWS 서버에 배포하는 방법은 위 글을 참고한다.
💡WAR 파일로 배포하기 위한 설정
배포 형식 지정
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.1</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.test</groupId>
<artifactId>bank</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging> <!-- 배포 형식 지정 -->
<name>AtlanBank</name>
<description>Atlan Bank project for Spring Boot</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
...
</dependencies>
<build
...
</build>
</project>
<packaging>war</packaging> <!-- 배포 형식 지정 -->
WAR 파일 형식으로 패키징을 하겠다는 의미로 pom.xml에서 war로 빌드하기 위한 설정을 추가하였다.
<packaging> 프로퍼티 값을 war로 작성하면 된다.
의존성 추가
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
JAR와는 다르게 WAR로 배포하려면 추가적으로 dependency를 추가해야 한다.
scope를 provided로 지정함으로써 컴파일 단계에서만 톰캣 라이브러리를 사용하고, 프로젝트를 배포할 때는 이미 구축된 톰캣 서버를 활용할 수 있다.
이 방법을 통해 프로젝트가 내장 톰캣이 아닌 외부 톰캣 서버에서 실행될 수 있도록 한다.
Spring Boot Application 클래스
package com.test.bank;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
@SpringBootApplication(exclude = SecurityAutoConfiguration.class)
@MapperScan(basePackages = { "com.test.bank.event.mapper", "com.test.bank.benefit.mapper", "com.test.bank.news.mapper", "com.test.bank.member.mapper", "com.test.bank.ticket.mapper", "com.test.bank.card.mapper", "com.test.bank.forex.mapper", "com.test.bank.loan.mapper", "com.test.bank.deposit.mapper" })
public class AtlanBankApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(AtlanBankApplication.class, args);
}
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(SpringApplication.class);
}
}
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(SpringApplication.class);
}
프로젝트에서 main 메서드가 존재하며 스프링 부트 앱을 시작하는 역할을 하는 클래스가 있다. 이 Spring Boot Application 클래스에 SpringApplicationBuilder 클래스를 추가하였다.
Spring Boot는 일반적으로 내장 Tomcat과 같은 서버를 포함한 독립 실행형 JAR 파일로 빌드되는데, 이는 단순히 JAR 파일을 실행함으로써 애플리케이션을 실행할 수 있게 해 준다.
JSP와 같은 특정 기술을 사용하는 경우에는 WAR 파일 형태로 빌드하여 외부 서블릿 컨테이너에서 실행해야 한다. 그리고 이 경우 Spring Boot 애플리케이션을 외부 서블릿 컨테이너에서 실행하려면 SpringBootServletInitializer를 상속받아야 한다.
SpringBootServletInitializer 상속받기
이 클래스에 SpringBootServletInitializer를 상속하고 @Override 메서드를 추가하여 구성 방법을 재정의하였다.
그리고 extends SpringBootServletInitializer를 통해 Tomcat에 필요한 Servlet 컨텍스트를 초기화하고 SpringBootServlet를 확장하였다.
SpringBootServletInitializer의 필요성
SpringBootServletInitializer를 상속받는 이유는 외부 서블릿 컨테이너에서 Spring Boot 애플리케이션을 실행하기 위해서이다. 이 클래스를 상속하면 Spring Boot 애플리케이션을 WAR 파일로 패키징하고 외부 서블릿 컨테이너에 배포할 수 있다. 이를 통해 전통적인 웹 애플리케이션 서버 환경에서 Spring Boot 애플리케이션을 실행할 수 있다.
이전에는 외부 Tomcat에서 Spring 웹 애플리케이션을 실행하기 위해서 web.xml 파일에 애플리케이션의 구성을 명시해야 했다. 이 파일은 Apache Tomcat(Servlet Container)이 시작될 때 읽혀서 웹 애플리케이션을 초기화한다.
그러나 Servlet 3.0 이후부터는 web.xml 없이도 Spring 웹 애플리케이션을 실행할 수 있게 되었고, 대신에 WebApplicationInitializer 인터페이스를 구현하여 Servlet 컨테이너의 초기화를 프로그래밍적으로 제어할 수 있게 되었다. 이 인터페이스를 사용하여 ServletContext에 Spring IoC 컨테이너(보통 AnnotationConfigWebApplicationContext를 사용)를 설정하고 추가할 수 있다.
Spring Boot에서도 이와 비슷한 원리가 적용된다. 외부 Tomcat에서 Spring Boot 웹 애플리케이션을 실행하기 위해서는 SpringBootServletInitializer를 상속받아 configure 메서드를 오버라이드하여 필요한 설정을 추가해야 한다. 이를 통해 Spring Boot 애플리케이션을 WAR 파일로 패키징하고 외부 Tomcat에 배포하여 실행할 수 있다.
결론적으로 web.xml 없이 Spring Boot 애플리케이션을 WAR 파일로 패키징하고 외부 Tomcat에 배포하여 실행하기 위해서는 SpringBootServletInitializer를 상속받아 configure 메서드를 오버라이드하여 필요한 설정을 추가하는 작업이 필요하다.
버전 번호 포함하지 않기
<finalName>${artifactId}</finalName>
버전 번호가 포함되지 않도록 하려면 <finalName> 태그를 사용하여 최종 WAR 파일 이름을 수정하면 된다.
web-services 등으로 WAR 파일의 이름을 수정할 수 있다.
❗오류 메시지 해결
ORA-12505 오류
[INFO] Running com.test.bank.[1mJDBCTest[m java.sql.SQLException: ORA-12505: 데이터베이스에 접속할 수 없습니다. SID xe이(가) host localhost port 1521의 리스너에 등록되지 않았습니다. (CONNECTION_ID=mp6KU5jfR5CaLPBCcWyRdg==)
https://docs.oracle.com/error-help/db/ora-12505/
Caused by: oracle.net.ns.NetException: ORA-12505: 데이터베이스에 접속할 수 없습니다. SID xe이(가) host localhost port 1521의 리스너에 등록되지 않았습니다. (CONNECTION_ID=mp6KU5jfR5CaLPBCcWyRdg==)
https://docs.oracle.com/error-help/db/ora-12505/
ORA-12505 오류는 오라클 중지 오류이다. Oracle Server를 실행시키면 오류가 해결된다.
Listener에 대한 오류가 발생하면 첫 번째로 오라클이 중지되어 있지는 않은지부터 확인한다.
2023.10.19 - [Programming/JDBC] - [JDBC] DB 접속 및 오류 해결
DB 오류에 관해서는 위 글을 참고한다.
JUnit Jupiter 버전 오류
java.lang.AbstractMethodError: Receiver class org.junit.jupiter.engine.descriptor.ClassExtensionContext does not define or inherit an implementation of the resolved method 'abstract org.junit.jupiter.api.extension.ExecutableInvoker getExecutableInvoker()' of interface org.junit.jupiter.api.extension.ExtensionContext.
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.10.1</version>
<!--<scope>test</scope>-->
</dependency>
JUnit Jupiter 버전이 호환되지 않아서 오류가 발생하고 있다.
프로젝트의 JUnit Jupiter 버전을 Spring Boot 3.2.1과 호환되는 버전으로 변경해야 한다. JUnit Jupiter의 버전을 최신 버전으로 업데이트하자 오류가 해결되었다.
Skip Tests
이러한 오류가 확인되는 것은 빌드 과정에서 Test가 진행되도록 했기 때문이다.
오류를 무시하고 싶다면 Edit Configuration에서 빌드를 할 때 Skip Tests 옵션을 체크하면 된다.
💡WAR 파일로 프로젝트 빌드
Edit Configuration
프로젝트를 우클릭하고, [Run As] 메뉴에서 [Maven build...] 메뉴를 선택한다.
기존에 빌드된 파일이 있을 경우 착오가 생길 수 있으므로 [Maven clean]을 우선 진행해도 된다.
Goals 설정
Goals에 package를 입력하고 Profiles에 pom.xml이 기입되어 있는 경우 지운다.
이후 Run 버튼을 클릭하면 빌드가 진행된다.
메이븐에서는 하나의 플러그인에서 여러 작업을 수행할 수 있도록 지원하며, 플러그인에서 실행할 수 있는 각각의 기능(명령)을 Goal이라고 한다.
Goals에 clean package와 같은 명령어를 입력할 수 있다. 이는 이전에 build 했던 것들을 clean 시키고 package 단계까지 진행한다는 설정이다.
https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html
clean | remove all files generated by the previous build |
package | take the compiled code and package it in its distributable format, such as a JAR. |
clean package를 하는 이유는 위 Document를 참고한다.
빌드 성공
BUILD SUCCESS! 빌드가 완료되었다.
WAR 파일이 target 폴더 내에 생성된 것을 확인할 수 있다.
Original 파일
Original 파일은 빌드 과정에서 생성되는 중간 단계의 결과물로, 일반적으로 작업 디렉터리 내에 위치하며 개발자가 직접 사용하는 것이 아니라 빌드 도구가 사용하는 파일이다.
배포 파일에는 이러한 original 파일들을 모두 포함하고 있으며, 추가적으로 메타 데이터와 리소스를 압축한 파일 형식으로 제공된다.
💡Tomcat에 WAR 파일 배포
Server.xml
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!-- Note: A "Server" is not itself a "Container", so you may not
define subcomponents such as "Valves" at this level.
Documentation at /docs/config/server.html
-->
<Server port="8005" shutdown="SHUTDOWN">
<Listener className="org.apache.catalina.startup.VersionLoggerListener" />
<!-- Security listener. Documentation at /docs/config/listeners.html
<Listener className="org.apache.catalina.security.SecurityListener" />
-->
<!-- APR library loader. Documentation at /docs/apr.html -->
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
<!-- Prevent memory leaks due to use of particular java/javax APIs-->
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
<!-- Global JNDI resources
Documentation at /docs/jndi-resources-howto.html
-->
<GlobalNamingResources>
<!-- Editable user database that can also be used by
UserDatabaseRealm to authenticate users
-->
<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>
<!-- A "Service" is a collection of one or more "Connectors" that share
a single "Container" Note: A "Service" is not itself a "Container",
so you may not define subcomponents such as "Valves" at this level.
Documentation at /docs/config/service.html
-->
<Service name="Catalina">
<!--The connectors can use a shared executor, you can define one or more named thread pools-->
<!--
<Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
maxThreads="150" minSpareThreads="4"/>
-->
<!-- A "Connector" represents an endpoint by which requests are received
and responses are returned. Documentation at :
Java HTTP Connector: /docs/config/http.html
Java AJP Connector: /docs/config/ajp.html
APR (HTTP/AJP) Connector: /docs/apr.html
Define a non-SSL/TLS HTTP/1.1 Connector on port 8080
-->
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
maxParameterCount="1000"
/>
<!-- A "Connector" using the shared thread pool-->
<!--
<Connector executor="tomcatThreadPool"
port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
maxParameterCount="1000"
/>
-->
<!-- Define an SSL/TLS HTTP/1.1 Connector on port 8443
This connector uses the NIO implementation. The default
SSLImplementation will depend on the presence of the APR/native
library and the useOpenSSL attribute of the AprLifecycleListener.
Either JSSE or OpenSSL style configuration may be used regardless of
the SSLImplementation selected. JSSE style configuration is used below.
-->
<!--
<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
maxThreads="150" SSLEnabled="true"
maxParameterCount="1000"
>
<SSLHostConfig>
<Certificate certificateKeystoreFile="conf/localhost-rsa.jks"
type="RSA" />
</SSLHostConfig>
</Connector>
-->
<!-- Define an SSL/TLS HTTP/1.1 Connector on port 8443 with HTTP/2
This connector uses the APR/native implementation which always uses
OpenSSL for TLS.
Either JSSE or OpenSSL style configuration may be used. OpenSSL style
configuration is used below.
-->
<!--
<Connector port="8443" protocol="org.apache.coyote.http11.Http11AprProtocol"
maxThreads="150" SSLEnabled="true"
maxParameterCount="1000"
>
<UpgradeProtocol className="org.apache.coyote.http2.Http2Protocol" />
<SSLHostConfig>
<Certificate certificateKeyFile="conf/localhost-rsa-key.pem"
certificateFile="conf/localhost-rsa-cert.pem"
certificateChainFile="conf/localhost-rsa-chain.pem"
type="RSA" />
</SSLHostConfig>
</Connector>
-->
<!-- Define an AJP 1.3 Connector on port 8009 -->
<!--
<Connector protocol="AJP/1.3"
address="::1"
port="8009"
redirectPort="8443"
maxParameterCount="1000"
/>
-->
<!-- An Engine represents the entry point (within Catalina) that processes
every request. The Engine implementation for Tomcat stand alone
analyzes the HTTP headers included with the request, and passes them
on to the appropriate Host (virtual host).
Documentation at /docs/config/engine.html -->
<!-- You should set jvmRoute to support load-balancing via AJP ie :
<Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1">
-->
<Engine name="Catalina" defaultHost="localhost">
<!--For clustering, please take a look at documentation at:
/docs/cluster-howto.html (simple how to)
/docs/config/cluster.html (reference documentation) -->
<!--
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>
-->
<!-- Use the LockOutRealm to prevent attempts to guess user passwords
via a brute-force attack -->
<Realm className="org.apache.catalina.realm.LockOutRealm">
<!-- This Realm uses the UserDatabase configured in the global JNDI
resources under the key "UserDatabase". Any edits
that are performed against this UserDatabase are immediately
available for use by the Realm. -->
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Realm>
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<!-- SingleSignOn valve, share authentication between web applications
Documentation at: /docs/config/valve.html -->
<!--
<Valve className="org.apache.catalina.authenticator.SingleSignOn" />
-->
<!-- Access log processes all example.
Documentation at: /docs/config/valve.html
Note: The pattern used is equivalent to using pattern="common" -->
<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에서 잘못된 설정이 있다면 Tomcat에 배포된 WAR 파일은 주소를 요청받아서 실행되자마자 바로 Tomcat이 꺼져 버리게 된다. 따라서 자꾸 Tomcat이 꺼지는 문제가 발생한다면 Server.xml에서 오류 코드가 있는지 확인해야 한다.
Host에 Context 태그 추가
<Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true">
<Context path="/demo" docBase="/opt/tomcat/apache-tomcat-9.0.86/webapps/demo/" reloadable="true">
<Resources cachingAllowed="true" cacheMaxSize="100000" />
</Context>
</Host>
ROOT.war가 아니라 다른 이름으로 배포한 경우에는 Server.xml의 Host 태그를 수정해야 한다.
또한 Resources 태그를 만들고, 캐시가 부족해서 생기는 오류를 방지하기 위해 cacheMaxSize를 설정하였다.
UTF-8 설정
<Connector port="8080" protocol="HTTP/1.1"
URIEncoding="UTF-8"
connectionTimeout="20000"
redirectPort="8443"
maxParameterCount="1000"
/>
URIEncoding UTF-8 설정을 해 주었다.
📌Legacy Java 구조
폴더 구조
1. src/main/java
Java 소스 코드가 모여 있는 폴더.
MVC 패턴의 서블릿 구조에 맞춰 패키지로 클래스를 분리하여 작성한다.
백엔드 로직을 처리하는 클래스들이 위치하는 곳으로, 프로젝트의 주요 기능이 담긴 코드가 위치한다.
2. src/main/resources
자바 클래스에서 사용하는 리소스를 보관하는 폴더.
DB 연결, 의존성 주입(DI)을 위한 XML 파일 등을 포함한다.
소스 코드 외의 모든 자원이 위치한다.
3. src/test/java, src/test/resources
테스트를 위한 자바 코드와 자원을 보관하는 폴더.
단위 테스트용 소스코드와 자원을 포괄한다.
테스트 코드는 소스 코드와 동일한 구조를 따르며, 해당 코드들은 개발 시 빌드 및 테스트에 사용된다.
4. src/main/webapp
웹에 관련된 자원이 담겨 있는 루트 폴더.
하위 폴더에는 정적 리소스(css, js, 이미지 등), 컴파일된 파일 및 환경설정 파일이 위치한다.
웹 애플리케이션의 핵심 부분을 담당하며, 프론트엔드 자원 및 웹 페이지 구성 요소가 위치한다.
5. src/main/webapp/resources
웹에 필요한 정적 자원(js, css, img 등)을 보관하는 폴더.
사용자가 직접 접근할 수 있는 정적 자원들이 위치한다.
주로 CSS, JavaScript, 이미지 등의 파일들이 포함되며, 웹 페이지의 디자인 및 기능 구현을 위해 사용된다.
6. src/main/webapp/WEB-INF
웹에 필요한 코드 파일, 컴파일된 파일, 환경설정 파일이 보관되는 폴더.
외부 사용자가 직접 접근할 수 없으며, 내부적으로만 접근 가능하다.
주로 보안이 필요한 설정 파일들이 이곳에 위치하며, 웹 애플리케이션의 중요한 구성 요소들이 이곳에 위치한다.
7. src/main/webapp/WEB-INF/classes
컴파일된 파일이 보관되는 폴더.
자바 소스 코드가 컴파일되어 생성된 클래스 파일들이 이곳에 위치하며, 웹 애플리케이션의 실행에 필요한 자바 클래스들이 이곳에 포함된다.
8. src/main/webapp/WEB-INF/spring
스프링 환경설정 파일이 보관되는 폴더.
스프링 프레임워크에서 사용하는 설정 파일들이 이곳에 위치하며, 스프링 빈 설정 및 라우팅 설정 등이 이곳에 포함된다.
9. src/main/webapp/WEB-INF/views
JSP, HTML 파일이 보관되는 폴더.
URL 구조를 따라가는 기준점이며, 사용자가 입력하고 컨트롤러가 받아주는 URL이 이 폴더의 구조를 따라가므로 잘 정리하여 사용해야 한다.
주로 웹 페이지의 뷰(View) 파일들이 이곳에 위치하며, 프론트엔드와 백엔드의 연동을 위해 사용된다.
Maven 빌드 관리
Maven이란?
Java 프로젝트를 관리하는 툴로, XML 파일을 이용하여 라이브러리를 자동으로 다운로드하거나 프로젝트를 빌드해 준다.
pom.xml
Maven 설정 파일로, 프로젝트의 빌드 옵션을 설정한다.
프로젝트의 정보, 의존성 라이브러리, 빌드 설정 등을 기술한다.
💡Java(JSP 형태) WAR 형태 Build 해서 배포
JSP 형태의 Java 언어로 개발한 Spring 프로젝트를 WAR 파일로 Build 해서 톰캣서버에 배포해 보도록 하자.
위에서 설명한 방법은 Spring Boot로 작업하려고 한 과정으로, Tomcat 버전과 Server.xml, 프로젝트 의존성을 설정하는 데 더 연구가 필요할 듯하다.
Spring Boot만 사용하다가 오랜만에 xml 방식의 Spring을 사용하려고 하니까 재미있기도 했지만 당시에 왜 이런 코드를 사용했었는지 헷갈리는 부분이 더러 있었다. 주석을 충분히 달아놓을 걸 하는 아쉬움도 있었다. 추후 Legacy Java 구조에 대해 보완해야겠다는 생각이 들었다.
root-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mybatis-spring="http://mybatis.org/schema/mybatis-spring"
xsi:schemaLocation="http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring-1.2.xsd
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- Root Context: defines shared resources visible to all other web components -->
<!-- HikariCP 데이터베이스 연결 풀 설정 -->
<bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig">
<!-- 사용할 JDBC 드라이버 클래스 지정 -->
<property name="driverClassName" value="net.sf.log4jdbc.sql.jdbcapi.DriverSpy"></property>
<!-- 데이터베이스 접속 URL 설정 -->
<property name="jdbcUrl" value="jdbc:log4jdbc:oracle:thin:@localhost or 퍼블릭 IPv4:1521:xe"></property>
<!-- 데이터베이스 접속 계정 정보 설정 -->
<property name="username" value="ddseungwon"></property>
<property name="password" value="pass"></property>
</bean>
<!-- HikariCP 데이터베이스 연결 풀 빈 등록 -->
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
<!-- HikariConfig 설정을 참조하여 DataSource 빈 생성 -->
<constructor-arg ref="hikariConfig"></constructor-arg>
</bean>
<!-- MyBatis SqlSessionFactory 설정 -->
<bean id="sessionfactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 데이터베이스 연결 DataSource 설정 -->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- MyBatis Mapper 스캔 설정 -->
<mybatis-spring:scan base-package="com.project.*" />
</beans>
root-context.xml 설정 파일은 Spring 프레임워크를 사용하여 데이터베이스와의 연결을 설정하고 MyBatis를 이용하여 데이터베이스와의 상호작용을 구성한다.
이 파일에서 문제가 발생한다면 주로 데이터베이스 연결 정보나 데이터베이스 서버 상태에 있을 가능성이 높다. 설정된 데이터베이스 URL이나 호스트, 포트, 사용자 이름, 비밀번호 등이 정확한지 확인하고, 데이터베이스 서버가 실행 중인지, 네트워크 연결에 문제가 없는지도 확인해야 한다.
기능 설명
- HikariCP 설정: 데이터베이스 연결 풀을 구성하고, 이를 위해 HikariConfig 클래스를 사용한다. 드라이버 클래스, JDBC URL, 사용자 이름 및 비밀번호를 설정한다.
- 데이터 소스 설정: HikariCP 데이터베이스 연결 풀을 사용하여 데이터 소스 빈을 설정한다.
- SqlSessionFactory 설정: MyBatis의 SqlSessionFactory를 설정한다. 데이터베이스 연결에 사용할 데이터 소스를 설정한다.
- MyBatis Mapper 스캔: MyBatis Mapper 인터페이스를 스캔하여 매퍼 구성을 자동으로 수행한다.
servlet-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-5.0.xsd
http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- DispatcherServlet Context: 이 서블릿의 요청 처리 인프라 정의 -->
<!-- Spring MVC의 @Controller 프로그래밍 모델 활성화 -->
<annotation-driven />
<!-- /resources/**에 대한 HTTP GET 요청을 처리하여 ${webappRoot}/resources 디렉토리에 있는 정적 리소스를 효율적으로 제공 -->
<resources mapping="/resources/**" location="/resources/" />
<!-- Tiles 뷰 리졸버 설정 -->
<beans:bean id="tilesViewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<!-- TilesView 클래스를 사용하여 뷰 해석 -->
<beans:property name="viewClass" value="org.springframework.web.servlet.view.tiles3.TilesView" />
<!-- 우선 순위 설정 -->
<beans:property name="order" value="1" />
</beans:bean>
<!-- TilesConfigurer 설정 -->
<beans:bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles3.TilesConfigurer">
<beans:property name="definitions">
<!-- Tiles 설정 파일의 위치 지정 -->
<beans:list>
<beans:value>/WEB-INF/tiles.xml</beans:value>
</beans:list>
</beans:property>
</beans:bean>
<!-- 지정된 패키지에서 컴포넌트 스캔 수행 -->
<context:component-scan base-package="com.project.dd" />
<!-- 멀티파트 리졸버 설정 -->
<beans:bean id="multipartResolver" class="org.springframework.web.multipart.support.StandardServletMultipartResolver"></beans:bean>
</beans:beans>
servlet-context.xml 설정 파일은 Spring MVC 웹 애플리케이션의 DispatcherServlet에 대한 설정을 정의한다.
기능 설명
- annotation-driven: Spring MVC에서 애노테이션을 사용하여 컨트롤러를 정의할 수 있도록 활성화한다.
- <resources>: /resources/** 경로에 대한 정적 리소스를 처리하기 위한 설정이다. 예를 들어, CSS, JavaScript 및 이미지 파일을 제공하는 데 사용된다.
- <beans:bean id="tilesViewResolver">: Tiles를 사용하여 뷰를 해결하는 데 필요한 View Resolver를 구성한다.
- <beans:bean id="tilesConfigurer">: Tiles의 정의를 설정하는 데 사용된다. tiles.xml 파일에 정의된 Tiles 정의 파일의 위치를 지정한다.
- <context:component-scan>: 지정된 패키지에서 Spring 컴포넌트를 검색하고 등록한다. 여기서는 com.project.dd 패키지의 컨트롤러를 스캔하여 등록한다.
- <beans:bean id="multipartResolver">: 파일 업로드를 처리하기 위한 Multipart Resolver를 구성한다. 이를 통해 멀티파트 요청에서 파일을 추출하고 처리할 수 있다.
DispatcherServlet(디스패처 서블릿)
DispatcherServlet은 Spring MVC 프레임워크에서 클라이언트의 요청을 받아서 처리하는 주요한 컨트롤러 역할을 한다. 이 서블릿은 웹 애플리케이션의 전반적인 요청 처리 인프라를 정의하며, 클라이언트로부터의 요청을 받아 적절한 컨트롤러에 전달하고, 컨트롤러가 처리한 결과를 View에 전달하여 응답을 생성한다. 이는 Spring MVC 프레임워크에서 HTTP 요청을 처리하는 핵심 역할을 담당하며, 서블릿 컨테이너에서 미리 정의된 서블릿 매핑에 따라 요청을 처리한다.
2024.02.08 - [Programming/Spring] - [Spring] Spring MVC Framework: MVC 패턴의 구조, 동작 과정
DispatcherServlet과 Spring MVC Pattern에 대해서는 위 글을 참고한다.
root-context.xml과 servlet-context.xml의 차이점
root-context.xml은 Spring 웹 애플리케이션의 root context에 대한 설정을 정의한다. 주로 데이터베이스 연결, 서비스 빈 등과 같은 애플리케이션 전반에 걸친 설정을 포함한다. 여러 서블릿 간에 공유되는 빈들이 이 파일에서 정의되며, 대개는 데이터베이스 연결, 트랜잭션 관리, 보안 설정 등과 같은 서비스적인 부분을 담당한다.
servlet-context.xml은 각각의 DispatcherServlet마다 해당하는 servlet context에 대한 설정을 정의한다. 이 파일은 주로 웹 계층과 관련된 설정을 포함하며, 요청 매핑, 뷰 리졸버, 핸들러 매핑 등과 같은 웹 계층의 기능들을 설정한다. 각각의 DispatcherServlet은 서로 다른 서블릿 매핑을 가지고 있을 수 있으며, 이에 따라 다른 servlet-context.xml 파일을 사용하여 설정을 정의할 수 있다. 이 파일은 주로 컨트롤러, 뷰 리졸버, 리소스 핸들러와 같이 웹 계층에 필요한 구성 요소들을 정의한다.
security-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-5.0.xsd
http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
<!-- Spring MVC의 @Controller 프로그래밍 모델을 활성화 -->
<annotation-driven />
<!-- /resources/** 경로의 HTTP GET 요청을 처리하여 ${webappRoot}/resources 디렉토리에서 정적 리소스를 효율적으로 제공 -->
<resources mapping="/resources/**" location="/resources/" />
<!-- Tiles 뷰 리졸버 설정 -->
<beans:bean id="tilesViewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<!-- Tiles 뷰 클래스 지정 -->
<beans:property name="viewClass" value="org.springframework.web.servlet.view.tiles3.TilesView" />
<!-- 뷰 리졸버 우선순위 설정 -->
<beans:property name="order" value="1" />
</beans:bean>
<!-- Tiles 설정을 위한 TilesConfigurer 설정 -->
<beans:bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles3.TilesConfigurer">
<!-- Tiles 정의 파일 위치 설정 -->
<beans:property name="definitions">
<beans:list>
<beans:value>/WEB-INF/tiles.xml</beans:value>
</beans:list>
</beans:property>
</beans:bean>
<!-- 컴포넌트 스캔을 통해 컨트롤러 등의 빈을 등록할 패키지 설정 -->
<context:component-scan base-package="com.project.dd" />
<!-- 파일 업로드를 위한 MultipartResolver 빈 설정 -->
<beans:bean id="multipartResolver" class="org.springframework.web.multipart.support.StandardServletMultipartResolver"></beans:bean>
</beans:beans>
security-context.xml 설정 파일은 Spring Security를 설정하는 데 사용되는 파일이다.
security-context.xml 에서 문제가 발생한다면 주로 설정된 패키지의 경로나 파일 경로, 빈 설정에 오타가 있는지 확인해봐야 한다. 또한 필요한 라이브러리들이 모두 프로젝트에 포함되어 있는지도 확인이 필요하다.
Spring Security는 웹 애플리케이션의 보안 기능을 구현하기 위해 사용되는 프레임워크로, 사용자 인증, 권한 부여, 보안 설정 등을 관리하는 역할을 한다.
기능 설명
- Spring MVC 활성화: annotation-driven 요소를 사용하여 Spring MVC의 @Controller 프로그래밍 모델을 활성화한다.
- 정적 리소스 처리 설정: /resources/** 경로의 HTTP GET 요청을 처리하여 웹 애플리케이션의 /resources/ 디렉터리에서 정적 리소스를 제공한다.
- Tiles 뷰 리졸버 설정: Tiles 뷰 리졸버를 설정하고, Tiles 뷰 클래스를 지정하며, 뷰 리졸버의 우선순위를 설정한다.
- TilesConfigurer 설정: Tiles 설정을 위한 TilesConfigurer를 설정하고, Tiles 정의 파일의 위치를 지정한다.
- 컴포넌트 스캔 설정: com.project.dd 패키지를 기준으로 컴포넌트 스캔을 수행하여 컨트롤러 등의 빈을 등록한다.
- 파일 업로드 설정: 파일 업로드를 위한 MultipartResolver 빈을 설정한다.
Spring Security
- 인증(Authentication): 사용자의 신원 확인을 처리하고, 사용자가 누구인지 확인한다. 예를 들어, 사용자의 아이디와 비밀번호를 사용하여 로그인하는 경우가 여기에 해당된다.
- 권한 부여(Authorization): 인증된 사용자에 대한 액세스 권한을 결정한다. 예를 들어, 특정 사용자가 특정 리소스에 대한 읽기 또는 쓰기 권한을 가지고 있는지 여부를 결정한다.
- 보안 설정: 보안 관련 설정을 정의하고 구성한다. 예를 들어, 특정 URL에 대한 보안 규칙을 설정하거나, 사용자가 로그인해야 하는지 여부를 결정할 수 있다.
2023.12.05 - [Programming/Spring] - [Spring] Security: 권한 부여 및 암호화 구현
Spring Security에 대한 자세한 설명은 위 글을 참고한다.
MobaXterm 배포
tomcat의 webapps 폴더 내에 WAR 파일을 위치하게 하면 웹 애플리케이션이 서버에 올라간다.
배포 성공
WAR 파일로 배포한 Spring 프로젝트가 AWS 서버에 올라가 잘 작동하는 것을 볼 수 있다.
회원 로그인
관리자 로그인
회원과 관리자 모두 로그인이 잘 된다.
OpenChat
OpenChat 기능도 AWS와 서버를 연결하여 잘 작동하고 있다.
데이터베이스
데이터베이스에는 PasswordEncoder를 이용하여 암호화된 비밀번호가 업데이트되어 있다.
tomcat.html
WAR 파일이 아닌 html 파일도 ROOT 폴더에 올려서 쉽게 공유할 수 있다.
참고 자료
[Spring] 스프링 부트 배포하기 (War 파일로 빌드), YoonhO, 2022.03.18.
[Spring boot] war로 배포하기, SpringBootServletInitalizer, codeyaki, 2023.07.22.
AWS EC2 Tomcat 설치하고 배포 준비하기, 가영아빠, 2023.04.13.
vi - 문서 편집하기 (입력 , 이동 , 수정 , 삭제 , 명령 취소), 헬로월드!, 2020.04.24.
[AWS] war 파일 배포하기 :: 내가 만든 프로젝트 AWS를 통해 배포하기 6, 캐떠린, 2024.03.03.
[SPRING] 프로젝트 폴더 구조 분석, ReCode.B, 2022.12.21.