Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
234 changes: 0 additions & 234 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@

Select Header


<div align="center">
<img width="1000" alt="Image" src="https://github.com/user-attachments/assets/c858f5b3-6ea5-4e64-a9ed-9f43c498585e" />
</div>
Expand Down Expand Up @@ -350,7 +347,6 @@ DB의 Pessimistic Lock (비관적 락) 을 활용하여 쿠폰 재고를 안전

<br>


## 📖 회고

🐣 [조예인](https://codingtrip.tistory.com) : 긴 여정을 좋은 튜터님, 팀원분들을 만나 잘 마무리할 수 있었습니다. 정말 감사합니다. 프로젝트 이후에도 같이 만나서복습, 개선 및 발전하고 싶습니다. <p>
Expand All @@ -360,233 +356,3 @@ DB의 Pessimistic Lock (비관적 락) 을 활용하여 쿠폰 재고를 안전
🎅 [장윤혁](https://velog.io/@hyuk905/posts) : 최종 프로젝트인 만큼 좋은 팀원들과 함께 여러 가지 시도를 다양하게 해 볼 수 있어서 좋았습니다. 제가 구현하지 못한 부분에 대해서 복습하며 직접 구현해 볼 예정입니다.<p>

</div>

Image
🅿️ ParkEZ: 쉽고 빠른 통합 주차 플랫폼 팀 프로젝트
🙋‍♀️ ParkEZ 팀 프로젝트 설명
ParkEZ 팀 프로젝트는 사용자와 주차장 소유자를 위한 예약, 결제, 정산이 가능한 통합 주차 플랫폼을 구현한 팀 프로젝트입니다.
팀원 : 조예인, 정준호, 이민정, 전서연, 장윤혁
기간 : 2025.04.01 - 2025.05.06

📑 팀 노션 : ParkEZ

📑 팀 브로셔 : 10조 - Park10EZ

📑 도메인 : parkez.click

📑 시연영상 : ParkEZ 시연영상


🧑‍🧑‍🧒‍🧒 역할 분담
🐣 조예인 : 주차공간 API, 리뷰 API, Redis Pub/Sub을 이용한 메일 전송 기능

🦦 정준호 : 유저 API, JWT 인증/인가, 카카오 로그인 기능, 프로모션 API + 동시성 제어 적용, AWS 아키텍처 구성, GitHub Actions 활용한 CI/CD

🐶️ 이민정 : 결제 API, 정산 API, AWS S3 이미지 기능, Toss payments API 연동, Redis를 이용한 예약 대기열 기능

🗿 전서연 : 주차장 API, 네이버 로그인 기능, AWS Lambda를 이용한 공공데이터 저장, 카카오맵 API 연동하여 주소 저장

🎅 장윤혁 : 예약 API + 동시성 제어 적용, 주차장 다건 조회 캐싱, Spring Batch를 이용한 정산 기능


🛠 목차
🧰 기술스택
🗂 시스템 아키텍처
💿 CI/CD
🧩 와이어프레임
🧾 ERD
📡 API 명세
🚀 기술 고도화
🧪 성능 테스트
📈 테스트 커버리지
🧯 트러블슈팅
🔭 향후 발전 방향
📖 회고

🧰 기술스택
💻 Language / Backend


⚙️ Database


🔍 Test


🔐 Security


🎨 Collaboration Tool


🛠 Deployment & Distribution





🗂 시스템 아키텍처
ParkEZ 시스템 아키텍처 이미지
💿 CI/CD
ParkEZ CICD
🧩 와이어프레임
ParkEZ 와이어프레임
🧾 ERD
Image
📡 API 명세
📑 Swagger 참조 : parkez.click


🚀 기술 고도화
☁️ AWS Lambda로 외부 API 호출 자동화
💰 Spring Batch 기반 정산 처리
📧 Redis Pub/Sub + Amazon SES 기반 메일 전송

🧪 성능 테스트
예약 생성 시 동시성 제어 테스트
테스트 시나리오
총 10명의 사용자가 동시에 예약 요청
응답 메시지
"예약 성공" : 실제 예약 완료
"대기열 등록됨" : 예약 실패 후 대기열 등록
🔍 테스트 결과
구분 응답 결과
✅ 동시성 제어 이전 10명 모두 "예약 성공" → 중복 예약 발생
✅ 동시성 제어 이후 1명 "예약 성공" + 9명 "대기열 등록됨" → 정상 처리됨
결론: 동시성 제어를 통해 하나의 주차 공간에 대한 중복 예약을 방지하고, 후순위 사용자를 대기열에 안전하게 등록할 수 있도록 개선되었습니다.
주차장 조회 성능 테스트
📊 JMeter를 활용한 주차장 조회 성능 테스트

🧪 테스트 개요
목표: Redis 캐시 도입 전/후 성능 비교
대상: 약 10만 건의 주차장 데이터
도구: Apache JMeter
조건: 동일한 Thread 수, Ramp-up 시간, Delay 설정
📈 테스트 결과
항목 캐시 적용 전 캐시 적용 후 변화율
Throughput 4.2/sec 31.9/sec 🔼 +659.5% 증가
평균 응답시간 61,641 ms 15,678 ms 🔽 -74.6% 감소
✅ 분석
Redis 캐시 적용으로 처리량(Throughput)이 6배 이상 증가
응답시간이 1/4 수준으로 단축되어 사용자 경험 개선
대용량 데이터에 대해 캐시 적용 시 확연한 성능 향상 확인

📈 테스트 커버리지
ParkEZ 테스트 커버리지 이미지
🧯 트러블슈팅
1. OSIV 설정 차이로 인한 상태 변경 미반영 문제
❗ 문제 상황

로컬에서는 정상 동작하던 예약 상태 변경 기능이, 개발 서버에서는 DB에 반영되지 않음
예시: reservation.cancel() 호출 후에도 ReservationStatus.CANCELED가 DB에 반영되지 않음
🔍 원인 분석

OSIV 설정 차이

로컬: spring.jpa.open-in-view=true (기본값)
→ 트랜잭션 종료 이후에도 영속성 컨텍스트 접근 가능
개발 서버: open-in-view=false
→ 트랜잭션 종료 시 영속성 컨텍스트도 종료
구조적 문제

@Transactional(readOnly = true)가 적용된 Reader에서 조회한 엔티티는 Detached 상태일 수 있음
이후 Writer에서 상태 변경 메서드만 호출하면 JPA의 dirty checking이 작동하지 않음
// ReservationService
Reservation reservation = reservationReader.findMyReservation(...); // ReadOnly 트랜잭션
reservationWriter.cancel(reservation); // 내부에서 reservation.cancel() 호출 → 변경 감지 안 됨
✅ 해결 방안

✅ 단기 해결: 명시적 save 호출

public void cancel(Reservation reservation) {
reservation.cancel();
reservationRepository.save(reservation); // Detached 객체 merge
}
✅ 근본적 해결: 트랜잭션 범위 재설계

// ReservationService
@Transactional
public void cancelReservation(...) {
Reservation reservation = reservationReader.findMyReservation(...);
reservation.cancel(); // 영속 상태에서 변경 → dirty checking 작동
}
🎯 결과

개발 환경에서도 예약 상태 변경이 정상 반영됨
구조적으로 역할 분리가 명확해짐:
Reader → 조회 책임
Writer → 도메인 변경 책임
Service → 트랜잭션 관리 및 흐름 조율
2. 동시성 테스트 - 커넥션 풀 고갈 발생
🔍 문제 발견

동시성 테스트 수행 중, 테스트가 끝나지 않고 대기 상태 지속
커넥션 풀 고갈로 인한 타임아웃 현상 발생
⚠️ 원인 분석

테스트 메서드에 @Transactional이 적용되어 전체 테스트가 하나의 트랜잭션으로 실행됨
내부 메서드도 동일 트랜잭션에 묶여 커밋/롤백 지연
결과적으로 DB 락이 해제되지 않고, 모든 스레드가 락 대기 상태에 빠짐
커넥션 풀 부족 → 새로운 커넥션 생성 불가 → 테스트 타임아웃
✅ 해결 방안 및 결과

@Transactional 어노테이션 제거
각 스레드가 독립된 트랜잭션으로 실행되어 DB 락 정상 해제
테스트 종료 후 deleteAllInBatch() 사용
테스트 간 데이터 잔존 문제 방지
트랜잭션 제거로 인한 데이터 정리를 명시적으로 수행

🔭 향후 발전 방향
🎟️ 프로모션 쿠폰 발급 처리 전략
✅ 현재 구조 DB의 Pessimistic Lock (비관적 락) 을 활용하여 쿠폰 재고를 안전하게 제어하고 있습니다.

장점

다중 사용자 환경에서도 중복 발급 없이 재고 제어 가능
단점

특정 프로모션 발급 요청이 몰리면 해당 레코드에 락이 걸려
→ 다른 트랜잭션이 지연되고
→ 쿠폰 조회 및 관련 로직에 병목이 발생할 수 있음
🚧 개선 방향

🔐 분산락 적용 예정
목표: 특정 프로모션 단위로 분산락 적용 (ex. Redis 기반)
효과:
발급에만 락을 제한하고 조회는 락 영향 없이 처리 가능
DB 레벨 병목 없이 확장성 높은 동시성 제어 가능
⚡ 발급과 관리 분리
발급 요청은 동기 처리, 사용자에게 빠른 응답 제공
발급 기록은 비동기 처리, 시스템 부하 분산
예시: Kafka, SQS 등 메시지 큐를 통해 처리 분리
🔔 알림 시스템 구조 개선 방향
✅ 현재 구조 현재 알림 시스템은 Redis Pub/Sub 기반으로, 이벤트 발생 시 알림 메시지를 발행하고, 구독자에서 이메일(SES)을 전송하는 구조입니다.
이 구조는 간단하고 빠르지만, 다음과 같은 운영상의 한계가 존재합니다.

❗ 문제점

📌 메시지 유실 가능성
Redis Pub/Sub은 실시간 메시지 전파만 지원하며, 메시지를 저장하지 않음
구독자가 다운된 경우 메시지가 유실되어 알림 손실 발생
📌 재처리 불가
알림 발송 실패 시 로그만 남고, 별도의 재시도 로직이 없어 운영 신뢰성 부족
✅ 개선 방향

📦 메시지 영속화 기반 구조 고려
Redis Streams 또는 Kafka 등 영속 메시징 큐로 교체 또는 보완 시스템 도입 예정
구독자 장애 시에도 재수신 및 복구 가능
🔁 실패 내역 저장 및 재처리 도입
발송 실패 이력을 Redis List 또는 DB Table 등에 저장
Scheduled Task 또는 Spring Batch를 활용한 재처리 구조 적용
장기적으로는 Kafka DLQ(Dead Letter Queue) 도입 고려

📖 회고
🐣 조예인 : 긴 여정을 좋은 튜터님, 팀원분들을 만나 잘 마무리할 수 있었습니다. 정말 감사합니다. 프로젝트 이후에도 같이 만나서복습, 개선 및 발전하고 싶습니다.

🦦 정준호 : 팀원들과의 지속적인 커뮤니케이션과 역할 분담을 통해 점차 안정적인 개발 흐름을 만들어갈 수 있었습니다. 짧은 기간이었지만 기술뿐만 아니라 협업 역량과 책임감까지 함께 키울 수 있었던 값진 시간이었습니다.

🐶️ 이민정 : 처음으로 기획부터 배포까지 해본 프로젝트인 만큼 어려운 점도 많았지만 배운 점도 많았던 시간이었습니다. 프로젝트는 끝났지만, 제가 구현하지 않은 부분에 대해서도꼭 복습하며 공부해야겠다고 생각했습니다. 좋은 팀원들과 튜터님과 소통하며 잘 마무리할 수 있어서 감사했습니다.

🗿 전서연 : 한 달 동안 최종 프로젝트를 진행하며 좋은 경험을 하게 되어 뜻 깊은 시간이었습니다. 어려운 부분이 많았지만 좋은 팀원과 튜터님 덕분에 잘 마무리 할 수 있게 되었습니다. 10조 취뽀 화이팅

🎅 장윤혁 : 최종 프로젝트인 만큼 좋은 팀원들과 함께 여러 가지 시도를 다양하게 해 볼 수 있어서 좋았습니다. 제가 구현하지 못한 부분에 대해서 복습하며 직접 구현해 볼 예정입니다.