
프로젝트 소개
우아한테크코스 4기에서 진행한 사이드 프로젝트로, 캠퍼스 주변 음식점에 대해 후기와 별점을 남기고 확인할 수 있는 서비스입니다.
프로젝트 기간
2022.05. ~
프로젝트 주소
https://matzip.today/
참여 인력
FE 3명 / BE 3명
사용 기술
- Java 11
- Spring Boot, Spring MVC, Spring REST Docs
- Spring Data JPA
- MySQL, H2
- OAuth 2.0, JWT
- nginx, AWS EC2, AWS RDS
- GitHub Actions
프로젝트 진행 내용
1. 프로젝트 아이디어를 제공하고 구체적인 서비스 기획
문제 상황
- 팀 프로젝트 기간으로 인해 크루들이 등교하는 캠퍼스를 옮기게 되면서 점심 및 저녁 메뉴에 대한 고민이 많아짐
- 네이버 지도나 카카오 지도를 통해서는 너무 많은 데이터가 뜨며, 네이버 리뷰는 광고 및 어뷰징의 문제, 카카오 리뷰는 전체적인 평점이 너무 낮다는 점에서 불편하다는 문제 인식
문제 해결
- 크루들의 입맛에 맞는 리뷰를 직접 작성하고 확인할 수 있는 서비스가 있다면 좋겠다는 의도에서 서비스 기획
- 최소 기능 제품(Minimum Viable Product; MVP) 모델로 기획
- 기획 시점부터 캠퍼스 이동 시기까지 한 달여 밖에 남지 않은 상황에서, 최소 기능만 구현하여 서비스 출시 뒤 피드백을 통해 문제점을 개선해 나가는 것을 제안
- 리뷰 및 별점을 남기고 확인할 수 있는 기능이 서비스를 구성하는 핵심 기능이므로, 해당 기능을 최소 기능으로 하여 서비스 v1.0.0 출시
- 이후 크루들의 사용 후기와 피드백을 반영하여 v1.1.0 출시
상세 내용 링크
블로그 - MAT.ZIP 개발기
2. 멀티 모듈 프로젝트 구성
문제 상황
- 기존 프로젝트는 모놀리식 단일 모듈 프로젝트
- 패키지 구조만 나누어도 의존성 분리를 할 수 있지만 개발 과정에서 의도치 않게 의존방향이 무너질 수 있음
- 의존방향은 웹 계층에서 도메인 계층을 향하는 방향으로 흘러야 하는데 단일 모듈 프로젝트에서는 반대 방향으로 의존하는 것을 강제로 막을 방법이 존재하지 않음
- 배치 시스템, admin api와 같은 다른 애플리케이션을 추가하려면 도메인 로직을 중복 작성해야 함
문제 해결
3. API 요청 당 쿼리 개수를 세는 쿼리 카운터 구현
문제 상황
- 하이버네이트 로그만으로는 API 요청 당 몇 개의 쿼리가 날아가는지 확인하기 어려움
- 인지하지 못한 N+1 문제 등을 캐치하는데 애로사항이 존재

문제 해결
- 쿼리가 실행될 때마다 count 값을 증가시키는 카운터 구현
- JPA가 아닌 다른 방식으로 쿼리를 작성하더라도 개수를 셀 수 있도록 JDBC, Dynamic Proxy, Spring AOP를 활용하여 쿼리 카운터를 구현
- 요청이 들어오면
@RequestScope
로 설정된 QueryCounter 빈이 생성되고, DataSource에 AOP 적용
- Connection, PreparedStatement는 스프링 빈이 아니므로 AOP 적용 불가
- JDK Dyamic Proxy를 이용하여 쿼리 개수를 세는 기능 구현
- DataSource에서 Connection을, Connection에서 PreparedStatement를 생성할 때 Dynamic Proxy를 반환하도록 구현
- PreparedStatement의 쿼리 실행 메서드들에 대해 Dynamic Proxy로 QueryCounter의 count 값을 증가시키는 로직 구현
상세 내용 링크
블로그 - JDBC, AOP, Dynamic Proxy를 이용한 쿼리 카운팅
4. 도메인 이벤트를 사용하여 의존성 및 트랜잭션이 관심사에 맞게 분리되도록 개선
문제 상황
- 음식점에 대한 리뷰를 작성, 수정, 삭제하는 메서드가 음식점의 리뷰 개수 및 평점을 수정하는 로직도 함께 가지고 있음
- 한 트랜잭션에 두 개의 관심사가 합쳐져 있음
- 리뷰에 대한한다는 메서드가 음식점의 정보를 수정한다는 로직도 알아야 함
- 리뷰를 작성, 수정, 삭제하는 것이 주 관심사인 메서드인데 음식점 수정에 실패하면 리뷰를 작성하는 로직도 롤백됨
- 음식점의 정보를 수정하는 로직에서 음식점에 대한 데이터베이스 락도 획득함
문제 해결
- 이벤트를 사용하여 리뷰 작성, 수정, 삭제 메서드에서 음식점 관련 로직 분리
- 리뷰 작성, 수정, 삭제에 대한 도메인 이벤트를 발행
- Spring Data JPA의 AbstractAggregateRoot 활용
- 별도의 도메인 메서드 호출이 없는 리뷰 삭제의 경우 @PreUpdate 어노테이션을 활용하여 삭제 이전 이벤트가 등록되도록 구현
- 음식점 쪽에서 리뷰에 대한 이벤트를 구독하고 음식점 정보 수정 로직을 실행
- 비동기 처리하여 트랜잭션을 완전히 분리하고 API 응답 시간을 줄임
상세 내용 링크
블로그 - 도메인 이벤트 기반 MAT.ZIP 프로젝트 개선기