[Spring with Docs] Spring Boot가 데이터베이스를 읽어오는 방법을 공식 문서를 통해 알아보자

Jihoon Oh·2022년 4월 25일
3

Spring with Docs

목록 보기
1/2

스프링 부트는 자동으로 내장 데이터베이스를 생성한다.

Spring Boot can auto-configure embedded H2, HSQL, and Derby databases. You need not provide any connection URLs. You need only include a build dependency to the embedded database that you want to use. If there are multiple embedded databases on the classpath, set the spring.datasource.embedded-database-connection configuration property to control which one is used. Setting the property to none disables auto-configuration of an embedded database.

스프링 부트는 내장된 H2, HSQL, Derby 데이터베이스를 자동으로 구성할 수 있어 연결 URL을 제공할 필요가 없다. 단지 사용하고자 하는 내장 데이터베이스의 의존성을 추가해주기만 하면 된다. 만약 클래스경로에 여러개의 내장 데이터베이스가 있다면, spring.datasource.embadded-database-connection property를 설정하여 사용할 데이터베이스를 제어할 수 있다. 해당 설정 값을 none으로 지정하면 내장 데이터베이스 자동 설정을 비활성화 할 수 있다.

스프링 부트는 내장 데이터베이스가 있어 해당 의존성을 build.gradle과 같은 의존성 관리에 추가하는 것으로 자동으로 데이터베이스를 구성할 수 있다. 물론 이 자동 구성은 설정 값을 변경해주는 것으로 비활성화도 가능하다. 그렇다면 내장 데이터베이스가 아닌 다른 데이터베이스를 사용하고 싶다면 어떻게 하면 될까?

You should at least specify the URL by setting the spring.datasource.url property. Otherwise, Spring Boot tries to auto-configure an embedded database.

최소한 spring.datasource.url property에 특정한 URL을 지정해 주어야 한다. 그렇지 않으면 스프링 부트는 자동으로 내장 데이터베이스를 구성하려고 시도한다.

정리하자면 URL이 명시되어 있으면 해당 URL로 커넥션을 연결하고, URL이 명시되어 있지 않다면 자동으로 내장 데이터베이스를 생성한다.

기본 내장 데이터베이스의 주소는 무작위로 결정된다.

그런데 URL을 명시하지 않아서 자동으로 내장 데이터베이스를 구성할 때 URL은 어떻게 되는 걸까? 따로 지정되는 특정한 URL이 있을까?

@SuppressWarnings("deprecation")
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@ConditionalOnMissingBean(type = "io.r2dbc.spi.ConnectionFactory")
@AutoConfigureBefore(SqlInitializationAutoConfiguration.class)
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({ DataSourcePoolMetadataProvidersConfiguration.class,
		DataSourceInitializationConfiguration.InitializationSpecificCredentialsDataSourceInitializationConfiguration.class,
		DataSourceInitializationConfiguration.SharedCredentialsDataSourceInitializationConfiguration.class })
public class DataSourceAutoConfiguration {

	@Configuration(proxyBeanMethods = false)
	@Conditional(EmbeddedDatabaseCondition.class)
	@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
	@Import(EmbeddedDataSourceConfiguration.class)
	protected static class EmbeddedDatabaseConfiguration {

	}
    ...
}

DataSource를 설정해주는 DataSourceAutoConfiguration 빈을 보면, DataSource.classXADataSource.class 빈을 찾지 못할 경우, EmbeddedDataSourceConfiguration를 바탕으로 빈을 등록한다.

@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(DataSourceProperties.class)
public class EmbeddedDataSourceConfiguration implements BeanClassLoaderAware {

	private ClassLoader classLoader;

	@Override
	public void setBeanClassLoader(ClassLoader classLoader) {
		this.classLoader = classLoader;
	}

	@Bean(destroyMethod = "shutdown")
	public EmbeddedDatabase dataSource(DataSourceProperties properties) {
		return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseConnection.get(this.classLoader).getType())
				.setName(properties.determineDatabaseName()).build();
	}

}

EmbeddedDataSourceConfiguration 클래스 안에는 EmbeddedDatabase를 빈으로 등록하는 dataSource 메서드가 있는데, 주입된 DataSourceProperties의 determineDatabaseName 메서드를 통해 데이터베이스의 이름을 결정한다.

@ConfigurationProperties(prefix = "spring.datasource")
public class DataSourceProperties implements BeanClassLoaderAware, InitializingBean {
    ...
    public String determineDatabaseName() {
		if (this.generateUniqueName) {
			if (this.uniqueName == null) {
				this.uniqueName = UUID.randomUUID().toString();
			}
			return this.uniqueName;
		}
		if (StringUtils.hasLength(this.name)) {
			return this.name;
		}
		if (this.embeddedDatabaseConnection != EmbeddedDatabaseConnection.NONE) {
			return "testdb";
		}
		return null;
	}
    ...
}

DataSourceProperties는 property 파일의 spring.datasource 부분을 읽어오게 되는데, application.property에 데이터베이스 URL과 이름을 지정하지 않았기 때문에 uniqueName은 null 값이 된다. 따라서 uniqueName은 자동으로 UUID가 제공하는 무작위 이름으로 설정된다.

때문에 EmbeddedDataBaseBuilder 안의 databaseFactory 필드 (여기서는 EmbeddedDatabaseFactory)에 무작위 이름이 저장된다. 그리고 build 메서드를 통해 EmbeddedDatabaseFactory가 DataSource를 만들어서 빈으로 등록하게 된다.

결과적으로, URL 및 이름을 명시하지 않을 경우 매 번 스프링 부트가 올라갈 때 마다 새로운 이름의 데이터베이스가 생성된다. 따라서 만약 여러 개의 SpringBootTest를 한번에 돌리는 경우가 있을 때, 테스트들끼리 같은 데이터베이스를 공유하는 문제가 생기지 않는다.

스프링 부트는 자동으로 schema.sql과 data.sql을 읽어온다.

Spring Boot can automatically create the schema (DDL scripts) of your DataSource and initialize it (DML scripts). It loads SQL from the standard root classpath locations: schema.sql and data.sql, respectively. In addition, Spring Boot processes the schema-${platform}.sql and data-${platform}.sql files (if present), where platform is the value of spring.datasource.platform. This allows you to switch to database-specific scripts if necessary. For example, you might choose to set it to the vendor name of the database (hsqldb, h2, oracle, mysql, postgresql, and so on).

스프링 부트는 자동으로 DataSource의 스키마(DDL 스크립트)를 생성하고 초기화(DML 스크립트)할 수 있다. 표준 루트 클래스 경로 위치(각각 schema.sql 및 data.sql)에서 SQL을 로드한다. 또한 Spring Boot는 schema-${platform}sql 및 data-${platform}sql 파일(파일이 있는 경우에)을 처리한다. 여기서 platform은 spring.datasource.platform의 값이다. 필요한 경우 데이터베이스별 스크립트로 전환할 수 있다. 예를 들어 데이터베이스의 벤더 이름(hsqldb, h2, oracle, mysql, postgresql 등)으로 설정할 수 있다.

Spring Boot automatically creates the schema of an embedded DataSource. This behavior can be customized by using the spring.datasource.initialization-mode property. For instance, if you want to always initialize the DataSource regardless of its type:

spring.datasource.initialization-mode=always

스프링 부트는 자동으로 내장 DataSource의 스키마를 생성한다. 이는 property의 spring.datasource.initialization-mode 속성을 사용하여 지정할 수 있다. 예를 들어, 매번 타입에 상관 없이 DataSource를 초기화 하기를 원한다면 다음의 옵션을 사용한다.

spring.datasource.initialization-mode=always

때문에 schema.sql 파일에 CREATE TABLE 명령이 있을 경우 테이블이 이미 데이터베이스 상에 존재하고 있다면 schema.sql 파일의 상단에 DROP TABLE을 해줘야 이미 존재하고 있는 테이블을 다시 생성하려고 하는 오류를 방지할 수 있다.

테스트 시 강제로 내장 데이터베이스 사용하기

@AutoConfigureTestDatabase

Annotation that can be applied to a test class to configure a test database to use instead of the application-defined or auto-configured DataSource. In the case of multiple DataSource beans, only the @Primary DataSource is considered.

어플리케이션이 정의하거나 또는 자동 구성한 DataSource 대신 사용할 테스트 데이터베이스를 구성하기 위해 테스트 클래스에 적용할 수 있는 어노테이션이다. 여러 DataSource가 존재할 경우 @Primary DataSource만 고려된다.

테스트에 @AutoConfigureTestDatabase 어노테이션을 붙여주면 property로 설정한 데이터베이스가 아닌 내장 데이터베이스를 사용하도록 강제할 수 있다. 다만 이 어노테이션을 통해 기본 내장 데이터베이스를 사용한다면 application.properties에 설정한 MODE 따위의 H2 설정이 적용되지 않는다는 것을 명심하자.

추가적으로, 테스트 관련 어노테이션 중 @DataJdbcTest@DataJpaTest@AutoConfigureTestDatabase를 포함하기 때문에 기본 내장 데이터베이스를 사용한다.

참고 자료
Spring Boot Reference 85.Data Access
Spring Boot Reference 86.Database Initialization
@AutoConfigureTestDatabase

우테코 최고 미남 영앤리치 제이슨(박재성)

profile
Backend Developeer

3개의 댓글

comment-user-thumbnail
2022년 4월 25일

(필즈) ㄷㄷ.. 와 잘봤습니다.. 대박이네요

답글 달기
comment-user-thumbnail
2022년 4월 25일

👍

답글 달기
comment-user-thumbnail
2022년 4월 25일

이해가 쏙쏙 되네요. 잘 보고 갑니다~

답글 달기