스프링과 스프링부트 비교-프로젝트 구조와 설정 방식

스프링과 스프링부트는 프로젝트 구조, 설정 방식, 서버 실행 방법, 패키징 형태 등에서 명확한 차이점을 가지며, 각각의 특성을 이해하면 프로젝트 요구사항에 맞는 적절한 프레임워크를 선택할 수 있다.



프로젝트 생성과 구조 차이

스프링과 스프링부트는 프로젝트 생성 방법부터 기본 디렉토리 구조까지 여러 차이점을 가진다.



스프링 프레임워크 프로젝트

스프링 프레임워크는 별도의 프로젝트 생성 사이트를 제공하지 않는다.

  • IDE(이클립스[STS3, eGov], 인텔리J 등)를 사용하거나 직접 디렉토리 구조 생성
  • 디렉토리 구조에서 WEB-INF 사용이 특징
    • src/main/webapp/WEB-INF/jsp 구조로 JSP 파일 관리
  • 템플릿 엔진(View)에서 JSP를 주로 사용
  • 외부 톰캣에 배포하는 WAR 패키징 형태가 일반적
스프링 프로젝트 기본 구조:
src/
├── main/
│   ├── java/         # 자바 소스 코드
│   ├── resources/    # 설정 파일(context-*.xml 등)
│   └── webapp/       # 웹 리소스
│       └── WEB-INF/  # 웹 설정 및 JSP 파일
│           ├── jsp/  # JSP 파일 위치
│           └── web.xml # 웹 애플리케이션 설정

스프링 프레임워크는 web.xml을 통한 설정과 WEB-INF 디렉토리 구조를 사용하는 전통적인 Java EE 웹 애플리케이션 구조를 따른다.



스프링부트 프로젝트

스프링부트는 프로젝트 생성을 위한 다양한 방법을 제공한다.

  • start.spring.io 웹사이트나 IDE를 통해 쉽게 프로젝트 생성 가능
  • 디렉토리 구조에서 webapp/WEB-INF 없이 java, resources 사용이 기본
  • 템플릿 엔진으로 타임리프(Thymeleaf)를 공식 권장
    • 외부 라이브러리(임베디드-톰캣-JSP 라이브러리) 추가 시 JSP도 사용 가능
  • JAR 패키징이 기본이며 내장 톰캣으로 바로 실행 가능
    • WAR 패키징도 지원하며 내부 톰캣으로 IDE에서 바로 실행 가능
스프링부트 프로젝트 기본 구조:
src/
├── main/
│   ├── java/          # 자바 소스 코드
│   │   └── Application.java  # 메인 클래스
│   └── resources/     # 설정 파일 및 리소스
│       ├── static/    # 정적 리소스(CSS, JS 등)
│       ├── templates/ # 타임리프 템플릿 파일
│       └── application.yml  # 애플리케이션 설정

💡 스프링부트는 JAR 파일로 패키징되어 JVM 위에서 바로 실행할 수 있는 반면, 전통적인 WAR는 별도의 WAS(톰캣)을 구동한 후 배포해야 한다.



설정 방식의 차이

스프링과 스프링부트는 애플리케이션 설정 방식에서 큰 차이를 보인다.



스프링 프레임워크 설정

Image

스프링 프레임워크는 XML 기반 설정이 전통적이다.

  • XML로 초기 설정을 직접 해야 하며, 대부분의 설정을 XML로 진행
    • Java Config 방식도 사용 가능하지만 XML이 더 일반적
  • 설정은 크게 세 가지 계층으로 구분
    1. web.xml(그림X): 최상위 설정 파일로 톰캣이 항상 체크
    2. WEB-INF 하위 XML(그림 오른쪽-Child): 컨트롤러 및 웹 관련 빈 관리
    3. resources 하위 XML(그림 왼쪽-Root): 서비스, 리포지토리 및 공통 빈 관리
예시 코드 XML 설정 3개


webapp/WEB-INF/web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="3.1" 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_3_1.xsd">
	<display-name>Lab301-mvc</display-name>
	<filter>
		<filter-name>encodingFilter</filter-name>
		<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
		<init-param>
			<param-name>encoding</param-name>
			<param-value>utf-8</param-value>
		</init-param>
	</filter>
<!--  -->
	<filter-mapping>
		<filter-name>encodingFilter</filter-name>
		<url-pattern>*.do</url-pattern>
	</filter-mapping>
<!--  -->
	<!-- Spring  context configuration -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath*:spring/context-*.xml</param-value>
	</context-param>
<!--  -->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
<!--  -->
	<!-- Spring WEB context configuration -->
	<servlet>
		<servlet-name>mvcAction</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>/WEB-INF/config/springmvc/context-*.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>
<!--  -->
	<servlet-mapping>
		<servlet-name>mvcAction</servlet-name>
		<url-pattern>*.do</url-pattern>
	</servlet-mapping>
<!--  -->
	<welcome-file-list>
		<welcome-file>index.jsp</welcome-file>
	</welcome-file-list>
	<login-config>
		<auth-method>BASIC</auth-method>
	</login-config>
</web-app>

webapp/WEB-INF/config/context-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"
	xmlns:p="http://www.springframework.org/schema/p"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
				http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<!--  -->
	<!-- set component scan -> include:Controller -->
	<context:component-scan base-package="com.easycompany">
		<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
		<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
		<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
	</context:component-scan>
<!-- -->	
	<mvc:annotation-driven/>
	<!-- 모든 핸들러매핑에 인터셉터 등록하는 부트와 유사한 방식! 
	인터셉터가 적용될 URL 매핑과 exclude로 제외할 URL을 지정할 수 있다. -->
	<mvc:interceptors>
		<mvc:interceptor>
			<mvc:mapping path="/*Employee.do" />
			<mvc:mapping path="/employeeList.do" />
			<bean class="com.easycompany.cmm.interceptor.AuthenticInterceptor" />
		</mvc:interceptor>
	</mvc:interceptors>
<!--  -->	
	<!-- set view resolver -->
	<!-- TODO [Step 1-1-1] ViewResolver - View를 처리할 해결사를 설정하자 (이거하면 /WEB-INF/jsp/ 접근가능) -->
	<bean  class="org.springframework.web.servlet.view.InternalResourceViewResolver"
		p:prefix="/WEB-INF/jsp/" p:suffix=".jsp" /> 
    <!-- 컨트롤러 메소드 필요없이 직접 매핑! login.jsp와 validator.jsp로 매핑
	validator.jsp는 JavaScript 유효성 검사 코드를 생성하는 역할을 합니다. -->
	<mvc:view-controller path="/login.do"/>
	<mvc:view-controller path="/validator.do"/>
<!--  -->
	<!-- set message source -->
	<!-- TODO [Step 1-2-1] SpringMessage - messageSource 활성화 설정 -->
	<!-- messageSource 활성화하는 부분 -->
	<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
		<property name="basenames">
			<list>
				<value>messages.message-common</value>
			</list>
		</property>
	</bean>
<!--  -->
	<!-- setting Locale -->
	<!-- setting Locale Locale Interceptor 설정하기  -->   
	<!-- TODO [Step 1-3-1] Internalization - 국제화 관련 bean 설정  -->
	<!-- *HandlerMapping 설정방법 참고 -->
	<bean id="localeChangeInterceptor" class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"
		p:paramName="lang" />
	<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver" />
	<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
		<property name="interceptors">
			<list>
				<ref bean="localeChangeInterceptor"/>
			</list>
		</property>
	</bean>
	<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter" />
</beans>

src/main/resources/spring/context-common.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"
		xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
				http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<!--  -->	
    <!-- set component scan -> include: Service, Repository -->
	<context:component-scan base-package="com.easycompany">
		<context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
		<context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
		<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
	</context:component-scan>
<!--  -->
	<bean id="leaveaTrace" class="org.egovframe.rte.fdl.cmmn.trace.LeaveaTrace">
		<property name="traceHandlerServices">
			<list>
				<ref bean="traceHandlerService" />
			</list>
		</property>
	</bean>
	<bean id="traceHandlerService" class="org.egovframe.rte.fdl.cmmn.trace.manager.DefaultTraceHandleManager">
		<property name="reqExpMatcher">
			<ref bean="antPathMater" />
		</property>
		<property name="patterns">
			<list>
				<value>*</value>
			</list>
		</property>
		<property name="handlers">
			<list>
				<ref bean="defaultTraceHandler" />
			</list>
		</property>
	</bean>
	<bean id="antPathMater" class="org.springframework.util.AntPathMatcher" />
	<bean id="defaultTraceHandler" class="org.egovframe.rte.fdl.cmmn.trace.handler.DefaultTraceHandler" />
<!-- 	 -->
	<!-- For Pagination Tag -->
	<bean id="imageRenderer" class="com.easycompany.cmm.tag.ImagePaginationRenderer"/>
	<bean id="paginationManager" class="org.egovframe.rte.ptl.mvc.tags.ui.pagination.DefaultPaginationManager">
		<property name="rendererType">
			<map>
				<entry key="image" value-ref="imageRenderer"/> 
			</map>
		</property>
	</bean>
</beans>

스프링 프레임워크에서는 web.xml과 컨텍스트 XML 파일 덕분에 main 함수 없이도 톰캣 위에서 정상 실행이 가능하다.



스프링부트 설정

스프링부트는 Java Config와 속성 파일 기반 설정을 권장한다.

  • XML보다 Java Config & properties(yml) 설정을 권장
  • 많은 MVC 설정들이 자동으로 구성됨(Auto Configuration)
  • Java로 설정하기 때문에 main 함수가 필수
  • 라이브러리 추가만으로 많은 설정이 자동화됨
    • 예: spring-boot-starter-web 추가 시 내장 톰캣, MVC 설정 등 자동 구성
// 스프링부트 메인 클래스 예시
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
# application.yml 예시
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mydb
    username: user
    password: pass
  jpa:
    hibernate:
      ddl-auto: update

스프링부트는 ‘Convention over Configuration’(설정보다 관례) 원칙을 따라 최소한의 설정으로 애플리케이션을 실행할 수 있게 해준다.



서버 실행 방식 차이

스프링과 스프링부트는 서버 실행 방식에서도 큰 차이를 보인다.



스프링 프레임워크 서버 실행

  • “내장 서버”가 없어 외부 톰캣에 배포해야 함
  • 톰캣은 웹앱 구동 시 해당 웹앱의 /WEB-INF/web.xml을 기본적으로 참고 (표준 서블릿 스펙)
  • main 함수 없이도 잘 구동됨 (web.xml 덕분)
  • WAR 패키징을 주로 사용 (외부 톰캣에 배포하기 위함)
스프링 프레임워크 실행 과정:
1. 외부 톰캣 서버 시작
2. WAR 파일 배포
3. 톰캣이 web.xml 파일 로드
4. ContextLoaderListener가 애플리케이션 컨텍스트 초기화
5. DispatcherServlet 초기화
6. 웹 애플리케이션 실행



스프링부트 서버 실행

  • “내장 서버(톰캣)”이 있어 별도 설치 없이 바로 실행 가능
  • JAR로 패키징하여 JVM 위에서 바로 서버 실행
  • main 함수를 통해 애플리케이션 실행
  • spring-boot-starter-web(“내장톰캣 있음”) 라이브러리가 없으면 정상 실행은 하지만 바로 종료됨
    • 이 경우 ApplicationRunner 구현체로 자바 코드 실행이 일반적
스프링부트 실행 과정:
1. main 메서드 실행
2. SpringApplication.run() 호출
3. 내장 톰캣 서버 자동 시작
4. 스프링 애플리케이션 컨텍스트 초기화
5. 웹 애플리케이션 실행

💡 ApplicationRunner는 스프링 부트 애플리케이션이 완전히 초기화된 후 실행되는 콜백 인터페이스로, 서버가 아닌 일반 애플리케이션 로직을 실행할 때 유용하다.



뷰 템플릿 엔진 차이

스프링과 스프링부트는 기본 뷰 템플릿 엔진에서도 차이가 있다.



스프링 프레임워크 뷰 템플릿

  • JSP를 기본 뷰 템플릿으로 사용
  • /WEB-INF/jsp/ 디렉토리에 JSP 파일 위치
  • InternalResourceViewResolver를 통해 뷰 경로 설정
<!-- JSP 뷰 리졸버 설정 예시 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
    p:prefix="/WEB-INF/jsp/" p:suffix=".jsp" />



스프링부트 뷰 템플릿

  • 타임리프(Thymeleaf)를 공식 권장 템플릿 엔진으로 사용
  • /resources/templates/ 디렉토리에 타임리프 파일 위치
  • JSP 사용 시 추가 설정 및 라이브러리 필요
# 타임리프 설정 예시
spring:
  thymeleaf:
    prefix: classpath:/templates/
    suffix: .html
    cache: false

스프링부트는 타임리프를 권장하는데, 이는 JAR 패키징과 더 잘 작동하고 자바스크립트와의 통합이 용이하며 자연스러운 템플릿 작성이 가능하기 때문이다.


스프링과 스프링부트는 각각의 장단점이 있으며, 프로젝트의 요구사항과 개발 환경에 따라 적절한 프레임워크를 선택하는 것이 중요하다. 스프링부트는 빠른 개발과 간소화된 설정을 제공하지만, 레거시 시스템과의 통합이나 특수한 요구사항이 있는 경우 전통적인 스프링 프레임워크가 더 적합할 수 있다.

댓글남기기