로컬에서는 정상적으로 작동하던 Spring Boot 웹 애플리케이션을 EC2에 배포 후 접속을 시도했을 때 아래와 같은 오류가 발생하였다.
org.thymeleaf.exceptions.TemplateInputException: Error resolving template [/user/login], template might not exist or might not be accessible by any of the configured Template Resolvers
오류 메시지를 보면, Thymeleaf 템플릿 엔진이 /user/login
템플릿을 찾지 못하는 것으로 판단된다.
서버 내에서 해당 파일이 존재하는지 확인하기 위해 EC2 내부를 탐색하였고 /home/ubuntu/resources/templates/user
디렉터리에 login.html
파일이 올바르게 존재하는 것을 확인하였다.
로컬에서는 잘 작동하던 것이 서버에서는 동작하지 않는다면 파일 경로의 차이나 설정의 차이가 있을 가능성이 높다고 판단하였다.
Spring Boot 애플리케이션의 경우 내부적으로 classpath:/templates/
경로를 템플릿으로 사용하게 설정되어 있다.
하지만 EC2에 배포된 애플리케이션은 JAR 파일 외부의 resources
디렉터리에 템플릿 파일들이 위치하게 되었기 때문에 참조 경로에 문제가 생긴 것으로 판단하였다.
Spring Boot의 property를 사용해 외부의 resources
디렉터리를 올바르게 참조하도록 설정하였다.
아래와 같이 JAR 파일 실행 시에 Thymeleaf와 static resources의 경로를 직접 지정하여 해결하였다.
java -Dspring.thymeleaf.prefix=file:/home/ubuntu/resources/templates/ -jar trelloServer.jar
배포 과정을 자동화하기 위해 사용하던 GitHub Actions 워크플로우에 위의 해결 방법을 적용하였다.
Deploy to EC2
단계에서 JAR 파일 실행 명령어를 수정하여 외부 resources
디렉터리를 올바르게 참조하도록 설정하였다.
name: Java CI with Gradle
on:
push:
branches: [ "main","develop" ]
pull_request:
branches: [ "main","develop" ]
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build with Gradle
uses: gradle/gradle-build-action@v2.6.0
with:
arguments: build
# 리소스 파일 복사
- name: Copy resources
run: cp -r src/main/resources/* build/resources/
- name: Upload build artifact
uses: actions/upload-artifact@v2
with:
name: trelloServer
path: build/libs/trelloServer-0.0.1-SNAPSHOT.jar
# 리소스 아티팩트로 업로드
- name: Upload resource artifact
uses: actions/upload-artifact@v2
with:
name: resources
path: build/resources/
deploy:
needs: build
runs-on: ubuntu-latest
steps:
- name: Download build artifact
uses: actions/download-artifact@v2
with:
name: trelloServer
path: build/libs/
# 리소스 파일 다운로드
- name: Download resource artifact
uses: actions/download-artifact@v2
with:
name: resources
path: build/resources/
- name: Deploy to EC2
run: |
echo "${{ secrets.EC2_SSH_KEY }}" > private_key.pem
chmod 600 private_key.pem
scp -i private_key.pem -o StrictHostKeyChecking=no build/libs/trelloServer-0.0.1-SNAPSHOT.jar ${{ secrets.EC2_USERNAME }}@${{ secrets.EC2_HOST }}:/home/${{ secrets.EC2_USERNAME }}/trelloServer.jar
# EC2에서 resources 디렉터리 생성
ssh -i private_key.pem -o StrictHostKeyChecking=no ${{ secrets.EC2_USERNAME }}@${{ secrets.EC2_HOST }} "mkdir -p /home/${{ secrets.EC2_USERNAME }}/resources/"
# 리소스 파일 EC2로 복사
scp -i private_key.pem -o StrictHostKeyChecking=no -r build/resources/static ${{ secrets.EC2_USERNAME }}@${{ secrets.EC2_HOST }}:/home/${{ secrets.EC2_USERNAME }}/resources/
scp -i private_key.pem -o StrictHostKeyChecking=no -r build/resources/templates ${{ secrets.EC2_USERNAME }}@${{ secrets.EC2_HOST }}:/home/${{ secrets.EC2_USERNAME }}/resources/
ssh -i private_key.pem -o StrictHostKeyChecking=no ${{ secrets.EC2_USERNAME }}@${{ secrets.EC2_HOST }} "pgrep java | xargs kill -9; nohup java -Dspring.resources.static-locations=file:/home/${{ secrets.EC2_USERNAME }}/resources/static/ -Dspring.thymeleaf.prefix=file:/home/${{ secrets.EC2_USERNAME }}/resources/templates/ -jar /home/${{ secrets.EC2_USERNAME }}/trelloServer.jar > app.log 2>&1 &"
rm -f private_key.pem