DDWU ACC 2nd Final Project Team 1 west-somsom (서쪽솜솜) https://github.com/orgs/west-somsom/repositories

프로젝트 소개


팝업 스토어를 이용하려는 사용자가 다양한 정보를 탐색하고 예약 및 소통을 원활하게 진행할 수 있도록 지원하는 통합 플랫폼

주요 기능

  1. 사용자 관리
    1. 카카오 소셜 로그인을 통한 간편 로그인
    2. 사용자 정보 등록, 조회 및 수정
  2. 팝업 스토어 정보 관리
    1. 팝업 스토어 등록
    2. 팝업 스토어 정보 전체 목록 조회 및 상세 조회
    3. 이름 및 카테고리 기반 검색
  3. 채팅을 통한 소통 지원
    1. 예약 등록 시점에 채팅방 함께 개설
    2. 팝업 스토어 당 한 개의 1 : N 채팅방
    3. 텍스트 채팅
  4. 예약 관리
    1. 예약 신청
    2. 예약 중 사용자의 현재 순번 실시간 확인
    3. 예약 내역 조회
    4. 예약 취소 및 삭제
  5. 알림 전송
    1. 예약 즉시 예약 확인 이메일 알림 전송
    2. 예약일 하루 전(오후 3시) 예약 확인 알림 전송
  6. 아이템 기반 사용자 맞춤형 추천
    1. 사용자 프로필 및 카드 결제 내역 데이터를 기반으로 개인화된 팝업 스토어 추천 제공

아키텍처 설계


  • 설계 핵심
    1. 팝업 스토어 예약 서비스는 예약 오픈 시점에 대량의 사용자 접속 발생
      • 특정 기간 동안 사용자 트래픽 폭증 → 트래픽 부하 분산 및 관리 필요
    2. 사용자 데이터의 무결성과 서비스 안정성을 보장하기 위해 Multi-AZ 기반의 이중화 구성이 필요

아키텍처 설계 및 구성

Infra

image

전반적인 구성

  • Public과 Private으로 분리하여 외부 사용자가 접근하는 네트워크와 데이터가 저장된 네트워크 분리
    • Public Subnet
      • 외부 트래픽 처리
      • Private Subnet에 위치한 인스턴스 접근을 위해 Bastion host 설정
      • Private Subnet에 위치한 인스턴스의 인터넷 사용을 위해 NAT Instance 설정
    • Private Subnet
      • 데이터베이스와 캐시, 애플리케이션 서버 등 민감한 데이터가 저장된 자원을 배치
      • Bastion Host를 통해 간접적으로 접근 가능
  • 트래픽 분산
    • 서비스의 안정성을 위해 2개의 가용 영역 사용
    • ALB를 통한 인스턴스 부하 분산
    • Auto Scaling 설정 및 ALB 대상 그룹 지정

기능

  • 팝업 스토어 정보 등록 및 조회 기능 구현

    1. Redis OSS with ElastiCache 활용
      • 등록 기능
        • 팝업 스토어 정보를 DB와 Redis에 동시에 저장하여 조회 속도 최적화
        • 등록 시, Redis 캐시에 중복 데이터가 저장되지 않도록 유효성 검사를 추가로 수행
      • 조회 기능
        • 전체 정보 조회 시 Redis에서 우선 데이터 조회
        • 캐시 미스 발생 시 DB에서 데이터를 조회하고, 이를 Redis에 저장
    2. 현재 채택 방식
      • 조회 기능은 RDS 직접 조회 방식을 사용 중으로, 캐시를 활용한 방식은 향후 효율성을 검토해 개선 예정
  • 예약 알림 기능 구현

    • EventBridge Scheduler → SQS → Lambda → SES 연결 구조
    • 예약 시 사용자 이메일로 예약 관련 내용이 발송
      • Spring Boot 서버에서 EventBridge Scheduler를 생성하기 위한 SDK를 호출
      • 생성된 Scheduler가 SQS로 메시지를 전송
      • SQS 메시지가 Lambda Trigger를 통해 Lambda 실행
      • Lambda에서 SES 이메일 발송을 위한 SDK를 호출
    • Scheduler 생성 시 전달한 파라미터의 내용이 SES를 통해 이메일로 전송
  • 예약 기능 구현

    • Redis OSS with ElastiCache 사용
      • 예약 가능 인원수 제한을 위해 슬롯 초기화(10개)
      • 사용자 예약 요청 처리
        • Redis의 Set을 사용해 사용자가 이미 대기열에 있는지 또는 예약 요청을 보낸 적이 있는지 확인
        • 확인 후 Redis 대기열(List)에 추가
      • Redis Pub/Sub(Publish/Subscribe)를 통해 실시간으로 예약 처리
        • 초반에는 스케줄러를 사용하였으나 이후 성능 개선을 위해 Redis Pub/Sub 도입 → 유의미한 성능 개선

        • Redis Pub/Sub 방식

          • Redis에서 제공하는 실시간 이벤트 기반 비동기 메시징 시스템

          • 메시지를 발행하고 이를 구독하는 애플리케이션 간 실시간 메시지 전달

          • 예약 요청이 들어오면 메시지를 Redis 채널에 발행

                  `redisTemplate.convertAndSend(channel, message);`
            
          • Redis는 해당 채널 구독 중인 구독자에게 메시지 전달

          • 메시지를 수신한 후 예약 요청 처리

                  ```jsx
                  @Override
                      public void onMessage(Message message, byte[] pattern) {
                          String channel = new String(pattern);
                          String receivedMessage = new String(message.getBody());
                          log.info("Received message: {} from channel: {}", receivedMessage,channel);
            
                          // 예약 처리 로직 실행
                          processReservation(receivedMessage);
                      }
                  ```
            
        • 예약 가능 슬롯키 확인 후 순서대로 예약 처리

        • 중복 예약을 막기 위해 예약한 사용자를 Redis에 추가

      • TTL 만료 설정을 팝업 스토어 예약 기간 동안 유지
      • 예약 취소 시 Redis에서 슬롯 수 업데이트 및 사용자 Redis 중복 확인 데이터에서 제거

Back-End


소셜 로그인 서비스

  • 카카오 오픈 API를 활용한 소셜 로그인/로그아웃 구현
  • 흐름
    • 로그인 흐름: 카카오 로그인 요청 → 콜백으로 인증 코드 수신 → 액세스 토큰 발급 → 사용자 정보 조회 및 저장
    • 정보 업데이트: API를 통해 사용자 정보를 수정 및 유지
  1. 카카오 인증 및 사용자 정보 관리
    • 카카오 로그인 URL 생성 : https://kauth.kakao.com/oauth/authorize를 활용해 로그인 요청 URL 생성
    • 액세스 토큰 발급 : 사용자 인증 후 받은 code로 카카오 API에서 토큰 요청
    • 사용자 정보 조회 : 액세스 토큰으로 사용자 정보 조회 후 DB에서 저장 및 세션 생성
  2. 콜백 및 사용자 정보 갱신
    • 카카오 로그인 콜백: 토큰 및 사용자 정보 처리 후 성공 메시지 반환
    • 사용자 정보 업데이트: 전화번호, 성별, 나이 등 사용자 정보를 업데이트(추가적으로 입력받음)하고 DB 및 세션 동기화
  3. 사용자 정보 API
    • 사용자 정보 조회: 특정 사용자의 정보를 DB에서 검색 및 반환
    • 사용자 정보 수정: 입력된 정보만 선택적으로 업데이트

추천 서비스

  • 구매 데이터 기반 팝업 스토어 추천

  • 접근 방식:

    • 협업 필터링(collaborative filtering)과 아이템 기반 코사인 유사도(cosine similarity) 활용
    • Precision@K, Recall@K, F1-Score 등의 평가 지표를 사용하여 성능 검증
      협업 필터링코사인 유사도
      목적사용자 및 아이템 간 유사성을 바탕으로 추천두 품목의 구매 패턴 방향성을 기반으로 유사성 측정
      장점데이터 패턴 분석을 통한 직관적 추천구매 금액의 크기보다 패턴에 집중, 희소 행렬에 적합
      사용 이유대규모 사용자와 다양한 아이템 조합을 다룰 수 있음효율적 계산과 사용자 구매 패턴 유사성 확인
  • 데이터 개요:

    image

    • 데이터 크기: 118,176개의 구매 기록과 10개의 특성 (성별, 나이, 구매금액 등)
    • 사용자 ID를 입력받아, 유사도 기반 상위 N개 추천 품목을 출력하는 방식
      • 사용자 ID는 주소, 성별, 나이를 결합해 생성
      • 구매 금액은 추천 점수 계산에 사용
  • 시각화 및 통찰: 추천 시스템의 구매 분석 결과

    • 성별 분석: 여성 사용자가 남성보다 높은 구매 금액을 기록
    • 연령대별 구매 경향: 30대 사용자층이 가장 높은 구매력을 보임
    • 시간대 분석: 주말 구매가 주중보다 많으며, 오후 구매가 오전보다 높음
    • 품목별 구매 경향: 특정 품목 카테고리의 구매 금액 비중이 높음
    • 월별 매출 추이: 계절별 매출 변화 확인
    • 고빈도 고객 분석: 상위 5명의 고빈도 구매 고객 식별 → 활용 방안 💡 : 해당 고객을 타겟으로 한 로열티 프로그램 기획 가능
  • 성능 평가 결과

    • Precision@K: 추천 품목 중 실제 구매한 품목 비율
    • Recall@K: 실제 구매 품목 중 추천된 품목 비율
    • F1-Score: Precision과 Recall의 조화 평균
    • 결과: 우수한 Precision@3(0.8020 → 80% 정확도)를 보임
  • 팝업 스토어 예약 시스템 적용

    • 사용자 정보(지역, 성별, 나이)를 통해 사용자 ID 생성 및 추천 결과 반환
    • 추천 결과를 통해 사용자에 맞는 팝업 스토어를 화면에 띄워 사용자의 클릭 유도

예약 서비스

  • List와 Set을 결합하여 Redis 대기열 사용
  • Redis Pub/Sub를 사용하여 예약 처리 자동화
  • 날짜와 시간대 별로 정해진 인원수만 선착순으로 처리
  • Redis에서 예약 가능한 인원수 확인
  • 대기열에서 순서대로 예약을 처리하고 슬롯 데이터 업데이트
  • Redis 키에 TTL을 설정하여 예약 가능 기간 종료 후 데이터 자동 삭제
  • 예약 시 중복 여부를 확인해 무분별한 요청 방지

어플리케이션 구성(100명이 요청하는 경우)

  1. 100명의 사용자가 특정 날짜의 특정 시간대에 예약 요청
  2. 100명의 사용자는 요청 순서대로 대기열에 등록
    • 중복으로 예약 요청을 넣을 수 없도록 Redis에 등록
  3. 스케줄러가 동작하여 대기열에 있는 사용자 예약 처리
  4. 성공 시 선착순으로 10명씩 예약 성공
  5. 실패 시 “예약이 마감되었습니다.”라는 응답 반환
  6. 대기 시간 동안 현재 자신의 순번과 뒤에 남은 사람 수 확인 가능
  7. 위의 과정을 통해 이벤트 종료(특정 시간대에 10명 예약 성공)

채팅 서비스

  • WebSocket을 활용한 실시간 채팅 기능
    • WebSocket을 사용하여 클라이언트와 서버 간 실시간 양방향 통신 구현
    • Spring WebSocket 을 통해 채팅 메시지의 입장, 퇴장, 채팅 내용을 실시간으로 주고받을 수 있도록 처리
  • WebSocket 연결 및 메시지 전송 및 저장 흐름
    • 클라이언트가 WebSocket 연결을 요청하면, WebSocketHandler 에서 연결을 수립하고, 연결 성공 메시지를 클라이언트에 전송
    • 클라이언트에서 채팅 메시지를 전송하면, 이를 ChatMessageDto 객체로 변환하여 처리하고, 메시지 유형(입장, 채팅, 퇴장)에 맞게 적절한 처리를 수행
    • 입장 (JOIN) 메시지는 해당 팝업 스토어에 참여한 세션을 관리하는 popupStoreSessions 맵에 세션을 추가
    • 채팅 (TALK) 메시지는 Message 객체로 변환하여 DB에 저장하고, 모든 연결된 클라이언트에 실시간으로 메시지를 전송
    • 퇴장 (LEAVE) 메시지는 해당 팝업 스토어의 세션에서 연결을 종료
  • 채팅 메시지 조회
    • 해당 스토어에 속한 모든 메시지를 조회하여 반환

검색 서비스

  • QueryDSL을 사용해 팝업스토어 검색 기능 구현
    • 이름, 팝업스토어 시작일, 마감일, 위치 값을 쿼리스트링으로 입력해 검색
    • 카테고리 값을 쿼리스트링으로 입력해 검색

기능 시연


성능 테스트


1. 스모크 테스트

목적: 주요 기능이 정상 작동하는지 확인하는 간단한 테스트

  1. 가상 사용자 수: 50명
    • 최소한의 사용자 수로 주요 기능을 빠르게 확인
  2. 지속시간: 3분
    • 시스템이 정상 동작을 보장하는지 빠르게 검증
  3. 피크타임, 램프업, 다운 여부 및 조건:
    • 램프업: 1분 동안 0명 → 50명
    • 피크타임: 1분
    • 다운: 1분 동안 50명 → 0명
  4. 사용 시나리오:
    • 팝업 스토어 정보 전체 조회: 데이터를 정상적으로 불러오는지 확인
    • 예약 신청: 예약 데이터가 정상적으로 들어가는지 확인
    • 현재 순번 확인: 예약 관련 데이터가 올바르게 표시되는지 검증

image

  • 응답 성공률: 100%
    • 주요 기능의 정상 작동 확인
  • http_req_duration 평균 요청 시간: 18.28ms
  • http_reqs 초당 요청 처리량: 초당 95건
  • iteration_duration 평균 반복 시간: 1.05s

2. 부하 테스트

목적: 시스템의 최대 처리량을 파악하고 성능 병목 현상을 진단

  1. 가상 사용자 수: 5000명
    • 예상되는 최대 사용량 근처에서 테스트
  2. 지속시간: 10분
    • 실제 사용 환경을 가정해 안정성을 확인
  3. 피크타임, 램프업, 다운 여부 및 조건:
    • 램프업: 2분 동안 0명 → 5000명
    • 피크타임: 6분
    • 다운: 2분 동안 5000명 → 0명
  4. 사용 시나리오:
    • 팝업 스토어 정보 전체 조회: 데이터 조회 요청이 많은 상황에서 성능 확인
    • 검색: 다수의 검색 요청을 처리할 수 있는지 검증
    • 팝업 스토어 정보 상세 조회: 여러 데이터 요청에 대한 복원력 확인
    • 예약 신청: 동시다발적인 예약 생성 요청 처리 여부 확인

image

  • 응답 성공률: 99.98%
  • http_req_duration 평균 요청 시간: 6.17s
  • http_reqs 초당 요청 처리량: 초당 362건
  • iteration_duration 평균 반복 시간: 31.28s

3. 스파이크 테스트

목적: 짧은 시간 동안 갑작스러운 사용량 증가에 대한 시스템의 복원력 확인

  1. 가상 사용자 수: 10000명
    • 극단적인 부하 상황을 가정
  2. 지속시간: 3분
    • 단시간 내 급격한 부하 처리 및 안정성 확인
  3. 피크타임, 램프업, 다운 여부 및 조건:
    • 램프업: 1분 동안 0명 → 10000명
    • 피크타임: 1분
    • 다운: 1분 동안 10000명 → 0명
  4. 사용 시나리오:
    • 검색: 갑작스러운 검색 요청 폭주 시 성능 확인
    • 팝업 스토어 정보 상세 조회: 여러 데이터 요청에 대한 복원력 확인
    • 예약 신청: 갑작스러운 예약 요청 폭주 시 안정성 검증
  • redis pub/sub 적용 전

    image

  • redis pub/sub 적용 후

    image

  • redis pub/sub 적용으로 유의미한 성능 개선 확인

  • 응답 수신 시간(http_req_receiving) 크게 개선

    http_req_receiving적용 전적용 후
    avg1.36ms708.93μs
  • 서버-클라이언트 간 불필요한 전송 데이터 양 감소

    적용 전적용 후
    data_received208MB94MB

개선할 점


  1. 팝업 스토어 전체 조회 Redis 사용 전략 설정
    • 도입 배경
      • 기존 DB 직접 조회 방식에서, “connection timeout” 오류 발생:
      • read tcp 192.168.1.6:58959->15.164.35.224:80: wsarecv: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond."
      • 원인은 대량 조회로 인한 DB 부하로 추정, 이를 해결하기 위해 캐시(Redis)를 통한 조회 도입
        • 변경 내용: 팝업 스토어 등록 시 캐시에 저장 → 전체 조회는 캐시에서 처리
    • 문제점
      • 캐시 미스 시 DB 조회 및 캐시 저장 과정 추가로, 단순 DB 조회보다 비효율적, 성능 저하
    • 개선 방향
      • 캐시 도입시 캐시 전략 최적화 필요
        • 자주 조회되는 데이터 우선 캐싱
        • 캐시 적중률 향상을 위한 TTL(Time-to-Live) 및 데이터 구조 개선
  2. EC2 접근을 위해 Bastion host 사용
    • Bastion host 대신 System Manager Session Manager 도입을 통해 보다 더 안전한 접근 관리 필요
  3. CI/CD 파이프라인 부재
    • 현재 배포 방식: 빌드 파일 생성 및 Filezilla를 통한 수동 배포
    • 적절한 CI/CD 파이프라인 구축 필요
      • github action + ECR 등의 파이프라인 구축 가능
  4. Auto Scaling 정책 부재
    • 정책 설정의 필요성
      • 성능 테스트 시 인스턴스의 컴퓨터 자원에 따라 결과가 다르게 나타남
      • 적절한 동적 크기 조정 정책 필요
        • Auto Scaling Group의 CPUUtilization을 경보로 설정한 단계 크기 정책 필요

개선사항


백엔드 개선

  • 검색 기능
    • category 검색 기능인덱스 설정
    • no offset 적용
  • 채팅 기능
    • 웹소켓 기반 단일 서버 구조 → Redis Pub/Sub 적용
      • 다중 서버 간 메시지 동기화 가능
  • 팝업 스토어 전체 정보 조회 기능
    • no offset 적용
  • 예약 기능
    • 지수 백오프 알고리즘 추가
      • 예약 데이터 저장 실패 가능성 고려
    • Redis Pub/Sub 적용
      • 기존의 스케줄러 방식보다 실시간성 개선

인프라 개선

image

  • Route53 + ACM 적용

  • ECS 클러스터 도입

    • CI/CD 파이프라인 구축
    • 컨테이너화
  • Bastion host → Instance connect endpoint 수정

    • Session Manager는 비용이 많이 들어 대신 Instance connect endpoint를 도입하기로 결정
    • Bastion host보더 더 안전하게 접근
  • CloudWatch 지표 + 경보 설정

    • ECS 컨테이너 인스턴스(EC2)에 CloudWatch Agent 설치 및 메모리 지표 확인
    • ECS Cluster의 CPUUtilization 지표를 통한 경보 설정
      경보지표시간조건
      scale-outECS CPUUtilization1분80% 이상 (최대), 2번 체크
      scale-inECS CPUUtilization10분10% 이하 (평균)
  • Auto Scaling 크기 조정 정책 설정

    • CloudWatch 경보를 바탕으로 EC2 ASG, ECS Service에 단계 조정 정책 설정