문제 발생

배너 데이터를 Redis에 저장하면서, bnrSeq 값이 없는 케이스가 종종 들어왔다. 원래는 무조건 존재한다고 가정하고 Map을 파싱했는데:

  • bnrSeq가 null이거나 비어 있는 경우 → NPE 발생
  • 리스트 안에 Map이 섞여 들어오는 경우도 있어서 캐스팅 에러 발생
  • 결국 캐시 로딩이 중간에 터져서 나머지 데이터도 못 올라감

원인 파악

  • 데이터 제공 API가 간헐적으로 bnrSeq를 내려주지 않는 경우가 있었다.
  • VO 변환 시 타입 체크를 제대로 하지 않아서 List<Object> 안에 Map이 섞이면 런타임 에러가 발생했다.
  • 개발 단계에서는 테스트 데이터가 항상 정상이라 실서버에서만 문제가 드러났다.

해결

안전하게 bnrSeq를 꺼내도록 방어 로직을 넣고, 리스트/맵 타입을 먼저 점검하도록 수정했다.

private String safeGetBnrSeq(Object obj) {
    if (obj instanceof Map) {
        Object seq = ((Map<?, ?>) obj).get("bnrSeq");
        return seq != null ? String.valueOf(seq) : "";
    }
    return "";
}

public void processBannerList(List<Object> list) {
    for (Object o : list) {
        String bnrSeq = safeGetBnrSeq(o);
        if (StringUtils.isNotBlank(bnrSeq)) {
            // 정상 케이스만 Redis에 적재
            redisService.set("bnr:" + bnrSeq, o.toString(), 3600);
        } else {
            log.warn("bnrSeq 누락 데이터 무시: {}", o);
        }
    }
}

포인트:

  • instanceof로 타입 보장
  • bnrSeq가 없으면 빈 문자열을 반환하고 무시 처리
  • Redis에 올라가는 값은 항상 key-value가 보장된 상태

효과 / 깨달음

  • 런타임 에러가 사라지고, 문제가 되는 데이터는 무시 처리로 전환해 서비스 안정성을 확보했다.
  • 로그로만 기록하니 원본 데이터 문제도 쉽게 트래킹 가능해졌다.
  • 외부 데이터는 절대 신뢰하면 안 된다. 캐시 적재 전에 무조건 필터링과 검증이 필요하다.