[문제 상황]
뉴스기사 조회 API에서 사용자가 기사를 클릭하면 이 두 작업을 하나의 트랜잭션에서 처리하고 있었다.
1. 조회 이력 1건 생성
2. 기사 조회수 1증가
// ===== 비즈니스 로직 =====
public void increaseViewCount() {
this.viewCount++;
}
기존 조회수 증가 방식은 엔티티 메서드를 통해 위 처럼 처리했다.
- 메모리에서 값을 증가시키는 방식.
- 단일 요청에서는 문제가 없지만, 다중 사용자가 동시에 같은 기사를 조회하는 상황에서는 동시성 문제 발생 할 수 있었다.
[고려한 해결 방법]
1. DB 레벨 원자적 증가
UPDATE news_article
SET view_count = view_count + 1
WHERE id = ?
2. 비관적 락
조회 시점에 행에 락을 걸어 다른 트랜잭션이 동시에 수정하지 못하게 하는 방식
3. 낙관적 락
버전 컬럼(@Version)을 두고, 수정 충돌이 발생하면 예외를 발생시켜 재시도 하는 방식
1번 방식을 선택하였다.
//뉴스기사 조회수 증가
@Modifying
@Query("UPDATE NewsArticle n SET n.viewCount = n.viewCount + 1 WHERE n.id = :articleId")
void incrementViewCount(@Param("articleId") UUID articleId);
- 엔티티를 읽지 않고 바로 UPDATE 쿼리를 날린다.
- @Modifying: 기본적으로 @Query는 SELECT용이라서 UPDATE시 붙여줘야한다.
[기존 방식]
NewsArticle article = findById(id);
article.increaseViewCount(); // viewCount++
- DB에서 조회하고, 메모리에서 +1한다음, 다시 DB에 저장하는 흐름
ex) 초기값 10, 동시에 2개 요청
A: 10읽음 -> 11 저장
B: 10읽음 -> 11 저장
결과: 11 (Lost Update)
[현재 방식]
- DB가 직접 현재 값 기준으로 +1 수행하기때문에 읽기 없이 바로 반영된다.
ex) A: DB -> 10 -> 11
B: DB -> 11 -> 12
DB가 각 UPDATE를 순차적으로 처리하면서 값 누락이 없다.
'코드잇 스프린트 > 실습' 카테고리의 다른 글
| Monew 프로젝트 개선: 뉴스기사 목록 조회 성능 개선 - 복합 인덱스 적용기 (0) | 2026.05.07 |
|---|---|
| Docker 컨테이너 포트를 80으로 쓰면 안되는 이유 (0) | 2026.04.29 |
| MoNew 프로젝트: ERD 설계 데이터 타입에 대한 고민(VARCHAR vs TEXT) (0) | 2026.04.15 |
| FINDEX 프로젝트: Railway로 프로젝트 배포하기 (0) | 2026.03.17 |
| Findex 프로젝트: 지수정보 update가 이루어지지않는다 400에러 (0) | 2026.03.17 |