Skip to content

refactor/#251 User, Post, Comment, FileEntity간 관계 매핑을 ID 참조로 변경 - #259

Merged
JangYeongHu merged 15 commits into
developfrom
refactor/#251
Sep 5, 2025
Merged

refactor/#251 User, Post, Comment, FileEntity간 관계 매핑을 ID 참조로 변경#259
JangYeongHu merged 15 commits into
developfrom
refactor/#251

Conversation

@JangYeongHu

Copy link
Copy Markdown
Contributor

Summary

User, Post, Comment, FileEntity 간의 관계 매핑을 ID 참조 기반으로 변경하였습니다.

Tasks

  • User, Post, Comment, FileEntity 간의 관계 매핑을 ID 참조 기반으로 변경
  • 이에 맞추어 조회 로직과 테스트 코드를 수정

@coderabbitai

coderabbitai Bot commented Sep 2, 2025

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

도메인(Post/Comment)에서 연관관계를 제거하고 ID 기반(authorId, fileId, postId)으로 전환. 이에 따라 생성/업데이트 메서드 시그니처 변경, 조회 시 User/File 로딩 추가, 응답 DTO/Facade들이 authorName 맵을 주입받아 작성자 이름을 채우도록 수정. 테스트/페이크 리포지토리/컨테이너 전반 갱신.

Changes

Cohort / File(s) Change summary
Domain: ID 기반 모델 전환 및 서비스/응답 수정
aics-domain/src/main/java/kgu/developers/domain/comment/domain/Comment.java, .../comment/application/command/CommentCommandService.java, .../post/domain/Post.java, .../post/application/command/PostCommandService.java, .../post/application/query/PostQueryService.java, .../post/application/response/PostDetailResponse.java, .../post/infrastructure/QueryPostRepository.java, .../user/application/query/UserQueryService.java, .../user/domain/User.java
Comment: User/Post 연관 제거 → authorId, postId 필드 및 create(content, authorId, postId)로 변경. Post: User/File 연관 제거 → authorId, fileIdcreate(..., authorId, fileId, ...), updateFileId(Long)로 변경. Command 서비스들 ID 기반 생성/업데이트로 수정. Query 서비스가 User/File 리포지토리 의존 추가 후 상세 조회 시 author/file 로딩하여 PostDetailResponse.from(post, author, file, ...) 사용. QueryPostRepository에서 댓글 삭제 조건을 서브쿼리 방식으로 변경. UserQueryService에 getUserNameMapByIds 추가. User 엔티티에서 posts 연관 제거.
API: Facade/Response에서 작성자 이름 맵 주입
aics-api/src/main/java/kgu/developers/api/comment/application/CommentFacade.java, .../comment/presentation/response/CommentListResponse.java, .../comment/presentation/response/CommentResponse.java, .../post/application/PostFacade.java, .../post/presentation/response/PostSummaryPageResponse.java, .../post/presentation/response/PostSummaryResponse.java
Comment/Post Facade에 UserQueryService 의존 추가, 목록 조회 시 authorId 수집→이름 맵 조회→응답에 주입. DTO 정적 팩토리 메서드들이 authorName 또는 authorNameMap 파라미터를 받도록 시그니처 변경 및 사용.
Test Fixtures: 도메인 테스트/페이크 리포지토리/컨테이너 갱신
aics-domain/src/testFixtures/java/mock/TestContainer.java, .../mock/FakeTestContainer.java, .../mock/repository/FakePostRepository.java, .../mock/repository/FakeCommentRepository.java, .../post/application/PostQueryServiceTest.java, .../comment/application/CommentCommandServiceTest.java, .../comment/application/CommentQueryServiceTest.java, .../comment/domain/CommentDomainTest.java, .../post/domain/PostDomainTest.java
PostQueryService 생성자에 FileRepository, UserRepository 추가 반영. 페이크 리포지토리들이 authorId, fileId, postId 사용하도록 저장/조회 로직 수정. 테스트 데이터 생성 시 User/Post 객체 대신 ID 사용으로 전면 교체.
API Test Fixtures: Facade 테스트 정비
aics-api/src/testFixtures/java/comment/application/CommentFacadeTest.java, .../post/application/PostFacadeTest.java
Facade에 UserQueryService 주입 추가 및 PostQueryService 3-의존성 생성자 반영. 엔티티 생성 호출을 ID 기반으로 수정. 보안 컨텍스트 설정 추가(Comment).
Admin Test: Post Admin Facade 테스트 정비
aics-admin/src/testFixtures/java/post/application/PostAdminFacadeTest.java
PostQueryService 생성자 변경 반영(3 의존성). Post 생성 시 author.getId() 전달로 변경.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

🔨refactor

Suggested reviewers

  • LeeHanEum
  • minjo-on
  • LeeShinHaeng
✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor/#251

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@JangYeongHu JangYeongHu self-assigned this Sep 2, 2025
@JangYeongHu JangYeongHu added the 🔨refactor refactoring code label Sep 2, 2025
@JangYeongHu JangYeongHu linked an issue Sep 2, 2025 that may be closed by this pull request
2 tasks

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 9

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
aics-domain/src/main/java/kgu/developers/domain/comment/application/command/CommentCommandService.java (2)

13-19: CommentCommandService 클래스에 @transactional 누락
updateComment(…)와 deleteComment(…)가 JPA 변경 감지에 의존하므로 클래스(또는 메서드)에 @Transactional을 적용해야 합니다. 조회 전용 메서드는 필요 시 @Transactional(readOnly = true)로 분리하세요.

 import lombok.RequiredArgsConstructor;
+import org.springframework.transaction.annotation.Transactional;

 @Service
 @RequiredArgsConstructor
-public class CommentCommandService {
+@Transactional
+public class CommentCommandService {

27-33: CommentCommandService에 @transactional 적용 필요
updateComment, deleteComment 메서드가 트랜잭션 없이 실행되어 영속성 변경이 보장되지 않습니다.
클래스 선언부 또는 각 메서드에 @Transactional 애노테이션을 추가하세요.

aics-api/src/main/java/kgu/developers/api/comment/presentation/response/CommentListResponse.java (1)

14-21: 스웨거 설명 오기(파일 → 댓글) 수정 필요

"게시글에 첨부된 파일"은 댓글 리스트 스키마에 부적절한 설명입니다. 사용자 문서 품질 이슈로 수정이 필요합니다.

-  @Schema(description = "게시글에 첨부된 파일",
+  @Schema(description = "게시글의 댓글 목록",
     example = "[{"
       + "\"commentId\": 1, "
-      + "\"author\": \"이신행\", "
+      + "\"author\": \"이신행\", "
       + "\"createdAt\": \"2024-11-11\", "
       + "\"content\": \"예시 코멘트입니다, 좋은 소식이네요!\"}]",
     requiredMode = REQUIRED)
aics-domain/src/main/java/kgu/developers/domain/post/application/response/PostDetailResponse.java (1)

70-86: author null 시 NPE 발생 가능

PostQueryService에서 author를 orElse(null)로 전달하므로 여기서 author.getName()이 NPE가 될 수 있습니다. 아래처럼 null-safe 처리하거나, 서비스에서 author 미존재 시 예외를 던지도록 일관화해 주세요.

-    return PostDetailResponse.builder()
+    String authorName = (author != null) ? author.getName() : "알 수 없음";
+    return PostDetailResponse.builder()
@@
-            .author(author.getName())
+            .author(authorName)
🧹 Nitpick comments (29)
aics-domain/src/main/java/kgu/developers/domain/comment/domain/Comment.java (1)

28-33: ID 필드 전환은 적절. 그러나 FK/인덱스와 컬럼명 명시는 필요

  • 연관관계 제거로 DB FK가 사라졌습니다. 무결성·삭제 전파 보장을 위해 DB 마이그레이션(Flyway/Liquibase)으로 FK를 추가해 주세요.
  • 조회가 자주 발생하는 postId/authorId에 인덱스가 없으면 N+1 제거 이점이 줄어듭니다. 인덱스를 권장합니다.
  • 컬럼명을 명시하면 물리적 네이밍 전략 변경 시에도 안전합니다.

적용 예(해당 라인 범위 내 변경):

-    @Column(nullable = false)
+    @Column(name = "post_id", nullable = false)
     private Long postId;

-    @Column(nullable = false)
+    @Column(name = "author_id", nullable = false)
     private String authorId;

추가 코드(선택, 범위 밖): 클래스 수준 인덱스 선언

// imports
import jakarta.persistence.Index;
import jakarta.persistence.Table;

// annotations
@Entity
@Table(indexes = {
    @Index(name = "idx_comment_post_id", columnList = "post_id"),
    @Index(name = "idx_comment_author_id", columnList = "author_id")
})

DB FK 예시(참고):

alter table comment
  add constraint fk_comment_post foreign key (post_id) references post(id);
alter table comment
  add constraint fk_comment_author foreign key (author_id) references "user"(id);
aics-domain/src/main/java/kgu/developers/domain/user/application/query/UserQueryService.java (1)

55-57: 중복 ID·NULL 입력 방어 및 충돌 안전 수집기로 보강 권장

  • 중복 ID가 들어와도 결과는 동일해야 합니다(merge 함수 지정).
  • NULL/빈 리스트 입력에 대한 빠른 반환으로 방어적 코딩.
-    public Map<String, String> getUserNameMapByIds(List<String> authorIds) {
-        return getAllUsersByIds(authorIds).stream().collect(Collectors.toMap(User::getId, User::getName));
-    }
+    public Map<String, String> getUserNameMapByIds(List<String> authorIds) {
+        if (authorIds == null || authorIds.isEmpty()) {
+            return Map.of();
+        }
+        // 중복 제거 후 조회
+        var distinctIds = authorIds.stream().distinct().toList();
+        return getAllUsersByIds(distinctIds).stream()
+            .collect(Collectors.toMap(User::getId, User::getName, (a, b) -> a));
+    }

추가(범위 밖): import java.util.Map;는 이미 있으므로, 별도 import 불필요.

aics-api/src/main/java/kgu/developers/api/post/presentation/response/PostSummaryResponse.java (3)

42-53: authorName 주입 전환은 타당. null/blank 방어 로직을 추가하세요.

호출측에서 이름 매핑이 누락되면 계약상 REQUIRED 스키마와 불일치할 수 있습니다. 기본 표시값을 두어 NPE/빈 문자열을 방지하세요.

-            .author(authorName)
+            .author((authorName != null && !authorName.isBlank()) ? authorName : "알 수 없음")

추가로, DateTimeFormatter는 매 호출 생성 대신 정적 상수로 공유하는 편이 좋습니다. 아래처럼 상단에 선언 후 사용을 권장합니다.

// record 본문 상단에 추가
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");

그리고 메서드 내 로컬 변수 formatter 사용부는 DATE_FORMATTER로 교체하세요.


45-47: 스키마(“앞부분 30자”)와 구현(80자) 불일치

현재 80자로 잘라내지만 스키마 설명은 30자로 표기되어 있습니다. 어느 쪽이 정답인지 결정해 일치시켜 주세요. 30자로 맞출 경우:

-        String description = content.length() > 80 ? content.substring(0, 80) : content;
+        String description = content.length() > 30 ? content.substring(0, 30) : content;

55-55: 첨부파일 여부 TODO 처리

ID 기반으로 전환되었으므로 파일 참조가 존재하는지로 계산 가능해 보입니다. 도메인에 fileId(또는 동등 필드)가 있다면 다음과 같이 반영하세요.

-            .hasAttachment(false) // TODO : 첨부파일 여부 확인
+            .hasAttachment(post.getFileId() != null)

필드명이 다르면 해당 규약에 맞춰 적용 바랍니다.

aics-domain/src/main/java/kgu/developers/domain/comment/application/command/CommentCommandService.java (1)

20-25: ID 기반 생성 로직은 일관적. 다만 content 등의 입력 검증은 어디서 하나요?

Comment.create(content, user.getId(), post.getId()) 사용은 변경 방향과 일치합니다. 다만 빈 내용/최대 길이 등의 검증이 도메인 팩토리에서 보장되는지 확인해 주세요. 서비스층에서의 선제 검증이 필요하면 추가하십시오.

aics-api/src/main/java/kgu/developers/api/comment/presentation/response/CommentResponse.java (1)

27-33: authorName 주입 OK. null/blank 방어 및 포맷터 상수화 제안

호출측 매핑 누락 시 안전하게 동작하도록 기본값을 둡니다.

-            .author(authorName)
+            .author((authorName != null && !authorName.isBlank()) ? authorName : "알 수 없음")

DateTimeFormatter도 정적 상수로 올리면 미세 최적화 및 가독성이 향상됩니다.

private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
aics-domain/src/main/java/kgu/developers/domain/post/infrastructure/QueryPostRepository.java (1)

60-66: ID 기반 서브쿼리 삭제 전환은 적절. 다만 JPA bulk DELETE의 서브쿼리 지원 여부를 런타임에서 확인하세요. 대안으로 EXISTS 패턴 제안

일부 JPA 구현/DB 조합에서 DELETE WHERE 절의 서브쿼리 제약이 있을 수 있습니다. 통합 테스트로 확인해 주세요. 성능 측면에서도 IN 서브쿼리 대신 EXISTS가 더 우호적인 실행계획을 타는 경우가 많습니다.

EXISTS 대안:

-            .where(comment.postId.in(
-                JPAExpressions.select(post.id)
-                    .from(post)
-                    .where(post.deletedAt.isNotNull()
-                        .and(post.deletedAt.before(thresholdDate)))
-            ))
+            .where(
+                JPAExpressions
+                    .selectOne()
+                    .from(post)
+                    .where(
+                        post.id.eq(comment.postId)
+                            .and(post.deletedAt.isNotNull())
+                            .and(post.deletedAt.before(thresholdDate))
+                    )
+                    .exists()
+            )

운영 DB에 comment.post_id 인덱스가 있는지 확인해 주세요. 없으면 대량 삭제 시 테이블 스캔이 발생할 수 있습니다.

aics-domain/src/testFixtures/java/mock/repository/FakeCommentRepository.java (1)

36-37: postId 비교 시 NPE 가능성 — Objects.equals로 안전 비교 권장

comment.getPostId()가 null일 경우 NPE가 날 수 있습니다. 안전 비교로 교체해 주세요.

-            .filter(comment -> comment.getPostId().equals(postId)
+            .filter(comment -> java.util.Objects.equals(comment.getPostId(), postId)
                 && comment.getDeletedAt() == null)
aics-domain/src/testFixtures/java/post/domain/PostDomainTest.java (1)

32-32: ID 기반 생성 호출 전환 OK + authorId 단언 추가 제안

author.getId()로의 전환은 올바릅니다. 생성된 Post에 authorId가 정확히 세팅되었는지 단언을 추가하면 회귀 방지에 도움이 됩니다.

   assertEquals(NEWS, post.getCategory());
+  assertEquals(user().getId(), post.getAuthorId());
aics-api/src/main/java/kgu/developers/api/post/application/PostFacade.java (1)

39-43: 이름 맵 직접 조회 메서드로 중복 제거 및 성능·가독성 개선

이미 Comment 쪽에서 사용하는 getUserNameMapByIds를 활용하면 불필요한 수집/매핑을 제거하고 서비스 간 일관성을 유지할 수 있습니다.

-    Map<String, String> authorNameMap = userQueryService.getAllUsersByIds(authorIds).stream()
-      .collect(Collectors.toMap(
-        User::getId,
-        User::getName
-      ));
+    Map<String, String> authorNameMap = userQueryService.getUserNameMapByIds(authorIds);
aics-api/src/main/java/kgu/developers/api/comment/presentation/response/CommentListResponse.java (1)

23-27: 작성자 이름 누락 시 기본값 처리 + 인자명 명확화(nameMap → authorNameMap)

이름 매핑이 없을 때의 표시값을 정해두면 응답 일관성이 좋아집니다. 또한 파라미터명을 명확히 하면 가독성이 향상됩니다.

-  public static CommentListResponse from(List<Comment> comments, Map<String,String> nameMap) {
+  public static CommentListResponse from(List<Comment> comments, Map<String,String> authorNameMap) {
     return CommentListResponse.builder()
       .contents(comments.stream()
-        .map(comment -> CommentResponse.from(comment,nameMap.get(comment.getAuthorId())))
+        .map(comment -> CommentResponse.from(
+          comment,
+          authorNameMap.getOrDefault(comment.getAuthorId(), "탈퇴한 사용자")
+        ))
         .toList())
       .build();
   }
aics-api/src/main/java/kgu/developers/api/comment/application/CommentFacade.java (2)

38-38: authorIds 중복 제거로 불필요한 조회 줄이기

중복 ID를 제거하면 사용자 이름 조회 호출을 줄일 수 있습니다. PostFacade와도 일관됩니다.

-    List<String> authorIds = comments.stream().map(Comment::getAuthorId).collect(Collectors.toList());
+    List<String> authorIds = comments.stream().map(Comment::getAuthorId).distinct().toList();

40-41: 지역 변수명 통일(가독성) 및 의미 명확화

nameMapauthorNameMap으로 변경하면 역할이 명확해집니다. (외부 시그니처 영향 없음)

-    Map<String, String> nameMap = userQueryService.getUserNameMapByIds(authorIds);
-    return CommentListResponse.from(comments,nameMap);
+    Map<String, String> authorNameMap = userQueryService.getUserNameMapByIds(authorIds);
+    return CommentListResponse.from(comments, authorNameMap);
aics-api/src/main/java/kgu/developers/api/post/presentation/response/PostSummaryPageResponse.java (1)

32-36: authorNameMap 누락/미스매칭 대비 방어 코드와 fallback을 추가해 주세요

null map 또는 매핑 누락 시 NPE/빈 authorName 가능성이 있습니다. 안전한 기본값과 getOrDefault로의 fallback을 권장합니다. 또한 파라미터 표기 공백(nit)도 함께 정리해 주세요.

- public static <T> PostSummaryPageResponse<T> of(List<Post> posts, PageableResponse<T> pageable, Map<String, String>authorNameMap) {
+ public static <T> PostSummaryPageResponse<T> of(List<Post> posts, PageableResponse<T> pageable, Map<String, String> authorNameMap) {
+   authorNameMap = (authorNameMap == null) ? Map.of() : authorNameMap;
    return PostSummaryPageResponse.<T>builder()
      .contents(posts.stream()
-       .map(post -> PostSummaryResponse.from(post, authorNameMap.get(post.getAuthorId())))
+       .map(post -> PostSummaryResponse.from(
+         post,
+         authorNameMap.getOrDefault(post.getAuthorId(), post.getAuthorId())
+       ))
        .toList())
      .pageable(pageable)
      .build();
 }

선택: 기존 호출부 호환을 위해 of(posts, pageable) 오버로드(내부에서 Map.of() 사용) 추가도 고려할 만합니다.

aics-domain/src/testFixtures/java/comment/application/CommentQueryServiceTest.java (1)

26-31: 하드코딩된 ID 의존(1L, 2L) 제거로 테스트 안정화

가짜 리포지토리의 ID 할당 정책에 암묵적으로 의존하고 있습니다. 저장 결과에서 실제 ID를 받아 사용하도록 리팩터링하면 테스트가 견고해집니다.

@@
- private static final Long SAVED_COMMENT_ID = 2L;
- private static final Long NOT_EXIST_COMMENT_ID = 3L;
- private static final Long TEST_POST_ID = 1L;
+ private static final Long NOT_EXIST_COMMENT_ID = 3L;
+ private Long savedCommentId;
+ private Long testPostId;
@@ public void init() {
- deletedComment(fakeCommentRepository, post.getId());
- saveTestComment(fakeCommentRepository, post.getId());
+ deletedComment(fakeCommentRepository, post.getId());
+ this.savedCommentId = saveTestComment(fakeCommentRepository, post.getId());
+ this.testPostId = post.getId();
@@ public void getComments_Success() {
- List<Comment> comments = commentQueryService.getComments(TEST_POST_ID);
+ List<Comment> comments = commentQueryService.getComments(testPostId);
@@ public void getById_Success() {
- Long savedCommentId = SAVED_COMMENT_ID;
+ Long savedCommentId = this.savedCommentId;

그리고 헬퍼가 ID를 반환하도록 바꾸면 호출부가 단순해집니다:

- private static void saveTestComment(FakeCommentRepository fakeCommentRepository, Long postId) {
-   fakeCommentRepository.save(
-     Comment.create(TARGET_COMMENT_CONTENT, TEST_AUTHOR_ID, postId)
-   );
- }
+ private static Long saveTestComment(FakeCommentRepository fakeCommentRepository, Long postId) {
+   return fakeCommentRepository
+     .save(Comment.create(TARGET_COMMENT_CONTENT, TEST_AUTHOR_ID, postId))
+     .getId();
+ }

Also applies to: 62-73, 79-89

aics-admin/src/testFixtures/java/post/application/PostAdminFacadeTest.java (2)

82-86: fileId=1L 하드코딩은 테스트 취약점—실재 파일 준비 또는 null 허용 확인 권장

가짜 파일 리포지토리에 ID=1 파일이 존재한다는 가정이 숨겨져 있습니다. 저장 없이 1L를 쓰면 구현에 따라 실패할 수 있습니다. 실제 파일을 저장해 얻은 ID를 사용하거나(권장), 파일 미첨부 시나리오면 명시적으로 null을 전달하세요.

- Long fileId = 1L;
+ Long fileId = null; // 파일 미첨부 시나리오라면 이렇게 명시

또는 테스트 setup에서 파일을 저장해 실제 ID를 주입해 주세요.


60-64: SecurityContext 정리 추가 제안

테스트 간 컨텍스트 누수 방지를 위해 @AfterEach에서 SecurityContextHolder.clearContext() 호출을 권장합니다.

// 파일 하단 등 적절한 위치
@AfterEach
void tearDown() {
  SecurityContextHolder.clearContext();
}
aics-domain/src/testFixtures/java/comment/domain/CommentDomainTest.java (1)

39-39: ID 기반 생성 시 null ID 가능성 점검 필요

author.getId(), post.getId()가 영속화 전 객체라면 null일 수 있어 생성 시점에 NPE/검증 실패가 날 수 있습니다. 테스트 픽스처에서 ID를 명시적으로 세팅하거나(예: TestEntityUtils로 setId) 저장을 통해 ID를 채운 뒤 사용해 주세요. 최소한 Comment.create/Post.create가 null ID를 허용하는지 확인이 필요합니다.

Also applies to: 47-47

aics-domain/src/main/java/kgu/developers/domain/post/application/command/PostCommandService.java (1)

5-5: 미사용 의존성 정리 제안(FileQueryService)

현재 클래스 내에서 FileQueryService를 사용하지 않습니다. 검증을 추가하지 않을 계획이라면 의존성 제거로 DI 단순화가 가능합니다.

-import kgu.developers.domain.file.application.query.FileQueryService;
@@
-    private final FileQueryService fileQueryService;

Also applies to: 18-19

aics-api/src/testFixtures/java/comment/application/CommentFacadeTest.java (1)

82-87: SecurityContext 정리 루틴 추가 제안

각 테스트 후 SecurityContext를 명시적으로 정리하면 테스트 간 교차 오염 가능성을 낮출 수 있습니다.

아래 메서드를 추가하세요:

@org.junit.jupiter.api.AfterEach
void tearDown() {
    org.springframework.security.core.context.SecurityContextHolder.clearContext();
}
aics-api/src/testFixtures/java/post/application/PostFacadeTest.java (1)

49-53: 동일 인스턴스 재사용 권장: UserQueryService 중복 생성

postFacade 생성 시 이미 확보한 userQueryService 대신 새 인스턴스를 생성하고 있습니다. 동일 리포지토리를 공유하더라도 테스트 가독성과 일관성을 위해 기존 인스턴스를 재사용하세요.

다음과 같이 변경:

         postFacade = new PostFacade(
             new PostCommandService(userQueryService, fakePostRepository, fileQueryService),
-            new PostQueryService(fakePostRepository, fakeFileRepository, fakeUserRepository),
-            new UserQueryService(fakeUserRepository)
+            new PostQueryService(fakePostRepository, fakeFileRepository, fakeUserRepository),
+            userQueryService
         );
aics-domain/src/testFixtures/java/comment/application/CommentCommandServiceTest.java (4)

58-59: Post.create에 하드코딩된 작성자 ID 제거

저장한 사용자(TEST_USER_ID)와 다른 “202412345”를 하드코딩했습니다. 테스트 일관성과 가독성을 위해 상수 사용을 권장합니다. fileId=null 처리도 서비스/조회 로직에서 NPE 없이 안전한지 확인해 주세요.

적용 제안:

-			"202412345", null, false
+			TEST_USER_ID, null, false

98-100: Comment.create에 매직값 대신 상수 사용 + 권한 시나리오 확인

작성자 ID와 postId를 직접 값으로 넣기보다 상수(TEST_USER_ID, TEST_POST_ID) 사용을 권장합니다. 또한 CommentCommandService가 “작성자만 수정 가능” 규칙을 가진 경우, 현재 보안 컨텍스트 사용자(TEST_USER_ID)와 댓글 작성자("202312345")가 달라 테스트가 실제 정책을 반영하지 않을 수 있습니다. 정책 확인을 부탁드립니다.

적용 제안:

-		Comment comment = Comment.create("SW 부트캠프 모집이 정말 기대됩니다.", "202312345",
-			1L);
+		Comment comment = Comment.create("SW 부트캠프 모집이 정말 기대됩니다.", TEST_USER_ID,
+			TEST_POST_ID);

113-115: 삭제 테스트도 상수 사용으로 일관성 확보

위와 동일하게 매직값을 상수로 치환해 유지보수성을 높이세요.

적용 제안:

-		Comment comment = Comment.create("SW 부트캠프 모집이 정말 기대됩니다.", "202312345",
-			1L);
+		Comment comment = Comment.create("SW 부트캠프 모집이 정말 기대됩니다.", TEST_USER_ID,
+			TEST_POST_ID);

81-92: SecurityContext 정리(@AfterEach) 추가 권장

테스트 간 보안 컨텍스트 누수를 방지하려면 매 테스트 이후 clearContext가 안전합니다.

아래 보조 메서드를 추가해 주세요:

import org.junit.jupiter.api.AfterEach;

@AfterEach
void tearDown() {
    SecurityContextHolder.clearContext();
}
aics-domain/src/main/java/kgu/developers/domain/post/domain/Post.java (3)

46-46: fileId 컬럼 매핑 명시(선택)

명시적 컬럼명을 두면 스키마와 코드의 대응이 분명해집니다.

적용 제안:

-	private Long fileId;
+	@Column(name = "file_id")
+	private Long fileId;

48-57: 팩토리 메서드 입력값 null 체크 추가 권장

도메인 무결성을 엔티티 생성 시 보장해 두면 이후 계층에서의 방어 코드가 줄어듭니다.

적용 제안:

+import java.util.Objects;
@@
-	public static Post create(String title, String content, Category category, String authorId, Long fileId, boolean isPinned) {
-		return Post.builder()
+	public static Post create(String title, String content, Category category, String authorId, Long fileId, boolean isPinned) {
+		Objects.requireNonNull(title, "title must not be null");
+		Objects.requireNonNull(content, "content must not be null");
+		Objects.requireNonNull(category, "category must not be null");
+		Objects.requireNonNull(authorId, "authorId must not be null");
+		return Post.builder()

80-82: updateFileId null 사용 의도 명시

null로 첨부 해제를 허용한다면 Javadoc 주석 또는 보조 메서드로 의도를 드러내는 것을 권장합니다.

보조 메서드 예시:

/**
 * 파일 첨부 해제
 */
public void clearFileId() {
    this.fileId = null;
}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 26394c6 and 840c045.

📒 Files selected for processing (27)
  • aics-admin/src/testFixtures/java/post/application/PostAdminFacadeTest.java (2 hunks)
  • aics-api/src/main/java/kgu/developers/api/comment/application/CommentFacade.java (3 hunks)
  • aics-api/src/main/java/kgu/developers/api/comment/presentation/response/CommentListResponse.java (2 hunks)
  • aics-api/src/main/java/kgu/developers/api/comment/presentation/response/CommentResponse.java (1 hunks)
  • aics-api/src/main/java/kgu/developers/api/post/application/PostFacade.java (2 hunks)
  • aics-api/src/main/java/kgu/developers/api/post/presentation/response/PostSummaryPageResponse.java (2 hunks)
  • aics-api/src/main/java/kgu/developers/api/post/presentation/response/PostSummaryResponse.java (2 hunks)
  • aics-api/src/testFixtures/java/comment/application/CommentFacadeTest.java (3 hunks)
  • aics-api/src/testFixtures/java/post/application/PostFacadeTest.java (2 hunks)
  • aics-domain/src/main/java/kgu/developers/domain/comment/application/command/CommentCommandService.java (1 hunks)
  • aics-domain/src/main/java/kgu/developers/domain/comment/domain/Comment.java (1 hunks)
  • aics-domain/src/main/java/kgu/developers/domain/post/application/command/PostCommandService.java (2 hunks)
  • aics-domain/src/main/java/kgu/developers/domain/post/application/query/PostQueryService.java (3 hunks)
  • aics-domain/src/main/java/kgu/developers/domain/post/application/response/PostDetailResponse.java (2 hunks)
  • aics-domain/src/main/java/kgu/developers/domain/post/domain/Post.java (2 hunks)
  • aics-domain/src/main/java/kgu/developers/domain/post/infrastructure/QueryPostRepository.java (2 hunks)
  • aics-domain/src/main/java/kgu/developers/domain/user/application/query/UserQueryService.java (2 hunks)
  • aics-domain/src/main/java/kgu/developers/domain/user/domain/User.java (0 hunks)
  • aics-domain/src/testFixtures/java/comment/application/CommentCommandServiceTest.java (5 hunks)
  • aics-domain/src/testFixtures/java/comment/application/CommentQueryServiceTest.java (1 hunks)
  • aics-domain/src/testFixtures/java/comment/domain/CommentDomainTest.java (1 hunks)
  • aics-domain/src/testFixtures/java/mock/FakeTestContainer.java (2 hunks)
  • aics-domain/src/testFixtures/java/mock/TestContainer.java (1 hunks)
  • aics-domain/src/testFixtures/java/mock/repository/FakeCommentRepository.java (2 hunks)
  • aics-domain/src/testFixtures/java/mock/repository/FakePostRepository.java (1 hunks)
  • aics-domain/src/testFixtures/java/post/application/PostQueryServiceTest.java (2 hunks)
  • aics-domain/src/testFixtures/java/post/domain/PostDomainTest.java (1 hunks)
💤 Files with no reviewable changes (1)
  • aics-domain/src/main/java/kgu/developers/domain/user/domain/User.java
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2024-12-03T07:09:18.535Z
Learnt from: minjo-on
PR: kgu-developers/aics-server#125
File: aics-domain/src/testFixtures/java/mock/FakeCommentRepository.java:41-45
Timestamp: 2024-12-03T07:09:18.535Z
Learning: `aics-domain/src/testFixtures/java/mock/FakeCommentRepository.java`의 `findById` 메서드에서는 데이터가 확장될 가능성이 없고 성능 차이가 크지 않으므로 Map을 사용하지 않고 List를 사용하는 것이 바람직합니다.

Applied to files:

  • aics-domain/src/testFixtures/java/mock/repository/FakePostRepository.java
  • aics-domain/src/testFixtures/java/comment/application/CommentQueryServiceTest.java
  • aics-domain/src/testFixtures/java/mock/repository/FakeCommentRepository.java
  • aics-api/src/testFixtures/java/comment/application/CommentFacadeTest.java
📚 Learning: 2024-12-03T05:01:55.233Z
Learnt from: LeeShinHaeng
PR: kgu-developers/aics-server#124
File: aics-domain/src/testFixtures/java/mock/FakeLabRepository.java:13-57
Timestamp: 2024-12-03T05:01:55.233Z
Learning: `LabServiceTest.java`에서 Java의 `BeforeEach` 애너테이션을 사용하여 각 테스트 전에 `FakeLabRepository`를 초기화하므로, `FakeLabRepository.java`에 `clear()` 메서드를 추가할 필요가 없다.

Applied to files:

  • aics-domain/src/testFixtures/java/comment/application/CommentQueryServiceTest.java
  • aics-domain/src/testFixtures/java/comment/application/CommentCommandServiceTest.java
  • aics-domain/src/testFixtures/java/post/application/PostQueryServiceTest.java
  • aics-api/src/testFixtures/java/comment/application/CommentFacadeTest.java
🧬 Code graph analysis (3)
aics-domain/src/testFixtures/java/comment/application/CommentCommandServiceTest.java (1)
aics-domain/src/testFixtures/java/mock/repository/FakeFileRepository.java (1)
  • FakeFileRepository (9-33)
aics-domain/src/testFixtures/java/post/application/PostQueryServiceTest.java (2)
aics-domain/src/testFixtures/java/mock/repository/FakeFileRepository.java (1)
  • FakeFileRepository (9-33)
aics-domain/src/testFixtures/java/mock/repository/FakeUserRepository.java (1)
  • FakeUserRepository (18-79)
aics-api/src/testFixtures/java/comment/application/CommentFacadeTest.java (1)
aics-domain/src/testFixtures/java/mock/repository/FakeFileRepository.java (1)
  • FakeFileRepository (9-33)
🪛 GitHub Actions: Validate Test Coverage and Upload to Codecov On Pull Request
aics-domain/src/testFixtures/java/post/application/PostQueryServiceTest.java

[error] 99-99: NullPointerException in PostQueryServiceTest.java at line 99 during test 'getPostById는 마지막 게시글 조회 시 다음 게시글은 null이어야 한다'.


[error] 77-77: NullPointerException in PostQueryServiceTest.java at line 77 during test 'getPostById는 해당 게시글과 이전, 다음 게시글을 조회할 수 있다'.

🔇 Additional comments (18)
aics-domain/src/main/java/kgu/developers/domain/user/application/query/UserQueryService.java (1)

18-19: 필요한 import 추가 LGTM

aics-domain/src/testFixtures/java/mock/TestContainer.java (1)

116-118: PostQueryService 3-인자 생성자 연결 LGTM

ID 참조 전환에 따른 의존성 주입이 올바르게 반영되었습니다. 테스트 컨테이너 내 다른 사용처와도 일관됩니다.

aics-domain/src/testFixtures/java/mock/repository/FakeCommentRepository.java (1)

23-24: ID 참조 전환 저장 로직 적합

builder에서 authorId, postId 사용으로 PR 목적과 일관성 있게 반영되었습니다. 테스트 픽스처 관점에서도 적절합니다. (과거 러닝: List 유지 선호 사항과도 충돌 없음)

aics-api/src/main/java/kgu/developers/api/post/presentation/response/PostSummaryPageResponse.java (1)

11-11: 필요한 import 추가 확인

ID→이름 매핑 도입에 따른 Map import 추가는 적절합니다.

aics-domain/src/testFixtures/java/comment/application/CommentQueryServiceTest.java (1)

58-59: 도메인 시그니처 변경 반영 적절

Post.create(..., String authorId, Long fileId, ...)로의 전환을 테스트가 정확히 따릅니다.

aics-admin/src/testFixtures/java/post/application/PostAdminFacadeTest.java (2)

47-47: PostQueryService 생성자 변경 반영 OK

리포지토리 3종을 주입하도록 수정된 시그니처에 맞게 테스트 초기화가 잘 맞춰졌습니다.


68-69: Post.create의 author 참조를 ID로 변경한 부분 적합

author.getId() 전달로 ID 기반 도메인 변경을 올바르게 반영했습니다.

aics-domain/src/testFixtures/java/mock/repository/FakePostRepository.java (1)

34-44: ID 참조 전환 저장 로직 적합(LGTM)

authorId/fileId로의 매핑 전환이 일관되게 반영되었습니다. fileId를 null 허용으로 유지하는 것도 조회 단계의 유연성을 보장합니다.

aics-domain/src/main/java/kgu/developers/domain/post/application/query/PostQueryService.java (1)

26-27: 레포지토리 직접 주입 전환 적절(LGTM)

조회 서비스 레이어에서 User/File을 로딩하도록 의존성 주입이 명확해졌습니다.

aics-domain/src/testFixtures/java/post/application/PostQueryServiceTest.java (2)

11-12: 의존성 임포트 추가 적절

ID 참조 전환에 맞는 FakeFileRepository/FakeUserRepository 임포트 추가는 타당합니다.


31-33: 테스트 필드 추가 OK

새 리포지토리 필드 주입으로 PostQueryService 시그니처 변경을 반영했습니다.

aics-api/src/testFixtures/java/comment/application/CommentFacadeTest.java (3)

43-48: PostQueryService 의존성 주입 정합성 확보

파일/사용자 리포지토리까지 주입하도록 수정된 구성은 현재 도메인 변경과 일치합니다.


49-58: Facade/서비스 배선 개선 LGTM

CommentCommandService가 외부에서 주입된 PostQueryService/UserQueryService를 사용하도록 정리되어 중복 생성을 줄였습니다.


69-79: ID 기반 생성 호출 전환 적절

Post/Comment 생성 시 authorId, postId를 사용하도록 수정된 부분은 도메인 시그니처 변경과 일치합니다.

aics-api/src/testFixtures/java/post/application/PostFacadeTest.java (1)

71-81: ID 기반 Post 생성 호출 전환 LGTM

author.getId() 사용으로 시그니처 변경을 정확히 반영했습니다.

aics-domain/src/testFixtures/java/comment/application/CommentCommandServiceTest.java (3)

9-9: FakeFileRepository import 추가 적절

테스트에서 PostQueryService가 파일 리포지토리를 요구하므로 import 보강은 타당합니다.


41-41: 파일 리포지토리 주입 추가 OK

테스트 픽스처 내 의존성 구성이 실제 서비스 시그니처와 일치합니다.


63-65: 초기화 메서드 시그니처 확장 적절

테스트 설정에서 추가된 리포지토리 의존성 반영이 잘 되어 있습니다.

Comment thread aics-domain/src/main/java/kgu/developers/domain/post/domain/Post.java Outdated
Comment thread aics-domain/src/testFixtures/java/mock/FakeTestContainer.java
Comment thread aics-domain/src/testFixtures/java/mock/FakeTestContainer.java
@JangYeongHu
JangYeongHu marked this pull request as draft September 2, 2025 13:28
@github-actions

github-actions Bot commented Sep 3, 2025

Copy link
Copy Markdown

Test Coverage Report

Overall Project 95.03% -0.58% 🍏
Files changed 92.65% 🍏

Module Coverage
aics-api 99.59% 🍏
aics-domain 92.31% -0.9% 🍏
Files
Module File Coverage
aics-api PostFacade.java 100% 🍏
CommentFacade.java 100% 🍏
aics-domain Post.java 100% 🍏
Comment.java 100% 🍏
PostQueryService.java 100% 🍏
PostCommandService.java 100% 🍏
User.java 100% 🍏
CommentCommandService.java 100% 🍏
UserQueryService.java 80.77% -19.23%

@codecov

codecov Bot commented Sep 3, 2025

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.29630% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...omain/user/application/query/UserQueryService.java 0.00% 1 Missing ⚠️

Impacted file tree graph

@@              Coverage Diff              @@
##             develop     #259      +/-   ##
=============================================
+ Coverage      91.42%   93.30%   +1.87%     
- Complexity       159      160       +1     
=============================================
  Files             49       49              
  Lines            455      463       +8     
  Branches           9        4       -5     
=============================================
+ Hits             416      432      +16     
+ Misses            29       26       -3     
+ Partials          10        5       -5     
Files with missing lines Coverage Δ Complexity Δ
...elopers/api/comment/application/CommentFacade.java 100.00% <100.00%> (ø) 5.00 <0.00> (ø)
...gu/developers/api/post/application/PostFacade.java 100.00% <100.00%> (ø) 2.00 <0.00> (ø)
...ent/application/command/CommentCommandService.java 100.00% <100.00%> (ø) 3.00 <0.00> (ø)
.../kgu/developers/domain/comment/domain/Comment.java 100.00% <100.00%> (ø) 2.00 <0.00> (ø)
...n/post/application/command/PostCommandService.java 100.00% <100.00%> (+9.52%) 5.00 <0.00> (ø)
...omain/post/application/query/PostQueryService.java 100.00% <100.00%> (ø) 3.00 <2.00> (ø)
...n/java/kgu/developers/domain/post/domain/Post.java 100.00% <100.00%> (ø) 9.00 <1.00> (ø)
...n/java/kgu/developers/domain/user/domain/User.java 97.72% <ø> (ø) 23.00 <0.00> (ø)
...omain/user/application/query/UserQueryService.java 91.66% <0.00%> (-8.34%) 5.00 <0.00> (ø)

... and 10 files with indirect coverage changes


Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 68c2a70...31e0f9c. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@JangYeongHu
JangYeongHu marked this pull request as ready for review September 3, 2025 15:36
@JangYeongHu

Copy link
Copy Markdown
Contributor Author

일단 테스트를 통과하도록 로직은 전부 변경했습니다.
CodeRabbit의 많은 리뷰들은 자고 일어나서 바로 고치도록 하겠습니다.
그 외의 것들 역시 피드백 부탁드립니다.

@dkdltm221 dkdltm221 left a comment

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.

LGTM👍👍

@JangYeongHu
JangYeongHu merged commit 2b14a38 into develop Sep 5, 2025
5 checks passed
@JangYeongHu
JangYeongHu deleted the refactor/#251 branch September 5, 2025 04:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🔨refactor refactoring code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

User-Post-Comment-File를 id를 사용하여 연관관계 매핑

2 participants