본문 바로가기

SpringBoot39

[Spring] Local Cache vs Global Cache 성능 비교 실험을 통해 배운 점 여러 프로젝트에서 성능 개선을 위한 고민을 하면서 특히 조회 API의 경우 캐시를 도입해봐야겠다는 생각을 많이 했었다. 그동안의 각기 다른 방식으로 캐시를 적용해본 PR 내역을 정리하면 아래와 같다. 1. Local Cache Caffeine PR 링크 : 후아 프로젝트 / 키워드별 꽃 조회 API2. Local Cache EhCache PR 링크 : 세종피어 프로젝트 / 단과대 전체 조회, 스터디 교외 카테고리 전체 조회 API3. Global Cache Redis PR 링크 : 세종피어 프로젝트 / 단과대 전체 조회, 스터디 교외 카테고리 전체 조회 API 실험을 진행할 때 비교군은 동일해야하기 때문에 같은 프로젝트에서 같은 메소드에 캐시를 도입한 2번과 3번 방식을 비교해보고자 한다. 3번 방법은 .. 2024. 8. 26.
[Spring] 레디스 없이 caffeine cache로 조회 API 성능 개선 1차 MVP 이후 출시를 하고 기능 고도화를 위해 2차 MVP를 진행하고 있는 프로젝트에서 꽃다발 커스터마이징을 할 때 필요한 꽃 선택을 위해 내가 맡았던 "키워드 별 꽃 조회 API" 통신 과정이 중요했었다. 하지만 QA 당시 꽃 데이터, 특히 서로 다른 꽃말에 대해 로딩해야할 이미지가 많다보니 일부 iOS 기기에서 이미지 로딩 속도가 너무 더디다는 의견이 나왔었고 프론트에서 이미지 캐싱을 적용해보기도 하였으나 WIFI 신호에 상관없이 빠른 통신을 위해 백엔드에서도 이 문제를 해결하고자 한다. 코드는 이 링크에서 확인할 수 있다.  Caffeine Cache 도입 배경이미지 로딩 속도를 개선하기 위해 내가 생각했던 여러 대안은 아래와 같았다. 1. cloudFront 도입AWS CloudFront를 도.. 2024. 8. 26.
[Spring] SpringBoot Actuator를 왜 써야하고 어떻게 잘 써야할까 "지금은 잘 돌아가니까 문제 없다"라는 접근은 문제가 발생하기 전까지 문제를 방치하는 마음가짐이기 때문에 이전에 이 게시글에서 언급한대로 EC2 내부 문제를 겪고 CloudWatch를 통해 모니터링을 하고 있지만 Memory check뿐만 아니라 전반적인 모니터링을 springboot 환경에서 할 수 있는 방법은 없을까 찾아보다가 SpringBoot Actuator를 적용해봐야겠다고 생각(PR 링크)했다.  Spring Actuator란?Spring Actuator는 아래와 같이 build.gradle에 dependencies로 추가만 해주면 즉각 사용할 수 있기 때문에 SpringBoot 프레임워크를 바탕으로 백엔드 API를 구현할 경우 서버 모니터링 관리를 용이하게 할 수 있게 해준다. // Actu.. 2024. 8. 24.
[Spring] GET 요청 쿼리 스트링이 많을 경우 @RequestBody vs @ModelAttribute 프로젝트에서 검색 필터링 Nullable 조건의 API를 개발(구현된 PR 링크)하면서 GET mothod와 @RequestBody 어노테이션을 같이 썼었다. HTTP GET with payload body는 RFC 7231 스펙에 공식적으로 명시(공식 문서 링크)되어있듯이 이 둘의 양립이 허용되었으나 일부 클라이언트에서는 GET 요청과 함께 body를 보낼 경우 이를 무시하거나 POST로 바꿔버리는 문제가 있을 수 있다는 것을 알게 되었고 실제 프론트 친구로부터 GET + body 요청이 리액트 개발환경 상에서 안된다는 연락을 받게 되었다. 문제 상황위에서 간략하게 언급했듯이 기능 자체가 "필터링 검색"이기 때문에 프론트에서 서버로 보내야 하는 필드 값이 많아서 GET 요청임에도 request dto .. 2024. 8. 23.
[Spring] Redis @Cacheable를 이용한 조회 API 성능 개선 Redis @Cacheable을 드디어 프로젝트에 적용하여 리펙토링에 성공했고 엄청난 성능 향상을 경험했다. 이 과정을 트러블슈팅과 함께 소개해보고자 한다. 적용 대상 조회 API 탐색레디스 캐시를 적용하려는 목적 자체가 hibernate mysql db에 직접 접근 없이 == 추가 쿼리가 날라가지 않고!!!! == db 성능 개선을 하고자 하는 것이다. 즉, 조회를 할때 일반적으로 db select 쿼리가 찍힐 텐데 이러한 쿼리 없이 레디스 캐시에 저장하여 조회 속도를 높이는 것이다. 따라서 동적인, 변경 가능성이 있는 데이터를 반환하는 API가 아니라 "정적인 데이터를 조회하는 API"에 redis cache를 적용했어야 했다. 따라서 다른 조회 api들은 CRUD에 따라서 변경가능성이 있는데 반해 .. 2024. 8. 15.
[Spring] JPA와 Redis 충돌 및 @RedisHash 간과한 부분 이전 게시글에서 SpringBoot에 Redis를 적용하는 방법(링크)에 대해서 정리한 적이 있었다. 이슈 링크에서 내가 해보려는 것은 기존에 적용했던 redisTemplate를 이용하는 방법이 아니라 아직 해보지 않은 @RedisHash를 이용해 별도의 클래스를 설정하고 레디스 캐시가 필요한 곳에 대해서 @Cacheable 어노테이션을 적용하는 것이다. 전자의 경우를 진행하면서 겪은 상황을 기록하고자 한다.   기존에 이 프로젝트에서는 MySQL로 로그인 시 발급받은 RefreshToken을 저장하고 있었으며 기존 코드는 아래와 같다. package com.sejong.sejongpeer.domain.auth.entity;import com.sejong.sejongpeer.domain.member.en.. 2024. 8. 13.
[Spring] @NoArgsConstructor (access = AccessLevel.PROTECTED)의 타당성 어느 분야든 마찬가지이듯이 개념을 확실히 알고 넘어가는 습관이 개발자에게 중요한 요건이라는 것을 잘 알고 있기에 "이 코드를 왜 쓰는가?"라는 질문을 lombok annotation의 NoArgsConstructor accessLevel에 던져보았다. 하지만 그 무엇보다도 진짜 학기 중에는 괜찮다가 방학 막바지에 겔겔거리고 있는데 건강도 잘 챙겨야 개발도 계속 할 수 있다는 점을 잊지 말자... 프로젝트를 진행할 때 팀원들끼리 코드 리뷰를 하면서 엔티티 클래스에 아래의 어노테이션을 붙이는 것보다@Entity@EntityListeners(AuditingEntityListener.class)@Getter 아래 어노테이션을 추가하는 것이 더 좋지 않을까에 대한 피드백이 달린 적이 있었다.@Entity@NoAr.. 2024. 8. 11.
[Spring] Facade Pattern 적용 계기 및 이유 해당 PR(퍼사트 패턴 적용 시도한 링크)을 통해서 Facade Pattern을 처음 적용해보게 되었다. 이 디자인 패턴을 도입하기까지의 고민 과정을 기록하고자 한다. 도입 배경내가 퍼사드 패턴을 도입하려는 프로젝트를 간략하게 소개하고자 한다. 그동안 IT 분야로 오게 되면서 컴퓨터공학과 학생으로서 현재 다니고 있는 대학교 재학생들을 위한 서비스를 졸업하기 전에 한 번은 꼭 개발해서 학생들이 이 서비스를 이용하는 모습을 정말 정말 보고 싶었다. 그러기까지 서로 다른 주제의 프로젝트들을 진행하면서 시도하였으나 출시 및 운영까지 이어지기가 쉽지 않았는데 이 프로젝트(출시 링크)에서 소망을 이룰 수 있게 되어서 이거를 많이 애정한다. 주제가 재학생들을 위한 네트워킹 서비스이며 그 중 선후배, 동기 매칭을 도와.. 2024. 8. 7.
[Spring] JPA LazyInitializationException 발생 원인 및 해결 방법 생전 처음 보는 LazyInitializationException 에러를 마주했다.  문제 상황이 에러가 발생한 상황은 아래와 같다.채팅 기능을 구현하기에 앞서 채팅 방을 생성하는 API를 작성했었다.(토큰 발급 이후에 SecurityFIlterChain에서 JwtAuthenticationFilter를 addFilterBefore에 추가해주었어야 했는데 이 작업을 하지 않고 채팅방 생성 API를 요청해서 엑세스 토큰을 헤더에 넣었음에도 403에러가 떴었다. SecurityFilterChain 요소에 대해서는 입사 전에 한번 게시글로 정리하려고 한다. 다짐!) 아래 서비스 레이어에서 isDuplicate 값을 얻기 위해 stream을 돌리기 전 loginMember에서 getParticipants()를 하.. 2024. 8. 7.
[Spring] static 메소드 호출이 있는 코드를 테스트 가능하게 만드는 방법 (feat. PSA와 DI) 그동안 프레임워크 공부를 하면서 아쉬웠던 부분은 프레임워크의 본질보다 당장 API 개발에 쓰일 수 있는 "실용성"과 "구현 속도"에 더 우선 순위를 둔 점이다. 이 점을 반성하며 스프링에서 놓치고 있었거나 잘못 이해하고 있었을지 모르는 스프링의 특징을 단단하게 다지고자 한다. GitHub gitHub = GitHub.connect(); GitHub API를 쓰지 않고 테스트 코드를 작성할 수 있는 방법은?아래는 특정 깃허브 레포지토리의 이름을 input으로 받으면 이 레포지토리의 point를 계산하여 int로 반환하는 코드이다. import org.kohsuke.github.*;import java.io.IOException;public class RepositoryRank { public int get.. 2024. 8. 6.
[Spring] 반복문 CompletableFuture과 스트림 parallelStream 성능 비교 재밌는 실험을 해봤다. 한 번의 API 호출로 여러 개의 이미지 파일을 S3에 업로드해야 할 때 나는 기존에 이 게시글에서 소개한 대로 for 문으로 completableFuture을 통해 순차적으로 서로 다른 스레드 풀에서 작업을 수행하도록 비동기처리를 함으로써 각 스레드에서 output이 나오는대로 resonse에 추가하는 로직을 설계했었다. 하지만 CS 기반을 다지고자 stream에 대해 공부를 해보니 이 completableFuture 처리를 하는 기존의 코드가 for문인데 이를 stream을 이용해서 병렬처리를 해본다면 성능이 어떻게 개선될까라는 의문점이 들어 실험을 해보게 되었다. 결과 PR은 이 링크에서 코드를 확인할 수 있다. 먼저 기존 completableFuture을 활용한 servic.. 2024. 7. 26.
[Spring] AWS S3 Base64 인코딩된 이미지 처리 과정 고민 및 구현 과정 백엔드에서 이미지를 처리하는 과정은 정말 다양하다. 내가 실제로 구현해 본 방법은 아래와 같다.1. AWS S3 presigned url 발급 2. List 형식의 업로드3. base64 인코딩된 List을 디코딩하여 S3에 업로드  이 게시글에서는 3번을 적용하기까지 과정을 기록하고자 한다.   PresignedUrl의 trade-off 고민 이번 상반기에 진행했던 프로젝트 중 두 개의 프로젝트에서 내가 이미지 처리를 맡게 되었었다. 처음에는 presignedUrl을사용함으로써 제한된 시간 동안만 유효한 url을 발급하여 서버에서 직접 이미지 업로드 및 다운로드 요청을 하는 것이 아니라 프론트에서 직접 S3로 다이렉트 업로드를 함으로써 서버 부하 뿐만 아니라 네트워크 전송 비용을 줄이고자 하였다. 또한.. 2024. 7. 12.
[Spring] soft delete와 임시 저장 구별 창업팀에서 봉착한 구현 고민!현재 상황임시 저장을 하는데 DB 테이블의 deleted 필드 (soft delete)를 사용하여 구현을 한 상황이었다. private boolean deleted; 를 통해 작성 완료 여부를 나타내고 작성 완료를 누르지 않고 중간에 나가버리면 임시 저장이 가능. 소프트 삭제가 이뤄지는 방식이다. @Getter@NoArgsConstructorpublic class PromiseCatchRequestDto { private EventType event; //종목 private Integer recruitmentNumber; // 모집 인원 private RequirementType requirement; // 참여 조건 private LocalDateTim.. 2024. 6. 28.
[Spring] Querydsl fetchjoin을 통한 쿼리 수 감소 성능 향상 릴리즈 전 내가 해야할 기능 구현은 모두 완료한 상태이고 기존에 구현했던 기능을 Querydsl을 통해 쿼리 수를 줄임으로써 성능을 높여보고자 했다(pr 링크). 처음에 querydsl을 적용해보았을 때 성능이 오히려 저하가 되었는데 이번에는 querydsl을 사용하기 전보다 성능이 개선되어서 이렇게 게시글로 정리한다. querydsl을 적용하려는 상황이 프로젝트는 주제가 꽃이었다. 그래서 꽃, 꽃말, 키워드 등 고려할 것이 정말 많았고... 하나의 꽃이어도 색깔(이미지)에 따라 꽃말이 다르고 꽃말이 여러 키워드를 포함할 수 있으면서 하나의 키워드가 여러 꽃말에 대응될 수 있어서 백엔드 개발자 관점에서 연관 관계 기반 DB 설계에 대해서 정말 제대로 고민하고 공부할 수 있게 만들어 주었던 프로젝트이다. .. 2024. 6. 24.
[Spring] AWS S3로 다중 이미지 업로드 한 번의 API 호출로 프론트에서 여러 이미지 파일을 넘겨주면 한 번에 S3로 업로드할 수 있는 방법(PR 링크)을 알아보자 문제 상황 : PresignedUrl의 한계내가 지금 해야 하는 것은 Bouquet 엔티티 객체가 등록되고 난 뒤 이 객체와 일대다 연관 관계를 맺고 있는 BouquetImage 객체에 프론트에서 요청한 이미지의 url(fileName)을 저장하는 것이다. 기존에 presignedUrl로 구현하여 이미지를 서버 측에서 관리하는 것이 아니라 프론트에서 바로 s3에 접근할 수 있게 권한을 줌으로써 서버 과부하를 막고자 하였다. 하지만 bouquet create 시 유저가 최대 3개의 이미지를 업로드할 수 있는데 매번 프론트에서 POST Bouquet 객체 생성 성공 시 200 OK 이.. 2024. 5. 26.