Spring
-
Redis Lock을 활용한 동시성 제어Spring/Trouble Shooting 2024. 11. 17. 17:11
프로젝트를 진행하면서 찜 목록을 관리하는 기능에서 사용하는 유저의 ID와 찜 대상의 ID를 저장하는 테이블이 있습니다.제공되는 API는 찜 상태를 추가/생성, 삭제 하는 형태로 단일 API로 제공하였는데,유저의 ID와 찜 대상의 ID 이 중복된 상태로 저장되는 문제가 발생하였습니다.제일 좌측에서 1, 2번째가 레코드의 생성 시기인데요.케이스 1의 경우 0.2초 사이에 중복으로 요청을 할 경우 발생케이스 2의 경우 0.001초 사이에 중복으로 요청을 할 경우 발생 하였습니다. 서버에서 동시성을 제어하지 못했기 때문에 발생한 문제였으므로 이를 해결해보고자 합니다.문제가 발생한 플로우입니다.요청에 대상 AnimalHospital에 대해 조회 후 현재 유저와 매핑되는 Favorite가 있는지 조회합니다.만약 레..
-
JPA -ToMany 대상으로 Fetch Join, Paging 문제점Spring/Trouble Shooting 2024. 11. 10. 19:31
빠른 정리N+1 문제를 해결하기 위해 EntityGraph를 통해 Fetch Join을 진행했는데, 결과적으로 N+1 문제는 해결하였으나 Paging이 메모리에서 수행되는 문제가 발생하였습니다.원인은 -ToMany 관계를 조회할 경우 조회시 데이터의 갯수가 실제 데이터의 갯수랑 달라져서 Paging처리를 메모리에서 진행하게 되는 것이였습니다.따라서 해결책으로 -ToOne 관계의 경우 FetchJoin을 통해 값을 가져오고 -ToMany 관계는 BatchSize를 통해 객체 조회시 추가 쿼리를 통해 값을 조회하도록 하였습니다. 문제 기록일단 ERD는 아래 이미지와 같습니다.event -> event_type 은 N:1event 와 hash_tag는 N:M 관계로 중간에 event_hash_tag 라는 테이..
-
TestFixture Monkey 객체 필드가 null로 생성되는 이슈해결Spring/Trouble Shooting 2024. 10. 20. 01:34
TestFixture Monkey를 통해 테스트 객체를 생성하는 방식을 구현 중에 다음과 같이 객체를 생성하고 테스트를 수행하는데 NPE가 발생하는 문제가 발생했습니다.public class AnimalHospitalTestFixture extends DomainTestFixture { public static AnimalHospital createAnimalHospital() { ArbitraryBuilder hospitalInfoArbitraryBuilder = SUT.giveMeBuilder(HospitalInfo.class) .set(javaGetter(HospitalInfo::getName), Arbitraries.strings().alpha().of..
-
ObjectMapper LocalTime등 날짜시간 타입 매핑 실패 해결법Spring/Trouble Shooting 2024. 1. 13. 01:21
문제 상황 테스트 코드 작성중에 void createOperatingTime() throws Exception { //given AnimalHospital savedAnimalHospital = getSavedAnimalHospital(); List requests = new ArrayList(); for (DayOfWeekStatus value : DayOfWeekStatus.values()) { HospitalOperationTimeCreateRequest hospitalOperationTimeCreateRequest = HospitalOperationTimeCreateRequest.builder() .dayOfWeekStatus(value.name()) .startTime(LocalTime.of(1..
-
ReflectionTestUtils를 활용한 단위 테스트, 객체의 ID 설정하기Spring/Trouble Shooting 2023. 11. 19. 01:21
문제 상황 질문의 번호와 유저의 번호를 받고, 질문의 작성자 번호가 유저의 번호와 같다면 삭제하는 과정이다. public void deleteById(Long questionId, Long memberId) { Question question = findById(questionId); if (validateQuestionWriter(memberId, question)) { questionRepository.deleteById(questionId); } } private Boolean validateQuestionWriter(Long memberId, Question question) { return question.getMember().getId().equals(memberId); } 그다지 이해하기 어..
-
QueryDsl 페이징 최적화 PageImpl vs PageableExecutionUtils 차이Spring/Study 2023. 11. 14. 01:07
PageImpl vs PageableExecutionUtils return PageableExecutionUtils.getPage(result, pageable, countQuery::fetchOne); return new PageImpl(result, pageable, count); 둘의 차이는 무엇일까? 상황 예시 24개의 content가 존재한다고 가정해보자 PageSize = 10 이라고 하면 페이지는 1,2,3 페이지가 존재하게 된다. 1, 2 페이지에서는 10개의 content가 채워져서 페이지 result가 반환되기 때문에 PageImpl, PageableExecutionUtils 둘 다 count 쿼리가 발생한다. 그런데 3페이지 마지막 페이지에서는 차이가 발생한다. 3페이지에서는 4개의 ..
-
Spring Test 환경에서 Audit 적용이 안될 때Spring/Trouble Shooting 2023. 11. 13. 17:13
상황 도메인의 설계 구조를 보고 가자 @Getter @Entity @NoArgsConstructor(access = AccessLevel.PROTECTED) @EntityListeners(AuditingEntityListener.class) public class Question { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; //... @Embedded private AuditEntity auditEntity; //... @Builder public Question( //.. ) //... this.auditEntity = new AuditEntity(); } } Question이라는 도메인에 AuditEntity를 ..
-
Docker 컨테이너 환경에서 Eureka Client 등록시 ip 문제Spring/Trouble Shooting 2023. 11. 7. 00:41
문제 상황 Eureka 클라이언트를 등록하고 Spring Cloud Gateway를 통해 로드 밸런싱 처리를 하도록 설정해뒀다. 하지만 로드밸런싱 처리는 되지 않았고 직접 ip 주소를 통해 요청을 할 때는 정상적으로 요청이 전달되고, Eureka Server에 등록된 클라이언트의 목록에는 이상이 없었다. 원인은? 문제는 Eureka Client가 등록될 때 클라이언트들이 외부 ip 기준으로 등록되는 것이 아니라, 내부 ip를 기준으로 등록되어 문제가 발생하는 것이였다. 원인은 배포 환경을 Docker를 통해 진행했는데, Eureka Client의 경우 컨테이너 환경에서 배포가 될 경우 알아서 실제 배포되는 ip로 등록되는 것이 아닌 내부 ip를 기준으로 등록되는 것이였다. 알아낸 방법 이를 알아낸 방법은..