본문 바로가기

SpringBoot34

[Spring] 양방향 맵핑 순환 참조 문제 해결 스프링부트 공부를 하고 처음 마주친 에러의 해결 과정에 대해서 기록하고자 한다. 해당 이슈는 이런 문제가 생길 수 있다~라고 대략적으로 들어보기만 했는데 실제로 맞닥뜨린 것은 처음이었다. Let's Go 문제 상황 : 객체 직렬화 시 연관된 양방향 매핑으로 인한 순환 참조 문제기존에 내가 원하던 것은 클라이언트의 req와 해당 요청에 대해 내 서버가 어떤 응답을 내렸는지를 로그 파일로 저장해두기 위해 작성했던 코드에서 발생한 에러였다.@Transactionalpublic BouquetCustomizingResponseV2 updateBouquet(BouquetCustomizingRequest request, Long memberId, Long bouquetId, List multipartFiles) { .. 2025. 4. 5.
[Spring] 단일 서버 환경에서 API 기능 동시성 문제 고려하기 2024년 상반기에 진행했던 프로젝트를 2025년 상반기(현재)에 다시 훑어보며 과거에는 신경쓰지 못했던 관점이 무엇이었는지 돌아보고 있는 시간을 가지고 있다. 내가 선정한 여러 관점 중 첫번째 관점이 "동시성 Concurrency" 문제였다. 동시성 문제 상황layered-architecture 상에서 studyRelationService는 교내 재학생들을 위한 스터디 "지원 현황" 이력을 관리하는 파트이다.내가 수정을 해야겠다고 느꼈던 부분은 아래 api들이었다.1. 스터디 지원 취소 API : studyRelation의 status를 CANCEL로 업데이트 한다2. 스터디 게시자가 지원자들을 수락/거절하는 상태 업데이트 API : 지원자의 studyRelation의 status를 ACCEPT || .. 2025. 3. 10.
[Spring] 콘솔 로그를 어떻게 잘 관리할 수 있을까 개발을 하다보면 트러블 슈팅을 해야할 일이 정말 많이 생긴다. 버그를 해결하는 과정에서 개발 테스트 상 스프링부트를 재실행하면 콘솔 로그가 초기화되면서 직전 로그 내역을 다른 곳에 기록해두지 않는 이상 확인을 할 수 없게 된다. 이뿐만 아니라 프론트에서 백엔드 서버로 API 요청 시 에러 사항에 대해서 확인하고 싶을 때 ec2에 접속해서 docker-compose logs(tail 옵션 붙여서 최근 로그를 바로 확인)를 통해 해당 에러가 언제 생겼는지 훑어야 하는 불편함이 존재한다. 따라서 로그의 영구적인 관리 중요성을 느끼고 WHOA 서비스에 (이슈 링크) 적용해보고자 했다. 먼저 콘솔 창에 로그를 출력할 때 System.out.println을 쓸 경우 성능 저하가 상당하기 때문에 loggerFacto.. 2024. 12. 14.
[Spring] 반복적인 추가 쿼리 @EntityGraph와 @Cacheable을 통한 성능 개선 WHOA 서비스는 주제가 꽃이기 때문에 하나의 꽃 종류에 대해서도 색에 따라서 꽃말이 달라질 수 있다는 점을 고려해 꽃, 꽃말, 꽃이미지, 키워드 테이블 간 도메인 지식을 반영해 연관 관계가 맺어져 있다. 그 중에서 "색깔에 따라 꽃말이 달라질 수 있는 부분"을 고려해서 꽃과 꽃말은 일대다, 꽃말과 꽃이미지는 일대일 양방향, 꽃말과 키워드는 다대다이기 때문에 매핑 테이블을 추가로 두어 1:N:1 매핑을 하고 있으며, 키워드는 꽃다발 구매 목적 테이블과 다대다 관계를 이루어 이 중간에도 1:N:1 관계의 매핑 테이블을 두고 있는 구조이다. 문제 상황 이 서비스 특성 상 특정 꽃에 대해서 특정 꽃말에 따라서 그에 맞는 꽃 사진을 보여주는 로직이 많다. 그래서 꽃말 → 꽃이미지로 get을 통해서 꽃이미지 객체.. 2024. 12. 8.
[Spring] RestTemplate -> WebClient 외부 API 호출 방식 변경 및 bulk 처리 지금 진행 중인 WHOA 서비스는 @Scheduler cron annotation을 통해 매주 월요일 자정마다 외부 API 호출을 하여 꽃 정보를 가져오고 있다. 기존 스케쥴러 작동 방식은 아래와 같다. 1. localDateTime now()를 통해 dateFormatter로 외부 api 요청 형식에 맞게 포매팅을 해서 service-key로 화훼 사이트에서 데이터를 요청한다2. restTemplate를 통해 apiUrl로 get 요청을 보내 JsonNode 형식의 객체를 응답 받는다3. 응답 json 형식에 따라 JsonNode 객체를 읽기 위해 readTree, path 내장함수를 이용해 key-value 형식의 값을 읽어들인다4. 외부 api 응답 값 중 db에 저장해야 하는 데이터를 선별하여 M.. 2024. 12. 7.
[Spring] DTO 반환 시 필요한 응답만 QueryProjection으로 매핑하여 반환 요새 병행하고 있는 일뿐만 아니라 내가 접해보지 못했던 새로운 지식을 적용해볼 수 있도록 하기 위해 기존의 사이드 프로젝트를 지속 및 유지하고 있는 중이다. 지금 내가 병행하고 있는 프로젝트는 2024년 극초반부터 진행했던 WHOA이다. WHOA는 iOS 앱스토어에 2차 릴리즈까지 마치고 3차 MVP를 개발하고 있는 꽃다발 커스터마이징 서비스이다. 백엔드는 현재 내가 혼자 맡고 있는데 이전에 함께 작업하시던 분께서 작업하시던 코드를 코드 성능 개선 및 리펙토링 목적으로 찬찬히 뜯어보고 있던 도중 비효율적이고 반복적인 쿼리가 발생하는 것을 알게 되었다.현재 게시글은 모두 다른 분의 코드를 보고 이해하며 성능 개선을 한 사례에 대한 글이다. 문제 상황성능 개선이 완료된 pr 링크WHOA 프로젝트는 화훼 .. 2024. 12. 7.
[Spring] Widget 구현 방안 보호되어 있는 글 입니다. 2024. 9. 29.
[Spring] ElasticSearch 검색 필터링 API 고도화 고민 : 쿼리 라이브러리 비교 추석 연휴를 제외하고 9월 12일부터 elasticSearch 기반의 조회 API(검색 필터링 기능) 작업을 시작하여 이번주(9월 3주차) 금요일까지 12일, 13일, 19일, 20일 elasticDB 습득 과정부터 실제 개발 과정까지 포함해 총 4일에 거쳐 드디어 전체 조회 시 requestParam으로 받은 여러 필드에 대해서 null 조건까지 포함하여 필터링을 하는 로직을 드디어 구현해냈다. 내가 ElasticSearchRepository, ElasticsearchClient, ElasticsearchOperation을 도입한게 처음이었던 것과 관계 없이 주어진 일정 기간 안에 구현을 끝냈어야 했고 앞으로 구현해야 할 부분의 마감 기간까지 고려했을 때 20일까지는 적어도 이 파트를 끝냈어야 차질이 .. 2024. 9. 22.
[Spring] SecurityFilterChain 구성 요소 보호되어 있는 글 입니다. 2024. 9. 1.
[Spring] @WebMvcTest를 이용한 API 테스트 코드 작성 시행착오 개발을 하면서 동시에 테스트 코드 작성까지 했으면 퍼펙트했겠지만 학기 중에 학점을 꽉 채워 듣고 다른 여러 가지를 병행하고 있었기 때문에 시간적으로 테스트 코드를 작성하지 못하였는데 2차 MVP에서는 팀의 목표와는 별도로 내가 맡은 API에 대해서 테스트 코드도 작성해보려고 한다. 앱 실행 스플래쉬 단계에서 먼저 프론트에서 연결해야 했던 API에 대한 테스트 코드를 작성하며 겪었던 시행 착오와 알게된 지식을 기록하고자 한다.  이 게시글에서 소개하는 최종 테스트 코드 작성은 이 PR 링크에서 확인할 수 있다.  테스트 코드를 작성하려는 API 소개테스트 코드를 작성하려는 API pr 링크이다. 이 이후로 리펙토링을 하여 아래와 같이 되었다. @RestController@RequiredArgsConstru.. 2024. 8. 27.
[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.