개발환경 복구 - SpringBoot 없이 Spring MVC 실행하기

HodaeSsi·2023년 12월 24일
1
post-thumbnail

먼저, 이 포스팅은 업무에서 경험한 사항을 재구성하여 재현한 글입니다.b

첫 회사, 첫 업무.

업데이트 사항이 생겨서 서버 하나를 담당하게 되었는데, 오래되고 관리가 잘되지 않은 서버라서, 잘 겪어보지 못한 상황을 많이 경험하게 되었습니다.

당황도 많이 했지만, 그 과정에서 배울 수 있는 것도 많았고, '뭐가 많이 없다.'라는 건 그만큼, '내가 채울 수 있는 게 많다.'라는 뜻이기도 해서, 재밌었던 것들이 꽤 있어서 이렇게 시리즈물로 게시해 보게 되었습니다.

저처럼 예상치 못한 레거시 프로젝트에 맞닥뜨려 당황하고 계실 다른 개발자분에게 도움이 되었으면 좋겠습니다. 🙏

(정보가 잘 못 되었거나 보충해야 할 부분이 있다면, 가감 없는 의견 남겨주시면 감사하겠습니다!)

이번 포스팅은 아래의 경험들로부터 탄생했습니다.

  • 방치된 개발 환경 : local, dev 개발환경이 없다고...?
  • 오래된 프로젝트 : Spring Boot가 없네? Ant Builder는 또 뭐야;;
  • 자바인데 m...main 문이 없어;;


1. 프로젝트 생성

프로젝트 생성은 IntelliJ의 'New Project' 기능을 통해 간단하게 빈 자바 프로젝트(main문 조차도 없는)를 생성해 주었습니다.

project_init 아무것도... 아무것도 없습니다...

2. 프로젝트 설정

2.1. 프로젝트 구조 설정

"톰캣은 배포된 웹 프로젝트의 /WEB-INF/web.xml 파일을 읽어, 웹 애플리케이션을 파악합니다."
아래와 같이 프로젝트 폴더(WebContent)를 구성해주고, web.xml 파일을 작성하여 줍니다.

project_structure classes, config, 그리고 lib 폴더는 지금은 비어있는 상태입니다.

[web.xml]

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                             http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
</web-app>

<web-app> 태그의 여러 속성들은, XML 스키마 정의(XML Schema Definition, XSD)로써, 해당 xml 파일이 “Java EE의 웹 애플리케이션 스펙(Servlet 스펙)을 준수하고 있음”을 명시하고, 검증하는데 사용되는 속성입니다.

또한 톰캣은, /index.html 파일을 Welcome Page로 사용하므로 html 파일도 추가해줍니다.

위와 같이 구성된 상태에서 톰캣을 통해 웹 앱을 배포해보면,

tomcat_test

루트 패스의 Welcome Page가 정상적으로 서빙되는 것을 확인할 수 있습니다.

(위의 내용들은 톰캣 폴더의, /conf/web.xml 파일에서 확인할 수 있습니다. 더 자세한 내용이 궁금하거나 설정하고 싶으신 게 있다면, /conf/server.xml 파일과 함께 확인해 보세요.)

2.2. 의존성 라이브러리 설치

Maven과 Gradle과 같은 의존성 관리 툴 또한 없는 환경이기 때문에, 스프링 라이브러리도 직접 받아주겠습니다.

Maven Central Repository에서 Jar 파일을 받아, /WEB-INF/lib 폴더에 넣어주시면 됩니다.

maven_repository_install jar 버튼을 눌러, 라이브러리 파일을 받아주면 됩니다.

여기서 가장 큰 문제는, 해당 라이브러리가 의존하는 다른 라이브러리들 또한 직접, 수동으로, 전부 받아주어야 합니다.

spring_web_mvc_dependency_list jar 버튼을 눌러, 라이브러리 파일을 받아주면 됩니다.

Spring Web MVC가 직접 의존하는 라이브러리들뿐만 아니라, 해당 의존성 라이브러리가 의존하는 또 다른 예하의 라이브러리까지도 일일이 받아주어야 하기 때문에, 여간 귀찮지 않을 수가 없습니다. 라이브러리 업데이트하려고 하는 날에는…
(이후 다른 포스팅에서 ‘의존성 관리 툴’을 적용하는 내용을 작성할 예정입니다. 여기서 한 번, Maven과 Gradle에 감사의 합장을🙏)

그래도, 이번 포스팅에서는 그렇게 많은 라이브러리가 필요하지 않기 때문에, Jar 파일을 직접 받아 아래와 같이 구성해 주었습니다.

library_install_list

Spring 프레임워크와 함께, log4j 라이브러리도 설치해주었습니다.
(log4j에 대해서도 직접 설치/설정을 해주지 않으면, 서버 로그가 출력 조차 되지 않습니다. 충격 포인트 2)

3. Spring MVC 설정

위에서 언급한 것처럼, 톰캣은 web.xml 파일을 통해 웹 애플리케이션을 분석하고 구동합니다. web.xml 파일에 Spring 관련 설정을 <web-app> 태그 사이에 추가해 줍니다.

3.1. ContextLoaderListener

<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

가장 먼저, ContextLoaderListener 입니다.

Spring 프레임워크에서 중요한 역할을 하는 클래스들 중 하나로,
Spring의 WebApplicationContext 를 초기화하고 관리하는 리스너입니다.
"서블릿 컨테이너(Tomcat)에 의해 웹 애플리케이션 시작 시에 호출되어 실행"됩니다.


*추가 팁) IntelliJ 프로젝트 의존성 라이브러리 등록

위와 같이 IntelliJ에서 해당 클래스를 찾을 수 없다고 빨간 에러줄이 표시된다면, IntelliJ의 Project Structure 설정 창에서 /WEB-INF/lib 폴더를 ’+’버튼 > Java 를 통해 라이브러리로 등록하고, Modules/Dependencies 탭에서 해당 라이브러리 폴더를 프로젝트의 의존성 라이브러리로 등록해주면 됩니다.

web_xml_error

3.2. WebApplicationContext

ContextLoaderListner 가 초기화하는 WebApplicationContext 에 대한 구성이 필요합니다. WebApplicationContext 에 대한 설정은, DispatcherServlet 설정과는 별개로, 모든 서블릿, 즉 웹 애플리케이션 전반("루트 컨텍스트")에 적용됩니다.

이번 포스팅에서는, 어노테이션을 기반으로 Controller 컴포넌트를 스캔할 수 있도록하는 정도로만, 설정을 진행하겠습니다.

  • [web.xml]

    <context-param>
    		<param-name>contextConfigLocation</param-name>
    		<param-value>/WEB-INF/config/context-root.xml</param-value>
    </context-param>
  • [context-root.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:context="http://www.springframework.org/schema/context"
           xmlns:mvc="http://www.springframework.org/schema/mvc"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
                               http://www.springframework.org/schema/beans/spring-beans.xsd
                               http://www.springframework.org/schema/context
                               http://www.springframework.org/schema/context/spring-context.xsd
                               http://www.springframework.org/schema/mvc
                               http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    
        <!-- 활성화할 컴포넌트 스캔 -->
        <context:component-scan base-package="no.springboot" />
    
        <!-- Spring MVC 설정 -->
        <mvc:annotation-driven />
    </beans>

    (이 포스팅에서는, context-root.xml 파일을 직접 컨텍스트 설정 파일로 명시하였지만, Spring은 /WEB-INF/applicationContext.xml 파일 위치를 루트 컨텍스트 설정 파일의 기본 위치로 지정하고 있기 때문에, 해당 파일에서 설정을 진행하여도 됩니다.)

3.3. DispatcherServlet

[web.xml]

<servlet>
		<servlet-name>spring</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
				<param-name>contextConfigLocation</param-name>
				<param-value>
						/WEB-INF/config/spring-servlet.xml
				</param-value>
		</init-param>
</servlet>
<servlet-mapping>
		<servlet-name>spring</servlet-name>
		<url-pattern>/</url-pattern>
</servlet-mapping>

[spring-servlet.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd">
	<!-- 별도의 설정을 진행하지 않습니다. -->
</beans>

HTTP 요청과 컨트롤러 메서드를 매핑시키고, 요청에 따른 핸들러 실행을 담당하는 DispatcherServlet 입니다.”

Spring 프레임워크의 DispatcherServlet을 사용하는 서블릿을 ‘spring’이라는 이름으로 정의해주고, 해당 서블릿과 매핑될 URL을 지정해 주었습니다.
(/ : 모든 요청이 Spring DispatcherServlet으로 전달 됩니다.)




*참고) “루트 패스 요청(/)에 대해 404 에러가 발생합니다.”

위에서 작성했던 톰캣이 서빙해주던 루트 패스의 웰컴 페이지, index.html 은, 모든 요청이 Spring의 DispatcherServlet 으로 매핑되면서 정상적으로 서빙 되지 않습니다.
DispatcherServlet 설정에, 정적 리소스 서빙 관련 설정이나, 동적 페이지 서빙에 관련된, ‘뷰 리졸버 설정’을 추가하여야 합니다.



3.4. 로거(log4j) 등록

아래의 파일과 내용들을 추가해줍니다.

[web.xml]

<listener>
		<listener-class>org.apache.logging.log4j.web.Log4jServletContextListener</listener-class>
</listener>
<context-param>
		<param-name>log4jConfiguration</param-name>
		<param-value>/WEB-INF/config/log4j2.xml</param-value>
</context-param>

[log4j2.xml]

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
		<!-- 콘솔 출력으로 간단하게 구성하였습니다. -->
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n"/>
        </Console>
    </Appenders>
    <Loggers>
        <Root level="info">
            <AppenderRef ref="Console"/>
        </Root>
    </Loggers>
</Configuration>


4. 컨트롤러 등록 및 빌드

위 설정 들을 통해, 저희의 웹 프로젝트는 ‘어노테이션을 사용한 컴포넌트 스캔’이 가능해졌습니다.

기존의 Spring Boot 에서 하던대로 Controller 메서드를 등록해 봅시다.

먼저, 프로젝트의 src 폴더에 아래의 자바 코드를 추가합니다.

[TestController.java]

package no.springboot;

import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class TestController {

    @GetMapping("/test")
    public ResponseEntity<String> test() {
        return ResponseEntity.ok("Test");
    }
}

Maven 혹은 Gradle을 사용했다면 빌드 환경 또한 자동으로 간편하게 설정해주었을 텐데,

현재 프로젝트는 빌드 관련한 설정도 직접 해주어야 합니다.

다음과 같이 Ant Builder, 빌드 스크립트를 프로젝트에 추가해줍니다.

[build.xml]

<?xml version="1.0" encoding="UTF-8"?>
<project name="build" basedir=".">
    <property name="project.dir.lib" value="./WebContent/WEB-INF/lib"/>
    <property name="project.dir.classes" value="./WebContent/WEB-INF/classes"/>

    <path id="compile.classpath">
        <fileset dir="${project.dir.lib}">
            <include name="*.jar"/>
        </fileset>
    </path>

    <target name="build">
        <javac nowarn="true" debug="true" listfiles="false"
               failonerror="true" optimize="false" includeantruntime="false"
               destdir="${project.dir.classes}" encoding="UTF-8"
               fork="yes">
            <src path="src"/>
            <classpath refid="compile.classpath"/>
        </javac>
    </target>
</project>

소스 파일들을 라이브러리 파일들과 함께 빌드하여, 결과물인 class 파일들을 WEB-INF/classes 폴더에 저장하도록 구성하였습니다.

IntelliJ 의 내장 Ant 빌더와 Ant 플러그인을 사용하여 빌드 스크립트를 실행시켜주면,
/WEB-INF/classes 폴더에 컴파일된 class 파일이 추가된 걸 확인할 수 있습니다.

intellij_build_screen

*IntelliJ에서 Ant 탭이 보이지 않는다면, 먼저 Ant 플러그인이 설치되어 있는지 확인해본 후에(IntelliJ를 설치하면 기본적으로 설치되어 있는 플러그인 입니다.), View → Tool Windows → Ant 를 추가해주시면 됩니다.


5. 톰캣을 통한 배포 및 구동

톰캣을 설치한 다음(https://tomcat.apache.org/), IntelliJ의 Run -> Edit Configurations 화면에서, '+'버튼 -> Tomcat Server -> Local (*TomEE Server가 아닙니다.)을 누르고, Application Server Configure를 통해 설치한 톰캣을 등록해줍니다.

이후, Deployment 탭에서 WebContent/ 폴더(External Source)를 등록하고, Application Context 값을 / 로 변경해 줍니다.

Before launch 탭에 위에서 작성한 빌드 스크립트까지 '+' 버튼 -> Run Ant Target 을 통해 등록해주면, 모든 Local 개발환경 설정이 끝나게 됩니다. (와- 이제 개발을 시작할 수 있어요-)

intellij_tomcat_setting

(여기서의 Application Context 값은, Tomcat 의 설정 값입니다.
이 Context 값을 달리하여 하나의 톰캣에 여러 애플리케이션을 띄울 수 있습니다.
Application Context 값을 /spring 으로 설정하면, Spring 서버로의 요청에 대한 URL path는, /spring/* 이 됩니다.)


마지막으로 IntelliJ를 통해 톰캣 서버를 구동시켜 보면…

test_page

위와 같이 /test 에 등록한 요청 핸들러, Controller 메서드가 정상적으로 동작하는 것을 확인할 수 있습니다.

main문이 없는 자바 프로그램의 구동을 축하드립니다 🥳 (도망쳐!!!)



'서버 개선기' 시리즈 다음 포스팅, (예정)

  • 로그 시스템 개선 및 ELK로 모니터링 시스템 적용하기
  • Maven, Gradle, 의존성 관리 툴 도입하기
  • Docker, k8s 적용으로 운영환경 개선하기
  • 자바 / Spring 버전 업데이트 하기
profile
항상 다같이, 즐겁게 일할 수 있으면 좋겠습니다 😎

2개의 댓글

comment-user-thumbnail
2023년 12월 25일

저도 부트 없는 스프링 프로젝트를 하여서 그런지 어떤 마음일지 공감이 됩니다..

몇 달 전 유명 스프링 개발자 토비님의 영상을 봤는데 본인도 부트 없는 스프링 구성은 어렵다고 하시면서 그렇지만 스프링의 본질적인 구성요소들을 익힐 수 있다고 하셨던 게 기억에 남더라구요

열심히 회사 업무하시며 훌륭한 스프링 개발자가 되시길 바랍니다!

1개의 답글

관련 채용 정보