Find My Item
1차 MVP 완료 후 하차 (서비스 운영 중)찾아줘
프로젝트 설명
지도에서 분실물을 등록하고 주변 사람들과 실시간 채팅으로 소통하는 커뮤니티 서비스입니다. 백엔드 Lead로 Spring Boot 3.5 + JPA/QueryDSL 기반 API 설계, AWS EC2 단일 인스턴스에 dev/prod 컨테이너를 docker-compose로 이중화 운영하고, GitHub Actions → ECR → EC2 자동 배포 파이프라인과 Prometheus·Grafana·Loki 모니터링 스택을 구축했습니다. k8s(deployment/service) + ArgoCD GitOps 매니페스트까지 작성해 쿠버네티스 전환 준비를 마쳤습니다. 1차 MVP 출시·운영까지 백엔드를 이끌었고(전체 커밋의 약 54%인 707개, PR 161개 작성), 2차 MVP 전환 시점(2026.06)에 하차했습니다.
- 단일 EC2 인스턴스 위에 dev(8081) / prod(8080) Spring Boot 컨테이너 + Redis + Alloy(log shipper)를 docker-compose로 이중화 — 같은 이미지에 SPRING_PROFILES_ACTIVE만 바꿔 운영/개발 병행
- GitHub Actions CI/CD: develop push → Gradle 빌드 → Docker 이미지 ECR 푸시(커밋 SHA + latest 이중 태깅) → EC2 SSH appleboy/ssh-action으로 docker compose pull/up -d 무중단 배포, concurrency group으로 중복 배포 차단
- 모니터링 스택: Micrometer + Prometheus(9090) 메트릭 수집, Grafana(3000) 대시보드, Grafana Alloy가 컨테이너 로그를 Loki(3100)로 집계 — 15d retention + structured logging (logstash-logback-encoder)
- k8s 매니페스트(Deployment + Service ClusterIP)와 ArgoCD Application(자동 sync + prune + selfHeal + CreateNamespace) 작성 — develop 브랜치 k8s/ 경로를 GitHub에서 선언적으로 추적
- Web Push(VAPID) 직접 구현: nl.martijndwars:web-push + BouncyCastle로 브라우저 푸시 구독/발송, PWA 백그라운드 알림 지원
- 21개 도메인 모듈화(post/chatroom/chatmessage/notification/report/user/auth/admin/map 등) + Flyway V1~V6 마이그레이션(풀텍스트 인덱스, 채팅방 스냅샷 컬럼, 푸시 구독 테이블, 썸네일 URL 컬럼)
- 이미지 썸네일 파이프라인: 업로드 시 썸네일 생성 → S3 저장, 목록 조회는 썸네일 URL 응답(없으면 원본 폴백), 게시물 삭제 시 썸네일 동반 삭제 — 목록 로딩 트래픽 절감
- SSE(SseEmitter) 인앱 실시간 알림 + Web Push 백그라운드 알림 이원화, 회원 탈퇴 시 카카오 연결 끊기·7일 경과 재가입 재활성화 등 계정 라이프사이클 플로우 구현
- Git Flow + PR 리뷰 게이트(review-gate.yml) + PR 리뷰 리마인더 자동화, Conventional Commits 컨벤션 팀 표준화
기술 스택
아키텍처
사용자 (Web / PWA)
│
▼
Vercel — Next.js 15 Frontend
├─ www.finditem.kr (운영, main)
└─ fmi-fe.vercel.app (개발, develop)
│ 지도 / 실시간 채팅(STOMP) / Web Push 구독 / Kakao OAuth
│
▼ HTTPS · WSS
AWS EC2 (ap-northeast-2, ubuntu)
└─ docker-compose.yml
├─ fmi-prod :8080 (SPRING_PROFILES_ACTIVE=prod, ECR latest)
├─ fmi-dev :8081 (SPRING_PROFILES_ACTIVE=dev, ECR latest)
├─ redis :6379 (세션/채팅 pub-sub, AOF persistence)
└─ alloy (Docker 소켓 → 컨테이너 로그 → Loki)
│
├─ AWS RDS MySQL 8 (Flyway V1~V6 마이그레이션)
├─ AWS S3 (fmi-project-s3-bucket, 이미지 업로드)
└─ AWS ECR (fmi-backend:<git-sha> + :latest)
CI/CD — GitHub Actions (.github/workflows/ci-cd.yml)
develop push → ci(Gradle bootJar) → deploy
→ AWS 인증(configure-aws-credentials)
→ ECR login → docker build + push (SHA + latest)
→ appleboy/ssh-action → EC2에서 docker compose pull/up -d
→ docker image prune (이전 이미지 정리)
Monitoring (monitoring/docker-compose.yml, 별도 stack)
Spring Actuator /prometheus ──▶ Prometheus (15d retention)
컨테이너 stdout ──▶ Alloy ──▶ Loki ──▶ Grafana 통합 대시보드
GitOps (k8s/, argocd/ — 전환 준비)
ArgoCD Application → GitHub(k8s/) auto-sync (prune + selfHeal)
├─ Deployment(ghcr.io/find-my-item/fmi-be:latest)
│ readiness /actuator/health/readiness
│ liveness /actuator/health/liveness
└─ Service ClusterIP :80 → :8080AWS EC2 단일 인스턴스 위에 Docker Compose로 Redis + Spring Boot(prod/dev 동시 운영) + Grafana Alloy(로그 수집기)를 오케스트레이션하는 2-tier 아키텍처입니다. GitHub Actions가 develop push를 감지해 Gradle 빌드 → ECR 푸시 → EC2 SSH로 docker compose pull/up 무중단 배포를 수행합니다. Spring Actuator가 노출하는 /prometheus 메트릭을 Prometheus가 스크랩하고, Alloy가 Docker 소켓을 통해 컨테이너 stdout을 Loki로 전달해 Grafana 단일 대시보드에서 메트릭·로그를 통합 조회합니다. Kubernetes Deployment/Service 매니페스트와 ArgoCD Application(auto-sync + selfHeal + prune)도 리포지토리에 포함해, 트래픽 증가 시 k8s로 즉시 GitOps 전환할 수 있도록 준비해두었습니다.
주요 기능
지도 기반 분실물 등록/검색
분실·습득 위치를 지도 핀으로 등록하고, 반경 기반으로 주변 게시물을 탐색. QueryDSL로 복합 필터(기간/카테고리/지역) 동적 쿼리 구성
WebSocket 실시간 채팅
Spring WebSocket(STOMP) + Redis pub/sub으로 분실물 등록자와 발견자 간 1:1 채팅. wss://www.finditem.kr/ws 엔드포인트로 프로덕션 트래픽 처리
SSE + Web Push 알림 이원화
앱 사용 중에는 SSE(SseEmitter) 스트림으로 인앱 실시간 알림, 백그라운드에서는 Web Push(VAPID)로 푸시. nl.martijndwars:web-push + BouncyCastle로 VAPID 키페어 발급/서명, 알림 생성 시 Web Push 발송 자동 연동
이미지 썸네일 파이프라인
이미지 업로드 시 썸네일을 생성해 S3에 원본과 함께 저장. 목록 조회 응답은 썸네일 URL 우선(없으면 원본 폴백), 게시물 삭제 시 썸네일까지 정리해 스토리지·목록 로딩 트래픽 절감
회원 탈퇴·재가입 플로우
탈퇴 시 카카오 연결 끊기(Admin Key) 처리, 7일 경과 탈퇴 유저 재가입 시 계정 재활성화, 약관 동의 API(PATCH /users/me/terms) 분리, 심사용 테스트 계정 재가입 제한 면제
dev/prod 컨테이너 이중화
같은 EC2 인스턴스에 동일 ECR 이미지로 fmi-prod(8080) + fmi-dev(8081) 컨테이너를 동시 기동. SPRING_PROFILES_ACTIVE와 DB 커넥션만 분기해 운영 영향 없이 dev 실환경 검증
GitHub Actions → ECR → EC2 CI/CD
develop push 시 Gradle 빌드 → Docker 이미지를 커밋 SHA + latest 이중 태깅으로 ECR 푸시 → EC2 SSH로 docker compose pull/up -d 수행. concurrency group으로 동시 배포 차단, 25분 타임아웃
Prometheus + Grafana + Loki 모니터링
Micrometer로 Spring Actuator /prometheus 메트릭 노출, Prometheus(15d retention)가 스크랩. Grafana Alloy가 Docker 로그를 Loki로 전달해 Grafana 단일 대시보드에서 메트릭·로그 통합 조회
k8s + ArgoCD GitOps 준비
Deployment(readiness/liveness probe + replicas) + Service(ClusterIP) 매니페스트와 ArgoCD Application(auto-sync + prune + selfHeal)을 리포지토리에 포함. GitHub k8s/ 경로 변경 시 즉시 선언적 배포 전환 가능
Flyway 스키마 마이그레이션
V1__init → V6__add_post_image_thumbnail_url까지 6단계 버전 관리. 풀텍스트 인덱스, 게시물 soft delete, 채팅방 스냅샷 컬럼, 푸시 구독 테이블, 썸네일 URL 컬럼을 순차 적용. baseline-on-migrate로 기존 DB 호환
Kakao OAuth 3-환경 분리
dev / release / prod 각각의 Kakao REST API Key + Redirect URI를 GitHub Secrets로 주입해 환경별 OAuth 앱 분리. JWT + HttpOnly 쿠키 세션, 요청 Origin 기준 쿠키 Domain 동적 설정으로 서브도메인(릴리즈 환경) 대응
21개 도메인 모듈 구조
post / chatroom / chatmessage / notification / report / inquiry / user / auth / admin / ipblock / userblock / notice / comment 등 기능별 도메인 패키지 분리. 도메인 내부에서 controller/service/repository/dto 계층화
보안 · 운영 기능
JWT 기반 인증 + Spring Security 필터, IP 차단(ipblock) · 유저 차단(userblock) · 금칙어 필터(banned-words.txt), Slack Webhook으로 문의·신고 실시간 알림
Git Flow + 리뷰 게이트
develop 기반 feature/#N 브랜치 전략, main/develop 직접 push 금지, PR 리뷰 게이트 워크플로우(review-gate.yml) + 리뷰 미완료 PR 리마인더 자동 발송
Storybook + Playwright E2E
FE에서 Storybook으로 컴포넌트 문서화(Chromatic 호스팅), Playwright로 핵심 사용자 시나리오(등록/검색/채팅) 자동화 테스트
기능

