프로젝트/트러블슈팅 (10) 썸네일형 리스트형 @RedisHash 주의해서 사용하기 도입프로젝트에서 @RedisHash를 사용하여 기능을 구현했다. Spring Data Redis의 @RedisHash는 자바 객체를 레디스의 Hash 데이터구조에 자동으로 매핑한다. 그리고 CrudRepository를 확장한 인터페이스를 사용하면, 데이터의 CRUD 작업을 쉽게 구현할 수 있다. 하지만 인덱싱을 위한 데이터는 만료시간이 지나도 삭제되지 않아 데이터가 계속 쌓이고 예상하지 못한 명령어가 발생한다. 예제를 통해 주의할 점을 자세히 알아보자. @RedisHash 주의점예제 코드@Getter@NoArgsConstructor(access = AccessLevel.PROTECTED)@RedisHash(value = "member")public class Member { @Id privat.. [트러블 슈팅] GitHub Actions를 활용해 슬랙봇으로 PR 자동화하기 배경사내에서 커밋과 PR 내역이 한 채널에 모두 섞여있었다. 그리고 GitHub 아이디와 슬랙 이름이 매칭되지 않아 PR이 오픈되어도 리뷰어에게 알림이 울리지 않았다. 그래서 팀 채널에서 직접 리뷰어를 멘션한 뒤 리뷰를 부탁하는 글을 작성해야 했다. 나는 이 과정이 코드 리뷰 활성화를 어렵게 만드는 허들이라고 생각했다. 따라서 PR을 위한 채널을 만들고 슬랙봇으로 자동화를 통해 허들을 낮추고자 하였다. 자동화 과정에서 GitHub Actions를 적극적으로 사용하였다. Github Actions를 사용하면, GitHub 하나의 플랫폼 내에서 모든 과정을 수행할 수 있다. 그리고 .github/workflows 디렉토리에 워크플로우 yaml 파일만 작성하면 별도의 세팅과 설치가 필요없이 간단하게 기능을 사.. [트러블 슈팅] 인덱스 컨디션 푸시다운, 인덱스를 이용한 정렬, 커버링 인덱스로 슬로우 쿼리 튜닝하기 도입지난 글에서 단건 반복 쿼리를 벌크 SELECT 쿼리로 변경함으로써 n + 1문제를 해결했다. 더 나아가 JMeter로 성능 테스트를 진행하여 얼만큼 성능이 높아졌는지 비교해보았다. 이번 글에서 실행 계획을 분석하고 쿼리를 튜닝한 뒤 다시 JMeter로 성능 테스트를 진행한 경험을 소개한다. 인덱스 생성과 인덱스 컨디션 푸시다운n + 1 문제를 해결한 /api/v1/missions/matching 엔드포인트는 쿼리가 총 3번 전송된다. 아래는 첫 번째로 전송되는 쿼리이다. 미션(missions), 미션 카테고리(m_categories), 지역(regions), 미션 북마크(mission_bookmarks) 총 4개의 테이블을 조인하고 있다. 그리고 미션에 10만개, 미션 카테고리에 8개, 지역에 42.. [트러블 슈팅] n + 1 문제를 IN절로 해결하기 도입원데이히어로 프로젝트에서 이미지 조회 시 n + 1 문제가 발생했다. 그래서 In절로 n + 1 문제를 해결하고 더 나아가 쿼리를 튜닝하여 성능을 개선하였다. 이번 글에서는 In절로 n + 1 문제를 해결한 경험만 이야기해 보겠다. 실행 계획을 통해 슬로우 쿼리를 튜닝한 경험은 다음 글에서 작성해 볼 계획이다. 객체 사이의 관계n + 1 문제 상황을 설명하기 전에 객체 사이의 관계에 대해 설명해 보겠다. 프로젝트 내 미션(Mission)이라는 도메인이 있다. 여기서 미션은 프로젝트 내 도메인 용어로 벌레 잡기, 포장, 청소처럼 소소한 일거리를 뜻한다. 미션은 이미지를 최대 5개 가질 수 있으므로 미션과 미션 이미지는 OneToMany 관계이다. 먼저 객체 참조와 아이디 참조 중 어떤 참조 관계를 선.. [트러블 슈팅] 리팩토링을 통한 복잡했던 모듈 구조를 단순화 도입지난 글, [트러블 슈팅] 레이어별 멀티 모듈 적용 (과도한 모듈 분리로 실패🤪) 에서 레이어 간 단방향 의존 관계를 갖도록 레이어 별로 모듈 분리를 시도했다. 그러나 과도한 분리로 인해 모듈 구조가 복잡해졌고 결국 실패로 이어졌다. 이번 글에서 복잡했던 멀티 모듈 구조를 리팩토링을 통해 단순화한 경험을 이야기하겠다.과도하게 분리된 모듈을 하나의 모듈로계층마다 독립적이고 관심사가 다르므로 모듈화를 통해 응집도를 높이려고 했다. 그리고 레이어드 아키텍처에서 단방향 의존 관계를 가져야 하므로 모듈의 의존 방향을 통해 제약을 걸어주었다. 그러나 아키텍처를 모듈과 일치시키려는 강박 때문에 모듈 구조가 너무 복잡해졌다. 그리고 구현에 대한 깊은 이해도 없이 성급하게 모듈화를 진행시켰다. 결국 과도한 추상화로.. [트러블 슈팅] 레이어별 멀티 모듈 적용 (과도한 모듈 분리로 실패🤪) 도입 원데이 히어로 프로젝트는 멀티 모듈 단일 프로젝트이다. 프로젝트에 멀티 모듈을 적용하면서 트러블슈팅을 겪었다. 이번 글에선 프로젝트 내에서 과도한 모듈 분리로 인한 실패 경험을 소개하고자 한다. 단어 그대로 실패 경험이니 우리처럼 하지 마세요!라는 글이다. 먼저 멀티 모듈을 왜 적용했고 어떻게 모듈을 나눴는지 그리고 발생했던 문제와 해결하긴 했지만 왜 잘못된 설계인지 이야기해보겠다. 멀티모듈이란 멀티 모듈을 왜 사용할까? 멀티 모듈은 역할과 의존성 분리를 통해 시스템의 분리, 통합을 유연하게 만들어 줄 수 있는 좋은 아키텍처를 만들 수 있다. MSA에서 멀티 모듈이 부각되곤 하는데, 멀티 모듈을 활용해 역할과 의존성을 잘 분리한다면 모놀리틱에서 MSA로 용이하게 전환될 수 있다. 잘 설계된 멀티 .. [트러블 슈팅] 랭킹 조회에 로컬 캐시 적용하기 도입[10분 테코톡] 📸소니의 Cache를 보고 프로젝트에 캐시를 적용해보고 싶었다. 그런데 어디에 적용할지 어떤 캐시를 사용할지 고민되었다. 참고로 웹 브라우저 캐시가 아닌 컴퓨터 운영체제에서의 캐시를 적용하고자 했다. 원데이 히어로 프로젝트에서 히어로 점수를 기준으로 히어로 랭킹 조회하는 기능이 있다. 여기서 히어로란 프로젝트 내 도메인 용어로 단기 알바, 심부름 등을 해주는 사람을 뜻한다. 이러한 랭킹 조회에 로컬 캐시를 적용했고 이번 글에선 캐시 적용 과정에 대해서 이야기해 보겠다. 캐시란 무엇인가?먼저 캐시에 대해서 소개하겠다. 캐시는 데이터나 값을 미리 복사해 놓는 임시 저장소이다. 캐시는 캐시의 접근 시간에 비해 원본 데이터를 접근하는 시간이 오래 걸리는 경우나 값을 다시 계산하는 시간을.. [트러블 슈팅] Testcontainers로 통합 테스트 환경 만들기 (+ Testcontainers를 Spring Bean으로 등록) 배경이전 글, [트러블 슈팅] 멀티모듈에서 JaCoCo와 SonarCloud를 적용하여 코드 품질 높이기에서 인프라 영역이 나타났고 그로 인해 통합 테스트가 어려워졌다고 이야기했다. 테스트는 멱등성(Idempotent)을 보장하도록 작성되어야 한다. 멱등성이란 어떤 연산이 여러 번 수행되어도 동일한 결과를 내놓는 성질을 의미한다. 결국 테스트는 한 번만 수행한 결과와 여러 번 수행한 결과가 동일해야 한다. 그렇다면 결정적인 테스트 결과를 가져오도록 통합 테스트를 구성하려면 어떻게 해야할까? 원데이히어로 프로젝트 내 통합 테스트에서 사용할 주요 모듈은 웹 애플리케이션 서버(WAS)와 MongoDB이다. 테스트를 위해서 이 두 가지 모듈이 어딘가에서 실행되어야 한다. 프로젝트에서 스프링 부트를 사용하고 있.. [트러블 슈팅] 멀티모듈에서 JaCoCo와 SonarCloud를 적용하여 코드 품질 높이기 도입어느 날 팀원이 SonarCloud를 적용한 글을 공유했다. 글을 읽어보니, 빠르고 간단하게 코드 품질을 높일 수 있을 것 같아 원데이히어로 프로젝트에 도입하기로 했다. 원데이히어로 프로젝트는 멀티모듈이다. 이번 글에서는 멀티모듈에서 jacoco와 SonarCloud를 어떻게 적용했는지 왜 도입했는지 이야기 해보겠다. SonarCloud란? SonarCloud은 정적 코드 분석 도구 중 하나인 SonarQube의 SaaS 버전이다. 여기서 정적 코드 분석이란, 코드 레벨에서 발견할 수 있는 코드 스멜, 잠재적 결함, 컨벤션 체크, 보안 취약점 등을 분석해서 보고해준다. SonarCloud는 SonarQube와 달리 서버에 설치하지 않아도 되지만, SonarQube만큼 많은 기능을 제공하지 않는다. 하.. [트러블 슈팅] 이벤트로 알림 서비스와 느슨한 결합 만들기 원데이히어로 프로젝트에서 알림 기능을 구현했다. 프로젝트 요구사항에 따르면 미션 플로우가 진행될 때마다 실시간으로 알림을 보내줘야 했다. 여기서 미션은 프로젝트 내의 도메인 용어로 단기 알바, 심부름 요청을 뜻한다. 이번 글에서 실시간 알림을 구현하면서 겪었던 문제와 이러한 문제를 어떻게 해결했는지 그리고 남아있는 문제는 무엇인지 공유해보겠다. 먼저 미션 서비스 로직 수행부터 알림을 보내는 로직까지 코드는 다음과 같은 흐름으로 동작한다.미션 서비스 로직 수행RDB에서 알림을 위한 데이터를 조회NoSQL에 알림을 저장SSE(Server-Sent-Event)를 통해 알림을 전송여기서 왜 알림 서비스의 메인 데이터베이스를 NoSQL로 선택했는지, SSE로 실시간 알림을 구현했는지는 이 글의 주제와 동떨어진 이.. 이전 1 다음