Skip to main content

Command Palette

Search for a command to run...

[Redis] Redis 캐시 설정 수정 - DefaultTyping.EVERYTHING 삭제

Updated
2 min read

지난번에 설정한 DefaultTyping.EVERYTHING에서 보안 문제가 생길 수 있다는 이슈를 들었다. 그래서 이번엔 그 부분을 제거하는 방향으로 수정하게 되었다.


1. DefaultTyping.EVERYTHING의 문제점

처음 설계는 Redis용 ObjectMapper에 DefaultTyping.EVERYTHING를 써서 모든 캐시 데이터에 강제로 자바 클래스 정보(@class)를 다 넣는 구조였다. 하지만 이러면 몇 가지 문제가 생기게 된다. 첫 번째, 내 설정 하나 때문에 다른 팀원이 만든 캐시 데이터 포맷까지 강제로 바뀔 수 있다. 두 번째, 모든 클래스를 다 받아 주는 구조라 외부에서 이상한 클래스 정보가 섞여 들어오면 역직렬화 과정에서 RCE 공격 위험이 생길 수 있다. 그래서 보안적으로도, 팀 단위 코드 기준으로도 사용하지 않는 게 좋다.


2. 해결 과정

수정 전 구조에서는 Redis에 Java 객체를 그대로 저장한 게 아니었다. 나는 그냥 객체로 들어갔다가 객체로 나오는 줄 알고 쓴 거고,

PostCacheDTO -> JSON -> Redis -> LinkedHashMap

실제로는 이 흐름인 것이다.

그래서 생각한 해결 방법은 캐시에서 꺼낸 Map을 우리가 직접 DTO로 변환해 주자였고,

LinkedHashMap -> PostCacheDTO

그 결과 이렇게 바꿔 주는 코드가 추가되었다.

그리고 인기글 캐시는 단순한 목록 캐시가 아니라 로그인/비로그인, 좋아요 여부, 이미지 URL 가공 로직까지 섞이기 때문에 결과를 무조건 캐시하게 되면 안 된다. 어디까지 캐시할지 결정하는 로직이 필요하다고 생각되어서

1. DB에서 가져온 공통 데이터만 캐시

2. 좋아요 여부, 이미지 URL 같은 건 요청 시점에 조립

이 구조를 만들기 위해 PopularPostCacheService 파일을 따로 생성하였다. 그리고 @Cacheable을 기존 Service나 Repository에 붙이지 않고 PopularPostCacheService로 분리했다. 이렇게 하니 캐시 정책이 한곳에 모이고, 요청자 기준 분기 로직도 훨씬 관리하기 쉬워졌다.

objectMapper 함수는 JacksonConfig 파일에 존재하기 때문에 중복 코드는 지우고 끌어와서 쓰게 되었다.


3. 해결 결과

Redis에서 캐시를 꺼낸다고 해서 항상 PostCacheDTO가 그대로 나오는 것은 아니었다. 스프링 캐시의 내부 동작 때문에 최초로 DB에서 조회하여 캐시에 저장하는 시점에는 메서드가 반환한 PostCacheDTO가 메모리에 남아 있어 그대로 반환한다. 하지만 캐시가 이미 존재하여 Redis에서 데이터를 읽어오는 시점에는 타입 정보(@class)가 없으므로 Jackson이 이를 LinkedHashMap으로 읽어오게 된다.

  • 첫 번째 호출 (DB 조회) -> PostCacheDTO

  • 두 번째 이후 호출 (캐시 히트) -> LinkedHashMap

최종적으로는 이런 흐름으로 진행하게 된다.


+. @Cacheable에 관하여

@Cacheable을 남발하면 좋지 않다는 얘기를 들었다. 그래서 조사해 보았더니 이 부분은 상황마다 다른 것 같다. 데이터가 자주 바뀌지 않거나, 조회가 매우 잦거나, DB 조인 쿼리가 무거울 때 쓰면 효과가 좋다고 한다. 내가 맡은 인기글도 조회가 잦은 홈페이지에서 사용되니 어노테이션을 사용해도 괜찮은 것이다. 반대로 주식처럼 실시간으로 계속 바뀌는 데이터나 어쩌다 한 번만 조회되는 데이터, DB 조회가 아주 단순하고 빠른 경우엔 오히려 손해라 안 쓰는 게 좋다고 한다.


오늘의 요약

  • Redis에 대해 제대로 이해하지 않고 진행하다 보니 역직렬화에서 계속 부딪히게 됐다. 꼭 이해하고 넘어가자.

More from this blog

[Elasticsearch] Elasticsearch 기본용어와 CRUD 명령어

-elastic ▶ 들어가며 이번 글에서는 Elasticsearch를 공부하면서 가장 먼저 익혀야 하는 기본 용어를 정리하고,직접 코드를 쳐가며 CRUD(Create / Read / Update / Delete) 명령어에 익숙해지는 시간을 가져보려고 한다. Elasticsearch는 처음 보면 생소한 용어가 많아서 막막할 수 있는데,사실 구조적으로는 우리가 익숙한 MySQL과 닮은 부분이 굉장히 많다. 둘 다 데이터베이스라는 큰 틀 안에서 데...

Feb 17, 20264 min read

Jpa N+1 문제, 우리는 이렇게 잡았다 — 1:1 문의 Api 실전 최적화기

코드 리뷰 한 줄에서 시작된 쿼리 최적화 여정 1. 시작 — "일단 돌아가게 만들자" Finders 프로젝트에서 1:1 문의(Inquiry) API를 맡았다. 현상소에 문의를 남기고, 답변을 받고, 목록을 조회하는 — 평범한 CRUD다. "JPA 쓰면 쿼리 안 짜도 되는 거 아니야?" 솔직히 처음엔 그렇게 생각했다. JpaRepository에 findAll, findById 쓰면 끝이니까. // 첫 번째 버전의 목록 조회 (QueryDS...

Feb 11, 20268 min read

[모니터링] Sentry 도입부터 Discord 에러 알림까지 — 서버 감시 시스템 구축기

"서버 죽었는데 아무도 몰랐다"에서 "에러나면 1분 안에 안다"까지 1. 모니터링을 시작한 계기 프론트엔드: "API 안 되는데요?"백엔드: "엥? 언제부터요?"프론트엔드: "...2시간 전부터요?"백엔드: 😱 어느 날 서버가 죽어있었는데 아무도 몰랐다. 그날 이후, 모니터링 시스템 구축을 결심했다. (Issue #102) 2. 모니터링 도구 비교: 뭘 쓸까? 처음에는 여러 도구를 비교했다. 도구무료 티어장점단점 Sent...

Feb 11, 20268 min read

[CI/CD] 수동 태그에서 자동 릴리즈까지 — Git Flow와 Auto Release

🚀 우리 auto-release.yml 바로 보러가기 → 이 글에서 설명하는 워크플로우의 전체 코드를 바로 확인할 수 있다! main에 머지만 하면 버전 태그부터 릴리즈 노트까지 알아서 생긴다 1. 우리의 Git 전략: Git Flow (경량 버전) Finders 프로젝트는 Git Flow 전략을 사용하고 있다. 다만 hotfix나 release 브랜치 없이, 조금 가볍게 운영한다. main ← 운영 서버 (prod) 배포 브...

Feb 11, 20265 min read

[ci/cd] 대학생 팀의 배포 파이프라인 진화기

PR 하나면 끝나는 무중단 배포까지, 삽질의 기록 1. 시작은 단순했다 "서버에 올려야 하는데... 어떻게 하지?" Finders 프로젝트를 시작했을 때, 배포라는 걸 해본 적이 없었다. 그래서 처음에는 이렇게 했다. 1. SSH로 서버 접속 2. git pull 3. ./gradlew build 4. java -jar app.jar 당연히 문제가 생겼다. 빌드하는 동안 서버가 꺼져있음 (프론트: "API 왜 안 돼요?" 🔥) 빌...

Feb 11, 20267 min read
F

Finders Tech Blog

16 posts