Post

신규 웹 프로젝트 - 3 (캐싱)

#naver-import

원문: https://blog.naver.com/qoxmfaktmxj/223861419362

Spring Boot에서 공통 코드 테이블 캐싱 처리하기

프로젝트에서 공통 코드를 매번 DB에서 조회하지 않고, 서버 기동 시 캐싱해서 사용하는 구조로 개선했습니다. Vue 3와 Spring Boot(Java 1.8) 환경에서 적용한 방식이며, 등록/수정/삭제 시에는 자동으로 캐시를 갱신하도록 구성했습니다.


왜 캐싱을 도입했나?

공통 코드는 자주 변경되지 않으며, 다양한 화면에서 반복적으로 조회됩니다. 매번 DB에 접근하면 다음과 같은 문제가 있습니다:

  • 불필요한 쿼리 트래픽 증가
  • 응답 속도 저하
  • 다수의 화면에서 동일 데이터를 중복 조회

따라서 서버 구동 시 한 번만 로딩하고, 메모리에서 사용하는 방식으로 최적화했습니다.


캐시 구조 설계

Map<String /* GRCODE_CD */, List<Code>> codeCache;

  • GRCODE_CD 기준으로 그룹핑
  • List<Code>에는 해당 그룹 코드 목록 저장
  • CodeCacheService 라는 별도 클래스에서 캐시 관리

구현 방식 요약

1. CodeCacheService 클래스 생성

@Service public class CodeCacheService {

@Autowired private CodeMapper codeMapper;

private final Map<String, List<Code>> codeCache = new ConcurrentHashMap<>();

@PostConstruct public void init() { refreshAll(); }

public List<Code> getCodesByGroup(String grcodeCd) { return codeCache.getOrDefault(grcodeCd, Collections.emptyList()); }

public void refreshAll() { List<Code> allCodes = codeMapper.selectAllCodes(); Map<String, List<Code>> grouped = allCodes.stream() .filter(code -> “Y”.equalsIgnoreCase(code.getUseYn())) .collect(Collectors.groupingBy(Code::getGrcodeCd)); codeCache.clear(); codeCache.putAll(grouped); }

public void refreshGroup(String grcodeCd, String enterCd) { Map<String, Object> param = new HashMap<>(); param.put(“grcodeCd”, grcodeCd); param.put(“enterCd”, enterCd); List<Code> codes = codeMapper.getCodeList(param); codeCache.put(grcodeCd, codes); } }


2. 컨트롤러 수정 (조회 \& 캐시 갱신)


/code/list 캐시에서 조회 @GetMapping(“/list”) public ResponseEntity<List<Code>> getCodeList(@AuthenticationPrincipal CustomUserDetails currentUser, @RequestParam String grcodeCd) { List<Code> list = codeCacheService.getCodesByGroup(grcodeCd); return ResponseEntity.ok(list); }

/code/list 캐시에서 조회 codeCacheService.refreshGroup(code.getGrcodeCd(), code.getEnterCd());

삭제의 경우: deleteList.stream() .map(item -> item.get(“grcodeCd”)) .distinct() .forEach(grcode -> codeCacheService.refreshGroup(grcode, enterCd));


3. 전체 캐시 강제 갱신 API 추가

@GetMapping(“/cache/refreshAll”) public ResponseEntity<String> refreshAllCodes() { codeCacheService.refreshAll(); return ResponseEntity.ok(“전체 코드 캐시 갱신 완료”); }


결과

  • 자주 조회되는 공통 코드 캐싱으로 전체 성능 개선
  • 등록/수정/삭제 시 캐시 자동 동기화
  • 전체 리프레시도 수동 API로 제공
  • 프론트 Vue는 기존 API 그대로 사용 가능 (/code/list?grcodeCd=xxx)

마무리

이 방식은 공통 코드뿐만 아니라, 자주 변경되지 않는 기준 테이블들에도 적용하기 좋습니다.

추후 Redis 등으로 분산 캐시 확장도 고려할 수 있습니다.

댓글