오늘은 내부 임직원이 사용하는 어드민 제품의 목록조회 기능을 평균 5~8초에서 100ms 미만 응답속도를 가지도록 성능 개선한 이야기를 공유해볼까 한다. 이야기에 앞서, 먼저 해당 제품으로 이뤄지는 업무 절차에 대해 간략히 소개해보겠다. 계좌를 만들기 위해서 고객의 정보를 확인해야 하는 절차가 있다. 그 절차 중 일부 고객은 어드민에서 승인을 해야 계좌를 개설할 수 있는 경우가 있다. 이번 개선기는 계좌개설 과정에서 승인이 필요한 대상들을 불러오는 목록 리스트 성능 개선 이야기이다. 이 업무를 앞으로 '검토 업무'라고 부르겠다. 검토업무는 고객의 계좌개설 대기 시간을 최소화하기 위해 빠르게 검토가 이뤄져야 한다. 검토가 필요하게 되면 검토건이 생성되고, 검토건은 검토대기, 검토 중, 검토완료 3가지 상..
2023년도 회고https://mopil.tistory.com/174 2023년 회고# 1월 친구들과 해커톤에 참여했다. https://mopil.tistory.com/132 SPARCS 2023 StartUp Hackathon 참여 후기 https://hackathon.sparcs.org/ SPARCS 2023 StartUp Hackathon 우리의 생각이 한계까지 함께하는 곳, SPARCS 2023 StartUp Hackmopil.tistory.com # 2023년도 목표 달성술술 사이드 프로젝트 출시 (3월 목표)7월 출시 완료 새로운 사이드 프로젝트...?청년모아 mini 사이드 프로젝트 합류컨퍼런스, 해커톤 1회 이상 씩 참여하기여럿 시도했지만 결국 둘 다 미달성오픈소스 기여 1회 이상 해보기미..
# Rate Limit이란?일정 시간 동안 호출될 수 있는 API 횟수를 제한하는 걸 말한다. 예를 들어 파일 업로드는 10분에 최대 3번만 가능 이런 걸 구현할 때 사용하는 개념이다. 자세한 건 아래글 참조https://gngsn.tistory.com/224 Rate Limiter, 제대로 이해하기Rate limiter의 역할과 강단점을 살펴보고, 구현 알고리즘 5가지를 이해하는 것이 해당 포스팅의 목표입니다. 본 포스팅의 모든 그림은 필자가 직접 그린 것으로 무단 사용을 금하며, 사용 시 출처gngsn.tistory.com 이번 글에서는 로컬 캐시 라이브러리로 유명한 Caffeine으로 Rate Limiter 기능을 구현해 볼 것이다. # 왜 Caffeine인가?Spring Cloud Gateway..
JPA 영속성 컨텍스트가 제공하는 기능 중 하나인 '1차 캐싱' 기능을 눈으로 직접 확인해 보자 @Serviceclass FirstService( private val userRepository: UserRepository,) { @Transactional fun callFirst() { var user = userRepository.findById(1).get() println("first call user = $user") user = userRepository.findById(1).get() println("second call user = $user") user = userRepository.findById(1).ge..
서버에서 예외(에러)가 나면 보통 이런 응답이 내려간다. 이 에러메시지 그대로 유저에게 보여주는 건 바람직하지 못하다. 그래서 보통 에러메시지를 유저편의적으로 가공하는데, 이를 '에러메시지 워싱'이라고 표현한다. # 얼마나 구체적인 에러 메시지를 보여줄까?워싱 주체에 대해 논하기 전, 먼저 유저에게 어떤 에러메시지를 보여주는 게 적합할지 생각을 해보자. 정답은 아닐 수도 있지만, 필자는 이렇게 생각한다. 1. 유저 스스로가 다른 액션을 통해 해당 에러를 해소할 수 있는 경우는 최대한 구체적으로 알려준다. ex) 회원가입 시 비밀번호 규칙 중, 특수기호가 포함되어야 하는데 빠진 경우추상적인 경우: 비밀번호 규칙에 올바르지 않습니다.구체적인 경우: 특수기호가 빠졌어요. 비밀번호에는 적어도 1개 이상의 특수..
# != 연산에 인덱스 태우기where 절의 not equal(!=) 연산은 인덱스를 걸어놔도 효과를 볼 수 없다. 이런 경우 해당 칼럼이 순차적인 코드값으로 되어있는 경우는 인덱스를 태우도록 꼼수(?)를 부릴 수 있다. * 디비는 오라클 기준 예를 들어, my_table이 존재하고 my_column이 이런 분포를 가진다고 해보자. 1000 : 32만 rows2000 : 10만 rows3000 : 5만 rows4000 : 22만 rows5000 : 1만 rows6000 : 43만 rows 그리고 my_column이 1000이 아닌 rows를 select 한다고 가정해보자. 인덱스를 걸어놔도, select * from my_table where my_column != '1000' 으로 하면 full sca..
build.gradle이나 build.gradle.kts의 dependencies를 정의하는 부분에 이런 식의 코드를 본 적 있을 것이다. dependencies { implementation("mysql:mysql-connector-java") api("org.springframework.boot:spring-boot-starter-data-jpa")} 이때 두 선언 모두 의존성 설정이 되는데, 어떤 차이가 있는지 알아보자. # 멀티모듈 의존성다음과 같은 멀티모듈 환경에서 api, core 모듈 모두 동일한 의존성이 필요하다고 해보자 api모듈의 dto에 bean validation을, core 모듈의 dto에도 bean validation을 걸고 싶은 상황이고, validation 의존..