Files
sam-docs/sam/docs/changes/20260304_eaccount_infinite_loop_fix.md
김보곤 ee42b12c2b docs: [changes] 계좌 입출금내역 무한루프 버그 분석 문서 추가
- 근본 원인: splitDateRangeMonthly() cursor 이동 버그
- 재현 조건, 검증 결과, 교훈/방지 규칙 포함
- 코드베이스 전체 유사 패턴 점검 결과 포함
2026-03-04 13:32:06 +09:00

5.3 KiB

계좌 입출금내역 부분 월 조회 시 무한루프 크래시 수정

날짜: 2026-03-04 작업자: Claude Code


변경 개요

계좌 입출금내역 페이지에서 날짜를 수동 입력하여 조회 시 500 에러가 발생하는 문제를 수정했다. 편의 버튼(이번달, 지난달 등)은 항상 전체 월(1일말일)을 사용하여 문제가 없었으나, 수동으로 날짜를 입력하면 부분 월(예: 12/0112/18)이 되어 무한루프가 발생했다.


근본 원인

splitDateRangeMonthly() 함수의 cursor 이동 버그

긴 기간 조회 시 바로빌 SOAP API의 한계로 인해 기간을 월별 청크로 분할하는 함수에서, endDate가 월 중간일 때 cursor가 같은 달 1일로 되돌아가 무한루프가 발생했다.

// ❌ 버그 코드 — endDate가 월 중간이면 무한루프
$cursor = $chunkEnd->copy()->addDay()->startOfMonth();

// 예시: endDate = 20251218
// chunkEnd = 20251218
// → addDay()      = 20251219
// → startOfMonth() = 20251201  ← 같은 달 1일로 되돌아감!
// → while($cursor <= $end) 조건 여전히 true → 무한 반복
// ✅ 수정 코드 — chunkStart 기준으로 다음 월로 이동
$cursor = $chunkStart->copy()->addMonth()->startOfMonth();

// 예시: startDate = 20251201
// chunkStart = 20251201
// → addMonth()     = 20260101
// → startOfMonth() = 20260101  ← 다음 달로 정상 이동
// → while($cursor <= $end) 조건 false → 루프 종료

재현 조건

조건 결과
전체 월 (12/01~12/31) 정상 — addDay() = 01/01 → startOfMonth() = 01/01
부분 월 (12/01~12/18) 무한루프addDay() = 12/19 → startOfMonth() = 12/01
다중 월 (12/01~02/18) 무한루프 — 마지막 월이 부분 월이면 동일 증상

증상

  • PHP 프로세스가 메모리 한도(256M/512M)에 도달하여 Fatal Error로 크래시
  • Laravel 로그에 에러 기록 없음 (try-catch 밖에서 프로세스가 종료)
  • 프론트엔드에 서버 응답 오류 (500): (빈 응답 본문)

수정된 파일

파일 변경 내용
app/Http/Controllers/Barobill/EaccountController.php splitDateRangeMonthly() cursor 이동 로직 수정

검증 결과

tinker에서 수정 전후 비교 테스트:

=== 수정 전 (버그): 20251201~20251218 ===
→ 같은 청크 무한 반복 (10회 제한으로 강제 중단)

=== 수정 후: 20251201~20251218 ===
→ [{start: 20251201, end: 20251218}]  ← 1개 청크, 정상

=== 수정 후: 20251201~20260218 (다중 월) ===
→ [{20251201~20251231}, {20260101~20260131}, {20260201~20260218}]  ← 3개 청크, 정상

=== 수정 후: 20251215~20251231 ===
→ [{start: 20251215, end: 20251231}]  ← 1개 청크, 정상

동일 패턴 코드베이스 점검 결과

sam/mng 전체를 검색하여 유사 패턴을 점검했다:

파일 함수 패턴 위험도
EaccountController.php splitDateRangeMonthly() 월별 청크 분할 수정 완료
DashboardStatService.php generateDateRange() addDay() 단순 증가 안전
InspectionCycle.php getHolidayDates() addDay() 단순 증가 안전
CorporateCardController.php getNextBusinessDay() addDay() 단순 증가 안전
PartitionManagementService.php addPartitions() for 루프 (고정 횟수) 안전

결론: EaccountController 외에 동일 버그 패턴 없음. 다른 코드들은 모두 addDay() 단순 증가 패턴을 사용하여 무한루프 위험 없음.


교훈 및 방지 규칙

R1. 날짜 cursor 이동 시 chunkEnd 기반 이동 금지

// ❌ 위험: chunkEnd가 월 중간이면 startOfMonth()가 같은 달로 되돌림
$cursor = $chunkEnd->copy()->addDay()->startOfMonth();

// ✅ 안전: chunkStart 기준으로 항상 다음 월로 이동
$cursor = $chunkStart->copy()->addMonth()->startOfMonth();

R2. 날짜 루프에 안전장치(max iterations) 추가 권장

$maxIterations = 120; // 10년 = 120개월
$iterations = 0;

while ($cursor->lte($end) && $iterations < $maxIterations) {
    // ... 청크 처리 ...
    $iterations++;
}

if ($iterations >= $maxIterations) {
    Log::error('날짜 분할 루프 안전장치 작동', compact('startDate', 'endDate'));
}

R3. 부분 월 테스트 필수

날짜 범위를 분할하는 코드 작성/수정 시 반드시 다음 케이스를 테스트:

  • 전체 월 (01일~말일)
  • 부분 월 — 시작 (01일~중간)
  • 부분 월 — 끝 (중간~말일)
  • 다중 월 (마지막 월이 부분 월)
  • 같은 날 (시작일 = 종료일)

부수 개선 사항

이 문제 조사 과정에서 추가로 발견/수정된 항목:

항목 내용
WSDL 캐싱 WSDL_CACHE_NONEWSDL_CACHE_BOTH (4개 바로빌 컨트롤러 전체)
소켓 타임아웃 default_socket_timeout 60→120초 연장
Shutdown handler PHP Fatal Error 감지 시 Laravel 로그에 기록
SOAP 호출 로깅 호출 시작/완료 시간 + 소요시간(ms) 기록

관련 문서

  • app/Http/Controllers/Barobill/EaccountController.php — 바로빌 계좌 입출금내역

최종 업데이트: 2026-03-04