본문 바로가기

⛳️ 공동구매 서비스 총대마켓

(14)
⚠️ 비동기 스레드 내부에서 발생한 LazyInitializationException 해결하기 이 글은 트랜잭션과 지연 로딩, 비동기 이벤트 처리에 대해 알고 있는 독자를 대상으로 작성한 글입니다. 사전 상황 설명: 비동기로 알림 로직 처리 비즈니스 로직과 알림 로직이 스프링 이벤트를 통해 분리되어 있으며, 알림 로직은 비동기 스레드에서 처리됩니다. 비동기 스레드에서 발생한 예외는 호출 스레드에 전파시키지 않기 위해 로그만 출력하도록 한 상황입니다.  문제: LazyInitializationException알림 로직에서 예외가 발생하였습니다. 비동기 스레드에서 발생했기 때문에 호출 스레드에 영향은 없었으나, 로컬 환경에서 개발 중 로그로 예외를 발견하였습니다. LazyInitializationException은 트랜잭션(세션) 외부에서 지연 로딩으로 인해 초기화되기 전의 데이터에 접근을 시도할 때..
♻️ JPA N+1 문제 해결하여 성능 개선하기: LAZY 로딩 전략, fetch join 이 글은 문제 해결 과정이 중심이며 개념은 가볍게 설명합니다. JPA의 N+1 문제를 학습하던 중 '과연 우리 서비스는 이 문제가 없을까?' 하는 궁금증에서 시작해, 모든 API의 쿼리 로그를 검증하며 N+1 문제를 찾아냈습니다. 그 중 하나의 예시를 바탕으로 문제를 해결한 과정을 공유합니다. 문제: N+1 문제문제를 명확히 설명하고자 최대한 간략하게 ERD를 작성해보았습니다. 참여 관계 정보를 저장하는 offering_member 테이블과 사용자 정보를 저장하는 member 테이블이 존재하며, 두 테이블은 다대일의 관계를 가집니다. N+1 문제가 발생하는 지점은 참여자 목록을 조회할 경우입니다. offering_member 테이블에서 참여 관계 목록을 조회한 후 사용자 정보를 가져오기 위해 member..
♻️ QueryDSL을 도입하면 무엇이 좋을까 (from JPQL) 이 글은 Querydsl 적용 방법을 소개하는 글이 아닙니다. Querydsl 도입 과정과 얻을 수 있었던 이점을 소개합니다.  상황: 조건절 중복 + 가독성 저하한방쿼리로 인한 성능 저하를 해결하기 위해 쿼리를 최적화했습니다. 쿼리 최적화와 인덱스로 API Latency 30배 개선하기문제 총대마켓 서비스의 메인페이지 필터링 및 검색 조회 API Latency가 100만 건 더미데이터 기준, 약 25초에 근접했습니다. 사용자가 메인페이지에서 30초간 아무런 데이터도 확인할 수 없는 것과helenason.tistory.com 그 결과 API 속도는 약 30배 개선되었지만, 한방쿼리를 잘게 분리하는 과정에서 코드가 중복되었습니다. 더불어 특정 변수가 존재하는지에 따라 호출되는 쿼리도 달라졌습니다. 따라서 ..
@TransactionalEventListener: 학습테스트 만들어 동작 방식 확인해보기 트랜잭션이 롤백될 때 발행된 이벤트를 처리하지 않도록 하여 문제를 해결했다. 하지만 문제 해결에 그치고 싶지 않았고 사용했던 @TransactionalEventListener 어노테이션의 동작 방식을 더 깊게 학습하고 싶었다. 그 여정을 공유하는 글이다. @TransactionalEventListener 이벤트 처리 시점아래와 같이 @TransactionalEventListener 어노테이션의 phase 옵션을 통해 이벤트가 처리되는 시점을 지정할 수 있다.BEFORE_COMMIT트랜잭션이 커밋되기 이전에 이벤트를 처리한다.AFTER_COMMIT트랜잭션이 성공적으로 커밋된 후 이벤트를 처리한다. (default)AFTER_ROLLBACK트랜잭션이 롤백될 때 이벤트를 처리한다.AFTER_COMPLETION..
⚠️ 트랜잭션이 롤백될 때 이미 발행된 이벤트를 어떻게 처리할까: @TransactionalEventListener 1. 문제: 발행된 이벤트는 무조건 처리된다.동시성 이슈를 해결하기 위해 낙관적 잠금을 구현하고 있었다. 충돌을 방지하기 위해 낙관적 잠금을 걸게 되면 애플리케이션 단에서 버저닝 필드를 검증한다. 그 후 버저닝 필드가 일치하지 않으면 트랜잭션 충돌로 판단하여 아래와 같이 낙관적 잠금 예외를 발생시키게 된다. 이 예외는 트랜잭션이 커밋되는 시점에 발생하는데, 그렇다면 예외가 발생해 트랜잭션이 롤백된다면 트랜잭션 내부에서 이전에 발행된 이벤트는 어떻게 처리될까? 총대마켓의 기존 코드에서는 트랜잭션이 롤백되어도 이미 발행된 이벤트는 실행되었다. 즉, 사용자가 참여하려고 할 때 공동구매가 마감되어 참여하지 못했는데(동시성 이슈로 인한 트랜잭션 롤백), "에버님이 공동구매에 참여했어요!"라는 알림이 전송(이벤트 ..
⚠️ 동시성 이슈 해결 과정: 트랜잭션 격리 수준, 낙관적 잠금, 비관적 잠금 문제 상황채팅방 목록을 조회할 때 500 예외가 반환되어 데이터가 조회되지 않고 앱에서 빈 화면이 출력되었습니다.LoggingErrorResponse[identifier=ceedb3fa-300d-4ee4-852d-c9d83826132e, memberIdentifier={"sub":"1010","exp":1729660386}, httpMethod=GET, uri=/comments, requestBody=, statusCode=500, errorMessage=, latency=24ms, stacktrace=org.springframework.dao.IncorrectResultSizeDataAccessException: Query did not return a unique result: 2 results we..
⚠️ 동시성 이슈 해결 과정: Spring Event로 비동기 처리하기 푸시알림 기능 추가 후 QA 과정에서 발생한 문제의 원인을 파악하고 해결하는 과정을 공유합니다.  문제 채팅방 목록을 조회할 때 500 예외가 발생하여, 채팅방 목록에 데이터가 조회되지 않는 상황이 발생했습니다.LoggingErrorResponse[identifier=ceedb3fa-300d-4ee4-852d-c9d83826132e, memberIdentifier={"sub":"1010","exp":1729660386}, httpMethod=GET, uri=/comments, requestBody=, statusCode=500, errorMessage=, latency=24ms, stacktrace=org.springframework.dao.IncorrectResultSizeDataAccessExcept..
FCM 푸시알림 도입기 (안드로이드 코틀린 & 백엔드 자바) 상황 공동구매 서비스 '총대마켓'에는 총대와 여러 참여자가 소통하는 채팅방이 있습니다. 하지만 서비스 초기에 알림 기능을 MVP로 보지 않았기 때문에, 채팅을 전송해도 알림을 수신하지 못하는 상황이었습니다. 직접 총대마켓의 사용자가 되어 거래를 진행해 본 결과, 아래 불편함이 크게 다가왔습니다.- 즉각적인 소통이 어려워, 거래 날짜와 장소를 빠르게 확정할 수 없다.- 거래의 진행 상황과 참여 사실을 앱에 접속하지 않는 이상 알기 어렵다.- 채팅 확인을 위해 불필요하게 앱에 접속하는 상황이 많아지거나, 반대로 앱에 접속하지 않게 된다. 위 불편함을 이유로 푸시알림은 해당 시점에 꼭 필요한 기능이라고 생각했습니다. 하지만 다른 팀원들은 우선순위가 높은 기능이라고 생각하지 않거나, 많은 리소스 투여로 구현 시..