Web/Kotlin & Spring

[Kotlin & Spring] - 스프링부트 자동 설정 & 커스텀 스타터

Hyunseo😊 2023. 6. 27. 17:31

1. 자동 설정

  • 자동 설정(Auto-configuration)은 스프링 부트를 말할 때 빠질 수 없는 핵심기술을 말합니다.
  • 이는 개발자들이 스프링 관련 프레임워크나 라이브러리를 추가했을 때 번거로운 설정 없이 동작하도록 최적화된 자동 설정을 내장하고 있는 것을 말합니다.
  • 예를 들어 스프링 부트 프로젝트에 스프링 데이터 JPA, 스프링 데이터 몽고디비와 같은 프로젝트를 적용하면 내장 설정이 동작하게 되므로 개발자는 최소한의 설정 값(DB 접속 정보 등)만 넣어주거나 경우에 따라서는 설정을 새롭게 구성할 수 있게 되는 것입니다.

2. 자동 설정 뜯어보기

  • 스프링 부트는 애플리케이션을 실행하는 시점에 autoconfigure 모듈 내부의 META-INF 하위에 있는 메타데이터 파일을 우선적으로 검색합니다.
...
org.springframework.boot.autoconfigure.data.jdbc.JdbcRepositoriesAutoConfiguration
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration
org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration
org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveRepositoriesAutoConfiguration
org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration
...
  • 메타데이터 파일 내부에는 자동 설정을 구현한 후보(candidate) 클래스들의 목록이 등록되어 있습니다
  • 예를 들어 (jpa.HibernateJpaAutoConfiguration, redis.RedisRepositoriesAutoConfiguration),...
  • 스프링 부트는 @EnableAutoConfiguration 애노테이션이 존재하면 자동 설정 클래스를 검색하고 조건에 따라서 자동 설정 클래스를 로드합니다.
  • 기본적으로 선언하지 않아도 @SpringBootApplication 애노테이션에 포함되어 있기 때문에 따로 선언할 필요가 없지만 직접 사용할 수도 있습니다.

3. 조건부 애노테이션

  • 자동 설정이 많다는 건 해야 할 설정이 적어지기 때문에 매우 편하지만 그렇다고 해서 사용하지도 않는데 모든 설정 클래스를 한 번에 로드하는 것은 너무 불필요한 작업일 것입니다.
  • 스프링 부트는 그래서 조건부 애노테이션(Conditional Annotation)인 @Conditiona을 사용해 조건에 따라 자동 설정 클래스를 로드합니다.

  • 또한 스프링 부트는 자주 사용하는 조건부 애노테이션을 재사용성을 위해 미리 정의하여 제공하고 있습니다. 이러한 애노테이션은 @ConditionalOn이라는 이름으로 시작합니다.
  • autoconfigure 모듈 내부의 ConditionalOn으로 시작하는 애노테이션

  • 위와같이 클래스가 존재하는 경우에만 동작하거나 이와 반대로 클래스가 존재하지 않는 경우에 동작하는
    • @ConditionalOnClass, @ConditionalOnMissingClass
  • 빈이 애플리케이션 컨텍스트에 존재하는 경우에만 동작하거나 이와 반대로 빈이 애플리케이션 컨텍스트에 존재하지 않는 경우에만 동작하는
    • @ConditionalOnBean, @ConditionalOnMissingBean

 

  • 다음은 미리 정의된 조건부 애노테이션이 어떤 식으로 사용되는지 알기 위해 스프링의 핵심 기능 중 하나인 AOP 자동 설정 클래스인 AopAutoConfiguration과 @ConditionalOnProperty를 예로 들어 설명합니다.
  • 프로퍼티가 존재하는 경우에만 동작하는 ConditionalOnProperty가 적용된 자동 설정 클래스 AopAutoConfiguration
@AutoConfiguration
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration

 

이제 위 지식을 바탕으로 직접 커스텀 스프링 부트 스타터를 만들어 보도록 하겠습니다.

  • 스프링 부트 스타터(spring-boot-starter)는 스프링 부트 기반의 애프리케이션에 따른 스프링 프로젝트를 쉽게 추가할 수 있도록 만들어진 라이브러리를 말합니다.
  • 각각의 스타터에는 주축이 되는 스프링 프로젝트와 프로젝트에서 필요로 하는 필수적인 라이브러리들이 포함되어 있습니다.
  • 이런 이유로 스프링 부트 스타터를 사용하면 스타터 외에 필요한 라이브러리를 따로 추가하거나 관리할 필요가 없습니다.

 

보통 서드 파티 스프링 부트 스타터의 명명은 *-spring-boot-starter와 같은 방식으로 명명하는 것이 일반적입니다.

 

커스텀 스프링 부트 스타터 만들기

지금부터 직접 만들어본 handgame 라이브러리와 스프링 부트의 기능을 사용해서 커스텀 스프링 부트 스타터를 만들어 보도록 하겠습니다. 

 

  • 우리는 그래이들의 멑리 프로젝트를 사용해서 하나의 프로젝트에 멀티 모듈을 구성했습니다.
  • 생성된 멀티 모듈의 구조는 아래와 같습니다.
    • handgame
      • 가위바위보 게임의 상세 구현이 포함된 라이브러리
    • handgame-spring-boot-autoconfigure
      • handgame 라이브러리의 자동 설정이 포함
    • handgame-spring-boot-starter
      • handgame, handgame-spring-boot-autoconfigure를 합친 커스텀 spring-boot-starter
    • handgame-sprint-boot-app
      • handgame-spring-boot-starter를 사용하는 스프링 부트 애플리케이션

 

최상위 build.gradle.kts

import org.springframework.boot.gradle.plugin.SpringBootPlugin 
plugins {
    id("org.springframework.boot") version "2.7.0" apply false
    id("io.spring.dependency-management") version "1.0.11.RELEASE"
    id("maven")
    id("maven-publish")
	kotlin("jvm") version "1.6.21"
    kotlin("plugin.spring") version "1.6.21"
    kotlin("kapt") version "1.6.21"
}

allprojects {
	group = "com.fastcampus.springboot"
    version = "1.0-SNAPSHOT"
	repositories {
    	mavenLocal()
		mavenCentral()
	}
}
...
  1. maven-publish는 메이븐 리파지토리에 배포할 수 있게 하는 기능을 하는 플러그인입니다. 이번 예시에서는 제작한 스타터 프로젝트를 로컬 메이븐 리파지토리에 배포해서 handgame-spring-boot-app에서 사용해보도록 하겠습니다.
  2. allprojects는 최상위 프로젝트를 포함한 전체 프로젝트에서 사용하는 빌드를 구성합니다.
  3. subprojects는 settings.gradle.kts레서 include에 감싸진 프로젝트의 빌드를 구성합니다. 예를 들면 include("handgame", "handgame-spring-boot-starter)와 같은 프로젝트가 서브 프로젝트입니다.

 

handgame/Handgame.kt

handgame-spring-boot-autoconfigure/build.gradle.kts

 

  • kapt는 코틀린의 애노테이션 프로세서입니다. 이는 컴파일 타임에 애노테이션을 읽어서 동적으로 코드를 생성하거나 변경하는 등의 기능을 말하는데 kapt는 코틀린 언어에서 이러한 애노테이션 프로세서가 동작하도록 하는 플러그인입니다.
  • 자동 설정 클래스에서 Handgame.kt클래스를 불러올 수 있어야 하기 때문에 handgame프로젝트에 대한 의존성을 추가합니다.
  • spring-boot, spring-boot-autoconfigure는 우리가 만들 자동 설정이 스프링 부트 자동 설정 기능을 사용해야 하므로 필수적인 의존성임

 

handgame-spring-boot-autoconfigure/HandgameAutoconfigure.kt

package com.fastcampus.springboot.autoconfigure.handgame

import com.fastcampus.springboot.handgame.Handgame
import org.springframework.boot.autoconfigure.AutoConfiguration
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
import org.springframework.context.annotation.Bean


@Configuration
@ConditionalOnClass(Handgame::class)
@ConditionalOnProperty(prefix = "my.handgame", name = ["enabled"], havingValue="true")
class HandgameAutoconfiguration {

    @Bean
    @ConditionalOnMissingBean
    fun handgame() = Handgame()

}
  • HandgameAutoconfiguration은 Handgame클래스의 인스턴스를 스프링 빈에 등록하는 자동 설정 클래스입니다. 자동 설정 클래스는 설정 클래스임을 나타내기 위해 @Configuration어노테이션을 선언합니다.
  • @ConditionalOnProperty를 사용해서 my.handgame으로 시작하고 enabled라는 프로퍼티가 true인 경우에만 해당 설정 클래스가 동작하도록 로드 시점을 조절합니다.
  • 만약 사용자가 handgame 빈을 재정의한 경우에는 충돌이 일어날 수 있으므로 @ConditionalOnMissingBean을 사용해서 handgame빈이 존재하지 않는 경우에만 빈을 로드합니다.

handgame-spring-boot-autoconfigure/META- INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

com.fastcampus.springboot.autoconfigure.handgame.HandgameAutoconfiguration
  • 그리고 우리가 작성한 자동 설정 클래스 handgameAutoConfiguration을 스프링에게 알려주기 위해 해당 파일에 명시해야 합니다.

handgame-spring-boot-starter/build.gradle.kts

dependencies {
	api(project(":handgame"))
    api(project(":handgame-spring-boot-autoconfigure"))
}
  • 앞서 구현한 handgame 라이브러리와 자동 설정 구현체인 handgame-spring-boot-autoconfigure에 대한 의존성을 추가합ㅂ니다.

 

그리고 handgame > handgame-spring-boot-autoconfigure > handgame-spring-boot-starter 순서로 jar파일로 배포합니다.

 

handgame-spring-boot-app/build.gradle.kts

dependencies { 
	implementation("org.springframework.boot:spring-boot-starter")

	implementation("com.fastcampus.springboot:handgame-spring-boot-starter:1.0-SNAPSHOT") 
}
  • 배포가 완료됐다면 스타터를 사용할 프로젝트에 의존성을 추가해줍니다.

정상 작동합니다. 그리고 git주소는 아래에 첨부합니다.

 

https://github.com/eunoiahyunseo/kotlin-thirdparty-module-starter-app

 

GitHub - eunoiahyunseo/kotlin-thirdparty-module-starter-app

Contribute to eunoiahyunseo/kotlin-thirdparty-module-starter-app development by creating an account on GitHub.

github.com