[Redis] 캐시 스탬피드 현상
·
트러블슈팅
✅ 개요Spring Boot 프로젝트에서 레디스를 사용하여 캐시를 사용하였을 때 발생한 캐시 스탬피드 현상에 대한 해결 과정을 기록하고자 합니다. ✅ 문제 상황먼저 캐싱 대상은 AI 후기 요약 결과로 다음과 같습니다. 캐싱을 적용하여 기본적인 응답 시간 감소와 함께 LLM을 호출하는 횟수를 줄여 토큰 비용 감소를 기대했습니다.@Cacheable(value = "postReviewSummary", key = "#postId")public String summarizePostReviews(Long postId) { List reviews = reviewQueryRepository.findTop30ByPostId(postId); if (reviews.isEmpty()) { return ..
[Spring WebSocket] STOMP - convertAndSendToUser 알고 사용하기
·
트러블슈팅
✅ 개요Spring Stomp 기술 중 `SimpMessageSendingOperations.convertAndSendToUser()` 메서드를 사용해 특정 사용자에게만 메시지를 전달하는 과정에서의 시행착오와 디버깅을 통해 해결하는 과정을 정리하고자 합니다. ✅ 문제 상황프로젝트 채팅 서비스 설계상 사용자 A가 사용자 B에게 채팅 요청을 보내면 사용자 B에게 채팅 요청을 수락 또는 거절할 수 있는 메시지를 보내는 이벤트를 발행하고 있습니다.그리고 이벤트를 받아 처리하는 서비스 로직에서 `convertAndSendToUser()` 메서드를 사용하고 있습니다.더보기@Component@RequiredArgsConstructorpublic class ChatEventListener { private fin..
쿠키 Samesite 설정으로 이슈 해결하기
·
트러블슈팅
✅ 개요로컬 개발 환경에서는 프론트엔드와 백엔드 간의 쿠키 전달에 문제가 없었지만, 배포 환경에서는 문제가 발생해 해결 과정을 기록하고자 합니다. ✅ 문제 상황프로젝트에서 다음과 같이 로그인 인증 이후 헤더와 쿠키로 각각 JWT 액세스 토큰과 리프레시 토큰을 전달하고 있습니다.private TokenResponse getTokenResponse(HttpServletResponse response, Guest guest, String principalName) { String accessToken = jwtProvider.generateAccessToken(guest, principalName); String refreshToken = jwtProvider.generateRefreshToken(..
Spring OAuth AccessToken 제대로 추출하기
·
트러블슈팅
✅ 개요사용자가 OAuth로 인증한 상태에서 로그아웃 요청을 받으면 서버에서 JWT 처리(만료 쿠키, 블랙리스트 처리 등) 외 추가적으로 OAuth에 토큰 삭제 요청 API를 사용하려고 할 때 발생한 문제와 해결방법을 기록하고자 합니다. ✅ 문제 상황OAuth 토큰 삭제 요청 API는 반드시 삭제 대상 액세스 토큰(JWT 아님)을 전달해야 합니다. 그리고 토큰은 `OAuth2AuthorizedClientService`를 통해 얻을 수 있으며, 토큰을 얻기 위해 필요한 메서드는 `loadAuthorizedClient`로 두 개의 인자를 받습니다.`clientRegistrationId`는 kakao, google같은 값으로 DB에 잘 저장했다면 어렵지 않게 얻을 수 있습니다. 그리고 `principalNam..
JpaRepository에서 default 키워드를 사용할 때 주의점
·
트러블슈팅
✅ 개요Spring Data Jpa 인터페이스에서 default 키워드를 사용할 때의 발생하는 문제점과 해결 방법을 기록하고자 합니다. ✅ 문제 상황평소와 같이 `JpaRepository`를 사용하여 편리하게 JPA를 사용할 때 default 키워드를 사용하면 서비스 로직에서 더 편리하게 사용할 수 있을 것 같아서 다음과 같이 작성했습니다.public interface MemberRepository extends JpaRepository { default Member getMemberById(Long id) { return findById(id).orElseThrow(() -> new EntityNotFoundException("Cannot be found Member for id: "..