[springboot] 스프링 부트로 개발하기

2022. 8. 12. 17:21JAVA/Spring

본 내용은 Spring Boot Reference Documentation의 일부 내용을 번역하여 정리한 것입니다. 해당 내용은 원문 내용과 틀릴수도 있습니다.

 

1. 스프링부트로 개발하기

스프링부트로 개발할때 다음과 같은 기능에 대해서 알아봅니다.

  • 빌드 시스템(Build System)
  • 설정 클래스(Configuration Class)

 

1.1 빌드 시스템(Build System)

빌드 시스템은 다음 두 기능을 제공하는 시스템을 선택하는 것을 권장합니다.

  • 의존성 관리 지원
  • "Maven Central" 저장소에 배포된 라이브러리 사용 가능

 

1.1.1 의존성 관리(Dependency Management)

스프링 부트의 각 릴리스(release)는 지원되는 의존성 목록을 제공합니다. 실제로 당신의 빌드 설정에서 의존성들의 어떤 특정한 버전을 명시하지 않아도 됩니다. 왜냐하면 스프링 부트는 의존성들의 버전을 관리하기 때문입니다. 만약 당신이 스프링 부트 그자체를 업그레이드 할때 의존성들도 일관된 방식으로 업그레이드 될 것입니다.

 

NOTE : 만약 특정한 버전의 의존성을 사용하고 싶다면 버전을 명시할 수 있습니다. 

 

명세된 목록에는 스프링 부트와 함께 사용할 수 있는 모든 스프링 모듈과 서드파티 라이브러리의 자세한 목록이 포함되어 있습니다. 이 목록은 빌드 시스템인 메이븐(Maven)그레이들(Gradle) 모두 사용할 수 있는 표준 BOM(spring-boot-dependencies)으로 제공됩니다.

 

주의 : 스프링 부트의 각 릴리즈는 스프링 프레임워크의 기본버전과 연관되어 있기 때문에 버전을 지정하지 않는 것을 권장합니다.

 

1.1.2 스타터(Starters)

"Starter"라는 이름이 붙은 의존성 라이브러리는 당신의 애플리케이션에 포함할 수 있는 편리한 의존성 설명자 집합입니다.스타더 의존성 라이브러리를 사용하면 샘플 코드를 검색하거나 의존성 설명자를 복사하여 붙여넣을 필요없이 필요한 모든 스프링 및 관련 기술을 한곳에서 전부 얻을 수 있습니다. 예를 들어 만약 당신이 스프링과 데이터베이스 접근과 관련된 JPA를 시작하기를 원한다면 당신의 프로젝트에 의존성 목록에 "spring-boot-starter-data-jpa"를 포함시키면 됩니다.

 

이 스타터라고 이름 붙은 의존성들은 당신의 프로젝트 구성에 필요한 또다른 많은 의존성들을 포함합니다.

 

모든 공식적인 스타터들은 spring-boot-starter-*로 비슷한 패턴을 따릅니다.
서드파티 라이브러리들은 spring-boot로 시작하지 않습니다.

 

2. 코드 구조화(Structuring Your Code)

스프링부트가 작동하기 위해서 특정한 코드 구조를 요구하지는 않습니다. 그러나 도움을 줄 수 있는 몇몇 모범사례(best-practice)가 있습니다.

 

2.1 디폴트(default) 패키지를 사용하지 않기

클래스에 패키지 선언이 포함되지 않은 경우에 그 클래스는 디폴트 패키지로 간주합니다. 디폴트 패키지의 사용은 일반적으로 권장되지 않습니다. 디폴트 패키지의 사용은 모든 .jar 파일의 모든 클래스를 읽기 때문에 @ComponentScan, @ConfigurationPropertiesScan, @EntityScan, @SpringBootApplication 어노테이션을 사용하는 스프링부트 애플리케이션에 특정한 문제를 발생시킬 수 있습니다. 

 

TIP : 당신이 자바의 권장 패키지 이름 작성규칙과 "com.example.project"와 같은 예약된 도메인 이름을 사용하는 것을 권장합니다.

 

2.2 메인 애플리케이션 클래스(Main Application Class)의 위치는 루트 패키지에 위치시켜라

우리는 일반적으로 다른 클래스들 상위에 있는 루트 패키지에서 메인 애플리케이션 클래스를 위치시키는 것을 권장합니다. @SpringBootApplication 어노테이션이 메인 클래스에 배치되면 해당 메인 클래스가 위치한 패키지 포함 하위 패키지까지 특정한 어노테이션이 포함되어 있는지 탐색합니다. 예를 들어 만약 당신이 JPA 애플리케이션을 작성하고 있다면 @SpringBootApplication 어노테이션이 적용된 클래스의 패키지는 @Entity 어노테이션을 탐색하기 위해 사용됩니다.

 

TIP : 만약 @SpringBootApplication 사용하고 싶지 않다면 @EnableAutoConfiguration과 @ComponentScan 어노테이션을 메인 클래스에 적용시켜라. 

 

다음 리스트는 일반적인 프로젝트 패키지 구조를 보여줍니다.

com
 +- example
     +- myapplication
         +- MyApplication.java
         |
         +- customer
         |   +- Customer.java
         |   +- CustomerController.java
         |   +- CustomerService.java
         |   +- CustomerRepository.java
         |
         +- order
             +- Order.java
             +- OrderController.java
             +- OrderService.java
             +- OrderRepository.java

  위 구조에서 MyApplication.java 파일에 main 메서드를 정의하고 @SpringBootApplication 어노테이션을 다음과 같이 적용할 수 있습니다.

 

@SpringBootApplication
public class MyApplication {

    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }

}

 

3. 설정 클래스들(Configuration Classes)

스프링부트는 자바-기반 설정을 선호합니다. 스프링 애플리케이션을 XML 소스와 함께 사용할수도 있지만 일반적으로 기본소스는 단일 @Configuration 클래스로 사용하는 것이 좋습니다. 보통 메인 메서드가 정의된 클래스는 기본 @Configuration으로 적합합니다.

 

TIP : 많은 스프링 설정 예제들은 XML 설정을 사용하여 인터넷에 배포되어 있습니다. 가능하면 동일한 자바-기반 설정을 사용하는 것을 권장합니다. "Enable*" 애노테이션들의 검색은 좋은 출발점입니다.

 

@Configuration 어노테이션이란 무엇인가?

@Configuration 어노테이션은 설정파일을 만들기 위한 어노테이션 또는 빈(Bean)을 등록하기 위한 어노테이션입니다.

 

@Configuration 어노테이션의 장점

@Configuration 어노테이션을 사용하면 가시적으로 해당 클래스가 설정파일임을 알 수 있고 빈으로 등록될 거라는 것을 알 수 있습니다.

 

하지만 다음과 같이 스프링 빈으로 등록은 할 수 있습니다.

AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(xxx.class);

위와 같이 @Configuration 어노테이션을 클래스에 사용하지 않고 생성자 매개변수안에 적어주는 것만으로도 스프링 빈으로 등록은 할 수 있습니다. 하지만 @Configuration 어노테이션을 설정하지 않고 위와 같이 설정하면 해당 빈이 싱글톤이 되도록 보장하지 않기 때문에 객체 생성 비용이 낭비될 수 있습니다. 즉, @Configuration 어노테이션을 클래스에 적용하여 빈으로 등록하면 스프링 컨테이너에서 해당 빈을 싱글톤이 되도록 보장해주기 때문에 위와 같이 설정하는 것은 부적절합니다.

 

@Configuration 어노테이션의 기능

  • 설정한 클래스가 설정 파일이라는 것을 가시적으로 보여줌
  • 설정한 클래스를 스프링 컨테이너에 빈(Bean)으로 등록하고 해당 클래스를 싱글톤이 되도록 보장해줌

다음 코드는 메서드 내용이 동일한 설정 클래스 파일 2개를 보여줍니다. 두 클래스의 차이는 한 클래스는 @Configuration 어노테이션을 적용하고 다른 한쪽은 적용하지 않았습니다.

@Configuration
public class AppConfig {

    @Bean
    public void method1(){
        System.out.println("call AppConfig.method1");
    }

    @Bean
    public void method2(){
        System.out.println("call AppConfig.method2");
        method1();
    }



}
public class AppConfigTest {


    // @Configuration 사용하는 경우
    @Test
    public void configurationTest() throws Exception{
        //given
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);

        //when
        AppConfig appConfig = ac.getBean(AppConfig.class);

        //then
        System.out.println(appConfig);
    }
}

위 코드에서 AppConfig.class에는 두개의 @Bean이 존재합니다. 그러면 method1(), method2() 두개의 메서드를 호출하게 되면 AppConfig.method1, AppConfig.method2, AppConfig.method1 3개가 출력되지 않고 AppConfig.method1, AppConfig.method2 2개만이 출력되는 것을 볼 수 있습니다.

17:08:36.080 [Test worker] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'appConfig'
17:08:36.090 [Test worker] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'method1'
call AppConfig.method1
17:08:36.111 [Test worker] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'method2'
call AppConfig.method2
17:08:36.113 [Test worker] DEBUG org.springframework.context.annotation.ConfigurationClassEnhancer - @Bean method AppConfig.method1 called as bean reference for type [void] returned null bean; resolving to null value.

위와 같이 2개만 출력된 이유는 Bean을 만들때 싱글톤으로 생성했기 때문에 만들어진 객체는 다시 생성하지 않기 때문입니다.

 

만약 @Configuration 어노테이션을 뺀다면 AppConfig.method1, AppConfig.method2, AppConfig.method1 3개가 출력될 것입니다. 왜나하면 @Bean만 사용해도 스프링 빈으로 등록은 되지만 싱글톤이 유지되지는 않기 때문에 객체를 새로 생성하여 호출하게 됩니다.

17:14:36.316 [Test worker] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'appConfig'
17:14:36.348 [Test worker] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'method1'
call AppConfig.method1
17:14:36.357 [Test worker] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'method2'
call AppConfig.method2
call AppConfig.method1

 

또한 @Configuration 어노테이션을 적용한 설정 클래스의 객체를 출력했을때의 결과도 차이를 보입니다. 다음 결과는 AppConfig 설정 클래스에 @Configuration 어노테이션을 적용한 것과 제거한 상태에서 빈으로 받은 인스턴스를 출력한 결과입니다.

@Configuration 어노테이션 적용한 클래스의 인스턴스

com.yh.basic.AppConfig$$EnhancerBySpringCGLIB$$3c7028cf@61526469

@Configuration 어노테이션을 적용하지 않은 클래스의 인스턴스

com.yh.basic.AppConfig@57eda880

위 두 결과의 차이를 보면 @Configuration 어노테이션을 적용한 클래스의 인스턴스는 CGLIB라는게 붙은 걸 볼 수 있습니다. 이것은 내가 만든 클래스가 아닌 스프링에서 CGLIB라는 바이트코드 조작 라이브러리를 사용해서 AppConfig를 상속받은 임의의 클래스를 생성하고 그것을 스프링 빈으로 등록했기 때문에 이러한 결과가 나오는 것입니다. 이때 임의의 클래스를 통해서 싱글톤이 되는 것을 유지해줍니다.

 

3.1 추가적인 설정 클래스들 가져오기

@Configuration 어노테이션을 모든 단일 클래스에 넣을 필요는 없습니다. @Import 애노테이션은 추가적인 설정 클래스들을 가져오는데 사용될 수 있습니다. 대체적으로 @Configuration 클래스들이 포함된 모든 스프링 컴포넌트들을 자동적으로 가져올 수 있도록 @ComponentScan 어노테이션을 사용할 수 있습니다.

 

3.2 XML 설정 가져오기

만약 XML 기반 설정을 사용해야 하는 경우에도 @Configuration 어노테이션 클래스로 시작하는 것을 권장합니다. XML 설정 파일들을 불러오기 위해서 @ImportResource 어노테이션을 사용할 수 있습니다.

 

 

References

Spring Boot Reference Documentation
[Spring] @Configuration 개념과 장점