5편에서는 구현전에 해놔야 하는 것들을 했다.
- 각 서비스의 Dockerfile
- docker-compose.yml
- 공통에러, 응답 처리
- 모든 서비스 연동 확인
- 컨피그 Repo에 설정 파일 추가
내일부터는 밴드 서비스 -> 위키 서비스 -> 게이트웨이 및 유저 서비스 -> 챗 서비스 -> CI 구현 -> 배포 -> 모니터링 -> 프런트 개발 순서로 진행할 것이다.
아래 토글(더보기)은 개발 체크리스트이다.
BandFeed 개발 체크리스트
0단계 — 사전 준비
- [x] API / 테이블 / 인프라 설계서 작성
- [x] GitHub 레포 생성 (BandFeed, BandFeed-config)
- [x] 모노레포 멀티모듈 구성 (Gradle)
- [x] Config 레포 생성 및 Config Server 연동
- [x] .gitignore / .env 설정
- [x] Docker Compose 구성 (MySQL, Kafka, Redis, 모니터링 profile)
- [x] 각 서비스 Dockerfile 작성 (7개)
1단계 — MSA 인프라 및 핵심 비즈니스 구축 (Base MVP)
인프라
- [x] Eureka Server 구축 및 동작 확인
- [x] Config Server 구축 및 각 서비스 yml 연동
- [x] Gateway Service 생성 및 유레카 등록
- [x] 공통 모듈 구성
- [x] BaseEntity (Auditing)
- [x] ErrorCode 인터페이스
- [x] BusinessException
- [x] CommonErrorCode
- [x] CommonResponse<T>
- [x] GlobalExceptionHandler
서비스 생성 및 유레카 등록
- [x] User Service
- [x] Band Service
- [x] Wiki Service (Post/Song)
- [x] Chat Service
Band Service — 핵심 기능 구현
- [ ] 도메인 설계 (Entity, Repository)
- [ ] Spotify API 연동
- [ ] Band CRUD API
Wiki Service — 핵심 기능 구현
- [ ] 도메인 설계 (Entity, Repository)
- [ ] Post/Song 위키형 게시판 CRUD API (인증 없이 우선 개발)
- [ ] Resilience4j 서킷 브레이커 적용
- [ ] @Retryable 재시도 로직 적용 및 검증
2단계 — 게이트웨이 라우팅/보안 및 커뮤니티 확장 (Feature MVP)
Gateway 라우팅 및 인증
- [ ] 각 서비스로의 라우팅(Routing) 설정
- [ ] User Service 로그인/회원가입 구현
- [ ] Gateway에서 JWT 토큰 발급·검증 흐름 완성
- [ ] X-User-Id 헤더 전달 필터 구현
인증 기반 기능
- [ ] BandMember 매핑 구현
- [ ] 프라이빗 타임라인 (접근 제어) 구현
Chat Service
- [ ] WebSocket + STOMP 개념 학습
- [ ] 1:1 DM 기능 구현
- [ ] Kafka 연동 (메시지 이벤트)
기타
- [ ] FeignClient 서비스 간 통신 구현
- [ ] 이벤트 기반 로직 구현 (Kafka)
- [ ] 통합 시나리오 테스트용 .http 파일 작성
3단계 — 인프라 고도화 및 아키텍처 검증 (Architecture MVP)
배포
- [ ] GitHub Actions CI 파이프라인 구축
- [ ] Docker 기반 AWS 멀티 컨테이너 CD 구현
- [ ] 배포 환경 동작 확인
모니터링
- [ ] Actuator + Prometheus 엔드포인트 설정
- [ ] Loki + Promtail 로그 수집 설정
- [ ] Grafana 대시보드 구성
- [ ] Alertmanager Slack 알림 설정
성능 검증
- [ ] JMeter / k6 부하테스트 환경 구성
- [ ] 외부 API 지연 시 서킷 브레이커 Fallback 검증
- [ ] Redis 캐싱 적용 전후 TPS 비교
프런트엔드
- [ ] 프론트 개발
- [ ] 프론트 배포
- [ ] 프론트로 동작 테스트
- [ ] 버그 수정
마무리
- [ ] 실사용자 배포
- [ ] 피드백 수집 및 반영
오늘 배운 것
1. 도커 컴포즈 구성시, 변수처럼 묶어서 설정을 적용할 수도 있다.
x-java-tz: &java-tz
TZ: Asia/Seoul
JAVA_TOOL_OPTIONS: -Duser.timezone=Asia/Seoul
x-common-service: &common-service
depends_on:
eureka-server:
condition: service_healthy
config-server:
condition: service_healthy
mysql:
condition: service_healthy
networks:
- bandfeed-network
band-service:
build:
context: .
dockerfile: apps/band-service/Dockerfile
container_name: bandfeed-band
ports:
- "${BAND_SERVER_PORT}:9020"
environment:
<<: *java-tz
SERVER_PORT: 9020
SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/${MYSQL_BAND_DB}?useSSL=false&serverTimezone=Asia/Seoul&allowPublicKeyRetrieval=true
SPRING_DATASOURCE_USERNAME: ${MYSQL_USER}
SPRING_DATASOURCE_PASSWORD: ${MYSQL_PASSWORD}
EUREKA_CLIENT_SERVICEURL_DEFAULTZONE: http://eureka-server:8761/eureka/
CONFIG_SERVER_URL: ${CONFIG_SERVER_URL}
<<: *common-service
- 모든 서비스의 공통으로 적용되는 타임존 설정
- 핵심 비즈니스 로직을 처리하는 마이크로 서비스에 적용하는 depends-on 설정
2. 도커 컴포즈 구성 시, 프로필 설정으로 원하는 서비스만 실행
prometheus:
image: prom/prometheus:v2.53.0
container_name: bandfeed-prometheus
profiles: ["monitoring"]
environment:
- TZ=Asia/Seoul
ports:
- "${PROMETHEUS_PORT}:9090"
volumes:
- ./monitoring/prometheus/prometheus.yaml:/etc/prometheus/prometheus.yaml
- ./monitoring/prometheus/alert-rules.yaml:/etc/prometheus/alert-rules.yaml
- prometheus-data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yaml'
- '--storage.tsdb.retention.time=14d'
restart: unless-stopped
networks:
- bandfeed-network
- profiles: ["프로필명"]을 추가했다.
docker-compose up -d
docker-compose --profile monitoring up -d
모니터링 서비스는 내가 설계한 개발 순서상, 나중에 사용하기에
지금 사용할 서비스만 도커 컴포즈로 실행할 수 있도록 했다.
이후 모니터링 관련 이미지도 포함하여 실행하고 싶다면 --profile monitoring을 추가하면 된다.
3. 컨피그 Repo에 있어야 하는 설정과 각 서비스에서 가지고 있어야 할 설정
# 각 서비스가 가져야할 설정들
spring:
application:
name: band-service # Config server가 이 이름으로 파일을 찾음
config:
import: optional:configserver:http://localhost:8888 # Config server 연결
server:
port: 9020 # 로컬 실행 시 fallback 포트
# 컨피그 Repo에 넣어야 할 설정들
spring:
datasource: # DB 접속 정보
url: jdbc:mysql://mysql:3306/band_db
username: bandfeed
password: secret
jpa:
hibernate:
ddl-auto: update
# 서비스 디스커버리
eureka:
client:
service-url:
defaultZone: http://eureka-server:8761/eureka/
# 외부 서비스 URL, 각종 키, feature flag 등
DB 정보같이 각 서비스별로 달라지는 설정들은 컨피그 Repo에서 관리한다.
4. 모니터링 개념 및 방식
각 서비스(Spring Boot)
↓ /actuator/prometheus (메트릭)
Prometheus (수집)
↓
Grafana (시각화)
각 서비스(로그)
↓ Docker 컨테이너 로그
Promtail (수집)
↓
Loki (저장)
↓
Grafana (시각화)
5. 공통 에러 처리

- ErrorCode 객체를 인터페이스로 선언.
- BusinessException에서 ErrorCode를 생성자 주입받음.
- 각 서비스에서 enum 객체를 생성 후 ErrorCode를 구현.
- 예외 발생 시, GlobalExceptionHandler에서 가로채어 예외 반환.
- 예외 반환 시, CommonResponse로 형식이 통일되어 반환됨.
6. 공통 모듈 자체의 jar 파일 생성 문제
문제 상황
Gradle> bandfeed> Tasks> build> build 중 문제 발생!
- 에러 로그
Error while evaluating property 'mainClass' of task ':apps:bootJar'.
> Main class name has not been configured and it could not be resolved
문제는 apps 모듈의 jar파일을 생성하는데, main 클래스가 없다는 점이었다.
분석
- 루트 폴더의 build.gradle의 일부분
// 루트 모듈을 제외한 하위 프로젝트 공통 설정
subprojects {
apply plugin: 'java'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
tasks.named('test') {
useJUnitPlatform()
}
}
루트 폴더 하위의 모듈들에게 공통 의존성을 추가하는 부분이다.
근데 setting.gradle 설정 때문에 apps 자체도 모듈이 된다.
rootProject.name = 'bandfeed'
include 'common'
include 'apps:config-server'
include 'apps:eureka-server'
include 'apps:gateway-service'
include 'apps:band-service'
include 'apps:user-service'
include 'apps:wiki-service'
include 'apps:chat-service'
apps:라고 prefix를 둔 순간 gradle이 apps라는 중간 부모 모듈을 만든다.
조치 및 해결
- apps 모듈의 자체 jar 파일 생성기능을 비활성화
plugins {
id 'java'
id 'org.springframework.boot' version '3.5.14'
id 'io.spring.dependency-management' version '1.1.7'
}
// root project 실행 가능 Jar 파일 생성기능 Off
bootJar.enabled = false
// subprojects 설정(루트 프로젝트 하위 모듈 서비스들 공통 설정)
// 모듈의 기본 jar 파일 생성기능 off
project(':common') {
bootJar.enabled = false
jar.enabled = true
}
project(':apps') {
bootJar.enabled = false
jar.enabled = false
}
// 나머지 서비스들 플러그인 적용(gradle 감지)
subprojects에 대상으로 포함되어도 jar파일을 생성하지 않게 함으로써 에러를 해결했다.
'개인 프로젝트' 카테고리의 다른 글
| [Spotify Web API 7편] 배포 방법 구상 (0) | 2026.06.15 |
|---|---|
| [Spotify Web API 6편] Band 서비스 구현 (0) | 2026.06.10 |
| [Spotify Web API 4편] MSA 프로젝트 초기 세팅 (0) | 2026.06.07 |
| [Spotify Web API 3편] 애그리거트 경계 설정 및 SA 문서 작성 (0) | 2026.06.06 |
| [Spotify Web API 2편] API 분석 (0) | 2026.06.05 |
