본문 바로가기
공식문서

[Spring Docs] Initializing a DataSource

by sangyunpark99 2025. 4. 15.

이번글은 공식 문서에서 소개하는 Initializing a DataSource에 대해 정리했습니다.

 

The org.springframework.jdbc.datasource.init package provides support for initializing an existing DataSource. The embedded database support provides one option for creating and initializing a DataSource for an application. However, you may sometimes need to initialize an instance that runs on a server somewhere.

 

org.springframework.jdbc.datasource.init 패키지는 기존 DataSource를 초기화하는 것을 지원합니다. 내장된 데이터베이스 지원 기능은 애플리케이션을 위해 DataSource를 생성하고 초기화하는 한 가지 옵션을 제공합니다. 하지만 때로는 어딘가의 서버에서 실행 중인 인스턴스를 초기화해야 할 필요가 있을 수 있습니다.

 

Initializing a Database by Using Spring XML

If you want to initialize a database and you can provide a reference to a DataSource bean, you can use the initialize-database tag in the spring-jdbc namespace:

 

만약 데이터베이스를 초기화하고 DataSource 빈에 대한 참조를 제공할 수 있다면, spring-jdbc 네임스페이스에서 initialize-database 태그를 사용할 수 있습니다.

<jdbc:initialize-database data-source="dataSource">
	<jdbc:script location="classpath:com/foo/sql/db-schema.sql"/>
	<jdbc:script location="classpath:com/foo/sql/db-test-data.sql"/>
</jdbc:initialize-database>

 

The preceding example runs the two specified scripts against the database. The first script creates a schema, and the second populates tables with a test data set. The script locations can also be patterns with wildcards in the usual Ant style used for resources in Spring (for example, classpath*:/com/foo/**/sql/*-data.sql). If you use a pattern, the scripts are run in the lexical order of their URL or filename.

앞선 예제는 지정된 두 개의 스크립트를 데이터베이스에 대해 실행합니다. 첫 번째 스크립트는 스키마를 생성하고, 두 번째 스크립트는 테이블에 테스트 데이터 집합을 채웁니다. 스크립트 위치는 또한 Spring에서 리소스에 사용되는 일반적인 Ant 스타일의 와일드카드를 포함한 패턴일 수도 있습니다 (예: classpath*:/com/foo/**/sql/*-data.sql). 패턴을 사용하는 경우, 스크립트는 URL 또는 파일 이름의 사전 순서대로 실행됩니다.

 

The default behavior of the database initializer is to unconditionally run the provided scripts. This may not always be what you want — for instance, if you run the scripts against a database that already has test data in it. The likelihood of accidentally deleting data is reduced by following the common pattern (shown earlier) of creating the tables first and then inserting the data. The first step fails if the tables already exist.

 

데이터베이스 초기화기의 기본 동작은 제공된 스크립트를 조건 없이 실행하는 것입니다. 이것이 항상 원하는 동작은 아닐 수 있습니다 — 예를 들어, 이미 테스트 데이터가 들어 있는 데이터베이스에 스크립트를 실행하는 경우가 그렇습니다. 일반적으로 먼저 테이블을 생성하고 그 다음에 데이터를 삽입하는 방식(앞서 보여준 방식)을 따르면 실수로 데이터를 삭제할 가능성이 줄어듭니다. 테이블이 이미 존재하는 경우 첫 번째 단계에서 실패하게 됩니다.

 

However, to gain more control over the creation and deletion of existing data, the XML namespace provides a few additional options. The first is a flag to switch the initialization on and off. You can set this according to the environment (such as pulling a boolean value from system properties or from an environment bean). The following example gets a value from a system property:

 

하지만 기존 데이터의 생성 및 삭제를 보다 더 세밀하게 제어하기 위해, XML 네임스페이스는 몇 가지 추가적인 옵션을 제공합니다.

첫 번째는 초기화를 켜고 끌 수 있는 플래그입니다. 이 플래그는 환경에 따라 설정할 수 있습니다 (예를 들어, 시스템 속성이나 환경 빈에서 불리언 값을 가져오는 방식). 다음 예제는 시스템 속성에서 값을 가져오는 방법을 보여줍니다.

 

<jdbc:initialize-database data-source="dataSource"
	enabled="#{systemProperties.INITIALIZE_DATABASE}"> 
	<jdbc:script location="..."/>
</jdbc:initialize-database>

 

(1) Get the value for enabled from a system property called INITIALIZE_DATABASE

INITIALIZE_DATABASE라는 이름의 시스템 속성으로부터 enabled 값을 가져옵니다.

 

The second option to control what happens with existing data is to be more tolerant of failures. To this end, you can control the ability of the initializer to ignore certain errors in the SQL it runs from the scripts, as the following example shows:

 

두 번째로 기존 데이터를 다룰 때 제어할 수 있는 옵션은 오류에 대해 더 관대하게 처리하는 것입니다. 이를 위해, 초기화기가 실행하는 스크립트 내 SQL에서 발생하는 특정 오류를 무시할 수 있는 기능을 제어할 수 있습니다. 다음 예제는 그 방법을 보여줍니다.

<jdbc:initialize-database data-source="dataSource" ignore-failures="DROPS">
	<jdbc:script location="..."/>
</jdbc:initialize-database>

 

In the preceding example, we are saying that we expect that, sometimes, the scripts are run against an empty database, and there are some DROP statements in the scripts that would, therefore, fail. So failed SQL DROP statements will be ignored, but other failures will cause an exception. This is useful if your SQL dialect doesn’t support DROP …​ IF EXISTS (or similar) but you want to unconditionally remove all test data before re-creating it. In that case the first script is usually a set of DROP statements, followed by a set of CREATE statements.

앞선 예제에서는, 때때로 스크립트가 비어 있는 데이터베이스에 대해 실행될 수 있다는 점을 예상하고 있다는 의미입니다. 그리고 스크립트 안에는 DROP 구문이 일부 포함되어 있어서, 그렇기 때문에 실패할 수 있습니다. 그래서 실패한 SQL DROP 구문은 무시되지만, 다른 실패는 예외를 발생시킵니다.

 

이것은 당신이 사용하는 SQL 방언(dialect)이 DROP ... IF EXISTS(또는 이와 유사한 문법)을 지원하지 않지만, 테스트 데이터를 다시 생성하기 전에 무조건 제거하고 싶을 때 유용합니다. 이러한 경우, 첫 번째 스크립트는 일반적으로 여러 DROP 문으로 구성되어 있고, 그 뒤에 여러 CREATE 문이 따라옵니다.

우리는 종종 비어 있는 데이터베이스에 SQL 스크립트를 실행해야 할 때가 있습니다. 그런데 스크립트 안에 DROP TABLE 같은 구문이 있으면, 삭제할 테이블이 없을 경우 에러가 발생할 수 있습니다.
이때 Spring에서는 DROP 문이 실패해도 무시하도록 설정할 수 있습니다. 이렇게 하면 DROP 문은 실패해도 계속 진행되지만, 그 외의 SQL 오류는 여전히 예외를 발생시킵니다.
이 기능은 DROP TABLE IF EXISTS 같은 구문을 지원하지 않는 데이터베이스를 사용할 때 특히 유용합니다.
보통 이런 경우에는 DROP → CREATE → INSERT 순서로 스크립트를 실행합니다.

 

The ignore-failures option can be set to NONE (the default), DROPS (ignore failed drops), or ALL (ignore all failures).

ignore-failures 옵션은 NONE(기본값), DROPS(DROP 실패만 무시), ALL(모든 실패 무시)로 설정할 수 있습니다.

 

Each statement should be separated by ; or a new line if the ; character is not present at all in the script. You can control that globally or script by script, as the following example shows:

각 구문은 ;로 구분되어야 하며, 스크립트에 ; 문자가 전혀 없는 경우에는 줄바꿈으로 구분할 수 있습니다. 이러한 구문 구분 방식은 전체 스크립트에 대해 전역으로 설정하거나, 스크립트별로 개별 설정할 수도 있습니다. 다음 예제는 그 방법을 보여줍니다.

<jdbc:initialize-database data-source="dataSource" separator="@@"> 
	<jdbc:script location="classpath:com/myapp/sql/db-schema.sql" separator=";"/> 
	<jdbc:script location="classpath:com/myapp/sql/db-test-data-1.sql"/>
	<jdbc:script location="classpath:com/myapp/sql/db-test-data-2.sql"/>
</jdbc:initialize-database>

 

(1) Set the separator scripts to @@

(2) Set the separator for db-schema.sql to ;

 

(1) 스크립트들의 구분자를 @@로 설정합니다.

(2) db-schema.sql의 구분자를 ;로 설정합니다.

 

In this example, the two test-data scripts use @@ as statement separator and only the db-schema.sql uses ;. This configuration specifies that the default separator is @@ and overrides that default for the db-schema script.

이 예제에서 두 개의 테스트 데이터 스크립트는 @@를 구문 구분자로 사용하고, db-schema.sql;를 사용합니다. 이 설정은 기본 구분자를 @@로 지정하고, db-schema.sql에 대해서만 그 기본값을 덮어씌웁니다.

 

If you need more control than you get from the XML namespace, you can use the DataSourceInitializer directly and define it as a component in your application.

XML 네임스페이스에서 제공하는 것보다 더 많은 제어가 필요하다면, DataSourceInitializer를 직접 사용하여 애플리케이션의 구성요소로 정의할 수 있습니다.

 

Initialization of Other Components that Depend on the Database

A large class of applications (those that do not use the database until after the Spring context has started) can use the database initializer with no further complications. If your application is not one of those, you might need to read the rest of this section.

 

많은 종류의 애플리케이션들(즉, Spring 컨텍스트가 시작된 이후에야 데이터베이스를 사용하는 애플리케이션들)은 추가적인 복잡함 없이 데이터베이스 초기화 기능을 사용할 수 있습니다. 만약 당신의 애플리케이션이 이러한 경우에 해당하지 않는다면, 이 절의 나머지 내용을 읽어야 할 수도 있습니다.

 

The database initializer depends on a DataSource instance and runs the scripts provided in its initialization callback (analogous to an init-method in an XML bean definition, a @PostConstruct method in a component, or the afterPropertiesSet() method in a component that implements InitializingBean). If other beans depend on the same data source and use the data source in an initialization callback, there might be a problem because the data has not yet been initialized. A common example of this is a cache that initializes eagerly and loads data from the database on application startup.

 

데이터베이스 초기화기는 DataSource 인스턴스에 의존하며, 초기화 콜백에서 제공된 스크립트들을 실행합니다. (이는 XML 빈 정의에서의 init-method, 컴포넌트의 @PostConstruct 메서드, 또는 InitializingBean을 구현한 컴포넌트의 afterPropertiesSet() 메서드와 유사합니다.)

만약 다른 빈들도 동일한 DataSource에 의존하면서, 초기화 콜백에서 해당 DataSource를 사용한다면, 데이터가 아직 초기화되지 않았기 때문에 문제가 발생할 수 있습니다.

대표적인 예로는 애플리케이션 시작 시 eager하게 초기화되며 DB에서 데이터를 미리 불러오는 캐시 빈이 있습니다.

 

Spring에서는 DataSource가 준비되면 SQL 스크립트를 실행해서 테이블을 만들고 데이터를 넣습니다. 그런데 어떤 빈이 너무 빨리 실행되어, 데이터베이스가 아직 초기화되기 전에 DB에 접근하면 문제가 생길 수 있습니다.
예를 들어, 어떤 캐시 빈이 애플리케이션 시작하자마자 DB에서 데이터를 불러오려고 하면, 아직 테이블이 없어서 에러가 날 수 있습니다. 그래서 이런 경우에는 DB 초기화보다 먼저 실행되지 않도록 주의해야 합니다.

 

To get around this issue, you have two options: change your cache initialization strategy to a later phase or ensure that the database initializer is initialized first.

 

이 문제를 해결하기 위해 두 가지 방법이 있습니다: 캐시 초기화 시점을 더 나중으로 변경하거나, 데이터베이스 초기화가 먼저 실행되도록 보장하는 것입니다.

 

Changing your cache initialization strategy might be easy if the application is in your control and not otherwise. Some suggestions for how to implement this include:

 

애플리케이션이 당신의 통제 아래에 있다면 캐시 초기화 전략을 변경하는 것은 쉬울 수 있지만, 그렇지 않은 경우에는 어려울 수 있습니다.

이를 구현하는 방법에 대한 몇 가지 제안은 다음과 같습니다:

 

  • Make the cache initialize lazily on first usage, which improves application startup time.
  • Have your cache or a separate component that initializes the cache implement Lifecycle or SmartLifecycle. When the application context starts, you can automatically start a SmartLifecycle by setting its autoStartup flag, and you can manually start a Lifecycle by calling ConfigurableApplicationContext.start() on the enclosing context.
  • Use a Spring ApplicationEvent or similar custom observer mechanism to trigger the cache initialization. ContextRefreshedEvent is always published by the context when it is ready for use (after all beans have been initialized), so that is often a useful hook (this is how the SmartLifecycle works by default).
  1. 캐시를 처음 사용할 때(lazy) 초기화하도록 만들 수 있습니다.이렇게 하면 애플리케이션 시작 속도도 빨라지고, 데이터베이스 초기화가 끝난 뒤에 캐시가 동작하게 됩니다.
  2. 캐시 클래스나 캐시를 초기화하는 컴포넌트가 Lifecycle 또는 SmartLifecycle 인터페이스를 구현하게 만들 수도 있습니다.
    •  SmartLifecycleautoStartup 플래그를 true로 설정하면 애플리케이션 컨텍스트가 시작될 때 자동으로 동작합니다. 
    •  Lifecycle은 수동으로 ConfigurableApplicationContext.start()를 호출해야 동작합니다.
  3. Spring의 ApplicationEvent를 활용해서 초기화를 트리거할 수 있습니다.
    •  예를 들어 ContextRefreshedEvent모든 빈이 초기화된 후 컨텍스트가 준비되었을 때 자동으로 발생하는 이벤트입니다.
    •  이 시점에 캐시를 초기화하면 데이터베이스도 이미 초기화된 상태이므로 안전하게 데이터를 가져올 수 있습니다.
    •  참고로 SmartLifecycle도 기본적으로 이 이벤트를 기준으로 동작합니다.
데이터베이스가 아직 초기화되지 않았는데 캐시가 먼저 실행되면 문제가 생길 수 있습니다.

이런 상황을 피하기 위해 캐시 초기화를 늦추는 방법은 다음과 같습니다:
캐시를 처음 사용할 때 초기화되도록(lazy) 설정하면, 애플리케이션 시작 시에는 캐시가 실행되지 않고, 실제로 필요한 시점에만 초기화되므로 더 안전합니다.

Spring의 Lifecycle이나 SmartLifecycle 인터페이스를 구현하면, Spring이 애플리케이션 컨텍스트를 다 초기화한 뒤에 캐시를 자동 또는 수동으로 시작할 수 있습니다.

Spring의 ContextRefreshedEvent와 같은 이벤트를 활용하면, 모든 빈이 초기화된 이후에 캐시를 실행하도록 설정할 수 있습니다. 이 방법은 캐시 초기화를 안전한 시점에 트리거할 수 있는 좋은 방법입니다.

 

Ensuring that the database initializer is initialized first can also be easy. Some suggestions on how to implement this include:

 

데이터베이스 초기화가 가장 먼저 실행되도록 보장하는 방법도 비교적 간단할 수 있습니다.

이를 구현하는 방법에 대한 몇 가지 제안은 다음과 같습니다:

 

  • Rely on the default behavior of the Spring BeanFactory, which is that beans are initialized in registration order. You can easily arrange that by adopting the common practice of a set of <import/> elements in XML configuration that order your application modules and ensuring that the database and database initialization are listed first.
  • Separate the DataSource and the business components that use it and control their startup order by putting them in separate ApplicationContext instances (for example, the parent context contains the DataSource, and the child context contains the business components). This structure is common in Spring web applications but can be more generally applied.

Spring BeanFactory의 기본 동작에 의존하세요. BeanFactory는 빈들을 등록된 순서대로 초기화합니다.

이는 XML 설정에서 <import/> 요소들을 사용하여 애플리케이션 모듈들의 순서를 지정하고, 데이터베이스 및 데이터베이스 초기화를 가장 먼저 나열함으로써 쉽게 구성할 수 있습니다.

 

DataSource와 그것을 사용하는 비즈니스 컴포넌트들을 분리하고, 이들을 각각 별도의 ApplicationContext 인스턴스에 넣어 초기화 순서를 제어할 수도 있습니다.

예를 들어, 부모 컨텍스트에는 DataSource를 넣고, 자식 컨텍스트에는 비즈니스 컴포넌트들을 넣는 방식입니다.

이 구조는 Spring 웹 애플리케이션에서 일반적으로 사용되지만, 보다 일반적인 상황에도 적용될 수 있습니다.

 

 

정리

  • Spring에서는 DataSource가 준비된 후 SQL 스크립트를 실행하여 데이터베이스를 초기화합니다.
  • 데이터베이스 초기화 전에 다른 빈이 DB에 접근하면 오류가 발생할 수 있습니다.
  • 이를 방지하려면 빈 초기화 순서를 조절하거나, 캐시 같은 컴포넌트를 지연 초기화해야 합니다.