본문 바로가기

분류 전체보기38

[지성인] JaCoCo 테스트 분석 도구 적용, 프로젝트 테스트 커버리지 확인 약 1개월 반 전에 테스트 개수가 200개였다..  추가적인 기능 개발과 리팩토링을 했더니 단위 테스트도 늘어나서 55개가 늘어난 258개가 되었다.우리 백엔드 파트는 '지성인' 프로젝트를 시작할 때부터 모든 분기 테스트를 작성하기로 마음 먹었다. 또한 레이어드 아키텍처를 적용해서 각 레이어별 역할을 명확히 했다.그에 따라 각 기능에 대해서 레이어별로 단위 테스트를 작성했다.힘들긴 했지만 .. 테스트 코드 덕분에 통해 코드의 품질이 올라갔다고 생각한다. 이쯤되니 우리 프로젝트의 테스트 커버리지는 얼마나 되는지 궁금해졌다.그래서 GitHub Actions을 통해 JaCoCo 테스트 분석 도구를 적용하기로 했다. 적용build.gradleplugins { id 'java' id 'org.sprin.. 2024. 7. 9.
[지성인] 한줄평 좋아요 중복 생성 동시성 문제 지성인 프로젝트를 개발하면서 한줄평 좋아요 기능에 동시성 문제를 발견했다.일단 한줄평 기능은 특정 도서에 대해 짧게 평가할 수 있는 기능이다.해당 한줄평은 다른 사용자가 좋아요를 누를 수 있다. 문제 상황 현재 사이트에서는 좋아요를 1번 누르면 하트가 표시되고 다시 누르면 좋아요가 해제된다.또한 본인의 한줄평에는 좋아요를 할 수 없다. 보기에는 문제가 없어보인다.하지만, 좋아요를 광클해서 같은 요청이 한번에 보내진다면 ?악의적인 사용자가 요청 로직을 알아내서 똑같은 좋아요 요청을 한번에 보낸다면 ?서버에 심각한 에러가 발생할 수 있다. 해당 로직은 발생할 수 있는 문제 상황이다.왜 문제가 발생하는가 ? 임계 영역(Critical Section)에 경쟁 상태(Race Condition)가 발생하기 때문이다.. 2024. 7. 6.
STOMP로 소켓 방식 채팅 구현 + Rate Limiter, Token Bucket으로 API 처리율 제한하기 소켓 프로그래밍으로 채팅 애플리케이션을 구현해보자.그리고 Rate Limiter와 Token Bucket 방식의 API 처리율 제한기를 적용해보자. ⚡️ 채팅 애플리케이션 일단 STOMP는 텍스트 기반의 메시징 프로토콜로, 주로 메시지 브로커와 클라이언트 간의 통신을 위해 사용된다.메시지의 송수신을 담당하며, 클라이언트가 구독한 주제에 대해 응답을 받을 수 있도록 해준다.웹 애플리케이션에서 채팅, 실시간 업데이트, 알림 등을 구현할 때 많이 사용된다.  해당 그림을 살펴보면 발행-구독 매커니즘으로 특정 주제에 메시지를 보내면, 해당 주제를 구독하고 있는 subscriber 모두에게 메시지가 전달되는 구조다.  💫 애플리케이션 구조 해당 채팅 애플리케이션은 크게 두 가지의 Controller가 존재한다... 2024. 6. 21.
Redis Sorted Sets으로 인기 검색어 구현하기 # Event프로젝트의 요구사항 중 검색칸을 눌렀을 때, 인기 검색어 보여줘야 했다. 처음에는 ElasticSearch를 이용해서 구현하고 싶었지만 알아야 할 내용이 너무 많았다.그래서 일단 MVP 구현을 위해 차선책으로 Redis의 Sorted Sets을 사용했다. 왜 차선책인가 ?DB보다는 Redis가 더 좋을 것이라는 것은 자명하다.만약 검색어를 DB에서 쿼리로 가져온다면 하드디스크 기반의 저장 공간에 접근해야 한다.하지만 Redis는 In-Memory DB이기 때문에 더 빠를 수밖에 없다. 또한 Sorted Sets을 통해 요청 검색어의 점수를 1씩 증가시킨다.조회할 때는 점수 Top-10을 반환한다. # Implementation@Configuration@RequiredArgsConstructo.. 2024. 4. 9.
리뷰 좋아요 조회 쿼리 N + 1 문제 해결하기 # Event @Override public PageResponse findAllReviewContentOrderBy( Long userId, RatingOrderType orderType, int size, int offset) { List reviewContents = queryFactory .select(new QReviewContentGetAllResponse( review.id, user.profileImage, user.name, review.rating, review.content, book.isbn, book.title, book.imageUrl .. 2024. 4. 5.
[Spring] could not initialize proxy [...] - no Session 오류 #1. Event @DisplayName("사용자가 리뷰 좋아요를 누른다.") @Test void likeReview() { //given User user = userRepository.save(createUser("1")); Book book = bookRepository.save(createBook()); Review review = reviewRepository.save(createReview(user, book)); //when reviewLikeService.createReviewLike(user.getId(), review.getId()); //then List reviewLike = reviewLikeRepository.findAll(); assertThat(reviewLike).hasSi.. 2024. 4. 2.