프로젝트에서 조회수 기능을 구현해야 했다.
처음에는 단순하게 조회수 컬럼을 게시판 테이블에 추가하여, 게시글을 조회할 때마다 조회수를 1씩 증가시키는 방식으로 설계했다. 예를 들면 다음과 같은 구조다.
@Entity
public class Article {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
private String content;
private int views;
public void increaseViews() {
this.views += 1;
}
}
그래서 GET 요청이 올 때마다 조회수를 단순히 증가시키는 접근으로 구현했다.
하지만, 이러한 설계는 특정 사용자가 새로고침을 반복하여 조회수를 악의적으로 증가시키는 문제가 발생할 수 있다!!
그래서 해결 방법을 찾아보니, 다대다 관계시 중간 테이블을 만드는 것처럼 중간 테이블을 추가하여 user_id와 article_id로 조회 기록을 관리하는 방법을 고려했다.
근데, 이제 또 고민할 게 생겼다. 게시글은 로그인하지 않은 사용자도 조회할 수 있는데 user_id를 어떻게 가져가지?
그래서 user_id는 null로 가져가고 추가로 ip주소나 세션 id를 이용한다고 한다. (user_id는 user테이블에서는 pk라 null이 불가하지만 fk로 사용되면 null을 허용할 수 있다)
1. IP 주소 이용
단점 : 동일 네트워크 내 여러 사용자가 접근할 때 조회수가 제한될 수 있으며, IP가 동적으로 변경되거나 VPN을 사용하는 경우 정확도가 떨어질 수 있다.
2. 세션 ID 이용
세션 ID를 통해 동일 브라우저와 장치의 조회를 식별하는 방법
단점 : 특정 사용자가 재접속 시 세션이 만료되면 새로운 세션 ID가 부여되므로, 중복 조회로 인식될 수 있는 점에서 제한이 있을 수 있다.
3. 그 외로 디바이스 정보를 저장하기도 하고 ip주소와 세션 ID를 합쳐서 사용하기도 하고 그런다고 한다.
@Entity
public class View {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name = "article_id", nullable = false)
private Article article;
@ManyToOne
@JoinColumn(name = "user_id", nullable = true)
private User user;
@Column(name = "ip_address", nullable = false)
private String ipAddress; // IP 주소
}
엄청 간단하다고 생각해서 구현했는데
역시 쉽게 구현되는 건 없는 것 같다.