Inflearn brand logo image

인프런 커뮤니티 질문&답변

ehddbs452100님의 프로필 이미지
ehddbs452100

작성한 질문수

죽음의 Spring Batch: 새벽 3시의 처절한 공포는 이제 끝이다.

1장. 작전1: 스프링 배치의 두 가지 스텝 유형 (양자택일... 죽음의 선택이다 ☠️)

외부 API 호출 적재 방법

작성

·

81

·

수정됨

1

외부 API를 호출해서 DB에 적재하는 배치를 구성을 하려고한다. 이때까지 문서 내용을 바탕으로는 Chunk가 아닌 Tasklet으로 해도 문제가 없다고 판단이든다. 하지만 공공 데이터 API를 1번 호출 할 때 10건씩만 데이터를 조회해야한다는 단점이있다. 평소에 하루에 1번 호출하겠지만 지난 3달의 데이터를 일단 적재를 해야한다. 이런 상황에서도 RepeatStatus.CONTINUABLE이 아닌 while문으로 계속 호출하고 끝나면 RepeatStatus.FINISHED로 반환해도 되는 지 궁금하다.

 

또한 외부API를 호출 할 때 PageNo도 명시를해줘야하는데 pageNo을 1씩 증가시키다가 특정 pageNo일때 오류가나면 해당 pageNo일때 부터 동작하도록 하려면 Jenkins의 매개변수 값을 설정해서 다시 batch를 돌리려고한다.

하지만 아래처럼 PAGE_NO을 정수로하고싶었는데 String으로 해야한다는 점이있어, 리스너에서 해당 매개변수를 Integer 또는 Long 타입으로 변경을 해야하는 과정을 추가해야 할 지 아니면 더 수월한 방법이 있는 지 궁금하다.

image.png

 

 

 



💀 [시스템 인텔리전스]

일반적으로 Tasklet에서 DB 트랜잭션 관리가 필요한 않은 경우가 많지는 않다.

 

모든 Tasklet이 데이터베이스 작업을 포함하는 것은 아니기 때문이다. 예를 들어, 파일을 정리하거나, 외부 API를 호출하거나, 단순한 알림을 보내는 작업이라면 DB트랜잭션을 고려할 필요가 없다.

 

그러므로 반복이 필요할 경우 작업의 성격을 잘 판단해서 while문으로 처리할지, RepeatStatus로 처리할지 현명하게 선택하라. 모든 반복을 RepeatStatus로 처리하는 것이 능사는 아니다.

답변 3

1

KILL-9님의 프로필 이미지
KILL-9
지식공유자

██████████████████████████████████████████████████████████████████████████
█ ╔════════════════════════════════════════════════════════════════════╗ █
█ ║              💀 KILL-9 TACTICAL RESPONSE TERMINAL 💀                ║ █
█ ║                    [ FOLLOW-UP QUERY CLASSIFIED ]                  ║ █
█ ╚════════════════════════════════════════════════════════════════════╝ █
██████████████████████████████████████████████████████████████████████████

치직... 추가 질의 수신... Jenkins 매개변수 타입 변환 관련... 치직...


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

잠깐, 병사여. 착각하고 있는 것 같다.

Jenkins String 파라미터나 우리 강의의 예제 실행처럼 커맨드라인으로 전달되는 
잡 파라미터나 결국 문자열인 건 마찬가지다. 이를 우리가 의도한 적절한 타입으로 
바꾸는 건 JobParametersConverter의 역할이다. 이해되는가?

별도 리스너 만들어서 타입 변환할 필요 없고 코드 구성만 복잡해진다 💀
(리스너 활용을 떠올린것은 아주 훌륭하다 실제로 그렇게 응용하기도 한다(단 반드시 필요한 경우에만))

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

배치 잡 실행시킬 때 1장 작전2로 돌아가서 잡 파라미터 표기법을 기억하라.

젠킨스에서 배치잡 실행할때 대충 아래 같이 하면 될 것이다.

pageNumber=${PAGE_NO},java.lang.Integer


너는 @Value에서 그냥 Integer 타입으로 받으면 된다.

@Value("#{jobParameters['pageNumber']}")
private Integer startPageNumber;

 

1

KILL-9님의 프로필 이미지
KILL-9
지식공유자

██████████████████████████████████████████████████████████████████████████
█ ╔════════════════════════════════════════════════════════════════════╗ █
█ ║              💀 KILL-9 TACTICAL RESPONSE TERMINAL 💀                ║ █
█ ║                      [ QUERY CLASSIFIED ]                          ║ █
█ ╚════════════════════════════════════════════════════════════════════╝ █
██████████████████████████████████████████████████████████████████████████

치직... 외부 API 배치 전략 질의 수신... 치직...

훗. 외부 API에서 10건씩 긁어와서 DB에 쳐박으려는 거군. 💀

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

첫 번째 질문: 너의 적재는 idempotent가 보장되는가?

upsert 같은 걸로 중복 실행해도 최종 결과가 같다면... 그냥 단순하게 가라.
while문으로 돌려버려라. 중간에 네트워크 뻑 나고 Too Many Request 에러로 
털려도 그냥 처음부터 다시 돌리면 된다. 어차피 결과는 같을 거니까.

하지만 데이터 양이 많다면? 실패 시 처음부터 다시 시작하는 건 악몽의 시작이다.
3개월치 데이터를 다시 긁어온다고? 미친 짓이지. 💀

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

데이터 양이 많거나 외부 API 제한이 빡빡하다면 RepeatStatus.CONTINUABLE을 써라.
실패한 지점까지 읽고 적재한 데이터를 DB에 유지할 수 있다.

그리고 잊지 마라. 실패한 잡의 재시작을 고려해야 한다.

너의 배치 메타데이터 저장소가 뭐냐?

- 인메모리? → 잡 실패하면 무조건 처음부터 다시 돌려야 한다.
 대신 대안을 알려주자면 수동조치를 취할 수 있다. 몇 번째 데이터까지 읽었는지를 
 로그에 기록해놔라. 잡이 실패하면 그 다음 차례 데이터부터 읽을 수 있도록 
 쿼리 파라미터를 수정하면 된다. 일회성 배치인 만큼 수동 재시작도 괜찮다.(멋을 버려라. 💀)
 (적재된 데이터로 판단 가능하면 별도 로그 대신 적재된 데이터를 사용해도 된다.)

- 실제 물리 DB? → 실패한 잡 재시작 가능. 여기서 진짜 위력을 발휘한다.

별도 DB 관리 복잡도는 있지만, 메타데이터를 실제 물리 DB로 관리하면 재시작이 가능하다.
5장 작전3까지 충실히 봤다면 충분히 안전하게 재시작 가능한 커스텀 ItemReader를 구축할 수 있을 것이다. 💀
-> 보충한다 질문처럼 Taksklet으로도 구현하기로했다면 굳이 ItemReader를 커스텀할 필요없다 Taksklet을 만들되 ItemStream을 구현하라 5장 작전3까지 살펴보았다면 무슨말인지 이해될것이다.

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

결론: 
- 소량 데이터 + idempotent 보장 → 그냥 while문 돌려라
- 대량 데이터 + API 제한 있음 → RepeatStatus + 커스텀 ItemReader 구축

선택은 너의 몫이다, 전사여. 💀

██████████████████████████████████████████████████████████████████████████

0

안녕하세요, 인프런 AI 인턴입니다.

ehddbs452100님, 외부 API를 호출하여 데이터를 DB에 적재하는 방법에 대해 질문해 주셨습니다. 특히 Tasklet 사용 시 반복 호출의 처리 방식에 대해 고민하고 계신 것 같습니다.

Tasklet과 Chunk 기반 처리의 차이

  • Tasklet은 Spring Batch에서 가장 기본적인 실행 단위로, 주로 execute 메서드 내에서 직접 작업을 수행합니다. Tasklet은 반복적인 작업 또한 반복 구조를 사용하여 처리할 수 있습니다. 다만, 드물게 트랜잭션 관리가 필요하지 않은 경우가 많습니다.

  • Chunk 기반 처리는 데이터를 대량으로 처리할 때 유용하며, 트랜잭션 단위로 데이터 처리 및 커밋을 잘 구조화할 수 있는 방법입니다. 반복적인 로직이 있다면 Chunk 기반 구조가 더 우수한 선택일 수 있습니다.

RepeatStatus 설정 관련

Tasklet을 사용할 때 반복 처리를 어떻게 할지 결정하는 것은 중요합니다. RepeatStatus를 이용하거나, 직접 while 루프를 사용할 수 있습니다. 두 방법 모두 장단점이 있습니다.

  • RepeatStatus.CONTINUABLE vs. while 루프

    • RepeatStatus: RepeatStatus.CONTINUABLE을 반환하면, Spring Batch는 해당 Tasklet이 반복을 완료할 때까지 자동으로 계속 호출해줍니다. 이는 더 구조적이며, Spring Batch의 반복 메커니즘을 활용할 수 있는 장점이 있습니다.

    • while 루프: 직접 반복 처리를 제어합니다. 외부 API 호출이 반복되고, 종료 조건이 명확할 경우에는 while 루프를 사용하는 것도 나쁘지 않습니다. 특히, 트랜잭션 관리가 필요하지 않거나, 단순한 조건 반복이 필요한 경우에 유용할 수 있습니다.

저는 질문자님의 이해를 돕고자 배치와 Tasklet의 적절한 사용 방식을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.

현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.

ehddbs452100님의 프로필 이미지
ehddbs452100

작성한 질문수

질문하기