1. AbstractRoutingDataSource란?
AbstractRoutingDataSource는 Spring Framework에서 제공하는 유용한 클래스 중 하나로, 다중 데이터 소스 환경에서 런타임에 데이터 소스를 동적으로 선택할 수 있도록 도와줍니다.
Spring에서 데이터 소스를 동적으로 선택해야 하는 상황이 종종 발생합니다. 예를 들어, 하나의 애플리케이션에서 여러 데이터베이스(MySQL, Oracle 등)를 다루거나, 멀티 테넌트 환경에서 테넌트별로 서로 다른 데이터베이스를 사용하는 경우가 이에 해당합니다. AbstractRoutingDataSource는 이러한 요구사항을 해결하기 위해 제공되는 추상 클래스입니다.
AbstractRoutingDataSource는 다음 두 가지 주요 구성 요소를 가지고 있습니다:
- DataSource 결정 로직: determineCurrentLookupKey() 메서드를 오버라이드하여 현재 사용할 데이터 소스를 결정하는 키를 반환합니다.
- DataSource 매핑: 미리 정의된 키와 데이터 소스 객체의 매핑을 설정합니다.
2. 동작 원리
AbstractRoutingDataSource는 내부적으로 DataSource 인터페이스를 구현하고 있어, DataSource를 사용하는 곳에서 투명하게 동작합니다. 클라이언트 요청 시 determineCurrentLookupKey()가 호출되어 적절한 키를 반환하면, 해당 키에 매핑된 데이터 소스가 사용됩니다.
주요 메서드
- determineCurrentLookupKey(): 현재 데이터 소스를 결정하는 키를 반환하는 메서드입니다. 주로 ThreadLocal을 사용하여 각 요청의 컨텍스트에 맞는 데이터를 설정합니다.
- setTargetDataSources(Map<Object, DataSource>): 키와 데이터 소스 간의 매핑을 설정합니다.
- setDefaultTargetDataSource(DataSource): 기본 데이터 소스를 설정합니다.
3. 구현 예제
3.1 데이터 소스 설정
아래는 두 개의 데이터 소스(MySQL과 Oracle)를 사용하는 예제입니다.
ApplicationContext 설정
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
@Configuration
public class DataSourceConfig {
@Bean
public DataSource mysqlDataSource() {
DataSourceBuilder<?> dataSourceBuilder = DataSourceBuilder.create();
dataSourceBuilder.url("jdbc:mysql://localhost:3306/mysql_db");
dataSourceBuilder.username("mysql_user");
dataSourceBuilder.password("mysql_password");
return dataSourceBuilder.build();
}
@Bean
public DataSource oracleDataSource() {
DataSourceBuilder<?> dataSourceBuilder = DataSourceBuilder.create();
dataSourceBuilder.url("jdbc:oracle:thin:@localhost:1521:xe");
dataSourceBuilder.username("oracle_user");
dataSourceBuilder.password("oracle_password");
return dataSourceBuilder.build();
}
@Bean
public DataSource routingDataSource() {
RoutingDataSource routingDataSource = new RoutingDataSource();
Map<Object, Object> dataSourceMap = new HashMap<>();
dataSourceMap.put("MYSQL", mysqlDataSource());
dataSourceMap.put("ORACLE", oracleDataSource());
routingDataSource.setTargetDataSources(dataSourceMap);
routingDataSource.setDefaultTargetDataSource(mysqlDataSource());
return routingDataSource;
}
}
|
cs |
RoutingDataSource 클래스 구현
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
public class RoutingDataSource extends AbstractRoutingDataSource {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
public static void setDataSourceKey(String key) {
contextHolder.set(key);
}
public static void clearDataSourceKey() {
contextHolder.remove();
}
@Override
protected Object determineCurrentLookupKey() {
return contextHolder.get();
}
}
|
cs |
3.2 데이터 소스 전환
사용자는 요청이나 특정 조건에 따라 데이터 소스를 전환할 수 있습니다.
Service에서 데이터 소스 전환
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
@Service
public class UserService {
@Autowired
private JdbcTemplate jdbcTemplate;
public void useMysqlDataSource() {
try {
RoutingDataSource.setDataSourceKey("MYSQL");
jdbcTemplate.execute("SELECT * FROM users");
} finally {
RoutingDataSource.clearDataSourceKey();
}
}
public void useOracleDataSource() {
try {
RoutingDataSource.setDataSourceKey("ORACLE");
jdbcTemplate.execute("SELECT * FROM employees");
} finally {
RoutingDataSource.clearDataSourceKey();
}
}
}
|
cs |
4. 주의사항
- ThreadLocal 사용: ThreadLocal을 사용하여 데이터 소스를 설정하므로, 적절히 초기화하거나 정리하지 않으면 메모리 누수가 발생할 수 있습니다.
- 동기화: 멀티 스레드 환경에서 동작을 신중히 설계해야 합니다.
- Default DataSource 설정: 기본 데이터 소스를 설정하지 않으면, 매핑되지 않은 키로 요청 시 예외가 발생할 수 있습니다.
5. 결론
AbstractRoutingDataSource는 다중 데이터 소스 환경에서 매우 유용한 도구입니다. 이를 통해 런타임에 데이터 소스를 유연하게 선택할 수 있으며, 멀티 테넌트 구조나 데이터베이스 이중화(예: Master-Slave) 환경에서 큰 이점을 제공합니다.
'Framework > Spring Boot' 카테고리의 다른 글
[Spring Boot] MyBatis의 SqlSessionFactory와 SqlSessionTemplate (0) | 2025.01.18 |
---|---|
[Spring Boot] Spring AOP를 활용한 Logging (0) | 2024.04.21 |
[Spring Boot] 자주 쓰이는 Spring Boot Annotation (Controller, Service, Model) (0) | 2024.04.21 |
[Spring Boot] @ExceptionHandler, @ControllerAdvice (Exception 공통 처리) (0) | 2024.03.09 |
[Spring Boot] Filter, Interceptor, AOP 차이 및 정리 (0) | 2024.03.08 |