본문 바로가기

분류 전체보기

(23)
깃허브 활동 알림 자동화: Slack + Github Actions 상황총대마켓 서비스를 개발할 당시 깃허브 활동을 슬랙에 알리는 기능이 구현되어있지 않아, 매번 팀원들에게 리뷰를 부탁한다는 메시지를 전송하거나 다른 팀원의 PR을 리뷰한 후 일일이 완료했다는 댓글을 달아야 했습니다. 그래서 이번에 개발하는 서비스 편히타에서는 이러한 불편함을 개선해야겠다 생각했고 깃허브 활동 알림을 자동화하고자 하였습니다. 그러나 슬랙 알림봇에 대한 반대 의견도 존재했습니다. 불필요한 알림을 많이 수신할수록 알림에 무뎌진다는 의견입니다. 이 의견에도 크게 공감하여 단점을 보완하고자 알림이 필요한 활동을 기반으로 슬랙 봇을 커스텀하여 저만의 슬랙 알림봇을 만들었습니다!  구현크게 세가지 과정을 거쳤습니다.1. 알림을 전송하는 슬랙 앱 만들기2. 깃허브와 슬랙 앱 연결하기3. Github A..
⚠️ 비동기 스레드 내부에서 발생한 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..