Node.JS 에서 중요한 패턴이라고 한다면 너무 많은 일을 하는 커다란 어플리케이션을 작성하지 말라는 것입니다.
사실 뭐 이건 이제 Node.JS 에만 국한된 이야기는 아닐거라고 생각하긴 합니다만..
이전의 포스팅에서 사용했던 그림을 재탕했습니다. 대규모 어플리케이션을 작성하는 대신 위 그림에서 Y축인
서비스 및 기능으로 분해하여 별도의 작은 어플리케이션으로 나눠서 운영하는 것이 그 방식입니다.
모놀리틱 아키텍쳐에 정반대되는 개념이며, Micro Service Architecture(MSA) 패턴으로 볼 수 있습니다.
물론 무조건 모든 어플리케이션을 MSA 로 만드는 것은 그리 적절한 방법은 아닐 수 있습니다.
MSA 설계시엔 우리가 소프트웨어 이론에서 늘상 얘기하는 높은 응집도, 낮은 결합도, 복잡성등의 요소를 고려해야하며
중요하진 않다고 하지만 어느정도 어플리케이션의 사이즈도 봐야합니다.
모놀리틱 아키텍쳐의 단점에 대해 적어보자면, 이제는 워낙 자주 등장하는 말이기도 합니다만 이커머스 시스템을 예시로 들어보겠습니다.
저도 이커머스 시스템을 만져본적은 없지만, 대략 예상해보자면 이용자에게 보여줘야하는 제품에 대한 서비스, 제품을 검색하는 서비스, 회원 서비스, 결제 서비스 등으로 구성이 될 수 있습니다. 각각의 서비스는 서로 다른 데이터 저장소를 쓸 수도 있을거고, 하나의 저장소를 사용할수도 있을 겁니다.
이런 여러 서비스들이 모놀리틱 아키텍쳐로 구성된 하나의 서버에서 전부 돌아간다고 가정할 때 만약 결제 서비스에 장애가 생겼을 경우
제품도 조회가 안되고, 로그인 또는 배송 조회도 안되는 등의 온갖 곳에서 문제가 발생할 수 있습니다.
서비스 서로서로가 높은 결합력을 갖고 있고, 문제가 생겼을 때 해결하기도 어렵지만 더 큰 시스템으로의 확장도 방해하는 요소입니다.
이런 이커머스 시스템을 구성하는 위의 서비스들을 모두 개별 어플리케이션으로 분리해서 운영한다고 했을 때, 이제는 결제 서비스에
장애가 생겨도 제품 조회나 검색 또는 회원 관련된 서비스에는 문제가 없습니다. 단일 책임에도 부합하고, 장애 파악 및 처리에도 한결
수월해졌으며 시스템을 확장해나가기에도 용이합니다. 아예 다른 서비스를 만들어나간다고 할 때 기존의 회원 시스템이나 검색 시스템을
사용할 수도 있으니 재사용성에서도 이득을 볼 수 있습니다.
만약 MSA 를 하기에 적절한 규모의 어플리케이션이라고 해도 쉽사리 MSA 로 가지 못하는 경우가 있을 수 있습니다.
아무래도 모놀리틱 아키텍쳐에 비해 관리 포인트가 많아지고 러닝 커브도 있기에 운영할 팀의 리소스가 한정적이라면 MSA 선택은
오히려 실이 될 수 있습니다.
- 모듈 시스템
우리가 여러 서비스를 MSA 로 구성했다면 각 서비스간에 어떻게 공통 코드를 공유해서 재사용할 수 있을까.. 하는 부분에 부딪히게 됩니다.
이런 경우에 Node.JS 의 모듈 시스템은 코드를 공유하기에 아주 적절합니다.
적절한 예시인지는 모르겠으나.. 회사의 서비스 중 일부 키에는 공통의 암호화를 적용하기로 결정했고, 이 암호화가 여러 서비스에서 필요한
경우가 있을 수 있습니다. 암호화에서 어떤 알고리즘을 사용하고, 무엇을 salt 로 사용할지에 대한 스펙을 바탕으로 필요한 서비스에서 각자 구현을 한다고 하면 초기에는 잘 될 수 있지만 갈수록 관리도 안되고 업데이트가 필요할 때마다 각 프로젝트 레포에 수정을 해줘야하는
과정이 필요해집니다. 단순 실수가 나오기에도 쉽고, 검색을 한다해도 누락되는 레포가 생기기 마련이죠.
npm install @xxx/common-encryptor@1.0.0
우리는 이런 경우 Node.JS 로 common-encryptor 라는 이름의 공통 암호화 모듈을 만들고, 필요한 레포에서 위처럼 패키지 매니저를
사용한 설치를 통해 공통 코드를 쉽게 공유할 수 있습니다. 상대적으로 업데이트 및 관리에도 용이하죠.
예전엔 별로 고려하지 못했으나 갈수록 팀 내의 여러 서버 또는 타팀이 관리하는 서버에까지 필요한 공통 코드가 생겨나면서
이런 형태의 공통 모듈을 작업해 공유하는 식의 작업을 많이 하게 되는 것 같습니다.
- API 프록시
MSA 로 구성할 땐 API Proxy 라는 패턴을 사용할 수 있습니다. 이 API Proxy 가 하는 일은 클라이언트와 API 집합 간의 통신을
프록시 하는 것입니다. 요즘은 이런 이름보단 API Gateway 라는 이름으로 더 흔히 사용하는 것 같습니다. AWS 에도 API Gateway 라는 서비스가 별도로 존재하니까요.
이는 본질적으론 역방향 프록시이며 로드 밸런싱 장치이기도 합니다. 레지스트리에 URL 경로에 따른 서비스를 매핑함으로써 거의 유사하게
동작하고, 시스템이 더 확장될 가능성이 있다면 유용한 패턴일 수 있습니다.
- API Orchestration
그림이 좀 개발이네요.. 이 방식은 하나의 기능을 하기 위해 A, B, C 서비스 호출을 필요로 할 때 API Orchestartion 이라는 계층을 두고
여기에 가상의 서비스를 둬서 이 서비스가 A, B, C 의 호출을 통해 하나의 기능을 하고, 일관된 상태를 유지하는 형태입니다.
virtual api 라고 써놨지만 분리된 Orchestartion 계층에 구현된 독립의 서비스라고 볼 수 있으며, 여러 서비스와 특정 어플리케이션 간에
추상화 역할을 하는 계층입니다. 이 패턴은 은근히 많이 사용되는 것 같긴 하지만.. 다음에 소개할 패턴과 꽤 대비되는 것 같습니다.
- Message Broker
위에서 적은 API Orchestration 패턴은 단점만 적자면 Orchestration 계층이 시스템의 기본 설계와 각 서비스의 동작 방식에 대해
아주 정확히 알고 있어야 합니다. 이런 부분 때문에 안티 패턴이라고 불리기도 하죠.
근래엔 이런 안티 패턴을 극복하기 위해 메세지 브로커를 사용해 pub/sub 패턴을 사용합니다. 이벤트 기반 아키텍쳐라고도 볼 수 있구요.
pub/sub 패턴은 어떠한 채널로 특정 이벤트를 publish 하면 해당 채널을 subscribe 하고 있던 서비스가 해당 이벤트를 받아
필요한 연산을 수행하는 방식입니다. 이커머스 시스템에서 회원이 어떤 상품을 구매했을 때 어떠한 개체가 개입해 결제 시스템을 호출하고,
회원의 잔액을 차감하고, 제품 수량을 없애는 일련의 과정을 이벤트 기반으로 변경한다면 구매 라는 이벤트가 발생했을 때 메세지 브로커는
채널에 이벤트만 전파하고 각각의 결제, 회원, 제품 서비스가 이벤트를 받아 알아서 필요한 연산을 처리합니다.
이런 구조에선 API Orchestration 처럼 외부 개체가 개입할 필요도 없고, 여러 서비스의 동작 방식에 알 필요도 없으며 그 동작에 대해
책임을 질 필요도 없습니다. 중앙의 메세지 브로커는 이런 이벤트가 발생했다는 것만 전파할 뿐, 누가 이벤트를 받았는지도 신경쓸 필요가
없습니다. 이제 해당 이벤트에 대한 처리의 책임은 각 서비스가 가져갑니다. 사실 약간 과장을 보태자면 API Orchestration 은 모놀리틱
구조에서 껍데기만 따로 뗀 것 같은 느낌이 들기도 하죠.
저도 읽은적은 없지만 마틴 파울러가 작성한 MSA 에 대한 글을 링크에 남기며 이번 포스팅을 마칩니다.
'Backend > Node.js' 카테고리의 다른 글
Use ZeroMQ (0) | 2023.02.05 |
---|---|
Messaging System (0) | 2023.01.28 |
peer-to-peer load balancing (0) | 2023.01.08 |
Node.JS 어플리케이션 확장 (0) | 2023.01.01 |
Service Locator (서비스 로케이터) (0) | 2022.12.25 |