관리 메뉴

나구리의 개발공부기록

자동 구성(Auto Configuration), 프로젝트 설정 및 예제 만들기, 자동 구성 확인, 스프링 부트의 자동 구성, 자동 구성 직접만들기 - 기반 예제, @Conditional, @Conditional - 다양한 기능 본문

인프런 - 스프링 완전정복 코스 로드맵/스프링 부트 - 핵심 원리와 활용

자동 구성(Auto Configuration), 프로젝트 설정 및 예제 만들기, 자동 구성 확인, 스프링 부트의 자동 구성, 자동 구성 직접만들기 - 기반 예제, @Conditional, @Conditional - 다양한 기능

소소한나구리 2024. 12. 2. 15:10

출처 : 인프런 - 스프링 부트 - 핵심 원리와 활용(유료) / 김영한님  
유료 강의이므로 정리에 초점을 두고 코드는 일부만 인용


1. 프로젝트 설정 및 예제 만들기

1) 프로젝트 설정

(1) 프로젝트 설정 순서

  • 제공된 프로젝트를 사용하였으나 build.gradle을 확인하여 직접 생성해도 상관없음

(2) build.gradle 확인

  • 스프링 부트에서 롬복, 스프링 웹, H2 DB, JDBC API 라이브러리를 선택하여 생성
  • 테스트 코드에서 롬복을 사용할 수 있도록 설정 추가
plugins {
    id 'org.springframework.boot' version '3.0.2'
    id 'io.spring.dependency-management' version '1.1.0'
    id 'java'
}

group = 'hello'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-jdbc'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    compileOnly 'org.projectlombok:lombok'
    runtimeOnly 'com.h2database:h2'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'

    //테스트에서 lombok 사용
    testCompileOnly 'org.projectlombok:lombok'
    testAnnotationProcessor 'org.projectlombok:lombok'
}

tasks.named('test') {
    useJUnitPlatform()
}

2) 예제 만들기

  • JdbcTemplate을 사용하여 회원 데이터를 DB에 저장하고 조회하는 간단한 기능의 예제

(1) Member

  • member패키지 생성하여 작성
  • memberId, name 필드가 있는 회원 객체
  • 기본 생성자와 member, name을 포함하는 생성자가 있으며 롬복의 @Data를 활용
package hello.member;

@Data
public class Member {
    private String memberId;
    private String name;

    public Member() {}

    public Member(String memberId, String name) {
        this.memberId = memberId;
        this.name = name;
    }
}

 

(2) DbConfig

  • config패키지를 생성하여 작성
  • JdbcTemplate을 사용하여 회원 데이터를 DB에 보관하고 관리할 수 있도록 DataSource, TransactionManager, JdbcTemplate을 스프링빈으로 직접 등록
  • DB는 별도의 외부 DB가 아니라 JVM 내부에서 동작하는 메모리  DB를 사용
  • JdbcTransactionManager는 DataSourceTransactionManager의 업그레이드 버전으로 같은 기능에 예외 변환 기능이 보강된 것임
package hello.config;

@Slf4j
@Configuration
public class DbConfig {

    @Bean
    public DataSource dataSource() {
        log.info("dataSource 빈 등록");
        HikariDataSource dataSource = new HikariDataSource();
        dataSource.setDriverClassName("org.h2.Driver");
        dataSource.setJdbcUrl("jdbc:h2:mem:test");  // 메모리모드로 사용
        dataSource.setUsername("sa");
        dataSource.setPassword("");
        return dataSource;
    }

    @Bean
    public TransactionManager transactionManager() {
        log.info("transactionManager 빈 등록");
        return new JdbcTransactionManager(dataSource());
    }

    @Bean
    public JdbcTemplate jdbcTemplate() {
        log.info("jdbcTemplate 빈 등록");
        return new JdbcTemplate(dataSource());
    }
}

 

(3) MemberRepository

  • member패키지에 작성
  • JdbcTemplate을 사용하여 회원을 관리하는 리포지토리
  • DbConfig에서 JdbcTemplate을 빈으로 등록했기 때문에 바로 주입받아서 사용할 수 있음
  • initTable: 보통 리포지토리에 테이블을 생성하는 스크립트를 두지는 않지만 예제를 단순화 하기 위해서 사용하였음
package hello.member;

@Repository
public class MemberRepository {

    public final JdbcTemplate template;

    public MemberRepository(JdbcTemplate template) {
        this.template = template;
    }

    // 예제를 간단하기 하기위해 테이블 생성 코드를 작성, 보통은 create 문을 직접 작성하지 않음
    public void initTable() {
        template.execute("create table member(member_id varchar primary key, name varchar)");
    }

    // 실무에서는 NamedParameterJdbcTemplate 사용
    public void save(Member member) {
        template.update("insert into member(member_id, name) values (?,?)",
                member.getMemberId(),
                member.getName());
    }

    public Member find(String memberId) {
        return template.queryForObject("select member_id, name from member where member_id=?",
                BeanPropertyRowMapper.newInstance(Member.class),
                memberId);
    }

    public List<Member> findAll() {
        return template.query("select member_id, name from member",
                BeanPropertyRowMapper.newInstance(Member.class));
    }
}

 

(4) MemberRepositoryTest

  • MemberRepository의 기능을 테스트하는 코드
  • @Transactional을 사용하여 트랜잭션 기능을 적용(해당 기능을 사용하려면 TransactionManager가 스프링 빈으로 등록되어있어야 함)
  • 테이블을 생성하고 조회해서 기존 데이터와 같은지 간단히 검증
  • 실행해보면 테스트가 성공하고 로그에 DbConfig가 정상적으로 동작하며 히카리 풀도 정상적으로 동작하는 것을 확인할 수 있음
package hello.member;

@SpringBootTest
class MemberRepositoryTest {

    @Autowired MemberRepository memberRepository;

    @Transactional
    @Test
    void memberTest() {
        Member member = new Member("idA", "memberA");
        memberRepository.initTable();
        memberRepository.save(member);
        Member findMember = memberRepository.find(member.getMemberId());
        assertThat(findMember.getMemberId()).isEqualTo(member.getMemberId());
        assertThat(findMember.getName()).isEqualTo(member.getName());
        
        memberRepository.save(member2);
        List<Member> members = memberRepository.findAll();
        assertThat(members.size()).isEqualTo(2);
        assertThat(members).contains(member1, member2);
    }

}

 

(5) 정리

  • 회원 데이터를 DB에 보관하고 관리하기 위해 직접 빈으로 등록한 JdbcTemplate, DataSource, TransactionManager들이 모두 사용되었음
  • DB에 데이터를 보관하고 관리하기 위해 이런 객체들을 스프링 빈으로 등록해야하는 번거로움이 있는데, 만약 DB를 사용하는 다른 프로젝트를 진행한다면 이러한 객체들을 계속 스프링 빈으로 등록해주는 반복작업이 필요함
  • 이를 스프링 부트가 해결해줌

2. 자동 구성 확인

1) 스프링 빈 등록 확인

(1) DbConfigTest

  • JdbcTemplate, DataSource, TransactionManager가 스프링 컨테이너에 잘 등록되었는지 확인
  • 해당 빈들을 DbConfig설정을 통해 스프링 컨테이너에 등록했기 때문에 null이면 안되며 사실 @Autowired는 의존관계에 주입에 실패하면 오류가 발생하도록 기본 설정이 되어있음(이해를 돕기 위해 테스트 코드를 작성한 것임)
  • 실행해보면 테스트가 정상적으로 모두 통과되고 의존관계 주입이 정상처리되어 로그들이 모두 출력된 것을 확인할 수 있음
package hello.config;

@Slf4j
@SpringBootTest
class DbConfigTest {

    @Autowired DataSource dataSource;   // javax.sql 꺼를 사용해야함
    @Autowired TransactionManager transactionManager;
    @Autowired JdbcTemplate jdbcTemplate;

    @Test
    void checkBean() {
        log.info("dataSource = {}", dataSource);
        log.info("transactionManager = {}", transactionManager);
        log.info("jdbcTemplate = {}", jdbcTemplate);

        assertThat(dataSource).isNotNull();
        assertThat(transactionManager).isNotNull();
        assertThat(jdbcTemplate).isNotNull();
    }
}

2) 빈 등록 제거

(1) DbConfig 수정

  • DbConfig에서 빈 등록을 제거하는 두가지 방법: @Configuration을 주석처리, @Bean을 주석처리
  • @Configuration을 주석처리하면 해당 설정 파일 자체가 스프링이 컴포넌트 스캔의 대상이 아니게 되고 @Bean을 주석처리하면 해당 코드가 스프링 빈으로 등록하지 않음
  • 여기서는 간단히 @Configuration을 주석처리
@Slf4j
//@Configuration
public class DbConfig {
    // ... 기존 코드 동일
}

 

(2) DbConfigTest 재실행

  • checkBean 테스트가 정상적으로 통과되지만 기존에 수동빈으로 작성했던 빈 등록 로그는 보이지 않음
  • 심지어 출력 로그에 JdbcTemplate와 DataSource, TransactionManager의 빈들이 존재하는 것이 로그로 확인되는데, 사실 이 빈들은 모두 스프링 부트가 자동으로 등록해준 것임

3. 스프링 부트의 자동 구성

1) 자동 구성 살짝 알아보기

(1) 스프링 설정 지옥 해결

  • 스프링 부트는 자동 구성(Auto Configuration)이라는 기능을 제공하는데, 설정 지옥에 빠져 일반적으로 자주 사용하는 수 많은 빈들을 직접 등록했던 것을 자동으로 등록해주는 기능임
  • 자동 구성 덕분에 개발자는 반복적이고 복잡한 빈 등록과 설정을 최소화 하고 애플리케이션 개발을 빠르게 시작할 수 있게됨
  • 스프링 부트는 spring-boot-autoconfigure라는 프로젝트 안에서 수 많은 자동 구성을 제공하며 스프링 부트 스타터라이브러리를 사용하면 해당 라이브러리 안에 autoconfigure가 들어있음

** 주의!

  • 여기서 모든 것을 깊이있게 이해하지 않고 대략 어떻게 동작하는지 감을 잡을 수 있는 정도면 충분함
  • 진행하면서 자동 구성과 관련된 부분들을 단계적으로 풀어나감

(2) JdbcTemplateAutoConfiguration

  • @AutoConfiguration: 자동 구성을 사용하려면 해당 애노테이션을 등록해야함
    자동 구성도 내부에 @Configuration이 있어서 빈을 등록하는 자바 설정 파일로 사용할 수 있음
  • after = DataSourceAutoConfiguration.class: 자동 구성이 실행되는 순서를 지정할 수 있는데, JdbcTemplate은 DataSource가 필요하기 때문에 DataSource를 자동으로 등록해주는 DataSourceAutoConfiguration.Class 다음에 실행하도록 설정 되어 있음
  • @ConditionalOnClass({DataSource.class, JdbcTemplate.class}): IF문과 유사한 기능으로 이런 클래스가 있는 경우에만 설정이 동작하도록 되어있음
    JdbcTemplate은 DataSource, JdbcTemplate라는 클래스가 있어야 동작할 수 있으므로 만약 없으면 여기 있는 설정들이 모두 무효화되고 빈도 등록되지 않음
  • @ConditionalOnXxx 시리즈: 자동 구성의 핵심이므로 뒤에서 자세히 설명
  • @Import: 스프링에서 자바 설정을 추가할 때 사용함
@AutoConfiguration(after = {DataSourceAutoConfiguration.class})
@ConditionalOnClass({DataSource.class, JdbcTemplate.class})
@ConditionalOnSingleCandidate(DataSource.class)
@EnableConfigurationProperties({JdbcProperties.class})
@Import({DatabaseInitializationDependencyConfigurer.class, JdbcTemplateConfiguration.class, NamedParameterJdbcTemplateConfiguration.class})
public class JdbcTemplateAutoConfiguration {
    public JdbcTemplateAutoConfiguration() { }
}

 

(3) JdbcTemplateConfiguration

  • JdbcTemplateAutoConfiguration의 @Import의 대상인 JdbcTemplateConfiguration도 함께 살펴보면 익히 진행해 봤던 수동 빈을 등록하던 형태를 볼 수 있음
  • @Configuration: 자바 설정 파일로 사용됨
  • @ConditionalOnMissingBean(JdbcOperations.class): JdbcOperations 빈이 없을 때 동작하도록 설정
  • JdbcTemplate의 부모인터페이스가 바로 JdbcOperations인데 쉽게 이야기하면 JdbcTemplate이 빈으로 등록되어 있지 않는 경우에만 동작하도록 되어있음
  • 만약 이런 기능이 없다면 내가 직접 등록한 JdbcTemplate과 자동 구성이 등록하는 JdbcTemplate이 중복 등록되는 문제가 발생할 수 있으므로 개발자가 특별한 사정에 의해 직접 빈을 등록하면 개발자가 등록한 빈을 사용하고 자동 구성은 동작하지 않음
  • 자동 구성이 동작하면 JdbcTemplate이 몇가지 설정을 거쳐서 빈으로 등록되는 것을 확인할 수 있음
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean({JdbcOperations.class})
class JdbcTemplateConfiguration {
    JdbcTemplateConfiguration() {
    }

    @Bean
    @Primary
    JdbcTemplate jdbcTemplate(DataSource dataSource, JdbcProperties properties) {
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
        JdbcProperties.Template template = properties.getTemplate();
        jdbcTemplate.setFetchSize(template.getFetchSize());
        jdbcTemplate.setMaxRows(template.getMaxRows());
        if (template.getQueryTimeout() != null) {
            jdbcTemplate.setQueryTimeout((int)template.getQueryTimeout().getSeconds());
        }

        return jdbcTemplate;
    }
}

 

(4) 자동 등록 설정

  • 다음과 같은 자동 구성 기능들이 다음 빈들을 등록해줌
  • JdbcTemplateAutoConfiguration: JdbcTemplate
  • DataSourceAutoConfiguration: DataSource
  • DataSourceTransactionManagerAutoConfiguration: TransactionManager
  • 그래서 개발자가 직접 빈을 등록하지 않아도 JdbcTemplate, DataSource, TransactionManager가 스프링 빈으로 등록된 것임

(5) 스프링 부트가 제공하는 자동 구성(AutoConfiguration)

  • 공식 링크
  • 스프링 부트는 수 많은 자동 구성을 제공하고 spring-boot-autoconfigure에 자동 구성을 모아둠
  • 스프링 부트 프로젝트를 사용하면 spring-boot-autoconfigure라이브러리는 기본적으로 사용됨

(6) 자동 구성 기능의 두가지 개념

  • 스프링 부트가 제공하는 자동 구성 기능을 이해하려면 다음 두 가지 개념을 이해해야함
  • @Conditional: 특정 조건에 맞을 때 설정이 동작하도록 함
  • @AutoConfiguration: 자동 구성이 어떻게 동작하는지 내부 원리 이해

2) Auto Configuration 용어, 자동 설정? 자동 구성?

  • 보통 두 용어로 번역되어 사용됨

(1) 자동 설정

  • Configuration이라는 단어가 컴퓨터 용어에서는 환경 설정 즉, 적성이라는 뜻으로 자주 사용됨
  • AutoConfiguratin은 크게 보면 빈들을 자동으로 등록하여 스프링이 등록하는 환경을 자동으로 설정해주기 때문에 자동설정이라는 용어도 맞음

(2) 자동 구성

  • Configuration이라는 단어는 구성, 배치라는 뜻도 있음
  • 예를 들어 컴퓨터에서 CPU, 메모리등을 배치해야 컴퓨터가 동작하는데 이렇게 배치하는 것을 구성이라하는데, 스프링도 스프링 실행에 필요한 빈들을 적절하게 배치해야 함
  • 자동 구성은 스프링 실행에 필요한 빈들을 자동으로 배치해주는 것임

(3) 정리

  • 즉, 자동 설정, 자동 구성 두 용어 모두 맞다고 볼 수 있으며 자동 설정은 넓게 사용되는 의미이고, 자동 구성을 실행에 필요한 컴포넌트 조각을 자동으로 배치한다는 더 좁은 의미에 가깝다고 볼 수 있음
  • 강의에서는 Auto Configuration은 자동 구성이라는 단어로 주로 사용하고, Configuration을 단독으로 사용될 때는 설정이라는 단어로 주로 사용함

4. 자동 구성 직접만들기 - 기반 예제

1) 기반 예제 작성

  • 자동 구성에 대해 자세히 알아보기 위한 실시간으로 자바 메모리 사용랴을 웹으로 확인하는 간단한 예제 작성

(1) Memory

  • 사용중인 메모리를 뜻하는 used와 최대 메모리를 뜻하는 max를 가지고있는 클래스
  • used가 max를 넘게되면 메모리 부족 오류가 발생하도록 할 예정

** 주의

  • hello 패키지 하위가 아닌 java하위(hello상위)에 memory패키지를 생성 후 작성
  • 이후에 다른곳으로 편하게 옮기기 위해 작성(hello와는 전혀 다른 기능을 가짐)
package memory;

public class Memory {
    private long used;
    private long max;

    public Memory(long used, long max) {
        this.used = used;
        this.max = max;
    }

    public long getUsed() {
        return used;
    }

    public long getMax() {
        return max;
    }

    @Override
    public String toString() {
        return "Memory{" +
                "used=" + used +
                ", max=" + max +
                '}';
    }
}

 

(2) MemoryFinder

  • JVM에서 메모리 정보를 실시간으로 조회하는 기능
  • max: JVM이 사용할 수 있는 최대 메모리, 이 수치를 넘어가면 OOM이 발생함
  • total: JVM이 확보한 전체 메모리(JVM은 처음부터 max까지 다 확보하지 않고 필요할 때 마다 조금씩 확보함)
  • free: total중에 사용하지 않은 메모리(JVM이 확보한 전체 메모리 중에 사용하지 않은 것)
  • used: JVM이 사용중인 메모리를 계산
package memory;

@Slf4j
public class MemoryFinder {

    public Memory get() {
        long max = Runtime.getRuntime().maxMemory();        // max
        long total = Runtime.getRuntime().totalMemory();    // total
        long free = Runtime.getRuntime().freeMemory();      // free
        long used = total - free;                           // used
        return new Memory(used, max);
    }

    @PostConstruct
    public void init() {
        log.info("init memoryFinder");
    }
}

 

(3) MemoryController

  • 메모리 정보를 조회하는 컨트롤러
package memory;

@Slf4j
@RestController
@RequiredArgsConstructor
public class MemoryController {

    private final MemoryFinder memoryFinder;

    @GetMapping("/memory")
    public Memory system() {
        Memory memory = memoryFinder.get();
        log.info("memory={}", memory);
        return memory;
    }
}

 

(4) MemoryConfig

  • 설정파일이므로 설정파일을 모아둔 hello.config에 작성
  • memoryController, memoryFinder를 빈으로 등록
package hello.config;

import memory.MemoryController;
import memory.MemoryFinder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MemoryConfig {
    
    @Bean
    public MemoryController memoryController() {
        return new MemoryController(memoryFinder());
    }
    
    @Bean
    public MemoryFinder memoryFinder() {
        return new MemoryFinder();
    }
}

 

(5) 애플리케이션 실행

  • 실행 후 localhost:8080/memory에 접속해보면 메모리 사용량을 json 형태로 실시간으로 확인할 수 있음

5. @Conditional

1) @Conditional 설명

(1) 적용 상황

  • 앞서 만든 메모리 조회 기능을 항상 사용하는 것이 아니라 특정 조건일 때만 해당 기능이 활성화 되도록 한다고 가정
  • 여기서의 핵심은 소스 코드를 고치지 않고 이런 것이 가능해야한다는 점이며, 프로젝트를 빌드해서 나온 동일한 빌드 파일을 개발 서버에도 배포하고, 운영서버에도 배포해야 함
  • ex) 개발 서버에서 확인 용도로만 해당 기능을 사용하고 운영 서버에서는 해당 기능을 사용하지 않을 때 등등...
  • 같은 소스 코드인데 특정 상황일 때만 특정 빈들을 등록해서 사용하도록 도와주는 기능이 바로 @Conditional이며 이 기능은 스프링 부트 자동 구성에서 자주 사용함
  • 이름 그대로 특정 조건을 만족하는가, 만족 하지 않는가를 구별하는 기능임

(2) Condition 살펴보기

  • @Conditional 기능을 사용하려면 먼저 Condition인터페이스를 구현해야 함
  • matches()메서드가 true를 반환하면 조건에 만족해서 동작하고, false를 반환하면 동작하지 않음
  • ConditionContext: 스프링 컨테이너, 환경 정보등을 담고 있음
  • AnnotatedTypeMetadata: 애노테이션 메타 정보를 담고 있음
package org.springframework.context.annotation;

import org.springframework.core.type.AnnotatedTypeMetadata;

@FunctionalInterface
public interface Condition {
    boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}

2) 구현

(1) 구현 목적

  • Condition인터페이스를 구현해서 자바 시스템 속성이 memory=on 이라고 되어 있을 때만 메모리 기능이 동작하도록 작성
#VM Options
#java -Dmemory=on -jar project.jar

 

(2) MemoryCondition

  • 상속받는 Condition의 패키지명 주의
  • 환경 정보에 memory=on이라고 되어있는 경우에만 true를 반환하도록 설정
  • 환경 정보와 관련된 부분은 뒤에서 아주 자세히 다룰 예정임
package memory;

@Slf4j
public class MemoryCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        // -Dmemory=on
        String memory = context.getEnvironment().getProperty("memory");
        log.info("memory={}", memory);
        return "on".equals(memory);
    }
}

 

(3) MemoryConfig - 수정

  • @Conditional(MemoryCondition.class): 해당 애노테이션의 적용으로 MemoryConfig의 적용 여부는 @Conditional에 지정한 MemoryCondition의 조건에 따라 달라짐
  • MemoryCondition의 matches()를 실행해보고 그 결과가 true이면 MemoryConfig는 정상 동작하여 작성된 빈들이 등록됨
  • MemoryCondition의 matches()의 실행결과가 false이면 MemoryConfig는 무효화가되며 빈들도 등록되지 않음
@Configuration
@Conditional(MemoryCondition.class) // 추가
public class MemoryConfig {
    // @Bean 코드들은 동일
}

 

(4) 그냥 실행

  • 아무 조건을 주지 않고 실행 후 localhost:8080/memory에 접속하면 오류페이지가 뜨는데, memory=on설정을 하지 않았으므로 MemoryConfig의 빈들이 등록되지 않았기 때문임
  • MemoryCondition에서 출력된 로그를 보면 memory=null이 출력되어 결과가 false가 되는 것을 확인할 수 있음

(5) 옵션 주고 실행

  • Run/Debug Configurations -> MOdify options -> Java의 Add VM options 활성화 -> -Dmemory=on 입력
  • 다시 실행하여 url에 들어가보면 정상상적으로 실시간 메모리 사용량이 출력됨
  • 출력 로그를 보면 memory=on이 출력고 init memoryFinder 로그도 출력되어 정상적으로 MemoryFinder가 동작하는 것을 확인할 수 있음

 

** 참고

  • 스프링이 로딩 되는 과정은 복잡하여 MemoryCondition이 여러번 호출될 수 있는데 이 부분은 크게 중요하지 않으므로 넘어가도 됨
  • 스프링은 외부 설정을 추상화하여 Environment로 통합해 다양한 외부 환경 설정을 Environment하나로 읽어 들일 수 있음(getEnvironment() 사용), 해당 내용은 매우 중요하고 자주 사용하므로 뒤에서 자세히 다룸
#VM Options
#java -Dmemory=on -jar project.jar
-Dmemory=on

#Program arguments
# -- 가 있으면 스프링이 환경 정보로 사용
#java -jar project.jar--memory=on
--memory=on

#application.properties
#application.properties에 있으면 환경 정보로 사용
memory=on

6. @Conditional - 다양한 기능

1) 스프링이 제공하는 구현체

(1)MemoryConfig - 수정

  • 직접 만든 @Conditional(...)를 주석 처리
  • @ConditionalOnProperty(name = "memory", havingValue = "on")을 추가하면 환경 정보가 memory=on이라는 조건에 맞으면 동작하고 그렇지 않으면 동작하지 않음
  • 기존에 memory=on 설정을 해두었기 때문에 다시 애플리케이션을 실행해보변 정상적으로 기능이 동작하는 것을 확인할 수 있음
@Configuration
//@Conditional(MemoryCondition.class)
@ConditionalOnProperty(name = "memory", havingValue = "on") // 추가
public class MemoryConfig {
    // @Bean 등록 코드는 유지
}

 

(2) @ConditionalOnProperty 구조 일부

  • @ConditionalOnProperty의 내부를 보면 직접 만든 것과 동일하게 내부에 @Conditional을 가지고 있음
  • 그리고 그 안에 Condition인터페이스를 구현한 OnPropertyCondition을 가지고 있음
// 다른 애노테이션 생략
@Conditional({OnPropertyCondition.class})
public @interface ConditionalOnProperty {
   // ... 다른 코드 생략
}

2) @ConditionalOnXxx

(1) 설명

  • 스프링은 @Conditional과 견련하여 개발자가 편리하게 사용할 수 있도록 수 많은 @ConditionalOnXxx를 제공함
  • 공식 메뉴얼
  • 이름이 직관적이기 때문에 바로 이해가 될 것이며 @ConditionalOnXxx는 주로 스프링 부트 자동 구성에 사용됨
  • JdbcTemplateAutoConfiguration, DataSourceTransactionManagerAutoConfiguration, DataSourceAutoConfiguration 등의 자동 구성 클래스들을 열어서 소스 코드를 확인해보면 @ConditionalOnXxx가 아주 많이 사용되는 것을 확인할 수 있음

(2) @ConditionalOnClass, @ConditionalOnMissingClass

  • 클래스가 있는 경우 동작
  • 나머지는 그 반대

(3) @ConditionalOnBean, @ConditionalOnMissingBean

  • 빈이 등록되어 있는 경우 동작
  • 나머지는 그 반대

(4) @ConditionalOnProperty

  • 환경 정보가 있는 경우 동작

(5) @ConditionalOnResource

  • 리소스가 있는 경우 동작

(6) @ConditionalOnWebApplication, @ConditionalOnNotWebApplication

  • 웹 애플리케이션인 경우 동작

(7) @ConditionalOnExpression

  • SpEL 표현식에 만족하는 경우 동작

** 참고

  • @Conditional 자체는 스프링 부트가 아니라 스프링 프레임 워크의 기능임(원래는 스프링 부트꺼였는데 스프링으로 넘어감)
  • 스프링 부트는 이 기능을 확장해서 @ConditionalOnXxx를 제공함

'인프런 - 스프링 완전정복 코스 로드맵 > 스프링 부트 - 핵심 원리와 활용' 카테고리의 다른 글

외부설정과 프로필1, 프로젝트 설정 및 외부 설정이란?, 외부 설정(OS 환경 변수/자바 시스템 속성/커맨드 라인 인수/커맨드 라인 옵션 인수/커맨드 라인 옵션 인수와 스프링 부트/스프링 통합), 설정 데이터(외부 파일/내부 파일 분리/내부 파일 합체), 우선순위(설정 데이터/전체)  (1) 2024.12.05
자동 구성(Auto Configuration), 순수 라이브러리 만들기, 순수 라이브러리 사용하기, 자동 구성 라이브러리 만들기, 자동 구성 라이브러리 사용하기, 자동 구성 이해(스프링 부트의 동작/ImportSelector)  (1) 2024.12.02
스프링 부트 스타터와 라이브러리 관리, 라이브러리 직접 관리, 스프링 부트 라이브러리 버전 관리, 스프링 부트 스타터  (0) 2024.11.21
스프링 부트와 내장 톰캣, WAR 배포 방식의 단점, 내장 톰캣(설정/서블릿/스프링/빌드와 배포), 편리한 부트 클래스 만들기, 스프링 부트와 웹 서버(프로젝트 생성/실행 과정/ 빌드와 배포), 스프링 부트 실행 가능 JAR  (1) 2024.11.20
웹 서버와 서블릿 컨테이너, 웹서버와 스프링 부트 소개, 톰캣 설치, 프로젝트 설정, WAR 빌드와 배포, 톰캣 설정(인텔리제이 무료/유료), 서블릿 컨테이너 초기화, 스프링 컨테이너 등록, 스프링 MVC 서블릿 컨테이너 초기화 지원  (2) 2024.11.18