문제 발생
Redis 키가 여기저기서 다른 규칙으로 만들어지고 있었다.
- 어떤 곳은
mdulInfo_home_main_01처럼 언더스코어 기반 - 어떤 곳은
home:main:01처럼 콜론 기반 - 어떤 곳은 빠른 만료 정책을 표기하려고 뒤에
Y만 덜렁 붙이기도 (예:home:main:01Y)
결과적으로 캐시 미스, 중복 저장, 운영 중 수작업 스캔 난이도가 상승했다.
로그 상으로는 “있어야 하는 키가 없거나(미스)” / “같은 데이터가 여러 키로 저장됨(중복)“이 반복됐다.
원인 파악
- 과거 코드에서 내려온
mdulInfo_접두사와 언더스코어(_) 구분 규칙이 새 코드로 넘어오며 일관성 없이 쓰였다. - 일부 모듈은
fastExpire같은 정책 플래그를 키 포맷 합의 없이 임의로 붙여서 파편화를 초래했다. - 운영/분석 시 Scan 패턴(
SCAN,KEYS)도 제각각이라 추적 난이도가 높았다.
핵심은 규칙(정규화) 부재였다.
해결
키 생성 시 단일 진입점을 강제하고, 다음 원칙을 합의했다.
- 접두사 제거 + 구분자는 콜론(
:) - 정책 플래그는 별도 세그먼트로 명시 (예:
:Y) - 호출부는 반드시 유틸 메서드만 사용
// 1) mdulInfo_ 접두사 제거 + '_' → ':' 통일
public static String getRedisModuleKey(String moduleKey) {
return moduleKey.replace("mdulInfo_", "").replace('_', ':');
}
// 2) fastExpire 정책 반영: 플래그는 별도 세그먼트로 명시
List<String> redisKeyList = moduleList.stream()
.map(module -> {
String moduleKey = RedisUtils.toMduleCode(module); // 내부 도메인 로직 → 최종 키 단위 문자열
String fastExpire = String.valueOf(module.get(RedisInfo.FAST_EXPIRE));
String baseKey = RedisUtils.getRedisModuleKey(moduleKey); // 규칙 진입점
return "Y".equals(fastExpire) ? baseKey + ":Y" : baseKey;
})
.collect(Collectors.toList());
중요 합의
- 키의 형태는
shop:module:group[:flag]로 고정- 빠른 만료는
:Y세그먼트로만 표현 (임의 접미 금지)- 생성은 반드시
getRedisModuleKey(...)를 통해 수행
예/반례로 검증
- 입력:
mdulInfo_home_main_01→ 출력:home:main:01 - 입력:
mdulInfo_home_main_01+ fastExpire=Y →home:main:01:Y - 잘못된 예:
home:main:01Y,mdulInfo_home_main_01:Y(접두사 잔존, 구분자 혼용)
간단한 단위 테스트 스케치:
@Test
void normalizeKey_basic() {
assertEquals("home:main:01", getRedisModuleKey("mdulInfo_home_main_01"));
}
@Test
void normalizeKey_withFlag() {
String base = getRedisModuleKey("mdulInfo_home_main_01");
assertEquals("home:main:01:Y", base + ":Y");
}
효과 / 깨달음
- 키 충돌·중복 저장 감소, 운영 스캔/모니터링이 한결 수월해졌다.
- 캐시 미스가 줄어들면서 TTL 정책 실험(fastExpire vs normal)도 투명해졌다.
- 새/구 코드가 섞여 있어도 유틸 진입점 강제만으로 정규화가 유지된다.
결국 캐시의 반은 네이밍 규칙이다. 규칙을 코드로 강제하지 않으면 반드시 새어 나온다.