본문 바로가기

Framework

(60)
[Spring Boot] 동시성 제어의 절대 반지 - 낙관적 락(Optimistic Lock) vs 비관적 락(Pessimistic Lock)의 철학과 분산 환경 재시도(Retry) 메커니즘 지금까지 우리는 조회(Read) 쿼리를 어떻게든 최적화하여 10만 명의 조회를 0.1초 만에 처리하는 극강의 성능 아키텍처를 만들어냈습니다. 하지만 데이터베이스 세계에서 가장 무섭고 까다로운 재앙은 조회할 때 터지는 것이 아니라, 여러 명의 유저가 "동시에 하나의 데이터를 수정(Write/Update)하려 달려들 때" 터집니다.유명 가수의 콘서트 티켓팅이나 한정판 명품 선착순 판매(Draw) 이벤트가 열렸다고 상상해 봅시다. 남은 재고는 딱 1개입니다. 그런데 A 유저와 B 유저가 0.001초의 오차도 없이 동시에 결제 버튼을 누릅니다. 두 명의 톰캣 쓰레드는 DB에서 남은 재고가 1개인 것을 동시에 확인(SELECT)하고, 각자 재고를 0으로 수정(UPDATE)한 뒤 커밋해 버립니다. 어? 재고는 1개..
[Spring Boot] QueryDSL 심화 - 동적 쿼리, 서브 쿼리(Sub-Query), 그리고 튜플(Tuple) 추출과 DTO 프로젝션 성능 최적화의 끝판왕 우리는 지난 58단계를 통해 JPA의 고질적인 퍼포먼스 버그인 N+1 문제를 @BatchSize 설정 하나로 우아하게 부숴버렸습니다. 정적인 조회 쿼리, 즉 검색 조건이 딱 정해져 있는 findAllByUsername(String username) 같은 쿼리는 스프링 데이터 JPA가 제공하는 메서드 이름명명 규칙(Query Method)이나 @Query 애노테이션으로 작성한 JPQL만으로도 충분히 아름답게 처리할 수 있습니다.하지만 여러분이 실무에서 마주하게 될 진짜 난관은 '어드민 백오피스(Admin Backoffice)' 화면입니다. 검색 폼에 아이디, 이름, 휴대폰 번호, 주문 날짜 범위, 회원 등급 등 무려 10개가 넘는 검색 필터 조건이 존재하고, 관리자가 이 중 몇 개의 조건을 입력할지 실행 ..
[Spring Boot] JPA N+1 문제의 완전한 종결 - Fetch Join의 함정과 EntityGraph, @BatchSize 심화 실무 튜닝 가이드 "어? 분명히 컨트롤러에서 teamRepository.findAll() 메서드 단 하나만 깔끔하게 호출했는데, 콘솔 창을 보니 SELECT 쿼리가 왜 101개나 미친 듯이 우수수 쏟아지는 거지?! 내 서버 해킹당했나?!" 스프링 데이터 JPA를 처음 접하는 1~2년 차 주니어 개발자들이 가장 먼저 마주하게 되는 거대한 절망의 벽. 그리고 백엔드 기술 면접관이 단골로 물어보지만, 대다수의 지원자가 "연관관계 매핑할 때 지연 로딩(LAZY)을 쓰면 해결되는 거 아니었나요?"라며 기초적인 헛소리를 하다가 광탈하게 만드는 최악의 성능 저하 원흉. 바로 전설적인 'N+1 문제(N+1 Problem)'입니다. 구글링을 해보면 수많은 기술 블로그들이 N+1 문제의 유일한 해결책으로 단순히 Fetch Join을 사용하..
[Spring Boot] 다중 DB 트랜잭션의 딜레마 - JTA(2PC) 글로벌 트랜잭션과 ChainedTransactionManager의 치명적 한계, 그리고 SAGA 패턴으로의 진화 여러분이 다니는 회사의 비즈니스가 급성장하면서, 10년 동안 온갖 레거시(Legacy) 데이터가 꼬여있는 기존의 오라클(Oracle) DB 서버로는 한계를 느끼기 시작했습니다. 그래서 새로운 마이크로서비스(MSA) 모듈을 런칭할 때는 최신의 트렌디한 MySQL 8.0(또는 PostgreSQL)을 도입하기로 결정했습니다. 축하합니다! 이제 여러분이 담당하는 스프링 부트 애플리케이션은 두 개의 완전히 다른 이기종 데이터베이스(Heterogeneous Database)와 동시에 커넥션을 맺고 통신해야 합니다. 여기까지는 단순히 application.yml에 DataSource를 두 개 적으면 되는 쉬운 일입니다. 그런데 여기서 백엔드 시스템의 데이터 무결성을 파괴하고 개발자들의 등골을 오싹하게 만드는 최악의 ..
[Spring Boot] DB Replication (Master-Slave) - AbstractRoutingDataSource 라우팅을 통한 읽기/쓰기 분리(Read/Write Splitting) 마스터 아키텍처 완벽 구현 가이드 우리는 지금까지 단 하나의 물리적인 데이터베이스(DB) 인스턴스만을 바라보고 스프링 부트 코딩을 해왔습니다. 트래픽이 적은 토이 프로젝트나 B2B 인트라넷 서비스일 때는 아무런 문제가 발생하지 않습니다. 하지만 서비스가 폭발적으로 성장하여 동시 접속자가 1만 명, 10만 명을 돌파하는 순간, 단일 DB는 엄청난 비명을 지르며 서버가 통째로 뻗어버리게 됩니다.백엔드 개발자가 아무리 애플리케이션 단(WAS)에서 Redis 로컬 캐싱(Cache)을 바르고 JPQL 쿼리를 미친 듯이 튜닝해도, 본질적으로 수만 건의 SELECT 조회 요청과 수천 건의 INSERT/UPDATE 쓰기 요청이 단 하나의 DB 하드웨어 자원(CPU 연산, Disk I/O, 메모리 버퍼)을 갉아먹으면 병목(Bottleneck) 현상은 물..
[Spring Boot] 카오스 엔지니어링 (Chaos Engineering) - 프로덕션 환경에 원숭이를 풀어 고의로 장애를 유발하고 서버의 회복력(Resiliency) 테스트하기 우리는 지난 54단계까지 단위 테스트(BDD), 통합 테스트(Testcontainers 격리), 커버리지 강제 검증 게이트(Jacoco/SonarQube), 그리고 극한의 성능 테스트(JMeter/nGrinder)라는 완벽한 4중 방어막을 구축했습니다. 이제 내 코드는 논리적으로 버그가 0개이며, 초당 1만 건의 트래픽(TPS)을 가뿐히 버텨냅니다. 완벽하죠? 하지만 실무의 거친 프로덕션(Production) 세계는 당신의 논리 정연한 코드와 테스트 결과를 비웃습니다.클라우드 벤더사(AWS, GCP)의 데이터센터 스위치가 갑자기 벼락을 맞아 타버려서 DB와의 네트워크가 5초간 단절된다면? 우리가 결제를 연동 중인 외부 PG사(카카오페이, 토스)의 API 서버가 트래픽 폭주로 인해 갑자기 500 에러를 뿜..
[Spring Boot] 성능 테스트 (Performance Test) 심화 - JMeter와 nGrinder로 서버 임계점(TPS) 추적하고 병목(Bottleneck) 완벽 타파하기 대규모 마케팅 선착순 이벤트 오픈 전날 밤, 텅 빈 서버에 혼자 조용히 접속해 결제 버튼을 누르며 "오, 응답 속도가 0.1초네! 서버 개발 진짜 완벽하게 잘했다!"라고 자화자찬하는 주니어 백엔드 개발자의 안도감은, 다음 날 오전 10시 정각 이벤트 오픈 5분 만에 지옥의 비명으로 바뀝니다. 선착순 쿠폰을 받기 위해 1만 명의 유저가 동시에 접속하여 광클을 시작하는 순간, 스프링 부트 내장 톰캣(Tomcat)의 쓰레드 풀(Thread Pool)이 단 1초 만에 꽉 차버리고, DB 커넥션 풀(HikariCP)이 고갈되며, 결국 화면에는 "502 Bad Gateway" 혹은 "504 Gateway Timeout"이라는 처참한 메시지와 함께 서버가 통째로 기절해 버리기 때문입니다.실무 백엔드 엔지니어의 진짜 ..
[Spring Boot] Jacoco와 SonarQube - 코드 커버리지 80%의 벽과 정적 분석으로 레거시 악취(Code Smell) 완벽 제거하기 앞선 51, 52단계 포스팅을 통해 우리는 Testcontainers의 DB 격리와 BDD 철학, Mockito를 활용하여 훌륭하고 빈틈없는 단위 테스트 코드를 작성하는 법을 배웠습니다. 하지만 개발팀의 규모가 10명, 50명으로 커지고 코드가 거대해지면, 누군가는 반드시 바쁘다는 핑계를 대거나 귀찮다는 이유로 테스트를 짜지 않고 슬쩍 프로덕션 브랜치(Branch)로 코드를 병합(Merge)하려 들 것입니다. "김 대리님, 이번에 PR(Pull Request) 올리신 코드, 테스트는 꼼꼼하게 다 작성하셨죠?"라는 사람의 말과 양심에 의존하는 검증은 더 이상 통하지 않습니다. 수동 코드 리뷰(Code Review) 과정에서 인간의 눈으로 수만 줄의 PR을 훑어보며 테스트가 빠진 엣지 케이스를 모두 찾아내는..
[Spring Boot] JUnit 5와 Mockito 심화 - BDD 철학과 엣지 케이스를 뚫어내는 단위 테스트의 정석과 실무 노하우 개발자들이 모인 컨퍼런스나 술자리에서 "TDD(Test-Driven Development, 테스트 주도 개발)를 실무에서 엄격하게 지키시나요?"라는 질문이 나오면 십중팔구 깊은 한숨과 변명이 쏟아집니다. "스타트업이라 일정이 너무 바빠서요", "기획과 요구사항이 매일같이 뒤집어지는데 테스트부터 짜면 코드를 두 번씩 고쳐야 해요". 네, 맞습니다. 실무의 치열하고 급변하는 비즈니스 환경에서 모든 코드를 교과서적인 TDD 방식(Red-Green-Refactor)으로 짜는 것은 이상론에 가깝습니다. 하지만 그렇다고 해서 '단위 테스트(Unit Test)' 자체를 작성하는 것을 포기하는 것은 개발자로서의 책임을 방기하는 죄악입니다. 내가 심혈을 기울여 짠 결제 로직이 장바구니에 중복 쿠폰을 먹이고 마일리지를 최..
[Spring Boot] Spring Boot Test 격리 - @SpringBootTest의 재앙과 Testcontainers를 이용한 완벽한 DB 테스트 구축 우리는 지난 50개의 포스팅을 통해 스프링 부트 생태계의 거대한 아키텍처와 다양한 기술 스택들을 낱낱이 해부하며 애플리케이션을 완성해 왔습니다. 하지만 현업에서 모든 비즈니스 로직을 다 짜고 나서 "기능 개발 다 끝났습니다!"라고 당당하게 말하는 주니어 개발자에게, 산전수전 다 겪은 시니어 개발자나 테크 리드가 가장 먼저 던지는 싸늘한 질문은 단 하나입니다."그래서, 테스트 코드는 다 작성하셨나요?"수만 줄의 코드가 유기적으로 얽혀있는 실무의 거대한 마이크로서비스(MSA) 환경이나 대형 모놀리식(Monolithic) 환경에서, 자동화된 테스트 코드가 없는 애플리케이션은 브레이크가 고장 난 상태로 시속 200km로 달리는 스포츠카와 같습니다. 언제 어디서 누구의 코드와 충돌하여 대폭발을 일으킬지 모르는 시..
[Spring Boot] Java 21 Virtual Threads (가상 쓰레드): 패러다임의 혁명과 시리즈의 피날레 우리는 앞서 29단계에서 대규모 트래픽을 감당하기 위해 WebFlux(리액티브 프로그래밍)를 배웠습니다. 톰캣(Tomcat)의 쓰레드가 DB 응답을 기다리며 멍때리는(Blocking) 현상을 없애기 위해, 코드를 Mono와 Flux라는 상자로 감싸고 flatMap으로 떡칠하는 기괴한 비동기 코드를 짜야만 했습니다. 러닝 커브는 끔찍했고 레거시 JPA와의 궁합은 최악이었습니다. 백엔드 개발자들은 눈물을 흘리며 외쳤습니다. "아니, 코드는 옛날처럼 위에서 아래로 읽히는 평범한 동기식(Blocking)으로 짜면서, 멍때릴 때 쓰레드 낭비만 안 하게 해주는 마법은 없나요?!" 자바를 만든 오라클(Oracle)은 10년 넘게 진행된 프로젝트 룸(Project Loom)을 통해 마침내 해답을 내놓았습니다. "운영체..
[Spring Boot] SonarQube (소나큐브): 코드 퀄리티를 감시하는 무자비한 경찰관 지금까지 우리는 스프링 부트의 온갖 심화 기술과 아키텍처를 총동원하여 서비스를 만들었고, 완벽한 테스트 코드(Testcontainers)까지 짜서 동작을 보증했습니다. 그런데 '동작하는 코드'가 곧 '좋은 코드'일까요? 후임 개발자가 들어와서 메서드 하나를 1,000줄짜리 스파게티로 짜놓고, 하드코딩된 비밀번호를 소스에 박아두고, 아무도 안 쓰는 쓰레기 변수(Dead Code)를 잔뜩 남겨둔 채 테스트만 턱걸이로 통과시켜서 배포를 요청했습니다. 바쁜 시니어 개발자가 눈으로 수천 줄의 PR(Pull Request)을 읽으며 이 악취(Code Smell) 나는 코드를 다 걸러낼 수 있을까요? "코드를 실행시키지 않고(Static), 그냥 텍스트 자체만 쓱 훑어보고 논리적 오류, 복잡도, 보안 취약점을 완벽하..
[Spring Boot] Testcontainers: 완벽히 고립된 통합 테스트의 마법 "분명히 내 컴퓨터에선 테스트가 100% 통과했는데, 젠킨스(CI)에 올리기만 하면 빌드가 실패해요!" 개발팀에서 하루가 멀다고 들리는 비명입니다. 20단계에서 배운 단위 테스트(Unit Test)는 순수한 자바 코드만 검증하므로 환경을 타지 않지만, 실제 쿼리가 날아가는지, Redis에 값이 잘 꽂히는지 확인하는 통합 테스트(Integration Test, @SpringBootTest)는 외부 인프라(MySQL, Redis)의 상태에 극도로 의존합니다. 철수의 노트북엔 MySQL 8.0이 있고 영희의 노트북엔 MySQL 5.7이 깔려있으며, 젠킨스 서버에는 아예 DB가 안 깔려있다면? 이 '테스트 환경 불일치'를 해결하기 위해 과거에는 H2 인메모리 DB로 흉내를 내봤지만, H2는 실제 MySQL 전용..
[Spring Boot] Flyway (플라이웨이): 데이터베이스 형상 관리의 정석 주니어 개발자들의 배포 날 밤, 가장 많이 터지는 대참사가 무엇일까요? 바로 "자바 코드는 최신으로 배포했는데, 운영 DB에 컬럼(Column) 추가하는 걸 깜빡해서 서버가 죽는 현상"입니다. JPA hibernate.ddl-auto=update 기능이 있긴 하지만, 실무 운영 서버에서 이 옵션을 켜는 것은 "내 손으로 DB를 날려먹고 사직서를 쓰겠다"는 선언과 같습니다(절대 사용 금지). 결국 개발자가 수동으로 ALTER TABLE 쿼리를 짜서 DBA에게 넘겨주거나 직접 날려야 하는데, 이 과정에서 사람이 실수하거나 쿼리를 누락하면 걷잡을 수 없는 재앙이 벌어집니다. "소스코드는 Git으로 1.0, 1.1 버전을 깔끔하게 관리하면서, 왜 데이터베이스 스키마(DDL)는 원시인처럼 구두로 관리하는가? DB..
[Spring Boot] 멀티 모듈(Multi-Module) 아키텍처: 거대해진 모놀리스의 분리 작은 쇼핑몰로 시작했던 우리의 스프링 부트 프로젝트가 3년 만에 거대해졌습니다. 처음에는 src/main/java 폴더 하나에 컨트롤러(API), 도메인(Entity), 그리고 새벽에 도는 배치(Batch) 코드까지 몽땅 때려 넣고 행복하게 개발했습니다. 그런데 어느 날, 배치 팀 개발자가 User 엔티티에 필드를 하나 추가하고 배포를 했더니, 갑자기 API 팀의 로그인 서버가 런타임 에러를 뿜으며 죽어버립니다. "아니, 나는 배치 코드만 고쳤는데 왜 API 서버가 죽어?!" 하나의 거대한 프로젝트(모놀리식) 안에서 모든 코드가 뒤엉켜버린 스파게티 의존성의 저주입니다. 그렇다고 당장 38단계에서 배운 MSA(마이크로서비스)로 완전히 찢자니 서버 인프라 비용과 관리의 압박이 너무 심합니다. "하나의 프로젝..
[Spring Boot] WebRTC 화상 회의: P2P 시그널링 서버의 완성 여러분이 당근마켓의 개발자인데, 사용자들이 중고 물품을 실시간으로 보여주며 대화할 수 있는 '1:1 영상 통화' 기능을 만들어 달라는 미션을 받았습니다. 일반적인 생각으로는 철수의 스마트폰 카메라(영상) 데이터를 스프링 부트 서버로 쏘고, 서버가 그걸 다시 영희에게 쏴주는(Relay) 방식을 떠올립니다. 하지만 영상 데이터는 1초에 수십 메가바이트(MB)를 차지합니다. 1,000명이 동시에 영상 통화를 한다면 서버의 네트워크 대역폭(Bandwidth) 요금만 한 달에 수억 원이 청구될 것이며 서버는 펑 하고 터질 것입니다. "서버를 거치지 않고, 철수 폰에서 영희 폰으로 직접(Peer-to-Peer, P2P) 영상 데이터를 쏴버릴 수는 없을까?" 이 미친 효율의 혁신적인 기술이 바로 웹 브라우저 간의 다..
gRPC 통신: JSON을 버리고 초고속 바이너리(Binary)를 달다 우리는 지금까지 프론트엔드와 백엔드가 데이터를 주고받을 때 당연하다는 듯이 { "name": "철수", "age": 25 } 같은 JSON (JavaScript Object Notation) 텍스트 포맷을 사용해 왔습니다. 사람이 읽기 쉽고 브라우저에서 잘 돌아가기 때문입니다. 그런데 마이크로서비스(MSA) 환경에서 뒷단의 [주문 서버]와 [결제 서버]끼리 통신할 때도 과연 사람이 읽기 쉬운 JSON을 써야 할까요? "name": 이라는 껍데기 글자를 파싱(Parsing)하느라 서버의 CPU와 네트워크 대역폭이 엄청나게 낭비됩니다. 구글(Google) 아저씨들은 참다못해 "서버끼리 통신할 때는 사람 눈치 보지 마! 텍스트(JSON) 집어치우고 컴퓨터가 제일 좋아하는 0과 1(이진수, Binary)로 압축..
GraphQL (그래프QL): REST API의 한계를 부수고 오버패칭을 끝내다 우리는 지난 십수 년간 REST API를 절대 진리라 믿고 사용해 왔습니다. 하지만 화면이 좁고 네트워크가 불안정한 모바일(Mobile) 시대가 오면서 REST API의 두 가지 치명적인 한계가 드러나기 시작했습니다. 첫째, 오버패칭(Over-fetching)입니다. 모바일 앱 메인 화면에 유저의 "닉네임" 딱 하나만 필요한데, 백엔드가 만들어둔 GET /users/1 API를 찌르면 이메일, 주소, 장바구니 내역까지 쓸데없는 수십 개의 데이터가 한꺼번에 쏟아집니다(데이터 낭비). 둘째, 언더패칭(Under-fetching)입니다. 유저의 프로필, 최근 본 상품, 찜한 목록을 하나의 화면에 그리려면 /users/1, /products/recent, /favorites 세 개의 API를 세 번이나 찔러야 ..
[Spring Boot] Spring HATEOAS: 진정한 REST API (Level 3) 달성기 웹 개발을 하다 보면 누구나 "우리는 RESTful API를 사용합니다"라고 말합니다. HTTP 메서드(GET, POST, PUT, DELETE)를 쓰고 /users/1처럼 예쁜 URL을 쓰면 REST API라고 믿기 때문입니다. 하지만 REST의 창시자인 '로이 필딩(Roy Fielding)' 아저씨는 분노하며 "네놈들이 만든 건 진짜 REST가 아니야!"라고 일갈합니다. 로이 필딩이 말하는 완벽한 REST(Richardson Maturity Model Level 3)의 필수 조건 중 하나는 바로 HATEOAS (Hypermedia As The Engine Of Application State)입니다. 이름이 끔찍하게 길지만 핵심은 단순합니다. "서버가 클라이언트(앱/웹)에게 응답(JSON)을 내려줄 ..
[Spring Boot] 낙관적 락(Optimistic) vs 비관적 락(Pessimistic): JPA 동시성 제어 이전 40단계에서는 거대한 트래픽이 몰려올 때 Redis 인프라를 활용하여 재고 초과 판매(동시성) 문제를 막아내는 '분산 락'을 배웠습니다. 하지만 Redis는 구축하고 유지보수하기 까다로운 외부 인프라입니다. 만약 "우리 회사는 그 정도로 트래픽이 몰리지 않는데 굳이 Redis까지 깔아야 하나요? 그냥 JPA랑 MySQL 기능만으로 동시성을 막을 수는 없나요?"라고 묻는다면 정답은 "가능하다!"입니다. 실무에서는 동시성 문제가 터질 확률(충돌 빈도)에 따라, 애플리케이션 레벨에서 가볍게 막아내는 낙관적 락(Optimistic Lock)과 DB 레벨에서 아예 진짜 자물쇠를 걸어버리는 비관적 락(Pessimistic Lock)을 골라서 사용합니다.이번 [Spring Boot 실무 완벽 가이드] 시리즈에서..
[Spring Boot] Redis 분산 락(Distributed Lock): 분산 환경의 동시성 제어 여러분이 티켓팅 사이트를 만들었고, 아이유 콘서트 VVIP석이 딱 1자리 남았습니다. 사용자 A와 B가 0.001초의 오차도 없이 동시에 [예매하기] 버튼을 눌렀습니다. 두 요청은 백엔드의 checkInventory()(재고가 1개 이상인지 확인)를 동시에 통과하고, decreaseInventory()(재고 차감)를 동시에 실행해버립니다. 결과는 재고는 1개인데 2명에게 예매가 완료되어 버리는 '초과 판매(Over-selling)' 대참사입니다. 학원 프로젝트(단일 서버)에서는 메서드에 synchronized 키워드를 붙여서 쓰레드를 줄 세우면 해결되었습니다. 하지만 우리의 실무 환경은 스프링 부트 서버가 10대 떠 있는 분산 환경(Scale-Out)입니다. 1번 서버의 JVM synchronized가 ..
[Spring Boot] Resilience4j 서킷 브레이커: 장애의 도미노를 끊어라 마이크로서비스 아키텍처(MSA)는 시스템을 여러 개로 쪼개어 하나가 죽어도 다른 놈들은 살려두자는 철학에서 출발했습니다. 그런데 현실은 정반대입니다. 사용자가 상품을 주문할 때, [주문 서버] -> [결제 서버] -> [재고 서버] 순으로 서로 꼬리를 물고 API를 호출(RestTemplate, FeignClient)하는 구조를 생각해 봅시다. 만약 막내인 [재고 서버]의 DB에 락(Lock)이 걸려 응답이 10초씩 지연된다면? 결제 서버도 응답을 기다리다 쓰레드(Thread)가 고갈되어 멈추고, 주문 서버도 멈추고, 결국 맨 앞의 클라이언트(앱)도 하얀 화면에서 멈춰버립니다. 제일 밑바닥에 있는 마이크로서비스 하나가 아프면, 그를 호출하는 모든 윗단 서비스들이 도미노처럼 쓰러져(Cascading Fai..
[Spring Boot] Eureka 서비스 디스커버리 마이크로서비스 아키텍처(MSA)를 도입하고 API Gateway까지 앞에 세웠습니다. 이제 클라이언트의 모든 트래픽은 Gateway가 yml 설정에 적힌 IP(192.168.0.10:8082)를 보고 주문 서버로 넘겨줍니다. 그런데 오늘 블록버스터급 할인 이벤트가 터지면서 주문 서버 CPU가 99%를 치기 시작했습니다! 다급해진 인프라 팀이 AWS 오토스케일링(Auto Scaling)을 돌려 주문 서버를 순식간에 5대로 늘렸습니다. 새로 켜진 서버 4대는 각자 랜덤한 새 IP(예: 192.168.0.11 ~ 14)를 발급받았습니다. 자, Gateway는 새로 켜진 4대의 서버 IP를 어떻게 알고 트래픽을 골고루 나눠줄(Load Balancing) 수 있을까요? yml 파일에 직접 적혀있지 않으니 Gate..
[Spring Boot] Spring Cloud Gateway 하나의 거대한 프로젝트(Monolithic)로 시작했던 쇼핑몰이 대박을 터뜨렸습니다. 개발자가 100명이 넘어가자, 코드가 꼬이고 배포 한 번 하려면 30분이 걸립니다. 결국 넷플릭스(Netflix)나 우아한형제들처럼 시스템을 '회원(Auth)', '상품(Product)', '주문(Order)' 서버로 잘게 쪼개는 마이크로서비스 아키텍처(MSA, Microservice Architecture)로 전환했습니다. 그런데 문제가 생겼습니다. 프론트엔드 앱이 회원가입을 하려면 auth.api.com을 찔러야 하고, 상품을 조회하려면 product.api.com을 찔러야 합니다. 클라이언트가 수십 개의 분산된 서버 주소를 전부 외우고 다녀야 하는 끔찍한 상황입니다. 게다가 JWT 토큰 검증은 30개의 서버마다 각..