Skip to content

Sodychoe/backend

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

💡서비스/프로젝트 소개

프로젝트 이미지

All in One 통합 러닝 서비스입니다. 러닝 기록 관리, 크루 활동, 대회 신청 등 러닝과 관련된 서비스들을 안정적으로 제공합니다.

🎯서비스/프로젝트 목표

대규모 트레픽 대응

  • MSA 구조로 장애 대응 및 확장성 확보
  • Kafka를 이용해 고성능과 고가용성 구현
  • Redis로 처리 속도 향상

안정적인 모니터링

  • Prometheus, Grafna 이용
  • 시스템, 어플리케이션 지표 관리

배포

  • Docker를 이용해 실행 환경 통일, 배포 단순화

🛠 설계도

  • DDD 설계 DDD구조

  • 프로젝트 인프라 구조 시스템 아키텍쳐

🔍 주요 기능

  • 러닝 기록
    • 사용자 ID, 달린 거리, 달린 시간, 평균 페이스를 입력 받아 관리하는 서비스입니다.
    • 러닝 기록 등록 및 저장을 합니다.
    • 러닝 기록 등록 시 Kafka를 이용하여 업적 서비스로 러닝 기록 생성 이벤트를 날립니다.
  • 랭킹
    • 러닝 기록 기반 랭킹 서비스입니다.
    • 사용자들간 혹은 크루간 기록을 확인하여, 경쟁을 부추길 수 있습니다.
    • 개인 랭킹 산정은 일정 기간마다 누적 달린 거리(Km) , 누적 달린 시간 , 평균 페이스로 각 랭킹이 산정됩니다.
    • 크루 랭킹 산정은 크루의 현재 가입된 크루원 수 , 모임의 참여율 등으로 랭킹이 산정됩니다.
    • 랭킹은 일정 기간마다 스케쥴링으로 스냅샷 형태로 기록됩니다.
  • 업적
    • 러닝 기록 기반으로 어플리케이션의 업적을 달성했는지 확인해주는 서비스입니다.
    • 업적 서비스를 통해 러닝 목표 달성을 유도해줍니다.
    • Kafka를 통해 러닝 기록 생성 이벤트를 받아 업적 달성 여부를 확인한 뒤, 달성 시 알림 서비스로 사용자들에게 업적 달성 알림을 남깁니다.
  • 이용자 보고서 (ReCap)
    • 사용자 러닝 기록 데이터를 기반으로 요약 보고서를 생성해주는 서비스입니다.
    • 사용자 러닝 기록 데이터에 있는 수치를 이용하여 이용자에게 의미 있는 통계량을 계산하여 제공합니다.
  • 러닝 크루
    • 크루 기능
      • 이용자들이 마음 맞는 사람들과 함께 러닝 경험을 공유할 수 있도록 크루를 모집하거나 기존 크루에 참여할 수 있습니다.
      • 크루 장이 크루 회원을 관리하여 가입 신청을 관리하고, 이미 가입 중인 멤버를 관리할 수 있습니다.
      • 활발한 크루 활동을 위해 크루 내 모임을 모집할 수 있는 기능과, 크루원들이 사용할 수 있는 게시판 기능을 제공합니다.
    • 크루 내 모임 기능
      • 크루 정기 모임이나 , 크루원이 자유롭게 크루 안에서 모임을 모집하여 같이 러닝 활동을 할 수 있습니다.
    • 게시판 기능
      • 크루 내 정보 게시를 담당하는 기능입니다.
      • 게시글 생성, 조회, 검색, 수정, 삭제 기능이 구현되어 있습니다.
  • 대회
    • 대회 관리 기능
      • 공식 대회를 등록하여 러너들에게 대회 참가를 유도해주는 서비스입니다.
      • 대회 생성, 조회, 검색, 수정, 삭제 기능이 구현되어 있습니다.
      • 주최자별 대회 관리 인터페이스를 제공합니다.
      • 선착순/추첨 방식의 다양한 접수 유형 지원합니다.
    • 참가 신청 프로세스
      • Saga Pattern을 활용하여 분산 트랜잭션을 관리합니다.
      • 약관 동의 → 기념품 선택 → 배송지 입력 → 결제 → 참가 확정의 단계적 프로세스로 구성되어 있습니다.
      • Redis를 활용한 Saga 상태 관리로 트랜잭션 일관성 유지하고 있습니다.
    • 알림 서비스
      • Kafka를 활용한 이벤트 기반 아키텍처 구현하였습니다.
  • 챗봇
    • 사용자의 런닝에 관한 질문에대해 AI챗봇이 미리 저장된 런닝정보를 참고하여 답변을 생성합니다.
      • 관리자가 vector store테이블 저장 API를 이용해서 런닝관련 정보를 저장합니다.
      • 사용자가 질문을 요청하면 사용자의 질문과 유사도가 비슷한 정보를 기반으로 프롬프트를 생성합니다.
      • 프롬프트 엔지니링의 페르소나 패턴을 적용해 AI에 런닝 선수 출신 코치라는 역할을 부여하여 답변의 정확도를 높입니다.
      • 생성된 답변을 SSE로 실시간으로 응답합니다.
  • 알림
    • 업적 달성, 대회 신청 완료 등 이벤트가 발생했을 시 Slack으로 알림 메시지를 전송합니다.
      • 알림 서비스에서 Kafka 컨슈머를 통해 이벤트를 받고 Slack API를 호출해 메시지를 전송합니다.
      • Kafka컨슈머 Lag, 메시지 전송 성공/실패 등을 Grafana대시보드를 통해 모니터링을 합니다.

🪄 적용 기술

카테고리 기술 버전 근거/ 목적
언어 Java 17 LTS 지원 최신 언어
빌드 Gradle 1.1.7 빌드 관리 자동화
프레임워크 Spring Boot 3.4.4 설정 간소화
라이브러리 Netflix Eureka Spring Cloud 2024.0.1 MSA 환경에서 서비스 인스턴스 자동 등록·검색
Spring Boot JPA SQL 없이도 간단히 구현
Spring Boot Kafka 비동기 처리하여 서비스 간 결합도를 낮추고 확장성 확보
Jackson2JsonRedisSerializer JSON 형태로 저장·조회
Swagger(springdoc-openapi) 2.8.5 API 명세 자동 생성, 테스트 편의성
Infra Docker 개발·테스트 환경 일치
PostgreSQL ankane/pgvector:latest 벡터 검색 기능 포함
Redis redis/redis-stack
Zookeeper wurstmeister/zookeeper 오프셋 관리·파티션 메타데이터 조율
Apache Kafka wurstmeister/kafka 멱등·재시도 설정
UI kafka-ui provectuslabs/kafka-ui
모니터링 Prometheus prom/prometheus 메트릭을 스크래핑해 시계열 데이터 수집
Grafana grafana/grafana 메트릭을 대시보드화
Loki grafana/loki 로그를 라벨링해 중앙집중식 저장
Micrometer Registry Prometheus 모니터링 파이프라인에 연결
Test Spring Boot Starter Test JUnit Platform Launcher 통합 테스트와 연동 테스트 지원

👥 기술적 의사결정

👥 이벤트 기반 처리 적용
Kafka를 선택한 이유

👥 중간 상태 저장
Redis 를 선택한 이유

🔬 트러블슈팅

🔬 Running-Record 생성 API 처리량 개선
RunningRecord 생성 API의 성능 병목 원인 분석 및 처리량 개선 과정

🔬 AWS 환경에 배포 후 Spring Cloud Gateway 가 Eureka Client 를 찾지 못하는 문제
ECS에서 컨테이너가 잘못된 IP로 Eureka에 등록되는 문제 분석 및 해결

🔬 사용자 인증인가 서버분리와 게이트웨이 순환참조 오류 FeignClient 빈 생성 전에 GatewayFilter가 먼저 주입을 요구하면서 서로 참조하는 구조 개선

♻️ CONTRIBUTORS

팀원명 포지션 담당 깃허브 링크
강민 팀원 ▶ 러닝기록
- 러닝기록 서비스는 유저 ID, 달린 거리, 달린 시간, 평균 페이스를 입력받아 러닝 기록으로 저장하는 서비스입니다.
- 웨어러블 디바이스에 따라 러닝 기록의 형식이 약간씩 차이가 있을 수는 있지만, 우선 공통적으로 사용되는 기록을 기반으로 러닝 기록 형식을 구현하였습니다.
- 러닝 기록 생성, 단건 조회, 전체 목록 조회, 검색, 삭제를 구현하였습니다.
- 러닝 기록 생성시, 업적 확인을 위해 업적 서비스에 Kafka를 이용하여 러닝 기록 생성 이벤트를 보냅니다.
▶ 업적
- 업적 서비스는 러닝 기록 기반으로 업적을 확인해주는 서비스입니다.
- Kafka를 이용하여 러닝 기록 생성 이벤트를 받아 이벤트 내용인 러닝 기록 수치와 업적 기준 및 수치를 비교하여 업적 달성 여부를 판단합니다.
- 업적 달성시, 해당 이용자에게 알람을 보내기위해 kafka를 이용하여 알람 이벤트를 알람 서비스에 보냅니다.
- 업적 확인 외 업적 등록, 단건 조회, 전체 목록 조회, 사용자 별 조회, 검색, 삭제를 구현하였습니다.
김도훈 팀원 ▶ 챗봇
- Spring AI, Google Vertext AI를 사용하여 RAG기반의 AI챗봇 서비스 개발하였습니다.
- SSE를 사용하여 응답을 사용자에게 실시간으로 전달하는 기능을 구현하였습니다.
▶ 알림
- 대회, 업적 도메인에서 오는 이벤트를 처리하는 Kafka 컨슈머 개발하였습니다.
- Kafka 컨슈머에서 이벤트를 처리하여 슬랙 메시지 전송하는 기능을 구현하였습니다.
- Prometheus, kafka expoter, Loki, Grafana를 사용하여 Kafka Lag, 메시지 전송 성공/실패 등 모니터링 구현하였습니다.
김재현 팀장 ▶ 사용자
- 사용자 서비스는 유저의 회원가입 , 회원탈퇴 , 회원정보 수정 , 회원정보 조회, 회원 검색등을 지원하는 서비스 입니다.
- 사용자 서비스는 책임 분리 원칙에 따라 인증(Authentication) 및 인가(Authorization) 관련 기능은 별도의 Auth 서비스에서 구현했습니다.
- 사용자 정보 생성, 수정 , 삭제는 인증 서버로 Kafka 비동기 이벤트 기반 구조와 아웃박스(outbox)패턴을 활용하여 두 서비스간의 데이터 정합성을 보장하며, 장애시에도 이벤트의 유실 없이 복구 및 재처리가 가능하도록 설계했습니다.
▶ 인증/인가 & Gateway
- JWT 기반의 Stateless 인증 방식 구현
- Spring Security를 적용하여 로그인 시, Access Token 을 짧은 유효기간으로 발급하고, Refresh Token 을 같이 발급하여 Redis 에 서버 내부에 설정해둔 유효기간만큼 TTL 설정을 하여 사용자 권한 정보를 효율적으로 저장 및 관리하여 , 인증 처리 속도를 개선하고 서버 부하를 최소화 했습니다. 또 로그아웃시 또는 블랙리스트를 구현하여 Access Token 을 탈취 당하더라도 더 이상 이용 할 수 없게 만들었습니다.
- 인증/인가 서비스는 게이트웨이에서 직접 전달된 JWT Access Token 을 필터에서 내부 로직에 따라 검증하고 , 유효성을 확인 후 분석하여 X-User-Id, X-User-Name, X-User-Role 등의 사용자 정보를 헤더에 삽입한 뒤 , 게이트웨이에 보내 각 서비스로 전파하도록 구현했습니다.
- 이를 통해 각 마이크로 서비스에서는 별도의 인증 로직 없이 헤더에 포함된 사용자 정보를 기반으로 접근(역할, 본인확인) 을 수행 할 수 있으며, 전체 시스템에서 중앙 집중식 인증 구조를 구현하여 보안 정책의 일관성과 유지보수 효율성을 확보 할 수 있었습니다.
- CheckPermission 이라는 Custom AOP 구현하여 각 서비스의 컨트롤러 단에서 사용자의 역할을 검증 할 수 있습니다.
- Gateway에서 Swagger UI를 연동하여 , 각 마이크로서비스의 API를 중앙에서 통합 문서화 하였습니다.
▶ 랭킹
- 랭킹 서비스는 일정기간(매주 월요일 오전 2시) 마다 스케쥴링을 통해 러닝기록 데이터 또는 크루데이터를 받아와 데이터를 가공하고 , 랭킹을 산정하여 이용자들의 동기부여 및 자극이 될 수 있게 하였습니다.
- 랭킹이 기준이 되는 지표는 리플랙션(Reflection) 기능을 통해 구현 하였으며 , 개인 랭킹의 경우 러닝 데이터 객체에서 총 거리 , 총 시간 , 평균 페이스 , 크루원 수 , 모임참여 인원등의 필드를 동적으로 추출하여 해당 값을 기준으로 랭킹을 산정할 수 있도록 하였습니다.
- 각 서비스의 정보를 요청할 때, 해당 데이터는 실시간성이 중요하지 않기 때문에 , 비동기나 캐싱이 필요없는 단순 요청-응답 구조로 처리하기 위해 FeignClient를 활용하여 통신하였습니다.
- 랭킹 산정의 성능을 향상시키기 위해 TreeSet 자료 구조를 사용하였습니다. 요소가 삽입될때마다 자동으로 정렬 되기 때문에 추가적인 정렬 작업 없이도 순위 산정이 가능했으며, 랭킹 계산 속도를 크게 개선 할 수 있었습니다.
▶kafka Interceptor & Auditing
- Kafka 통신 시 별도의 ProducerInterceptor를 만들어 메세지를 보낼 때 , 헤더의 유저의 정보를 같이 보냈습니다.
- Kafka는 Http기반이 아니므로 기존의 AuditorAware 기반의 자동 Auditing이 적용되지않았으며, Kafka Consumer 측에서 사용자 정보를 추적하기 위해 별도의 AOP(AuditorKafkaAspect) 를 구현했습니다. → 해당 어노테이션이 적용된 메소드에선 userId를 추출하여 AuditorAwareImpl 에 직접 넣어주는 방식으로 동작하게 하였습니다.
김지수 팀원 ▶ 대회
- 짧은 응답 시간,  순서가 꼬이지 않는 다단계 트랜잭션, 장애 발생 시 데이터 유실 없이 정확히 복구되는 신뢰성을 위해 Kafka + Saga 패턴 + Redis 로 분산 트랜잭션 구현하였습니다.
- 대회 신청 요청 시 사용자·대회(paticipant_id - competition_id) 조합에 대한 SagaState를 Redis에서 찾고(없으면 새 ID 생성) 현재 진행 단계를 확인합니다.
- CompetitionSagaOrchestrator가 해당 단계의 도메인 데이터를 저장하고 동일한 sagaId를 키로 하는 Kafka 이벤트를 competition_saga 토픽에 발행합니다.
- SagaEventConsumer가 @KafkaListener로 해당 파티션을 단일 스레드로 소비합니다.
- Consumer는 Redis의 SagaState를 갱신하면서 약관 → 기념품 → 배송지 → 결제 시작 → 결제 완료 → 자격 검사 → 참가 확정 → 알림 발송 순으로 도메인 서비스를 호출합니다. (단일 엔드 포인트로 요청을 보내면서 진행합니다.)
- 결제 단계가 성공하면 Consumer는 DB에 참가 확정을 적재합니다.
- Kafka 토픽(competition_notification)에 알림 이벤트를 발행합니다.
- 중복 신청 또는 정원 초과처럼 검증이 실패하면 Consumer가 보상 이벤트를 만들어 역순으로 취소됩니다.
▶ 모니터링
- 대회 신청 프로세스의 단계별 완료율과 전체 프로세스 성공률을 측정하기 위해 다중 계층의 모니터링 시스템을 구축하였습니다.
- Micrometer 라이브러리를 활용,각 단계별 카운터를 정의하였습니다.
- Spring Boot Actuator의 Prometheus 엔드포인트를 통해 메트릭 데이터 노출, 시계열 데이터베이스에 저장하였습니다.
- 단계별 완료율과 전체 프로세스 성공률을 계산하기 위해Prometheus의 PromQL 쿼리 언어를 사용하였습니다. (약관 동의 단계의 완료율 = 약관 동의 완료 횟수 / 신청 시작 횟수, 전체 프로세스 성공률 = 최종 완료 횟수 / 신청 시작 횟수)
- Grafana 대시보드에서 시각화. 게이지 차트로 현재 성공률, 시계열 그래프를 통해 시간에 따른 변화 추이를 보여주도록 JSON 형식으로 작성하였습니다.
최해인 테크리더 ▶ 이용자 보고서
- 이 서비스는 사용자가 러닝 활동을 기록할 수 있는 기능을 제공하며 이용자 보고서 기능은 사용자의 러닝 기록에서 의미 있는 수치를 가공하여 사용자가 참고할 수 있는 통계 수치를 제공하는 기능입니다
- 특정 기간 동안 사용자의 러닝 기록을 이용해 주기적으로 이용자 보고서를 생성하며
특정 기간 (예를 들어, 한 주, 한 달 동안)의 축적된 데이터를 읽고 쓰는 과정을 최적화하기 위해 Spring Scheduler 과 Spring Batch 를 사용하였습니다.
- Spring Batch 는 데이터를 읽고 쓰는 과정과 비즈니스 로직을 분리하여 코드의 재사용성을 높혔습니다.
- Spring Batch 는 데이터를 읽는 ItemReader 인터페이스와 데이터를 가공하는 ItemProcessor 인터페이스 그리고 가공된 데이터를 쓰는 ItemWriter 인터페이스를 제공하며 이를 재정의하여 사용자 보고서 배치 작업(Job)을 구성하였습니다.
- 사용자 보고서 배치 작업은 다른 마이크로서비스는 러닝 기록 서비스(runningRecord-service) 로 부터 데이터를 불러오는 ApiCallStep 과 통계 수치를 생성하는 reCapStep 두개로 분리하여 하나의 작업이 실행됩니다.
- recapStep 은 사용자 기록에서 가공하고 싶은 항목마다 독립적인 Step 을 생성하여 별개 스레드에 할당하고 각 항목마다 목표 통계 수치를 계산합니다. (예: 러닝 기록의 거리 항목으로부터 누적 거리를 계산합니다.)
- 서비스 발전에 따라 목표 통계 수치가 많아져도 Batch 코드를 수정하지 않아도 되도록 전략 패턴을 이용해 Step을 생성합니다.
▶ 배포
• AWS 환경에서 배포하였습니다.
• 별도의 프론트엔드는 없지만 확장성과 서비스 개발의 독립성을 확보하기 위하여 ALB, 애플리케이션, 인프라를 AWS Private Subnet 을 이용해 별도의 계층으로 분리한 전형적인 3티어 계층 아키텍쳐로 설계하였습니다.
• 사용자가 도메인을 통해 API 를 호출하면 인터넷 게이트웨이를 통해 트래픽이 접근하고 Route53 과 연결된 ALB가 이를 애플리케이션이 있는 Private Subnet으로 보냅니다 ALB와 직접적으로 연결된 서비스는 Spring Cloud Gateway 가 있는 Gateway 서비스입니다.
• 애플리케이션은 ECR에 저장한 각 MSA 의 도커 이미지를 ECS에 독립적인 테스크로 작성하고 이를 서비스로 실행시키는 방식으로 배포하였습니다.
• 개발자가 코드를 변경하고 Github 에 Push 하면 Github actions 를 이용해 어느 서비스가 변경되었는 지 감지하고 해당 서비스에 해당하는 이미지를 ECR 에 다시 Push 하는 방식으로 ECS 서비스를 재배포합니다.
• 롤링 배포 방식의 무중단 배포를 사용하였습니다. 시간과 예산의 한계로 한 서비스에 여러 인스턴스를 실행하지 못해 실질적인 경험은 못 해봤고 추후 고도화가 필요할 것 같습니다.
• 인프라 계층은 AWS RDS, AWS ElasticCache를 사용해 로컬에서 사용하는 docker-compose 의 postgresql, 과 redis 컨테이너를 대체하였습니다.
•kafka 는EC2에 별도로 설치하여 서버를 운영하였습니다.
• 모니터링 서버를 두어 prometus 를 이용해 서비스들의 매트릭을 수집합니다.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Java 99.0%
  • Dockerfile 1.0%