[SPOT] 조회수 어뷰징을 방지해보자.

김민석·2025년 9월 20일
1

SPOT

목록 보기
11/11
post-thumbnail

문제 상황

스팟에서 게시판 메인에는 ‘BEST 인기글’이라는 기능이 있다. 여기서 인기 점수를 계산할 때 좋아요 수, 조회 수, 댓글 수를 섞어서 계산하는데, 이 중 제일 조작하기 쉬운 게 조회 수다.

지금 구조는 단순하다. 게시글 상세 페이지에 들어오면 무조건 조회수가 +1 된다. 그런데 이러면 누가 새로고침만 눌러도 조회수가 쭉쭉 올라가 버린다. 사실 이건 악용 가능성이 너무 높고, 형평성에도 맞지 않는다. 예를 들어 A라는 글은 사람들이 진짜로 읽어서 조회수가 늘어난 거고, B라는 글은 글쓴이가 새로고침으로 억지로 올려버린 거라면, 둘이 똑같이 취급되는 게 말이 안 된다고 생각했다.

그래서 이번에 리팩토링 하면서 조회 수 어뷰징 방지를 고민하게 됐다.

일단 고민 범위를 줄였다. 스팟은 로그인하지 않은 사용자에게는 애초에 게시판 기능을 제공하지 않는다. 즉, 비로그인 사용자는 조회수를 올릴 수 있는 구조 자체가 아예 없다. 그래서 “로그인한 사용자”에 대해서만 조회수를 어떻게 셀지 신경 쓰면 된다.

회원 식별 기준 고민

“그럼 로그인 한 사용자를 고유하게 식별할 수 있는 값이 뭐가 있을까?”라는 고민이 들었다.

처음에 떠오른 건 JWT 토큰이었다. 토큰 자체를 게시글 ID랑 묶어서 키로 쓰면 한 번 조회한 사용자가 또 조회하는 건 막을 수 있겠다고 생각했다. 근데 문제는 토큰은 재로그인할 때마다 새로 발급된다는 거다. 이러면 사실상 같은 회원인데, 토큰이 달라져서 다른 사람처럼 인식되는 허점이 생긴다. 그래서 토큰은 배제했다.

그 다음 고민은, “애초에 토큰 안에 들어있는 회원 ID를 쓰면 되지 않나?” 였다. 이러면 토큰이 재발급되든 말든, 회원 단위로 조회 여부를 체크할 수 있다. 결국 이를 통해 어뷰징을 막기로 결정했다.

구현 방식 후보

“어떤 회원이 어떤 게시글을 조회했는지”를 기록하는 게 핵심이었다. 그래서 세 가지 방법을 비교했다.

1) Java Map

제일 먼저 떠오른 건 그냥 서버 메모리에 Map을 두는 방식이었다.

  • 장점: 빠르고 간단하다.
  • 단점: 분산 환경에서 각 인스턴스끼리 공유가 안 된다. 서버를 3대 띄우면, 한 서버에서 막아도 다른 서버에선 또 올라간다. 그리고 트래픽이 몰리면 힙 메모리가 터질 수 있다.

2) RDB (DB에 기록)

두 번째는 그냥 DB에 기록하는 방식이었다.

  • 장점: 영속성 보장, 정합성 보장. DB에 남아있으니 조회 이력 자체를 관리할 수도 있다.
  • 단점: 속도가 느리다. 조회 수 카운트처럼 엄청 자주 일어나는 작업을 DB로 가져가면 커넥션 풀 고갈 등 서비스 전체에 병목 가능성이 존재한다.

3) Redis

세 번째가 Redis였다.

  • 장점: 빠르다. 그리고 TTL을 걸 수 있어서 조회수 같은 임시성 데이터 관리에 좋다. 분산 환경에서도 공유된다.
  • 단점: 영속성이 약하다. 장애로 데이터가 날아가면 조회수가 조금 어긋날 수 있다.

성능 테스트

그래서 RDB와 Redis를 비교하기 위해 약 100,000회 테스트를 진행 했다.

구분응답 평균 시간(ms)P95(ms)P99(ms)
RDB108.02482129325
Redis64.082487126

Redis가 RDB 대비 평균 40% 이상 빨랐고, tail latency도 더 안정적이었다. 또한 Redis는 요청이 몰려도 응답 시간이 일정하게 유지됐는데, RDB는 몰리면 체감 지연이 확 뛰었다.

좋아요 수나 댓글 수 처럼 실제 데이터의 갯수를 저장하는 경우라면 100% 정합성이 중요하다고 판단했겠지만, 조회 수 같은 경우는 정합성 보다는 성능에 더 집중하는 것이 더 옳은 방법이라는 생각이 들었다.

최종 결정

그래서 최종적으로는 Redis를 도입했다.

구현 방식은 다음과 같다.

  • Key: view:guard:{postId}:{memberId}
  • Value: 그냥 1 같은 더미 값
  • TTL: 10분

즉, 회원이 게시글을 최초 조회할 때만 Redis에 기록되고, 그때만 DB 조회수가 증가한다. 이후 같은 회원이 10분 안에 새로고침을 하든 다시 들어오든 조회수는 안 올라간다. 이후 TTL이 지나면 다시 올라간다.

profile
경험하며 성장하는 개발자 지망생

0개의 댓글