전체 글

어쩌겠어요? 늘 그렇듯 해내야지
트러블 슈팅

대용량의 외부 API 호출 최적화하기 (feat. WebClient, RateLimiter)

배경회사에서 맡은 프로젝트 중 특정 API 호출을 통하여 대량의 작업들이 외부 API를 호출하여 데이터를 얻어야 하는 상황이 있었다.예를 들어 특정 위치 좌표들이 묶인 하나의 경로들이 List형태로 데이터 요청이 이루어지고 있었다. 이것을 각 묶음들의 위치 데이터에 대해 외부 API로 경로를 조회하는 작업이었다. (쉽게 말해 중첩된 List 형태로 많은 데이터로 요청을 보낸다!)이 과정에서 다음과 같은 문제점들이 발견되었다.1. API 호출 제한 (Rate Limit)외부 API는 초당 50건으로 호출이 제한되어 있었다.제한을 초과하면 해당 외부 서버에서 많은 요청이 있다고 에러가 발생했다.초기에는 이 제한을 고려하지 않고 동시에 많은 요청을 보내 자주 실패했다.2. 동기 방식의 API 호출 문제해당 외..

Spring

Spring Boot + JPA 환경에서 AOP를 활용하여 N+1 문제 모니터링하기! (feat N+1 디텍터?!)

들어가기 앞서평소 JPA를 사용할 때 생기는 N+1 문제를 중요하게 생각하고 있습니다.현재 재직중인 회사에서도 개발 단계에서 간략하게 모니터링을 하고 싶다는 니즈가 있었습니다. 왜냐하면 프로젝트 초기 단계였어서, API를 호출해보고 테스트할 때마다 늘 DB 호출이 몇번 일어났는지 하나 하나 세어가며 확인을 해야하는 상황이 늘 반복되었기 때문입니다.그래서 많은 아티클들을 참고하여 직접 만들어 팀에게 공유해보고자 하였습니다.JPA를 사용하다 보면 가장 흔하게 마주치는 성능 문제 중 하나가 바로 N+1 문제이다.이 문제는 개발 단계에서 쉽게 놓칠 수 있으며, 실제 운영 환경에서 성능 저하의 주요 원인이 되곤 한다.이번 글에서는 Kotlin과 Spring Boot 환경에서 AOP를 활용해 N+1 문제를 효과적으..

JAVA

불변 객체는 뭘까? 그리고 코틀린은 디폴트가 final?!

프로그래밍에서 가변성의 문제가변성은 상태를 가지는 경우를 얘기한다.상태를 변경하는 행위는 메모리의 저장된 값을 변경하는 행위이렇게 메모리에 저장된 하나의 값을 누구든지 변경할 수 있다는 것은 무분별한 상태가 변경이 된다는 것을 의미한다.가변성의 문제멀티스레드에서 값을 보장하지 못함.값의 예측이 어렵고 변경에 있어서 위험하다.테스트와 디버깅이 어렵다.상태 변경 발생 시 처리를 해주어야한다.불변 객체 (Immutable Object)란- 객체지향 프로그래밍에 있어, 생성 후 그 상태를 바꿀 수 없는 객체를 말한다.- 경우에 따라서 내부에서 사용하는 속성이 변화해도 외부에서 그 객체의 상태가 변하지 않은 것처럼 보인다면 불변 객체로 보기도 한다.- 불변 객체를 사용하면 복제나 비교를 위한 조작을 단순화 할 수..

테스트

제대로 알고 쓰자! 테스트 대역 (Test Double) [더미 / 페이크 / 스텁 / 모의객체 / 스파이]

업무나 협업 프로젝트를 진행하면서 테스트 코드를 작성할 때 다양한 테스트 방법이 사용된다.그 중에서도 Test Double은 실제 객체를 대신하여 테스트에 사용되는 객체를 말하는데, 종종 개발자간 서로 이해한 개념이 달랐던 적이 있어서 오해가 생긴 일도 있었다. 특히 Mock과 Stub의 차이점이나, Spy와 Mock의 차이점 등에서 혼란이 있었다. 그래서 이 개념을 정확히 이해하고 상황에 맞게 사용하는 것이 중요하기에 짚고 넘어가려고 한다.더미 (Dummy)class DummyEmailService : EmailService { override fun sendEmail(email: String, message: String) { // 아무 동작도 하지 않음 - 단순히 파라미터를 채우기..

트러블 슈팅

JPA를 사용한 데이터 조회 시 findByXXX() vs findAll() + stream(filter): 성능 비교 분석

🤔 문제 상황DB 조회 시 성능 최적화는 개발자들이 자주 마주치는 과제이다.특히 복잡한 엔티티 관계와 다양한 응답 값 설정이 필요한 상황에서는 더욱 그렇다. 이러한 맥락에서 findByXXX() 메소드를 사용하여 DB에서 직접 필터링하는 방식과 findAll()로 모든 데이터를 가져온 후 Java의 stream API를 이용해 필터링하는 방식 중 전자가 더욱 성능에 좋다고는 알고 있었지만, 어떻게/ 왜 더 효율적인지에 대한 의문이 생겼다.실무에서는 종종 findAll()을 사용한 후 stream의 filter 등으로 데이터를 가공하는 접근법을 볼 수 있었다. 이는 주로 빠른 개발 속도를 위해 선택되며, 복잡한 연관 관계에서 SQL 쿼리 작성에 시간을 들이는 대신 컴파일 시점에 확인 가능한 stream ..

JPA

JPA/DDD 관점에서의 직접 참조/간접 참조

🤔 문제 상황JPA를 사용하며 객체를 설계할 때, 직접 참조와 간접 참조 각각 어느때에 사용하면 적절할지에 대한 고민이 생겼다.더불어 회사에서 코드 리뷰를 하다가 간접 참조로 설계를 한 팀원의 코드에 왜 여기서 간접 참조를 했나요? or 왜 여기는 직접 참조를 했나요? 등의 리뷰를 보고 나도 아무 생각없이 객체간 설계를 하고 있던 것은 아닌가 하는 생각이 들었다.🕊️ 나의 생각우선 각 용어의 뜻을 보자면직접 참조 : 말 그대로 Entity간의 참조를 직접적으로 하는 것이다. 구체적인 방법으로는 JPA 사용시 @OneToOne / @ManyToOne등의 어노테이션을 써서 매핑(참조)를 하는 것을 말한다.간접 참조 : 직접 참조의 반대의 의미로, 외래키(FK)를 맺지 않고 다른 Entity의 (더 나아가..

트러블 슈팅

try-catch와 @Transactional을 함께 사용시 트랜잭션 롤백 여부

🤔 문제 상황스케줄러를 사용하고 있는 프로젝트가 있다. 해당 스케줄러가 주기적으로 동작하던 와중 아래와 같은 롤백 예외 로그가 찍혔다.... ERROR ... [askScheduler-10] c.w.b.b.l.CreateDeliveryMessageListener : org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Transaction marked as rollbackOnlyat o.s.orm.jpa.JpaTransactionManager.doCommit(JpaTr..

JPA

연관 관계가 Lazy Loading일 때, 해당 엔티티를 사용하는 메서드에 @Transactional이 없다면?

🤔 문제 상황JPA에서 엔티티 간 연관 관계를 Lazy Loading으로 설정했을 때, 이 엔티티를 사용하는 메서드에 @Transactional 어노테이션이 없다면 LazyInitializationException이 발생할 수 있다.예시 코드로 보자면@Entitypublic class User { @Id private Long id; @OneToMany(fetch = FetchType.LAZY) private List orders;}public class UserService { public void processUserOrders(Long userId) { User user = userRepository.findById(userId).orElseThrow(); ..

맹수호빵
Be Water, My Friend