좋아요 순 정렬은 왜 7초가 걸렸을까?
·
dev/Spring
TL;DR좋아요순 정렬은 ORDER BY COUNT(likes) 집계라, 매 요청마다 좋아요 297만 건을 조인·집계·정렬한다 → 7.3초.인덱스로 풀스캔·해시조인은 잡아 14배 빨라졌지만(0.45초), 집계값 정렬(Using temporary; filesort)은 인덱스로 못 없앤다.like_count 컬럼으로 비정규화해 집계 자체를 없애고 정렬 인덱스를 붙이면 0.13ms. 대신 "어떻게 동기화·정합성을 유지하나"라는 새로운 문제가 생긴다.상품과 상품 좋아요는 테이블 정규화로 분리되어 있다. 상품 목록에 "좋아요 순" 정렬을 붙이고, 시드 데이터를 넣어 실제로 호출해봤다. 데이터는 상품 10만, 좋아요 약 297만, 재고 10만, 브랜드 50 규모다.일반 목록 조회는 1초쯤 나왔는데, 좋아요 순으로 ..
상품 재고 동시성 처리, 무엇을 골라야 할까? (feat. Pessimistic vs Optimistic vs Atomic update)
·
dev/Spring
이커머스 시스템을 개발하면서 상품 재고에 대한 동시성 처리에 대한 부분이 고민이 되었다.현재 Product와 ProductStock이 분리되어 있는 구조로 Product는 상품에 대한 메타 데이터를 관리하고 ProductStock은 ProductId를 가지며, Product에 대한 재고를 가지고 있다. 재고에 대한 동시성처리를 위해서 주로 많이 사용하는 3가지 케이스에 대해서 테스트를 진행했고, 테스트 결과는 어느정도 예상은 했지만 아래와 같았다.============= (threads=8, 요청량=8000, 재고=6000 → 품절 2000건) =============A) 비관적 락 : 전체 14,599 ms | 410 ops/s | 지연(avg) 14.595 ms | 락대기 누적 98,68..
테스트 더블 정리 (Dummy, Fake, Stub, Spy, Mock)
·
dev/Spring
TL;DR테스트 더블(Test Double)은 특정한 프레임워크나 도구에 종속된 기술이 아니라,"외부 의존성으로 인해 테스트가 어려운 상황을 해결하기 위해 진짜 객체 대신 사용하는 모든 대체재" 다.Java에서 테스트코드 작성시 Mockito를 주로 사용하는데, 대부분의 테스트 더블을 mock() 기반으로 표현할 수 있다.문제는 같은 mock() 객체라도 어떤 테스트는 반환값 제어에 집중하고,어떤 테스트는 호출 여부 검증에 집중하며, 어떤 테스트는 둘 다 사용한다는 점이다.하지만 코드만 봐서는 그 mock 객체가 어떤 의도의 테스트 더블인지 드러나지 않는 경우가 많다.들어가며회원 도메인을 개발하면서 UserService 단위 테스트 코드를 작성하던 중 UserRepository를 mock()으로 만들어 ..
Resilience4j CircuitBreaker 슬라이딩 윈도우 동작 원리(COUNT_BASED vs TIME_BASED)
·
dev/Spring
서킷브레이커를 도입하고 나서 슬라이딩 윈도우 타입에 대한 설정값들이 헷갈렸다.COUNT_BASED의 경우 사실 직관적이지만 TIME_BASED의 경우 딱 와닿지 않는 것 같다.Resilience4j를 기준으로 두 가지 윈도우 타입의 내부 동작 원리를 자세히 살펴보고, 어떤 상황에서 무엇을 골라야 하는지 정리해보려고 한다.슬라이딩 윈도우란?서킷브레이커는 세 가지 상태를 가진다.CLOSED: 정상 상태 (모든 요청이 통과한다.)OPEN: 차단 상태 (요청을 즉시 실패시킨다.)HALF_OPEN: 시험 상태 (일부 요청만 허용해서 회복 여부를 판단한다.)이때 CLOSED 상태에서 OPEN으로 전환할지를 결정하는 핵심 매커니즘이 바로 슬라이딩 윈도우다.윈도우는 두 가지 일을 한다.기록: 매 호출의 결과(성공/실패..
[SpringMVC] 요청 매핑, API 요청 매핑
·
dev/Spring
요청 매핑 package hello.springmvc.basic.requestmapping; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.*; @Slf4j @RestController public class MappingController { @RequestMapping(value = "/hello-basic", method = RequestMethod.GET) public String helloBasic(){ log.info("helloBasic"); return "ok"; } @GetMapping(value = "/mapping-get-v1") public String mappingGetV1(){ lo..
스프링 컨테이너와 스프링 빈
·
dev/Spring
스프링 컨테이너 생성되는 과정 ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class); @Configuration public class AppConfig { @Bean public MemberService memberService(){ return new MemberServiceImpl(memberRepository()); } @Bean public OrderService orderService(){ return new OrderServiceImpl( memberRepository(), discountPolicy()); } @Bean ... ... } ApplicationContext를 ..
[Spring] @ResponseBody 어노테이션
·
dev/Spring
@ResponseBody HTTP의 BODY에 문자 내용을 직접 반환 즉, 데이터만 전송할 경우 사용 ( API 방식 ) 객체를 return할 경우 기본 default가 json방식으로 데이터를 만들어서 http에 response 하는 게 기본! view Resolver 대신, HttpMessageConverter가 동작한다. 기본 문자 처리 StringHttpMessageConverter 기본 객체 처리 MappingJackson2HttpMessageConverter byte 처리 등등 기타 여러 HttpMessageConverter가 기본으로 등록되어 있음 @GetMapping("hello-string") @ResponseBody public String helloString(@RequestParam..
[Spring] Spring의 컨텍스트?
·
dev/Spring
Chef Class package org.zerock.sample; import org.springframework.stereotype.Component; import lombok.Data; @Component @Data public class Chef { } Restaurant Class package org.zerock.sample; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import lombok.Data; import lombok.Setter; @Component @Data public class Restaurant { @Setter(onM..
[Spring] sts3, 이클립스 Spring Legacy Project 기본 구조
·
dev/Spring