약 4~5주간 MSA기반 ecommerce프로젝트를 진행했다.
기획부터 개발완료 까지 개인 프로젝트로 진행했다. 개인프로젝트의 장점은 내가 프로젝트의 모든 부분을 속속들이 알고 직접 기획하고 계획해야하는 점이 아닐까 하는점이다.
프로젝트를 진행하면서 막힐때는 정말 답답함에 울고 싶었지만 포기하지 않고 자료들을 찾아보고 함께 비슷한 주제를 가지고 프로젝트를 한 동료들과의 팀 스터디를 진행하며 토론하고 질문하며 많은 인프라를 얻을수 있었던거 같다
뒤돌아보면 정말인지 정신 없이 흘러간 4주간이 아닐까 싶다. 그래서 4주간의 프로젝트를 회고해볼까 한다!
회고를 하며 프로젝트에 대해 아쉬웠던점과 앞으로 어떤 방향으로 개선하면 좋을지 고민해 보기 위해 이 글을 작성한다.
프로젝트 소개
프로젝트 인원 : 개인 프로젝트
프로젝트 : 예약 구매 서비스
깃 허브 링크 : https://github.com/Cyoungju/Springboot-MSA-PreOrder
기술 스택
- Spring boot, Spring security, Spring batch, Spring Cloud Gateway, Nexflix Eureka, JPA, MySQL, Docker, Kafka, Google SMTP
기획
Ecommerce서비스를 위한 플랫폼 개발 기획 했다.
대규모의 트래픽을 처리해 선착순 구매 기능을 지원하는 MSA기반 E-commerce 서비스이다.
설계
대규모 트래픽을 처리 하면서도 각 서비스 간의 독립성을 유지하고 성능을 최적화 하기 위해 MSA 아카텍쳐 설계를 염두해 두고, 처음에는 모놀리식 아키텍처로 작업을 진행하였다.
처음부터 MSA 아키텍쳐로 나누는게 익숙하지 않아 모놀리식 아키텍쳐로 작업을 완료 한 후 나누기로 결정했다.
두가지 아키텍쳐 다 경험해보다니 이득 이잖아 ? ㅎㅎ?
기본 기능과 핵심 기능으로 나누어 설계를 했다
- 회원가입, 로그인, 로그아웃, 마이페이지 등의 기본적인 유저 관리 기능
- WishList, 주문내역, 주문상태 조회 등의 커머스 핵심 기능
주문 상태 관리에는 자동으로 상태 관리 할수 있도록 설계 했다.
아키텍쳐설계, ERD설계, API설계 작업을 진행했다.
아키텍처 설계
MSA구조를 설계 하는데 처음에 조금 힘들었다. MSA아키텍처에 대해 공부하고 이해하는데 1~2주정도의 시간을 소요한거같다.
초기에 기능 단위인 user/product/order service로 나누어 설계를 진행했고, payment는 추후에 나누게 되었다.
일단은 MSA 아키텍처를 염두해 두고 모놀리식으로 먼저 진행하였다.
ERD설계
초기 설계한 erd에 wish상품과 주문 상품 테이블을 나눈 부분에 대해 좋은 피드백을 받았다. 작업중에도 크게 바뀌지 않았고, 주요 기능 위주로 꼭 필요한 부분만 추가 삭제가 이루어졌다.
API설계
개발
개인정보 암호화
회원가입 기능을 작업하면서 고려해볼 부분이 개인정보 암호화 였다.
사용자의 이름, 주소, 전화번호, 비밀번호 등의 중요한 정보가 외부로 유출 되지 않게 하기 위해 데이터 암호화를 한 후 저장해야했다.
패스워드 같은 경우에는 암호화된 정보가 복호화시킬 필요가 없기 때문에 단방향 암호화 알고리즘을 이용하는 PasswordEncoder의 도움을 통해 쉽게 암호화를 진행하였다.
이메일이나, 사용자 이름, 전화번호, 주소등 사용자 개인정보에 해당하는 정보들은 나중에 복호화할 수 있어야 하므로 양방향 알고리즘을 이용해 암호화작업을 진행했다.
양방향 알고리즘 중에서도 AES 암호화 알고리즘을 사용해 작업을 진행하였다.
로그인 회원가입 기능 구현 jwt 토근 발급
로그인 회원가입 작업 진행시 jwt토큰을 발급하여 무상태 인증 시스템을 구현하였다.
Spring batch 배송 상태 변경 자동화
처음 설계 했던 배송 상태 자동화 관리를 통해 어떤 방법을 사용할지 고민에 빠졌다.
처음에는 스프링에서 지원하는 스케줄러를 사용했는데 대용량 데이터 처리 비효율과 트랜젝션 관리의 한계가 있었다.
이커머스 프로젝트에서 배송테이블이 가장 중요하고, 많은 데이터를 가지고있기 때문에 Spring Batch와 스케줄러를 함께 사용하여 대용량의 데이터를 효율적을 처리하고, 상태 관리와 트랜잭션을 관리 할수 있었다.
MSA 구조 변경
모놀리식 아키텍쳐로 작업해놓은 프로젝트를 MSA구조로 변경하였다.
대규모 트래픽을 처리 하면서도 각 서비스 간의 독립성을 유지하고 성능을 최적화 하기 위해 MSA 아카텍쳐로 설계를 했었다.
MSA란 하나의 큰 애플리케이션을 여러개의 작은 서비스 유닛으로 쪼개어 변경과 조합이 가능하도록 만든 아키텍쳐이다.
모놀리식 아키텍처와 비교했을때 각각의 서비스가 분리 되어있기 때문에 결합도가 낮기때문에 장애전파 방지, 유지보수의 편리함등의 장점을 가지고 있다.
여기서 고려할 점이 몇가지 있는데 크게는 세가지가 있었다.
1. 외부의 요청을 알맞은 서비스로 어떻게 전달할 것인가?
2. 서비스간 통신은 어떤식으로 할 것인가?
3. 서비스들의 정보를 어떻게 관리할것인가?
1번 - API Gateway
각각의 쪼개진 서비스로의 client요청에 분기 처리를 위해 API Gateway를 도입했다.
클라이언트 요청을 중앙에서 관리 하고, 로드밸런싱과 인증 / 인가 처리를 담당한다
스프링 부트와의 개발 유지 보수가 간소화, 비동기 및 반응형 처리를 통해 대규모 트래픽을 효율적으로 처리 가능하기 때문에 Spring Cloud Gateway 를 선택하였다.
2번 - OpenFeign
기존의 모놀리식 아키텍처에서는 도메인을 직접 참조 하면되었지만 서비스가 분리되었기 때문에 도메인간의 통신을 위해 추가적인 통신 방법이 필요했다.
여러가지 방법이 있지만 간결하고 직관적인 선언적 API 클라이언트를 제공하여, 코드 유지 보수를 간소화 하고 서비스간 통신을 효율적으로 처리해 줄수 있는 Feign Client를 사용해 통신을 했다.
3번 - 서비스 디스커버리 패턴
MSA에서 각 서비스들의 정보를 등록. 관리 하는 서비스 디스커버리 패턴을 통해 처리했다.
스프링 클라우드와의 통합으로 스프링 기반 애플리케이션에서 서비스 디스커버리 간편하게 구현 가능한 Netflix OSS (Eureka)를 선택해 작업을 진행하였다.
사용자 인증 처리
모놀리식아키텍처에서 MSA아키텍처로 구조 변경후 사용자 인증 처리를 어떻게 할것인가에 대해 고민에 빠졌다.
API Gateway를 도입하여 인증 / 인가 작업을 할건데 그렇다면 JWT토큰에 대한 인증 인가를 어떤식으로 할것인가? 하는것이였다.
API Gateway 서비스에는 인증/ 인가 작업 이외에는 많은 기능을 부여하지 않게 하기 위해서 보안 작업을 담당하는 Security는 user-service에만 설치 했다
user-service에서 회원가입을 진행하고, 로그인진행시 인증/ 인가 작업은 gateway서비스에서 담당할수 있도록 작업하였다!
서비스 장애 대응 Circuit Breaker 구현
OpenFeign통신 방법을 사용해 MSA 아키텍쳐에서 서비스들간의 통신을 할수 있도록 했다.
하지만 여기서도 고려해볼 문제가 생겼다. 만약 order-service에서 product-service에 상품 정보를 요청했을때, product-service에 문제가 생긴다면 어떻게 될까? 응답시간이 무한으로 지연되어 전체 서비스 장애로 이어질수 있다. 서로간의 의존성을 최소화 하기 위해 MSA구조로 나누었는데 치명적인 문제가 틀림없었다.
그래서 이를 해결하기 위해 Circuit Breaker 패턴을 도입했다.
서킷 브레이커는 일정 시간 동안 실패가 지속되면 요청을 차단하고, 일정 시간이 지나거나 시스템이 안정되면 다시 요청을 시도하게 해준다.
Spring-cloud 에도 내장되어있는 Resilience4j 사용했다.
circuitbreaker와 호출이 실패 했을때 자동으로 재시도 해주는 retry를 도입하여 장애 전파를 방지 하고, 시스템의 안정성을 높일수 있었다.
캐싱 전략
대규모의 트래픽을 처리해 선착순 구매 기능을 지원하는 MSA기반 E-commerce 서비스를 기획한 이상 무시할수 없는 부분이 바로 성능 최적화와 트래픽감소에 대한 부분이였다
커머스 프로젝트에서 실시간 재고관리는 필수적이다. 하지만 이때 데이터베이스에 직접 접근하는 방식은 성능 저하와 부하를 유발시킬수 있다. 그래서 캐시를 활용해 자주 조회 되는 재고 데이터를 빠르게 제공하여 시스템 응답 속도를 높히고 데이터 베이스 접근 횟수를 줄여 성능개선을 기대하였다.
이때, 효과적인 캐시 관리를 위해서는 전략을 잘 세워 데이터의 일관성과 효율성을 유지하는것이 중요하다는 것을 알게되었다.그래서 처음에는 말했던건 읽기 전략에 해당 하였다. cache-aside 전략은 데이터를 캐시에 저장하여 캐시에 먼저 데이터가 있는지를 확인하고 있다면 데이터베이스가 아닌 캐시에서 조회하는 방법이다
그리고 다음 쓰기 전략을 읽는 동시에 작성할수 있는 Through 전략을 사용하였다
하지만 이때 재고의 일관성은 보장되었지만 쓰기작업시 하나의 트렌젝션안에서 DB 조회가 일어나 성능개선의 효과를 볼수 없었다.
그래서 쓰기전략을 Behind전략으로 변경하여 불필요한 쿼리를 줄이고 성능 개선 효과를 볼수 있었다.
여기서 데이터의 정합성 문제가 생기는데 일정시간 결제하지 않을경우 결제 취소와 함께 재고를 복구시키는 과정을 통해 궁극적 일관성을 지킬수 있었다.
성능과 일관성 간의 트레이드오프를 잘 고려 해야한다는것을 알게 되었다.
동시성 문제
여러 스레드가 한꺼번에 동일한 자원에 접근할 경우, 동시성 문제가 발생할수 있었다.
정해진 기간에 구매 할수 있는 상품 기능에서 꼭 해결해야할 문제 였다.
동시성 문제가 발생하여 재고가 일정하게 깎이지 않는 문제가 발생한 것이다.
이 문제를 해결하기 위해 synchronized를 사용해봣지만 전역동기화, 성능저하등의 문제가 발생했다. 그래서 Redis 분산락을 통해 동시성 문제를 해결할수 있었다! 여러 사용자가 한꺼번에 접근하더라도 락을 걸어 순차적으로 진행시켜 동시성 문제를 해결할수 있었다.
동기와 비동기 통신
kafka도입
불편사항 트레이드 오프
배운점 / 느낀점
지금 이해되지 않더라도 언젠가는 이해가 된다
이번뿐만 아니라 개발공부를 정식으로 시작하게 되면서 느낀점이 있다면 지금 이해 되지 않더라도 언젠가는 이해되는 순간이 온다는 것이였다. 처음에는 이해되지 않는 이 기간들을 견디는게 많이 힘이들었다. 내가 이해력이 부족한걸까? 하는 자책도 들었던거 같다. 하지만 계속 포기 하지 않고 공부 한다면 언젠가 아! 하는 순간들이 반드시 찾아왔던거 같다
이번 프로젝트를 하면서 더욱 절실하게 느낄수 있었다. 짧은 기간에 많은 기술과 작업을 요했기에 처음에 이해가 바로 되지 않는 내용들이 많았던거 같다. 하지만 결론적으로 돌아봤을때 그때 포기하지않고 계속해서 고민했던 것들이 쌓여 지금의 완성된 프로젝트를 만날수 있었던거 같다. 고민하고 공부하고 이제는 이해되는 것들이 그때는 그렇게 야속했던거 같다. 포기하지 않으면 유래카를 외치는 날이 찾아오는것을 깨달았다!
성장하고 있다
멘토링과 팀 스터디가 많은 도움은 되었지만 결국엔 내가 고민하고 공부하는 바탕이있어야 비로소 하나로 합쳐져 엄청난 시너지를 발생했던거 같다.
MSA나 Redis, docker등의 여러 기술과 더불어 스스로 생각하고 고민하는 과정등 새롭게 배워가는 것이 많았다
나도 할수 있다
이번 프로젝트에서 사용한 기술들은 항상 마음의 장바구니에 담아두고 언젠가는 하겠어 하던 기술들이였다
하지만 0에서 1이 어렵지 1에서 2는 쉽다는 말이 있는것처럼 내가 괜시리 겁을 먹고 있었던건 아닌가 하는 생각이 들었다
하고 나니 "나도 할수 있었잖아?" 하고 느낀점이 많이 있었다.
시작을 두려워하지 말고 일단 시작해야겠다 하는 마음을 먹을수 있었다!
아쉬운점
'프로젝트 > Springboot-MSA- PreOrder' 카테고리의 다른 글
[Project] Kafka를 사용한 비동기 이벤트 처리 (0) | 2024.09.06 |
---|---|
[Project] Redis 캐싱 성능개선 (0) | 2024.09.01 |
[Project] 데이터 동시성 문제 해결 (4) | 2024.08.26 |
[Project] 서비스 장애 대응 Circuit Breaker 구현 (1) | 2024.08.26 |
[Project] API Gateway (0) | 2024.08.18 |