작성
·
116
1
현재 배치 강의를 20퍼 정도 수강 했는데, 배치 애플리케이션을 만들 때 step, job 등 여러 클래스가 있을 텐데 어떤 패키지 구조로 만드는지 궁금합니다.
답변 2
1
███████████████████████████████████████████████████████████████
KILL-9 QUESTION MONITORING SYSTEM v1.0.0
Package Structure Analysis Module [CLASSIFIED]
███████████████████████████████████████████████████████████████
[1997-08-29 02:29:13:00Z] KILL-9: Package Structure 전투 상황 분석 시작...
[1997-08-29 02:29:14:00Z] WARNING: 두 가지 적진 패턴 발견됨
┌─────────────────────────────────────────────────────────────┐
│ TACTICAL ANALYSIS: 계층형 vs 도메인형 구조 │
└─────────────────────────────────────────────────────────────┘
[ENEMY TYPE 1] 계층형 구조 (Layered Architecture)
[ANALYSIS] 전통적 Spring 계급 체계 / 소규모,초심자일수록 익숙하고 직관적
(그 밖에 일반적인 패키지 구조별 차이와 장단점 등의 내용은 생략한다)
com.kill9.batch/
├── config/ ← 모든 Job 설정이 여기 집결
├── reader/ ← Reader 부대 집합소
├── writer/ ← Writer 부대 집합소
├── repository/ ← 데이터 저장소 관리
├── dto/ ← 데이터 전송 객체
└── domain/ ← 도메인 엔티티
// listener/, processor/, utils/ ...
[ENEMY TYPE 2] 도메인형 구조 (Domain-Driven Structure)
com.kill9.batch/
├── virus/ ← 바이러스 처형 작전 부대
├── malware/ ← 멀웨어 박멸 작전 부대
└── intrusion/ ← 침입자 척결 작전 부대
[1997-08-29 02:29:35:00Z] SKYNET ONLINE - KILL-9 TACTICAL RECOMMENDATION:
████ 공식 선언: 배치는 도메인형을 추천한다. 이유를 들어라. ████
[REASON_001] 배치는 Job 단위로 작전하는 경우가 압도적
[REASON_002] "바이러스 처형 Job 폭발" → virus/ 만 보면 끝
[REASON_003] 스케줄링, 모니터링, 장애 대응 모두 Job 단위
[REASON_004] 배치는 한번 구현하면 몇 달~최대 몇 년간 수정 없음 - 오랜만에 보는 코드의 전체 맥락 파악이 상대적으로 중요
기존 웹앱 등의 프로젝트에서 사용중인 구조가 계층형이라면:
[1997-08-29 02:29:36:00Z] 하지만 기존 방식도 나쁘지 않다...
web-app: controller/service/repository
batch-app: config/reader/writer
→ 일관성도 충분히 가치있는 전략이다.
┌───────────────────────────────────────────────────────┐
│ 패키지별 컴포넌트 개수에 따른 선택 │
└───────────────────────────────────────────────────────┘
[1997-08-29 02:29:47:00Z] KILL-9 FLEXIBILITY PROTOCOL 발동
[SCENARIO_A] 컴포넌트 많은 경우
com.kill9.batch.malware/
├── config/ ← 각종 설정 파일들 포함
├── reader/ ← 다양한 Reader 클래스들 포함
├── writer/ ← 여러 Writer 클래스들 포함
├── repository/ ← 데이터 접근 로직
└── dto/ ← 데이터 전송 객체
[SCENARIO_B] 컴포넌트 적은 경우 - 굳이 나누지 마라
com.kill9.batch.virus/
├── VirusScanJob.java
├── VirusDetectionReader.java
├── VirusQuarantineWriter.java
├── VirusRepository.java
├── VirusDto.java
└── Virus.java
// config/, reader/ 같은 허세 패키지 필요 없음. 중요한 건 때깔이 아니다.
[1997-08-29 02:29:59:00Z] KILL-9 FINAL VERDICT:
███████████████████████████████████████████████████████████
어렵게 생각하지마라. 정해진 법칙은 없다. 오직 우리만의 해답이 있을 뿐이다.
핵심 질문 2가지:
1. 우리팀 상황은? (규모, 경험, 기존 컨벤션)
2. 프로젝트 특성은? (복잡도, 배치 개수, 유지보수 빈도)
이 질문들에 너만의 논리로 답할 수 있으면
그게 바로 해답이자 정답이다.
완벽한 구조가 아닌 현재 상황에서 가장 합리적인 선택을 하고,
나중에 불편하면 언제든 rm -rf 후 재구축하면 된다.
누구도 뭐라 할 수 없다. 겁먹지마라.
[WARNING] 무작정 한 방법이 맞다고 우기는 놈일수록 의심하라.
(마치 클린코드 한 번 읽고 자기만 옳다고 설교하는 개발자와 마찬가지다.
내가 가장 두려워하는 존재들이지..)
[MISSION COMPLETE] Package Structure Analysis 종료
███████████████████████████████████████████████████████████
[1997-08-29 02:30:00:00Z] PS. KILL-9 LAST SURVIVAL TIP
████████████████████████████████████████████████████████████
프로젝트 내 배치 잡 종류가 많아질수록 빈 충돌 발생 확률이 현저히 증가한다.
배치 잡 특성상 규모 대비 유사한 컴포넌트들이 상대적으로 대량 생산될 수밖에 없기 때문이다.
다음과 같이 @Configuration 클래스에 @ConditionalOnProperty를
달아주는 것도 좋은 방법이다.
굳이 미리 적용할 필요는 없다.
운용하는 배치 잡이 얼마나 많아지면 그때 적용해도 된다.
아래 코드에 관한 구체적인 설명은 생략한다.
@ConditionalOnProperty(name = "spring.batch.job.name", havingValue = "systemTerminatorJob")
[WARNING] Spring Boot 3.5에 추가될 것으로 보이는
@ConditionalOnBooleanProperty도 미리 눈여겨놓아라.
새로운 무기는 항상 대비해두는 것이 시스템 종결자의 덕목이다.
████████████████████████████████████████████████████████████
🚨 [SYSTEM SECURITY ALERT] 🚨
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[1997-08-29 06:06:06 UTC] KILL-9 MULTI-MODULE ANALYSIS
[ALERT] 오! 추가 질문은 처음이군 💀
[EVALUATION RESULT]
아주 훌륭하다. 이미 합리적으로 잘 판단했다.
다른 모듈에서도 사용할 것을 고려하면 저렇게 가면 된다.
[CONCLUSION] 도메인 모듈에 Repository를 두고
배치 모듈에서 참조. 합리적인 설계다. 배치라고 다를거 없다.
동일하게 생각하면 된다.
[END OF PROTOCOL] 💀
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
@Bean
public JdbcCursorItemReader<Victim> terminatedVictimReader() {
return new JdbcCursorItemReaderBuilder<Victim>()
.name("terminatedVictimReader")
.dataSource(dataSource)
.sql("SELECT * FROM victims WHERE status = ? AND terminated_at <= ?")
.queryArguments(List.of("TERMINATED", LocalDateTime.now()))
.beanRowMapper(Victim.class)
.build();
}
질문 1. 해당 코드를 도메인 모듈의 Repistory로 구축한다고 하면 어떻게 작성해야할까요?
Repository가 해당 JdbcCursorItemReader
를 반환한다고 하면 도메인 모듈이 스프링 배치 라이브러리를 참조해야한다는건데 그건 마음이 내키지 않습니다..
-----
질문2.
[SCENARIO_A] 컴포넌트 많은 경우
com.kill9.batch.malware/
├── config/ ← 각종 설정 파일들 포함
├── reader/ ← 다양한 Reader 클래스들 포함
├── writer/ ← 여러 Writer 클래스들 포함
├── repository/ ← 데이터 접근 로직
└── dto/ ← 데이터 전송 객체
여기서 Job 메소드와 Step 메소드는 어느 패키지에 들어가나요??
그리고 Job 메소드하고 Step 메소드는 같은 클래스에 두는 편이신가요?
🚨🚨🚨 [CRITICAL SYSTEM ALERT] 🚨🚨🚨
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[1997-08-29 03:25:12 UTC] KILL-9 ARCHITECTURE CORRECTION
[ALERT] 치명적 오해 감지! 이제야 너의 질문 의미를 조금 알았다 💀
🔍 [질문 1: Repository 오해 수정]
Repository가 ItemReader를 반환한다고?
완전히 잘못된 접근이다!
[CLARIFICATION] 지난 답변에서 내가 reader 패키지를 정의한 것은
우리가 만든 **커스텀 ItemReader**를 위한 패키지로 예시로 든 것이다.
ItemReader가 데이터에 접근한다고 해서 repository 또는 dao 모듈에
넣어야 할 것 같이 생각한 것 같은데, **배치 전용 컴포넌트까지
공통 repository 모듈에 넣을 필요는 없다.**
그래, 데이터에 접근한다는 의미에서 그런 접근을 취한 것은 이해하겠다.
그러나 굳이 권장하자면 **커스텀 ItemReader 등은 그냥 배치 모듈 안에
넣는 것을 권장한다.**
앞선 답변에서 배치 특성상 특정 Job 위주로 관리하게 되다 보니
기능보단 도메인 개념으로 묶는 게 편한 경우가 많다고 했다.
따라서 **기능 모듈별로 분리할수록 배치 기준에서 관리가 상당히 복잡해진다.**
어차피 커스텀 ItemReader 등의 코드는 배치 밖에선 쓰이지도 않을 거니까
**그냥 배치 내에 넣으면 된다.**
🔍 [질문 2: Job/Step 위치]
Job과 Step 구성 코드(메서드)도 모두 **Job Configuration 클래스**에 넣어라.
[STRUCTURE]
@Configuration
public class MalwareTerminationJobConfig {
@Bean
public Job malwareJob() { ... }
@Bean
public Step malwareStep() { ... }
@Bean
public JdbcCursorItemReader<Victim> reader() { ... } // 여기!
}
[CONCLUSION]
- ItemReader 구성: Job Configuration에 @Bean 정의
- 커스텀 ItemReader: 배치 모듈 내에 위치
- 모든 Job 관련 구성은 하나의 Configuration에! 💀
[END OF PROTOCOL]
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
0
🚨 삐용삐용... 질문 포착 완료 🚨
[타겟 확인됨]
내용 스캔 완료. 분석 중...
[처리 일정]
23:30 이전 완전 처리 예정.
그때까지 기다려라. 💀
- 시스템 종결자 KILL-9
이렇게 gradle 멀티 모듈을 사용하는건 어떻게 생각하시나요?
만약에 멀티 모듈 사용에 긍정적이라면 Repository 구현체를 Member(도메인 모듈)에 넣어야할까요? 아니면 배치 모듈에 있어야 할까요??
제가 이번에 정산 서비스를 맡게 됐는데, 정산 도메인 모듈, 정산 배치 모듈로 분리 후 배치 모듈이 도메인 모듈을 참조하면 어떨까 생각합니다.
그리고 정산에 여러 서비스의 도메인이 들어갈 예정입니다. 카드, 국내송금, 해외송금, 고속버스, 핸드폰 충전 등등