한 프로젝트에서 하나의 데이터베이스만 다루다가, 새로운 데이터베이스가 추가되면 기존 구조를 확장해야 합니다. 이 글에서는 JdbcTemplate
를 활용해 여러 개의 데이터베이스를 연결하고 선택적으로 실행하는 과정을 정리합니다.
문제 정의
- 기본적으로
JdbcTemplate
는 하나의DataSource
를 참조하여 단일 데이터베이스에 접근합니다. - 새로운 데이터베이스를 연결해야 할 때, 별도의 설정과
JdbcTemplate
등록이 필요합니다. - SQL 실행기를 공통화하여 코드 중복을 줄이고, 대상 DB만 선택해서 실행할 수 있어야 합니다.
DataSource 등록
Spring Boot에서는 DataSourceProperties
를 통해 손쉽게 데이터소스를 생성할 수 있습니다. 데이터베이스마다 접두어를 달리 설정하면 여러 개의 DataSource
를 만들 수 있습니다.
datasource:
main:
url: jdbc:mysql://localhost:3306/maindb
username: main_user
password: main_pass
driver-class-name: com.mysql.cj.jdbc.Driver
analytics:
url: jdbc:mysql://localhost:3306/analyticsdb
username: analytics_user
password: analytics_pass
driver-class-name: com.mysql.cj.jdbc.Driver
@Configuration
public class DataSourceConfig {
@Bean
@ConfigurationProperties("datasource.main")
public DataSourceProperties mainProps() {
return new DataSourceProperties();
}
@Bean(name = "mainDataSource")
public DataSource mainDataSource() {
return mainProps().initializeDataSourceBuilder().build();
}
@Bean
@ConfigurationProperties("datasource.analytics")
public DataSourceProperties analyticsProps() {
return new DataSourceProperties();
}
@Bean(name = "analyticsDataSource")
public DataSource analyticsDataSource() {
return analyticsProps().initializeDataSourceBuilder().build();
}
}
JdbcTemplate 등록
각 데이터소스마다 JdbcTemplate
를 등록합니다.
@Configuration
public class JdbcTemplateConfig {
@Bean(name = "mainJdbcTemplate")
public JdbcTemplate mainJdbcTemplate(@Qualifier("mainDataSource") DataSource ds) {
return new JdbcTemplate(ds);
}
@Bean(name = "analyticsJdbcTemplate")
public JdbcTemplate analyticsJdbcTemplate(@Qualifier("analyticsDataSource") DataSource ds) {
return new JdbcTemplate(ds);
}
}
데이터베이스 식별 Enum
어떤 데이터베이스를 사용할지 구분할 수 있도록 Enum을 정의합니다.
public enum DbTarget {
MAIN,
ANALYTICS
}
SqlExecutor 구현
Map<DbTarget, JdbcTemplate>
를 주입받아, 호출 시 원하는 DB에 SQL을 실행할 수 있도록 작성합니다.
@Component
public class SqlExecutor {
private final Map<DbTarget, JdbcTemplate> templates;
public SqlExecutor(@Qualifier("mainJdbcTemplate") JdbcTemplate main,
@Qualifier("analyticsJdbcTemplate") JdbcTemplate analytics) {
this.templates = Map.of(
DbTarget.MAIN, main,
DbTarget.ANALYTICS, analytics
);
}
public void runSqlFile(String sqlFilePath, DbTarget target) throws Exception {
JdbcTemplate jdbcTemplate = templates.get(target);
if (jdbcTemplate == null) {
throw new IllegalArgumentException("JdbcTemplate not found for " + target);
}
Resource resource = new ClassPathResource(sqlFilePath);
try (InputStream is = resource.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
StringBuilder sb = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
String t = line.trim();
if (t.isEmpty() || t.startsWith("--")) continue;
sb.append(t).append('\n');
if (t.endsWith(";")) {
String sql = sb.toString().trim();
sql = sql.substring(0, sql.length() - 1);
if (!sql.isEmpty()) jdbcTemplate.execute(sql);
sb.setLength(0);
}
}
if (sb.length() > 0) {
jdbcTemplate.execute(sb.toString());
}
}
}
}
서비스와 컨트롤러에서 활용
서비스 계층에서는 실행 성공 여부를 반환하도록 구성합니다.
@Service
public class SqlService {
private final SqlExecutor sqlExecutor;
public SqlService(SqlExecutor sqlExecutor) {
this.sqlExecutor = sqlExecutor;
}
public boolean updateAnalyticsData() {
try {
sqlExecutor.runSqlFile("sql/update_analytics.sql", DbTarget.ANALYTICS);
return true;
} catch (Exception e) {
return false;
}
}
}
컨트롤러에서는 API 요청에 대해 성공 여부를 JSON으로 반환합니다.
@RestController
@RequestMapping("/sql")
public class SqlController {
private final SqlService sqlService;
public SqlController(SqlService sqlService) {
this.sqlService = sqlService;
}
@PostMapping("/update-analytics")
public ResponseEntity<Map<String, Object>> updateAnalytics() {
boolean success = sqlService.updateAnalyticsData();
Map<String, Object> result = new HashMap<>();
result.put("success", success);
result.put("message", success ? "성공" : "실패");
return ResponseEntity.ok(result);
}
}
정리
DataSourceProperties
와DataSource
를 DB별로 분리하여 등록한다.- 각 DB마다
JdbcTemplate
를 만들어 사용한다. SqlExecutor
에서Map<DbTarget, JdbcTemplate>
구조를 사용하면 DB 선택이 단순하고 확장성이 좋다.- 서비스와 컨트롤러에서 성공 여부를 반환해 클라이언트가 실행 결과를 쉽게 확인할 수 있다
반응형
'개발 (Development) > Java' 카테고리의 다른 글
[Java] 객체 리스트를 특정 속성으로 정렬하는 방법 (1) | 2025.10.06 |
---|---|
[Java/Spring Boot] "No thread-bound request found" 에러 원인과 해결법 (0) | 2025.09.19 |
[Java/Spring Boot] Spring Boot에서 OAuth2 인증 401 오류 해결하기 (0) | 2025.09.14 |
[Java] JPA에서 DTO는 Interface로 구현할까? Class로 구현할까? (4) | 2025.08.03 |
[Java] Interface란? 클래스와 다른 점은? (0) | 2025.08.03 |