최근 프로젝트에서 기존처럼 java -jar 방식이 아니라 WAR 파일로 빌드한 뒤 JBoss EAP 환경에 배포하는 작업을 진행하게 되었다.
평소에는 Spring Boot 내장 톰캣 방식만 사용하다 보니 외부 WAS 기반 구조는 처음에 꽤 낯설게 느껴졌다.
특히 “배포 = 파일 복사”라고 단순하게 생각했는데 실제로는 서버 실행 + 감시 구조 + 로그 확인까지 포함된 흐름이라 차이가 컸다.
이번 글에서는 실제 작업 흐름 기준으로 정리했다.
기존 Spring Boot 실행 방식
기존에는 대부분 아래처럼 실행했다.
java -jar app.jar
Spring Boot 내부에 내장 톰캣이 포함되어 있어서 별도의 WAS 없이 바로 실행 가능했다.
하지만 JBoss 환경에서는 구조가 완전히 다르다.
- Spring Boot 애플리케이션
- JBoss WAS
이 두 개가 분리되어 동작하고 WAR 파일을 WAS에 올리는 구조로 바뀐다.
WAR 배포를 위한 설정
Gradle 설정
먼저 WAR 플러그인을 추가
plugins {
id 'war'
}
그리고 내장 톰캣 제외
dependencies {
providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
}
JBoss가 자체적으로 서블릿 컨테이너 역할을 하기 때문에 중복 실행을 막기 위한 설정
Maven 설정
Maven 프로젝트에서는 packaging 타입을 war로 변경
<packaging>war</packaging>
그리고 톰캣 의존성은 provided로 설정
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
SpringBootServletInitializer 설정
WAR 배포를 위해 Application 클래스를 수정했다.
@SpringBootApplication
public class Application extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
설정은 WAR로 배포했을 때 JBoss 같은 외부 WAS가 Spring Boot 애플리케이션을 초기화할 수 있도록 하는 진입점 역할을 한다.
즉, Spring Boot를 단독 실행(java -jar)하는 구조가 아니라 WAS가 애플리케이션을 대신 로딩하는 구조에서는 이 설정이 필요하다.
이 설정이 없을 경우 WAS는 Spring Boot 애플리케이션의 시작 위치를 찾지 못해 정상적인 초기화가 이루어지지 않는다.
WAR 파일 빌드
Gradle:
gradlew clean war
Maven:
mvn clean package
빌드 결과물은 다음 위치에 생성된다.
- Gradle → build/libs
- Maven → target
JBoss 실행
여기서 처음 헷갈렸던 부분이 있었다. “WAR를 먼저 넣고 실행해야 하나?” “아니면 서버부터 켜야 하나?”
정리하면 실제 흐름은 이렇다: JBoss 실행 → deployments 감시 시작 → WAR 자동 배포 즉 서버가 먼저 떠 있어야 한다.
기본 실행
./standalone.sh
이 상태에서는 터미널이 붙어 있기 때문에 SSH 끊기면 같이 종료될 수 있다.
운영 방식
실제 서버에서는 보통 이렇게 실행했다.
nohup ./standalone.sh -b 0.0.0.0 > jboss.log 2>&1 &
여기서 중요한 포인트
- nohup
→ 터미널을 닫아도 프로세스가 죽지 않게 유지 - -b 0.0.0.0
→ 외부 접속 허용 (로컬만이 아니라 서버 IP 접근 가능) - > jboss.log 2>&1
→ 로그를 파일로 저장 (stdout + stderr 같이) - &
→ 백그라운드 실행
이 조합의 의미는 단순 실행이 아니라 **“서버가 완전히 독립 프로세스로 떠 있는 상태”**다.
그래서 SSH 끊어도 JBoss는 계속 살아있었다.
WAR 파일 배포
JBoss가 실행된 상태에서 WAR를 넣는다.
JBOSS_HOME/standalone/deployments
예시:
ROOT.war
내부 동작
WAR를 넣으면 JBoss가 자동으로:
- 파일 감지
- 압축 해제
- context 생성
- 애플리케이션 로딩
을 수행
정상 배포
ROOT.war.deployed
실패
ROOT.war.failed
여기서 .failed 뜨면 거의 무조건 로그 봐야 한다.
로그 확인
배포 문제는 거의 여기서 끝난다.
tail -f standalone/log/server.log
JBoss 환경에서는 애플리케이션 로그와 별개로 WAS 자체 로그(server.log)를 통해 배포 및 초기화 상태를 확인했다.
특히 아래와 같은 문제들은 애플리케이션 로그가 아니라 server.log에서 확인해야 했다.
- WAR 배포 실패 (.failed 발생)
- Spring 컨텍스트 초기화 오류
- Bean 생성 실패
- DB DataSource 연결 문제
반면 실제 비즈니스 로직 에러나 SQL 오류는 기존처럼 설정된 애플리케이션 로그에서 확인했다.
접속 확인
배포가 정상적으로 되면 브라우저에서 확인한다.
http://localhost:8080
정리
이번 작업은 단순히 WAR를 만들어 배포하는 과정이라기보다 Spring Boot가 어디에서 실행되느냐에 따라 구조가 어떻게 달라지는지를 체감하는 과정이었다.
기존에는 java -jar로 실행하면 끝나는 구조였지만 JBoss 환경에서는 서버가 먼저 실행되고 그 위에서 애플리케이션이 올라가는 구조였다.
이 차이로 인해 개발 관점이 아니라 서버가 애플리케이션을 관리하는 구조로 바뀐 느낌이 있었다.
처음에는 WAR 빌드, standalone 실행, deployments 구조가 낯설었지만 하나씩 따라가면서 결국 동작 흐름 자체는 단순했다.
그리고 이 과정을 통해 그동안 익숙하게 사용하던 Spring Boot 실행 방식이 서버 환경에 따라 전혀 다른 의미를 가진다는 점을 이해하게 되었다.
결국 중요한 건 배포 자체가 아니라 어디에서 애플리케이션이 실행되고 있느냐였다 그 차이를 이해하니 전체 구조가 한 번에 정리됐다.
'Backend > Spring' 카테고리의 다른 글
| [Spring] Filter 정리 (0) | 2025.12.19 |
|---|---|
| 스프링 AOP(Spring AOP) 제대로 이해하기 (0) | 2025.12.18 |