이전 포스팅에서도 적었지만 위 사진은 우리의 큰 비즈니스 플로우이다. (다른 부분들은 다 제외하고 외부 API 활용 기준으로 그려봤다.) 우리 서비스는 사실상 브랜드가 이용하는 서비스 하나, 인플루언서가 이용하는 서비스 총 2개로 나눠져 있고, 이때 우리는 Cafe24라는 외부 API를 활용해서 보다 쉽게 둘을 연결해 주는데 도움을 준다. 우리 서비스에서 외부(Cafe24) API를 쓰는 3곳상품 조회: 브랜드가 우리 서비스에서 자사 상품 목록을 보고 등록할 협찬 상품을 고를 수 있도록 Cafe24 상품 조회 API을 읽어온다.할인 코드 생성: 협찬 승인 시 인플루언서별 할인 코드를 만든다.주문 조회: 매일 00:10에 전일 주문을 모아서 동기화한다.이처럼 서비스의 핵심 화면 다수에서 외부 API가 사용..
외부 API가 흔들릴 때 우리 서비스 전체가 멈추지 않도록 영향 범위를 최소화하는 건 개발자의 몫이다. 클라이언트·서버 어디서 무엇을 처리할지 합의는 필요하지만, 어떤 선택을 하든 장애나 지연, 레이트 리밋 상황을 가정한 사전 대안은 필수다. 따라서 오늘은 외부 API 호출이 실패할 때 어떻게 대응할 것인가를 주제로 글을 작성해보려 한다. 외부 API 실패 시 대응 방법1. 단순한 예외처리 전달가장 최소한의 방식은 외부 API가 돌려준 상태코드/에러바디를 받아서 그대로(or 최소한으로 가공해서) 우리 API의 에러로 반환하는 거다. 재시도도, 대체 데이터도 없이 그냥 실패를 인정하는 흐름이기 때문에 결국 사용자 화면에서는 멈추게 된다. 물론 이런 에러를 받아서 클라이언트에서 플로우 흐름을 연결해주기도 한..
내가 실제로 만드는 서비스는 브랜드(카페24 입점사)와 인플루언서를 연결해 주는 협업/정산 플랫폼이다.브랜드는 협찬할 의향이 있는 자사 상품을 등록하고, 인플루언서는 올라 온 상품들에 한해서 협찬을 신청할 수 있다. 브랜드가 이를 승인하면 인플루언서별 고유 추적 링크(어필리에이트 서비스 개념과 동일하다)를 발급하고, 해당 링크를 통해 발생한 매출을 추적해 리워드를 정산한다. 따라서 "어떤 셀럽 링크로 어떤 주문이 발생했는가"를 가능한 정확하고 신속하게 파악하는 것이 중요하다. 문제는 구매(결제)가 우리 서비스 내부에서 발생하는 것이 아니라 외부(Cafe24 쇼핑몰)에서 발생한다는 점이다. 따라서 우리는 외부(Cafe24) API를 호출해 우리 DB와 데이터를 동기화해야 한다. 그리고 단순히 주문 정보만 ..
최근 프로젝트에서는 외부 API 호출이 빈번하게 발생했다. 특히 Cafe24 API를 통해 쇼핑몰별 상품, 주문, 쿠폰 등 다양한 데이터를 가져와야 했는데, 호출 수가 많지는 않더라도 인증, URL 구성, 실패 대응 등 처리할 요소는 상당했다. 자연스레 이런 고민이 들었다.“Spring에서는 외부 API를 어떤 방식으로 호출하는 게 가장 적절할까?”이 글에서는 Java/Spring에서 사용할 수 있는 다양한 HTTP Client를 정리하고, 실제로 발생한 문제와 어떤 선택을 했는지까지 정리해보려 한다. 1. HttpURLConnectionHTTP 클라이언트를 처음 접할 때 가장 먼저 마주치는 것이 바로 HttpURLConnection이다. (물론 너무 예전에 사용된 기능이라 모를 수도 있다.) java..
이전에도 리팩토링에 대한 회고를 한 번 쓴 적이 있었다. 하지만 지금 돌이켜보면 대부분 결과론적인 이야기였다. 어떤 결과가 있었고, 그로 인해 어떤 성과가 있었는지만 말했지, 왜 그렇게 결정했는지, 어떤 대안을 고민했는지, 그 선택이 어떤 트레이드오프를 동반했는지에 대한 기록은 없었다. 그래서 이번에는 내가 리팩토링을 하며 Tech Spec을 작성하게 된 이유와, 그 과정에서 어떤 기술적 배경과 판단이 있었고 어떻게 선택했는지를 자세히 남겨두고자 한다. 1. 왜 Tech Spec을 쓰게 되었는가?새로운 프로젝트에 투입되었을 때, 너무나 명확하게 보였다. 구조는 무너져 있었고, 코드와 데이터는 얽히고설켜 있었고, DAO, Controller, Service의 경계는 없었으며, 무엇보다 이 시스템이 어떻게 ..
오늘은 새로운 팀에 들어가서 리팩토링한 지난날의 과정을 회고해보려 한다!!! 나는 새로운 프로젝트에 투입되어서 동시에 여러 서비스를 만들고 유지보수 해야 했다. 기존에는 실제 고객에게 보이는 서비스 외에, AI 서버를 통해 값을 입력받아 관리하는 CMS(Admin) 서비스도 존재했다. 하지만 이 두 시스템 모두 객체지향적 설계는 거의 고려되지 않았다. 모두 JDBC 기반으로 작성되어 있었고, 마이그레이션 파일조차 없이.. 직접 쿼리를 날린 것도 아닌,, db 툴에서 GUI를 활용해서 클릭 -> 추가 이런 식으로 수작업으로 관리하셨다고 말하고 그분은 팀을 떠나셨다.. 지금까지는 늘 새로 프로젝트를 생성해서 만들다 보니 남의 코드를 보면서 고칠 일이 없었고 실제로 유지보수성과 가독성 이런 것들이 왜 중요한지..
서비스가 많아지면서 MSA구조로 서비스를 확장하게 되었고, 자연스럽게 Spring Cloud Gateway를 도입하게 되었다. Gateway가 모든 요청을 가장 먼저 받아 각 서비스로 전달하는 구조로 바뀌면서, 기존 단일 서버 환경에서는 발생하지 않았던 CORS 에러에 직면하게 되었다. 이 글에서는 해당 문제를 겪으면서 어떤 방식으로 접근했고, 실제 원인은 무엇이었으며, 어떻게 해결했는지를 정리해보려 한다. CORS 에러란?CORS(Cross-Origin Resource Sharing)는 웹 브라우저가 다른 출처(origin)의 서버에 요청을 보낼 때 발생할 수 있는 보안 정책이다.예를 들어, 프론트엔드가 http://localhost:3000에서 실행되고 있고, API 서버가 http://localho..
오늘은 뜬금없이 MSA에서 SpringBoot Gateway 환경에서 Swagger 연동을 어떻게 하는지 정리하러 돌아왔다. 사실 요즘 프로젝트에서 갑작스럽게 MSA로 구조가 바뀌면서, 배포하고 설정하고 관리하는 일들이 갑자기 확 몰아쳤다. 이럴 때일수록 빠르고 정확한 문서화는 중요하고, Swagger를 통해 API를 통합적으로 보고 싶은데 막상 해보니 생각보다 간단하지만은 않아서 이 글을 쓰게 되었다. 프로젝트 상황과 Spring Cloud Gateway에서의 Swagger 통합우선 나는 원래는 하나의 모놀리식(Spring Boot) 프로젝트로 진행하고 있었다. 거기엔 당연히 Swagger 설정도 하나만 되어 있었다. 그런데 이번에 각기 다른 사용자 그룹을 위한 서비스를 기획하게 되면서, 구조를 멀티 ..
이번 글에서는 운영 환경에서의 데이터베이스 최적화 작업 중 인덱스 설정이 제대로 반영되지 않은 문제를 겪으면서, ddl-auto update 옵션의 치명적인 단점과 그 원인에 대해 정리해보고자 한다.실제 운영 중인 서비스에서, 한 팀원이 인덱스 설정을 통해 데이터베이스 최적화를 시도한 적이 있었다. 나는 인덱스 설정을 보다 합리적으로 개선하고, 테스트 코드로 최적화 정도를 확인해 보고자 이걸 자세히 파보려 했다. 하지만 테스트를 진행하는 과정에서 실제 운영 데이터베이스에 인덱스 테이블이 존재하지 않음을 가장 먼저 발견했다. (매우 당황스러웠지만.. 블로그를 쓸 생각에 나름 재밌기도?!)문제의 원인을 찾은 결과, ddl-auto 설정이 update 옵션으로 되어 있었던 것이 큰 패착이라는 사실을 알게되었다..
문제 발견: 중복 회원가입 문제회원가입 로직에는 중복 검증 로직이 존재하지만, 회원가입이 완료되기 전에 사용자가 버튼을 다시 누르면 중복으로 회원가입이 처리되는 문제가 발생한다. 이는 여러 요청이 동시에 처리되면서 데이터베이스의 동시성 제어가 제대로 작동하지 않기 때문이다. 이러한 문제는 데이터의 무결성을 훼손하고 리소스 낭비를 초래하며, 사용자 경험에도 악영향을 끼친다. 실제 서비스의 문제를 확인한 뒤 테스트 코드를 통해서 회원가입이 동시에 발생하는 환경을 만들어 주었을 때도 같은 오류가 나는 것을 확인할 수 있었다. 테스트는 동일한 회원가입 요청이 동시에 처리되는 상황을 시뮬레이션하고, 중복 회원가입이 방지되었는지 확인한다.threadCount = 2으로 2개의 동시 요청을 준비하고, 이를 병렬로 ..