분류 전체보기
-
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..
-
CORS 정리ETC 2024. 10. 6. 23:18
CORS에 대한 사전 지식CORS에 대해 서술하기 전에 사전에 알아둬야 할 내용을 정리해봤습니다.CORS 란?CORS(Cross-Origin Resource Sharing)로 한국어로 해석하면 "교차 출처 자원 공유"입니다.의미를 풀자면 서버가 다른 출처로 부터의 접근을 허용하거나 제한하는 HTTP 기반 보안 정책입니다.CORS가 생긴 이유해킹 기법중 하나인 XSS(Cross Site Scripting)과 CSRF(Cross Site Request Forgery)라는 공격과 관련이 있습니다.XSS와 CSRF 공격을 막기 위해 SOP(Same Origin Policy)라는 정책이 브라우저에 적용이 되었는데이로 인해 서버 클라이언트(브라우저) 통신이 불가능하자 이 문제를 해결하기 위해 CORS가 생겨난 것..
-
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를 ..