일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- JAR
- gradle
- ubuntu
- mariadb
- Change port
- Jenkins
- local
- ^M바꾸기
- change file content
- remove
- driverspy
- install
- 전송포맷
- object
- 줄복사
- key bindings
- JavaScript
- jdbc
- Java
- ADB
- docker
- javaascript
- maven
- not to accept jdbcUrl
- pkgutil
- svn backup
- spring
- duplicate lines
- install maven
- spring boot
- Today
- Total
Simplify
Spring Boot WAR 로 배포하기 (How to deploy with *.war file with Spring Boot) 본문
Spring Boot WAR 로 배포하기 (How to deploy with *.war file with Spring Boot)
Simplify - Jonghun 2019. 6. 17. 10:26Spiring Boot 는 기본적으로 jar 배포형태를 가지고 있습니다. 그 말은 그 자체로서 java 실행 프로그램이다 라는 의미로 해석될 수 있다고 생각합니다. tomcat 같은 웹서버도, db 도 다 내장으로 가질 수 있는 형태이기 때문에, '독립적인 프로그램이다' 라는 의미를 가져간 것이라고 보입니다. 잘 알려진 것 처럼 war 는 웹 프로젝트, 즉 tomcat 과 같은 웹 서버 위에서 돌아가는 프로젝트라고 보시면 될 것 같습니다.
지금까지 프로젝트, 운영을 해오던 환경에서는, 혹은 웹 프로젝트인데 기존 환경에 익숙한 경우에, 그 구조를 변경하는 것을 꺼려하는 것이 일반적입니다. (명령어 한줄 조차도 말이죠) 그렇기 때문에 기존 프로젝트에서 신규 프로젝트로 변경하려면 war로 변경/배포해야 하는 경우가 생깁니다. (제 경우에는 docker와의 조합에서 유리함을 가져가려고 그렇게 했습니다) 여기서는 기존 jar를 war로 변경 해 보고, 이때 발생한 문제 해결했던 것 까지 공유합니다. (여기서는 기존 방식대로 Maven 을 사용하는 경우 기준으로 설명합니다. Gradle이어도 비슷하리라 추측됩니다)
다음의 세 가지 과정으로 진행됩니다.
- SpringBootServletInitializer 상속받는 것으로 변경
- Embed servlet 을 'provided' 로 변경
- war로 빌드
SpringBootServletInitializer 상속받는 것으로 변경
Spring Boot 프로젝트를 생성하고 나면, main method를 가진 하나의 클래스가 자동 생성됩니다. 아래와 같은 @SpringBootApplication annotation을 갖는 클래스가 그것인데, 이 안에서는 SpringApplication 클래스의 run 함수를 호출하게 되어 있습니다.
package com.simplify.sample;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
@SpringBootApplication
public class SpringBootSampleApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootSampleApplication.class, args);
}
}
이제 코드를 수정하여 다음과 같이 만들어 둡니다.
package com.simplify.sample;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
@SpringBootApplication
public class SpringBootSampleApplication extends SpringBootServletInitializer{
public static void main(String[] args) {
SpringApplication.run(SpringBootSampleApplication.class, args);
}
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(SpringBootSampleApplication.class);
}
}
달라진 것은 SpringBootServletInitializer 를 상속받고, configure 함수를 override합니다.
Embed servlet 을 'provided' 로 변경
이제 pom.xml 을 열어 아래 소스를 추가합니다.
<!-- marked the embedded servlet container as provided -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
제가 보기에, 이 tomcat 은 기본적으로 추가되어 있으나(임의로 pom.xml 에 추가하지 않아도), scope 태그를 바로잡기 위해서 적어주는 것으로 보입니다.
war로 빌드
그리고 아래 처럼 packaging 태그를 war로 수정합니다. (안적혀 있다면 추가합니다)
<packaging>war</packaging>
여기까지 진행하고 나서 Maven build ... 을 눌러서 clean package 로 빌드하면 정상적으로 빌드됩니다. 그런데, 앞선 포스트에서 처럼 저는 jdbc 에 추가적으로 log4jdbc 를 추가하여 조금 더 보기좋게 log 부분을 수정한 내용이 있습니다. 여기부터 문제가 발생합니다.
처음에 구동하면 db에 접속하지 않는 일반적인 사항은 잘 동작하지만, db에서 데이터를 가져오는 등의 작업을 하는 것들은 다음과 같은 에러가 발생합니다. (검색 용이성 때문에 로그 내용을 거의 다 첨부합니다)
tomcat | 06-17 02:08:12.503 INFO [http-nio-8080-exec-3] com.zaxxer.hikari.HikariDataSource | HikariPool-1 - Starting...
tomcat | 06-17 02:08:12.506 ERROR [http-nio-8080-exec-3] o.s.b.w.servlet.support.ErrorPageFilter | Forwarding to error page from request [/openapi/query] due to exception [nested exception is org.apache.ibatis.exceptions.PersistenceException:
tomcat | ### Error querying database. Cause: java.lang.RuntimeException: Driver net.sf.log4jdbc.sql.jdbcapi.DriverSpy claims to not accept jdbcUrl, jdbc:log4jdbc:mariadb://jonghiphop.asuscomm.com:63306/TestDB
tomcat | ### The error may exist in file [/usr/local/tomcat/webapps/SpringBootSampleWeb/WEB-INF/classes/mybatis/mapper/TestDB.xml]
tomcat | ### The error may involve com.simplify.sample.db.mapper.TestMapper.getAll
tomcat | ### The error occurred while executing a query
tomcat | ### Cause: java.lang.RuntimeException: Driver net.sf.log4jdbc.sql.jdbcapi.DriverSpy claims to not accept jdbcUrl, jdbc:log4jdbc:mariadb://jonghiphop.asuscomm.com:63306/TestDB]
tomcat | org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.PersistenceException:
tomcat | ### Error querying database. Cause: java.lang.RuntimeException: Driver net.sf.log4jdbc.sql.jdbcapi.DriverSpy claims to not accept jdbcUrl, jdbc:log4jdbc:mariadb://jonghiphop.asuscomm.com:63306/TestDB
tomcat | ### The error may exist in file [/usr/local/tomcat/webapps/SpringBootSampleWeb/WEB-INF/classes/mybatis/mapper/TestDB.xml]
tomcat | ### The error may involve com.simplify.sample.db.mapper.TestMapper.getAll
tomcat | ### The error occurred while executing a query
tomcat | ### Cause: java.lang.RuntimeException: Driver net.sf.log4jdbc.sql.jdbcapi.DriverSpy claims to not accept jdbcUrl, jdbc:log4jdbc:mariadb://jonghiphop.asuscomm.com:63306/TestDB
tomcat | at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:77)
tomcat | at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:446)
tomcat | at com.sun.proxy.$Proxy66.selectList(Unknown Source)
tomcat | at org.mybatis.spring.SqlSessionTemplate.selectList(SqlSessionTemplate.java:230)
tomcat | at org.apache.ibatis.binding.MapperMethod.executeForMany(MapperMethod.java:137)
tomcat | at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:75)
tomcat | at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:59)
tomcat | at com.sun.proxy.$Proxy67.getAll(Unknown Source)
tomcat | at com.simplify.sample.db.service.TestService.getAll(TestService.java:18)
tomcat | at com.simplify.sample.db.controller.TestController.query(TestController.java:21)
tomcat | at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
tomcat | at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
tomcat | at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
tomcat | at java.lang.reflect.Method.invoke(Method.java:498)
tomcat | at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:209)
tomcat | at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:136)
tomcat | at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102)
tomcat | at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:891)
tomcat | at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:797)
tomcat | at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
tomcat | at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:991)
tomcat | at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:925)
tomcat | at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:974)
tomcat | at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:866)
tomcat | at javax.servlet.http.HttpServlet.service(HttpServlet.java:634)
tomcat | at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:851)
tomcat | at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
tomcat | at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
tomcat | at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
tomcat | at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
tomcat | at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
tomcat | at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
tomcat | at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:209)
tomcat | at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178)
tomcat | at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:357)
tomcat | at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:270)
tomcat | at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
tomcat | at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
tomcat | at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
tomcat | at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
tomcat | at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
tomcat | at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
tomcat | at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:109)
tomcat | at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
tomcat | at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
tomcat | at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
tomcat | at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93)
tomcat | at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
tomcat | at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
tomcat | at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
tomcat | at org.springframework.boot.web.servlet.support.ErrorPageFilter.doFilter(ErrorPageFilter.java:130)
tomcat | at org.springframework.boot.web.servlet.support.ErrorPageFilter.access$000(ErrorPageFilter.java:66)
tomcat | at org.springframework.boot.web.servlet.support.ErrorPageFilter$1.doFilterInternal(ErrorPageFilter.java:105)
tomcat | at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
tomcat | at org.springframework.boot.web.servlet.support.ErrorPageFilter.doFilter(ErrorPageFilter.java:123)
tomcat | at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
tomcat | at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
tomcat | at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)
tomcat | at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
tomcat | at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
tomcat | at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
tomcat | at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199)
tomcat | at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
tomcat | at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:493)
tomcat | at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:137)
tomcat | at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)
tomcat | at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:660)
tomcat | at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
tomcat | at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
tomcat | at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:798)
tomcat | at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
tomcat | at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:808)
tomcat | at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1498)
tomcat | at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
tomcat | at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
tomcat | at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
tomcat | at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
tomcat | at java.lang.Thread.run(Thread.java:748)
tomcat | Caused by: org.apache.ibatis.exceptions.PersistenceException:
tomcat | ### Error querying database. Cause: java.lang.RuntimeException: Driver net.sf.log4jdbc.sql.jdbcapi.DriverSpy claims to not accept jdbcUrl, jdbc:log4jdbc:mariadb://jonghiphop.asuscomm.com:63306/TestDB
tomcat | ### The error may exist in file [/usr/local/tomcat/webapps/SpringBootSampleWeb/WEB-INF/classes/mybatis/mapper/TestDB.xml]
tomcat | ### The error may involve com.simplify.sample.db.mapper.TestMapper.getAll
tomcat | ### The error occurred while executing a query
tomcat | ### Cause: java.lang.RuntimeException: Driver net.sf.log4jdbc.sql.jdbcapi.DriverSpy claims to not accept jdbcUrl, jdbc:log4jdbc:mariadb://jonghiphop.asuscomm.com:63306/TestDB
tomcat | at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30)
tomcat | at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:150)
tomcat | at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:141)
tomcat | at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
tomcat | at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
tomcat | at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
tomcat | at java.lang.reflect.Method.invoke(Method.java:498)
tomcat | at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:433)
tomcat | ... 76 common frames omitted
tomcat | Caused by: java.lang.RuntimeException: Driver net.sf.log4jdbc.sql.jdbcapi.DriverSpy claims to not accept jdbcUrl, jdbc:log4jdbc:mariadb://jonghiphop.asuscomm.com:63306/TestDB
tomcat | at com.zaxxer.hikari.util.DriverDataSource.<init>(DriverDataSource.java:106)
tomcat | at com.zaxxer.hikari.pool.PoolBase.initializeDataSource(PoolBase.java:332)
tomcat | at com.zaxxer.hikari.pool.PoolBase.<init>(PoolBase.java:107)
tomcat | at com.zaxxer.hikari.pool.HikariPool.<init>(HikariPool.java:108)
tomcat | at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:112)
tomcat | at org.springframework.jdbc.datasource.DataSourceUtils.fetchConnection(DataSourceUtils.java:151)
tomcat | at org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection(DataSourceUtils.java:115)
tomcat | at org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:78)
tomcat | at org.mybatis.spring.transaction.SpringManagedTransaction.openConnection(SpringManagedTransaction.java:82)
tomcat | at org.mybatis.spring.transaction.SpringManagedTransaction.getConnection(SpringManagedTransaction.java:68)
tomcat | at org.apache.ibatis.executor.BaseExecutor.getConnection(BaseExecutor.java:336)
tomcat | at org.apache.ibatis.executor.SimpleExecutor.prepareStatement(SimpleExecutor.java:84)
tomcat | at org.apache.ibatis.executor.SimpleExecutor.doQuery(SimpleExecutor.java:62)
tomcat | at org.apache.ibatis.executor.BaseExecutor.queryFromDatabase(BaseExecutor.java:324)
tomcat | at org.apache.ibatis.executor.BaseExecutor.query(BaseExecutor.java:156)
tomcat | at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:109)
tomcat | at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:83)
tomcat | at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:148)
tomcat | ... 82 common frames omitted
위에 보면 DriverSpy 클래스가 claims not to accept jdbcUrl 이라고 되어 있습니다.url이 달라졌나 소스를 보니 Driver를 로드하게 되는데 내장 톰캣에는 내장된 Driver가 아무래도 일반 tomcat 8 에는 없는 것 같습니다.
결국 어떤 방법을 써봐도 안되서, 다음과 같이 해결하였습니다.
#spring.datasource.driverClassName=org.mariadb.jdbc.Driver
#spring.datasource.url=jdbc:mariadb://jonghiphop.asuscomm.com:63306/TestDB
spring.datasource.driverClassName=net.sf.log4jdbc.sql.jdbcapi.DriverSpy
spring.datasource.url=jdbc:log4jdbc:mysql://jonghiphop.asuscomm.com:63306/TestDB
우선, 위와 같이 jdbc:log4jdbc:mariadb 부분을 jdbc:log4jdbc:mysql 로 변경합니다. 이는 다음과 같이 mysql driver를 강제 로드해서 사용하기 위함입니다. (mysql과 mariadb 는 유사한 형태를 가지고 있습니다. mariadb가 mysql 기반이라는..?)
그리고 pom.xml 에 가서 다음과 같이 driver를 추가합니다.
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
이렇게 하고 나면 정상적으로 접속되고 로그도 아래처럼 잘 나옵니다.
tomcat | 06-17 02:22:32.178 INFO [http-nio-8080-exec-1] com.zaxxer.hikari.HikariDataSource | HikariPool-1 - Starting...
tomcat | 06-17 02:22:32.680 INFO [http-nio-8080-exec-1] com.zaxxer.hikari.HikariDataSource | HikariPool-1 - Start completed.
tomcat | 06-17 02:22:32.693 DEBUG [http-nio-8080-exec-1] c.s.sample.db.mapper.TestMapper.getAll | ==> Preparing: SELECT * FROM Test
tomcat | 06-17 02:22:32.737 DEBUG [http-nio-8080-exec-1] c.s.sample.db.mapper.TestMapper.getAll | ==> Parameters:
tomcat | 06-17 02:22:32.740 DEBUG [http-nio-8080-exec-1] jdbc.sqltiming | com.zaxxer.hikari.pool.ProxyPreparedStatement.execute(ProxyPreparedStatement.java:44)
tomcat | 1. SELECT * FROM Test
tomcat | {executed in 1 msec}
tomcat | 06-17 02:22:32.783 INFO [http-nio-8080-exec-1] jdbc.resultsettable |
tomcat | |---|------|
tomcat | |id |name |
tomcat | |---|------|
tomcat | |1 |Test1 |
tomcat | |2 |Test2 |
tomcat | |3 |Test3 |
tomcat | |---|------|
tomcat |
tomcat | 06-17 02:22:32.784 DEBUG [http-nio-8080-exec-1] c.s.sample.db.mapper.TestMapper.getAll | <== Total: 3
참고 : https://www.mkyong.com/spring-boot/spring-boot-deploy-war-file-to-tomcat/