Skip to content

☀️ Comment: 댓글별 첫 페이지 답글을 JSON 필드로 관리 #110

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: main
Choose a base branch
from

Conversation

seonghooni
Copy link
Contributor

@seonghooni seonghooni commented Jun 6, 2025

Issues

Description

  • CommentEntity에서 List를 JSON 컬럼으로 갖는 필드를 추가합니다.
  • JSON 컬럼 추가에 따른 CRUD 처리를 수정했습니다.
    • 모든 답글은 reply 테이블에 추가합니다.
    • READ :: 댓글별 reply 10개를 첫 페이지로 comment 테이블의 JSON 컬럼으로 관리합니다.
    • CREATE :: 첫 페이지 이내일 때: comment 테이블의 JSON 컬럼에 추가합니다.
    • UPDATE :: 첫 페이지 이내일 때: comment 테이블의 JSON 컬럼에 반영합니다.
    • DELETE :: comment 테이블의 JSON 컬럼에 동일한 reply가 있다면 반영하고, 다음 reply를 추가합니다.

✅ JSON 컬럼 직렬화를 위해 필요한 라이브러리 import

    // JSON 컬럼 직렬화/역직렬화를 위한 라이브러리
    implementation("com.fasterxml.jackson.core:jackson-databind:2.17.0")

    // JavaTimeModule 을 통한 Instant 타입의 createdAt, updatedAt 직렬화를 위한 라이브러리
    implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310")

✅ CommentEntity 클래스

    @Convert(converter = ReplyListConverter.class)
    @JdbcTypeCode(SqlTypes.JSON)
    public List<ReplyEntity> replies = new ArrayList<>();

✅ ReplyListConverter 클래스

@Converter
public class ReplyListConverter implements AttributeConverter<List<ReplyEntity>, String> {

    private final ObjectMapper objectMapper = new ObjectMapper()
        .registerModule(new JavaTimeModule())
        .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); // 날짜 필드를 숫자가 아닌 ISO 8601 문자열("2025-06-06T04:37:00Z")로 직렬화합니다.

    @Override
    public String convertToDatabaseColumn(List<ReplyEntity> replies) {
        try {
            return objectMapper.writeValueAsString(replies);
        } catch (JsonProcessingException e) {
            throw DEFAULT.exception();
        }
    }

    @Override
    public List<ReplyEntity> convertToEntityAttribute(String dbData) {
        try {
            if (dbData == null || dbData.isBlank()) return new ArrayList<>();
            return objectMapper.readValue(dbData, new TypeReference<List<ReplyEntity>>() {});
        } catch (JsonProcessingException e) {
            throw DEFAULT.exception();
        }
    }
}

Review Points

  • Domain -> Entity의 Mapping 작업에서 private인 id, created_at, updated_at 을 처리하기 위해서는 Builder를 필요로 합니다.
@Getter
@MappedSuperclass
@SuperBuilder           // 추가
@NoArgsConstructor      // 추가
@EntityListeners(AuditingEntityListener.class)
public abstract class LongBaseTimeEntity extends LongBaseEntity {
    @CreatedDate
    private Instant createdAt;

    @LastModifiedDate
    private Instant updatedAt;
}

@Getter
@SuperBuilder           // 추가
@NoArgsConstructor      // 추가
@MappedSuperclass
public abstract class LongBaseEntity implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
}

  • CommentQueryApi, CommentQueryService 에 현재 JSON을 통한 조회 함수와 JSON을 사용하지 않는 기존 함수가 같이 존재합니다.
    향후 성능 테스트를 위해 남겨 놓았습니다. 코드 리뷰시 참고해주시면 감사하겠습니다!

How Has This Been Tested?

  • API 요청을 발생시켜 각각의 동작에 대해 정상적인 결과를 확인했습니다.
  • 추가로 테스트 코드 및 성능 비교도 진행한 후 공유하겠습니다.

Additional Notes

  • 이 PR과 관련된 추가적인 정보가 있다면 여기에 기재해 주세요.

Copy link
Member

@wch-os wch-os left a comment

Choose a reason for hiding this comment

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

꼼꼼히 주석을 달아주셔, 코드를 읽고 이해하기 수월했습니다!

Jpa 내 Pageable 사용과 JSON 컬럼 지정, Mapper 사용 측면에서 새롭게 알아가는 부분도 많았던 리뷰였던 것 같습니다!

수고 많으셨습니다!!👍

@@ -24,11 +33,16 @@ public class CommentEntity extends LongBaseTimeEntity {
@Convert(converter = CommentEntityStatusConverter.class)
public CommentEntityStatus status;

@Convert(converter = ReplyListConverter.class)
@JdbcTypeCode(SqlTypes.JSON)
Copy link
Member

Choose a reason for hiding this comment

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

Entity에서의 json 컬럼 지정을 이렇게 하네요!


private final ObjectMapper objectMapper = new ObjectMapper()
.registerModule(new JavaTimeModule())
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); // 날짜 필드를 숫자가 아닌 ISO 8601 문자열("2025-06-06T04:37:00Z")로 직렬화합니다.
Copy link
Member

Choose a reason for hiding this comment

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

아하 ReplyEntity의 LongBaseTimeEntity 시간 관련 필드들을 직렬화, 역직렬화하기 위해서는 해당 작업이 필요하나 보네요!

import org.mapstruct.Mapper;

@Mapper(componentModel = "spring")
@Mapper(componentModel = "spring", uses = ReplyEntityMapper.class)
Copy link
Member

Choose a reason for hiding this comment

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

현재 mapper 클래스에서 다른 mapper를 사용할 수 있게 지정할 수도 있네요..!!

@Param("commentId") Long commentId,
@Param("createdAt") Instant createdAt,
@Param("status") ReplyEntityStatus status,
Pageable pageable);
Copy link
Member

Choose a reason for hiding this comment

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

모든 데이터를 조회해서 메모리에 불러오는 대신에, Pageable을 파라미터로 이렇게 가볍게도 호출할 수 있군요!!👍

Copy link
Contributor

@sweetykr7 sweetykr7 left a comment

Choose a reason for hiding this comment

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

댓글의 답글을 JSON 컬럼으로 관리하는 방식이 깔끔하게 잘 설계된 것 같습니다~!
페이지 내 reply만 comment 테이블에 반영하는 구조도 성능을 잘 고려하신 부분이라 인상 깊습니다.
ReplyListConverter 예외 처리도 꼼꼼하게 되어 있어서 좋았습니다.

리뷰를 보기 편하게 잘 적어주셔서 감사합니다 ^^
성능 비교 결과도 기대됩니다, 좋은 작업 감사합니다!

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.

☀️ Comment: 댓글별 첫 페이지 답글을 JSON 필드로 관리
4 participants