스프링 부트의 의존성 관리

Sungwoo Hwang·2021년 1월 28일
2

스프링 부트

목록 보기
1/2

본 글은 공부만 하면 까먹을 것 같아서 정리하는 글입니다.

본 글은 인프런 백기선 강사님의 '스프링 부트-개념과 활용' 강좌에서 발췌한 내용들입니다.
스프링 부트 버전은 2.4.2 버전 입니다.

첫 글이어서 매우 미숙합니다. 주의요망 🙇‍♂️

pom.xml 들여다보기

스프링 부트 프로젝트를 Spring Initializr를 이용하거나 IntelliJ와 같은 IDE를 이용하여 생성할 때,
build tool에 gradle,maven과 같은 선택지가 있습니다.

이때 maven을 선택한다면 스프링 부트의 의존성을 포함한 사용되는 java 의 버전,프로젝트의 이름,프로젝트의 버전,프로젝트 개발자와 같은 많은 정보가 pom.xml에 담기게 됩니다.

pom.xml 파일 안의 많은 내용 중에 주목할 부분은 dependencies(의존성들) 부분입니다.

아래 그림과 같이 아주 간단한 스프링 부트 프로젝트만을 만들어도 스프링의 핵심뿐만 아니라 tomcat,junit,slf4j와 같은 많은 라이브러리들을 가져옵니다.

그러나 pom.xml 파일의 dependencies를 보게 되면 의문점이 듭니다.

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
</dependencies>

의존성 선언에 단 두개만의 의존성을 선언해놓고, 심지어 하나의 의존성은 테스트에 관련된 의존성입니다.
그런데 어떻게 위의 그림처럼 수 많은 라이브러리들을 프로젝트에 가져올수 있는걸까요?
또 maven을 한번이라도 써보신분들이라면 이상한 점이 하나 더 있는데요,

보통 maven에서 의존성을 추가하려면 아래처럼 추가를 해야합니다.

<dependency>
    <groupId>org.example.code</groupId>
    <artifactId>example-depedency</artifactId>
    <version>1.0.0</version>
</dependency>

위의 스프링 부트 dependency와 가장 큰 차이점은 <version>이 명시가 되어있다는 것입니다. 라이브러리들의 수 많은 배포판 중에 어느 버전을 사용 할지 명시하지 않으면 maven을 사용할 수 없습니다.

그런데 실제로 빌드를 해보면 아무 문제 없이 작동이 됩니다.
정말 이상합니다.

전 이상하다고 느꼈습니다. 뉴비여서 그런가봐요..

Parent POM

먼저 POM 파일들의 상속구조에 대해서 설명이 필요합니다.

pom.xml 파일의 역할은 POM(Project Object Model)을 설정하는 것으로 프로젝트 내 빌드 옵션을 설정하는 파일입니다.
pom.xml은 부모 pom.xml을 상속하여 미리 설정된 값이나 의존성들의 버전정보를 받아올수 있습니다.

스프링 부트 프로젝트에 생성된 pom.xml 파일의 윗 부분을 살짝 보겠습니다.

<!-- pom.xml -->

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.2</version>
        <relativePath/> <!-- lookup parent from repository -->
</parent>

<parent>가 사용된 걸 볼 수 있습니다.

이것은 spring-boot-starter-parent을 상속했다는 것이고
spring-boot-starter-parent pom 파일인
spring-boot-starter-parent-2.4.2.pom 을 살펴보면

<!-- spring-boot-starter-parent-2.4.2.pom -->

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.4.2</version>
</parent>

한번 더 spring-boot-dependencies 2.4.2를 상속했다는 것을 볼 수 있습니다.

spring-boot-starter-parent-2.4.2.pom 파일을 좀 더 살펴보면

<!-- spring-boot-starter-parent-2.4.2.pom -->

<properties>
    <java.version>1.8</java.version>
    <resource.delimiter>@</resource.delimiter>
    <maven.compiler.source>${java.version}</maven.compiler.source>
    <maven.compiler.target>${java.version}</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>

...

<resources>
	<resource>
        <directory>${basedir}/src/main/resources</directory>
        <filtering>true</filtering>
        <includes>
          	<include>**/application*.yml</include>
			<include>**/application*.yaml</include>
			<include>**/application*.properties</include>
		</includes>
	</resource>
</resources>

기본적인 java의 버전정보와 소스코드의 인코딩 타입과 같은 정보도 기록되어 있습니다.
또 스프링 부트의 환경설정 값들이 적혀 있는 application.yml이나 application.properties 리소스들도 추가해주는 정보가 적혀있습니다.

위와 같은 부모 POM 파일에 적힌 값들은 상속받은 자식 POM에서 오버라이드가 가능합니다.

예를 들어 부모 POMspring-boot-starter-parent에서는
<java.version>이 1.8로 명시가 되어 있습니다.


<!-- parent.1.0.0.pom-->

<properties>
    <java.version>1.8</java.version>
</properties>

이때 본인이 사용하는 프로젝트에서
JDK 15를 사용하고 싶다면 자식 POM 파일에서 <java.version>을 재정의 하면 됩니다.
이때 자식 POM에서 재정의된 값은 부모의 그것보다 높은 우선순위를 가집니다.

<!-- child.pom-->

<parent>
  <groupId>org.example.code</groupId>
  <artifactId>parent</artifactId>
  <version>1.0.0</version>
</parent>

<properties>
    <java.version>15</java.version>
</properties>

이처럼 상속구조를 사용하면 미리 선언되어 있는 값을 편하게 가져다 쓰거나
재정의하여 사용할 수 있습니다.

의존성 관리 (Dependency Management)

위에 언급한 내용 중에 pom.xml에서 의존성을 추가해주는 부분에
버전 정보를 명시하지 않고 넘어갔다는 부분이 있었습니다.

분명 이상하지만 실제로는 정상작동하는 그 부분입니다.

<!-- pom.xml -->

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
</dependencies>

위 처럼 <version>을 사용하지 않고 사용할 의존성들을 선언합니다.

어떻게 가능한걸까요?

정답은 spring-boot-starter-parent가 상속한 spring-boot-dependencies에 있습니다.

<parent> 태그를 통한 상속 구조 :
pom.xml -> spring-boot-starter-parent -> spring-boot-dependencies

spring-boot-dependencies 파일을 살펴봐야겠습니다.

파일 이름부터 의존성들을 담당하겠다는것이 느껴집니다.

<!-- spring-boot-dependencies-2.4.2.pom-->
  
  <project xmlns="http://maven.apache.org/POM/4.0.0" 
           xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
                               http://maven.apache.org/xsd/maven-4.0.0.xsd" 
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.4.2</version>
    <packaging>pom</packaging>
    <name>spring-boot-dependencies</name>
    <description>Spring Boot Dependencies</description>
    ...
    ...
    ...
    <dependencyManagement>
      <dependencies>
        ...
        ...
      	<dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot</artifactId>
          <version>2.4.2</version>
        </dependency>
        <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-test</artifactId>
          <version>2.4.2</version>
        </dependency>
        ...
        ...
      </dependencies>
    </dependencyManagement>
    ...
    ...
  </project>
  

<dependencyManagement>

실제로 spring-boot-dependencies-2.4.2.pom 파일을 열어보면 굉장히 긴데
그 중에서 <dependencyManagement> 영역이 우리가 찾던 정보입니다.

차근차근 살펴보겠습니다.

<!-- spring-boot-dependencies-2.4.2.pom-->

<dependencyManagement>
 <dependencies>
       ...
       ...
   <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot</artifactId>
     <version>2.4.2</version>
   </dependency>
   <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-test</artifactId>
     <version>2.4.2</version>
   </dependency>
       ...
       ...
 </dependencies>
</dependencyManagement>
   

<dependencyManagement> 영역을 보면 <dependencies> 안에
spring-boot-starter-web,spring-boot-starter-test의 의존성에 대한 버전 정보가 <version>2.4.2</version>이라고 명시되어 있습니다.

<parent>를 이용하여 spring-boot-dependencies를 상속하는 구조를 가지게 되면 <dependencyManagement> 영역에 버전이 명시되어 있는 라이브러리들은 pom.xml에서
의존성 추가를 할 때 어느 버전을 사용할지 직접 명시를 하지 않아도 됩니다.

만약 여러분이 사용하는 스프링 부트 버전을 변경해야 한다면 프로젝트에서
사용된 많은 라이브러리들의 의존성 또한 변경되어야 합니다.

이 때 <dependencyManagement>를 이용한다면 라이브러리 버전 선택에 대한 고민을 해결할 수 있습니다.

이러한 상속 구조를 통해 pom.xml 파일에서 직접 관리해야 할 의존성의 수가 줄어듭니다.

Parent POM을 사용하지 않고 <dependencyManagement> 사용하기

이 챕터는 스프링 부트 maven plugin 공식 문서 3.2를 일부 번역한 것입니다.

만약 프로젝트 내부적으로 사용하는 parent가 있거나 명시적으로 Maven 설정을 선언하기를 원한다면 spring-boot-starter-parent를 parent로 사용하지 않고도 dependency management 기능만을 사용할 수 있습니다.

아래와 같이 import scope dependency를 이용하여 dependency management 기능을 사용 할 수 있습니다.

<dependencyManagement>
	<dependencies>
		<dependency>
			<!-- Import dependency management from Spring Boot -->
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-dependencies</artifactId>
			<version>2.4.2</version>
			<type>pom</type>
			<scope>import</scope>
		</dependency>
	</dependencies>
</dependencyManagement>

만약 spring-boot-dependencies 중에서 특정 라이브러리만
다른 버전을 사용하기를 원한다면 spring-boot-dependencies 항목 전에 선언을 해야합니다.

예를 들어 SLF4J 라이브러리와 Spring Data release trainspring-boot-dependencies에 명시된 버전과 다른 버전을 사용하기를 원한다고 하면 아래와 같이 구성해야 합니다.

<dependencyManagement>
	<dependencies>
		<!-- Override SLF4J provided by Spring Boot -->
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>1.7.30</version>
		</dependency>
		<!-- Override Spring Data release train provided by Spring Boot -->
		<dependency>
			<groupId>org.springframework.data</groupId>
			<artifactId>spring-data-releasetrain</artifactId>
			<version>2020.0.0-SR1</version>
			<type>pom</type>
			<scope>import</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-dependencies</artifactId>
			<version>2.4.2</version>
			<type>pom</type>
			<scope>import</scope>
		</dependency>
	</dependencies>
</dependencyManagement>

긴 글이 끝났습니다. 포스팅 하는게 쉽지 않네요. 앞으로 검색할때는 경건한 마음가짐으로 해야겠습니다.

참고한 자료와 출처들

profile
becomeweasel.tistory.com로 이전

0개의 댓글