[Spring Boot] 지도 서비스 대용량 DB 조회 성능 개선
·
Trouble Shootings/성능 개선
문제 상황소프티어 부트캠프 5기에 참여해서 개발한 팀 프로젝트에서,대학생을 위한 버스 쉐어링 예매 서비스를 개발했다.  서비스의 큰 특징은 유저가 원하는 탑승지를 설정할수 있다는 것이다.후에 어드민이 버스 노선을 배차해준다. (버스의 정원은 15명 이기 때문에, 최대 15명의 유저를 경유하도록 노선을 배차해준다) 노선을 배차해줄때는, TMAP의 경유지 최적화 api를 이용하였다.https://openapi.sk.com/products/detail?linkMenuSeq=38 SK open API장소 장소 검색 장소 통합 검색 장소 상세 정보 검색 주변 카테고리 검색 읍면동/도로명 검색 지역 분류 코드 검색 경로 반경 검색 지오펜싱 공간 검색 영역 검색 지오코딩 Reverse Geocoding Geocodi..
[Spring boot] Spring AOP - CGLIB Proxy vs Dynamic Proxy
·
Backend/Spring Boot
문제 상황usecase를 작성중 , 메소드에 @Transactional 컴포넌트를 붙이자 오류가 발생하였다 나는 이 class를 다른 클래스에 상속한 적이 없었는데, 오류 메세지를 보니 해당 클래스는 implicitly subclassed이기 때문에 open 키워드를 명시해주어야 한다고 적혀있다.즉 , 이 클래스가 나중에 상속될 가능성이 있는 클래스라는 것이다.메소드에 @Transactional 어노테이션을 제거하면, 오류가 사라지는것을 보아, 해당 문제의 원인이 @Transactional이라고 생각을 했다.  문제 원인어노테이션에 적용되는 Spring AOP의 내부적인 구조를 알게 되면 이 문제의 원인을 알 수 있다. Spring AOP는 Proxy패턴을 기반으로 동작하는데, 런타임시 동적으로 프록시 ..
[SpringBoot] QueryDsl의 Pagenation + PageableExecutionUtils을 이용해 count 쿼리 최적화
·
Backend/Spring Boot
JPA Repository를 사용할 때 public interface PostRepository extends JpaRepository { Page findByTitle(String title, Pageable p); } JPA repository는 Pageable을 매개변수로 받을 시 , 반환형을 Page로 추상화 해준다.    QueryDsl을 사용할 때@Repository@RequiredArgsConstructorpublic class PostRepositoryImpl implements PostRepositoryCustom { private final JPAQueryFactory queryFactory; @Override public Page findByTitleCustom(Str..
[Spring boot] soft delete에서 duplicate entry 문제
·
Trouble Shootings/버그 해결
현재 상황현재 나는 모든 Entry를 soft delete로 구현을 하였다.soft delete를 적용한 이유는, 현재 프로젝트가 3D모델 저장소인 만큼, 삭제 복구 기능이 필요할 것이라고 판단하였기 때문이다.따라서 유저는 삭제(회원 탈퇴, 리포지토리 삭제)를 해도, 물리적 DB에서 해당 튜플이 삭제되는것이아닌,Entry의 delete_at 필드가 갱신된다. 삭제가 되지 않은 entry는 deleted_at IS NULL로 구분하고 있다. 또한, JPA의 Unique Constraints로 {userId,title} 를 설정해 두었다. 이는 유저가 중복된 title의 게시글을 막기위해서인데, 여기서 문제가 발생했다. 문제가 발생한 상황테스트를 하고있었는데, 어느순간 갑자기 duplicate title이 ..
[Spring boot] Redis로 JWT 관리하기
·
Trouble Shootings/성능 개선
문제 상황프론트엔드와 협업중에 있었는데, 한가지 성능 이슈를 발견했다. 프론트엔드에서 header를 렌더링할때/api/auth/check-token을 매번 호출해서 user Id, profile image, nickname등을 가져와 렌더링 한다. 그런데 header의 특성상 어느페이지에서나 존재하므로, 페이지를 이동할때마다 DB에서 token정보를이용해 user정보를 반환하는 select 쿼리문이 매번 실행된다. -> 유저의 수가 늘어날 수록 DB접근 빈도가 늘어나 성능 문제가 발생할 수 있다. 이를 Redis를 이용한 캐시로 Token에 대한 User profile 정보를 저장하기로 하였다.  구현 나는 내 프로젝트를 최근 DDD + 헥사고널 아키텍쳐로 리팩토링 하였다. 리팩토링한 아키텍쳐에 맞게, ..
[Spring boot] JPA에서 Slice와 Page의 차이
·
Backend/Spring Boot
Slice public interface Slice extends Streamable { int getNumber(); int getSize(); int getNumberOfElements(); List getContent(); boolean hasContent(); // ...} JPA에서 정의한 Slice 인터페이스는 다음과 같다. 현재 페이지의 데이터들을 불러오고, 다음 페이지가 있는지 여부를 확인한다. Page public interface Page extends Slice { long getTotalElements(); int getTotalPages(); //...} Page 인터페이스는, Slice를 상속받기..
[Spring Boot] JPA N + 1 문제 직면과 해결, 그리고 Fetch Join
·
Trouble Shootings/성능 개선
문제 상황내 프로젝트에는 최근 DDD와 헥사고날 아키텍쳐로 리팩토링을 하게되었는데 JPA 영속성 객체인 UserEntity와 User라는 POJO 도메인을 따로 분리시켜서 관리하고있다따라서 모든 도메인로직은 외부 의존성이 없는 순수 자바 코드로 구현하고, 영속성은 JPA Entity가 따로 맡아서 관리한다. 그렇게 분리하게되면, Entity 객체 Domian 객체간의 mapping이 필요하다. (나의 경우 PersistenceAdapter에서 UserEntity를 DB에서 로드한후에, Mapper를통해 User 도메인으로 변환시켜서 Service에게 반환한다.) 그런데 여기서 문제가 발생했다. UserEntity에는 OneToMany로 이루어진 관계들이 많았기에, N+1문제가 발생하는것이었다. UserE..
[Springboot + Redis] 레디스를 이용한 캐싱을 통해 API 성능 개선하기 ( +JMeter)
·
Trouble Shootings/성능 개선
Github를 오마주한 3D model 버전 관리 웹 사이트를 만드는 프로젝트를 하던중 한가지 문제점을 발견했다.유저가 Mypage에 들어갈때마다, 매번  내 정보 (닉네임, 이메일 , 팔로워 수, 팔로잉 수)를 받아오는 API와, 내 리포지토리 리스트를 불러오는 API를 매번 호출하게되어, 응답시간이 매우 느리다는 것이었다. 유저가 새로고침할때마다, API호출을 매번 하게되면 서비스가 성장하여 유저 트래픽이 많아지게되면 DB에 큰 부하가 가게 된다. 따라서 나는 처음에 Spring boot 프레임워크에서 자체적으로 지원하는 Cache 라이브러리를 사용해 , 서버의 로컬 메모리에서 캐싱을 하도록 구현하였다.  결과적으로 응답속도는 매우 빨랐다. 왜냐하면 서버 내부의 메모리에서 다이렉트로 접근하기 때문에,..