1. 프로젝트 개요
- 제목: 덕후감(Deokhugam)
- 소개: 도서 이미지 OCR 및 ISBN 매칭 서비스
- 활용 기술 스택
- Java
- Spring Boot
- Spring Data JPA
- PostgreSQL
- AWS
- Docker
- Redis
- H2 Database
- Elasticsearch
- logstash
- Kafka
- Grafana
- Prometheus
2. 담당한 작업
- 리뷰 생성
- DB 레벨에서 유저 ID와 책 ID로 부분 유니크 인덱스를 적용하여 책 한권 당 리뷰를 하나 작성할 수 있게 구현
- 리뷰 수정
- 리뷰를 작성한 사용자만 리뷰를 수정할 수 있게 구현
- 리뷰 삭제
- 리뷰를 작성한 사용자만 리뷰를 삭제할 수 있게 구현
- 리뷰 목록 조회
- 기본 리뷰 목록 조회는 PostgreSQL과 QueryDsl로 구현
- 필터링 리뷰 목록 조회는 Elasticsearch의 nori + ngram을 커스텀하여 구현
- 커서 페이지네이션을 통해 무한 스크롤 기능 구현
- Elasticsearch의 highlighting을 추가하여 필터링이 정확하게 동작하는지 시각화할 수 있게 구현
- 리뷰 상세 목록
- 리뷰 ID를 조회하여 해당 리뷰를 보여줄 수 있게 구현
- 리뷰 좋아요
- 트래픽이 몰릴 수 있는 기능이므로 Kafka(비동기) + 비관적 락을 도입하여 대규모 데이터 환경에서도 정상 동작할 수 있도록 비동기 파이프라인 구현
3. 기술적 성과
- 동시성 제어를 위한 하이브리드 파이프라인 구축
- 결정한 이유
- DB레벨에서 비관적 락을 도입하여 리뷰 서비스에서 리뷰 좋아요, 논리 삭제, 수정 부분에서 동시성 제어를 구현하였지만 대용량 처리 시스템에서 한계가 있다고 생각함
- 트래픽 스파이크 시 DB 병목 현상 발생 가능성이 있음
- 기존 비관적 락은 정합성을 완벽히 보장하지만, 특정 리뷰에 트래픽이 집중됐을 경우 대기 상태에 빠지는 스레드가 급증하게 됨
- DB커넥션 풀 고갈과 전체 API 응답 지연으로 이어져, 리뷰 서비스 뿐만 아니라 시스템 전체에 장애로 번질 수 있음
- 해결 방안
- 동시성 제어를 위한 하이브리드 파이프라인 구축
- 결과
- 트래픽 완충
- 순간적으로 많은 양의 요청을 웹 어플리케이션 서바가 직접 DB에 삽입하는 대신, Kafka 메시지 큐로 발행하여 비동기 처리 구조로 전환
- 낙관적인 응답(사용자가 좋아요를 눌렀을 것이라 가정)하여 사용자는 지연 없이 빠른 응답을 받을 수 있음
- 파티셔닝을 통한 순서 보장
- Review ID를 Kafka의 파티션 키로 설정하여, 동일한 리뷰에 대한 변경 요청이 순차적으로 Consumer 에게 전달되도록 설계
- 최종 안전망으로서의 락 유지
- Kafka를 통해 트래픽을 제어하고 순서를 보장하더라도, 분산 환경의 네트워크 지연이나 Consumer 재균형 과정에서 발생할 수 있는 엣지 케이스를 원천 차단하기 위해 DB의 비관적 락을 최종 방어선으로 구축
- 관련 문서
- 올바른 필터링을 확인하는 Elasticsearch의 Highlighting 도입
- 결정한 이유
- 기존 RDBMS 방식은 검색어의 노출 위치를 파악하기 어려워 검색 결과의 신뢰성을 확인하기 불편하다는 단점이 있었습니다.
- 해결 방안
- 이를 해결하기 위해 Elasticsearch의 Highlighting 기능을 적용하여 검색어와 일치하는 부분을 시각적으로 강조하여 사용자가 검색 의도에 부합하는 결과를 즉각적으로 인지할 수 있도록 UX를 개선하였습니다.
- 결과
- 사용자뿐만 아니라 개발자 역시 매칭된 필드와 데이터를 실시간으로 확인가능하며, 검색 품질 이슈 발생 시 신속한 원인 파악과 트러블 슈팅이 가능해짐
- 해결 방안
4. 문제점 및 해결 과정
문제 1: 유니크 제약조건으로 리뷰 논리 삭제 시 리뷰가 생성되지 않는 문제
- 상황
- 원인 1: 데이터 무결성을 위해 유저 ID와 책 ID로 JPA 엔티티 레벨의 유니크 인덱스 제약조건을 설정
- 원인 2: 히스토리 관리를 위해 @SQLRestricion(”delete_at IS NULL)을 사용
- 사용자가 리뷰를 삭제하면 물리적 레코드가 지워지는 것이 아닌 delete_at 칼럼에 삭제 시간이 기록됨
- 문제 해결
- 좀 더 직관적이고 확실한 데이터 무결성 보장을 위해 데이터베이스의 부분 인덱스 기능을 도입
- JPA 엔티티 레벨에 선언되어 있던 기존 @UniqueContraint를 제거하고 데이터 베이스에 직접 조건부 유니크 인덱스를 생성하는 DDL 적용
- 결과
- 무결성 보장 및 버그 해결
- 활성화된 리뷰는 여전히
1인 1도서 1리뷰 원칙이 DB레벨에서 완벽하게 강제되며, 삭제 후에도 기능은 정상적으로 동작하게 됨
- 히스토리 보존
- 사용자가 삭제한 과거의 리뷰 데이터는 중복 제한 없이 DB에 안전하게 보관됨
- 관련 문서