<aside> 📌
게시물 · 미디어 시스템은 밴드가 생성한 콘텐추의 라이프사이클과 파일 저장 인프라의 기반을 관리합니다. 밴드는 텍스트, 이미지, 동영상으로 업데이트를 공유할 수 있으며, 팬은 좋아요와 스레드형 댓글로 상호작용합니다.
| Post Type | 미디어 제약 | 용도 |
|---|---|---|
| TEXT | 0 URLs | 순수 텍스트 업데이트입니다. |
| PHOTO | 1개 이상의 URL | 이미지 갤러리입니다. |
| VIDEO | 1개 URL | 선택적 썸네일을 가진 단일 동영상 파일입니다. |
MediaService는 Presigned URL을 통한 S3 업로드를 관리하며 POST, POST_THUMBNAIL, USER_PROFILE, BAND_PROFILE, SESSION_PORTFOLIO 카테고리를 지원합니다.
| Entity | 설명 | 관계 |
|---|---|---|
| Post | 핵심 메타데이터(제목, 설명, 타입, 썸네일)입니다. | aggregate root |
| PostMedia | sortOrder를 가진 이미지 또는 동영상 URL입니다. | One-to-Many (Post) |
| PostTag | 게시물당 최대 8개의 분류 문자열입니다. | One-to-Many (Post) |
| PostLike | 특정 게시물에 대한 사용자 좋아요를 추적합니다. | One-to-Many (Post) |
| PostComment | Fan과 Band 정체성을 모두 지원하는 사용자 댓글입니다. | One-to-Many (Post) |
PostRepository는 게시물 ID를 커서로 사용하는 커서 기반 페이지네이션을 구현하며, id < :cursor 로직을 이용한 인덱스 range seek 쿼리를 수행하여 대규모 피드를 효율적으로 처리합니다.
PostCommentListResponse는 댓글을 두 그룹으로 분리합니다. myComments는 현재 사용자 본인의 댓글로 상단에 표시되며, items는 차단된 사용자를 제외하고 필터링된 다른 사용자들의 댓글입니다.
| Port | Adapter | 목적 |
|---|---|---|
| FollowPort | FollowAdapter (Band 도메인) | 푸시 알림 배포 대상 팔로워 ID를 조회합니다. |
| UserPort | UserAdapter | Fan 프로필을 조회하고 댓글 노출 필터링을 위한 사용자 차단 목록을 가져옵니다. |
| BandPort | BandAdapter | 밴드 멤버십 상태를 검증하고 밴드 프로필 식별자를 조회합니다. |
| NotifyPort | NotificationAdapter | 사용자에게 FCM 및 인앱 알림을 발송합니다. |
| PostPort | SearchAdapter | Elasticsearch 문서 색인에 필요한 게시물 엔티티 데이터를 제공합니다. |
게시물이 생성되면 notifyFollowersAfterCommit이 호출되며, TransactionSynchronizationManager를 사용하여 데이터베이스 트랜잭션이 성공적으로 완료된 경우에만 알림이 발송되도록 보장합니다. 모든 CRUD 연산은 PostChangedEvent를 트리거하며, SearchIndexEventListener가 이를 구독하여 SearchAdapter를 호출해 Elasticsearch 색인을 갱신합니다.
BScene 플랫폼은 서버 처리 부하와 네트워크 지연을 줄이기 위해 클라이언트 측 업로드 전략을 구현합니다. MediaService는 시간 제한이 있는 암호학적으로 안전한 S3 Presigned URL을 생성하는 중앙 오케스트레이터 역할을 합니다.
| Category | 용도 | S3 Prefix |
|---|---|---|
| POST | 밴드 게시물에 첨부되는 일반 이미지/동영상입니다. | post/ |
| POST_THUMBNAIL | 자동 생성된 동영상 프레임입니다. | post_thumbnail/ |
| STREAM_THUMBNAIL | 라이브 스트림 미리보기 이미지입니다. | stream_thumbnail/ |
| BAND_PROFILE | 밴드 로고 및 커버 사진입니다. | band_profile/ |
| PERFORMANCE_POSTER | 공연 및 이벤트 포스터입니다. | performance_poster/ |
| USER_PROFILE | 기본 사용자 계정 아바타입니다. | user_profile/ |
| SESSION_PORTFOLIO | 세션 지원용 PDF 또는 미디어입니다. | session_portfolio/ |
클라이언트가 URL을 요청하면 시스템은 파일명 충돌을 방지하기 위해 UUID로 고유 식별자를 생성하며, Presigned URL은 보안 제약을 강제하기 위해 5분의 짧은 만료 시간을 사용합니다.
sequenceDiagram
participant Post as "PostService"
participant Event as "PostVideoThumbnailRequestedEvent"
participant Thumb as "PostThumbnailService"
participant S3
participant FFmpeg as "ffmpeg (ProcessBuilder)"
Post->>Event: "커밋 후 이벤트 발행"
Event->>Thumb: "@Async(postThumbnailExecutor) handle"
Thumb->>S3: "원본 동영상 다운로드"
Thumb->>FFmpeg: "-frames:v 1 로 첫 프레임 추출"
Thumb->>S3: "POST_THUMBNAIL 카테고리로 .jpg 업로드"
Thumb->>Post: "applyGeneratedThumbnail로 URL 갱신"
시스템은 presignedUrl(임시 업로드 권한)과 fileUrl(영구 공개 위치)를 구분합니다. PostCreateRequest는 mediaUrls 목록과 선택적 thumbnailUrl을 받으며, PostSummaryResponse는 thumbnailUrl이 없는 경우 첫 번째 미디어 항목을 기본값으로 사용하여 목록 화면에서 시각 요소가 항상 표시되도록 보장합니다.