이 글은 "마이크로서비스 아키텍처 구축 (한빛미디어)"을 읽고 정리한 내용입니다.
통합
통합에 대한 올바른 이해는 마이크로서비스 관련 기술에서 가장 중요한 요소다.
이상적인 통합 기술 모색
하나의 마이크로서비스가 다른 마이크로서비스와 통신하는 방법은 정말 다양하다. SOAP, XML-PRC, REST, 프로토콜 버퍼 등이 그 예다.
- 호환성을 꺠뜨리는 변경 피하기
- 마이크로서비스가 데이터 일부분에 새로운 필드를 추가해서 노출하더라도 기존 소비자 서비스에는 영향을 미치지 않아야 한다.
- 기술 중립적인 API 생성
- IT 업계는 빠르게 변화한다.
- 소비자를 위한 서비스 단순화
- 소비자의 편의를 위해 클라이언트 라이브러리를 사용하면 결합의 비용이 늘어난다.
고객과의 인터페이싱
우선 가장 일반적인 기술들을 살펴보고, 그중 우리에게 가장 적합한 것을 시도해보자.
공유 데이터베이스
데이터베이스 통합의 문제점
- 외부에서 내부의 구현 상세를 조회하고 결합하는 것을 허용한다. DB에 접근하는 모든 대상에게 DB에 저장되어 있는 데이터 구조 전체가 공유된다.
- 소비자들이 특정 기술을 선택하는 범위가 제한된다. 서비스 내부 변경 방식에 관한 자율성을 서비스에게 줄 수 있도록 소비자에게 서비스의 세부 구현은 은폐되어야 한다.
- 소비자가 DB를 직접 조작한다면 소비자는 관련 로직을 직접 소유해야 한다. 같은 종류의 조작을 수행하는 로직이 다수의 소비자에게 퍼지게 된다. 응집력이 낮아진다.
- 강한 응집력과 느슨한 결합력 모두 잃는다.
동기와 비동기
통신은 동기식이어야 하는가 비동기식이어야 하는가, 이 근원적인 선택은 필연적으로 우리를 특정 세부 구현을 이끈다.
- 요청/응답 스타일: 동기 통신 방식과 명확히 일치하지만 비동기 통신에서도 잘 작동한다. 클라이언트는 작업 요청을 보내고 그 작업이 완료되었을 때 서버가 알려주도록 요청하는 콜백을 등록할 수 있다.
- 콜백: 다른 코드의 인수로 넘겨주는 실행 가능한 코드를 말한다. 콜백을 넘겨받는 코드는 이 콜백을 필요에 따라 즉시 실행할 수도 있고 나중에 실행할 수도 있다.
- 이벤트 기반 협업: 클라이언트는 완료되어야 할 작업을 요청하는 대신 일이 발생했음을 알리고 다른 당사자들이 무엇을 해야 할지 알기 기대한다.
- 다른 누구에게도 해야 할 일을 말하지 않는다.
- 비즈니스 로직이 핵심 두뇌로 집중화되기보다는 다양한 협업자들에 고르게 분배된다.
- 매우 결합도가 낮은 방식이다.
- 이벤트를 발산하는 클라이언트는 이벤트에 반응하는 대상이 누구인지 또는 무엇인지 전혀 알지 못한다. (클라이언트 몰래 이벤트에 새로운 구독자를 추가할 수 있다)
우리가 특정 방식을 선택하게 하는 요인은 바로 복잡하고 잦은 문제(예: 서비스의 경계를 넘어 장기간 실행될 수 있는 프로세스를 어떻게 처리할 것인가)를 해결하는데 이러한 방식들이 얼마나 적합한가다.
오케스트레이션과 코레오그래피
오케스트레이션 방식
- 오케스트 지휘자처럼 프로세스를 안내하고 구동하는 하나의 중앙 두뇌에 의존한다.
- 특정 서비스에 지나치게 많은 중앙 관리 권한이 부여되는 단점이 있다.
- 예) 회원가입을 하면 고객 서비스에서 적립 포인트 은행 서비스, 우편 서비스, 이메일 서비스를 각각 호출한다.
코레오그래피 방식
- 발레 무용수들이 자신의 역할을 알고 주변의 다른 무용수에 반응하는 것처럼 시스템 각 부분에 작업 내용을 알리고 세부 사항을 수행하게 한다.
- 예) 비동기 방식으로 '고객이 생성되었다'라는 이벤트를 발산할 뿐이다. 적립 포인트 은행, 우편, 이메일 서비스가 이벤트를 구독하고 적절히 행동한다.
- 비즈니스 프로세스의 명확한 뷰와 반대로 시스템에 암시적으로 반영된다는 단점이 있다.
- 일이 제대로 수행되는지 모니터하고 추적하기 위한 추가 작업이 필요하다.
기술들은 자유롭게 조합되어 질 수 있고 각자 어울리는 방식이 있을 것이다.
우리는 올바른 호출을 하도록 많은 도움이 되는 일부 다른 기술의 세부 구현을 제대로 이해할 필요가 있다.
이제 요청/응답을 고려할 때 아주 적합한 두 가지 기술인 RPC와 REST에 대해 살펴보자
원격 프로시저 호출(RPC)
RPC: 지역 호출(local call)을 통해 원격 서비스를 실행하는 기술이다.
SOAP, 쓰리프트, 프로토콜 버퍼와 같은 인터페이스 정의에 의존하는 몇몇 다른 형태의 RPC 기술이 현업에 사용된다.
- RPC 구현체들은 빠르게 시작할 수 있는 클라이언트와 서버 스텁을 생성하게 한다.
- 쉬운 사용성: 네트워크 경계를 넘어 즉시 데이터를 보낼 수 있다.
- 일반적인 메서드 호출처럼 사용하고, 이론적으로 다른 것은 신경 쓰지 않아도 된다.
기술 결합
- 자바 RMI와 같은 일부 RPC 메커니즘은 특정 기술과 지나치게 엮여 있어서 클라이언트와 서버에 사용될 기술을 제한한다.
- 기술 결합은 내부의 기술 구현 상세를 노출하는 한 형태일 수 있다.
- RMI를 사용한다는 것은 JVM이 클라이언트뿐만 아니라 서버와도 관계가 있음을 의미한다.
지역 호출은 원격 호출과 다르다
- RPC 아이디어의 핵심은 원격 호출의 복잡성을 은폐하는 것이다. 하지만 RPC의 수많은 구현체가 이를 지나치게 은폐한다.
- 원격 인터페이스와 지역 인터페이스에 대한 API 디자인을 별개로 생각해야 한다.
- 네트워크는 신뢰할 수 없다.
취성(쉽게 꺠지거나 부서지는 성질)
- RPC 구현체들도 형편없기 깨지기 쉬운데, 자바 RMI가 좋은 예다.
- 서버 스텁에 새로운 메서드를 매우 쉽게 추가할 수 있지만, 클라이언트 스텁도 다시 생성해야 한다.
- 필드 변경 시 클라이언트와 서버를 동시에 배포해야 한다. (필드를 안전하게 삭제하기 어렵다)
- 클라이언트와 서버 배포를 분리할 수 없는 것은 바이너리 스텁 생성 방식을 장려하는 모든 RPC 메커니즘의 핵심 난제다.
RPC는 형편없는가?
- 많은 연산 작업은 RPC 기반의 모델에 적합하다.
- 프로토콜 버퍼나 쓰리프트처럼 더 현ㄴ대적 메커니즘은 클라이언트와 서버 코드 간의 릴리스 보조가 필요 없도록 RPC의 과거 단점들을 보완하고 있다.
- 데이터베이스 통합과 비교해보면 RPC는 요청/응답의 협업 방식 측면에서 확실히 개선되었다.
REST
REST와 HTTP
- HTTP는 자체적으로 REST 방식과 궁합이 맞는 유용한 기능들을 정의한다.
- 예) GET, POST, PUT과 같은 HTTP 동사들
- HTTP는 지원 도구와 기술의 거대한 생테계를 제공한다.
- 예) Varnish와 같은 HTTP 캐시 프로시, mod_proxy와 같은 부하분산기, 그리고 많은 모니터링 도구
애플리케이션 상태 엔진으로서의 하이퍼미디어(HATEOAS)
- 예) 특정 앨범 목록을 표현하는 자원 안에는 앨범 정보에 따라 수많은 하이퍼미디어 컨트롤이 있다.(가수 정보 링크, 구입 링크)
- 고객과 웹사이트 간에 암묵적 계약이 존재하는 한 웹페이지의 수정은 호환성을 깨뜨리는 변경이 되지 않는다.
- 클라이언트는 링크를 따라가면서 점진적으로 API를 발견하게 되는데 우리가 새로운 클라이언트를 구현할 때 매우 유용한 기능이 될 수 있다.
JSON, XML 또는 다른 것?
- JSON이 XML에 비해 상대적으로 간소하다.
- XML은 link 컨트롤을 하이퍼미디어 컨트롤로 정의하지만 JSON 표준은 자체 방식을 사용한다.
- HAL는 JSON과 XML에 공통 표준을 정의하여 문제를 해결한다.
- HTTP 상으로 어떤 것이든 전송할 수 있다.
- 점점 더 많은 사람들이 XML 대신 HTML을 데이터 포맷으로 사용한다.
- 일부 인터페이스에 대해서는 HTML이 UI와 API 두 가지 역할을 할 수 있다.
- 일부 도구의 지원은 XML이 더 좋다.
- 페이로드의 특정 부분을 추출하려 할때: XPATH, CSS 선택자 사용 (JSON에 JSONPATH는 광범위하게 지원되지 않음)
지나친 편의를 주의하라
- RESTful 웹 서비스 생성을 도와주는 프레임워크가 인기를 얻고 있는데, 작업 속도를 높일 수는 있겠지만 몇 가지 나쁜 행동을 범하게 된다.
- 일부 프레임워크는 객체의 데이터베이스 표현을 바로 전달받아 프로세스 내의 객체로 역질렬화하고, 외부로 직접 노출하는 일련의 작업을 매우 쉽게 만들어버린다.
- 데이터를 어떻게 저장할 것인가 그리고 어떻게 소비자에게 노출시킬 것인가
- 예) 마이크로서비스에 대한 적절한 영속성의 구현을 인터페이스가 충분히 안정될 때까지 미루자.
HTTP 기반 REST의 단점
- 클라이언트 스텁이 RPC처럼 쉽게 생성되지 않는다.
- 일부 웹 서버 프레임워크가 실제로는 모든 HTTP 메서드를 제대로 지원하지 않는다.
- 성능 문제:
- HTTP 기반의 REST 페이로드는 JSON 또는 바이너리 등의 포맷을 지원하므로 SOAP보다 훨씬 간결할 수 있지만 쓰리프트와 같은 바이너리 프로토콜에는 상대가 되지 못한다.
- 각각의 HTTP 요청에서 늘 발생하는 HTTP 부하 역시 낮은 지연시간이 요구될 때 문제가 될 수 있다.
- HTTP가 대규모 트래픽에는 적합할 수 있지만 TCP 또는 다른 네트워킹 기술 기반의 대체 프로토콜과 비교하면 낮은 지연시간이 필요한 통신에는 좋은 선택이라 할 수 없다. (예: 웹소켓)
비동기 이벤트 기반의 협업 구현
기술 선택
- 마이크로서비스가 이벤트를 발산하는 방법, 소비자가 생성된 이벤트를 찾는 방법을 고려해야 한다.
- RabbitMQ
- 위 두 문제를 모두 처리한다.
- 생산자는 API를 사용하여 브로커(중개자)에게 이벤트를 발행하고 브로커는 이벤트가 도착하면 소비자에 알릴 수 있도록 구독을 처리한다.
- 중개자는 소비자 상태도 처리하는데, 소비자가 이전에 확인했던 메시지를 추적할 수 있도록 돕는다.
- 중개자 시스템은 대개 확장 및 회복이 가능하도록 설계되어 있지만 또 다른 시스템인 만큼 개발 과정의 복잡성이 높아진다.
- 미들웨어를 멍청하게, 엔드포인트를 지능적으로 만들어라.
- HTTP(ATOM 사용)
- ATOM: 웹로그나 최신 소식고 같은 웹 콘텐트의 신디케이션을 위한 XML 기반의 문서 포맷이자 웹로그 편집을 위한 HTTP 기반 프로토콜이다.
- 서비스는 변경될 때 해당 피드에 대한 이벤트만 발행하고, 서비스의 소비자들은 단지 피드의 변경이 있는지 폴링만 하면 된다.
- 경쟁 소비자 패턴에서 작업자를 늘리면 중복 작업의 가능성을 낮추도록 모든 작업자 간에 공유 상태를 관리하는 것이 필요하다.
비동기 아키텍처의 복잡성
- 장기간 수행되는 비동기 요청/응답의 경우 응답이 돌아올 때 무엇을 해야 할지 고민해야 한다.
- 요청을 시작했던 동일한 노드로 응답이 돌아오는가?
- 그렇다면 그 노드가 다운된다면 어떻게 할 것인가?
- 그렇지 않다면 요청/응답의 정보를 저장해서 적절히 동작할 수 있는가?
- 재앙적 대체 작동이 발생하지 않도록 신경쓰자.
- 적재적소에서 모니터링하고 프로세스 경계 요청을 추적할 수 있도록 상관관계 ID의 사용을 적극 검토하자. (8장)
상태 기계로서의 서비스
- 고객 서비스는 자신의 로직을 기반으로 해서 요청의 수용 여부를 결정하고, 해당 고객과 연관된 이벤트의 수명주기를 통제한다.
- 단순한 CRUD 래퍼처럼 멍청하고 빈약한 서비스를 원하지 않는다.
- 고객 정보 변경에 관한 결정이 고객 서비스 외부에서 이뤄진다면 응집력을 잃은 것이다.
반응형 확장
- Rx로 알려진 반응형 확장은 다수의 호출 결과를 조합하고 그 결과에 따라 연산을 실행하는 메커니즘이다.
- 더 많은 서비스를 호출하게 될 때, 특히 하나의 작업을 수행하기 위해 다수의 호출일 필요할 때 사용하면 좋다.
- 데이터를 요청하는 대신 데이터에 대한 연산을 수행하고, 그 연산의 결과를 관찰하며, 변경에 따라 반응한다.
- 분산 시스템은 호출 방법의 구현 세부 사항을 추상화하고 더 쉽게 추론하게 하므로 Rx 구현체와 아주 잘 어울린다. 하위 서비스에 대한 호출 결과를 관찰해보면 클라이언트는 그 호출이 블로킹 또는 논블로킹인 것에 관계없이 응답을 기다리고 반응할 뿐이다.
- Rx의 백미는 하위 서비스에 대해 동시에 발생하는 호출들을 훨씬 쉽게 처리하면서 다수의 호출을 함께 조합할 수 있다는 것이다.
마이크로서비스 세계에서 코드 재사용의 위험과 DRY
- DRY: 시스템의 행동양식과 지식의 중복을 회피하는 모든 시도
- 공유 코드를 서비스 경계를 넘어서 사용한다면 잠재적인 결합의 문제를 안고 있는 셈이다.
- 공용 도메인 객체들의 라이브러리를 사용하다가 변경사항이 있을 경우 전체 서비스를 업데이트해야 한다.
- 하지만 로깅 라이브러리와 같은 공통 코드는 외부에서는 보이지 않는 내부의 개념이므로 사용해도 문제없다.
- 강한 결합으로 인해 생기는 문제가 코드 중복이 초래하는 문제보다 더 심각하다.
클라이언트 라이브러리
- 클라이언트 라이브러리에 더 많은 로직이 스며들수록 응집력이 더 많이 무너진다.
- 서비스 발견과 서비스 실패등을 처리하는 하부 전송 계층의 클라이언트 코드를 목적지인 서비스 자체의 것들과는 분리하는 것이 중요하다.
- 하부 API 호출을 위해 클라이언트 라이브러리의 사용을 강조할지 아니면 다른 기술 그택을 허용할 것인지 결정하라.
- 클라이언트 라이브러리의 업데이트 시점을 클라이언트가 담당하게 하라. 각 서비스를 항상 독집적으로 릴리스 할 능력을 유지해야 한다.
참조에 의한 접근
- 메모리에 오래 두면 변경 사항이 있을 수도 있으므로, 당시 도메인 개체의 내용이 담긴 메모리를 전달하는 결정과 관계없이 자원의 새로운 상태를 확보할 수 있도록 원본 자원의 참조도 포함하라.
- 주문이 발송되면 이메일 서비스에 이메일 발신을 요청하는 예:
- 요청을 고객의 이메일 주소, 이름, 주문 상세와 함께 이메일 서비스에 보낼 것이다.
- 이메일 서비스가 받은 요청을 큐에 넣거나 꺼내오는 사이에 요청된 데이터가 변경될 수 있다.
- 따라서 Customer와 Order 자원에 대한 URI만 보내고, 이메일 서버가 이메일을 보내는 시점에 해당 자원을 찾아서 가져오도록 하자.
- 우리는 이벤트 발생 자체뿐만 아니라 무슨 일이 일어났는지도 알아야 한다.
- 해당 이벤트가 발생했을 때 Customer가 어떤 상태였는지 아는 것이 중요하다.
- 개체에 대한 참조를 유지한다면 개체의 현재 상태를 알 수 있다.
- 단점: 접근이 많아지므로 부하가 매우 커질 수 있다.
- 자원이 추출되는 시점에 언제 리소스가 특정 상태가 되는지와 정보 갱신에 얼마나 오랜 시간이 소요되는지 알 수 있는 부가 정보가 제공된다면 캐시를 통해 부하를 줄이기 위한 많은 일을 할 수 있다.
- HTTP는 기본적으로 다양한 캐시 컨트롤을 통해 많은 지원을 한다.
- 단점: Customer 자원 전부를 알 필요가 없는데도 해당 자원의 검색을 주장하며 잠재적으로 결합도가 증가된다.
- 여기에는 고정불변의 법칙은 없지만, 데이터의 갱신 시점을 알 수 없는 경우라면 요청에 데이터를 삽입해서 주고받는 것을 매우 조심해야 한다.
버전 관리
가능하면 지연하기
- 마틴 파울러가 말하는 관대한 독자 패턴
- 전송할 때는 보수적으로, 받아들일 때는 자유롭게라는 포스텔의 법칙을 입증하자.
호환성을 깨뜨리는 변경 일찍 찾아내기
- 문제를 일찍 발견할 수 있도록 소비자 주도 계약(CDC)를 강력히 지지하자.(7장)
- 소비자를 고장낼 것 같으면 그것을 피할지 아니면 수용할지 다 함께 결정하고 소비자 서비스 담당자들과 적절한 대화를 시작하라.
유의적 버전 관리
- 클라리언트가 서비스의 버전 번호만 보고도 해당 서비스와 통합 가능한지 알 수 있도록 하는 명세이다.
- MAJOR
- MAJOR 버전 번호의 증가는 하위 호환성이 깨진 변경이 발생했음을 의미한다.
- MINOR
- MINOR 버전 번호의 증가는 하위 호환성을 유지하면서 새로운 기능들이 추가되었음을 의미한다.
- PATCH
- PATCH 번호의 증가는 기존 기능의 버그를 수정했다는 것을 의미한다.
- 이러한 접근 방식은 분산 시스템 분야에서는 자주 쓰이지 않는 듯하다.
다른 엔드포인트와 공존
- 호환성을 깨뜨리는 변경을 릴리스하고 싶다면 구버전, 신버전의 엔드포인트를 모두 노출하는 새로운 서비스 버전을 배포해야 한다.
- 구 소비자들이 신식 방법을 따른다면 이전 기능을 제거하고 API를 축소한다.
- 여러 버전의 엔드포인트가 공존할 때 호출자가 그들의 요청을 적절히 라우팅할 방법:
- HTTP를 사용하는 시스템에서 버전 정보를 요청 헤더에 삽입
- /v1/customer/, /v2/customer/ 와 같이 URI에 삽입
다수의 병행 서비스 버전 사용하기
- 다양한 버전의 서비스를 동시에 실행하고 구 소비자의 트래픽을 구버전에, 신규 소비자를 신버전에 라우팅하는 것이다.
- 넷플릭스가 여간해서 이 방법을 사용하지 않는 이유
- 한 서비스의 내부 버그를 고치려면 두 서비스를 수정하고 배포해야 한다.
- 소비자가 찾는 서비스로 유도하기 위한 부가적인 로직이 필요하다.
- 필연적으로 미들웨어의 특정 장소 or 다수의 nginx 스크립트 등에 추가되어 시스템 동작을 유추하기 어렵게 만든다.
- 그 서비스가 처리해야 할 영속적(저장할) 상태가 있는지 고려해야 한다.
사용자 인터페이스(UI)
디지털을 향해
- 고객이 우리와 어떻게 상호작용할지 정확히 예측할 수 없기에 더 세분화된 API가 필요하다.
- 서비스들이 다양한 방식으로 노출하는 기능들을 서로 결합시킴으로써 데스크톱 애플리케이션, 모바일 기기, 웨어러블 기기의 사용 고객뿐만 아니라 오프라인 상점을 방문하는 등의 물리적 방식을 사용하는 고객에게도 다양한 경험을 심어줄 수 있다.
- 사용자 인터페이스는 우리가 제공하는 다양한 기능요소를 한데 엮을 수 있는 구성 가능한 계층이다.
제약
- 제약은 사용자가 시스템과 상호작용하는 또 다른 형태다.
- 데스크톱 웹 애플리케이션에서는 브라우저 종류, 해상도의 제약이 있고 모바일에는 완전히 새로운 제약들이 존재한다.
- 딘순히 모바일 네트워크의 한계에 따른 제약 사항인 대역폭 문제에 국한되지 않는다.
- 다양한 종류의 상호작용이 배터리 수명을 단축시키고 고객을 화나게 만든다.
- 상호작용의 성질이 달라진다.
- 태플릿에서는 PC처럼 쉽게 오른쪽 버튼 클릭을 할 수 없다.
- 모바일 폰에서는 대부분 동작을 엄지손가락으로 수행하며 한 손으로 사용 가능한 인터페이스를 원할 것이다.
- 한편 네트워크 대역이 아주 귀한 곳에서 사람들은 SMS를 통해 서비스와 상호작용하도록 허용할 수 있다.
- 따라서 핵심 서비스들이 동일하더라도 각 인터페이스의 다양한 제약에 따라 그 서비스들을 적절히 적용할 방법이 필요하다.
API 구성
- UI가 직접 다수의 API와 통신하는 방식일 경우
- 다양한 종류의 디바이스에 맞춤화된 응답 능력이 거의 없다.
- 서비스 담당자들은 그들의 서비스가 사용자에게 보여지는 방식에 대해 관여하지 않는다.
- 다른 팀이 UI를 생성한다면 작은 변경 하나도 여러 팀에 요청해야 한다.
- 꽤 많은 호출이 있을 수 있다. 서비스에 직접적으로 수많은 호출을 하는 것은 모바일 기기에 꽤 부담이 될 수 있고 더 많은 모바일 요금을 초래할 수도 있다.
- 이때 다수의 하부 호출을 모아 외부로 호출할 수 있는 API 게이트웨이를 사용하면 도움이 될 수 있다.
UI 부분 구성
- UI가 API를 호출하고 UI 컨트롤과 매핑하는 대신 서비스들이 UI 생성에 필요한 일부 UI를 직접 제공할 수 있다.
- 큰 단위의 UI 부분을 서버 측 애플리케이션으로부터 제공한다.
- UI 부분들을 한 곳에 구성할 수 있는 조립 계층이 필요하다.
- 조립 계층은 서버 측 템플릿처럼 단순하거나 다른 애플리케이션으로 전달되는 페이지들의 집합이 될 수 있다. 후자의 경우 정교한 URI 라우팅이 필요하다.
- 일정 수준의 일관성을 유지하도록 HTML 컴포넌트, CSS, 이미지 등의 asset을 공유할 수 있다.
- UI 컴포넌트를 제공할 수 없는 네이티브 애플리케이션 또는 무거운 클라이언트에 대한 경우를 생각해야 한다. (하이브리드 방식, 반응형 컴포넌트 개발)
- 교차 형태의 상호작용이 많을수록 이 모델은 적절하지 않으며, 단순히 API 호출 하는 방식으로 회귀할 가능성이 높다.
프론트엔트를 위한 백엔드
- 일반적인 해결책은 서버 측의 집합 엔드포인트 또는 API 게이트웨이를 제공하는 것이다.
- 다수의 백엔드 호출을 마샬링할 수 있고 서로 다른 종류의 디바이스를 위해 필요하다면 콘텐트에 변화를 주거나 집계하여 표현할 수 있다.
- 하지만 서버 측 엔드포인트가 너무 많은 동작을 하며 계층이 두터워지면 그 계층은 결국 별도의 팀에 의해 관리되고, 일부 기능이 변경될 때마다 로직도 바뀌어야 한다.
- 모든 서비스에 대한 거대한 단일 계층이 될 수 있는 문제가 있다.
- 프론트엔드를 위한 백엔드(BFF)라고 하는 패턴에서는 특정 UI에 집중하는 팀이 해당 서버 측 컴포넌트도 담당한다.
- API 인증 및 권한 계층이 필요하다면 BFF와 UI 사이에 배치될 수 있다.
- 이 방법의 위험성은 포함해서는 안 되는 로직을 포함할 수 있는 집합 계층의 위험성과 유사하다.
- 따라서 백엔드가 사용하는 다양한 기능의 비즈니스 로직은 서비스에 존재해야하고 BFF는 특정 사용자 경험을 제공하기 위한 행동양식만 포함해야 한다.
하이브리드 방식
- 웹사이트에는 부품 기반의 조립 방식을 사용하고, 모바일에는 BFF 방식을 적용하는 조직도 있다.
- 사용자에게 제공하는 하부 기능들의 응집력을 유지해야 한다.
- 음반을 주문하거나 고객 세부 정보를 변경하는 것과 관련한 로직은 시스템 전체에 흩어지지 않으면서 해당 작업을 처리하는 서비스 내에 상주하도록 해야 한다.
- 하지만 지나치게 많은 행위를 중간 계층에 집어넣으려는 유혹을 피하는 것은 균형을 유지하기 어려운 과정이다.
외부 소프트웨어와 통합
- 변경할 수 없는 경우라는 어떨까?
- 상용제품 소프트웨어(COTS)를 구매하거나 약간의 통제만 가능한 SaaS를 사용하는 경우이다.
- 어떻게 이런 제품들을 현명하게 통합할 수 있을까?
- 상용제품 또는 오픈 소스와 같은 외부 소프트웨어 제품을 사용하는 이유
- 조직이 필요로 하는 소프투웨어 수요량은 내부에서 충당할 수 있는 범위를 넘어선다.
- 비용면에서 효과적이다.
- 하지만 끝에 가서 대부분의 사람들이 일부 시스템을 욕하게 된다.
통제 부족
- 콘텐트 관리 시스템(CMS) 또는 Saas 도구와 같은 상용제품의 기능을 통합하고 확장하는 일과 관련한 한 가지 난제는 수많은 기술적 결정이 이미 정해져 있다는 것이다.
- 요령은 통합과 맞춤화를 우리의 방식 안으로 끌어들이는 것이다.
맞춤화
- 기업에서 구매하는 많은 돋구는 우리를 위해 충분히 맞춤화 될 수 있다고 광고하며 판매된다. 하지만 맞춤화의 비용은 처음부터 맞춤 개발을 한 것보다 더 비쌀 것이다.
스파게티 통합
- 또 다른 문제는 외부 도구와의 통합 방법이다.
- 이상적으로 우리는 적은 수의 통합 융형을 표준화하기 원한다.
- 그러나 만약 한 제품이 독점적인 바이너리 프로토콜, SOAP의 일부 기능, XML-RPC를 사용하기로 정해진다면 어떻게 할 것인가? 더 큰 문제는 우리가 해당 도구의 제품 내부 데이터 저장소에 직접 접근해야 한다는 것이다. 이는 결합 문제를 야기한다.
여러분의 방식대로
- 상용제품(COTS)과 Saas 제품은 분명한 제 위치가 있으며, 우리 대다수가 처음부터 개발하는 일은 가능하지도 합당하지도 않다.
- 이러한 문제를 해결할 수 있는 비결은 우리 고유의 방식으로 되돌아가는 것이다.
- 그 어떤 맞춤화든 우리가 통제하는 플랫폼 상에서 진행하고, 해당 도구를 사용하는 서로 다른 소비자의 수를 제한하는 것이다.
- 사례: 서비스로서의 CMS
- 외부용 웹사이트를 제공하는 우리의 서비스를 CMS 앞단에 배치하라.
- CMS를 콘텐트 생성과 추출의 역할을 담당하는 서비스로 취급하라.
- 우리 자신의 서비스에서 코드를 작성하고 다른 서비스와 원하는 방식으로 통합하라.
- 우리는 웹사이트 확장에 대한 통제권을 가지고 적절한 템플릿 시스템을 선택할 수 있다.
- 대부분의 CMS는 콘텐트 생성 API를 제공하므로 우리의 서비스 파사드를 앞단에 배치할 수 있다. 어떤 경우에는 콘텐트를 추출하는 API를 추상화하기 위해 파사드를 사용하기도 한다.
- 사례: 다기능의 고객 관계 관리(CRM) 시스템
- 우리를 위해 무엇이든 하려는 도구들로 가득하며, 그 자체가 단일 장애 지점이 되거나 의존성으로 얽힌 매듭이 될 수 있다.
- CRM이 처리하는 다른 도메인의 개념을 인식하고 해당 도메인을 위한 더 많은 파사드를 생성하는 방법이 있다. 그런 다음 하부의 CRM으로부터 이전할 때가 되면 교체할 내부 소프트웨어 솔루션 또는 상용 제품이 조건을 충족하는지 결정하기 위해 각 파사드를 하나씩 살펴볼 수 있었다.
교살자 패턴
- 우리에게 완전한 통제권이 없는 레거시 플랫폼 또는 상용 플랫폼의 경우 이들을 제거하거나 또는 그로부터 이전하려 할 때 발생하는 문제들을 잘 처리해야 하는데 이때 유용한 패턴이 교살자 애플리케이션 패턴이다.
- CMS의 앞단을 자체 코드로 작성했던 사례와 흡사하게 교살자를 통해 구버전 시스템에 대한 호출을 잡아내 가로챈다.
- 교살자는 이 호출을 기존의 레거시 코드 또는 우리가 작성한 새로운 코드로 바로 경유할지 결정할 수 있다. 엄천난 분량의 코드 재작성 없이도 한번에 기능을 교체하게 해준다.
- 마이크로서비스의 경우 본래의 호출을 잡아내고 재전송하는 일이 더 복잡해질 수 있으므로 프록시가 필요할 수 있다.
마치며
- 데이터베이스 통합은 최대한 피하라.
- REST와 RPC의 장단점을 이해하고 요청/응답을 통합하는 좋은 출발점으로 REST를 고려하라.
- 오케스트레이션보다는 코레오그래피를 우선하라.
- 포스텔의 법칙을 이해하고 관대한 독자패턴을 사용해서 호환성을 깨뜨리는 변경과 불필요한 버전을 피하라.
- 구성 계층으로서의 사용자 인터페이스를 생각하라.
'DevOps | MSA' 카테고리의 다른 글
[마이크로서비스 아키텍처 구축] 모놀리스 분해하기 (0) | 2020.03.13 |
---|---|
[마이크로서비스 아키텍처 구축] 마이크로서비스 / 진화적 아키텍트 / 서비스 모델링하기 (0) | 2020.03.08 |