Skip to content

Commit

Permalink
Feat: 게시글 조회수 기능 구현하기 (#82)
Browse files Browse the repository at this point in the history
* #76 - feat: 게시글 상세 조회시 조회수 증가 기능 구현

쿠키를 사용하여 중복된 조회수 증가를 방지한다.

* #76 - test: 조회수 증가 기능 테스트 코드 작성

쿠키 생성을 확인함
  • Loading branch information
GGHDMS authored Feb 6, 2024
1 parent 6445bb1 commit fc47f5a
Show file tree
Hide file tree
Showing 5 changed files with 151 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
import cotato.bookitlist.post.dto.response.PostListResponse;
import cotato.bookitlist.post.dto.response.PostResponse;
import cotato.bookitlist.post.service.PostService;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Pageable;
Expand All @@ -25,6 +28,7 @@
@RequestMapping("/posts")
public class PostController {
private static final Long DEFAULT_USER_ID = 0L;
private static final String POST_VIEW_COOKIE_NAME = "post_view";

private final PostService postService;

Expand Down Expand Up @@ -57,13 +61,20 @@ public ResponseEntity<Void> updatePost(

@GetMapping("/{post-id}")
public ResponseEntity<PostResponse> getPost(
HttpServletRequest request,
HttpServletResponse response,
@PathVariable("post-id") Long postId,
@AuthenticationPrincipal AuthDetails details
) {
ResponseEntity<PostResponse> responseEntity;
if (details == null) {
return ResponseEntity.ok(PostResponse.from(postService.getPost(postId, DEFAULT_USER_ID), DEFAULT_USER_ID));
responseEntity = ResponseEntity.ok(PostResponse.from(postService.getPost(postId, DEFAULT_USER_ID), DEFAULT_USER_ID));
} else {
responseEntity = ResponseEntity.ok(PostResponse.from(postService.getPost(postId, details.getId()), details.getId()));
}
return ResponseEntity.ok(PostResponse.from(postService.getPost(postId, details.getId()), details.getId()));

handlePostViewCount(request, response, postId);
return responseEntity;
}

@GetMapping("/all")
Expand Down Expand Up @@ -92,4 +103,34 @@ public ResponseEntity<PostCountResponse> getPostCount(
) {
return ResponseEntity.ok(postService.getPostCount(isbn13));
}

private void handlePostViewCount(HttpServletRequest request, HttpServletResponse response, Long postId) {
Cookie[] cookies = request.getCookies();
Cookie postViewCookie = findCookie(cookies);

if (postViewCookie != null) {
if (!postViewCookie.getValue().contains("[" + postId + "]")) {
postViewCookie.setValue(postViewCookie.getValue() + "[" + postId + "]");
postService.increaseViewCount(postId);
postViewCookie.setPath("/posts");
}
response.addCookie(postViewCookie);
} else {
Cookie newCookie = new Cookie(POST_VIEW_COOKIE_NAME, "[" + postId + "]");
newCookie.setPath("/posts");
postService.increaseViewCount(postId);
response.addCookie(newCookie);
}
}

private Cookie findCookie(Cookie[] cookies) {
if (cookies != null) {
for (Cookie cookie : cookies) {
if (POST_VIEW_COOKIE_NAME.equals(cookie.getName())) {
return cookie;
}
}
}
return null;
}
}
4 changes: 4 additions & 0 deletions src/main/java/cotato/bookitlist/post/domain/Post.java
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,8 @@ public void increaseLikeCount() {
public void decreaseLikeCount() {
this.likeCount--;
}

public void increaseViewCount(){
this.viewCount++;
}
}
6 changes: 6 additions & 0 deletions src/main/java/cotato/bookitlist/post/service/PostService.java
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,10 @@ public PostListResponse searchPost(String isbn13, Long memberId, Pageable pageab
public PostCountResponse getPostCount(String isbn13) {
return PostCountResponse.of(postRepository.countByBook_Isbn13(isbn13));
}

@Transactional
public void increaseViewCount(Long postId) {
Post post = postRepository.getReferenceById(postId);
post.increaseViewCount();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import cotato.bookitlist.annotation.WithCustomMockUser;
import cotato.bookitlist.post.dto.requeset.PostRegisterRequest;
import cotato.bookitlist.post.dto.requeset.PostUpdateRequest;
import jakarta.servlet.http.Cookie;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
Expand Down Expand Up @@ -183,6 +184,37 @@ void givenPostId_whenGettingPost_thenPostResponse() throws Exception {
;
}

@Test
@DisplayName("쿠키없이 게시글을 조회하면 쿠키를 생성한다.")
void givenNonCookie_whenGettingPost_thenCookie() throws Exception {
//given

//when & then
mockMvc.perform(get("/posts/1")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(cookie().value("post_view", "[1]"))
.andExpect(cookie().path("post_view", "/posts"));
;
}

@Test
@DisplayName("쿠키를 가지고 게시글을 조회하면 쿠키에 id를 추가하여 넘겨준다.")
void givenCookie_whenGettingPost_thenCookie() throws Exception {
//given
Cookie cookie = new Cookie("post_view", "[1]");

//when & then
mockMvc.perform(get("/posts/2")
.contentType(MediaType.APPLICATION_JSON)
.cookie(cookie))
.andExpect(status().isOk())
.andExpect(cookie().value("post_view", "[1][2]"))
.andExpect(cookie().path("post_view", "/posts"))
;
}


@Test
@WithCustomMockUser
@DisplayName("로그인 된 유저가 본인 게시글을 조회한다.")
Expand Down
66 changes: 66 additions & 0 deletions src/test/java/cotato/bookitlist/post/service/PostServiceTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package cotato.bookitlist.post.service;

import cotato.bookitlist.book.domain.entity.Book;
import cotato.bookitlist.config.security.oauth.AuthProvider;
import cotato.bookitlist.member.domain.Member;
import cotato.bookitlist.post.domain.Post;
import cotato.bookitlist.post.repository.PostRepository;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.test.util.ReflectionTestUtils;

import java.time.LocalDate;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.then;

@DisplayName("게시글 서비스 테스트")
@ExtendWith(MockitoExtension.class)
class PostServiceTest {

@InjectMocks
PostService sut;
@Mock
PostRepository postRepository;

@Test
@DisplayName("게시글 조회수 증가 요청시 조회수가 증가한다.")
void givenPostId_whenIncreasingViewCount_thenIncreaseViewCount() throws Exception{
//given
Long postId = 1L;
Post post = createPost(postId);
given(postRepository.getReferenceById(postId)).willReturn(post);

//when
sut.increaseViewCount(postId);

//then
then(postRepository).should().getReferenceById(postId);
assertThat(post.getViewCount()).isEqualTo(1);
}

Post createPost(Long postId) {
Post post = Post.of(createMember(), createBook(), "title", "content");
ReflectionTestUtils.setField(post, "id", postId);
return post;
}

Book createBook() {
return Book.of("title", "author", "pubisher", LocalDate.now(), "description", "link", "isbn13", 10000, "cover");
}

Member createMember(Long memberId) {
Member member = new Member("email", "name", "oauth2Id", AuthProvider.KAKAO);
ReflectionTestUtils.setField(member, "id", memberId);
return member;
}

Member createMember() {
return createMember(1L);
}
}

0 comments on commit fc47f5a

Please sign in to comment.