공공부하자개발 · 영어 학습 노트
자바
미니 프로젝트쇼핑몰 정산 시스템 4단계0/4 완료
  • 011단계: 콘솔 주문 관리 (초급 기술)
  • 022단계: 객체지향 리팩터링 (중급 기술)
  • 033단계: 모던 자바 적용 (고급 기술)
  • 044단계: 일마감 정산 배치 (배치 기술)
사이트 소개개인정보처리방침연락처
© 2026 공부하자
홈 › 미니 프로젝트 › 04 / 4

4단계: 일마감 정산 배치 (배치 기술)

섹션 6진행 0 / 4
1이번 단계의 목표2설계3구현 따라가기4실행 결과와 해설5확장 과제6다음 단계로‹ 이전다음 ›

2. 설계

2.1 파이프라인

text
 data/orders.csv (10만 행)
      │
      ▼  ChunkReader.nextChunk(1000)          ← 체크포인트의 lastCommittedLine까지 건너뛰고 시작
 ┌────────────────────────────────────────────────────────────────────────┐
 │ 청크 1개 = 트랜잭션 1개                    work = new JobState()          │
 │                                                                        │
 │   RawLine ──▶ OrderParser.parse ──▶ OrderRecord                        │
 │                 │ SkipException(FIELD_COUNT / NUMBER_FORMAT / ...)      │
 │                 ▼                                                      │
 │              work.recordSkip(reason)                                   │
 │                                                                        │
 │   OrderRecord ──▶ RetryTemplate.chargeWithRetry ──▶ 승인 코드            │
 │                     │ PaymentGateway.charge(order, attempt)            │
 │                     │   TransientFailure → backoff → 재시도 (최대 3회)   │
 │                     │   Declined        → SkipException(DECLINED)      │
 │                     │   3회 소진        → SkipException(RETRY_EXHAUSTED)│
 │                     ▼                                                  │
 │              work.recordSuccess(order)   ← byCategory / byPayType 집계   │
 │                                                                        │
 │   청크 끝: committed.merge(work) ──▶ CheckpointStore.save(committed)     │
 │            └── 커밋 (메모리)             └── 커밋 (디스크, 원자적 교체)     │
 │   예외:    work 폐기 ──▶ 롤백. committed와 체크포인트는 그대로             │
 └────────────────────────────────────────────────────────────────────────┘
      │ 모든 청크 완료
      ▼
 SettlementWriter.write ──▶ settlement.tmp ──▶ ATOMIC_MOVE ──▶ settlement_2024-03-31.csv
 BatchReport.print

2.2 컴포넌트와 책임

컴포넌트 책임 배치 레벨 레슨
SampleDataGenerator 시드 고정 CSV 생성. 1.2% 오염 행 주입 02 파일 I/O
ChunkReader 헤더 건너뛰기, N행씩 반환, 시작 행까지 스킵 03 청크
OrderParser 행 → OrderRecord. 실패는 SkipException(사유) 05 스킵
PaymentGateway 해시 기반 결정적 장애: 일시 5%, 영구 0.1% —
RetryTemplate 일시 장애 최대 3회, 지수 백오프. 재시도 횟수 집계 05 재시도
JobState 커밋된 누적 상태 또는 청크 작업 상태. merge = 커밋. Properties 직렬화 04 트랜잭션
CheckpointStore JobState를 파일로 저장/복원. 임시 파일 + 원자적 교체 04 체크포인트
SettlementJob 파이프라인 조립. 청크 루프, 커밋/롤백, 장애 주입, 진행 로그 01 아키텍처
SettlementWriter 정산 CSV 출력. 임시 파일 + ATOMIC_MOVE 02 파일 I/O
BatchReport 최종 리포트 01 아키텍처
Main CSV 생성 → 1차 실행(장애) → 2차 실행(재개) → 검증 → 파일 확인 —

2.3 JobState 하나로 "커밋된 상태"와 "작업 중 상태"를 모두 표현

text
committed : JobState        ← 체크포인트에서 복원. 마지막 커밋까지의 누적
work      : JobState        ← 이 청크만의 카운터·집계. 매 청크 새로 생성

성공:  committed.merge(work)         work의 숫자를 committed에 더하고 lastCommittedLine 갱신
       checkpoint.save(committed)    디스크에 반영
실패:  (work를 그냥 버림)             committed는 한 글자도 안 바뀜 = 롤백

DB 트랜잭션의 "작업 중 변경은 커밋 전까지 다른 세션에 보이지 않는다"를 두 객체로 흉내 냈다. 같은 클래스를 두 역할로 쓰므로 merge가 대칭적이고, 3단계의 Inventory 분리처럼 "가변 상태를 한 클래스에 격리"한 것이다.

2.4 왜 결정적 장애인가

방법 재현성 교육 효과
Random.nextDouble() < 0.05 실행마다 다름. 시드를 고정해도 스레드·호출 순서가 바뀌면 달라짐 출력 대조 불가
hash(orderId, attempt) % 100 < 5 항상 같음. 어떤 주문이 몇 번째 시도에서 실패하는지 고정 재시작 후 결과가 단일 실행과 같음을 증명할 수 있음

PaymentGateway.mix는 SplitMix64 계열의 비트 섞기다. 순차 ID(ORD-0000001, ORD-0000002...)의 해시가 규칙적으로 몰리지 않게 큰 홀수 상수를 곱하고 시프트-XOR한다.

2.5 체크포인트 파일 형식

text
# settlement checkpoint
lastCommittedLine=39001
chunksCommitted=39
read=39000
processed=38495
skipped=505
retried=1976
skip.QUANTITY=226
skip.NUMBER_FORMAT=121
cat.전자=12776,1548012000,24471690
pay.CARD=19212,1585372500,39632985
...

Properties를 골랐다. JSON 라이브러리 없이 JDK만으로 읽고 쓸 수 있고, 사람이 열어서 확인할 수 있다. Writer로 저장하면 한글 키(cat.전자)도 그대로 쓰인다(OutputStream으로 저장하면 \uXXXX로 이스케이프된다).

2.6 원자적 파일 교체가 필요한 두 곳

파일 위험 해법
체크포인트 저장 도중 죽으면 반쪽 파일 → 재시작 시 파싱 실패 → 처음부터 .tmp에 쓰고 Files.move(..., ATOMIC_MOVE)
정산 결과 쓰는 도중 다운스트림이 읽음 → 합계 누락된 파일로 회계 처리 동일

ATOMIC_MOVE는 같은 파일 시스템 안에서 이름 변경(rename)이 원자적이라는 OS 보장을 쓴다. 파일은 "없음" 아니면 "완성" 두 상태만 갖는다.

설계
  • 2.1 파이프라인
  • 2.2 컴포넌트와 책임
  • 2.3 JobState 하나로 "커밋된 상태"와 "작업 중 상태"를 모두 표현
  • 2.4 왜 결정적 장애인가
  • 2.5 체크포인트 파일 형식
  • 2.6 원자적 파일 교체가 필요한 두 곳
이전 섹션1 이번 단계의 목표2 / 6다음 섹션3 구현 따라가기