Skip to content

[feat] 여행 정보 community 게시물 및 댓글 관련 서비스 구현 #104

Merged
LEEDONGH00N merged 7 commits intowith-travel:developfrom
iamseojin:feat/issue-#102
Aug 26, 2025
Merged

[feat] 여행 정보 community 게시물 및 댓글 관련 서비스 구현 #104
LEEDONGH00N merged 7 commits intowith-travel:developfrom
iamseojin:feat/issue-#102

Conversation

@iamseojin
Copy link
Copy Markdown
Contributor

이슈

구현 기능

  • 커뮤니티 게시글 CRUD

    • 여행 정보 커뮤니티 게시글 작성, 수정, 삭제, 조회 기능 구현
    • 게시글 작성자만 수정 가능하도록 검사하여 수정 및 삭제 권한 부여
    • 게시글 조회 시 대륙/국가/도시/키워드 검색 조건을 기반으로 Specification 활용하여 동적 쿼리 처리
    • 페이징(Page) 방식으로 게시글 목록 조회
  • 커뮤니티 댓글 CRUD

    • 특정 게시글에 대한 댓글 작성, 수정, 삭제, 조회 기능 구현
    • 댓글 목록 조회 시 Slice 기반 무한 스크롤 방식 적용
  • 댓글 좋아요 기능

    • 댓글에 좋아요(Like) 등록 및 취소 기능 구현
    • 동일 회원이 중복 좋아요를 할 수 없도록 제약 처리

구현 근거

  • 검색 기능 (Specification)

    • 검색 조건(대륙, 국가, 도시, 키워드)을 조합해 동적으로 SQL WHERE 절을 생성
    • 필요 없는 조건은 자동으로 무시되므로 불필요한 쿼리 복잡도 감소
  • 게시글 조회 (Page)

    • 게시판의 특성상 전체 데이터 개수와 페이지 수를 알아야 하는 경우가 많음 → Page 객체로 구현하여 총 게시글 수 및 페이지 메타데이터 제공
    • 리스트 단순 조회보다 정렬, 페이지네이션, 전체 개수 조회 측면에서 유리
  • 댓글 조회 (Slice)

    • 댓글은 게시글보다 상대적으로 데이터가 많고 계속 추가되는 특성이 강함
    • 무한 스크롤 UX에 적합하도록 Slice 적용 → hasNext 여부만 조회하여 성능 최적화 (count 쿼리 생략)
  • 좋아요 기능

    • 댓글 활성화와 커뮤니티 몰입도 증가
    • exists 쿼리를 활용하여 불필요한 좋아요 insert 중복을 방지

@iamseojin iamseojin requested review from LEEDONGH00N and Copilot and removed request for Copilot August 22, 2025 11:26
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Implements a comprehensive community feature for travel information with CRUD operations for posts and comments. The PR adds the ability for users to create, read, update, and delete community posts with search functionality, along with comment management and a like system.

Key changes include:

  • Community post management with dynamic search filtering using JPA Specifications
  • Comment system with infinite scroll pagination using Slice-based queries
  • Like functionality for comments with duplicate prevention

Reviewed Changes

Copilot reviewed 21 out of 21 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
SecurityUtils.java Utility class for extracting current authenticated member ID from security context
ErrorCode.java Added error codes for community and reply validation scenarios
Image.java Added methods to attach/detach images from community posts
CommunityService.java Core business logic for community CRUD operations with search functionality
CommunityRepository.java JPA repository with custom queries for community data access
Community.java Enhanced entity with builder pattern and business methods
CommunityReplyService.java Service layer for comment CRUD operations and like functionality
CommunityReply.java Enhanced entity with builder pattern and content modification
CommunityReplyLike.java New entity representing comment likes
Various DTOs and Controllers Request/response objects and REST endpoints for the community features

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

@RequestParam(defaultValue = "50") int size) {

Long me = null;
try { me = SecurityUtils.currentMemberIdOrThrow(); } catch (Exception ignore) {}
Copy link

Copilot AI Aug 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Catching all exceptions with an empty catch block is a poor practice. Consider catching only the specific expected exception (IllegalStateException) or use a more explicit approach to handle optional authentication.

Suggested change
try { me = SecurityUtils.currentMemberIdOrThrow(); } catch (Exception ignore) {}
try { me = SecurityUtils.currentMemberIdOrThrow(); } catch (IllegalStateException ignore) {}

Copilot uses AI. Check for mistakes.
public interface CommunityReplyRepository extends JpaRepository<CommunityReply, Long> {

@EntityGraph(attributePaths = {"member"})
Page<CommunityReply> findByCommunity(Community community, Pageable pageable);
Copy link

Copilot AI Aug 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method is not used in the service layer. The service uses findByCommunityOrderByCreatedAtAsc which returns a Slice. Consider removing this unused method or documenting its intended use.

Suggested change
Page<CommunityReply> findByCommunity(Community community, Pageable pageable);

Copilot uses AI. Check for mistakes.
c.update(req.getTitle(), req.getContent(), req.getContinent(), req.getCountry(), req.getCity());

if (req.getImages() != null) {
c.getImages().forEach(Image::detachFromCommunity);
Copy link

Copilot AI Aug 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This operation may cause an N+1 query problem if images are not eagerly loaded. Consider using a bulk delete query or ensure the images are fetched in a single query before this operation.

Suggested change
c.getImages().forEach(Image::detachFromCommunity);
List<Image> images = c.getImages().stream().collect(Collectors.toList()); // Force initialization to avoid N+1
images.forEach(Image::detachFromCommunity);

Copilot uses AI. Check for mistakes.
…ityDetailResponse.java

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Comment on lines +6 to +8
public class CommunitySpecs {
private CommunitySpecs() {}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

어떤 목적으로 만든 클래스인가요?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

JPA Specification 모아 둔 유틸 클래스인데 대륙/국가/도시/검색 키워드 조건을 동적으로 조합해서 커뮤니티 게시글 검색할 때 사용하려고 만든 거예요

Comment on lines +52 to +63
public static Community create(Member writer, String title, String content,
String continent, String country, String city) {
Community c = Community.builder()
.member(writer)
.title(title)
.content(content)
.continent(continent)
.country(country)
.city(city)
.build();
return c;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

연관관계 양방향 매핑도 추가해주세요

Comment on lines +65 to +71
public void update(String title, String content, String continent, String country, String city) {
if (title != null) this.title = title;
if (content != null) this.content = content;
if (continent != null) this.continent = continent;
if (country != null) this.country = country;
if (city != null) this.city = city;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

null 여부를 확인하지 말고 그냥 값을 초기화하면 될것 같습니다. 게시글 수정 시 수정된 부분만 받는것이 아닌 전체 수정으로 받을 것 같기 때문입니다.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines +73 to +75
communityRepository.increaseViewCount(id);
Community c = communityRepository.findDetailById(id);
if (c == null) throw BaseException.from(ErrorCode.COMMUNITY_NOT_FOUND);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

조회 수를 늘리기 전에 먼저 게시글을 DB에서 조회해오고 나서 조회수를 증가시켜주세요

Comment on lines +120 to +124
Specification<Community> spec = Specification
.where(continentEq(continent))
.and(countryEq(country))
.and(cityEq(city))
.and(keywordLike(q));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Specification 클래스가 어떤 의미로 쓰이나요?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

국가·도시 등 검색 파라미터 중 값이 설정된 항목만 조건으로 추가하고, 값이 없는 필드는 자동으로 무시되도록 하여 필요한 조건만 AND로 조합하여 필터링에 유연성을 두기위해 사용했습니다

.community(community)
.member(writer)
.content(content)
.build();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

양방향 매핑 추가해주세요

Comment on lines +25 to +27
public static CommunityReplyLike of(CommunityReply reply, Member member) {
return CommunityReplyLike.builder().reply(reply).member(member).build();
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

양방향 매핑 추가해주세요

Comment on lines +118 to +119
public Page<CommunityListItemResponse> search(String continent, String country, String city, String q,
Pageable pageable) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

어느 곳에선 Slice를 반환하고, 어느곳에선 Page를 반환하던데, 이유가 있을까요?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Page 는 게시물들 리스트를 불러올때 검색 조건에 맞는 게시물이 몇개인지 페이지 수랑 그런거 ui에 필요할 것 같아서 게시물에 쓰였고, Slice 는 게시물의 댓글 리스트 불러올때 사용했습니다

…mmunityService.java

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@LEEDONGH00N LEEDONGH00N merged commit cfabe69 into with-travel:develop Aug 26, 2025
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants