Skip to content

Conversation

impmonzz
Copy link
Collaborator

@impmonzz impmonzz commented Mar 16, 2025

요구사항

기본

  • 데이터베이스 환경 설정 (PostgreSQL)
  • ERD 기반 DDL 작성 및 테이블 생성 (schema.sql 포함)
  • application.yaml 설정 작성 (DB 연결, SQL 로그 등)
  • 도메인 모델 공통 속성 추상 클래스 정의 및 상속 구현
  • @CreatedDate, @LastModifiedDate를 통한 자동 시간 처리
  • 연관관계 매핑 및 JPA 어노테이션 적용
  • 영속성 전이와 고아 객체 설정
  • Spring Data JPA 적용 및 기존 Repository 수정
  • DTO와 Mapper 도입으로 Entity 노출 방지
  • BinaryContentStorage 인터페이스 및 로컬 저장소 구현
  • 파일 다운로드 API 추가
  • 페이징 및 정렬 처리 (Page/Slice 활용)

심화

  • BinaryContentStorage의 로컬 저장 방식 Bean 등록 조건 처리
  • 페이징 응답을 위한 제네릭 DTO 및 Mapper 구현

주요 변경사항

  • schema.sql을 작성하여 ERD 기반 테이블 생성
  • 공통 엔티티 BaseEntity를 통해 createdAt, updatedAt 필드 자동 처리
  • 연관관계 및 외래키 제약조건에 맞는 JPA 매핑 구현
  • 기존 Repository를 JpaRepository로 대체하고 서비스 레이어 수정
  • Entity → DTO 변환을 위한 UserMapper, ChannelMapper 등 생성
  • BinaryContentStorage 인터페이스 설계 및 LocalDisk 구현체 작성
  • 파일 다운로드 API 추가 및 파일 저장 방식 local 조건 처리
  • PageablePage<T> 기반 메시지 목록 조회 기능 구현

엔티티 관계

관계 (Entity) 다중성 방향성 부모-자식 관계 연관관계의 주인
User ↔ UserStatus 1:1 양방향 부모: User, 자식: UserStatus UserStatus
Message → User 1:N Message → User (단방향) 부모: User, 자식: Message Message
User → BinaryContent 1:1 User → BinaryContent (단방향) 부모: User, 자식: BinaryContent User
ReadStatus → User N:1 ReadStatus → User (단방향) 부모: User, 자식: ReadStatus ReadStatus
ReadStatus → Channel N:1 ReadStatus → Channel (단방향) 부모: Channel, 자식: ReadStatus ReadStatus
Message → Channel N:1 Message → Channel (단방향) 부모: Channel, 자식: Message Message
Message → BinaryContent 1:N Message → BinaryContent (단방향) 부모: Message, 자식: BinaryContent Message


멘토에게

  • git 충돌이나서 pr을 2번올렸습니다 죄송합니다

@impmonzz impmonzz changed the title sprint6 [이규석]sprint6 Mar 16, 2025
@impmonzz impmonzz requested a review from youngxpepp March 16, 2025 14:50
Copy link
Collaborator

@youngxpepp youngxpepp left a comment

Choose a reason for hiding this comment

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

안녕하세요 규석님.
과제하는데 시간이 오래 걸렸을거라 예상이 됩니다. 구현이 누락된 요구사항이 눈에 들어오네요😭
그래도 controller-service-repository 계층 분리와 각 계층의 역할은 명확하게 구현해주신듯 합니다.

이번 과제의 핵심이라고 할 수 있었던 페이징과 N+1 쿼리에 대해선 꼭 다시 구현해보시길 바랍니다.
궁금하시다면 아래 내용 참고해도 좋아요!

https://jojoldu.tistory.com/165
https://jojoldu.tistory.com/528
https://jojoldu.tistory.com/529
https://jojoldu.tistory.com/530
https://jojoldu.tistory.com/531

sql:
init:
mode: always # 스키마 초기화 실행
schema-locations: classpath:schema.sql # schema.sql 파일 실행
Copy link
Collaborator

Choose a reason for hiding this comment

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

기본값이 classpath:schema.sql 인 것 아실까요~~?
그래도 명시적으로 선언해주시면 설정을 보는 사람이 이해하기 쉬울듯 합니다 👍

Comment on lines +28 to +29
@OneToMany(mappedBy = "channel", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Message> messages = new ArrayList<>();
Copy link
Collaborator

Choose a reason for hiding this comment

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

해당 내용은 과제 요구사항에 없긴 한데요!
실제 디스코드 서버라고 한다면 채널에 무수히 많은 메시지가 있을거에요.
이를 페이징 없이 lazy loading 한다고 하면 오랜 수행시간이 필요할겁니다!

Comment on lines +30 to +32
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "message_id")
private List<BinaryContent> attachments = new ArrayList<>();
Copy link
Collaborator

Choose a reason for hiding this comment

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

BinaryContent 와의 연관관계는 다시 확인해보시는게 좋겠습니다!
Message와 BinaryContent는 ManyToMany 관계거든요.
그래서 중간에 매핑 테이블이 필요합니다. 따라서 매핑 엔티티를 만들어서 OneToMany로 참조하거나
아니면 JoinTable 어노테이션을 통해 매핑 엔티티 없이 BinaryContent를 OneToMany 참조하는 방법이 있겠습니다.

Comment on lines +6 to +8
public interface MessageRepository extends JpaRepository<Message, UUID> {
List<Message> findAllByChannelId(UUID channelId);
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

페이징 처리에 대한 요구사항 구현이 안되어 있네요ㅠㅠ
시간이 되시면 꼭 구현해보시길 바래요. 이번 과제에 중요한 내용 중 하나입니다!

Comment on lines +73 to +79
public ResponseEntity<List<Message>> findAllByChannelId(
@RequestParam("channelId") UUID channelId) {
List<Message> messages = messageService.findAllByChannelId(channelId);
return ResponseEntity
.status(HttpStatus.OK)
.body(messages);
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

API에선 DTO를 사용자에게 반환하도록 하면 어때요?
API의 변경사항이 엔티티에 영향을 주지 않도록, 그리고 엔티티의 영향이 API에 영향을 끼치지 않도록 하기 위해서 DTO와 엔티티 분리는 중요해요!

return ResponseEntity.ok()
.contentType(mediaType)
.contentLength(contentLength)
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + fileName + "\"")
Copy link
Collaborator

Choose a reason for hiding this comment

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

ContentDisposition 이라는 클래스를 사용하면 헤더를 쉽게 만들 수 있습니다!

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.

2 participants