본문 바로가기

📕 Spring Framework55

Spring Cloud OpenFeign 더 잘 사용해보기 마이크로 서비스에서,Spring Boot 를 이용하여 애플리케이션을 개발할 때면,외부 API 를 호출해야 하는 상황이 존재한다. Spring 프레임워크가 지원하는 여러 가지 Http Client 가 있다. RestTemplate 의 경우 Blocking 방식으로 Http 요청을 진행할 수 있다.하나의 요청을 위해, 코드를 작성하려면,재사용성을 고려한다 할때, 작디 작은 컴포넌트로 추상화를 많이 진행하여 번거로운 코드를 작성해야 한다는 점이 있었다.무엇보다 어떠한 요청을 하는지 한눈에 들어오지 않았다. WebClient 의 경우, Non-Blocking 방식으로 Http 요청을 진행할 수 있다.물론 Blocking call 도 가능하다.빌더 패턴을 활용한 방식으로, RestTemplate 보다는 가독성이 .. 2024. 5. 11.
Spring Boot 애플리케이션 k8s 환경에서 WarmUp 적용하기 이 글을 작성하는 이유 현재 회사에서는 EKS 기반 k8s 환경에서 spring boot 를 포함 각종 프레임워크 애플리케이션을 운영 중이다. 기존 모노리스 Django Rest Framework 에서 점진적으로 도메인 분리를 Spring Boot 를 이용한 Micro Service Application 으로 진행 중인데, 내가 맡은 결제 도메인 관련 애플리케이션도 마찬가지이다. (Kotlin 기반 Spring Boot App) 문제는, 결제가 주문 및 유관 DB 와 너무 강하게 얽혀 있어서 바라보아야 하는 테이블이 많다는 점이고, 이것은 곧 배포 직후 속도에 커다란 영향을 끼쳤다. JVM 은 컴파일 된 .class 파일을 필요 시 클래스 로딩을 통해 사용하며, 기본적으로 인터프리터 방식을 사용하므로 J.. 2024. 3. 17.
OutBox Pattern 을 활용한 메일 전송 서비스 개발 [At Least Once] 스프링에서 메일 전송은 정말 간단하게 구현할 수 있다. JavaMailSender 로 말이야. 단순히, 메일만 전송하는 함수만 구현하면 끝일까? 물론 상황에 따라 간단한 구현이나, 복잡한 구현이 나눠져야 한다. 실 서비스에서는? 메일로 전송해야 하는 데이터 중요도에 따라 다르겠지만, 아무래도 메일을 수신하는 클라이언트 입장에서는 서버 장애 때문에, 메일 수신이 안 될 경우 매우 당황스러울 것이다. 즉, 적어도 한번 전송 (At least once) 를 만족하는 Eventually Consistency 를 구현해야 하는 것은 메일 전송 서비스에서 기본적으로 다뤄져야 할 사항이다. 일례로, 분산 서버 환경에서는, 알림 서비스만을 다루는 애플리케이션이 존재하는데, 이때 outbox 패턴이라는 것을 사용하여 구.. 2023. 4. 20.
[Spring data JPA] N+1 문제 해결 이렇게 이루어진 ERD 모델이 있다. (예시) 조건에 맞춰 불러오고자 하는 Data는 세 테이블의 정보를 모두 필요로 한다. 이때 Fetch join을 고려해볼 수 있다. Permission 입장에서 user와 document를 두번 다 fetch join 할 수 있지만, 이능 데이터 베이스 성능 상 엄청난 문제가 있다. 데이터가 많을 경우, 연관된 엔티티의 수를 제한하는 방법으로 쿼리를 나눠서 발생시키는 것도 좋은 방법일 것이다. 해당 엔티티들은 모두 FetchType.LAZY 로 설정되어 있다. 기존 쿼리 : val documents = documentQueryService.findDocumentsByIdFetchJoinPermissions(projectId) return Response.UserPe.. 2023. 2. 28.
[Reactive Programming] 비동기-논블로킹 프로그래밍 동기 프로그래밍 (Synchronous) 작업의 실행 흐름은 순차적으로 동작 코드를 파악하기 쉽고 결과를 쉽게 예측 가능하므로 디버깅이 쉬움 특정 작업 중 다른 작업을 할 수 없다는 단점 비동기 프로그래밍 (Asynchronous) 작업의 실행 흐름은 기본적으로 순차적이지 않음 현재 실행 중인 작업 이외에 다른 작업 가능. 클라이언트, 서버 등 모든 환경에서 유용하게 사용 가능 대표적으로 CallBack, Promise, Future, Coroutine 등이 있다. 비동기 프로그래밍 구현 방식 - Kotlin Thread 가장 기본이 되는 방식 Runnable 인터페이스를 구현하여 구현 하나의 스레드 - 싱글 스레드, 다중 스레드 - 멀티스레드 fun main() { for (i in 0..5) { va.. 2023. 2. 11.
[@DataJpaTest] h2 인메모리 db를 이용한 테스트 설정 방법 서론 테스트 코드를 작성하는 것은 매우 중요하다고 익히 들었고, 필수적인 영역이기 때문에 이에 항상 만전을 기하고 있다. 모든 케이스를 테스트할 이유는 없기 때문에, 크게 경우를 나누어 필요하다고 생각하는 부분만 시나리오, 통합, 단위 테스트를 나누어 진행하는 것이 효율적이라고 들었다. 배포를 계속 해오면서 jar 파일을 빌드하는 도중 테스트를 제외하는 것은 무의미하다고 판단했기 때문에, 모든 테스트를 어떤 환경에서든 돌아 갈 수 있게 고민을 했던 기억이 있다. 이렇게 하나하나 완성도 있게 쌓아나가는 공부를 하는 것이 속도는 느리지만, 유의미한 발전 과정이라 느낀다. 이번에 대규모 리팩토링을 실시하면서 코드 수정 및 구조가 크게 개편되었고, 테스트 코드도 변경 사항이 많이 생겨서 다 수정했다. persi.. 2022. 12. 21.
[리팩토링] 도메인 모델 중심 Clean Architecture 로의 리팩토링 프로젝트를 수도 없이 리팩토링했다. 보다 더 객체지향적인 코드를 작성하기 위한, 유지 보수가 쉬운 코드를 작성하기 위한, 더 작은 객체를 위한 코드를 계속해서 고민하고 구조를 변경했다. 지난 달부터 해서 소프트 웨어 아키텍처에 관해서 관심이 생겼다. 클린 코드를 추구하다 보니 자연스럽게 설계적 고민으로 귀결되었다. 원티드 백엔드 챌린지를 하며 알게된 클린 아키텍처, 도메인 주도 설계 철저 입문, 도메인 주도 설계로 시작하는 마이크로 서비스를 읽어가며, 내가 구성해오던 소프트웨어 설계의 큰 전환점을 맞이하게 되었다. 단순히 예제 프로젝트만을 만드는게 아닌 본 프로젝트에 이를 적용시켜보기로 결정했다. MSA 는 오버 엔지니어링이라 판단했고, 모노리스 구조이지만 최대한 도메인 별 분리가 된 상위 바운디드 컨텍.. 2022. 12. 12.
[이슈] Pageable test 관련 에러 💡문제 API Controller를 테스트 하는데 잘 성공하던 테스트가 계속 실패한다. org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.IllegalStateException: No primary or single unique constructor found for interface org.springframework.data.domain.Pageable 테스트 코드 mockMvc.perform(get("/api/v2/users") .param("page", String.valueOf(page))) .andExpect(status().isOk()) .andE.. 2022. 11. 23.
[Refactor] 패키지 구조와 의존성 두 번째 프로젝트의 코드 작성이 거의 끝났고, 테스트 코드 작성을 앞두며 코드 리뷰를 받았다. 가장 큰 골자는 아무래도 참조 관계이다. 패키지 구조를 Layered에서 약간의 DDD(애매하지만 ㅎㅎ) 를 곁들인 구조로 변경했다. 그 과정에서 패키지 간 의존성에 대해서 고민해보고 작명하는 것과 설계하는 시간이 정말 오래 걸렸다. 코딩을 공부하면 할 수록 작은 것에 시간을 오래 들이게 되는 걸 느낀다. 어제는 패키지 이름을 짓는데 반나절이 걸렸다. 회사에서는 변수명 짓는 걸로도 회의를 한다고 하니 약간 실감이 나기도 한다. 이렇게 디테일하게 채워나가면 그 만큼 내 실력이 된다고 믿습니다. 최상위 구조 auth : 인증, 인가 처리 스프링 시큐리티 이용 스프링 컨테이너까지 도달하지 않는 필터 위주이기 때문에 .. 2022. 10. 14.
[Redisson] 트랜잭션 문제 발생 및 해결 지난 포스트 [Redisson]을 이용한 분산 Lock 구현 & 동시성 문제 해결 내 프로젝트의 Payment를 개발하면서 가장 기본 중에 기본이 되는 문제를 직면했었다. 그것은 바로 동시성 문제! 스프링부트의 내장 서버는 기본적으로 톰캣, 언더토우 등등의 WAS로 돌아가는데 이 sweeeetgoguma.tistory.com 지난 포스트에서 Redisson을 이용하여 동시성 문제를 해결하는 코드를 구현했다. 프로젝트 리팩토링이 거의 끝나가 조회 API를 구체화하여 몇 개 추가하던 도중, 스레드 100개의 동시 요청을 직접적으로 받는 과정을 확인하고 싶어졌다. 그래서 실험해봤다. 결과는?? 처참하다.. 무엇이 문제였을까 트랜잭션 처리가 씹혔다. @GetMapping("/test") public void t.. 2022. 10. 1.
[Redisson]을 이용한 분산 Lock 구현 & 동시성 문제 해결 내 프로젝트의 Payment를 개발하면서 가장 기본 중에 기본이 되는 문제를 직면했었다. 그것은 바로 동시성 문제! 스프링부트의 내장 서버는 기본적으로 톰캣, 언더토우 등등의 WAS로 돌아가는데 이 WAS는 멀티스레드 기반으로 동작한다. A라는 상품 (재고 3개) 을 [가]군이 2개 구매하려 한다. 동시에 [나]군이 2개 구매하려 한다. 미세하게 나마 0.00001초의 차이가 있을 수 있다. 결국 각각의 스레드가 같은 상품의 재고를 조회한다. 원래대로라면 한 명은 못 사야 정상이다. 위 문제를 해결하기 위한 방법이 뭐가 있을까? 1. Synchronized 자바로 해결하는 방법이다. Thread-Safe 하기 때문에 매우 좋아보이나, 서버가 증설될 경우 의미가 없어진다. 2. Database Lock D.. 2022. 9. 27.
결제 API 리팩토링 - [2] (feat. WebClient) https://sweeeetgoguma.tistory.com/entry/%EA%B2%B0%EC%A0%9C-API-%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81-1-feat-%EC%A0%84%EB%9E%B5-%ED%8C%A8%ED%84%B 결제 API 리팩토링 - [1] (feat. 전략 패턴) 결제 API를 리팩토링 시작하며 외부 API를 연동 부분에 대해서 생각해봤다. 기존에도 카카오페이를 이용했었고, 지금도 카카오페이를 이용할 것이지만, 추가적으로 다른 결제 API를 연동할 수 있 sweeeetgoguma.tistory.com 지난 포스팅에 이어서 작성하겠습니다~ 실제 결제 API를 호출하기 위해서는 httpClient 기반의 모듈이 필요하다. 기존에는 동기방식, 멀티스레드를 이용.. 2022. 9. 22.
결제 API 리팩토링 - [1] (feat. 전략 패턴) 결제 API를 리팩토링 시작하며 외부 API를 연동 부분에 대해서 생각해봤다. 기존에도 카카오페이를 이용했었고, 지금도 카카오페이를 이용할 것이지만, 추가적으로 다른 결제 API를 연동할 수 있을 만한 상황을 생각해봤다. 스프링을 처음 공부하기 시작할 때 읽었던 책인 개구리 (스프링 입문을 위한 뭐시기..) 책에서 스프링에서 사용하는 다양한 디자인 패턴들을 알게 되었다. 그 때는 디자인 패턴이란 것에 대해 감이 잘 오지 않았는데, 직접 적용할 기회와 상황이 없었기 때문이라고 생각해본다. 계속 면접 질문 대비해 앵무새처럼 달달 외우고 다니던 도중 직접 적용할 기회가 딱 생겼고, 객체지향 개발 2원칙인 OCP에 찰떡일 것이라는 머리 속의 외침이 울렸다. 그대로 적용해보았다. 기존 플로우 컨트롤러 서비스(카카.. 2022. 9. 20.
동시성 조회 문제 해결 및 성능에 관한 고민 [Lock, Queue, Redis] 주문 건에 대한 상품 재고 파악 동시성 관련 이슈에 대해 고민한 하루다. 프로젝트 리팩토링을 시작하며 지난 도메인들은 기본 crud API만을 다루었다. 5일동안 JPA 강의들을 수강하며 본격적으로 주문 및 결제 API 리팩토링에 다시 착수했다. 아무래도 프로젝트의 토픽이 쇼핑몰이니 주문 및 결제 파트에서 단순 CRUD가 아닌 핵심 비즈니스를 고려하고 싶어, 외부 결제 API 및 디자인 패턴을 적용한 깔끔한 코드들을 고려하며 작성하는 중이다. 대략적인 틀을 만든 뒤, 본격적으로 주문을 구현하던 중, 동시성 이슈 문제에 직면했다. 기존 동시성 문제 해결 방법 MSA를 고려했기에, 데이터 정합성 문제를 해결하기 위해 Outbox pattern을 이용중이었다. 이 방식을 통해 상품 동시성 문제를 해결했었다. .. 2022. 9. 14.
WebFlux는 무엇이고, 왜 나왔고, 언제 쓰이는가? 어제 Cart API 코드 리팩터링을 마무리하고, 리뷰를 받기 위해 PR을 올렸다. 클린 한 코드로 작성하려고 노력하기 위해 리뷰어님이 주신 의견을 되뇌고, 클린 코드를 작성하기 위한 방법을 구글링을 통해 하루 종일 모니터를 노려보며 클래스 간 책임을 나누어 보았다. 빨리 성장하고 싶다!! 리뷰를 앞두고, 카카오페이를 이용한 도메인 코드들을 리팩터링 하기 앞서, 기존에 외부 API와 통신하기 위해 사용하던 템플릿인 RestTemplate의 대체 여부에 대해 떠올랐다. RestTemplate은 deprecated 되었으므로, WebClient의 사용을 고려해보라는 의견을 받았다. WebClient에 대해 알아보다가 동기/비동기, 블로킹/논블로킹이라는 개념의 정의에 대해 다시 공부하게 되었고, 나아가 Web.. 2022. 8. 31.
반응형