- 근본 원인: splitDateRangeMonthly() cursor 이동 버그 - 재현 조건, 검증 결과, 교훈/방지 규칙 포함 - 코드베이스 전체 유사 패턴 점검 결과 포함
5.3 KiB
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_NONE → WSDL_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