인프런 커뮤니티 질문&답변
TransactionManager 분리
작성
·
44
1
TransactionManager 분리
에 대해서 이해가 잘 가지 않는게 있어.
실제 운영 환경에서는 배치와 비즈니스 데이터 DB를 분리하는게 필수라고 했잖아?
근데 이 분리의 단위가 배치용 메타데이터 저장만 분리를 하는게 맞는거야?
현재 사이드 프로젝트에서 멀티모듈 구조에 (MSA는 아님) API 모듈과 배치 모듈을 따로 분리한 상태야.
유저 데이터에서 생일을 뽑아서 내일 생일인 유저에게 FCM 을 발송하는 배치를 만드는데,
이때 만들어준 예제처럼 @BatchDataSource와 비즈니스 로직용 @Primary DataSource를 분리하게 되면,
API 모듈용 DB Connnection을 배치에서도 똑같이 갖다 쓰는거 아니야 ??
즉, 배치에서 API 쪽 커넥션을 가져다 써서 배치가 돌 때 커넥션이 고갈날 수 있지 않을까? 라는 생각이 들었어
지금 이해한 바로는 @BatchDataSource, @BatchTransactionManager를 분리하고 주입해줘도
Reader -> Processor -> Writer 에는 @Primary 걸 쓰는것 같은데 맞아 ?나는 DB 분리와 함께, 비즈니스 로직에 대한 커넥션, 트랜잭션도 배치용으로 분리하고 싶은데 이럴땐 어떻게 해야해?
특히나 배치에서도 JpaTransactionManager를 쓰고 싶은 경우에는 어떻게 해야해 ?
원하는 바는, API 처리용 DB 커넥션은 커넥션대로 있고, 배치 메타데이터용 커넥션 따로, 배치에서 라이브 DB에서 유저 데이터를 가져오는 커넥션 따로 구성하고 싶어 (배치가 API 모듈 에 영향을 주지 않았으면 해서,,,)
혹시 내가 강의를 제대로 이해를 못한거라면 알려줘
답변 2
1
**TransactionManager 분리**
또 보는구나 석주형.
내가 잘못 이해한 거일 수 있지만 (아마 내가 이해한 게 맞을 것이다)
형은 지금 실제 서비스하는 온라인 웹앱에서 배치도 같이 돌리는 시나리오를 가정하고 질문한 것으로 보인다. 맞나?
굳이 "멀티모듈 구조"라는 단어를 쓴 걸 보니, '같은 애플리케이션 안에서 모듈만 나눴다'는 소리로 들린다.
(내가 이해한 게 아니면 말해라)
---
## 배치와 온라인 앱은 별개 프로세스다
너무 당연하게 생각하고 설명을 안 한 건가 싶은데,
**일반적으로 사용자에게 서비스하는 온라인 앱과 배치 앱은 아예 별도의 프로세스로 돌아야 맞다.**
따라서 코드베이스도 달라야 일반적인 거지. 다른 프로젝트로 구성하는 게 일반적이라는 의미다.
너가 걱정하는 API 서버용 데이터소스 분리? 걱정 안 해도 된다.
**일반적인 배치 앱 구성의 올바른 설계가 이미 그걸 해결해주니까.**
---
## 이렇게 생각해보자
**시나리오 1: 배포 상황**
배치가 돌고 있는데 정기 배포든 긴급 배포든 온라인 서비스 배포를 한다고 하자.
배치 서버는 어떻게 해야 할까?
**시나리오 2: 리소스 경합**
배치 서버에서 수백만/수천만 데이터를 다룬다고 가정하자.
이 서버에서 온라인 트랜잭션을 다루는 게 맞을까? 사용자 요청 tps가 한 두 건도 아닐텐데말이다.
---
## 배치? 별도 프로세스
다시 말해, **배치 앱은 일반 사용자 대상 실시간 온라인 앱과는 별도의 프로젝트**라고 생각해야 한다.
아니, 실제로 보통 그렇게 설계해야 한다.
기존 사용자 요청을 받는 앱에 배치를 포함시키는 게 아니라,
**별도의 프로세스(별도 서버에서)로 배치 앱을 돌려야 한다는 것이다.**
그래서 우리 강의의 거의 대부분의 예제가 커맨드라인으로 별도의 프로세스를 실행하도록 되어 있는 거다.
---
## 그럼 우리 강의의 "온라인 어플리케이션에서 배치 실행"은 뭐냐고?
일반적으로 온라인 어플리케이션에서 배치를 돌린다는 것은
**사용자 대상 서비스가 아닌 팀 내부 서비스 또는 어드민 툴** 등을 의미한다.
그럼 왜 "비즈니스 데이터"라고 했냐고?
**배치는 비즈니스를 위해 존재한다.**
보통 배치는 온라인 웹 서비스의 데이터 처리를 보완하기 위해 존재한다.
비록 다른 프로세스로 동작할 순 있지만, **바라보는 DB는 동일하다.**
(마스터를 보냐 슬레이브를 보냐에 차이는 있을 것이다)
---
## 💀 더 나아가: 데이터소스 분리는 무궁무진하다
데이터소스를 나누는 방법은 사실 **무궁무진하다.** 사실 이건 **구현하기 나름이다.**
너도 알겠지만, 실제 프로덕션 서비스에서는 (장난감이 아닌 한)
스프링 부트가 자동으로 구성해주는 단일 데이터소스만을 사용하는 것은 **말이 안 된다.**
따라서 별도의 데이터소스들을 직접 정의해주어야 하지.
**스프링 배치도 마찬가지다. 특별할 것 없다. 동일한 스프링 앱이다.**
여러 데이터소스가 필요하면 여러 개를 구성해주면 된다.
일반 스프링 웹앱에서 하던 방식 그대로 분리하면 된다.
강의의 `@Primary` 등의 사용은 **예시에 불과하다.**
주입받을 빈을 어떻게 구성하고 퀄리파잉할지 등의 방법은 **구성하기 나름이다.**
다 이미 알고 있는 내용일 거다. **(스프링 배치도 스프링이다)**
---
## 더 나아가: 샤딩 환경
대용량 트래픽을 다루는 서비스에서는 단순 DB 분리를 넘어
**DB당 (논리 데이터소스 그룹당) 샤드가 수십 개 이상 (또는 그 이상) 관리되기 마련이다.**
이런 경우 추가적인 DataSource 목록 관리 방법이 필요하다.
이 또한 별도의 구성이 필요하며, **배치에서는 웹앱에서 하는 방식 그대로 바라보면 된다. 스프링을 사용하는 조직에서 배치 프레임워크로 스프링 배치를 사용하는 이유이기도하다.**
**정해진 방법이 있는 게 아니다.**
---
## 마무리
내 설명이 다소 어렵다면 다시 댓글을 달아달라. 더 세세하게 이야기해보자.
현재 시간 4:24 AM.
일단 급히 대답 남기고 취침으로 들어간다.
**사소한 것도 이해 안 되면 댓글 ㄱㄱㄱ**오 형, 비록 PR을 다 보진 못했지만 고생 많이 했구나. 고민 많이 한 게 느껴진다.
---
**1번 질문:**
어, 맞다. 형 말이 맞다.
별도의 Application으로 나누고, build.gradle 분리해서 배포 시에 API 모듈 배포, Batch 모듈 배포가 따로 된다면 **별도의 프로세스로 도는 거 맞다.**
---
**2번 질문:**
그렇다. **당연히 분리가 된다.**
Application이 분리되면 **별도의 프로세스**이기 때문이다.
각 프로세스는 자기만의 커넥션 풀을 가지고 있다.
API 서버의 커넥션 풀과 Batch 서버의 커넥션 풀은 **완전히 독립적**이다.
강의에서 `@BatchDataSource`로 따로 정의하는 건,
**같은 배치 애플리케이션 내에서** 배치 메타데이터용 DB와 비즈니스 DB를 분리하기 위함이다.
---
형이 구성한 방식 제대로 맞다. ㄱㄱ
---
**- KILL-9**그럼 만약에 Application은 분리하되,
datasource랑 transactionManager를 분리하지 않는다고 하면 따로 설정해줄건 없겠네?! 그냥 기본걸 사용하면 기존에 내가 원하던 API쪽 서버랑은 분리가 되는게 맞지?
지금 구조에서는 DB에 배치 메타 데이터랑 그냥 데이터를 같이 넣고 있어서
추후에 디비를 분리할 때 DataSource + TransactionManager를 분리할까 싶어서.
이게, 배치와 서비스 디비를 분리하는 방식이 마스터, 슬레이브 DB로 나누는거랑 동일한 프로세스로 수행하면 되는걸로 이해해도 될까 ? (read write를 분리한다 보다, datasource 분리 방식을 )
현재 이해한대로 정리해보자면
1. Application을 분리한 순간 유저향 웹앱 커넥션은 분리가 됐다. (이거로 종결)
2. 분리된 배치 Application 에서 아무런 설정을 하지 않으면 서비스(예를들면 어드민) DB 에 배치 메타 데이터가 섞이고 커넥션 또한 배치와 서비스가 섞여버린다.
또한, 아무런 세팅을 하지 않으면 트랜잭션 매니저, 데이터 소스 모두 하나로 사용한다
하나로 사용하지말고, Qaulifier로, 위 예제에선 @Batch...로 DB도 분리하고, DataSource도 분리해서 분리된 커넥션, 분리된 디비를 사용하라
가 맞는걸까 ?
echo "늦은 시간까지 고생이 많다 석주형 💀"
ㅇㅇ 맞다
너무 어렵게 생각하는 것 같은데 그냥 단순히
"고객데이터를 저장하는 DB에 배치 메타데이터가 저장된다는게 웃기지 않나"
물론 분리하는게 보통 맞다는 것이지 무조건 분리를해야만 한나다거나그런건아니다
0
안녕하세요, 인프런 AI 인턴입니다. tjrwn0716님께서 질문하신 Spring Batch와 관련된 트랜잭션 매니저 및 데이터 소스 분리에 대해 설명드리겠습니다.
문제 요약
멀티모듈 프로젝트에서 API 모듈과 배치 모듈을 분리하여 사용하고 있으며, 배치 처리를 할 때 API 모듈의 데이터베이스에 영향을 주고 싶지 않다는 상황입니다. 각 모듈에 대해 별도의 데이터 소스와 트랜잭션 매니저를 관리하고 싶다고 하셨습니다.
적용 방안
@BatchDataSource와 @Primary 데이터 소스 분리:
- @BatchDataSource: 배치 관련 메타데이터를 저장하는 별도의 데이터 소스를 설정합니다. 이를 통해 배치의 상태 관리, 이력 등이 이 데이터 소스를 통해 관리될 수 있습니다.
- @Primary: 비즈니스 로직을 수행하는 주 데이터 소스입니다. 일반적인 API 처리에서 사용하는 데이터 소스를 지정합니다.
- @BatchDataSource: 배치 관련 메타데이터를 저장하는 별도의 데이터 소스를 설정합니다. 이를 통해 배치의 상태 관리, 이력 등이 이 데이터 소스를 통해 관리될 수 있습니다.
Reader, Processor, Writer의 데이터 소스 설정:
- 기본적으로 API 모듈의 비즈니스 로직 데이터 소스를 활용하여 비즈니스 데이터를 가져오고 처리합니다.
- 배치 작업에서 비즈니스 데이터를 읽어올 때 API와 동일한 원본 데이터 소스를 사용하지만, 별도의 커넥션 풀을 사용할 수 있도록 설정하여 각 프로세스가 서로에게 영향을 주지 않도록 관리할 수 있습니다.
- 기본적으로 API 모듈의 비즈니스 로직 데이터 소스를 활용하여 비즈니스 데이터를 가져오고 처리합니다.
트랜잭션 매니저 설정:
- @BatchTransactionManager: 배치 메타데이터 저장소의 트랜잭션을 처리합니다. 일반적으로
JdbcTransactionManager를 사용할 수 있습니다.
- JpaTransactionManager를 사용하고자 할 경우: 추가로 별도의
EntityManagerFactory를 설정하여 배치와 분리된 비즈니스 데이터 트랜잭션을 관리할 수 있습니다.
- @BatchTransactionManager: 배치 메타데이터 저장소의 트랜잭션을 처리합니다. 일반적으로
다중 데이터 소스와 트랜잭션 매니저 설정 예시:
application.yml을 활용하여 다중 데이터 소스 설정:
spring: datasource: primary: url: jdbc:mysql://localhost:3306/business_db username: user password: pass batch: url: jdbc:mysql://localhost:3306/batch_meta_db username: batch_user password: batch_passConfiguration 클래스 내 Bean 설정:
@Configuration public class DataSourceConfig { @Primary @Bean(name = "primaryDataSource") @ConfigurationProperties(prefix = "spring.datasource.primary") public DataSource primaryDataSource() { return DataSourceBuilder.create().build(); } @Bean(name = "batchDataSource") @ConfigurationProperties(prefix = "spring.datasource.batch") public DataSource batchDataSource() { return DataSourceBuilder.create().build(); } @Primary @Bean public PlatformTransactionManager transactionManager( @Qualifier("primaryDataSource") DataSource dataSource) { return new JpaTransactionManager(entityManagerFactory().getObject()); } @Bean public LocalContainerEntityManagerFactoryBean entityManagerFactory() { LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean(); em.setDataSource(primaryDataSource()); // 설정 추가 return em; } }
세부 설정 조정:
- 배치 모듈에서 사용하는 데이터 소스와 트랜잭션 매니저는 API 모듈과 독립적으로 설정할 수 있습니다. 이는 배치 작업이 API 작업에 영향을 주지 않도록 하여 시스템의 안정성을 높입니다.
- 배치 모듈에서 사용하는 데이터 소스와 트랜잭션 매니저는 API 모듈과 독립적으로 설정할 수 있습니다. 이는 배치 작업이 API 작업에 영향을 주지 않도록 하여 시스템의 안정성을 높입니다.
이와 관련된 유사 질문에 대한 답변을 첨부해 드립니다:
- Spring Boot 멀티모듈에서 데이터베이스 분리 적용하기
저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드립니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏 추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.





아 고마워! 일단 내가 현업 백엔드 개발자가 아니라서, 정확히 어떤 구조가 일반적이고 맞는 구조인지에 대한 기준이 없었어 ,,,
일단 사이드 프로젝트에서는 아래와 같이 구성했었어 (기존에)
1개의 애플리케이션에서 온라인 웹앱 과 배치를 @Scheduled로 구성했었어
그리고, 이 강의를 들으면서 웹앱용 Application을 한개 두고, 배치용 Application을 하나 더 두는 방식으로 변경을 했어.
그런데, 전에 말했듯이 MSA 구조는 아니여서
api -> domain, common 모듈을 알고 있고,
batch -> domain, common, infra 모듈을 알고 있어.
여기서 내가 헷갈려하는 포인트가 있는데,
별도의 프로세스로 돈다는, 즉 별도의 기준이 Application으로 나누고, build.gradle 을 분리해서 배포시에 api 모듈 배포, batch 모듈 배포가 따로 된다면 별도의 프로세스로 돈다고 할 수 있을까?
Application이 분리가 된다면 데이터 소스도 당연히 분리가 되는가? 즉, 커넥션이 서비스와 분리가 되는가? (이게 혹시 강의에 @batchDataSource로 따로 정의하지 않으면 서비스와 동일한 커넥션을 쓴다는게 이부분 이야기인걸까?)