2026. 7. 24. 16:19ㆍ부트캠프/TIL(Today I Learned)
오늘 배운 내용
오늘은 크게 다섯 가지를 배웠다.
- Redis Sorted Set과 리더보드
- CQRS
- NoSQL
- Event Sourcing
- 캐싱(Cache)
1. Redis Sorted Set과 리더보드
게임을 하면 항상 랭킹이 있다.
- 점수 랭킹
- 인기 검색어
- 인기 상품
- 좋아요 순위
이런 기능을 리더보드(Leaderboard) 라고 한다.
처음에는 이런 생각을 했다.
"그냥 MySQL에서 ORDER BY 하면 끝 아닌가?"
하지만 실제 서비스에서는 수백만 명이 계속 점수를 올리고 있기 때문에 문제가 생긴다.
예를 들어 인기 상품 Top10을 가져오려면
SUM()
COUNT()
GROUP BY
ORDER BY
같은 집계가 계속 발생한다.
사용자가 많아질수록 DB는 계속 계산해야 하므로 점점 느려진다.
Redis Sorted Set은 왜 빠를까?
Redis에는 Sorted Set(ZSET) 이라는 자료구조가 있다.
여기에는
상품A 300점
상품B 120점
상품C 700점
처럼
값 + 점수(score) 를 함께 저장한다.
그리고 내부적으로 항상 정렬된 상태를 유지한다.
그래서
Top10 가져와
라고 하면
이미 정렬되어 있기 때문에 바로 반환한다.
데이터 흐름
사용자가 상품 구매 -> Redis 점수 증가(ZADD) -> Redis 내부에서 자동 정렬 -> Top10 요청 -> 바로 반환
MySQL은
조회할 때 정렬
Redis는
저장할 때 미리 정렬
이라는 차이가 있다.
2. CQRS
오늘 가장 중요한 개념이었다.
CQRS는
Command Query Responsibility Segregation
명령(Command)과 조회(Query)의 책임을 나누는 패턴이다.
CRUD에서는?
평소 Spring에서는
Controller
Service
Repository
DB
모든 요청을 하나의 Service가 처리한다.
조회
수정
삭제
생성
모두 Service 하나
CQRS에서는?
조회와 변경을 아예 분리한다.
ProductCommandService
↓
생성
수정
삭제
그리고
ProductQueryService
↓
조회
이렇게 나눈다.
왜 나눌까?
게임을 생각하면 이해가 쉽다.
하루 동안
닉네임 조회
레벨 조회
인벤토리 조회
친구 조회
이런 조회는 엄청 많다.
반면
닉네임 변경
아이템 구매
레벨 저장
이런 쓰기는 훨씬 적다.
예를 들어
조회 99%
쓰기 1%
라면
조회 서버를 훨씬 많이 두는 게 효율적이다.
CQRS 데이터 흐름
사용자 -> 쓰기 요청 -> Command Service -> MySQL -> 이벤트 발생 -> Kafka
-> Projector -> MongoDB -> 조회 요청 -> Query Service MongoDB 응답 -> 사용자
읽기와 쓰기가 서로 독립적으로 움직인다.
3. NoSQL
NoSQL은
RDBMS가 아닌 데이터베이스를 통틀어 부르는 이름이다.
대표적으로
- MongoDB
- Redis
- Cassandra
등이 있다.
왜 조회에 많이 사용할까?
상품 상세 페이지를 생각해보자.
화면 하나에
- 이름
- 가격
- 리뷰
- 평점
- 배송
- 이미지
- 할인
모든 데이터를 보여준다.
RDBMS에서는
JOIN
JOIN
JOIN
JOIN
계속 발생한다.
하지만 MongoDB에서는
상품 하나를
JSON 하나에 저장한다.
{
"name": "...",
"price": "...",
"review": "...",
"rating": "...",
"delivery": "..."
}
조회하면 끝이다.
JOIN이 없다.
핵심 한 줄
쓰기는 정규화, 읽기는 비정규화
4. Event Sourcing
처음에는 이해가 어려웠다.
기존 CRUD는
현재 상태만 저장한다.
예를 들어 장바구니라면
현재
상품A
2개
만 저장한다.
Event Sourcing은 다르다.
현재 상태 대신
발생한 모든 사건(Event)을 저장한다.
예를 들어
장바구니 생성
↓
상품 추가
↓
상품 삭제
↓
수량 변경
↓
상품 추가
이 모든 기록이 저장된다.
현재 상태는
이 이벤트들을 다시 재생해서 계산한다.
장점
- 언제든 과거 상태를 복원 가능
- 감사 로그 생성
- 디버깅 쉬움
단점
매번 이벤트를 재생하면 느리다.
그래서
CQRS와 함께
조회용 View를 따로 만든다.
5. 캐싱(Cache)
오늘 가장 실무적인 내용이었다.
캐시는
자주 사용하는 데이터를 미리 저장해두는 기술
이다.
CPU도 캐시를 사용한다.
브라우저도 캐시를 사용한다.
웹 서버도 Redis 캐시를 사용한다.
결국
"자주 쓰는 건 가까이에 두자."
라는 아이디어이다.
데이터 흐름
사용자 요청
↓
캐시 확인
↓
있음(Hit)
↓
바로 응답
-------------------
없음(Miss)
↓
DB 조회
↓
캐시에 저장
↓
사용자 응답
처음에는 느리지만
두 번째부터는 엄청 빠르다.
Cache Hit
캐시에 데이터가 있다.
사용자
↓
Redis
↓
응답
DB를 안 간다.
Cache Miss
캐시에 없다.
사용자
↓
Redis
↓
없음
↓
MySQL 조회
↓
Redis 저장
↓
응답
캐시 삭제 정책(Eviction)
캐시는 메모리가 한정되어 있다.
가득 차면 오래된 데이터를 버려야 한다.
대표적인 방법은
- LRU : 가장 오래 안 쓴 것 삭제
- LFU : 가장 적게 사용한 것 삭제
- FIFO : 먼저 들어온 것 삭제
- Random : 랜덤 삭제
캐싱 전략
오늘 여러 전략도 배웠다.
Cache Aside
가장 많이 사용하는 방식.
캐시 확인
↓
없음
↓
DB
↓
캐시 저장
Write Through
쓰기
↓
DB 저장
↓
캐시 저장
항상 둘 다 업데이트한다.
Write Back
캐시에 먼저 저장
↓
나중에 DB 저장
빠르지만 데이터 유실 위험이 있다.
Refresh Ahead
만료되기 전에
미리 캐시를 갱신한다.
Spring Cache 적용
Spring에서는
@EnableCaching
하나로 캐시 기능을 활성화할 수 있다.
그리고
@Cacheable
을 사용하면
메서드 결과를 자동으로 캐시에 저장한다.
첫 호출
↓
DB 조회
↓
Redis 저장
↓
응답
-----------------
두 번째 호출
↓
Redis
↓
응답
코드를 거의 수정하지 않고 성능을 높일 수 있다는 점이 인상 깊었다.
오늘 메모에서 추가하면 좋은 개념
1. TTL(Time To Live)
캐시는 영원히 저장되면 안 된다.
예를 들어 상품 가격이 바뀌었는데 캐시에 예전 가격이 남아 있으면 잘못된 정보를 보여준다.
그래서 캐시에는 유효 시간(TTL) 을 둔다.
10초 저장
↓
10초 후 자동 삭제
↓
다음 요청에서 다시 DB 조회
오늘 실습에서 10초 뒤 itemCache가 사라진 이유가 바로 TTL 때문이다.
2. Cache Invalidation (캐시 무효화)
캐시에서 가장 어려운 문제 중 하나가 언제 캐시를 지울 것인가이다.
예를 들어 상품 정보를 수정했다면,
상품 수정
↓
DB 저장
↓
기존 캐시 삭제
↓
다음 조회 시 새로운 데이터 저장
이 과정을 캐시 무효화(Cache Invalidation) 라고 한다.
Spring의 @CacheEvict가 이 역할을 한다.
3. 왜 CQRS는 결과적 일관성을 사용할까?
읽기 DB와 쓰기 DB가 분리되어 있기 때문에,
쓰기 직후에는 읽기 DB가 아직 최신 데이터를 반영하지 못할 수 있다.
예를 들어,
상품 수정
↓
MySQL 저장 완료
↓
아직 MongoDB 동기화 전
↓
사용자가 조회
잠깐 동안 이전 데이터가 보일 수 있다.
이를 결과적 일관성(Eventual Consistency) 이라고 한다.
시간이 조금 지나면 두 DB의 데이터가 같아진다.
오늘 느낀 점
처음에는 Redis, CQRS, NoSQL, Event Sourcing이 서로 완전히 다른 기술이라고 생각했다. 그런데 흐름을 따라가 보니 모두 연결되어 있었다.
조회가 많아지면 캐시를 사용하고, 조회와 쓰기의 특성이 달라지면 CQRS를 도입하며, 조회를 더 빠르게 만들기 위해 NoSQL을 사용한다. 여기에 변경 이력을 모두 보관해야 하는 시스템이라면 Event Sourcing까지 적용할 수 있다.
내일은 주말이다.
주말간에 인메모리와 대규모스트림 강의를 완강할 예정이다. 그러면서 주말에 생각을 정리할 생각이다.
'부트캠프 > TIL(Today I Learned)' 카테고리의 다른 글
| [내일배움캠프_사전캠프_8기] TIL Days-22 인 메모리 데이터베이스 (0) | 2026.07.22 |
|---|---|
| [내일배움캠프_사전캠프_8기] TIL Days-21 Docker, Redis, MSA 진짜 시작 (0) | 2026.07.21 |
| [내일배움캠프_사전캠프_8기]_Days_19 TIL_ 팀 프로젝트-발표 (0) | 2026.07.20 |
| [내일배움캠프_사전캠프_8기]_Days_18 TIL_ 팀 프로젝트-Day10 (0) | 2026.07.15 |
| [내일배움캠프_사전캠프_8기]_Days_17 TIL_ 팀 프로젝트-Day9 (0) | 2026.07.14 |