DDWU ACC 2nd Final Project Team 1 west-somsom (서쪽솜솜) https://github.com/orgs/west-somsom/repositories
프로젝트 소개
팝업 스토어를 이용하려는 사용자가 다양한 정보를 탐색하고 예약 및 소통을 원활하게 진행할 수 있도록 지원하는 통합 플랫폼
주요 기능
- 사용자 관리
- 카카오 소셜 로그인을 통한 간편 로그인
- 사용자 정보 등록, 조회 및 수정
- 팝업 스토어 정보 관리
- 팝업 스토어 등록
- 팝업 스토어 정보 전체 목록 조회 및 상세 조회
- 이름 및 카테고리 기반 검색
- 채팅을 통한 소통 지원
- 예약 등록 시점에 채팅방 함께 개설
- 팝업 스토어 당 한 개의 1 : N 채팅방
- 텍스트 채팅
- 예약 관리
- 예약 신청
- 예약 중 사용자의 현재 순번 실시간 확인
- 예약 내역 조회
- 예약 취소 및 삭제
- 알림 전송
- 예약 즉시 예약 확인 이메일 알림 전송
- 예약일 하루 전(오후 3시) 예약 확인 알림 전송
- 아이템 기반 사용자 맞춤형 추천
- 사용자 프로필 및 카드 결제 내역 데이터를 기반으로 개인화된 팝업 스토어 추천 제공
아키텍처 설계
- 설계 핵심
- 팝업 스토어 예약 서비스는 예약 오픈 시점에 대량의 사용자 접속 발생
- 특정 기간 동안 사용자 트래픽 폭증 → 트래픽 부하 분산 및 관리 필요
- 사용자 데이터의 무결성과 서비스 안정성을 보장하기 위해 Multi-AZ 기반의 이중화 구성이 필요
- 팝업 스토어 예약 서비스는 예약 오픈 시점에 대량의 사용자 접속 발생
아키텍처 설계 및 구성
Infra

전반적인 구성
- Public과 Private으로 분리하여 외부 사용자가 접근하는 네트워크와 데이터가 저장된 네트워크 분리
- Public Subnet
- 외부 트래픽 처리
- Private Subnet에 위치한 인스턴스 접근을 위해 Bastion host 설정
- Private Subnet에 위치한 인스턴스의 인터넷 사용을 위해 NAT Instance 설정
- Private Subnet
- 데이터베이스와 캐시, 애플리케이션 서버 등 민감한 데이터가 저장된 자원을 배치
- Bastion Host를 통해 간접적으로 접근 가능
- Public Subnet
- 트래픽 분산
- 서비스의 안정성을 위해 2개의 가용 영역 사용
- ALB를 통한 인스턴스 부하 분산
- Auto Scaling 설정 및 ALB 대상 그룹 지정
기능
팝업 스토어 정보 등록 및 조회 기능 구현
- Redis OSS with ElastiCache 활용
- 등록 기능
- 팝업 스토어 정보를 DB와 Redis에 동시에 저장하여 조회 속도 최적화
- 등록 시, Redis 캐시에 중복 데이터가 저장되지 않도록 유효성 검사를 추가로 수행
- 조회 기능
- 전체 정보 조회 시 Redis에서 우선 데이터 조회
- 캐시 미스 발생 시 DB에서 데이터를 조회하고, 이를 Redis에 저장
- 등록 기능
- 현재 채택 방식
- 조회 기능은 RDS 직접 조회 방식을 사용 중으로, 캐시를 활용한 방식은 향후 효율성을 검토해 개선 예정
- Redis OSS with ElastiCache 활용
예약 알림 기능 구현
- 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의
- 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 중복 확인 데이터에서 제거
- Redis OSS with ElastiCache 사용
Back-End
소셜 로그인 서비스
- 카카오 오픈 API를 활용한 소셜 로그인/로그아웃 구현
- 흐름
- 로그인 흐름: 카카오 로그인 요청 → 콜백으로 인증 코드 수신 → 액세스 토큰 발급 → 사용자 정보 조회 및 저장
- 정보 업데이트: API를 통해 사용자 정보를 수정 및 유지
- 카카오 인증 및 사용자 정보 관리
- 카카오 로그인 URL 생성 : https://kauth.kakao.com/oauth/authorize를 활용해 로그인 요청 URL 생성
- 액세스 토큰 발급 : 사용자 인증 후 받은 code로 카카오 API에서 토큰 요청
- 사용자 정보 조회 : 액세스 토큰으로 사용자 정보 조회 후 DB에서 저장 및 세션 생성
- 콜백 및 사용자 정보 갱신
- 카카오 로그인 콜백: 토큰 및 사용자 정보 처리 후 성공 메시지 반환
- 사용자 정보 업데이트: 전화번호, 성별, 나이 등 사용자 정보를 업데이트(추가적으로 입력받음)하고 DB 및 세션 동기화
- 사용자 정보 API
- 사용자 정보 조회: 특정 사용자의 정보를 DB에서 검색 및 반환
- 사용자 정보 수정: 입력된 정보만 선택적으로 업데이트
추천 서비스
구매 데이터 기반 팝업 스토어 추천
접근 방식:
- 협업 필터링(collaborative filtering)과 아이템 기반 코사인 유사도(cosine similarity) 활용
- Precision@K, Recall@K, F1-Score 등의 평가 지표를 사용하여 성능 검증
협업 필터링 코사인 유사도 목적 사용자 및 아이템 간 유사성을 바탕으로 추천 두 품목의 구매 패턴 방향성을 기반으로 유사성 측정 장점 데이터 패턴 분석을 통한 직관적 추천 구매 금액의 크기보다 패턴에 집중, 희소 행렬에 적합 사용 이유 대규모 사용자와 다양한 아이템 조합을 다룰 수 있음 효율적 계산과 사용자 구매 패턴 유사성 확인
데이터 개요:

- 데이터 크기: 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명이 요청하는 경우)
- 100명의 사용자가 특정 날짜의 특정 시간대에 예약 요청
- 100명의 사용자는 요청 순서대로 대기열에 등록
- 중복으로 예약 요청을 넣을 수 없도록 Redis에 등록
- 스케줄러가 동작하여 대기열에 있는 사용자 예약 처리
- 성공 시 선착순으로 10명씩 예약 성공
- 실패 시 “예약이 마감되었습니다.”라는 응답 반환
- 대기 시간 동안 현재 자신의 순번과 뒤에 남은 사람 수 확인 가능
- 위의 과정을 통해 이벤트 종료(특정 시간대에 10명 예약 성공)
채팅 서비스
- WebSocket을 활용한 실시간 채팅 기능
- WebSocket을 사용하여 클라이언트와 서버 간 실시간 양방향 통신 구현
Spring WebSocket을 통해 채팅 메시지의 입장, 퇴장, 채팅 내용을 실시간으로 주고받을 수 있도록 처리
- WebSocket 연결 및 메시지 전송 및 저장 흐름
- 클라이언트가 WebSocket 연결을 요청하면,
WebSocketHandler에서 연결을 수립하고, 연결 성공 메시지를 클라이언트에 전송 - 클라이언트에서 채팅 메시지를 전송하면, 이를
ChatMessageDto객체로 변환하여 처리하고, 메시지 유형(입장, 채팅, 퇴장)에 맞게 적절한 처리를 수행 - 입장 (JOIN) 메시지는 해당 팝업 스토어에 참여한 세션을 관리하는
popupStoreSessions맵에 세션을 추가 - 채팅 (TALK) 메시지는 Message 객체로 변환하여 DB에 저장하고, 모든 연결된 클라이언트에 실시간으로 메시지를 전송
- 퇴장 (LEAVE) 메시지는 해당 팝업 스토어의 세션에서 연결을 종료
- 클라이언트가 WebSocket 연결을 요청하면,
- 채팅 메시지 조회
- 해당 스토어에 속한 모든 메시지를 조회하여 반환
검색 서비스
- QueryDSL을 사용해 팝업스토어 검색 기능 구현
- 이름, 팝업스토어 시작일, 마감일, 위치 값을 쿼리스트링으로 입력해 검색
- 카테고리 값을 쿼리스트링으로 입력해 검색
기능 시연
성능 테스트
1. 스모크 테스트
목적: 주요 기능이 정상 작동하는지 확인하는 간단한 테스트
- 가상 사용자 수: 50명
- 최소한의 사용자 수로 주요 기능을 빠르게 확인
- 지속시간: 3분
- 시스템이 정상 동작을 보장하는지 빠르게 검증
- 피크타임, 램프업, 다운 여부 및 조건:
- 램프업: 1분 동안 0명 → 50명
- 피크타임: 1분
- 다운: 1분 동안 50명 → 0명
- 사용 시나리오:
- 팝업 스토어 정보 전체 조회: 데이터를 정상적으로 불러오는지 확인
- 예약 신청: 예약 데이터가 정상적으로 들어가는지 확인
- 현재 순번 확인: 예약 관련 데이터가 올바르게 표시되는지 검증

- 응답 성공률: 100%
- 주요 기능의 정상 작동 확인
http_req_duration평균 요청 시간: 18.28mshttp_reqs초당 요청 처리량: 초당 95건iteration_duration평균 반복 시간: 1.05s
2. 부하 테스트
목적: 시스템의 최대 처리량을 파악하고 성능 병목 현상을 진단
- 가상 사용자 수: 5000명
- 예상되는 최대 사용량 근처에서 테스트
- 지속시간: 10분
- 실제 사용 환경을 가정해 안정성을 확인
- 피크타임, 램프업, 다운 여부 및 조건:
- 램프업: 2분 동안 0명 → 5000명
- 피크타임: 6분
- 다운: 2분 동안 5000명 → 0명
- 사용 시나리오:
- 팝업 스토어 정보 전체 조회: 데이터 조회 요청이 많은 상황에서 성능 확인
- 검색: 다수의 검색 요청을 처리할 수 있는지 검증
- 팝업 스토어 정보 상세 조회: 여러 데이터 요청에 대한 복원력 확인
- 예약 신청: 동시다발적인 예약 생성 요청 처리 여부 확인

- 응답 성공률: 99.98%
http_req_duration평균 요청 시간: 6.17shttp_reqs초당 요청 처리량: 초당 362건iteration_duration평균 반복 시간: 31.28s
3. 스파이크 테스트
목적: 짧은 시간 동안 갑작스러운 사용량 증가에 대한 시스템의 복원력 확인
- 가상 사용자 수: 10000명
- 극단적인 부하 상황을 가정
- 지속시간: 3분
- 단시간 내 급격한 부하 처리 및 안정성 확인
- 피크타임, 램프업, 다운 여부 및 조건:
- 램프업: 1분 동안 0명 → 10000명
- 피크타임: 1분
- 다운: 1분 동안 10000명 → 0명
- 사용 시나리오:
- 검색: 갑작스러운 검색 요청 폭주 시 성능 확인
- 팝업 스토어 정보 상세 조회: 여러 데이터 요청에 대한 복원력 확인
- 예약 신청: 갑작스러운 예약 요청 폭주 시 안정성 검증
redis pub/sub 적용 전

redis pub/sub 적용 후

redis pub/sub 적용으로 유의미한 성능 개선 확인
응답 수신 시간(http_req_receiving) 크게 개선
http_req_receiving 적용 전 적용 후 avg 1.36ms 708.93μs 서버-클라이언트 간 불필요한 전송 데이터 양 감소
적용 전 적용 후 data_received 208MB 94MB
개선할 점
- 팝업 스토어 전체 조회 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) 및 데이터 구조 개선
- 캐시 도입시 캐시 전략 최적화 필요
- 도입 배경
- EC2 접근을 위해 Bastion host 사용
- Bastion host 대신 System Manager Session Manager 도입을 통해 보다 더 안전한 접근 관리 필요
- CI/CD 파이프라인 부재
- 현재 배포 방식: 빌드 파일 생성 및 Filezilla를 통한 수동 배포
- 적절한 CI/CD 파이프라인 구축 필요
- github action + ECR 등의 파이프라인 구축 가능
- Auto Scaling 정책 부재
- 정책 설정의 필요성
- 성능 테스트 시 인스턴스의 컴퓨터 자원에 따라 결과가 다르게 나타남
- 적절한 동적 크기 조정 정책 필요
- Auto Scaling Group의 CPUUtilization을 경보로 설정한 단계 크기 정책 필요
- 정책 설정의 필요성
개선사항
백엔드 개선
- 검색 기능
- category 검색 기능인덱스 설정
- no offset 적용
- 채팅 기능
- 웹소켓 기반 단일 서버 구조 → Redis Pub/Sub 적용
- 다중 서버 간 메시지 동기화 가능
- 웹소켓 기반 단일 서버 구조 → Redis Pub/Sub 적용
- 팝업 스토어 전체 정보 조회 기능
- no offset 적용
- 예약 기능
- 지수 백오프 알고리즘 추가
- 예약 데이터 저장 실패 가능성 고려
- Redis Pub/Sub 적용
- 기존의 스케줄러 방식보다 실시간성 개선
- 지수 백오프 알고리즘 추가
인프라 개선

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-out ECS CPUUtilization 1분 80% 이상 (최대), 2번 체크 scale-in ECS CPUUtilization 10분 10% 이하 (평균)
Auto Scaling 크기 조정 정책 설정
- CloudWatch 경보를 바탕으로 EC2 ASG, ECS Service에 단계 조정 정책 설정