💎 들어가며

이번 포스팅에서는 앞선 포스팅에서 생성한 프로젝트(New > Spring Legacy Project > Spring MVC Project)를 바탕으로 프로젝트의 구조를 설명하고, 프로젝트가 로드되고 요청이 처리되는 과정에 대해 설명합니다.

참고로 저는 Blog라는 프로젝트를 생성했고, 게시판 목록을 보여주는 것까지 시리즈에 포스팅 할 예정입니다.


1. Project Architecture

프로젝트 생성: [New > Spring Legacy Project > Spring MVC Project]
아래는 생성된 프로젝트의 초기 구조입니다.

Spring Boot 부터는 공식적으로 GradleMaven을 지원하지만, 이전에는 기존 구조로 Maven을 채택했기 때문에 Maven 프로젝트로 생성되는 것을 볼수 있습니다.
(아주 예전에는 Ant라는 빌드 툴을 많이 사용했다고 합니다. 시대 흐름: AntMavenGradle)


1.1 Description

스프링 프로젝트의 디렉터리 구조와 설명은 아래와 같습니다.

📁 Project명

  • src/main/java: Java 소스 디렉터리로 생성할 수 있는 여러 자바파일이 위치한 디렉터리입니다.
    @Controller, @Service, @Component, ... etc

  • src/main/resources: 각종 환경설정 파일이 위치한 디렉터리입니다. 해당 디렉터리는 classpath로 지정되어있어, classpath:/로 접근할 수 있습니다. mapper 파일, logging 설정파일, 시스템 설정파일(.properties) 등이 있습니다.

  • /src/test/java, /src/test/resources: JUnit과 같은 테스트 라이브러리로 테스트할 때 사용하는 디렉터리입니다.

  • /src/main/webapp
    resources: 정적 리소스 디렉터리. js, css, image 등의 정적 파일을 넣어두는 디렉터리입니다.
    classes: 웹 어플리케이션에서 사용하는 클래스 파일이 위치한 디렉터리입니다. war 파일 형태로 배포하게 되면 src/main/java 소스가 컴파일된 .class 파일, src/main/resources 내의 파일이 저장됩니다.
    WEB-INF: Web Information의 약자로 웹에 관한 중요 정보를 넣는 디렉터리.
    web.xml: 웹 애플리케이션 설정 파일

  • target 디렉터리: 빌드 출력 결과 (.war)

  • pom.xml: Maven 빌드 관리자

web.xml

web.xml은 웹 애플리케이션(Web Application) - webapp을 개발하기 위해 반드시 필요한 파일입니다.

배포 설명자(DD, Deployment Descriptor)로 불리며, 웹애플리케이션 설정 파일입니다.

Servlet Container 구동시 로드하는 파일 입니다. 즉, Servlet 요청을 처리와 관련된 내용이 들어갑니다.

예를 들어 Servlet, Listner, Filter, Error Page, Session 설정 등이 포함될 수 있습니다.


1.2 Project Settings

프로젝트 세팅에 대해

대학에서 부터 학원까지 들었던 공통적인 말이 있습니다. "Spring은 초기세팅이 어렵다."

햇수로 4년차인 지금도 아직 모르는게 많지만, 프로그램에는 언제나 정답이 없으니 처음부터 엄청난 의미있고 구조적인 세팅을 할 수도 없고, 할 필요도 없다는 생각이 드네요

프로젝트 세팅을 어려워할 필요는 없다고 생각합니다. (일단 돌아가게라도 만들어라!!)

프로젝트 세팅은 단순히 그냥 프로젝트의 환경을 구성하는 것, 즉 프로젝트 커스터마이징입니다. 이는 초기에 국한되지 않고, 개발 중에도 변경될 수 있습니다.


세팅에 앞서 유의 깊게 봐야하는 두가지 파일이 있습니다.

  • pom.xml: 프로젝트 빌드 관리자. 패키지 관리, 의존성(라이브러리) 관리
  • web.xml: 배포 설명자. Servlet 요청처리 관리, Spring 설정파일 관리

2절에서는 pom.xml에 관련된 Maven과 pom.xml에 대해 설명하고
3절에서는 web.xml에 관련된 웹 애플리케이션 동작과정과 Spring 설정파일에 대해 설명할 예정입니다.


2. Maven Build Manager

2.1 What is Build?

빌드에 대해 들어본적이 있나요?

신입 시절 가장 어려웠던 개념 중에 하나가 바로 빌드ㆍ배포인데요. 그 이유는 바로, 프로젝트 경험을 위해 개발만 해놓고 애플리케이션을 실제로 설치(이하 배포)해줄 일이 없었기 때문입니다.


애플리케이션을 사용자들이 사용하기 위해 애플리케이션을 배포해주기 위해서는 배포 파일이 필요하고, 빌드는 우리가 작성한 소스 코드 파일을 컴퓨터에서 독립적으로 실행할 수 있는 독립 소프트웨어 가공물(배포 파일)로 변환하는 과정을 의미합니다.

다시 말해, 소스코드(java), 파일 및 자원등(xml, jsp, js, jar, properties, etc)을 JVM이나 톰캣같은 WAS가 인식할 수 있는 구조로 패키징하는 과정 및 결과물(.jar, .war, 등) 이라고 할 수 있습니다.

Maven은 이러한 빌드 과정을 지원하기 위한 빌드 도구(build tool)입니다. Maven은 빌드ㆍ배포 뿐만 아니라 패키지 의존성(라이브러리)를 관리하는 기능도 제공합니다.


2.2 pom.xml

Maven 기능을 이용하기 위해서는 POM이 사용됩니다. POM이란 프로젝트 객체 모델(Project Object Model)로서 프로젝트의 다양한 정보를 처리위한 객체 모델입니다.

Maven에서는 pom.xml을 통해 이러한 POM 설정을 XML 태그로 설정할 수 있습니다.
아래는 pom.xml의 예시입니다.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
         https://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.khjdev</groupId>
	<artifactId>blog</artifactId>
	<name>Blog</name>
	<packaging>war</packaging>
	<version>1.0.0-BUILD-SNAPSHOT</version>
	<properties>
		<java-version>1.6</java-version>
		<spring-version>3.1.1.RELEASE</spring-version>
		...
	<dependencies>
		<!-- Spring -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${spring-version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${spring-version}</version>
		</dependency>
      	...
	</dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.5.1</version>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                    <compilerArgument>-Xlint:all</compilerArgument>
                    <showWarnings>true</showWarnings>
                    <showDeprecation>true</showDeprecation>
                </configuration>
            </plugin>
            
          	...
          
        </plugins>
    </build>
</project>

pom.xml에는 여러가지 태그가 있습니다. 아래는 그 설명입니다.


🔖<project>: 루트 태그로 루트 태그 내에 모든 정보를 기술합니다.
modelVersion: POM model 버전
groupId: 프로젝트를 생성하는 조직의 고유 아이디. 일반적으로 도메인 이름을 거꾸로 적는다.

artifactId: 프로젝트 빌드시 파일 대표이름이다. groupId 내에서 유일해야한다.
version: 프로젝트의 현재 버전, 프로젝트 개발 중일 때는 SNAPSHOT을 접미사로 사용
packaging: 패키징 유형(jar, war, 등)
name: 프로젝트 이름
description: 프로젝트에 대한 간략한 설명
url: 프로젝트에 대한 참고 Reference 사이트
properties: 라이브러리의 버전관리시 용이하다. 변수라고 보면된다.
ex) java.version 선언 후 dependencies에서 다음과 같이 활용한다.

<version>${java.version}</version>

dependencies: 프로젝트와 의존관계에 있는 라이브러리들(dependency)을 관리한다.
build: 빌드에 사용할 플러그인 목록


🔖<dependencies>: 태그 아래에는 사용할(의존 관계에 있는) 라이브러리 목록을 기술합니다.

dependency 태그를 통해 기술된 의존관계는 Maven에서 Maven Repository에 등록된 라이브러리라면 자동으로 다운로드 해줍니다.

<dependecies>
  <dependency>
      <groupId>그룹 ID</groupId>
      <artifactId>아티팩트 ID</artifactId>
      <version>버전</version>
      <scope>범위</scope>
  </dependency>
</dependencies>

2.3 Summary

정리하자면, 프로젝트의 정보(버전, 이름, 라이브러리, 등)를 변경하기 위해서는 pom.xml 설정 파일을 변경해야 합니다.


3. Process

3.1 web.xml, servlet container, spring container

그 다음 중요한 스텝은 web.xml을 통해 스프링 설정파일을 파악하고, 해당 파일을 통해 객체(bean)를 선언하는 것입니다.

사용하기 전에 온전한 설정을 위해서 웹 애플리케이션 동작과정에 대해 이해하고 넘어가야 합니다.


3.2 Process of Web Application

Web Application의 동작 과정을 이해하기 위해서는 두가지 컨테이너(Servlet ContainerSpring Container)에 대해서 이해해야 합니다.

🌟 What is Container?

본디 컨테이너는 여러 의미로 쓰일 수 있지만, 여기서 말하는 컨테이너의 의미는 인스턴스(instance)수명주기(Lifecycle)(생성 ~ 소멸)를 관리하는 역할을 수행하는 쉽게 말해 인스턴스 관리자입니다.

Servlet 컨테이너Servlet의 생성, 수행, 소멸을 관리합니다.

Servlet이란

Servlet은 사용자의 요청(request)을 처리해서 동적인 컨텐츠를 응답(response)하기 위한 프로그램입니다.

Spring 컨테이너는 Spring 객체(bean)의 생성, 수행, 소멸을 관리합니다.


"컨테이너가 관리를 한다는 것"은 정확히 어떤 의미인가요?

정확히 말해, 개발자가 컨테이너의 인스턴스의 수명주기에 관여하지 않는다는 것입니다.

아래에 두가지 예시가 있습니다.
1. Servlet Container - 우리는 어떠한 원리로 Tomcat Server가 실행되는지 알수 없습니다.
2. Spring Container - @Controller 어노테이션을 사용했지만 우리는 new 명령어를 통해 객체를 생성하지 않았습니다


💬 Relationship between Servlet Container and Spring Container

Servlet ContainerSpring container 사이의 관계를 도식화하면 아래와 같습니다.

  1. Tomcat Server를 구동한다
    web.xml을 로딩하여 Servlet Container가 구동된다.

  2. Servlet Container가 web.xml의 기술된 listener들과 servlet 목록 등 요청처리에 필요한 각종 설정들을 불러와 로드한다.
    ContextLoaderListenerRootContext를 생성
    Servlet에는 DispatcherServlet 하나만 생성하여 Servlet의 모든 요청을 받게하고, ServletContext를 생성

  3. RootContext 및 ServletContext 생성시 param으로 전달된 xml 파일을 통해 Spring Container객체(bean)을 생성ㆍ관리한다.


💡 RootContext vs ServletContext

ServletContainer로 생성된 RootContext와 ServletContext는 서로 비슷한 부분이 많아, 비교하여 설명해볼까 합니다.

1) ServletContext

서블릿 요청(request)와 관련된 객체를 정의합니다. 주로 View와 관련된 객체(controllers, view resolvers, 등)를 설정합니다.

2) RootContext

Root Context는 직역하자면 전역적으로 사용할 빈들을 선언한다는 의미로 해석될 수 있습니다. Servlet Context와는 반대로 View와 관련되지 않은 객체를 정의합니다.

Service, Repository(DAO), DB 등 비즈니스 로직과 관련된 설정합니다. (일반적으로 여러 Servlet 인스턴스에서 공유해야 하는 데이터 저장소 및 비즈니스 서비스와 같은 인프라 Bean)

3) XML에서 적용

context:component-scan

Spring에서 빈을 생성하기 위해 XML에서 beans:bean 태그로 정의하거나, context:component-scan 태그을 통해 스프링 객체를 로드

  • servlet-context.xml 설정: servlet-context에서는 기존 component-scan 설정에서 프로젝트 패키지 안에서의 Controller 어노테이션만 스캔하게끔 변경합니다.
<!-- servlet context -->
<context:component-scan base-package="com.khjdev.blog" use-default-filters="false">
		<context:include-filter type="annotation"
			expression="org.springframework.stereotype.Controller" />
	</context:component-scan>
  • root-context.xml 설정
<!-- root context -->
<context:component-scan base-package="com.khjdev.blog">
  <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan> 

📑 Summary

이러한 설정으로 인해, 우리는 자바코드에서 ControllerService, Repository 등의 Spring Component를 사용할 수 있는 것입니다.

정리하자면 아래와 같습니다.

  1. web.xmlRoot Context, Servlet Context의 설정을 바꿀수 있다.
  2. Root Context에는 Business Layer인 Service, Repository 등의 객체를 선언한다.
  3. Servlet Context에서는 Presentation Layer인 View, ViewResolver 등 View와 관련된 객체를 선언한다.

3.3 Process of Client requests

웹 애플리케이션의 동작 과정을 설명하면서, Servlet 요청을 처리하는 과정도 함께 기술할까 합니다.

앞서 설명했듯이 Servlet이란 놈이 웹의 요청을 처리합니다. 하지만, 자바에서 공식 지원하는 Servlet API를 이용하여 매번 Servlet 객체를 구현하여 요청을 처리할 수는 있지만, 정말 매~~우 귀찮은 일이기 때문에 우리는 Spring이라는 웹 프레임워크를 사용하는 것입니다.

Spring에서는 이러한 문제를 해결하기 위해 모든 '/'로 들어오는 Servlet 요청을 DispatcherServlet이 받아서 처리하도록 구현했습니다.

web.xml을 보면 아래와 같이 servlet이 한개 등록되어 있습니다.

<web-app>
	...
	<!-- Processes application requests -->
	<servlet>
		<servlet-name>appServlet</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>

	<servlet-mapping>
		<servlet-name>appServlet</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>
</web-app>

Spring MVC가 어떤 구조로 되어있는지 설명하기 위해 웹 요청 처리 과정을 도식화하였습니다.

  1. 클라이언트가 요청합니다.
    ex) 게시판 요청 - /boards

  2. DispatcherServletHandlerMapping에게 해당 요청(url)을 처리할 수 있는 Controller를 찾아 달라고 요청합니다.
    └ 없으면 404 Error 반환
    └ 있으면 해당 Controller에게 전달합니다.

  3. Controller는 받은 요청을 처리합니다.
    └ 이때 Service단은 표시하시는 않지만, Controller에서도 Service에게 처리를 위임합니다.

  4. 해당 결과값으로, View 또는 ModelAndView 객체를 다시 DispatcherServlet 에게 반환합니다.

  5. DispatcherServlet은 Controller에게 받은 값을 ViewResolver에게 전달하여, ViewResolver는 prefix와 suffix를 적용한 값을 반환합니다.

  6. DispatcherServlet은 해당 뷰를 호출하면서 Model도 함께 전달합니다.

  7. View은 Model 객체를 가져와서 응답결과를 만들고 클라이언트에게 응답 결과를 전달합니다.


3.4 Summary

결론적으로 우리는 web.xml에는 여러가지 웹요청에 관한 설정이 등록될 수 있습니다.

그중에서도 특히 Servlet에는 DispatcherServlet 하나만 등록되고, 이 서블릿이 웹에 들어오는 모든 요청을 받아 분산시켜주는 역할을 합니다.

또한 web.xmlcontextConfigLocation에 기술된 스프링 빈 환경설정 파일을 바탕으로 Spring Container가 구동되어 해당 파일에서 찾을 수 잇는 빈(bean)들이 메모리에 올라갑니다. 그렇기 때문에 각 설정파일의 용도에 맞게 bean을 등록하거나, Spring이 객체를 인식할 수 있도록 component를 scan하는 작업을 해야합니다.

  • RootContext - Business Layer
  • ServletContext - Presentation Layer

💎 마치며

이번 포스팅에서는 Spring MVC 프로젝트의 구조를 살펴보고 웹 애플리케이션의 동작원리, 웹 요청 처리 과정에 대해 포스팅했습니다.

다음 포스팅에서는 이들과 실제 프로젝트에서 어떤 파일을 봐야되고, 어떻게 세팅해야되는지에 대해 포스팅하겠습니다. 읽어주셔서 감사합니다. 🥰🥰

profile
Java, Spring 기반 풀스택 개발자의 개발 블로그입니다.

0개의 댓글