[Spring Boot] 게시글 조회수 및 좋아요 동시성 문제와 성능 개선 (Redis 도입기)
🚨 문제 인식 (Troubleshooting)
현재 개발 중인 커뮤니티 플랫폼에서 게시글 상세 조회와 좋아요 기능을 구현했습니다. 기능적으로는 정상 작동하는 MVP(Minimum Viable Product) 상태였지만, "대용량 트래픽이 발생한다면 이 코드가 버틸 수 있을까?"라는 의문이 들었습니다.
기존 코드를 분석해 본 결과, 실무 환경에서는 치명적일 수 있는 두 가지 문제점을 발견했습니다.
java
// As-Is: 기존 BoardService 로직
@Transactional
public PostResponse incrementViewCount(Long postId) {
Post post = postRepository.findById(postId)
.orElseThrow(() -> new IllegalArgumentException("게시글을 찾을 수 없습니다."));
post.incrementViewCount(); // DB의 조회수 + 1
return new PostResponse(...);
}
문제점:
한 명의 유저가 F5(새로고침)를 100번 누르면 조회수가 그대로 100이 올라가는 어뷰징에 무방비했습니다.
유저가 게시글을 클릭할 때마다 무조건 DB에 UPDATE 쿼리가 날아갑니다. 트래픽이 몰릴 경우 DB 커넥션이 고갈되고 엄청난 병목 현상이 발생할 수 있습니다.
2. 좋아요(Likes) 동시성 문제 및 DB Lock
좋아요 기능 역시 PostLike라는 매핑 테이블을 두어 정석대로 구현했습니다.
java
// As-Is: 기존 BoardService 로직
@Transactional
public boolean toggleLike(Long postId, String username) {
Post post = postRepository.findById(postId)...
Member member = memberRepository.findByUsername(username)...
Optional existingLike = postLikeRepository.findByMemberAndPost(member, post);
if (existingLike.isPresent()) {
postLikeRepository.delete(existingLike.get());
post.decrementLikeCount(); // 취소 시 감소
} else {
postLikeRepository.save(new PostLike(member, post));
post.incrementLikeCount(); // 클릭 시 증가
}
}
문제점: 로직 자체는 흠잡을 데가 없지만, 동시성(Concurrency) 측면에서 위험합니다. 유명인의 게시물에 수백 명이 동시에 좋아요를 누르게 되면, 1번 게시글(Row)에 수많은 쓰레드가 동시에 UPDATE 쿼리를 날리게 됩니다. 이는 심각한 DB Lock 경합을 유발하여 서버 장애로 이어질 수 있습니다.
🛠 해결 방안: Redis 인메모리 캐시 도입 (To-Be)
관계형 데이터베이스(RDBMS)에 직접 쿼리를 때리는 기존 방식을 버리고, 응답 속도가 매우 빠른 Redis(인메모리 데이터 저장소)를 활용한 아키텍처로 전면 개편하기로 결정했습니다.
단순히 비즈니스 로직을 구현하는 것을 넘어, DB 트랜잭션을 최소화하고 캐시 서버를 적재적소에 활용하는 것이 백엔드 개발자의 진짜 핵심 역량이라는 것을 깨닫는 귀중한 트러블슈팅 경험이었습니다. 앞으로 남은 동기화 스케줄러 로직 구현과 프론트 연동 테스트도 꼼꼼히 진행할 예정입니다! 🚀