주요글: 도커 시작하기
반응형

Confluence Wiki를 설치해 본 사람은 설치 과정이 참 편하다는 것을 알 수 있다. 홈디렉토리 경로만 지정해주면 주요 설정은 웹 어플리케이션을 처음 구동할 때 설정하게 된다. 예를 들어, JDBC 연결 정보를 웹 브라우저를 이용해서 입력하고, DB를 생성하는 작업을 설치 마법사와 같은 방식으로 진행하게 된다.


많은 웹 어플리케이션들이 스프링을 이용해서 기능을 구현하는 경우가 많은데, 스프링 기반의 웹 어플리케이션을 사용하는 경우 어떻게 설치 위자드를 제공할 수 있을까? 요즘 이런 고민을 해 봤는데 답은 아주 간단하다. 스프링의 리로드 기능을 사용하는 것이다.


예를 들기 위해 다음과 같은 아주 간단한 웹 어플리케이션을 하나 만들어 보자.

  • WAR 파일로 배포되는 웹 어플리케이션
  • 최초 실행 시 설정 과정
    • 설치 과정1: 설치 안내
    • 설치 과정2: DB 정보 입력
    • 설치 과정3: 설정 완료
      • 지정 디렉토리에 설정 파일 저장
    • 설정 완료 후, 서비스 제공
  • 설정 완료 후, 이후 WAS를 재시작할 경우 설정 없이 서비스 제공
    • 설정 여부는 설정 파일 존재 여부로 확인
간단함을 위해 다음과 같이 코드를 구성했다.
  • 설정 과정 위한 파일
    • SetupController 클래스: 설정 과정을 처리하는 컨트롤러
    • ContextReloader 클래스: 설정 완료된 경우 또는 이미 설정된 경우 서비스 제공하도록 스프링 컨테이너 리로딩
    • setup-config.xml: 설정 과정에서 사용되는 스프링 설정 파일
  • 실 서비스 위한 파일
    • HomeController: 서비스의 첫 화면을 위한 컨트롤러
    • application-config.xml: 실제 서비스 용 스프링 설정 파일
web.xml 설정은 설정 위한 설정으로

web.xml 파일은 설정 과정을 위한 정보를 담는다.

<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:/setup-config.xml
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>

setup-config.xml 파일은 다음과 같이 사용자의 설정 과정을 처리하기 위한 SetupController와 설정이 완료되면 스프링 컨테이너를 리로딩해주는 ContextReloader로 구성된다.


<?xml version="1.0" encoding="UTF-8"?>

<beans ...>

<bean id="contextReloader" class="org.chimi.rr4s.setup.ContextReloader">

</bean>


<bean class="org.chimi.rr4s.setup.SetupController">

<property name="contextReloader" ref="contextReloader" />

</bean>

         .... 스프링 MVC 관련 나머지 설정 들

</beans>


SetupController는 설정 과정을 처리하고 설정이 완료되면 설정 정보를 저장한 뒤에 ContextReloader를 이용해서 컨테이너를 리로딩한다. 아래는 SetupController의 예시 코드이다.


public class SetupController {


private ContextReloader contextReloader;


@RequestMapping("/home")

public String home() {

                // 설정 과정에서의 첫 화면은 설정 관련 안내 페이지

return "setup/home";

}


@RequestMapping("/setup/step1")

public String step1() {

                // 설정 폼을 보여주기 위한 처리

return "setup/step1";

}


@RequestMapping("/setup/save")

public String step2(...) {

saveConfig(); // 설정 저장

contextReloader.reloadContext(); // 리로딩

return "setup/completed";

}


private void saveConfig() {

FileOutputStream fos = new FileOutputStream(new File("./target/conf.properties"));

// 설정 파일을 지정한 경로에 저장

}


public void setContextReloader(ContextReloader contextReolader) {

this.contextReloader = contextReolader;

}

}


위 코드에서 핵심은 설정을 입력하면 설정 정보를 지정한 위치의 파일에 저장하고, ContextReloader.reloadContext()를 호출한다는 점이다. (실제로는 필요한 DB를 생성하는 과정 등이 포함되겠지만 여기서는 설명에 필요한 것만 보여주었다.)


ContextReloader는 다음과 같다.


public class ContextReloader implements ApplicationContextAware,

ApplicationListener<ContextRefreshedEvent> {


private ApplicationContext applicationContext;


public void reloadContext() {

if (applicationContext instanceof ConfigurableWebApplicationContext) {

ConfigurableWebApplicationContext configurableCtx = 

(ConfigurableWebApplicationContext) applicationContext;

String[] configLocations = { "classpath:/application-config.xml" };

configurableCtx.setConfigLocations(configLocations);

configurableCtx.refresh();

}

}


@Override

public void onApplicationEvent(ContextRefreshedEvent event) {

// 설정 확인

if (new File("./target/conf.properties").exists()) {

// 설정 존재하면 reloadContext();

reloadContext();

}

}


@Override

public void setApplicationContext(ApplicationContext context)

throws BeansException {

this.applicationContext = context;

}


}


ContextReloader는 스프링 컨테이너를 리로딩해야 하기 때문에 ApplicationContextAware를 implement해서 ApplicationContext를 전달받을 수 있도록 했다. 또한, 컨테이너가 초기화가 완료되는 이벤트를 받을 수 있도록 ApplicationListener를 implement하였다. 이 이벤트를 받는 이유는 뒤에서 다시 설명한다.


reloadContext() 메서드는 현재 스프링 컨테이너가 사용할 설정 파일을 application-config.xml로 변경한 뒤에 refresh()를 수행한다. 즉, reloadContext()를 호출하면 application-config.xml에 설정된 내용을 이용해서 스프링 컨테이너가 초기화된다. 따라서, 앞서 SetupController 클래스에서 설정을 완료한 뒤에 reloadContext()를 호출하게 되면, 실제 서비스를 위한 스프링 설정이 사용되는 것이다.


onApplicationEvent() 메서드는 ContextRefreshedEvent를 처리하는데, 이 이벤트를 처리하는 이유는 최초에 한 번만 설정 과정을 거치고 이후에는 거치지 않기 위함이다. 예를 들어, WAS를 재구동해야 하는 경우 기존에 이미 설정을 완료했다면 설정 화면이 아닌 서비스 화면이 나와야 할 것이다. 그런데, web.xml 파일은 설정 과정을 위한 setup-config.xml 파일을 사용하므로, 다시 설정 화면이 나올 것이다. 이를 방지하기 위해 컨테이너 초기화가 완료될 때 기존 설정 정보가 존재하는 지 확인하는 과정을 구현할 필요가 있는데, 컨테이너 초기화 시에 발생하는 이벤트가 ContextRefreshedEvent 이다. 따라서, ContextRefreshedEvent 이벤트를 수신하는 onApplicationEvent() 메서드에서 설정 파일이 존재하는 지 확인하고, 만약 존재한다면 서비스용 스프링 설정 파일로 컨테이너를 리로딩함으로써 서비스를 제공할 수 있게 된다.


+ Recent posts