Skip to content

[김하은] Sprint6#190

Open
shong9124 wants to merge 36 commits intocodeit-bootcamp-spring:김하은from
shong9124:sprint6
Open

[김하은] Sprint6#190
shong9124 wants to merge 36 commits intocodeit-bootcamp-spring:김하은from
shong9124:sprint6

Conversation

@shong9124
Copy link
Copy Markdown
Collaborator

@shong9124 shong9124 commented Mar 10, 2026

✅ 기본 요구사항

API 명세

  • 제공된 API 스펙 v1.1을 기준으로 API 구현
  • 구현한 API와 제공된 API 스펙 비교
  • oasdiff 등을 활용하여 API 스펙 차이 분석
  • 제공된 프론트엔드 코드와 호환되도록 API 구현

🗄 데이터베이스

데이터베이스 설정

  • 데이터베이스 환경 설정

    • Database: discodeit
    • User: discodeit_user
    • Password: discodeit1234
  • ERD를 기반으로 DDL 작성

  • 테이블 생성

  • 작성한 DDL 파일을 src/main/resources/schema.sql 경로에 포함


⚙️ Spring Data JPA 적용

  • Spring Data JPA 의존성 추가
  • PostgreSQL 의존성 추가
  • application.yaml에 DB 연결 설정 작성
  • SQL 로그 확인을 위한 설정 추가

🧩 엔티티 정의

공통 엔티티 추상 클래스

  • 도메인 공통 속성을 추상 클래스로 정의
  • 상속 구조 구현

패키지
com.sprint.mission.discodeit.entity.base

  • Serializable 인터페이스 제외

Auditing 적용

  • createdAt 자동 생성
  • updatedAt 자동 수정

사용 어노테이션

  • @CreatedDate
  • @LastModifiedDate

도메인 참조 관계 수정

  • 클래스 다이어그램 기준으로 참조 관계 수정
  • 생성자 및 update 메소드 수정

연관관계 매핑 정리

PR 첨부 내용

엔티티 관계 다중성 방향성 부모-자식 관계 연관관계의 주인
User - UserStatus 1 : 1 양방향 User (부모) - UserStatus (자식) UserStatus
User - BinaryContent (profile) 1 : 0..1 단방향 BinaryContent (부모) - User (자식) User
User - Message (author) 1 : N 단방향 User (부모) - Message (자식) Message
Channel - Message 1 : N 단방향 Channel (부모) - Message (자식) Message
User - ReadStatus 1 : N 단방향 User (부모) - ReadStatus (자식) ReadStatus
Channel - ReadStatus 1 : N 단방향 Channel (부모) - ReadStatus (자식) ReadStatus
Message - BinaryContent (attachments) N : N 단방향 Message (부모) - MessageAttachment (자식) MessageAttachment

다음 기준으로 정리

  • 엔티티 관계
  • 다중성
  • 방향성
  • 부모 / 자식 관계
  • 연관관계의 주인

JPA 연관관계 매핑

다음 어노테이션을 활용하여 ERD 반영


영속성 전이 및 고아 객체 설정

  • cascade 설정
  • orphanRemoval 설정

🗃 Repository와 Service에 JPA 도입

  • 기존 Repository 인터페이스를 JpaRepository 기반으로 변경
  • Query Method 활용
  • FileRepository 제거
  • JCFRepository 제거

서비스 레이어 수정

영속성 컨텍스트 특성 반영

  • 트랜잭션 적용
  • 변경 감지 활용
  • 영속성 전이 활용
  • 지연 로딩 고려

📦 DTO 적극 도입

Entity를 Controller에 직접 노출했을 때 문제점

PR 첨부 내용

  • Entity와 API가 강하게 결합되며 Entity 수정시 API도 수정해야한다는 문제점이 있음
  • Entity의 구조가 노출되기 때문에 민감한 정보(password 등)이 노출 될 수 있음
  • 서비스 계층에서 트랜잭션이 종료된 이후 Controller에서 Entity의 연관 객체를 접근하게 되면 LazyInitializationException이 발생할 수 있음
    -> DTO를 사용하면 필요한 데이터만 서비스 계층에서 조회하여 전달할 수 있어 이러한 문제를 예방 가능
  • 양방향 연관관계에서 순환 참조 문제가 발생할 수 있음

DTO 정의

  • 클래스 다이어그램 기반 DTO 정의

Mapper 컴포넌트 구현

  • Entity → DTO 매핑 로직 구현
  • 반복 코드 감소

패키지
com.sprint.mission.discodeit.mapper


📁 BinaryContent 저장 로직 고도화

BinaryContent 엔티티 수정

  • bytes 필드 제거
  • 메타 정보만 저장
    • fileName
    • size
    • contentType

BinaryContentStorage 인터페이스 설계

패키지
com.sprint.mission.discodeit.storage

역할

  • 바이너리 데이터 저장
  • 바이너리 데이터 조회
  • 다운로드 API 응답 생성

서비스 로직 리팩토링

  • 기존 BinaryContent 저장 로직 제거
  • BinaryContentStorage 활용

BinaryContent 다운로드 API

엔드포인트

GET /api/binaryContents/{binaryContentId}/download

  • BinaryContentController에 다운로드 API 추가
  • BinaryContentStorage에 로직 위임

로컬 스토리지 구현

  • LocalBinaryContentStorage 구현
  • 로컬 디스크 기반 파일 저장

Bean 등록 조건

discodeit.storage.type=local


스토리지 설정

설정값

discodeit.storage.local.root-path

구현 내용

  • 루트 디렉토리 초기화
  • 파일 저장 경로 규칙 정의
  • 파일 저장
  • 파일 조회
  • 파일 다운로드 응답 생성

📄 페이징과 정렬

메시지 목록 조회

  • 50개 단위 조회
  • 최신 메시지 순 정렬
  • 전체 메시지 수 조회 생략

PageResponse DTO

패키지
com.sprint.mission.discodeit.dto.response

구성

  • content
  • number
  • size
  • totalElements

PageResponse Mapper

패키지
com.sprint.mission.discodeit.mapper

  • Slice 또는 Page → DTO 변환
  • 제네릭 메소드 활용

🚀 심화 요구사항

N+1 문제 해결

  • N+1 문제가 발생하는 쿼리 식별
  • Fetch 전략 또는 EntityGraph 등을 활용하여 해결

읽기 전용 트랜잭션 활용

설정

spring.jpa.open-in-view=false

  • OSIV 비활성화
  • 조회 서비스에 readOnly 트랜잭션 적용

페이지네이션 최적화

Offset vs Cursor Pagination

PR 첨부 내용

Offset Pagination

Offset Pagination은 페이지 번호와 페이지 크기를 기반으로 데이터를 조회하는 방식으로
page=2, size=50과 같이 요청하면 데이터베이스에서는 offset과 limit을 활용하여 데이터를 조회함

특징

  • 구현이 비교적 단순하다
  • 특정 페이지로 바로 이동할 수 있다
  • 데이터가 많아질수록 offset 값이 커져 성능이 저하될 수 있다

offset이 매우 커질 경우 데이터베이스는 많은 데이터를 스캔한 뒤 일부만 반환하게 되므로 성능 문제가 발생할 수 있음

Cursor Pagination

Cursor Pagination은 마지막으로 조회한 데이터의 기준 값(cursor)을 기반으로 다음 데이터를 조회하는 방식으로
예를 들자면, 마지막으로 조회한 메시지의 createdAt이나 id 값을 기준으로 다음 데이터를 조회함

특징

  • 대용량 데이터 환경에서 성능이 안정적이다
  • 무한 스크롤과 같은 UX에 적합하다
  • 특정 페이지로 바로 이동하기는 어렵다

Cursor Pagination은 이전 페이지의 마지막 데이터를 기준으로 조회하기 때문에 offset 방식처럼 불필요한 데이터를 스캔하지 않아 성능상 이점이 있음

정리 항목

  • Offset Pagination 특징
  • Cursor Pagination 특징
  • 성능 차이
  • 사용 사례

커서 기반 페이지네이션 적용

  • 기존 오프셋 페이지네이션 제거
  • 커서 기반 페이지네이션 구현
  • API 스펙 v1.2 준수

🧰 MapStruct 적용

  • Entity와 DTO 매핑 로직을 MapStruct로 리팩토링
  • 보일러플레이트 코드 감소

💻 실행 결과

image

@shong9124 shong9124 requested a review from jaeyeon518 March 10, 2026 02:49
@shong9124 shong9124 self-assigned this Mar 10, 2026
@shong9124 shong9124 added the 매운맛🔥 뒤는 없습니다. 그냥 필터 없이 말해주세요. 책임은 제가 집니다. label Mar 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

매운맛🔥 뒤는 없습니다. 그냥 필터 없이 말해주세요. 책임은 제가 집니다.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant