2편에서는 밴드피드(BandFeed) 프로젝트에 필요한 서비스들을 찾아봤었다.
이번 편에서는 각 마이크로 서비스의 구조 구체화 및 도식화, 필요한 API 및 테이블 명세, 인프라 설계도까지 만들어봤다.
애그리거트 경계 설정
먼저 각 마이크로 서비스 내부 애그리거트들 간의 경계를 그림으로 나타내어 보았다.
- MSA 및 애그리거트 경계

다음으로는 각 애그리거트 안에 어떤 엔티티가 포함되는지 나타내어보았다.
- 애그리거트 구분 구체화

각 서비스별로 핵심 엔티티를 나타낸 모습이다.
DB 테이블 명세
MSA 환경이므로 다른 서비스의 ID는 FK를 걸지 않는다.
(예: User 서비스의 user_id를 Band 서비스 DB에서 참조할 때)
1. User 서비스 DB
users
| 컬럼명 | 타입 | 제약조건 | 설명 |
| user_id | VARCHAR(36) | PK | UUID |
| VARCHAR(100) | UNIQUE, NOT NULL | 이메일 | |
| password | VARCHAR(255) | NOT NULL | 암호화된 비밀번호 |
| nick_name | VARCHAR(50) | NOT NULL | 닉네임 |
| profile_image_url | VARCHAR(500) | NULL | 프로필 이미지 URL |
| bio | VARCHAR(255) | NULL | 자기소개 |
| created_at | TIMESTAMP | NOT NULL | 생성일시 |
| updated_at | TIMESTAMP | NOT NULL | 수정일시 |
follows
| 컬럼명 | 타입 | 제약조건 | 설명 |
| follow_id | VARCHAR(36) | PK | UUID |
| from_user_id | VARCHAR(36) | NOT NULL, FK → users | 팔로우한 유저 |
| to_user_id | VARCHAR(36) | NOT NULL, FK → users | 팔로우 당한 유저 |
| created_at | TIMESTAMP | NOT NULL | 팔로우 일시 |
UNIQUE(from_user_id, to_user_id) — 중복 팔로우 방지
2. Band 서비스 DB
bands
| 컬럼명 | 타입 | 제약조건 | 설명 |
| band_id | VARCHAR(36) | PK | UUID |
| band_name | VARCHAR(100) | NOT NULL | 밴드명 |
| cover_image_url | VARCHAR(500) | NULL | 커버 이미지 URL |
| description | VARCHAR(500) | NULL | 밴드 소개 |
| created_at | TIMESTAMP | NOT NULL | 생성일시 |
| updated_at | TIMESTAMP | NOT NULL | 수정일시 |
band_members
| 컬럼명 | 타입 | 제약조건 | 설명 |
| band_member_id | VARCHAR(36) | PK | UUID |
| band_id | VARCHAR(36) | NOT NULL, FK → bands | 소속 밴드 |
| user_id | VARCHAR(36) | NOT NULL | 유저 ID (FK 없음, 타 서비스) |
| position | VARCHAR(50) | NOT NULL | 담당 파트 (GUITAR, BASS, DRUM, KEYBOARD, VOCAL 등) |
| role | VARCHAR(20) | NOT NULL | 역할 (LEADER, MEMBER) |
| created_at | TIMESTAMP | NOT NULL | 가입일시 |
UNIQUE(band_id, user_id) — 같은 밴드에 중복 가입 방지
time_line_posts
| 컬럼명 | 타입 | 제약조건 | 설명 |
| time_line_post_id | VARCHAR(36) | PK | UUID |
| band_id | VARCHAR(36) | NOT NULL, FK → bands | 소속 밴드 |
| writer_member_id | VARCHAR(36) | NOT NULL, FK → band_members | 작성자 |
| title | VARCHAR(200) | NOT NULL | 제목 |
| content | TEXT | NOT NULL | 본문 |
| rehearsal_date | DATE | NULL | 합주 날짜 |
| rehearsal_studio | VARCHAR(100) | NULL | 합주실 이름 |
| rehearsal_location | VARCHAR(200) | NULL | 합주실 위치 |
| video_url | VARCHAR(500) | NULL | 합주 영상 URL |
| created_at | TIMESTAMP | NOT NULL | 생성일시 |
| updated_at | TIMESTAMP | NOT NULL | 수정일시 |
time_line_post_images
| 컬럼명 | 타입 | 제약조건 | 설명 |
| image_id | VARCHAR(36) | PK | UUID |
| time_line_post_id | VARCHAR(36) | NOT NULL, FK → time_line_posts | 소속 포스트 |
| image_url | VARCHAR(500) | NOT NULL | 이미지 URL |
| sort_order | INT | NOT NULL | 이미지 순서 |
time_line_post_comments
| 컬럼명 | 타입 | 제약조건 | 설명 |
| comment_id | VARCHAR(36) | PK | UUID |
| time_line_post_id | VARCHAR(36) | NOT NULL, FK → time_line_posts | 소속 포스트 |
| writer_member_id | VARCHAR(36) | NOT NULL, FK → band_members | 작성자 |
| content | TEXT | NOT NULL | 댓글 내용 |
| created_at | TIMESTAMP | NOT NULL | 작성일시 |
3. Wiki 서비스 DB
songs
| 컬럼명 | 타입 | 제약조건 | 설명 |
| song_id | VARCHAR(36) | PK | UUID |
| spotify_track_id | VARCHAR(100) | UNIQUE, NOT NULL | Spotify 트랙 ID (중복 저장 방지 기준 키) |
| title | VARCHAR(200) | NOT NULL | 곡 제목 |
| artist_name | VARCHAR(100) | NOT NULL | 아티스트명 |
| album_image_url | VARCHAR(500) | NOT NULL | 앨범 커버 이미지 URL (300x300) |
| created_at | TIMESTAMP | NOT NULL | 최초 저장일시 |
posts
| 컬럼명 | 타입 | 제약조건 | 설명 |
| post_id | VARCHAR(36) | PK | UUID |
| song_id | VARCHAR(36) | NOT NULL, FK → songs | 대상 곡 |
| user_id | VARCHAR(36) | NOT NULL | 작성자 ID (FK 없음, 타 서비스) |
| band_id | VARCHAR(36) | NULL | 참여 밴드 ID (FK 없음, 타 서비스) |
| content | TEXT | NOT NULL | 본문 |
| tip | TEXT | NULL | 연주 팁 |
| sheet_music_url | VARCHAR(500) | NULL | 편곡 악보 링크 |
| video_url | VARCHAR(500) | NULL | 유튜브 영상 URL |
| created_at | TIMESTAMP | NOT NULL | 생성일시 |
| updated_at | TIMESTAMP | NOT NULL | 수정일시 |
post_instruments
| 컬럼명 | 타입 | 제약조건 | 설명 |
| post_instrument_id | VARCHAR(36) | PK | UUID |
| post_id | VARCHAR(36) | NOT NULL, FK → posts | 소속 게시글 |
| instrument | VARCHAR(50) | NOT NULL | 악기 (GUITAR, BASS, DRUM, KEYBOARD, VOCAL 등) |
post_images
| 컬럼명 | 타입 | 제약조건 | 설명 |
| image_id | VARCHAR(36) | PK | UUID |
| post_id | VARCHAR(36) | NOT NULL, FK → posts | 소속 게시글 |
| image_url | VARCHAR(500) | NOT NULL | 이미지 URL |
| sort_order | INT | NOT NULL | 이미지 순서 |
4. Chat 서비스 DB
chat_rooms
| 컬럼명 | 타입 | 제약조건 | 설명 |
| chat_room_id | VARCHAR(36) | PK | UUID |
| creator_id | VARCHAR(36) | NOT NULL | 채팅방 개설자 ID (FK 없음, 타 서비스) |
| target_id | VARCHAR(36) | NOT NULL | 상대방 ID (FK 없음, 타 서비스) |
| created_at | TIMESTAMP | NOT NULL | 생성일시 |
UNIQUE(creator_id, target_id) — 동일 유저 간 채팅방 중복 생성 방지
chat_messages
| 컬럼명 | 타입 | 제약조건 | 설명 |
| chat_message_id | VARCHAR(36) | PK | UUID |
| chat_room_id | VARCHAR(36) | NOT NULL, FK → chat_rooms | 소속 채팅방 |
| sender_id | VARCHAR(36) | NOT NULL | 발신자 ID (FK 없음, 타 서비스) |
| content | TEXT | NOT NULL | 메시지 내용 |
| is_read | BOOLEAN | NOT NULL, DEFAULT false | 읽음 여부 |
| created_at | TIMESTAMP | NOT NULL | 전송일시 |
API 명세
1. User 서비스
- /api/users
| Method | Endpoint | 설명 | 인증 | 권한 |
| POST | /api/users/signup | 회원가입 | ✗ | - |
| POST | /api/users/login | 로그인 | ✗ | - |
| POST | /api/users/reissue | 액세스 토큰 재발급 | ✗ | - |
| POST | /api/users/logout | 로그아웃 | ✓ | 본인 |
| GET | /api/users/me | 내 프로필 조회 | ✓ | 본인 |
| PUT | /api/users/me | 내 프로필 수정 | ✓ | 본인 |
| DELETE | /api/users/me | 회원 탈퇴 | ✓ | 본인 |
| GET | /api/users/{userId} | 특정 유저 프로필 조회 | ✗ | - |
- /api/follows
| Method | Endpoint | 설명 | 인증 | 권한 |
| POST | /api/follows/{userId} | 팔로우 | ✓ | 본인 |
| DELETE | /api/follows/{userId} | 언팔로우 | ✓ | 본인 |
| GET | /api/follows/followers/{userId} | 팔로워 목록 조회 | ✗ | - |
| GET | /api/follows/followings/{userId} | 팔로잉 목록 조회 | ✗ | - |
2. Band 서비스
- /api/bands
| Method | Endpoint | 설명 | 인증 | 권한 |
| GET | /api/bands | 내가 속한 밴드 목록 조회 | ✓ | 본인 |
| POST | /api/bands | 밴드 생성 | ✓ | - |
| GET | /api/bands/{bandId} | 밴드 상세 조회 | ✗ | - |
| PUT | /api/bands/{bandId} | 밴드 정보 수정 | ✓ | 리더 |
| DELETE | /api/bands/{bandId} | 밴드 삭제 | ✓ | 리더 |
| POST | /api/bands/{bandId}/invite | 초대 링크 생성 | ✓ | 리더 |
| POST | /api/bands/join | 초대 코드로 밴드 가입 | ✓ | - |
| GET | /api/bands/{bandId}/members | 밴드 멤버 목록 조회 | ✗ | - |
| GET | /api/bands/{bandId}/members/{memberId} | 밴드 멤버 단건 조회 | ✗ | - |
| PUT | /api/bands/{bandId}/members/{memberId} | 멤버 파트/역할 수정 | ✓ | 리더·본인 |
| DELETE | /api/bands/{bandId}/members/me | 밴드 탈퇴 | ✓ | 본인 |
| DELETE | /api/bands/{bandId}/members/{memberId} | 멤버 강퇴 | ✓ | 리더 |
- /api/time-line-posts
| Method | Endpoint | 설명 | 인증 | 권한 |
| POST | /api/time-line-posts | 타임라인 포스트 작성 | ✓ | 밴드 멤버 |
| GET | /api/time-line-posts | 밴드 타임라인 목록 조회 | ✓ | 밴드 멤버 |
| GET | /api/time-line-posts/{timeLinePostId} | 타임라인 포스트 상세 조회 | ✓ | 밴드 멤버 |
| PUT | /api/time-line-posts/{timeLinePostId} | 타임라인 포스트 수정 | ✓ | 작성자 |
| DELETE | /api/time-line-posts/{timeLinePostId} | 타임라인 포스트 삭제 | ✓ | 작성자·리더 |
| POST | /api/time-line-posts/{timeLinePostId}/comments | 댓글 작성 | ✓ | 밴드 멤버 |
| GET | /api/time-line-posts/{timeLinePostId}/comments | 댓글 목록 조회 | ✓ | 밴드 멤버 |
| DELETE | /api/time-line-posts/{timeLinePostId}/comments/{commentId} | 댓글 삭제 | ✓ | 작성자·리더 |
3. Wiki 서비스
- /api/songs
| Method | Endpoint | 설명 | 인증 | 권한 |
| GET | /api/songs/search | 곡 검색 (Spotify 프록시) | ✗ | - |
| POST | /api/songs | 곡 선택 확정 및 저장 (Upsert) | ✓ | - |
| GET | /api/songs/{songId} | 곡 상세 조회 (위키 페이지) | ✗ | - |
- /api/posts
| Method | Endpoint | 설명 | 인증 | 권한 |
| POST | /api/posts | 게시글 작성 | ✓ | - |
| GET | /api/posts | 곡별 게시글 목록 조회 | ✗ | - |
| GET | /api/posts/{postId} | 게시글 상세 조회 | ✗ | - |
| PUT | /api/posts/{postId} | 게시글 수정 | ✓ | 작성자 |
| DELETE | /api/posts/{postId} | 게시글 삭제 | ✓ | 작성자 |
| GET | /api/posts/me | 내 게시글 목록 조회 | ✓ | 본인 |
4. Chat 서비스
- /api/chat-rooms
| Method | Endpoint | 설명 | 인증 | 권한 |
| POST | /api/chat-rooms | 채팅방 생성 (1:1 DM) | ✓ | - |
| GET | /api/chat-rooms | 내 채팅방 목록 조회 | ✓ | 본인 |
- /api/chat-messages
| Method | Endpoint | 설명 | 인증 | 권한 |
| GET | /api/chat-messages | 이전 메시지 조회 | ✓ | 참여자 |
| PATCH | /api/chat-messages/read | 메시지 읽음 처리 | ✓ | 참여자 |
- WebSocket (STOMP)
| 구분 | 값 |
| 연결 URL(배포환경) | ws://api.bandfeed.com/ws/chat |
| 연결 URL(로컬환경) | ws://localhost:8080/ws/chat |
| 구독 경로 | /topic/chat/{chatRoomId} |
| 발행 경로 | /pub/chat/message |
| 인증 | STOMP CONNECT 헤더에 Authorization: Bearer {token} |
인프라 설계도

ECR에 도커 이미지를 저장하고, ECS를 통해 EC2로 가져와서 실행하는 흐름이다.
- 사용자의 요청 흐름
- Public 서브넷 안의 ALB에서 Private 서브넷 안의 EC2의 Spring Cloud Gateway로 요청을 보냄
- 각 서비스로 라우팅을 해주게 된다.
나는 CI/CD를 Github Actions로 구현할 예정이다. 위 아키텍처와 연결 지어 설명하면 아래와 같다.
- CI
- JAR 빌드
- AWS ECR 로그인
- 도커 이미지 빌드
- AWS ECR에 도커이미지 Push
- CD
- ECR에 새 도커 이미지가 들어왔음을 ECS에 알린다.
- ECS -> ECR 도커 이미지 Pull
- EC2안에서 새 도커 이미지로 갈아 끼운다.
'개인 프로젝트' 카테고리의 다른 글
| [Spotify Web API 5편] Docker 및 공통모듈 세팅 (0) | 2026.06.07 |
|---|---|
| [Spotify Web API 4편] MSA 프로젝트 초기 세팅 (0) | 2026.06.07 |
| [Spotify Web API 2편] API 분석 (0) | 2026.06.05 |
| [Spotify Web API 1편] 주제 및 기능 설계 (0) | 2026.06.05 |
| [Riot API 4편] LoL 전적 조회 – Service/Controller 구현 (0) | 2025.10.30 |
