API 디자인과 프로세스 간 통신
”마이크로서비스 간의 통신”이라고 이름 붙이지 않고, “프로세스 간 통신(Inter-process communication, IPC)”이라고 이름 붙인 것은, 결국 마이크로서비스는 하나의 프로세스 단위로 실행되기 때문입니다. IPC라는 용어가 개발 도메인에서 보다 대중적으로 쓰이는 용어이므로, “마이크로서비스 간의 통신”임을 염두해두고, 이 챕터를 학습하기를 바랍니다.
프로세스 간의 통신(Inter-Process Communication)
서비스끼리 서로 통신하기 위해서는 인터페이스(interface)가 존재해야 하고, 인터페이스가 요구하는 방식대로 커뮤니케이션 해야 한다
앞서 클라이언트/서버 아키텍처를 통해 HTTP라는 프로토콜을 배우고, HTTP 위에서 작동하는 메소드와 엔드포인트로 구성된 REST API(Application Programming Interface), 즉 인터페이스를 이용한 통신을 배웠다.
동기/비동기


HTTP 프로토콜은 기본적으로 TCP(또는 UDP) 연결을 만들고, 이 위에서 요청에 따라 즉시 응답이 오는 형태로 구현이 되어 있습니다. 즉 동기적인 응답을 제공한다.
그런데 세상에는 요청에 따른 응답이 즉시 도착하는 커뮤니케이션만 존재하지않는다. 예를 들어 문자 메시지를 생각해보면 발신자는 수신자가 즉시 메시지를 보고 답장하지는 않는다. 또다른 예로 뉴스레터와 같은 구독 서비스를 생각해보면 구독을 해놓으면 콘텐츠 생산자가 메시지를 정해진 때에 보낸다. 푸시 알림 등으로 우리는 이를 인지하고 소비할 수 있으며 이러한 패턴은 비동기적인 커뮤니케이션이다.
💡 HTTP는 비동기 아닌가?
프로그래밍 언어를 배우신 분들은 이러한 의문을 가질 수도 있다. 어쨌든 HTTP는 컴퓨터와 컴퓨터 사이의 네트워크 통신이고, 이는 네트워크 지연에 따라 즉시 응답이 오지 않을 수도 있기 때문에 이를 비동기로 인식할 수 있다. 이는 프로그래밍 언어의 관점에서 틀린 말은 아니다.
하지만 프로세스 간 통신의 관점에서 HTTP는 동기적인 메커니즘으로 분류한다. 왜냐하면, 어쨌든 서로 통신하는 두 컴퓨터는 모두 켜져 있어야 하며, 클라이언트는 서버가 제 때 응답을 줄 것이라고 기대하기 때문이다.
| 서비스 | 작업 |
| 동기 | 요청 / 응답 |
| 비동기 | 비동기 요청 / 응답 단방향 알림(푸쉬 알림) |
일대일 및 일대다 통신

시간의 관점이 아닌 다른 관점에서도 살펴보면 1:1로 작동하지 않는 커뮤니케이션 방식도 세상에는 많이 존재한다. 앞서 설명한 뉴스레터 구독 서비스는 1:1로 커뮤니케이션 하지 않고 구독자 모두에게 동일한 메시지를 제공하는 형태, 즉 일대다 커뮤니케이션 방식을 취한다.
그럼 HTTP는 일대일인가? 아니면, 일대다인가요? 요청/응답으로 이뤄진 한번의 트랜잭션(transaction)에서 서버는 여러 클라이언트에게 동시에 응답을 전달하지 않는다. 서버가 여러 클라이언트를 상대할 수는 있지만, 그것은 여러번의 1:1 커뮤니케이션이지, 동시에 여러 클라이언트를 상대하는 1대다 커뮤니케이션이 아니기에 일대일 통신이라고 볼 수 있다.
프로세스 간의 직접/간접 연결
클라이언트/서버 아키텍처를 통해 두 프로세스 간의 직접 연결을 구현했지만, 중간에 메시지 브로커가 두 프로세스 사이에 위치해서 오고가는 메시지 자체를 관리하는 형태의 연결 방식도 있다. 이 경우 비동기적인 처리는 물론이고, 설사 둘 중 하나의 프로세스가 실행중이 아니더라도 서로 메시지를 주고받을 수도 있다.
대표적인 데이터 교환 포맷 JSON

JavaScript Object Notation의 약자로 데이터 교환을 위해 만들어진 객체 형태의 포맷이다. 주로 경랑 데이터를 교환하며 JSON 표현식은 사람과 기계 모두 이해하기 쉬우며 용량이 작아서, 최근에는 JSON이 XML을 대체해서 데이터 전송 등에 많이 사용한다. 또한 특정 언어에 종속되지 않으며, 대부분의 프로그래밍 언어에서 JSON 포맷의 데이터를 핸들링 할 수 있는 라이브러리를 제공한다.
네트워크를 통해, 어떤 객체 내용을 다른 프로그램에게 전송한다고 가정할 때 이 객체 내용을 일종의 메신저 혹은 채팅 프로그램에서 쓰는 하나의 메시지라고 한다면, 다음 객체를 어떻게 전송할 수 있을까?
const message = {
sender: "김코딩",
receiver: "박해커",
message: "해커야 오늘 저녁 같이 먹을래?",
createdAt: "2021-01-12 10:10:10"
}
위 객체를 전송할려면 수신자와 발신자가 같은 프로그램을 사용하거나 또는 문자열처럼 범용적으로 읽을 수 있어야한다. 객체는 타입 변화를 통해 string 형태로 변환하면 객체 내용을 포함하지않기에 JavaScript에서 객체에 메소드(message.toString())나 형변환(String(message))을 시도하면, [object Object] 라는 결과를 리턴한다.
해결하는 방법은 객체를 JSON의 형태로 변환하거나 JSON을 객체의 형태로 변환하는 방법이 있으며 메소드는 다음과 같습니다.
- JSON.stringify : Object type을 JSON으로 변환합니다.
- JSON.parse : JSON을 Object type으로 변환합니다.
let transferableMessage = JSON.stringify(message)
console.log(transferableMessage) // `{"sender":"김코딩","receiver":"박해커","message":"해커야 오늘 저녁 같이 먹을래?","createdAt":"2021-01-12 10:10:10"}`
console.log(typeof(transferableMessage)) // `string`
위와 같이 JSON.stringify를 쓰면 Object 타입을 JSON로 변환해주며 그 과정을 직렬화(serialize)라고 한다.
JSON으로 변환된 객체의 타입은 문자열이며 발신자는 객체를 직렬화한 문자열을 누군가에게 객체의 내용을 보낼 수 있다. 그렇다면 수신자는 이 문자열 메시지를 어떻게 다시 객체의 형태로 만들 수 있을까?
JSON.stringify와 정반대의 작업을 수행을 하는 메소드 JSON.parse 를 사용할 수 있습니다.
let packet = `{"sender":"김코딩","receiver":"박해커","message":"해커야 오늘 저녁 같이 먹을래?","createdAt":"2021-01-12 10:10:10"}`
let obj = JSON.parse(packet)
console.log(obj)
/*
* {
* sender: "김코딩",
* receiver: "박해커",
* message: "해커야 오늘 저녁 같이 먹을래?",
* createdAt: "2021-01-12 10:10:10"
* }
*/
console.log(typeof(obj))
// `object`
JSON.parse를 사용하면 JSON형태를 다시 objecrt 형태로 변환하며 이를 역직렬화(deserialize)라고 한다.

이처럼, JSON은 서로 다른 프로그램 사이에서 데이터를 교환하기 위한 포맷이며 JSON 포맷은 자바스크립트을 포함한 많은 언어에서 범용적으로 사용하는 유명한 포맷이다.
JSON의 기본 규칙
JSON을 얼핏 보기에 자바스크립트의 객체와 별반 다를 바가 없어 보이지만, 자바스크립트의 객체와는 미묘하게 다른 규칙이 있다.
| 자바스크립트 객체 | JSON | |
| 키 | 키는 따옴표 없이 쓸 수 있음 | 반드시 큰따옴표를 붙여야 함 |
| 문자열 값 | 문자열 값은 어떠한 형태의 따옴표도 사용 가능 | 반드시 큰따옴표로 감싸야 함 |
또한 JSON은 키와 값 사이, 그리고 키-값 쌍 사이에는 공백이 있어서는 안된다.
동기식 요청/응답 통신 REST
REST
REST는 HTTP로 소통하는 프로세스 간 통신 규약이다. REST API는 웹에서 사용되는 데이터나 자원(Resource)을 HTTP URI로 표현하고, HTTP 프로토콜을 통해 요청과 응답을 정의하는 방식이다.
현대에 있어서 HTTP 메시지의 body 부분을 JSON의 형태로 다루는 것이 보통이며, 이 때 HTTP 헤더의 Content-Type의 값(MIME 타입)은 application/json으로 설정한다.
REST의 장점
- 포스트맨, curl 등의 도구를 사용해 간편하게 테스트가 가능하다.
- 요청/응답 통신을 직접 지원한다.
- 시스템 아키텍처가 단순하다.
REST의 단점
- 요청/응답만 지원한다.
- 메시지를 주고받기 위해서는 클라이언트와 서버 프로세스가 둘 다 실행 중이어야한다.
- 요청 한 번으로 여러 리소스를 조회하기 어렵다.
- 메소드만으로는 한번의 요청을 통해 이루어지는 다양한 작업들을 대표하기 어렵다.
메시지 브로커를 이용한 비동기식 통신
- 요청을 보내는 즉시 수신자로부터 응답이 오길 기대하는 "동기적 방법"
- 클라이언트-서버 아키텍처의 REST(HTTP)가 대표적
- 요청을 일단 보내놓고 수신자가 받을때까지 보관했다가 처리하는 "비동기적 방법"
- 수신자가 받기 전에 누군가는 메시지를 보관해놓아야 한다 → 메시지 브로커 (메시지 큐)
메시지 브로커가 필요한 이유
분산 애플리케이션에서 프로세스 간의 느슨한 결합(loosely coupled)을 제공하는 것이 가장 큰 장점이다. 그러면 강하게 결합된 시스템에서의 단점은 무엇일까?
서로 연결되어 있는 시스템 중 한 곳에서 장애가 발생했을 때 그 장애가 연결된 다른 시스템들에 영향이 간다.

Server가 오류나면 서버1에서 서버2의 데이버베이스로 가는 데이터가 유실된다.

하지만 중간에 메세지 브로커가 있으면 Server2가 복구하는 동안에 메세지를 복구해두기 때문에 데이터가 유실되지않는다.
주요 용어
사람이 생산자(producer), 메시지를 받는 사람이 소비자(consumer)라 하며, 메시지를 보내는 것을 발행(publish), 메시지를 받는 것을 소비(consume)라 부른다.
메시지가 적재되는 공간을 Message Queue(메세지 큐)라고 하며 메시지의 그룹을 Topic(토픽)이라고 한다.
메시지 브로커의 특징
- 프로그램 간의 직접 연결 없기 때문에 낮은 의존성, 장애 허용 시스템을 구현할 수 있다.
- 소비자 프로세스가 죽어있어도 생산자는 메시지를 보낼 수 있다
- 생산자 프로세스가 죽어있어도 소비자는 메시지를 수신할 수 있다
- 메시지 브로커에 있는 메시지는 소비자(수신자)가 꺼낼 때까지 안전하게 보관되기때문에 내구성이 좋다
- (프로세스가 죽어 있어도) 메시지는 소비하기 전까지는 사라지지 않는다
- 따라서, 프로그램 간 통신은 시간과 독립적이다
- 통신이 이벤트에 의해 구동될 수 있다
- 큐의 상태에 따라 프로그램을 제어할 수 있습니다. 예를 들어 메시지가 큐에 도착하는 즉시 프로그램이 시작되도록 설정할 수 있습니다. 또는 예를 들어 큐에서 특정 우선 순위 이상의 메시지가 10개가 되거나 임의의 우선 순위의 메시지가 10개가 될 때까지 프로그램이 시작되지 않도록 지정할 수 있습니다.
- 확장에 용이하다
- 메시지 브로커는 여러 큐를 만들거나, 수평적으로 확장하여 메시지 부하 증가를 처리할 수 있다.
- 프로세스 직접 연결보다 아키텍쳐가 복잡하다
메시지 브로커 선택의 기준
메시지 브로커의 종류로는 Apache Kafka, Amazon Kinesis, Amazon SQS 등이 있다. 어떤 메시지 브로커를 선택해야 할 경우, 다음 기준으로 선택하면 좋다.
- 프로그래밍 언어 지원 여부
- 메시징 표준 지원 여부
- 메시지 순서 보장 여부
- 전달 보장 여부: 어떤 종류의 전달을 보장하는가?
- 영속성: 브로커가 고장나도 문제가 없도록 메시지가 디스크에 저장되는가?
- 내구성: 소비자가 메시지 브로커에 다시 접속할 경우, 접속이 중단된 시간에 전달된 메시지를 받을 수 있는가?
- 확장성
- 지연 시간
도메인 모델
- Eric Evans는 도메인 모델이 Value Object, 엔티티로 표현된다고 했습니다. 각각은 무슨 의미를 가지고, 어떻게 다른가요?
엔티티는 속성이 바뀌더라도 여전히 같은 것으로 인식되는 객체라고 하며 엔티티는 속성이 바뀔 수 있기 때문에 가변(mutable) 객체다.
값객체는 객체의 속성이 바뀌지않기에 그 객체를 불변(immutable) 객체라고한다.
어떤 객체가 id 로 식별된다면 엔티티로, 그렇지 않다면 값 객체라고 구분할 수 있다
시나리오: 교육 상품의 구매와 교육과정 수강
- 빨간 색으로 강조된 부분이 왜 불안한 징조일까요?
- 기존의 설계대로 구현했을 경우 어떤 문제가 생길까요?
마이크로서비스
- 데이터베이스를 꼭 분리해야 하나요? 한곳에 모여있지 않고 중복되어도 괜찮은가요?
- 상품 구매 서비스는 성공했는데, 수강 관리 서비스에서 실패해서 데이터가 안들어가면 어떡하죠?
- 개발자가 도메인에 종속되어서 개발하면, 자율성이 줄어들지는 않을까요?
'TIL' 카테고리의 다른 글
| 46일차 마이크로서비스 작성 (0) | 2022.06.21 |
|---|---|
| 45일차 마이크로서비스 (0) | 2022.06.20 |
| 43일차 마이크로 서비스 (0) | 2022.06.16 |
| 42일차 마이크로서비스 (0) | 2022.06.15 |
| 36일차 배포 자동화 (0) | 2022.06.07 |