[chore] 도메인 엔티티 및 mapper 추가#23
Conversation
|
""" Walkthrough도메인 엔티티와 매퍼 클래스가 대거 추가 및 리팩토링되었습니다. 각 도메인별로 엔티티 클래스가 신설되거나 필드가 명시적으로 선언되었고, JPA 엔티티와 도메인 엔티티 간 변환을 수행하는 매퍼 클래스가 도입되었습니다. 일부 기존 엔티티 및 매퍼에도 변환 메서드가 추가되었습니다. Changes
Sequence Diagram(s)sequenceDiagram
participant Domain as Domain Entity
participant Mapper as Mapper
participant JPA as JPA Entity
Domain->>Mapper: toJpaEntity(domain, [연관 JPA 엔티티])
Mapper->>JPA: JPA 엔티티 생성 및 반환
JPA->>Mapper: toDomainEntity(jpaEntity)
Mapper->>Domain: 도메인 엔티티 생성 및 반환
Assessment against linked issues
Assessment against linked issues: Out-of-scope changes
Suggested labels
Suggested reviewers
Poem
✨ Finishing Touches
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 15
♻️ Duplicate comments (2)
src/main/java/konkuk/thip/user/adapter/out/mapper/SavedBookMapper.java (2)
12-17: SavedFeedMapper와 동일한 null 안전성 문제SavedFeedMapper와 동일하게 null 체크가 없어서 NPE 위험이 있습니다.
입력 파라미터에 대한 null 검증을 추가해주세요:
public SavedBookJpaEntity toJpaEntity(UserJpaEntity userJpaEntity, BookJpaEntity bookJpaEntity) { + if (userJpaEntity == null || bookJpaEntity == null) { + throw new IllegalArgumentException("UserJpaEntity and BookJpaEntity cannot be null"); + } return SavedBookJpaEntity.builder() .userJpaEntity(userJpaEntity) .bookJpaEntity(bookJpaEntity) .build(); }
19-28: 중첩 객체 접근 시 null 안전성 확보 필요JPA 엔티티의 중첩 객체 접근에서 NPE 방지가 필요합니다.
public SavedBook toDomainEntity(SavedBookJpaEntity savedBookJpaEntity) { + if (savedBookJpaEntity == null) { + throw new IllegalArgumentException("SavedBookJpaEntity cannot be null"); + } return SavedBook.builder() .id(savedBookJpaEntity.getSavedId()) - .userId(savedBookJpaEntity.getUserJpaEntity().getUserId()) - .bookId(savedBookJpaEntity.getBookJpaEntity().getBookId()) + .userId(savedBookJpaEntity.getUserJpaEntity() != null ? + savedBookJpaEntity.getUserJpaEntity().getUserId() : null) + .bookId(savedBookJpaEntity.getBookJpaEntity() != null ? + savedBookJpaEntity.getBookJpaEntity().getBookId() : null) .createdAt(savedBookJpaEntity.getCreatedAt()) .modifiedAt(savedBookJpaEntity.getModifiedAt()) .status(savedBookJpaEntity.getStatus()) .build(); }
🧹 Nitpick comments (19)
src/main/java/konkuk/thip/user/domain/SavedBook.java (1)
7-9: 동등성 비교를 위한equals/hashCode자동 생성 고려
도메인 엔티티 간 값 동등성 비교를 위해 Lombok의@EqualsAndHashCode(callSuper = true)를 추가하여 상위 클래스 필드까지 포함한equals/hashCode메서드를 생성하는 것을 권장합니다.src/main/java/konkuk/thip/room/domain/PostLike.java (2)
11-15:BaseDomainEntity중복id선언 및 필드명 일관성 검토
BaseDomainEntity에 이미id가 정의되어 있는지 확인하고, 중복 선언 시 제거하세요.- 도메인 모델 전반의 가독성을 위해
targetPostId대신postId로 간단히 명명하는 것을 고려해보세요.
7-9: 도메인 동등성 비교 메서드 자동 생성 고려
Lombok의@EqualsAndHashCode(callSuper = true)를 추가하여 상위 클래스 필드까지 포함한equals/hashCode를 생성하면, 도메인 간 비교 로직 구현 시 안전성이 높아집니다.src/main/java/konkuk/thip/user/domain/UserVote.java (1)
7-9: 값 동등성 비교를 위한equals/hashCode추가 고려
Lombok@EqualsAndHashCode(callSuper = true)를 도입하면, 도메인 엔티티 간 비교 시 논리적 동등성 보장이 용이해집니다.src/main/java/konkuk/thip/user/domain/Following.java (1)
7-9: 도메인 엔티티 동등성 비교 메서드 자동 생성 고려
Lombok@EqualsAndHashCode(callSuper = true)를 추가하여equals/hashCode를 자동 생성하면, 도메인 로직에서 객체 비교가 간편해집니다.src/main/java/konkuk/thip/room/domain/VoteItem.java (2)
11-17:BaseDomainEntity중복id선언 및count기본값 검토
- 상위 클래스에
id가 정의되어 있는지 확인하고, 중복 시 제거하세요.count필드 기본값이 0이어야 할 경우 Lombok@Builder.Default를 사용해 명시적으로 초기값을 설정하는 것을 고려해보세요.
7-9: 도메인 동등성 비교 메서드 자동 생성 고려
값 동등성 비교를 위해 Lombok@EqualsAndHashCode(callSuper = true)를 함께 사용하여equals/hashCode를 자동 생성하는 것을 권장합니다.src/main/java/konkuk/thip/user/domain/SavedFeed.java (1)
11-15: 필드 검증 및 문서화 추가를 고려해보세요.데이터 무결성을 위해 필수 필드에 대한 검증 어노테이션과 클래스 및 필드에 대한 JavaDoc 문서화를 추가하는 것을 권장합니다.
+/** + * 사용자가 저장한 피드 정보를 나타내는 도메인 엔티티 + */ @Getter @SuperBuilder public class SavedFeed extends BaseDomainEntity { + @NotNull private Long id; + @NotNull private Long userId; + @NotNull private Long feedId; }src/main/java/konkuk/thip/feed/domain/Tag.java (1)
11-17: 필드 검증 및 비즈니스 규칙 고려사항을 추가해보세요.태그 시스템의 데이터 무결성을 위해 다음 사항들을 고려해보세요:
- value 필드의 길이 제한 및 null 체크
- targetPostId와 categoryId의 필수 여부 확인
+ @NotNull private Long id; + @NotBlank + @Size(max = 50) private String value; + @NotNull private Long targetPostId; private Long categoryId; // 선택적 카테고리 분류src/main/java/konkuk/thip/room/domain/Content.java (1)
13-13: contentUrl 필드에 URL 형식 검증을 추가하는 것을 권장합니다.콘텐츠 URL의 유효성을 보장하기 위해 URL 형식 검증과 길이 제한을 추가해보세요.
+import org.hibernate.validator.constraints.URL; + /** + * 콘텐츠 파일의 URL (이미지, 동영상 등) + */ + @NotBlank + @URL + @Size(max = 2000) private String contentUrl;src/main/java/konkuk/thip/user/domain/UserRoom.java (1)
15-15: userPercentage 필드의 유효성 검증을 고려해보세요.
userPercentage필드가 0-100 범위를 벗어난 값을 가질 수 있습니다. 도메인 로직에서 유효성 검증이나 제약 조건을 추가하는 것을 권장합니다.src/main/java/konkuk/thip/feed/adapter/out/jpa/FeedJpaEntity.java (1)
28-28: 필수 매개변수에 대한 null 검증을 고려해보세요.생성자의
isPublic,bookJpaEntity매개변수들이 null이 될 수 없도록 검증하는 것을 권장합니다.src/main/java/konkuk/thip/common/entity/BaseDomainEntity.java (1)
12-14: 불변성 보장을 위한 final 키워드 고려도메인 엔티티의 불변성을 더욱 강화하려면 필드들을
final로 선언하는 것을 고려해볼 수 있습니다.- private LocalDateTime createdAt; - private LocalDateTime modifiedAt; + private final LocalDateTime createdAt; + private final LocalDateTime modifiedAt;src/main/java/konkuk/thip/book/adapter/out/mapper/BookMapper.java (1)
23-38: 매핑 로직이 정확하고 완전합니다.JPA 엔티티에서 도메인 엔티티로의 변환 로직이 모든 필드를 적절히 매핑하고 있으며, 감사 필드들도 올바르게 포함되어 있습니다.
동일하게 null 안전성 검증 추가를 권장합니다:
public Book toDomainEntity(BookJpaEntity bookJpaEntity) { + if (bookJpaEntity == null) { + return null; + } return Book.builder() // ... 기존 코드src/main/java/konkuk/thip/user/adapter/out/mapper/AttendanceCheckMapper.java (1)
25-25: 필드명 일관성을 고려해보세요.
creatorId필드명을 사용하고 있는데, 다른 매퍼들에서는 주로userId를 사용합니다. 도메인 전체에서 일관된 명명 규칙을 유지하는 것이 좋겠습니다.만약 의도적으로 역할을 구분하기 위해
creatorId를 사용한 것이라면, 다른 관련 매퍼들에서도 동일한 명명 규칙을 적용하는 것을 고려해주세요.src/main/java/konkuk/thip/room/domain/Room.java (1)
13-31: 필드 선언과 캡슐화 개선 제안모든 필드가 private으로 적절히 선언되었으나, 다음 사항들을 고려해보세요:
password필드가Integer타입인데, 보안상String타입이나 암호화된 형태가 더 적절할 수 있습니다.roomPercentage필드명이 도메인 의미를 명확하게 표현하지 못합니다.progressPercentage또는completionRate등이 더 명확할 수 있습니다.- private Integer password; + private String password; // 또는 암호화된 형태 - private double roomPercentage; + private double progressPercentage; // 또는 completionRatesrc/main/java/konkuk/thip/user/adapter/out/mapper/RecentSearchMapper.java (1)
1-31: 매퍼 클래스들의 일관성을 위한 공통 베이스 클래스 고려모든 매퍼 클래스에서 동일한 null 체크 패턴이 반복됩니다. 공통 베이스 클래스나 유틸리티 메서드를 도입하여 코드 중복을 줄이는 것을 고려해보세요.
예시:
public abstract class BaseMapper { protected void validateNotNull(Object obj, String paramName) { if (obj == null) { throw new IllegalArgumentException(paramName + " cannot be null"); } } protected <T> T safeGet(Supplier<T> supplier) { try { return supplier.get(); } catch (NullPointerException e) { return null; } } }src/main/java/konkuk/thip/comment/domain/Comment.java (1)
11-21: 필드 선언의 final 키워드 고려해보세요모든 필드가 명시적으로 선언되어 명확성이 향상되었습니다. 불변성을 보장하기 위해 필드에
final키워드 추가를 고려해보세요.- private Long id; - private String content; - private int reportCount; - private Long targetPostId; - private Long creatorId; - private Long parentCommentId; + private final Long id; + private final String content; + private final int reportCount; + private final Long targetPostId; + private final Long creatorId; + private final Long parentCommentId;src/main/java/konkuk/thip/book/domain/Book.java (1)
11-27: 필드 선언의 불변성 개선을 고려해보세요모든 필드가 명시적으로 선언되어 명확성이 향상되었습니다. 도메인 엔티티의 불변성을 보장하기 위해 필드에
final키워드 추가를 고려해보세요.- private Long id; - private String title; - private String isbn; - private String authorName; - private boolean bestSeller; - private String publisher; - private String imageUrl; - private Integer pageCount; - private String description; + private final Long id; + private final String title; + private final String isbn; + private final String authorName; + private final boolean bestSeller; + private final String publisher; + private final String imageUrl; + private final Integer pageCount; + private final String description;
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (51)
src/main/java/konkuk/thip/book/adapter/out/mapper/BookMapper.java(1 hunks)src/main/java/konkuk/thip/book/adapter/out/mapper/CategoryMapper.java(1 hunks)src/main/java/konkuk/thip/book/domain/Book.java(1 hunks)src/main/java/konkuk/thip/book/domain/Category.java(1 hunks)src/main/java/konkuk/thip/comment/adapter/out/mapper/CommentLikeMapper.java(1 hunks)src/main/java/konkuk/thip/comment/adapter/out/mapper/CommentMapper.java(1 hunks)src/main/java/konkuk/thip/comment/domain/Comment.java(1 hunks)src/main/java/konkuk/thip/comment/domain/CommentLike.java(1 hunks)src/main/java/konkuk/thip/common/entity/BaseDomainEntity.java(1 hunks)src/main/java/konkuk/thip/common/entity/BaseJpaEntity.java(1 hunks)src/main/java/konkuk/thip/common/entity/StatusType.java(1 hunks)src/main/java/konkuk/thip/feed/adapter/out/jpa/FeedJpaEntity.java(1 hunks)src/main/java/konkuk/thip/feed/adapter/out/mapper/FeedMapper.java(1 hunks)src/main/java/konkuk/thip/feed/adapter/out/mapper/TagMapper.java(1 hunks)src/main/java/konkuk/thip/feed/domain/Feed.java(1 hunks)src/main/java/konkuk/thip/feed/domain/Tag.java(1 hunks)src/main/java/konkuk/thip/room/adapter/out/jpa/PostJpaEntity.java(0 hunks)src/main/java/konkuk/thip/room/adapter/out/jpa/RecordJpaEntity.java(2 hunks)src/main/java/konkuk/thip/room/adapter/out/mapper/ContentMapper.java(1 hunks)src/main/java/konkuk/thip/room/adapter/out/mapper/PostLikeMapper.java(1 hunks)src/main/java/konkuk/thip/room/adapter/out/mapper/RecordMapper.java(1 hunks)src/main/java/konkuk/thip/room/adapter/out/mapper/RoomMapper.java(1 hunks)src/main/java/konkuk/thip/room/adapter/out/mapper/VoteItemMapper.java(1 hunks)src/main/java/konkuk/thip/room/adapter/out/mapper/VoteMapper.java(1 hunks)src/main/java/konkuk/thip/room/domain/Content.java(1 hunks)src/main/java/konkuk/thip/room/domain/PostLike.java(1 hunks)src/main/java/konkuk/thip/room/domain/Record.java(1 hunks)src/main/java/konkuk/thip/room/domain/Room.java(1 hunks)src/main/java/konkuk/thip/room/domain/Vote.java(1 hunks)src/main/java/konkuk/thip/room/domain/VoteItem.java(1 hunks)src/main/java/konkuk/thip/user/adapter/out/jpa/FollowingJpaEntity.java(1 hunks)src/main/java/konkuk/thip/user/adapter/out/mapper/AliasMapper.java(1 hunks)src/main/java/konkuk/thip/user/adapter/out/mapper/AttendanceCheckMapper.java(1 hunks)src/main/java/konkuk/thip/user/adapter/out/mapper/FollowingMapper.java(1 hunks)src/main/java/konkuk/thip/user/adapter/out/mapper/NotificationMapper.java(1 hunks)src/main/java/konkuk/thip/user/adapter/out/mapper/RecentSearchMapper.java(1 hunks)src/main/java/konkuk/thip/user/adapter/out/mapper/SavedBookMapper.java(1 hunks)src/main/java/konkuk/thip/user/adapter/out/mapper/SavedFeedMapper.java(1 hunks)src/main/java/konkuk/thip/user/adapter/out/mapper/UserMapper.java(1 hunks)src/main/java/konkuk/thip/user/adapter/out/mapper/UserRoomMapper.java(1 hunks)src/main/java/konkuk/thip/user/adapter/out/mapper/UserVoteMapper.java(1 hunks)src/main/java/konkuk/thip/user/domain/Alias.java(1 hunks)src/main/java/konkuk/thip/user/domain/AttendanceCheck.java(1 hunks)src/main/java/konkuk/thip/user/domain/Following.java(1 hunks)src/main/java/konkuk/thip/user/domain/Notification.java(1 hunks)src/main/java/konkuk/thip/user/domain/RecentSearch.java(1 hunks)src/main/java/konkuk/thip/user/domain/SavedBook.java(1 hunks)src/main/java/konkuk/thip/user/domain/SavedFeed.java(1 hunks)src/main/java/konkuk/thip/user/domain/User.java(1 hunks)src/main/java/konkuk/thip/user/domain/UserRoom.java(1 hunks)src/main/java/konkuk/thip/user/domain/UserVote.java(1 hunks)
💤 Files with no reviewable changes (1)
- src/main/java/konkuk/thip/room/adapter/out/jpa/PostJpaEntity.java
🔇 Additional comments (58)
src/main/java/konkuk/thip/common/entity/StatusType.java (1)
3-5: enum 이름 변경이 적절합니다.
JpaEntityStatus에서StatusType으로의 이름 변경이 합리적입니다. 도메인 엔티티와 JPA 엔티티 양쪽에서 공통으로 사용할 수 있는 더 일반적인 이름으로 개선되었습니다.src/main/java/konkuk/thip/room/adapter/out/jpa/RecordJpaEntity.java (1)
13-13: 공백 조정 변경사항입니다.로직에는 영향이 없는 코드 포맷팅 개선입니다.
Also applies to: 30-30
src/main/java/konkuk/thip/user/adapter/out/jpa/FollowingJpaEntity.java (1)
18-18: Java 네이밍 컨벤션에 맞는 개선입니다.
following_id에서followingId로의 필드명 변경이 Java camelCase 규칙에 맞춰 개선되었습니다. 데이터베이스 컬럼 매핑은@Column(name = "following_id")로 그대로 유지되어 적절합니다.기존 코드에서 이 필드를 직접 참조하는 부분이 있는지 확인해주세요:
#!/bin/bash # 기존 following_id 필드 참조 확인 rg -A 2 -B 2 "following_id" --type javasrc/main/java/konkuk/thip/common/entity/BaseJpaEntity.java (1)
40-40: enum 타입 변경이 일관성 있게 적용되었습니다.
JpaEntityStatus에서StatusType으로의 타입 변경과 기본값 설정이 적절하게 반영되었습니다. StatusType.java의 enum 이름 변경과 일관성 있는 변경사항입니다.src/main/java/konkuk/thip/user/domain/Alias.java (1)
1-14: 도메인 엔티티 구조가 적절합니다.
BaseDomainEntity상속과@SuperBuilder어노테이션 사용이 올바르게 구현되었습니다. 별칭을 표현하는 간단하고 명확한 도메인 모델 구조입니다.src/main/java/konkuk/thip/user/domain/SavedBook.java (2)
1-6: 도메인 엔티티 구조 적절
@Getter와@SuperBuilder를 활용해 불변성을 확보하고, 빌더 패턴으로 객체 생성을 명확하게 구성했습니다.
11-15: 다음으로BaseDomainEntity파일에서id필드 선언이 보호(protected)나 public으로 되어 있진 않은지 추가로 확인해 보겠습니다.#!/bin/bash set -e FILE=src/main/java/konkuk/thip/common/entity/BaseDomainEntity.java grep -n -E '(private|protected|public)\s+.*\s+id\b' "$FILE" || echo "명시적 id 필드 선언 없음"src/main/java/konkuk/thip/room/domain/PostLike.java (1)
1-6: 클래스 정의 승인
@Getter,@SuperBuilder사용으로 도메인 객체 표현이 깔끔하게 잘 구성되었습니다.src/main/java/konkuk/thip/user/domain/UserVote.java (2)
1-6: 도메인 엔티티 정의 적절
@Getter및@SuperBuilder활용을 통해 도메인 객체 설계가 명확하고 일관성 있습니다.
11-15: BaseDomainEntity에 실제로id필드가 선언되어 있는지 추가로 확인해 봅시다. 다음 스크립트를 실행해 주세요:#!/bin/bash file="src/main/java/konkuk/thip/common/entity/BaseDomainEntity.java" echo "=== Checking Long id declarations in $file ===" grep -n 'Long .*id' "$file" || echo "No Long id declarations found" echo "=== Checking @Id annotations in $file ===" grep -n '@Id' "$file" || echo "No @Id annotations found"src/main/java/konkuk/thip/user/domain/Following.java (2)
1-6: 도메인 엔티티 구조 적절
@Getter와@SuperBuilder사용으로 도메인 모델이 명확하고 확장 가능하게 설계되었습니다.
11-15: 다음 스크립트를 실행하여Following클래스의 상속 관계와BaseDomainEntity에 이미id가 선언되어 있는지 확인해 주세요.#!/bin/bash set -e echo "== Following.java 클래스 정보 ==" grep -n "class Following" src/main/java/konkuk/thip/user/domain/Following.java || echo "Following.java 파일을 찾을 수 없습니다." sed -n '1,20p' src/main/java/konkuk/thip/user/domain/Following.java || true echo "" echo "== BaseDomainEntity 위치 검색 ==" rg "class BaseDomainEntity" -n src || echo "BaseDomainEntity 클래스를 찾을 수 없습니다." echo "" file=$(rg "class BaseDomainEntity" -n src | head -n1 | cut -d: -f1) if [ -f "$file" ]; then echo "== BaseDomainEntity 내용 (앞 200줄) ==" sed -n '1,200p' "$file" else echo "BaseDomainEntity 파일을 찾을 수 없습니다." fisrc/main/java/konkuk/thip/room/domain/VoteItem.java (1)
1-6: 도메인 엔티티 선언 승인
@Getter및@SuperBuilder적용으로 도메인 객체 정의와 불변성 확보가 잘 이루어졌습니다.src/main/java/konkuk/thip/user/domain/SavedFeed.java (1)
7-16: 도메인 엔티티 구조가 깔끔하게 구현되었습니다.SavedFeed 클래스가 BaseDomainEntity를 상속하고 @SuperBuilder를 사용하여 일관성 있는 패턴을 따르고 있습니다. 사용자와 피드 간의 저장 관계를 명확하게 표현하고 있습니다.
src/main/java/konkuk/thip/book/domain/Category.java (1)
7-16: 카테고리 도메인 엔티티가 적절하게 설계되었습니다.BaseDomainEntity 상속과 @SuperBuilder 사용으로 일관성 있는 패턴을 따르고 있으며, 필드 타입 선택도 적절합니다.
src/main/java/konkuk/thip/feed/domain/Tag.java (1)
7-18: 태그 도메인 엔티티가 체계적으로 구현되었습니다.BaseDomainEntity 상속과 @SuperBuilder 패턴을 올바르게 사용하고 있으며, 포스트와 카테고리와의 관계를 명확하게 표현하고 있습니다.
src/main/java/konkuk/thip/user/domain/RecentSearch.java (1)
7-18: 최근 검색 도메인 엔티티가 잘 구조화되었습니다.BaseDomainEntity 상속과 @SuperBuilder 패턴을 적절히 사용하고 있으며, 사용자의 검색 이력을 효과적으로 모델링하고 있습니다.
src/main/java/konkuk/thip/room/domain/Content.java (1)
7-16: 콘텐츠 도메인 엔티티가 명확하게 구현되었습니다.BaseDomainEntity 상속과 @SuperBuilder 패턴을 올바르게 사용하고 있으며, 포스트와 연관된 콘텐츠를 효과적으로 모델링하고 있습니다.
src/main/java/konkuk/thip/comment/domain/CommentLike.java (1)
1-16:CommentLike도메인 엔티티 구조 적절
@SuperBuilder와@Getter를 활용해 빌더 및 접근자가 잘 생성되었으며,BaseDomainEntity상속으로 공통 속성도 포함된 구조가 올바릅니다.src/main/java/konkuk/thip/user/domain/AttendanceCheck.java (1)
1-18:AttendanceCheck도메인 엔티티 구조 적절
@SuperBuilder와@Getter적용으로 빌더·접근자 생성,BaseDomainEntity상속을 통해 공통 필드 일관성이 확보되었습니다.src/main/java/konkuk/thip/room/domain/Record.java (1)
1-22:Record도메인 엔티티 구조 적절
@SuperBuilder와@Getter를 사용해 일관된 빌더 패턴과 접근자 메서드가 제공되며,BaseDomainEntity상속으로 공통 속성이 통합되어 있습니다.src/main/java/konkuk/thip/room/domain/Vote.java (1)
1-22:Vote도메인 엔티티 구조 적절
@SuperBuilder및@Getter를 통해 빌더 패턴과 접근자 메서드가 올바르게 생성되었고,BaseDomainEntity상속으로 공통 속성도 포함되어 있습니다.src/main/java/konkuk/thip/user/domain/Notification.java (1)
1-20:Notification도메인 엔티티 구조 적절
@SuperBuilder와@Getter사용으로 빌더 및 접근자 생성,BaseDomainEntity상속으로 공통 필드가 잘 포함되어 있습니다.src/main/java/konkuk/thip/common/entity/BaseDomainEntity.java (1)
8-17: 잘 설계된 기반 클래스입니다.공통 필드들을 적절하게 추상화하고
@SuperBuilder를 통해 상속 구조에서 빌더 패턴을 지원하는 설계가 우수합니다. 모든 도메인 엔티티의 일관성을 보장할 수 있는 좋은 접근입니다.src/main/java/konkuk/thip/user/domain/User.java (2)
8-9: BaseDomainEntity 상속과 @SuperBuilder 적용이 훌륭합니다.기존 코드를 BaseDomainEntity를 상속받도록 적절히 리팩토링하고
@SuperBuilder를 통해 상속 구조에서 빌더 패턴을 지원하는 변경이 우수합니다.
11-20: 명시적 필드 선언으로 가독성이 향상되었습니다.필드들을 명시적으로 선언하여 도메인 모델의 구조를 명확하게 파악할 수 있게 된 점이 좋습니다.
src/main/java/konkuk/thip/user/adapter/out/mapper/AliasMapper.java (2)
10-14: toJpaEntity 메서드의 사용 용도를 확인해주세요.현재 구현은 새로운 엔티티 생성에 적합하지만, 기존 엔티티 업데이트가 필요한 경우 id 필드가 누락될 수 있습니다. 이 메서드가 새로운 엔티티 생성 전용인지 확인해주세요.
16-24: 도메인 엔티티 매핑이 올바르게 구현되었습니다.모든 필드가 적절히 매핑되고 있으며, BaseDomainEntity의 공통 필드들(createdAt, modifiedAt, status)도 올바르게 처리되고 있습니다.
src/main/java/konkuk/thip/feed/domain/Feed.java (2)
3-9: BaseDomainEntity 상속 구조가 올바르게 적용되었습니다.공통 필드들을 기반 클래스로 분리하고 @SuperBuilder를 사용하여 상속 구조에서의 빌더 패턴을 적절히 지원하고 있습니다.
11-21: 필드 명시적 선언이 코드 가독성을 향상시켰습니다.모든 필드가 명확히 선언되어 있어 도메인 엔티티의 구조를 쉽게 파악할 수 있습니다.
src/main/java/konkuk/thip/user/adapter/out/mapper/UserMapper.java (2)
12-19: UserJpaEntity 생성 로직이 올바르게 구현되었습니다.UserRole.from() 메서드를 통한 역할 변환과 AliasJpaEntity를 별도 파라미터로 받는 설계가 적절합니다.
21-32: aliasForUserJpaEntity 필드 선언부와 매핑 어노테이션(@OnetoOne 등) 확인을 위해 아래 스크립트를 실행해주세요.#!/bin/bash # aliasForUserJpaEntity 선언 위치 및 어노테이션 확인 rg -n -C5 "aliasForUserJpaEntity"src/main/java/konkuk/thip/feed/adapter/out/mapper/FeedMapper.java (2)
12-20: FeedJpaEntity 생성 로직이 올바르게 구현되었습니다.필요한 연관 엔티티들을 파라미터로 받아 매핑하는 설계가 적절하며, reportCount가 명시적으로 설정되는 것도 좋습니다.
22-34: PostJpaEntity의 ID 매핑과 도메인 Feed 클래스의 id 타입을 확인하기 위해 아래 스크립트를 실행해주세요.#!/bin/bash # 1) PostJpaEntity 정의 및 매핑 확인 FILE_POST=$(rg -n "class PostJpaEntity" -n src/main/java | grep -v Test | cut -d: -f1 | head -n1) echo "▶ PostJpaEntity 소스: $FILE_POST" sed -n '1,200p' "$FILE_POST" grep -n "@Id" "$FILE_POST" grep -n "@ManyToOne" "$FILE_POST" grep -n "private .*JpaEntity" "$FILE_POST" # 2) 도메인 Feed 클래스의 id 타입 확인 FILE_FEED=$(rg -n "class Feed" -n src/main/java | grep "Feed.java" | cut -d: -f1 | head -n1) echo "▶ 도메인 Feed 소스: $FILE_FEED" sed -n '1,200p' "$FILE_FEED" grep -n "private .*id" "$FILE_FEED"src/main/java/konkuk/thip/room/adapter/out/mapper/VoteItemMapper.java (2)
11-17: VoteItemJpaEntity 생성 로직이 올바르게 구현되었습니다.VoteJpaEntity를 별도 파라미터로 받아 의존성을 명확히 분리한 설계가 적절합니다.
19-29: 추가로 VoteItemJpaEntity 클래스의 voteJpaEntity 필드 선언 및 관계 어노테이션(nullable 여부 포함)을 확인하기 위해 다음 스크립트를 실행하겠습니다:#!/bin/bash # VoteItemJpaEntity 클래스 정의와 voteJpaEntity 필드 어노테이션 확인 rg -n "class VoteItemJpaEntity" -A10 src/main/java rg -n "voteJpaEntity" -C3 src/main/java/konkuk/thipsrc/main/java/konkuk/thip/room/adapter/out/mapper/PostLikeMapper.java (1)
1-29: 매퍼 구현이 일관되고 적절합니다.PostLike 도메인 엔티티와 PostLikeJpaEntity 간의 변환 로직이 올바르게 구현되었습니다. 양방향 변환 메서드와 관련 엔티티 처리가 적절하게 구현되어 있습니다.
src/main/java/konkuk/thip/user/adapter/out/mapper/UserRoomMapper.java (2)
17-17: 열거형 변환 메서드 확인이 필요합니다.
UserRoomRole.from()메서드가 올바르게 구현되어 있는지 확인해주세요. null 값 처리와 유효하지 않은 값에 대한 예외 처리가 적절히 되어 있는지 검증이 필요합니다.#!/bin/bash # UserRoomRole enum의 from() 메서드 구현 확인 rg -A 10 "public static.*from" --type java | grep -A 10 "UserRoomRole"
28-28: 아래 스크립트로getType()구현과 도메인 엔티티의userRoomRole필드 타입을 다시 확인해주세요.#!/bin/bash set -e echo "1. UserRoomRole#getType() 구현 확인" rg -n "getType" -A5 src/main/java/konkuk/thip/user/adapter/out/jpa/UserRoomRole.java echo "" echo "2. 도메인 엔티티 UserRoom 클래스 정의 확인" rg -n "class UserRoom" -A10 src/main/java/konkuk/thip/user/domain echo "" echo "3. UserRoom의 userRoomRole 필드 타입 확인" rg -n "userRoomRole" -A5 src/main/java/konkuk/thip/user/domainsrc/main/java/konkuk/thip/room/adapter/out/mapper/ContentMapper.java (1)
1-28: 매퍼 구현이 깔끔하고 일관됩니다.Content 도메인 엔티티와 ContentJpaEntity 간의 변환이 올바르게 구현되었습니다. 다른 매퍼 클래스들과 일관된 패턴을 따르고 있어 유지보수성이 좋습니다.
src/main/java/konkuk/thip/book/adapter/out/mapper/CategoryMapper.java (1)
1-28: 매퍼 구현이 적절하고 관계 처리가 올바릅니다.Category 도메인 엔티티와 CategoryJpaEntity 간의 변환이 올바르게 구현되었습니다. AliasJpaEntity와의 관계 매핑도 적절하게 처리되었습니다.
src/main/java/konkuk/thip/user/adapter/out/mapper/AttendanceCheckMapper.java (1)
1-32: 전반적으로 잘 구현된 매퍼입니다.AttendanceCheck 엔티티 간 변환 로직이 올바르게 구현되었고, 관련 엔티티들과의 관계 매핑도 적절합니다.
src/main/java/konkuk/thip/user/adapter/out/mapper/FollowingMapper.java (2)
11-16: JPA 엔티티 변환 메서드가 올바르게 구현되었습니다.도메인 엔티티를 JPA 엔티티로 변환하는 로직이 정확합니다. 필요한 연관 엔티티들을 적절히 설정하고 있습니다.
18-27: ```shell
#!/bin/bashLocate and inspect the FollowingJpaEntity to check nullability of userJpaEntity relations
Find the file path
FOLLOWING_JPA_FILE=$(fd -t f FollowingJpaEntity.java)
Print path and first 50 lines to inspect annotations
echo "File: $FOLLOWING_JPA_FILE"
sed -n '1,50p' "$FOLLOWING_JPA_FILE"</details> <details> <summary>src/main/java/konkuk/thip/user/adapter/out/mapper/UserVoteMapper.java (2)</summary> `12-17`: **JPA 엔티티 변환 로직이 적절합니다.** 사용자와 투표 아이템 간의 관계를 올바르게 매핑하고 있습니다. --- `19-28`: **도메인 엔티티 변환에서 null 안전성을 고려해야 합니다.** `getUserJpaEntity().getUserId()`와 `getVoteItemJpaEntity().getVoteItemId()` 호출에서 중간 객체가 null일 경우 NPE가 발생할 수 있습니다. 다른 매퍼 클래스들과 일관성 있는 null 처리 방식을 적용하는 것을 권장합니다. </details> <details> <summary>src/main/java/konkuk/thip/comment/adapter/out/mapper/CommentLikeMapper.java (1)</summary> `12-17`: **댓글 좋아요 엔티티 매핑이 올바르게 구현되었습니다.** 사용자와 댓글 간의 좋아요 관계를 적절히 설정하고 있습니다. </details> <details> <summary>src/main/java/konkuk/thip/room/adapter/out/mapper/VoteMapper.java (1)</summary> `12-20`: **도메인 엔티티에서 JPA 엔티티로의 변환이 올바릅니다.** Vote 도메인 엔티티의 모든 필드가 적절히 매핑되고 있습니다. </details> <details> <summary>src/main/java/konkuk/thip/comment/adapter/out/mapper/CommentMapper.java (1)</summary> `12-20`: **댓글 엔티티 변환 로직이 적절합니다.** 댓글의 모든 필드와 연관 관계가 올바르게 매핑되고 있습니다. 부모 댓글 설정도 적절히 처리되고 있습니다. </details> <details> <summary>src/main/java/konkuk/thip/room/domain/Room.java (1)</summary> `10-11`: **@SuperBuilder 패턴 적용이 적절합니다.** BaseDomainEntity 상속을 위한 @SuperBuilder 사용이 올바르게 적용되었습니다. 상속 기반 빌더 패턴이 정상적으로 동작할 것입니다. </details> <details> <summary>src/main/java/konkuk/thip/comment/domain/Comment.java (1)</summary> `3-9`: **BaseDomainEntity 상속 및 @SuperBuilder 적용이 올바르게 구현되었습니다** 도메인 엔티티의 공통 속성을 상속받고 계층적 빌더 패턴을 지원하기 위한 변경사항이 적절합니다. </details> <details> <summary>src/main/java/konkuk/thip/room/adapter/out/mapper/RoomMapper.java (2)</summary> `11-23`: **도메인에서 JPA 엔티티로의 매핑이 적절하게 구현되었습니다** 모든 필드가 올바르게 매핑되었고 빌더 패턴을 적절히 사용하고 있습니다. --- `25-41`: 추가로 `RoomJpaEntity` 내부에서 `BookJpaEntity` 필드의 어노테이션과 nullable 여부를 정확히 확인해 보겠습니다: ```shell #!/bin/bash # 1) BookJpaEntity 선언부 및 어노테이션 확인 echo "=== BookJpaEntity 필드 주변 ===" rg -n -C3 "BookJpaEntity" src/main/java/konkuk/thip/room/adapter/out/jpa/RoomJpaEntity.java # 2) @JoinColumn(nullable) 설정 확인 echo "=== @JoinColumn(nullable) 설정 ===" rg -n "@JoinColumn" -C2 src/main/java/konkuk/thip/room/adapter/out/jpa/RoomJpaEntity.javasrc/main/java/konkuk/thip/feed/adapter/out/mapper/TagMapper.java (2)
12-18: 도메인에서 JPA 엔티티로의 매핑이 올바르게 구현되었습니다필드 매핑이 정확하고 다른 매퍼들과 일관된 패턴을 따르고 있습니다.
20-30: 추가 검증:TagJpaEntity.java에서 연관 필드 선언부와 적용된 어노테이션(@ManyToOne,@JoinColumn등)을 확인해주세요.#!/bin/bash # TagJpaEntity 파일 경로 찾기 FILE=$(rg --files-with-matches "class TagJpaEntity") echo "→ TagJpaEntity 파일: $FILE" # 클래스 선언부와 연관 필드 선언부 출력 rg -n "class TagJpaEntity" -A5 "$FILE" rg -n "private.*PostJpaEntity" -A2 "$FILE" rg -n "private.*CategoryJpaEntity" -A2 "$FILE" # ManyToOne, JoinColumn 어노테이션 유무 확인 rg -n "@ManyToOne" -A1 "$FILE" rg -n "@JoinColumn" -A1 "$FILE"src/main/java/konkuk/thip/user/adapter/out/mapper/NotificationMapper.java (2)
11-18: 도메인에서 JPA 엔티티로의 매핑이 적절하게 구현되었습니다필드 매핑이 정확하고 일관된 패턴을 따르고 있습니다.
20-31: 아래 스크립트로NotificationJpaEntity내부의userJpaEntity필드 선언 및 애노테이션(@manytoone, @joincolumn)의 null 허용 여부를 재검증해주세요.#!/bin/bash # NotificationJpaEntity 파일 경로 찾기 file=$(fd NotificationJpaEntity.java -t f) echo "NotificationJpaEntity 파일: $file" # 클래스 선언 확인 rg -n "class NotificationJpaEntity" "$file" # userJpaEntity 필드와 애노테이션 확인 rg -n -A3 -B3 "UserJpaEntity" "$file" # @ManyToOne 애노테이션 확인 rg -n -A3 -B3 "ManyToOne" "$file" # @JoinColumn 애노테이션 확인 rg -n -A3 -B3 "JoinColumn" "$file"src/main/java/konkuk/thip/book/domain/Book.java (1)
3-9: BaseDomainEntity 상속 및 @SuperBuilder 적용이 올바르게 구현되었습니다Comment 도메인과 동일한 패턴으로 일관성 있게 구현되었습니다.
|
|
||
| private String value; | ||
|
|
||
| private Long aliasId; |
There was a problem hiding this comment.
🛠️ Refactor suggestion
aliasId 필드의 용도에 대한 문서화가 필요합니다.
aliasId 필드가 어떤 관계를 나타내는지 명확하지 않습니다. 자기 참조인지 다른 엔티티와의 관계인지 JavaDoc으로 설명을 추가해주세요.
+ /**
+ * 카테고리 별칭 ID (자기 참조 또는 관련 카테고리 참조)
+ */
private Long aliasId;🤖 Prompt for AI Agents
In src/main/java/konkuk/thip/book/domain/Category.java at line 15, the aliasId
field lacks documentation explaining its purpose and relationship. Add a JavaDoc
comment above the aliasId field clearly describing whether it represents a
self-reference or a relation to another entity, and explain its role within the
Category class.
|
|
||
| private String searchTerm; | ||
|
|
||
| private String type; |
There was a problem hiding this comment.
🛠️ Refactor suggestion
type 필드를 enum으로 변경하는 것을 고려해보세요.
검색 타입을 문자열로 관리하는 것보다 enum을 사용하면 타입 안전성과 코드 가독성을 향상시킬 수 있습니다.
+import konkuk.thip.user.domain.type.SearchType;
@Getter
@SuperBuilder
public class RecentSearch extends BaseDomainEntity {
private Long id;
+ @NotBlank
private String searchTerm;
- private String type;
+ private SearchType type;
+ @NotNull
private Long userId;
}🤖 Prompt for AI Agents
In src/main/java/konkuk/thip/user/domain/RecentSearch.java at line 15, the
'type' field is currently a String, which reduces type safety and readability.
Change the 'type' field to use an enum that defines the possible search types.
Create an enum representing the allowed search types and update all references
to 'type' accordingly to use this enum instead of a String.
|
|
||
| private double userPercentage; | ||
|
|
||
| private String userRoomRole; |
There was a problem hiding this comment.
🛠️ Refactor suggestion
타입 안전성을 위해 enum 사용을 고려해보세요.
userRoomRole 필드가 String 타입으로 선언되어 있어 런타임에 잘못된 값이 할당될 가능성이 있습니다. 타입 안전성과 코드 명확성을 위해 enum 타입 사용을 권장합니다.
- private String userRoomRole;
+ private UserRoomRole userRoomRole;별도의 UserRoomRole enum 클래스를 생성하는 것도 고려해보세요:
public enum UserRoomRole {
OWNER, ADMIN, MEMBER, READER
}🤖 Prompt for AI Agents
In src/main/java/konkuk/thip/user/domain/UserRoom.java at line 17, the
userRoomRole field is declared as a String, which risks invalid values at
runtime. To fix this, define a separate enum class UserRoomRole with constants
like OWNER, ADMIN, MEMBER, and READER, then change the userRoomRole field's type
from String to UserRoomRole to enforce type safety and improve code clarity.
| public FeedJpaEntity(String content, UserJpaEntity userJpaEntity, Boolean isPublic, int reportCount, BookJpaEntity bookJpaEntity) { | ||
| super(content, userJpaEntity); | ||
| this.isPublic = isPublic; | ||
| this.reportCount = 0; // Default 값 | ||
| this.reportCount = reportCount; | ||
| this.bookJpaEntity = bookJpaEntity; |
There was a problem hiding this comment.
🛠️ Refactor suggestion
생성자 매개변수 검증을 추가해보세요.
reportCount 매개변수가 음수값을 가질 수 없도록 검증 로직을 추가하는 것을 권장합니다.
public FeedJpaEntity(String content, UserJpaEntity userJpaEntity, Boolean isPublic, int reportCount, BookJpaEntity bookJpaEntity) {
super(content, userJpaEntity);
this.isPublic = isPublic;
+ if (reportCount < 0) {
+ throw new IllegalArgumentException("신고 횟수는 음수가 될 수 없습니다");
+ }
this.reportCount = reportCount;
this.bookJpaEntity = bookJpaEntity;
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| public FeedJpaEntity(String content, UserJpaEntity userJpaEntity, Boolean isPublic, int reportCount, BookJpaEntity bookJpaEntity) { | |
| super(content, userJpaEntity); | |
| this.isPublic = isPublic; | |
| this.reportCount = 0; // Default 값 | |
| this.reportCount = reportCount; | |
| this.bookJpaEntity = bookJpaEntity; | |
| public FeedJpaEntity(String content, UserJpaEntity userJpaEntity, Boolean isPublic, int reportCount, BookJpaEntity bookJpaEntity) { | |
| super(content, userJpaEntity); | |
| this.isPublic = isPublic; | |
| if (reportCount < 0) { | |
| throw new IllegalArgumentException("신고 횟수는 음수가 될 수 없습니다"); | |
| } | |
| this.reportCount = reportCount; | |
| this.bookJpaEntity = bookJpaEntity; | |
| } |
🤖 Prompt for AI Agents
In src/main/java/konkuk/thip/feed/adapter/out/jpa/FeedJpaEntity.java around
lines 28 to 32, the constructor lacks validation for the reportCount parameter.
Add a check to ensure reportCount is not negative, and if it is, throw an
IllegalArgumentException or handle it appropriately to prevent invalid state.
| public BookJpaEntity toJpaEntity(Book book) { | ||
| return BookJpaEntity.builder() | ||
| .title(book.getTitle()) | ||
| .isbn(book.getIsbn()) | ||
| .authorName(book.getAuthorName()) | ||
| .bestSeller(book.isBestSeller()) | ||
| .publisher(book.getPublisher()) | ||
| .imageUrl(book.getImageUrl()) | ||
| .pageCount(book.getPageCount()) | ||
| .description(book.getDescription()) | ||
| .build(); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
null 안전성 검증을 추가해보세요.
매핑 메소드에서 입력 매개변수의 null 검증을 추가하는 것을 권장합니다.
public BookJpaEntity toJpaEntity(Book book) {
+ if (book == null) {
+ return null;
+ }
return BookJpaEntity.builder()
.title(book.getTitle())
.isbn(book.getIsbn())
.authorName(book.getAuthorName())
.bestSeller(book.isBestSeller())
.publisher(book.getPublisher())
.imageUrl(book.getImageUrl())
.pageCount(book.getPageCount())
.description(book.getDescription())
.build();
}🤖 Prompt for AI Agents
In src/main/java/konkuk/thip/book/adapter/out/mapper/BookMapper.java between
lines 10 and 21, add a null check for the input parameter 'book' at the start of
the toJpaEntity method. If 'book' is null, handle it appropriately by either
returning null or throwing an IllegalArgumentException to prevent
NullPointerExceptions during mapping.
| public SavedFeedJpaEntity toJpaEntity(UserJpaEntity userJpaEntity, FeedJpaEntity feedJpaEntity) { | ||
| return SavedFeedJpaEntity.builder() | ||
| .userJpaEntity(userJpaEntity) | ||
| .feedJpaEntity(feedJpaEntity) | ||
| .build(); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
null 안전성 검증이 필요합니다.
매퍼 메서드에 null 체크가 없어서 NPE 위험이 있습니다. 입력 파라미터에 대한 검증을 추가하는 것이 좋습니다.
public SavedFeedJpaEntity toJpaEntity(UserJpaEntity userJpaEntity, FeedJpaEntity feedJpaEntity) {
+ if (userJpaEntity == null || feedJpaEntity == null) {
+ throw new IllegalArgumentException("UserJpaEntity and FeedJpaEntity cannot be null");
+ }
return SavedFeedJpaEntity.builder()
.userJpaEntity(userJpaEntity)
.feedJpaEntity(feedJpaEntity)
.build();
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| public SavedFeedJpaEntity toJpaEntity(UserJpaEntity userJpaEntity, FeedJpaEntity feedJpaEntity) { | |
| return SavedFeedJpaEntity.builder() | |
| .userJpaEntity(userJpaEntity) | |
| .feedJpaEntity(feedJpaEntity) | |
| .build(); | |
| } | |
| public SavedFeedJpaEntity toJpaEntity(UserJpaEntity userJpaEntity, FeedJpaEntity feedJpaEntity) { | |
| if (userJpaEntity == null || feedJpaEntity == null) { | |
| throw new IllegalArgumentException("UserJpaEntity and FeedJpaEntity cannot be null"); | |
| } | |
| return SavedFeedJpaEntity.builder() | |
| .userJpaEntity(userJpaEntity) | |
| .feedJpaEntity(feedJpaEntity) | |
| .build(); | |
| } |
🤖 Prompt for AI Agents
In src/main/java/konkuk/thip/user/adapter/out/mapper/SavedFeedMapper.java
between lines 12 and 17, the toJpaEntity method lacks null checks for its input
parameters userJpaEntity and feedJpaEntity, which can cause
NullPointerExceptions. Add null validation at the start of the method to verify
that neither parameter is null, and handle the null case appropriately, such as
throwing an IllegalArgumentException or returning null, to ensure null safety.
| public RecordJpaEntity toJpaEntity(Record record, UserJpaEntity userJpaEntity, RoomJpaEntity roomJpaEntity) { | ||
| return RecordJpaEntity.builder() | ||
| .content(record.getContent()) | ||
| .userJpaEntity(userJpaEntity) | ||
| .page(record.getPage()) | ||
| .isOverview(record.isOverview()) | ||
| .roomJpaEntity(roomJpaEntity) | ||
| .build(); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
복잡한 매핑에서 null 안전성과 검증 강화 필요
3개의 파라미터를 받는 메서드에서 더 철저한 검증이 필요합니다.
public RecordJpaEntity toJpaEntity(Record record, UserJpaEntity userJpaEntity, RoomJpaEntity roomJpaEntity) {
+ if (record == null) {
+ throw new IllegalArgumentException("Record cannot be null");
+ }
+ if (userJpaEntity == null || roomJpaEntity == null) {
+ throw new IllegalArgumentException("UserJpaEntity and RoomJpaEntity cannot be null");
+ }
return RecordJpaEntity.builder()
.content(record.getContent())
.userJpaEntity(userJpaEntity)
.page(record.getPage())
.isOverview(record.isOverview())
.roomJpaEntity(roomJpaEntity)
.build();
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| public RecordJpaEntity toJpaEntity(Record record, UserJpaEntity userJpaEntity, RoomJpaEntity roomJpaEntity) { | |
| return RecordJpaEntity.builder() | |
| .content(record.getContent()) | |
| .userJpaEntity(userJpaEntity) | |
| .page(record.getPage()) | |
| .isOverview(record.isOverview()) | |
| .roomJpaEntity(roomJpaEntity) | |
| .build(); | |
| } | |
| public RecordJpaEntity toJpaEntity(Record record, UserJpaEntity userJpaEntity, RoomJpaEntity roomJpaEntity) { | |
| if (record == null) { | |
| throw new IllegalArgumentException("Record cannot be null"); | |
| } | |
| if (userJpaEntity == null || roomJpaEntity == null) { | |
| throw new IllegalArgumentException("UserJpaEntity and RoomJpaEntity cannot be null"); | |
| } | |
| return RecordJpaEntity.builder() | |
| .content(record.getContent()) | |
| .userJpaEntity(userJpaEntity) | |
| .page(record.getPage()) | |
| .isOverview(record.isOverview()) | |
| .roomJpaEntity(roomJpaEntity) | |
| .build(); | |
| } |
🤖 Prompt for AI Agents
In src/main/java/konkuk/thip/room/adapter/out/mapper/RecordMapper.java around
lines 12 to 20, the toJpaEntity method takes three parameters but lacks null
checks and validation. Add null safety checks for each parameter and validate
critical fields before mapping to ensure robustness and prevent potential
NullPointerExceptions or invalid data propagation.
| public Record toDomainEntity(RecordJpaEntity recordJpaEntity) { | ||
| return Record.builder() | ||
| .id(recordJpaEntity.getPostId()) | ||
| .content(recordJpaEntity.getContent()) | ||
| .creatorId(recordJpaEntity.getUserJpaEntity().getUserId()) | ||
| .page(recordJpaEntity.getPage()) | ||
| .isOverview(recordJpaEntity.isOverview()) | ||
| .roomId(recordJpaEntity.getRoomJpaEntity().getRoomId()) | ||
| .createdAt(recordJpaEntity.getCreatedAt()) | ||
| .modifiedAt(recordJpaEntity.getModifiedAt()) | ||
| .status(recordJpaEntity.getStatus()) | ||
| .build(); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
중첩 객체 접근에서 null 안전성 보장 필요
복잡한 도메인 매핑에서 null 체크가 더욱 중요합니다.
public Record toDomainEntity(RecordJpaEntity recordJpaEntity) {
+ if (recordJpaEntity == null) {
+ throw new IllegalArgumentException("RecordJpaEntity cannot be null");
+ }
return Record.builder()
.id(recordJpaEntity.getPostId())
.content(recordJpaEntity.getContent())
- .creatorId(recordJpaEntity.getUserJpaEntity().getUserId())
+ .creatorId(recordJpaEntity.getUserJpaEntity() != null ?
+ recordJpaEntity.getUserJpaEntity().getUserId() : null)
.page(recordJpaEntity.getPage())
.isOverview(recordJpaEntity.isOverview())
- .roomId(recordJpaEntity.getRoomJpaEntity().getRoomId())
+ .roomId(recordJpaEntity.getRoomJpaEntity() != null ?
+ recordJpaEntity.getRoomJpaEntity().getRoomId() : null)
.createdAt(recordJpaEntity.getCreatedAt())
.modifiedAt(recordJpaEntity.getModifiedAt())
.status(recordJpaEntity.getStatus())
.build();
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| public Record toDomainEntity(RecordJpaEntity recordJpaEntity) { | |
| return Record.builder() | |
| .id(recordJpaEntity.getPostId()) | |
| .content(recordJpaEntity.getContent()) | |
| .creatorId(recordJpaEntity.getUserJpaEntity().getUserId()) | |
| .page(recordJpaEntity.getPage()) | |
| .isOverview(recordJpaEntity.isOverview()) | |
| .roomId(recordJpaEntity.getRoomJpaEntity().getRoomId()) | |
| .createdAt(recordJpaEntity.getCreatedAt()) | |
| .modifiedAt(recordJpaEntity.getModifiedAt()) | |
| .status(recordJpaEntity.getStatus()) | |
| .build(); | |
| } | |
| public Record toDomainEntity(RecordJpaEntity recordJpaEntity) { | |
| if (recordJpaEntity == null) { | |
| throw new IllegalArgumentException("RecordJpaEntity cannot be null"); | |
| } | |
| return Record.builder() | |
| .id(recordJpaEntity.getPostId()) | |
| .content(recordJpaEntity.getContent()) | |
| .creatorId(recordJpaEntity.getUserJpaEntity() != null | |
| ? recordJpaEntity.getUserJpaEntity().getUserId() | |
| : null) | |
| .page(recordJpaEntity.getPage()) | |
| .isOverview(recordJpaEntity.isOverview()) | |
| .roomId(recordJpaEntity.getRoomJpaEntity() != null | |
| ? recordJpaEntity.getRoomJpaEntity().getRoomId() | |
| : null) | |
| .createdAt(recordJpaEntity.getCreatedAt()) | |
| .modifiedAt(recordJpaEntity.getModifiedAt()) | |
| .status(recordJpaEntity.getStatus()) | |
| .build(); | |
| } |
🤖 Prompt for AI Agents
In src/main/java/konkuk/thip/room/adapter/out/mapper/RecordMapper.java around
lines 22 to 34, the method toDomainEntity accesses nested objects without null
checks, risking NullPointerExceptions. Add null safety checks before accessing
nested properties like getUserJpaEntity() and getRoomJpaEntity(), returning
default values or handling null cases appropriately to ensure the method is
null-safe.
| public RecentSearchJpaEntity toJpaEntity(RecentSearch recentSearch, UserJpaEntity userJpaEntity) { | ||
| return RecentSearchJpaEntity.builder() | ||
| .searchTerm(recentSearch.getSearchTerm()) | ||
| .type(SearchType.from(recentSearch.getType())) | ||
| .userJpaEntity(userJpaEntity) | ||
| .build(); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
SearchType 변환과 null 안전성 개선 필요
SearchType 변환 로직에서 예외가 발생할 수 있고, null 체크가 필요합니다.
public RecentSearchJpaEntity toJpaEntity(RecentSearch recentSearch, UserJpaEntity userJpaEntity) {
+ if (recentSearch == null || userJpaEntity == null) {
+ throw new IllegalArgumentException("RecentSearch and UserJpaEntity cannot be null");
+ }
+
+ SearchType searchType;
+ try {
+ searchType = SearchType.from(recentSearch.getType());
+ } catch (Exception e) {
+ throw new IllegalArgumentException("Invalid search type: " + recentSearch.getType(), e);
+ }
+
return RecentSearchJpaEntity.builder()
.searchTerm(recentSearch.getSearchTerm())
- .type(SearchType.from(recentSearch.getType()))
+ .type(searchType)
.userJpaEntity(userJpaEntity)
.build();
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| public RecentSearchJpaEntity toJpaEntity(RecentSearch recentSearch, UserJpaEntity userJpaEntity) { | |
| return RecentSearchJpaEntity.builder() | |
| .searchTerm(recentSearch.getSearchTerm()) | |
| .type(SearchType.from(recentSearch.getType())) | |
| .userJpaEntity(userJpaEntity) | |
| .build(); | |
| } | |
| public RecentSearchJpaEntity toJpaEntity(RecentSearch recentSearch, UserJpaEntity userJpaEntity) { | |
| if (recentSearch == null || userJpaEntity == null) { | |
| throw new IllegalArgumentException("RecentSearch and UserJpaEntity cannot be null"); | |
| } | |
| SearchType searchType; | |
| try { | |
| searchType = SearchType.from(recentSearch.getType()); | |
| } catch (Exception e) { | |
| throw new IllegalArgumentException("Invalid search type: " + recentSearch.getType(), e); | |
| } | |
| return RecentSearchJpaEntity.builder() | |
| .searchTerm(recentSearch.getSearchTerm()) | |
| .type(searchType) | |
| .userJpaEntity(userJpaEntity) | |
| .build(); | |
| } |
🤖 Prompt for AI Agents
In src/main/java/konkuk/thip/user/adapter/out/mapper/RecentSearchMapper.java
between lines 12 and 18, the conversion of SearchType using
SearchType.from(recentSearch.getType()) can throw exceptions and lacks null
safety. Modify the code to safely handle null values from recentSearch.getType()
before conversion and catch or prevent exceptions during the conversion. Ensure
that if the type is null or invalid, a default or null value is set without
throwing an error.
| public RecentSearch toDomainEntity(RecentSearchJpaEntity recentSearchJpaEntity) { | ||
| return RecentSearch.builder() | ||
| .id(recentSearchJpaEntity.getRecentSearchId()) | ||
| .searchTerm(recentSearchJpaEntity.getSearchTerm()) | ||
| .type(recentSearchJpaEntity.getType().getSearchType()) | ||
| .userId(recentSearchJpaEntity.getUserJpaEntity().getUserId()) | ||
| .createdAt(recentSearchJpaEntity.getCreatedAt()) | ||
| .modifiedAt(recentSearchJpaEntity.getModifiedAt()) | ||
| .status(recentSearchJpaEntity.getStatus()) | ||
| .build(); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
enum 역변환과 null 안전성 처리 필요
SearchType enum의 역변환과 중첩 객체 접근에서 안전성을 보장해야 합니다.
public RecentSearch toDomainEntity(RecentSearchJpaEntity recentSearchJpaEntity) {
+ if (recentSearchJpaEntity == null) {
+ throw new IllegalArgumentException("RecentSearchJpaEntity cannot be null");
+ }
return RecentSearch.builder()
.id(recentSearchJpaEntity.getRecentSearchId())
.searchTerm(recentSearchJpaEntity.getSearchTerm())
- .type(recentSearchJpaEntity.getType().getSearchType())
- .userId(recentSearchJpaEntity.getUserJpaEntity().getUserId())
+ .type(recentSearchJpaEntity.getType() != null ?
+ recentSearchJpaEntity.getType().getSearchType() : null)
+ .userId(recentSearchJpaEntity.getUserJpaEntity() != null ?
+ recentSearchJpaEntity.getUserJpaEntity().getUserId() : null)
.createdAt(recentSearchJpaEntity.getCreatedAt())
.modifiedAt(recentSearchJpaEntity.getModifiedAt())
.status(recentSearchJpaEntity.getStatus())
.build();
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| public RecentSearch toDomainEntity(RecentSearchJpaEntity recentSearchJpaEntity) { | |
| return RecentSearch.builder() | |
| .id(recentSearchJpaEntity.getRecentSearchId()) | |
| .searchTerm(recentSearchJpaEntity.getSearchTerm()) | |
| .type(recentSearchJpaEntity.getType().getSearchType()) | |
| .userId(recentSearchJpaEntity.getUserJpaEntity().getUserId()) | |
| .createdAt(recentSearchJpaEntity.getCreatedAt()) | |
| .modifiedAt(recentSearchJpaEntity.getModifiedAt()) | |
| .status(recentSearchJpaEntity.getStatus()) | |
| .build(); | |
| } | |
| public RecentSearch toDomainEntity(RecentSearchJpaEntity recentSearchJpaEntity) { | |
| if (recentSearchJpaEntity == null) { | |
| throw new IllegalArgumentException("RecentSearchJpaEntity cannot be null"); | |
| } | |
| return RecentSearch.builder() | |
| .id(recentSearchJpaEntity.getRecentSearchId()) | |
| .searchTerm(recentSearchJpaEntity.getSearchTerm()) | |
| .type(recentSearchJpaEntity.getType() != null | |
| ? recentSearchJpaEntity.getType().getSearchType() | |
| : null) | |
| .userId(recentSearchJpaEntity.getUserJpaEntity() != null | |
| ? recentSearchJpaEntity.getUserJpaEntity().getUserId() | |
| : null) | |
| .createdAt(recentSearchJpaEntity.getCreatedAt()) | |
| .modifiedAt(recentSearchJpaEntity.getModifiedAt()) | |
| .status(recentSearchJpaEntity.getStatus()) | |
| .build(); | |
| } |
🤖 Prompt for AI Agents
In src/main/java/konkuk/thip/user/adapter/out/mapper/RecentSearchMapper.java
around lines 20 to 30, the method toDomainEntity lacks null safety checks and
does not properly convert the SearchType enum from the JPA entity to the domain
enum. Add null checks for recentSearchJpaEntity, its nested objects, and fields
to prevent NullPointerExceptions. Implement a safe enum conversion method to map
the JPA SearchType to the domain SearchType, handling possible null or unknown
values gracefully.
| super(content, userJpaEntity); | ||
| this.isPublic = isPublic; | ||
| this.reportCount = 0; // Default 값 | ||
| this.reportCount = reportCount; |
There was a problem hiding this comment.
이거는 왜 수정하신건가용?.. 단순궁금
새로 생성되는 FeedJpaEntity들은 신고횟수가 0이지 않나용..?
There was a problem hiding this comment.
이건 도메인 -> jpa 로 매핑시에 도메인의 reportCount 값을 jpa의 reportCount 값으로 매핑하는게 더 직관적이지 않나 싶어서 이렇게 수정했습니다!
피드를 최초로 create 하는 시점에 Feed 도메인 엔티티의 reportCount 값이 0일텐데, 이 값을 jpa로 그대로 매핑하는 느낌을 생각했습니다!
| public Comment toDomainEntity(CommentJpaEntity commentJpaEntity) { | ||
| return Comment.builder() | ||
| .id(commentJpaEntity.getCommentId()) | ||
| .content(commentJpaEntity.getContent()) | ||
| .reportCount(commentJpaEntity.getReportCount()) | ||
| .targetPostId(commentJpaEntity.getPostJpaEntity().getPostId()) | ||
| .creatorId(commentJpaEntity.getUserJpaEntity().getUserId()) | ||
| .parentCommentId(commentJpaEntity.getParent().getCommentId()) | ||
| .createdAt(commentJpaEntity.getCreatedAt()) | ||
| .modifiedAt(commentJpaEntity.getModifiedAt()) | ||
| .status(commentJpaEntity.getStatus()) | ||
| .build(); | ||
| } |
There was a problem hiding this comment.
P1: 여기서 CommentJpaEntity가 부모 Comment가 없을 경우 NPE가 발생할 것 같은데 어떻게 생각하시나욥
There was a problem hiding this comment.
당연히 그렇네요! 이거 매퍼에서 null 체크하도록 코드 수정하겠습니다!
null 체크하도록 수정
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
src/test/java/konkuk/thip/domain/room/adapter/out/jpa/RoomJpaEntityTest.java (1)
56-68: CategoryJpaEntity 생성 로직을 개선할 수 있습니다.현재 하드코딩된 값들("칭호1", "카테고리1")을 사용하고 있습니다. 테스트의 가독성과 유지보수성을 위해 상수로 추출하는 것을 고려해보세요.
+ private static final String TEST_ALIAS_VALUE = "칭호1"; + private static final String TEST_CATEGORY_VALUE = "카테고리1"; AliasJpaEntity alias = AliasJpaEntity.builder() - .value("칭호1") + .value(TEST_ALIAS_VALUE) .build(); CategoryJpaEntity category = CategoryJpaEntity.builder() - .value("카테고리1") + .value(TEST_CATEGORY_VALUE) .aliasForCategoryJpaEntity(alias) .build();src/test/java/konkuk/thip/domain/room/adapter/out/jpa/RecordJpaEntityTest.java (1)
91-99: 코드 중복을 줄일 수 있습니다.이 createCategory 메서드는 VoteJpaEntityTest의 동일한 메서드와 거의 같습니다. 공통 테스트 유틸리티 클래스로 추출하는 것을 고려해보세요.
테스트 간 공통 엔티티 생성 로직을 별도의 TestEntityFactory 클래스로 추출할 수 있습니다:
public class TestEntityFactory { public static CategoryJpaEntity createCategory(AliasJpaRepository aliasRepository, CategoryJpaRepository categoryRepository) { AliasJpaEntity alias = AliasJpaEntity.builder().value("테스트_별칭").build(); aliasRepository.save(alias); return categoryRepository.save(CategoryJpaEntity.builder() .value("테스트_카테고리") .aliasForCategoryJpaEntity(alias) .build()); } }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
src/main/java/konkuk/thip/book/adapter/out/persistence/CategoryJpaRepository.java(1 hunks)src/test/java/konkuk/thip/domain/room/adapter/out/jpa/RecordJpaEntityTest.java(4 hunks)src/test/java/konkuk/thip/domain/room/adapter/out/jpa/RoomJpaEntityTest.java(4 hunks)src/test/java/konkuk/thip/domain/room/adapter/out/jpa/VoteJpaEntityTest.java(4 hunks)
✅ Files skipped from review due to trivial changes (1)
- src/main/java/konkuk/thip/book/adapter/out/persistence/CategoryJpaRepository.java
🔇 Additional comments (8)
src/test/java/konkuk/thip/domain/room/adapter/out/jpa/RoomJpaEntityTest.java (2)
6-6: 새로운 의존성 추가가 적절하게 구현되었습니다.CategoryJpaEntity와 AliasJpaEntity 관련 의존성들이 일관성 있게 추가되었습니다.
Also applies to: 8-8, 11-12, 30-31, 33-34
77-77: RoomJpaEntity와 CategoryJpaEntity의 연관관계 설정이 올바릅니다.새로운 categoryJpaEntity 필드가 적절하게 설정되었습니다.
src/test/java/konkuk/thip/domain/room/adapter/out/jpa/RecordJpaEntityTest.java (3)
5-5: CategoryJpaEntity 관련 의존성이 적절하게 추가되었습니다.새로운 엔티티 지원을 위한 import와 repository 의존성이 올바르게 추가되었습니다.
Also applies to: 7-7, 49-50
78-89: createRoom 메서드의 시그니처 변경이 적절합니다.CategoryJpaEntity 파라미터를 받아 RoomJpaEntity에 설정하는 로직이 올바르게 구현되었습니다.
105-105: 테스트 로직 변경이 적절합니다.createCategory()를 호출하여 CategoryJpaEntity를 생성하고 createRoom에 전달하는 로직이 올바릅니다.
src/test/java/konkuk/thip/domain/room/adapter/out/jpa/VoteJpaEntityTest.java (3)
6-6: CategoryJpaEntity 관련 의존성이 적절하게 추가되었습니다.새로운 엔티티 지원을 위한 import와 repository 의존성이 올바르게 추가되었습니다.
Also applies to: 8-8, 50-51
79-90: createRoom 메서드의 시그니처 변경이 적절합니다.CategoryJpaEntity 파라미터를 받아 RoomJpaEntity에 설정하는 로직이 올바르게 구현되었습니다.
106-106: 테스트 로직 변경이 적절합니다.createCategory()를 호출하여 CategoryJpaEntity를 생성하고 createRoom에 전달하는 로직이 올바릅니다.
| private CategoryJpaEntity createCategory() { | ||
| AliasJpaEntity alias = AliasJpaEntity.builder().value("익명1").build(); | ||
| aliasRepository.save(alias); | ||
|
|
||
| return categoryRepository.save(CategoryJpaEntity.builder() | ||
| .value("카테고리1") | ||
| .aliasForCategoryJpaEntity(alias) | ||
| .build()); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
중복 코드를 반드시 제거해야 합니다.
이 createCategory 메서드는 RecordJpaEntityTest의 동일한 메서드와 완전히 같습니다. 이는 DRY 원칙에 위배되며, 유지보수 시 일관성 문제를 야기할 수 있습니다.
공통 테스트 유틸리티로 추출하거나, 최소한 상수를 사용하여 하드코딩을 피해주세요:
// 옵션 1: 공통 유틸리티 클래스 생성
// 옵션 2: 상수 사용으로 최소 개선
private static final String TEST_ALIAS_VALUE = "테스트_별칭";
private static final String TEST_CATEGORY_VALUE = "테스트_카테고리";
private CategoryJpaEntity createCategory() {
AliasJpaEntity alias = AliasJpaEntity.builder().value(TEST_ALIAS_VALUE).build();
aliasRepository.save(alias);
return categoryRepository.save(CategoryJpaEntity.builder()
.value(TEST_CATEGORY_VALUE)
.aliasForCategoryJpaEntity(alias)
.build());
}🤖 Prompt for AI Agents
In src/test/java/konkuk/thip/domain/room/adapter/out/jpa/VoteJpaEntityTest.java
around lines 92 to 100, the createCategory method duplicates code found in
RecordJpaEntityTest, violating the DRY principle. Refactor by extracting this
method into a shared test utility class or at least replace hardcoded string
literals with static final constants for alias and category values to improve
maintainability and consistency across tests.



#️⃣ 연관된 이슈
📝 작업 내용
모든 jpa entity (PostJpaEntity 제외) 와 1대1로 대응되는 도메인 엔티티 및 mapper 클래스들을 추가하였습니다
(ref : https://velog.io/@dmsqo1403/SuperBuilder-%EB%9E%80)
📸 스크린샷
💬 리뷰 요구사항
📌 PR 진행 시 이러한 점들을 참고해 주세요
Summary by CodeRabbit
Summary by CodeRabbit
신규 기능
버그 수정
리팩터링