본문 바로가기
Projects/청하-청년을 위한 커뮤니티 서비스

[청하] 36. 게시글 조회수 기능 구현

by Lpromotion 2024. 10. 25.

게시글도 조회수 기능을 추가하게 되었다.

게시글 조회수는 이전 정책 조회수와 다르게, 시글 테이블에 조회수 컬럼을 직접 추가하여 더 간단하게 구현했다.

 

1. 프로젝트 구조

src/main/java/com/example/withpeace/
│
├── domain/                 # 도메인 모델 (엔티티)
│   └── Post.java           # 게시글 엔티티
│
├── repository/             # 데이터 접근 계층
│   └── PostRepository.java     # 게시글 레포지토리
│
├── controller/             # 컨트롤러 계층
│   └── PostController.java # 게시글 관련 API 엔드포인트
│
└── service/                # 비즈니스 로직 계층
    └── PostService.java    # 게시글 관련 비즈니스 로직

 

2. 도메인 엔티티 수정

Post

public class Post {
    // ... (기존 컬럼 생략)
    
	// 조회수 컬럼 추가
    @Column(name = "view_count", nullable = false)
    private Long viewCount;

    @Builder
    public Post(User writer, String title, String content, ETopic type) {
        this.writer = writer;
        this.title = title;
        this.content = content;
        this.type = type;
        this.commentCount = 0L;
        this.viewCount= 0L; // 추가
        this.createDate = LocalDateTime.now();
    }

    // ... (기존 메서드 생략)

	// 조회수 증가
    public void incrementViewCount() { this.viewCount++; }

}

Post 엔티티에 `viewCount`(조회수) 컬럼를 추가하고, 조회수를 증가시키는 `incrementViewCount` 메서드를 추가했다.

 

3. 서비스 코드 수정

PostService

@Transactional
public PostDetailResponseDto getPostDetail(Long userId, Long postId) {
    getUserById(userId);
    Post post = getPostById(postId);

    List<String> postImageUrls = Optional.ofNullable(imageRepository.findUrlsByPost(post))
            .orElse(Collections.emptyList());
    List<CommentListResponseDto> comments = Optional.ofNullable(commentRepository.findCommentsByPost(post))
            .orElse(Collections.emptyList())
            .stream()
            .map(comment -> CommentListResponseDto.builder()
                    .commentId(comment.getId())
                    .userId(comment.getWriter().getId())
                    .nickname(comment.getWriter().getNickname())
                    .profileImageUrl(comment.getWriter().getProfileImage())
                    .content(comment.getContent())
                    .createDate(TimeFormatter.timeFormat(comment.getCreateDate()))
                    .build())
            .collect(Collectors.toList());

    PostDetailResponseDto postDetailResponseDto =
            PostDetailResponseDto.builder()
                    .postId(postId)
                    .userId(post.getWriter().getId())
                    .nickname(post.getWriter().getNickname())
                    .profileImageUrl(post.getWriter().getProfileImage())
                    .title(post.getTitle())
                    .content(post.getContent())
                    .type(post.getType())
                    .createDate(TimeFormatter.timeFormat(post.getCreateDate()))
                    .postImageUrls(postImageUrls)
                    .comments(comments)
                    .build();

    post.incrementViewCount(); // 조회수 증가

    return postDetailResponseDto;
}

`getPostDetail`는 “게시글 상세 조회”의 서비스 코드이다. 사용자가 한 게시글을 “상세 조회”하게 되면, 게시글의 상세 정보를 조회하고 마지막에 `incrementViewCount` 메서드를 호출하여 조회수를 증가시킨다.

 

게시글 조회수 증가 구현 로직 선택

“정책 조회수 기능 구현”에서는 조회수 컬럼을 추가할 수 없는 상황이어서 조회수 테이블을 따로 생성했고, 레포지토리를 이용하여 중복 방지를 할 수 있었다. 하지만 게시글은 Post 엔티티에 `viewCount` 컬럼을 추가하고, `incrementViewCount` 메서드를 사용하도록 했다.

그 이유로는

  1. 별도의 조회수 테이블을 생성하게 되면 추가적인 리소스가 더 필요하고 구조가 복잡해진다.
  2. 중복 방지도 중요하지만 단순한 조회수 카운팅을 우선순위로 생각했다.
  3. 추후 중복 방지가 필요해지면 그때 테이블을 분리하는 것이 더 효율적이라고 판단했다.

위 이유 이 외에도 Post 엔티티가 자신의 조회수를 직접 관리하는 것이 객체지향적 설계에 더 부합하고, *JPA의 기능(변경 감지) 을 통해 별도의 UPDATE 쿼리를 작성할 필요없이 자동으로 데이터베이스에 반영된다는 장점도 있다.

* JPA의 변경 감지(Dirty Checking) 기능: 엔티티의 변경사항을 감지하여 트랜잭션이 끝나는 시점에 데이터베이스에 자동으로 반영하는 기능

 

4. API 응답 예시

요청 URL

[GET] http://cheongha.site/api/v1/posts/10 

“게시글 상세 조회” 시 조회수를 증가시킬 수 있다.

반응형

댓글