23.06.15
DEVOTEE를 활성화 시키면
지금 작성한 커뮤니티 글에 대해 1개의 댓글을 달아줍니다.
버튼을 누르면 글 수정 시 ChatGPT가 작성한 댓글이 수정됩니다.
| 컨텐츠 유형 | 제목 | 저장일 | 삭제 |
|---|
본인인증 로그인에 실패하였습니다.
회원이 아니시거나 본인인증 등록이
완료되지 않은 사용자입니다.
Spring Boot 는 자동설정 기능을 제공하여 Spring 에서 개발자가 직접 설정하던 많은 설정 파일들 없이 손쉽고 빠르게 제품을 개발할 수 있습니다.
이번 포스트 에서는 Spring Boot 의 자동설정에 대해 간단히 살펴보고 나만의 자동설정 파일을 만들어 보도록 하겠습니다.
간단한 스프링 부트 프로젝트를 만들어 보겠습니다.
저는 인텔리 제이 Ultimate 버전을 사용 하는데요,
굳이 Ultimate 가 아닌 무료 버전도 손쉽게 프로젝트를 만들 수 있는 방법을 제공 합니다.
다음> 버튼을 클릭해서 의존성을 설정해 줍니다.
Web 만 설정해 주겠습니다.
프로젝트가 준비되면 애플리케이션을 실행해 줍니다.
보시면 아무 것도 없는 빈 프로젝트이지만 톰캣도 구동되고 실제 서비스도 동작하는 것을 로그로 볼 수 있습니다.
Tomcat 을 구동시키는 프로그램이 없는데도 불구하고 로그파일에 Tomcat이 실행되어 있음
우리가 설정하지 않았는데도 불구하고 Tomcat 이 구동되어 있는 것을 보면, Spring Boot 가 미리 설정을 해 놓은 것 같습니다.
그러면 어디에 정의가 되어 있을까요?
비밀(?) 은 @SpringBootApplication 어노테이션에 있습니다.
쉬프트키를 누른 상태에서 해당 어노테이션을 클릭하면 세부 내용으로 들어 가는데요,
이 안에 또 다른 어노테이션이 포함되어 있는 것을 볼 수 있습니다.
@SpringBootApplication 어노테이션은 아래 어노테이션들을 포함하고 있습니다.
@SpringBootConfiguratio
@EnableAutoConfiguration
@ComponentScan
이중 @EnableAutoConfiguration 이름에서부터 알 수 있듯이 필요한 수많은 자동설정을 해주는 어노테이션 입니다.
참고로, Spring Boot 에서 관리하는 자바 프로그램을 Bean 이라고 합니다.
우리가 만들게 될 Bean 들 혹은 미리 생성해 놓은 수많은 Bean 들을 애플리케이션이 시작되면 메모리로 로드하게 되는데요..
Spring Boot 는 Bean 을 로드 하는 과정이 두 번 있습니다.
첫 번째는 @ComponentScan 으로 메인 애플리케이션 진입점(@SpringBootApplication이 있는 프로그램)의 패키지 내의 모든 Bean 을 로드 하게 되고
두 번째로는 @EnableAutoCongiguration 의 Bean 들을 로드하게 됩니다.
(만약 우리가 만든 임의의 Bean 의 이름이 두 번째 로드 한 Bean 과 로드 하게 되면 기본 설정 상태에서는 BeanDefinition Exception 이 발생하게 됩니다)
어떤 것들을 로드 하는지 알아 보겠습니다.
인텔리 제이 > 프로젝트 창 > 외부 라이브러리로 이동해서.
Maven: org.springframework.boot:spring-boot-auitoconfigure 로 이동합니다.
META-INF > Spring > org.springframework.boot.autoconfigure.AutoConfiguration.imports 로 이동하면 많은 라이브러리들을 등록하는 것을 볼 수 있습니다.
이 목록들이 설정파일로 구성되게 됩니다.
우리가 설정하지 않아도 기본적으로 위의 설정들이 자동으로 구성됩니다
META-INF > Spring > org.springframework.boot.autoconfigure.AutoConfiguration.imports 파일에 우리가 원하는 설정만 지정해 주면 우리만의 설정파일을 만들 수 있습니다.
위에서 자동설정을 위한 구성파일이 무엇인지 알아봤습니다. 이제, 나만의 의존성을 만드는 방법을 진행해 보겠습니다.
메이븐 프로젝트 생성 > 의존성 Pom파일 작성 > 테스트를 위한 Bean파일 작성 의로 의존성 구성을 해주고,
빌드해서 jar 로 install 해주면 됩니다.
이렇게 install 한 jar 파일은 로컬 리포지토리에 저장되고 다른 프로젝트들에서 (물론 로컬), 자동설정을 임포트 할 수 있습니다.
pom.xml 에 아래처럼 의존성을 추가해 줍니다.
간단하게 spring-boot-autoconfigure, spring-boot-autoconfigure-processor 과 이 의존성들의 버전을 관리해주는 spring-boot-dependencies 를 추가했습니다.
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>self-di</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>self-di</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure-processor</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>3.2.2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>다음으로 테스트를 할 두 개의 파일을 만들어 줍니다.
DevOcean.java
package org.example;
public class DevOcean {
private String groupName;
private int memberCount;
public String getGroupName() {
return groupName;
}
public void setGroupName(String groupName) {
this.groupName = groupName;
}
public int getMemberCount() {
return memberCount;
}
public void setMemberCount(int memberCount) {
this.memberCount = memberCount;
}
@Override
public String toString() {
return "DevOcean{" +
"groupName='" + groupName + '\'' +
", memberCount=" + memberCount +
'}';
}
}DevOceanConfig.java
package org.example;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class DevOceanConfig {
@Bean
public DevOcean devOcean(){
DevOcean devOcean = new DevOcean();
devOcean.setGroupName("Back-End");
devOcean.setMemberCount(4);
return devOcean;
}
}그리고, resources 폴더 밑에 META-INF 를 만들고, spring.factories를 만든 다음 아래 설정파일을 추가해 줍니다.
이제 buile > install 을 해서 로컬 레지스터리에 등록해 줍니다.
당연히 jar 파일이 만들어 지겠죠.
위에서 만든 jar 파일을 자동설정파일로 구성하는 새로운 프로젝트를 만들어 보겠습니다.
위와 마찬가지로 maven 프로젝트를 하나 만들어 주시고, pom 파일에 dependency 를 추가해 줍니다.
프로젝트 이름은 self-di-test로 만들었습니다.
<dependency>
<groupId>org.example</groupId>
<artifactId>self-di</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>새로운 프로젝트는 단순 Maven 프로젝트이지만, 우리가 만들었던 스프링부트 의존성이 추가됐기 때문에 @SpringBootApplication 을 사용할 수 있습니다.
프로젝트를 테스트하기 위해 두 가지 파일을 만들어 줍니다.
DevoceanRunner.java
@Component
public class DevOceanRunner implements ApplicationRunner
{
@Autowired
DevOcean devOcean;
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println(devOcean.toString());
}
}웹어플리케이션을 띄우지 않고(의존성이 없기 때문에), 아래와 같이 스프링부트 메인 프로그램을 작성해 줍니다.
App.java
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(App.class);
springApplication.setWebApplicationType(WebApplicationType.NONE);
springApplication.run(args);
}
} 프로그램을 시작하면, Bean 으로 등록한 DevOcean 객체가 정상적으로 등록되는 것을 볼 수 있습니다.
우리가 만든 스프링부트 자동설정과 빈이 정상적으로 등록됐음을 확인할 수 있습니다.
제가 아까 위에서 스프링 빈이 등록되는 과정이 두 단계가 있다고 얘기 했는데요..
ComponentScan 으로 우리가 만든 설정 빈등이 우선 등록 되고
자동설정으로 생성된 빈이 다음으로 등록이 됩니다.
위에서 문제점이 하나 있는데요.. devOcean 빈은 자동설정으로 구성된 빈 입니다.
따라서 빈 등록 순서에 따르면 2번째로 생성이 되지만 만약 동일한 이름으로 우리가 만든 빈이 있다고 하면 BeanDefinition Exception 이 발생하게 됩니다.
아래 코드로 재현해 볼 수 있습니다.
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(App.class);
springApplication.setWebApplicationType(WebApplicationType.NONE);
springApplication.run(args);
}
//todo : ComponentScan 에서 생성되는 동일한 이름의 빈을 생성
@Bean
public DevOcean devOcean(){
return new DevOcean();
}
}실행하면 아래처럼 오류가 발생합니다.
***************************
APPLICATION FAILED TO START
***************************
Description:
The bean 'devOcean', defined in org.example.App, could not be registered. A bean with that name has already been defined in class path resource [org/example/DevOceanConfig.class] and overriding is disabled.
Action:
Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true이걸 막으려면 properti 파일에서 bean재정의를 true 로 해주는 방법이 있습니다.
그렇게 되면 동일한 이름의 빈이라 하더라도 우선순위에 의해 덮어쓰일 수 있습니다.
spring.main.allow-bean-definition-overriding=true혹은, 자동설정의 빈 파일에서 아래처럼 @ConditionalOnMissingBean 을 설정해 줄 수 있습니다.
public class DevOceanConfig {
@Bean
@ConditionalOnMissingBean
//todo : devOcean 빈이 없을 때 빈으로 등록하는 옵션ㄴ
public DevOcean devOcean(){
DevOcean devOcean = new DevOcean();
devOcean.setGroupName("Back-End");
devOcean.setMemberCount(4);
return devOcean;
}
}위에서 테스트한 예시는 빈을 만들고 해당 빈의 Getter 와 Setter 를 이용해서 약간은 복잡한 방식으로 속성 값들을 가지고 왔습니다.
좀 더 간단하게 프로퍼티 파일(application.properties) 에 해당 값을 추가하고 이 값을 가져오게 만들 수도 있는데 아래의 파일에서 간단하게 예시를 들어 보겠습니다.
먼저 프로퍼티 파일을 위한 파일을 자동설정으로 구성한 프로젝트에 추가해 줍니다.
DevOceanProperties.java(추가)
@ConfigurationProperties(prefix = "devocean")
public class DevOceanProperties {
private String groupName;
private int memberCount;
public String getGroupName() {
return groupName;
}
public void setGroupName(String groupName) {
this.groupName = groupName;
}
public int getMemberCount() {
return memberCount;
}
public void setMemberCount(int memberCount) {
this.memberCount = memberCount;
}
@Override
public String toString() {
return "DevOcean{" +
"groupName='" + groupName + '\'' +
", memberCount=" + memberCount +
'}';
}
}DevOceanConfig.java (수정)
@Configuration
@EnableConfigurationProperties(DevOceanProperties.class)
public class DevOceanConfig {
@Bean
@ConditionalOnMissingBean(DevOcean.class)
public DevOcean devOcean(DevOceanProperties properties){
DevOcean devOcean = new DevOcean();
devOcean.setGroupName(properties.getGroupName());
devOcean.setMemberCount(properties.getMemberCount());
return devOcean;
}
}pom 파일에 아래 의존성을 추가해 줍니다.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>이제 build > install 을 해 줍니다.
자, 이제 의존성을 주입받는 di-self-test 로 돌아와서, application.properties 파일에 아래처럼 추가해 줄 수 있습니다.
spring.main.allow-bean-definition-overriding=true
devocean.group-name=JAVABACKEND
devocean.member-count=200실행하면 아래처럼 정상적으로 설정파일에 등록한 값을 가져오는 것을 볼 수 있습니다.
/Users/jaeyoungpark/Library/Java/JavaVirtualMachines/corretto-17.0.10/Contents/Home/bin/java -XX:TieredStopAtLevel=1 -Dspring.output.ansi.enabled=always -Dcom.sun.management.jmxremote -Dspring.jmx.enabled=true -Dspring.liveBeansView.mbeanDomain -Dspring.application.admin.enabled=true -Dmanagement.endpoints.jmx.exposure.include=* -javaagent:/Users/jaeyoungpark/Applications/IntelliJ IDEA Ultimate.app/Contents/lib/idea_rt.jar=63327:/Users/jaeyoungpark/Applications/IntelliJ IDEA Ultimate.app/Contents/bin -Dfile.encoding=UTF-8 -classpath /Users/jaeyoungpark/Desktop/start_spring/self-di-test/target/classes:/Users/jaeyoungpark/.m2/repository/org/example/self-di/1.0-SNAPSHOT/self-di-1.0-SNAPSHOT.jar:/Users/jaeyoungpark/.m2/repository/org/springframework/boot/spring-boot-autoconfigure/3.2.2/spring-boot-autoconfigure-3.2.2.jar:/Users/jaeyoungpark/.m2/repository/org/springframework/boot/spring-boot/3.2.2/spring-boot-3.2.2.jar:/Users/jaeyoungpark/.m2/repository/org/springframework/spring-core/6.1.3/spring-core-6.1.3.jar:/Users/jaeyoungpark/.m2/repository/org/springframework/spring-jcl/6.1.3/spring-jcl-6.1.3.jar:/Users/jaeyoungpark/.m2/repository/org/springframework/spring-context/6.1.3/spring-context-6.1.3.jar:/Users/jaeyoungpark/.m2/repository/org/springframework/spring-aop/6.1.3/spring-aop-6.1.3.jar:/Users/jaeyoungpark/.m2/repository/org/springframework/spring-beans/6.1.3/spring-beans-6.1.3.jar:/Users/jaeyoungpark/.m2/repository/org/springframework/spring-expression/6.1.3/spring-expression-6.1.3.jar:/Users/jaeyoungpark/.m2/repository/io/micrometer/micrometer-observation/1.12.2/micrometer-observation-1.12.2.jar:/Users/jaeyoungpark/.m2/repository/io/micrometer/micrometer-commons/1.12.2/micrometer-commons-1.12.2.jar:/Users/jaeyoungpark/.m2/repository/org/springframework/boot/spring-boot-autoconfigure-processor/3.2.2/spring-boot-autoconfigure-processor-3.2.2.jar:/Users/jaeyoungpark/.m2/repository/org/springframework/boot/spring-boot-configuration-processor/3.2.2/spring-boot-configuration-processor-3.2.2.jar org.example.App
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.2.2)
[2024-03-17 08:36:51.742] - 25330 정보 [main] --- org.example.App: Starting App using Java 17.0.10 with PID 25330 (/Users/jaeyoungpark/Desktop/start_spring/self-di-test/target/classes started by jaeyoungpark in /Users/jaeyoungpark/Desktop/start_spring/self-di-test)
[2024-03-17 08:36:51.753] - 25330 정보 [main] --- org.example.App: No active profile set, falling back to 1 default profile: "default"
[2024-03-17 08:36:51.961] - 25330 정보 [main] --- org.example.App: Started App in 0.349 seconds (process running for 0.506)
DevOcean{groupName='JAVABACKEND', memberCount=200}스프링 부트의 3가지 주요 내용인 DI(Dipendency Injection), 자동설정, 추상화가 중에서 자동설정과 DI에 대해서 간단히 알아 봤습니다.
스프링 부트는 이러한 복잡한 설정 등을 최대한 자동화 해주고 또한 개발자가 설정을 재 정의할 수 있는 아주 많은 옵션을 제공해 주고 있습니다.
프로젝트 구성을 간단하게 정리해 봤습니다.
self-di 프로젝트
self-di-test 프로젝트
DEVOTEE를 활성화 시키면
지금 작성한 댓글에 AI가 댓글을 달아줍니다.