Skip to content

KTB-IDLE/WeatherWise-Server

Repository files navigation

실시간 날씨 정보와 AI가 결합된 새로운 날씨 플랫폼, WeatherWise


🔖 프로젝트 개요

주제: 사용자 맞춤형 날씨 정보 제공 및 상호작용 플랫폼
대상: 날씨 정보 활용 및 상호작용에 관심이 있는 사용자


🎯 목표

  1. 정확한 날씨 정보 제공: 기상청 데이터와 AI 분석을 통해 지역별 맞춤형 날씨 정보 전달
  2. 미션 수행과 커뮤니티 강화: 날씨 기반 미션과 커뮤니티 상호작용을 통해 사용자 참여 유도
  3. 긴급 상황 대응: 기상특보 채팅으로 실시간 정보 공유 및 대응

📚 기술 스택

Frontend

  • ⚛️ React: 사용자 인터페이스(UI) 개발을 위한 라이브러리

Backend

  • 🌱 Spring Boot: 강력한 백엔드 애플리케이션 개발 프레임워크
  • 🐬 MySQL: 데이터 저장 및 관리에 사용된 관계형 데이터베이스
  • 🚀 Redis: 캐싱 및 실시간 데이터 저장
  • 💬 Kafka: 메시지 큐 및 데이터 스트리밍 도구로 실시간 채팅 및 알림 구현

DevOps / Infrastructure

  • 🛠️ Jenkins: CI/CD 자동화를 위한 도구
  • 📦 Docker: 애플리케이션 컨테이너화
  • ☸️ Kubernetes: 컨테이너 오케스트레이션 및 관리
  • 🐙 Argo: DevOps 워크플로우 관리
  • ☁️ AWS: 클라우드 인프라 호스팅
  • 🔀 Nginx: 리버스 프록시 및 웹 서버

AI / Automation

  • 🐍 Python: 데이터 처리 및 AI 모델 개발
  • 🔗 LangChain: AI 어플리케이션 구축을 위한 프레임워크

Collaboration / Tools

  • 🐙 GitHub: 버전 관리 및 소스 코드 관리
  • 📝 Notion: 프로젝트 관리 및 문서화
  • 🎮 Discord: 팀 커뮤니케이션
  • 🎨 Figma: UI/UX 디자인

🔗 주요 기능

1️⃣ 회원 및 인증 관리 시스템

  • JWT 기반 사용자 인증

    • 사용자 로그인/회원가입 시 JWT 토큰 발급 및 인증 관리.
    • STOMP 헤더에 JWT 토큰을 포함해 실시간 채팅에서도 인증 처리.
    • 토큰에서 사용자 ID를 추출하여 커뮤니티, 미션 등 다양한 기능에 활용.
  • 회원가입 및 로그인

    • 일반 회원가입 및 카카오 소셜 로그인 지원.
    • 비밀번호 검증 및 닉네임 중복 방지 처리.
  • 사용자 정보 관리

    • 사용자 정보 수정, 닉네임 변경, 로그아웃 및 회원 탈퇴 기능 제공.

2️⃣ 미션 생성 및 인증

  • 날씨 기반 맞춤형 미션 제공

    • 기상 데이터를 활용하여 아침, 점심, 저녁 기준으로 최대 3개의 미션 생성.
    • 예시 미션: "자전거를 타세요", "텀블러를 사용하세요".
    • 각 미션마다 포인트 지급률 및 진행 상태 표시.
  • AI 기반 미션 인증

    • 사용자가 미션에 맞는 사진을 첨부하면 AI가 해당 사진의 적합성을 검증.
    • 인증 성공 시 포인트 지급 및 실패 시 재도전 가능.
  • 포인트 및 랭킹 시스템

    • 포인트를 기반으로 사용자 랭킹 제공.
    • 순위 확인 및 검색 기능을 통해 사용자 경험 향상.

3️⃣ 커뮤니티

  • 위치 기반 게시글

    • 사용자의 현재 위치를 기준으로 반경 5km 내 게시글 표시.
    • 위치 변경 시 카카오맵 API를 통해 새로운 위치 반영 및 게시글 업데이트.
  • 게시글 작성

    • 제목(20자) 및 본문(150자) 글자 수 제한을 통해 사용자 경험 최적화.
    • 좋아요/싫어요 버튼으로 게시글 신뢰도 평가 가능.
  • 내가 작성한 글 관리

    • 사용자가 작성한 글의 리스트를 확인 및 삭제 가능.

4️⃣ 기상특보 오픈채팅

  • 기상특보에 따른 실시간 채팅방 생성
    • 기상특보 발생 시 해당 지역의 사용자들을 위한 오픈 채팅방 자동 생성.
    • 예: "강원도 건조/한파 경보 채팅방".
  • 기상특보 데이터 업데이트:
    • Spring Scheduler를 활용해 1시간마다 기상청 API 호출.
    • 기상특보 발령 시 '시/도' 기준으로 실시간 단체 오픈채팅방 자동 생성.
  • 실시간 채팅:
    • Redis:
      • 최신 채팅 메시지 100개를 캐싱하여 빠른 채팅 로드 제공.
      • 사용자 간 실시간 메시지 교환 성능 향상.
    • WebSocket + STOMP:
      • 실시간 양방향 통신 구현.
      • 채팅방 내 메시지 브로드캐스팅 및 실시간 알림 기능 제공.
  • WebFlux와 R2DBC를 활용한 확장:
    • WeatherWise-Server-Chatting 레포지토리에서 MSA 구조로 구현.
    • WebFlux와 R2DBC를 사용해 비동기 처리로 확장성과 성능 향상.

🌈 개선 사항

1️⃣ 공유자원 동시성 문제 해결 및 성능 개선 - 응답속도 92.8% 개선

문제 상황

  • 커뮤니티 게시글에서 "도움돼요" / "도움 안 돼요" 변수들은 공유 자원이기 때문에 멀티스레드 환경에서 Race Condition 문제 가능성을 확인하고, ExecutorService를 활용한 멀티스레드 환경 테스트를 진행한 결과 요청 수만큼 카운팅되지 않는 문제 발생.

문제 상황을 그림으로 표현 image

  1. Thread-1 이 addVoate() 를 실행하여 upVote 값을 읽는데, 이때 upVote 값은 0
  • Thread-1 은 0 에서 1 을 더한 값을 계산하여 커밋을 하려고 준비
  1. Thread-1 이 아직 커밋을 완료하지 않은 상태에서, Thread-2 가 addVote() 를 실행
  • Thread-2 역시 upVote 값을 읽는데 Thread-1 이 커밋을 하지 않았기 때문에 upVote 값은 여전히 0
  1. Thread-2 는 0 에서 1을 더한 값을 계산하여 자신의 트랜잭션에 저장하고 커밋을 준비
  2. 결과적으로 Thread-1 , Thread-2 모두 정상적으로 커밋이 되었음에도 불구하고, upVote 값은 두 번 증가하지 않고 1로 남는 문제가 발생 (Race Condtion)

문제 해결

  • 간단하게 생각해보면 Thread-1 이 upVote 를 업데이트 하는 동안 다른 Thread 들은 접근하지 못 하도록 하면 된다.
  • 비관적 락 , 낙관적 락 , 트랜잭션 격리 수준 , Kafka 들을 활용해 동시성 문제를 해결

첫 번쨰 문제 해결 방법 : MySQL 비관적 락

  • 트랜잭션이 데이터를 읽거나 수정할 때, 다른 트랜잭션이 해당 데이터에 접근하려는 시도를 "비관적" 으로 바라보기 때문에 아예 다른 트랜잭션의 접근을 사전에 차단하는 방법
  • 장점
    • 다른 트랜잭션의 접근을 사전에 차단하기 때문에 즉각적인 충돌을 방지하여 데이터 정합성을 보장
  • 단점
    • Lock 이 걸린 동안 다른 트랜잭션은 해당 데이터에 접근할 수 없기 떄문에 높은 트래픽 환경에서는 성능 저하가 발생할 수 있음 image

두 번쨰 문제 해결 방법 : Java ReentrantLock

  • Java 에서 제공하는 synchronized 와 BLOCKED 상태를 통한 임계 영역 관리의 한계를 극복하기 위해 Lock 인터페이스와 ReentrantLock 구현체를 제공하는데, 이를 활용하여 동시성 문제를 해결
  • 첫 번쨰 문제 해결 방법과 마찬가지로 Java ReentrantLock 은 비관적 락이지만, MySQL 비관적 락은 데이터베이스 레벨의 Lock 이고 Java ReentrantLock 은 애플리케이션 레벨의 Lock
  • 장점
    • lock() , unlock() 으로 Lock 을 명시적으로 제어할 수 있어 정교한 Lock 제어가 가능함
    • ReentrantLock은 공정락(Fair Lock) 을 설정할 수 있기 때문에 스레드 기아 상태에 빠지는 것을 방지할 수 있음
    • Condition 객체를 사용해 특정 조건을 만족할 때만 스레드를 꺠우거나 대기 상태로 전환할 수 있어, 동기화 로직을 더 세밀하게 구현할 수 있음
  • 단점
    • 첫 번째 문제 해결 방법과 같은 비관적 락 메커니즘을 사용하기 떄문에 높은 트래픽 환경에서는 성능 저하가 발생할 수 있음
    • 직접 lock() , unlock() 을 명시해야 하기 때문에 코드 복잡성이 증가

세 번쨰 문제 해결 방법 : MySQL 낙관적 락

  • 데이터에 대한 충돌 가능성이 낮다고 가정하고 락을 사용하지 않고 작업을 진행
  • 데이터 변경 시점에 충돌 여부를 확인하여 충돌이 발생한 경우 트랜잭션을 롤백하고 재시도 하는 방식으로 동작
  • 장점
    • Lock 을 걸지 않기 떄문에 Lock 로 인한 CPU 와 메모리 오버헤드가 없음
    • 데이터 충돌 가능성이 낮은 환경에서 높은 트래픽을 효율적으로 처리할 수 있음
    • 충돌이 드물다면 비관적 락 보다 빠르고 효율적
  • 단점
    • 충돌이 많이 발생하게 된다면 트랜잭션을 롤백하고 재시도 해야 하기 때문에, 충돌 빈도가 높으면 성능이 저하
    • 충돌 발생 시 트랜잭션을 어떻게 재시도 할지 설계해야 하므로 구현이 복잡
    • 충돌로 인해 업데이트가 반복되면 데이터베이스에 불필요한 부하를 초래할 수 있음

image


네번쨰 문제 해결 방법 : Kafka 사용

  • Kafka 는 메시지를 파티션 단위로 관리하며, 파티션 내 메시지는 순차적으로 처리 되는 것을 보장하기 때문에 "도움돼요" / "도움 안 돼요" 를 증가하거나 감소할 경우 그 이벤트를 Kafka 에 발행하여 비동기적으로 처리할 수 있도록 함
  • 장점
    • 비동기적으로 메시지를 처리하기 때문에 높은 트래픽에서도 안정적인 처리가 가능
  • 단점
    • Kafka 를 설정하고, 메시지 생산 및 소비 코드를 추가해야하기 때문에 초기 설계가 복잡할 수 있음
    • Kafka 를 관리하기 위한 추가적인 인프라 비용이 발생

결론 image

  • 성능 관점에서는 TPS 가 100 이하인 경우에는 MySQL 낙관적 락을 사용
  • TPS 100 ~ 300 에서는 MySQL 비관적 락 or Java ReentrantLock 을 사용
  • 그 이상의 TPS 인 경우에는 Kafka 를 사용
  • 물론 Kafka 를 사용하는 것이 모든 TPS 에서 유리하지만, Kafka 를 관리하기 위한 인프라 비용과 낮은 TPS 인 경우에 굳이 Kafka 를 사용할 필요성이 없다고 생각함


2️⃣ 채팅 성능 개선 - 최대 트래픽 처리량 약 4배 증가


문제 상황

  • 기존 모놀리식 구조에서는 Spring MVC를 사용하여 초당 최대 400명의 사용자 요청을 처리했으나, 이 이상의 트래픽이 발생하면 서버가 과부하로 인해 다운되는 문제가 발생.
  • 응답 지연이 심해지고, 데이터의 정합성에도 문제가 생길 가능성이 높아짐.

1. 성능 병목 분석 및 원인 추론

  • 단순 코드 최적화로는 문제 해결이 어려움을 인식하고, Prometheus와 Grafana를 도입해 실시간 메트릭 수집 및 병목 구간 분석.
  • 분석 결과, Tomcat 워커 스레드가 최대값(200개)에 도달해 더 이상 요청을 처리하지 못하고 대기하는 현상 확인.
  • 이때 CPU 사용률은 낮은 반면, 스레드는 과포화 상태였으며 이는 I/O 블로킹 구조로 인해 리소스가 비효율적으로 소비되고 있음을 의미한다고 판단.
  • 즉, Spring MVC 기반의 동기 구조에서는 DB 접근, Redis 접근과 같은 I/O 작업 동안 스레드가 블로킹되어 다른 요청을 처리하지 못하는 구조적 한계가 병목의 근본 원인이었음.

2. MSA 도입과 WebFlux 기반 비동기 구조 전환

  • 채팅 기능을 독립적인 MSA로 분리하고, Spring WebFlux 기반으로 리팩토링.
  • WebFlux의 이벤트 루프 기반 아키텍처와 논블로킹 특성을 활용해 동시 처리 성능 대폭 향상.
    • MVC: 요청당 스레드 1개 고정 → WebFlux: 이벤트 루프 내에서 스레드 재사용 가능.
    • 같은 부하에서 Runnable 스레드 수: MVC 197개 → WebFlux 72개.
    • 즉, WebFlux 구조는 적은 스레드로 더 많은 요청을 처리할 수 있어 리소스 효율성이 높음.
  • Kafka 도입으로 메시지 I/O 처리 병렬화 및 서비스 간 결합도 감소.
    • WebSocket → Kafka Producer → Kafka Consumer → Redis 캐시/DB 저장 → WebSocket Broadcast 흐름 구성.
    • Kafka 파티션 분할을 통해 메시지 병렬 소비 구조 구성.
  • R2DBC 도입으로 MySQL DB 접근의 블로킹 문제 해결.
    • 기존 JDBC의 커넥션 대기/점유 문제 해소 → 비동기 처리로 저장 속도 개선.
  • Redis 캐싱 최적화:
    • 최신 메시지 100개를 캐싱하고, 요청 시 Redis에서 즉시 응답.
    • 동시성 이슈 방지를 위해 락 기반 접근 구조 설계.

3. 데이터 정합성 보장

  • Kafka를 통해 메시지를 유실 없이 소비하고 DB에 저장.
  • 4만 건 이상의 메시지 전송 시에도 데이터 일치율 100% 확인.
  • 비동기 구조에서도 메시지 순서 보장과 중복 방지 로직 설계.

4. 수치 기반 성과

  • TPS 증가: 초당 메시지 처리량 400건 → 3,700건 이상 (약 9배 증가).
  • 응답 지연 감소: 평균 응답 시간 5.52초 → 0.13초 (약 40배 단축).
  • 사용자 처리량 증가: 초당 400명 수준 → 3700명 이상 동시 처리 가능.
  • 스레드 효율성 향상: Runnable 스레드 수 약 60% 절감 (197개 → 72개).
  • 데이터 정합성: 100% 메시지 저장 일치율 유지.

요약

  • 단순 성능 개선을 넘어, 병목 원인을 메트릭 기반으로 분석하고, 구조적 한계를 해결하기 위한 아키텍처 리디자인을 수행함.
  • 구조적 전환(WebFlux + R2DBC + Kafka)을 통해 높은 확장성과 안정성을 확보하고, 동시 접속자 증가에도 대응 가능한 실시간 채팅 시스템 구현.

image

3️⃣선착순 쿠폰 이벤트 (모놀리식 → MSA 마이그레이션)

기존 모놀리식 아키텍처 에서 마이크로서비스 아키텍처(MSA) 로 전환하는 과정을 설명합니다.
특히, 쿠폰 발급 서비스의 성능 문제를 해결하기 위해 Kafka를 활용한 비동기 메시지 처리 방식으로 전환한 사례를 중심으로 다룹니다.


🚨 모놀리식 아키텍처의 문제점

쿠폰 이벤트

  • 특정 프로모션을 통해 사용자에게 쿠폰을 발급하는 이벤트입니다.
  • 아래 발급 조건을 충족해야 쿠폰을 받을 수 있습니다.

쿠폰 발급 조건

  1. 한 사람당 한 개의 쿠폰만 가질 수 있다.
  2. 당일 미션 하나를 인증 받아야 한다.
  3. 이벤트 기간 동안, 매일 특정 시간에 오픈하며 총 지급 수량을 한정한다.
  4. 쿠폰 지급 수량은 당일 정해진 양을 초과할 수 없다.

🚧 트래픽 몰림 시 문제점

모놀리식 아키텍처는 모든 기능이 단일 서비스 내에 통합되어 있어 아래와 같은 문제가 발생했습니다:

  • 서버 마비: 트래픽이 급증하면 서버 자원이 고갈되어 기본 기능(예: 회원가입, 로그인)조차 정상적으로 작동하지 않음.
  • TPS 한계: 평균 TPS 200에 도달 시 웹서버가 병목현상을 일으키며, 전체 서비스가 중단.

🔧 MSA 도입 배경

모놀리식 구조의 한계를 극복하고, 높은 트래픽 상황에서도 안정적인 서비스를 제공하기 위해 MSA를 도입했습니다.

  • 각 기능을 독립적인 마이크로서비스로 분리.
  • 특정 서비스 장애가 전체 시스템에 영향을 미치지 않도록 설계 가능.

⚙️ MSA 도입 과정

1️⃣ 서비스 분리 및 HTTP 동기 방식 문제

  • 초기에는 서비스 간 통신 방식으로 가장 익숙한 HTTP를 사용.
  • 그러나 HTTP는 동기 요청 방식이기 때문에 모놀리식의 한계(서비스 간 결합도)와 장애 전파 문제를 해결하지 못함.

2️⃣ Kafka를 활용한 EDA로 전환

도입 배경

  • 기존 HTTP 요청 방식의 결합도 높음장애 전파 위험을 해결하기 위해 Kafka를 도입.
  • Kafka를 통해 비동기 메시지 기반 아키텍처로 전환.

Kafka 도입 이유

  1. 비동기 처리
    • 메시지를 큐에 저장하여 서비스 간 비동기적으로 데이터를 처리.
  2. 내결함성
    • 메시지가 큐에 저장되므로 특정 서비스가 다운되더라도 메시지가 유지됨.
    • 서비스 복구 후 메시지 재처리 가능.
  3. 높은 처리량
    • Kafka는 높은 스루풋을 지원해 대량의 메시지를 효율적으로 처리.
  4. 확장성
    • 메시지 브로커를 통해 서비스 간 결합도를 낮춰, 각 서비스를 독립적으로 확장 가능.

🛠️ 비동기 방식의 장점

  1. 서비스 독립성 강화
    • 서비스 간 직접적인 의존성을 제거하고, 메시지 브로커를 통한 간접적 통신으로 독립성 강화.
  2. 유연한 에러 처리
    • 장애 발생 시 메시지를 큐에 저장하여 재처리하거나 다양한 방식으로 에러 처리 가능.
  3. 성능 향상
    • 비동기 메시지 처리로 블로킹 없이 다수의 요청을 처리해 전체 시스템 성능 개선.

✅ 전환 후 성과

Kafka 기반 비동기 메시지 처리로 다음과 같은 성과를 달성했습니다:

  1. 성능 개선
    • 트래픽 폭주 상황에서도 서비스 마비 없이 안정적 동작.
  2. 신뢰성 향상
    • 서비스 다운 시에도 메시지가 큐에 저장되어 복구 후 처리가 가능.
  3. 확장성 강화
    • 각 서비스를 독립적으로 스케일링 가능해 전체 시스템 유연성 증가.
  4. 데이터 정합성 유지
    • 비동기 메시지 처리로 데이터 정합성을 유지하며, 고트래픽 상황에서도 안정적 데이터 처리 가능.

결론

모놀리식 아키텍처에서 MSA로의 전환은 초기에는 복잡해 보일 수 있습니다. 그러나 높은 트래픽과 장애 상황에서 문제를 효과적으로 해결할 수 있는 강력한 방법입니다.

Kafka를 활용한 비동기 메시지 처리는:

  • 서비스 간 결합도를 낮추고,
  • 시스템 신뢰성과 확장성을 향상시키며,
  • 안정적인 사용자 경험을 제공합니다.

이러한 전환을 통해 서비스 간 독립성을 확보하고, 높은 트래픽 상황에서도 확장성과 안정성을 겸비한 아키텍처를 구현할 수 있었습니다.

아키텍처별 성능 비교

아키텍처별 성능 비교



MSA 전환한 Git Repository


About

WeatherWise-Server

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 3

  •  
  •  
  •