[Spring] 조회수 기능 구현 (조회수 중복 방지)

Jerry·2022년 7월 9일
3

1. 도입 배경

스프링부트로 게시판 만들기 프로젝트 중 추가 기능인 조회수 기능을 구현해보기로 했다.
여기에 단순히 클릭시 카운트만 올라가는 게 아니라 실제 서비스 되고 있는 게시판처럼 한 계정이 중복으로 조회수를 올릴 수 없게 구현하기로 단서를 걸었다.

조회수 기능 구현 과정

  1. 엔티티
	@Column(columnDefinition = "integer default 0", nullable = false)	// 조회수의 기본 값을 0으로 지정, null 불가 처리
	private int view;
}

조회수 컬럼을 추가 후 기본 값을 0으로 지정, null 불가 처리

  1. Repository
	@Modifying
	@Query("update Question q set q.view = q.view + 1 where q.id = :id")
	int updateView(@Param("id") Integer id);

QuestionRepository에 해당 게시물의 조회수만 증가시키는 메소드 추가

@Modifying :
@Modifying 어노테이션은 @Query 어노테이션에서 작성된 조회를 제외한 데이터의 변경이 있는
삽입(Insert), 수정(Update), 삭제(Delete) 쿼리 사용시 필요한 어노테이션이다.
출처: https://dev-coco.tistory.com/113 [슬기로운 개발생활:티스토리]

이런 어노테이션이 있는 줄 진작에 알았으면 카테고리 기능 개발할때 그렇게 삽질을 안 했을텐데...

  1. Service
	/* Views Counting */
	@Transactional
	public int updateView(Integer id) {
		return this.questionRepository.updateView(id);
	}

@Transactional :
@Transactional은 클래스나 메서드에 붙여줄 경우, 해당 범위 내 메서드가 트랜잭션이 되도록 보장해준다.
출처 : https://kafcamus.tistory.com/30

  1. Controller
	@RequestMapping("/detail/{cate}/{id}")
	public String detail(Model model, 
			@PathVariable("id") Integer id, 
			@PathVariable("cate") String cate,
			AnswerForm answerForm, CommentForm commentForm, 
			@RequestParam(value="page", defaultValue="0") int page,
			HttpServletRequest request,
            HttpServletResponse response) {
		Question question = this.questionService.getQuestion(id);
		
		/* 조회수 로직 */
		Cookie oldCookie = null;
		Cookie[] cookies = request.getCookies();
		if (cookies != null) {
			for (Cookie cookie : cookies) {
				if (cookie.getName().equals("postView")) {
					oldCookie = cookie;
				}
			}
		}
		
		if (oldCookie != null) {
			if (!oldCookie.getValue().contains("["+ id.toString() +"]")) {
				this.questionService.updateView(id);
				oldCookie.setValue(oldCookie.getValue() + "_[" + id + "]");
				oldCookie.setPath("/");
				oldCookie.setMaxAge(60 * 60 * 24); 							// 쿠키 시간
				response.addCookie(oldCookie);
			}
		} else {
			this.questionService.updateView(id);
			Cookie newCookie = new Cookie("postView", "[" + id + "]");
			newCookie.setPath("/");
			newCookie.setMaxAge(60 * 60 * 24); 								// 쿠키 시간
			response.addCookie(newCookie);
		}
        
		Page<Answer> answerPaging = this.answerService.getAnswerList(id, page);				// answerList는 페이징처리하여 별도로 조회하기
		model.addAttribute("question", question);
		model.addAttribute("answerPaging", answerPaging);
		return "/question/question_detail";
	}

먼저, 조회수를 올리는데 쿠키를 활용하기 위해 request, response를 파라미터에 추가했다.

  1. oldCookie 객체를 선언한 후 빈값으로 초기화
  2. request 객체에서 쿠키들을 가져와 Cookie 타입을 요소로 가지는 리스트에 담는다.
  3. cookies가 null이 아닌지 체크한다.
  4. cookies가 null이 아니면 for문을 돌려서 cookie의 이름이 postView인지 확인하고, 맞으면 oldCookie에 이 cookie를 대입한다.
  5. oldCookie가 null이 아닐때
    5-1. oldCookie의 value중 게시물의 id 값이 없을 때 (있다면 이미 조회한 게시물로 조회수가 올라가지 않음) 조회수 올리는 메소드 호출
    5-2. old쿠키에 경로, 쿠키유지 시간을 추가하여 response에 oldCookie 를 전달한다.
  6. oldCookie가 null일 때
    6-1. 조회수 올리는 메소드 호출
    6-2. postView라는 이름으로 쿠키를 만들고 거기에 게시물 id 값을 괄호로 감싸 추가한다.
    6-3. 5-2처럼 경로, 쿠키유지 시간을 추가하여 response에 oldCookie 를 전달한다.

시연


먼저, 게시판 목록. 테스트를 한 탓에 몇 개의 게시물 번호가 postView에 추가 되었다. 저 번호의 게시물들을 클릭해도 조회수가 올라가지 않을 것이다.


304번 게시물을 클릭하자 postView에 [304]가 추가 되었다. 신기신기.


304번 게시물 조회수가 1 올라간 것이 보인다.
이후 뒤로 가기를 한 다음에 다시 304번 게시물을 클릭해도 조회수는 올라가지 않는다.

참고

https://dev-coco.tistory.com/113
https://mighty96.github.io/til/view/

profile
함께 일 하고 싶은 개발자가 되길 희망합니다.

1개의 댓글

comment-user-thumbnail
2023년 12월 26일

안녕하세요 작성하신 조회수 쿠키 구현 도움이 되었습니다

답글 달기