STUDY/Data Engineering

04장 부호화와 발전

wonpick 2024. 3. 31. 15:21

데이터 중심 어플레케이션 설계 스터디를 진행하며 작성한 글 입니다.


서론

데이터 부호화란(Data Encoding)?

필요성 : 데이터부호화 형식에 따라 human-readable(사람이 읽기에 좋은), 데이터 압축률(가용성 증가, 통신 성능 증가), 호환성 유지에 영향을 줄 수 있기 때문

 

호환성이 중요한 이유
  • 서버 단 애플리케이션: 대규모 시스템에서는 트래픽 처리를 위해 여러 개 서버군이 한 역할을 하는데 여기서 무중단 배포를 위해서 이전 버전 서버, 새 버전 서버가 공존할수밖에 없다.
  • 클라이언트 단 애플리케이션: 사용자에게 전적으로 좌우된다. 사용자가 업데이트 하지 않으면 적용할 수 없다.

애플리케이션은 시간이 지나면서 점점 변한다. 이 변화에 DB의 변화도 포함되고 column, field 가 추가되거나 삭제되며, data type이 변경되기도 한다.

이러한 DB 관점의 변경은 바로바로 적용된다. 하지만, application의 코드는 대체로 바로 적용되지 않는다.

시스템이 계속 원할하게 실행되게 하기 위해 양뱡향으로 호한성을 유지해야 한다. 새로운 코드는 예전 버전의 코드가 기록한 데이터의 형식을 명확히 알 수 있기 때문에 하위 호환성은 일반적으로 어렵지 않다. 하지만 예전 코드가 새로운 코드가 기록한 데이터의 형식을 예측할 수 없기 때문에 상위 호환성은 일반적으로 다루기 더 어렵다.

  • 하위 호환성: 새로운 코드는 예전 코드가 기록한 데이터를 읽을 수 있어야 한다.
  • 상위 호환성: 예전 코드는 새로운 코드가 기록한 데이터를 읽을 수 있어야 한다.

정의 : 정보의 형태나 형식을 표준화, 보안, 처리속도 향상, 저장공간절약 등을 위해 애플리케이션 환경에 구애 받지 않는 다른 형태나 형식으로 변환하는 과정

프로그램은 보통(최소한) 두 가지 형태로 표현된 데이터를 사용해 동작한다.

  • 애플리케이션에서는 메모리에 데이터를 저장 할 때 객체(Obeject), 구조체(Struct), 목록(List), 배열(Array), 해시테이블(Hash table),트리(Tree)로 유지
  • 보통 CPU가 효율적으로 접근할 수 있도록 ‘포인터'를 사용하는데, 이는 다른 언어를 사용하는 프로세스에서는 이해하지 못할 수 있기 때문에 데이터를 파일에 쓰거나 네트워크로 전송하려면 바이트 형태로 저장

부호화: 인메모리 → 바이트 (직렬화, 마샬링)

복호화: 바이트 → 인메모리 (역질렬화, 언마샬링, 파싱)

 

💡 컴퓨터 구조 이해하기

  • 대부분 OS의 프로세스 구현은 서로 다른 가상메모리주소공간(Virtual Address Space, VAS)를 갖기 때문에 Object 타입의 참조값(주소값) 데이터 인스턴스를 전달할 수 없다.(전달해도 서로 다른 메모리 공간에서는 전달된 참조값이 무의미하다.) 때문에 서로 다른 메모리 공간 사이의 데이터 전달을 위해서는 메모리 공간의 주소값이 아닌 Byte 형태로 직렬화(변환)된 객체 데이터를 전달하면, 사용하는 쪽에서 역직렬화하여 사용할 수 있게 된다.
    • 프로세스란?
      • 프로세서요리사고, 조리대메모리이다. 대량주문이 들어오는 이 식당에서 끊임없이 만들어내는 요리 메뉴 하나하나가 프로세스가 된다. 프로세스 하나를 만들어내기 위한 과정들이 스레드이다. 햄버거를 만드는 프로세스안에서 패티를 굽는 스레드, 소스뿌리는 스레드 등등이 진행된다. 한 메뉴의 스레드들은 같은 조리대에서 이루어진다. 프로세스들은 컴퓨터의 자원을 분할해서 쓰지만 스레드는 프로세스마다 주어진 전체 자원을 함께 사용한다.
      • 프로세스는 실행 중인 프로그램이다. 수많은 요소들로 구성된 개체로도 생각될 수 있다. 프로세스의 두 필수적인 요소는 프로그램 코드 및 그 코드와 연계된 데이터의 집합이다. 프로세서(CPU)가 프로그램 코드를 수행한다고 가정하면, 그 수행중인 개체가 프로세스이다.
    • 프로세스간의 통신이 왜 필요한데?
      • 요즘 CPU는 코어를 적게는 8개, 많게는 64개까지 갖고있다.CPU의 효율/성능을 높이기 위해 여러 코어를 동시에 활용하도록 다수의 프로세스를 동시에 실행시킨다.다양한 프로세스를 동시에 실행시키는 경우, 프로세스간의 상태를 확인하기 위하여 프로세스간 데이터 통신을 필요로 한다.
        • 정보 공유 (Information sharing)
        여러 사용자가 동일한 정보에 흥미를 가질 수 있으므로, 그러한 정보를 병행적으로 접근할 수 있는 환경을 제공해야 한다.
        • 모듈성 (Modularity)
        시스템 기능을 별도의 프로세스들 또는 스레드들로 나누어 시스템을 모듈식 형태로 구성할 경우, 각 시스템 모듈이 서로 협력하게 만들기 위해서
        • 계산 가속화 (Computation speedup)
        만일 우리가 특정 작업을 빨리 실행하고자 한다면, 우리는 해당 작업을 여러 개의 서브태스크(sub-task)로 나누어서 각각 다른 서브태스크들과 병렬로 실행되게 해야 한다. 이 계산 가속화는 여러 개의 처리요소(processing element - CPU나 입출력 채널 등)들을 가진 경우에만 달성할 수 있음에 유의해야 한다.
        • 편의성 (Convenience)
        개별 사용자들이 동시에 작업할 여러 태스크를 가질 수도 있다. 예를 들면, 한 사용자가 편집, 인쇄, 컴파일 등을 병렬로 할 수 있다.
    • 프로세스 간 협력(**Interprocess Communication)
      • 독립적 프로세스 (independent process)
        • 프로세스는 각자의 주소 공간을 가지고 수행되므로 원칙적으로 하나의 프로세스는 다른 프로세스의 수행에 영향을 미치지 못함
      • 협력 프로세스 (Cooperating process)
        • 프로세스 협력 메키니즘을 통해 하나의 프로세스가 다른 프로세스의 수행에 영향을 미칠 수 있음
      • 프로세스 간 협력 메커니즘(IPC: interprocess communication)
        • 메세지를 전달하는 방법 : message passing  커널을 통해서 메세지 전달 (메시지 전달 모델에서는 협력 프로세스들 사이에 교환되는 메시지를 통하여 통신이 이루어진다.)
        • 주소 공간을 공유하는 방법 : shared memory  서로 다른 프로세스 간에도 일부 주소 공간을 공유하게 하는 shared memory 메커니즘이 있음. (공유 메모리 모델에서는 협력 프로세스들 사이에 공유되는 메모리 영역이 구축된다.프로세스들은 그 영역에 데이터를 읽고 쓰고 함으로써 정보를 교환할 수 있다.)
        • 예시) 파이프(pipe), Named PIPE(FIFO), Message Queue,Shared Memory (공유 메모리),Memory Map
    • 서로 다른 메모리 공간❓
      메모리 구조는 크게 다음 2가지로 나뉜다. 
      • 값 형식 데이터: integer, float(single), charactor(또는 char 의 집합인 string) 등 → ('저장/전송 가능한 데이터’)
      • 오브젝트(레퍼런스) 형식 데이터: 메모리 번지(주소, Address)값 --> 주소값을 최종적으로 따라가면 값 형식 데이터를 참조 하게 됨. (C/C++) 또는 언어 차원에서 이 과정을 생략해줌 (C#, JAVA) --> 클래스의 인스턴스는 해당 프로세스의 메모리 상에서만 유효한 번지 주소를 갖는 오브젝트(레퍼런스) 데이터.
    • 오브젝트(레퍼런스) 형태의 참조 데이터(메모리 번지 주소 데이터)는 상식적으로도 파일 저장이나 네트워크 전송이 불가능합니다. 
    • 예시) 일례로 32비트 시스템에서 Class A 의 인스턴스를 만들었고, 그 참조/주소값이 0x00121212 이었습니다. 그리고 이 참조/주소값 자체도 강제로 파일에 포함 시켜 저장하였습니다. 하지만 다음에 프로그램(서비스)를 다시 Start 시키고 이전에 저장했던 파일에서 0x00121212 참조/주소를 다시 읽어와도 클래스 A 의 인스턴스는 부활 할 수 없으며 이해할 수 도 없는 쓰레기 값일 뿐입니다. 네트워크 전송도 마찬가지로 받는 상대방 입장에서는 전달자가 사용한 참조/주소값 자체는 무의미 합니다. 서로 물리적으로 사용중인 메모리 공간(OS의 가상메모리 포함)은 일치하지 않기 때문입니다.
    • JAVA관점에서의 예시)
      1️⃣ 자바는 내부적으로 오브젝트(또는 Reference) 형식의 데이터를 많이 사용 (오브젝트의 주소 메모리 번지 값 접근/편집을 일반적인 JAVA 코딩에 쓰지 않는다.)
      2️⃣ JAVA 의 클래스 설계에는 오브젝트 안에 오브젝트가 또 들어있을 수 있습니다. (인스턴스 포함 관계) → 오브젝트 안에 내부적으로 다른 오브젝트를 참조할 수 있는 주소값이 담긴 것을 의미
      3️⃣ 주소값의 실체를 다 끌어와서 Primitive 한 값 형식 데이터로 전부 변조하는 작업을 바로 직렬화(Serialization)라 한다. → XML, JSON 등의 데이터 구조를 떠올리면 이해가 빠를것입니다. → C/C++ 을 해보셨다면 좀 더 이해가 빠를 것입니다. (포인터 데이터를 모두 실제 값의 묶음 형식으로 전달, NPOD 데이터를 POD 데이터로 전달, 그리고 한방에 memcpy!)
      4️⃣ 직렬화 된 데이터 형식은 언어에 따라 텍스트로 된 데이터 또는 바이너리 등의 모양을 띄게 된다. (어차피 텍스트든 바이너리든 결국 둘 다 Primitive 한 값들의 집합임)
      5️⃣ 결국 직렬화가 된 데이터는 최종적으로 오브젝트 타입이 없다. 모든것이 Primitive 한 값 형식의 데이터 묶음이며, 이것은 파일 저장이나 네트워크 전송시 파싱 할 수 있는 유의미한 데이터가 된다.
      → 직렬화는 보통 파일 저장이나, 패킷 전송시에 '파싱할 수 있는 데이터를 만들기 위해' 사용
  • 프로세스간에 데이터 전송에도 직렬화된 데이터가 왜 사용되야해?
    • 프로세스 간에 데이터 전송에도 직렬화된 데이터가 사용 되는 이유는 대부분의 OS 가 현재 가상메모리를 운영 중이며 대부분의 OS 의 프로세스 구현은 서로 다른 가상메모리주소공간(Virtual Address Space, VAS) 를 갖기 때문에 역시 마찬가지로 오브젝트 타입의 참조값(결국 주소값)데이터 인스턴스를 직접 줄 수 없어서 직렬화된 데이터로의 교환을 주로 사용한다.
  • Java의 클래스 설계에서는 객체 안에 객체가 존재할 수 있다. 객체 A 안에 들어있는 객체는 B는 객체 B를 참조할 수 있는 주소값인데, 직렬화를 하면 이 주소값이 아니라 객체 B 자체의 데이터를 Primitive 타입(Byte 타입) 데이터로 변환한다. 때문에 직렬화 된 데이터는 모두 Primitive 타입(Byte 타입)의 데이터 묶음이며, 이것이 파일 저장이나 네트워크 전송 시 파싱할 수 있는 유의미한 데이터가 되는 것이다. 즉, 전송/저장 가능한 데이터를 만드는 것이 직렬화(Serialization) 이다.

참고 | Java Serialization 개념 정리 , https://okky.kr/questions/224715

 

언어별 부호화 기능

  • 많은 프로그래밍 언어는 인메모리 객체를 바이트열로 부호화하는 기능을 내장한다.(대표적으로 Java의 Serializable)
  • 이러한 부호화 라이브러리는 편리하지만 심각한 문제점들이 많이 때문에 일반적으로 이를 사용하는건 좋지 않다.
    • 특정 언어에 결합되어 있어 다른 언어에 지원이 좋지 못하다.
    • 동일한 객체 유형의 데이터를 복원하기 위해 복호화 과정에서 클래스를 인스턴스화할 수 있어야하고 이것은 보안의 큰 문제가 될 수 있다.

1. 데이터 부호화 형식

Text Encoding(이진 변형)

표준화된 텍스트 인코딩 형식으로써 JSON과 XML, 또는 CSV가 있다. 텍스트 형식이라 사람이 읽기에 유리하다.

그러나 이들도 조금 부족한 점들이 있는데:

  • number 형식의 인코딩이 어렵다. XML, CSV는 number type과 문자열에 숫자가 들어간 형식을 구분할 수 없다. JSON은 문자열과 수를 구분하지만 정수와 부동소수점 수를 구별하지 않는다.
  • JSON과 XML은 유니코드 문자열(사람이 읽을 수 있는 텍스트)를 잘 지원하지만 이진 문자열(문자 인코딩이 없는 바이트열)을 지원하지 않는다. Base64를 사용해 이진 데이터를 텍스트로 부호화해 사용할 수 있기는 하다. 그러나 데이터 크기가 33% 증가한다.
  • 바이너리 타입에 비해 훨씬 큰 공간을 차지한다.

Binary Encoding(이진 부호화)

작은 데이터셋의 경우에는 인코딩 타입에 따른 영향이 거의 없지만 테라바이트 단위가 되면 데이터 타입의 선택이 큰 영향을 미친다.

JSON이나 XML은 바이너리 타입과 비교하면 훨씬 많은 공간을 사용한다. 이런 이유로 BSON, BJSON, WBXML 등 JSON과 XML을 발전 시킨 바이너리 인코딩 방식이 개발되기는 했으나, 사용 빈도가 보편적이지는 않다.

Thrift and Protocol Buffers(protobuf)

프로토콜 버퍼는 구글에서 개발했고 스리프트는 페이스북에서 개발했다. 같은 원리를 기반으로 한 바이너리 인코딩 라이브러리다.

모두 부호화할 데이터를 위한 스키마가 필요하다. 스리프트를 예시로 들면 다음과 같이 스리프트 인터페이스 정의 언어(interface definition language, IDL)로 스키마를 기술해야 한다.

struct Person {
    1: required string    userName,
    2: optional i64       favoriteNumber,
    3: optional list<string> interests

Schema Evolution스키마는 필연적으로 시간이 지남에 따라 변하기 마련이다. 이를 Schema Evolution이라고 한다. 프로토콜 버퍼와 스리프트는 상위 호환성과 하위 호환성을 유지하면서 스키마를 변경할 수 있다.

  • 상위 호환성은 예전 코드가 새로운 코드로 기록된 레코드를 읽을 수 있는 것
  • 하위 호환성은 새로운 코드가 예전 데이터를 읽을 수 있는 것

Avro

이제 이 글의 주인공인 Avro(보통 아브로라고 읽으면 된다)에 대해서 알아보자. 아브로는 하둡의 하위 프로젝트로 시작 했다. 이것도 또 더그 커팅 형님이 창시했다.

Avro도 인코딩할 데이터를 위해 스키마를 사용한다.

두 개의 스키마 언어가 있는데, 하나는 사람이 편집할 수 있는 Avro IDL이고 하나는 기계가 더 쉽게 읽을 수 있는 JSON 기반 언어다.

  • Avro IDL 예시:
"record Person {
    string               userName;
    union { null, long } favoriteNumber = null;
    array<string>        interests;
}
  • JSON 기반:
{
  "type": "record",
  "name": "Person",
  "fields": [
    {"name": "userName", "type": "string"},
    {"name": "favoriteNumber", "type": ["null", "long"], "default": null},
    {"name": "interests", "type": {"type": "array", "items": "string"}}
  ]
}

Avro는 어떻게 Schema Evolution을 지원할까?

애플리케이션이 Avro로 인코딩을 원한다면 알고 있는 스키마 버전을 사용해 데이터를 인코딩한다. 해당 스키마를 애플리케이션에 포함할 수 있고, 이를 쓰기 스키마(writer’s schema)라고 한다.

애플리케이션이 네트워크로부터 수신한 데이터를 복호화하길 원한다면 복호화를 위한 스키마가 필요하다. 이 스키마를 읽기 스키마(reader’s schema)라 한다.

Avro의 핵심 아이디어는 쓰기 스키마와 읽기 스키마가 동일하지 않아도 되며, 단지 호환 가능하면 된다는 점이다. 데이터를 읽을 때(디코딩) Avro 라이브러리는 쓰기 스키마와 읽기 스키마를 모두 살펴보고 쓰기 스키마로부터 읽기 스키마로 데이터를 변환해 그 차이를 해소한다.

  • 쓰기 스키마와 읽기 스키마 간 필드 순서가 달라도 관계 없다. Schema Resolution(스키마 해석) 시 이름으로 필드를 일치시키므로.
  • 데이터를 읽는 코드가 읽기 스키마에는 없고 쓰기 스키마에만 있는 필드를 만나면 이 필드는 무시 한다.
  • 데이터를 읽는 코드가 기대하는 어떤 필드가 쓰기 스키마에는 포함되어 있지 않은 경우에는 읽기 스키마에 선언된 default value로 채운다.

Avro reader가 쓰기 스키마와 읽기 스키마의 차이를 해소한다. (데이터중심 어플리케이션 설계. 그림 4.6)

Writer’s schema는 어떻게 전달할까?

Avro 데이터를 읽는 코드가 특정 데이터를 인코딩한 쓰기 스키마를 어떻게 알 수 있을까? 모든 레코드마다 전체 스키마를 포함시킨다면 데이터보다도 더 커질 수 있다. 이러면 이진 부호화로 절약한 공간이 소용 없어진다.

아래와 같은 전달 방법들이 있다.

  • 대용량 파일Avro의 일반적인 용도(하둡과 함께 활용하는)는 수백, 수천만 레코드를 포함한 큰 파일을 저장하는 용도이다. 이 경우 파일의 시작 부분에 쓰기 스키마를 포함시킨다.object container file이라는 파일 형식을 명시한다.
  • 데이터베이스데이터베이스의 다양한 레코드들은 다양한 쓰기 스키마를 사용해 서로 다른 시점에 쓰여졌을 수 있다. 이 경우 레코드의 시작 부분에 버전 번호를 포함하고 데이터베이스에는 스키마의 버전별 히스토리를 보관한다. 읽기 코드는 레코드에서 버전 번호를 추출한 다음 해당 버전에 해당하는 쓰기 스키마를 가져올 수 있다
  • 네트워크 연결네트워크를 통해 통신할 때는 연결 설정에서 스키마 버전 합의를 할 수 있다. 연결을 유지하는 동안 합의된 스키마를 사용한다. Avro RPC protocol이 이렇게 동작한다.

스키마의 장점

위와 같이 프로토콜 버퍼, 스리프트, 아브로는 스키마를 사용해 이진 부호화 형식을 기술한다. 이 스키마 언어는 XML이나 JSON보다 훨씬 더 간단하며 더 자세한 유효성 검사 규칙을 지원한다.

관계형 데이터베이스에도 이러한 데이터베이스 네트워크 프로토콜로부터 응답을 인메모리 데이터 구조로 복호화하는 드라이버(ODBC나 JDBC API)를 제공한다.

JSON, XML, CSV와 같은 텍스트 데이터 타입보다 스키마를 기반으로 한 바이너리 인코딩 타입이 갖는 장점들이 있다.

  • 인코딩된 데이터에서 필드 이름을 생략하므로 다양한 “이진 JSON”보다도 크기가 더 작다.
  • 디코딩을 위해 스키마가 필요하므로 스키마가 최신 상태인지 확신할 수 있다.(문서로 수동으로 관리되는 스키마는 버전과 동떨어지기 쉽다)
  • 스키마의 상위 호환성과 하위 호환성을 확인할 수 있다.
  • Static 타입 프로그래밍 언어(Java, C++)의 경우 컴파일 시점에 타입 체크를 할 수 있다.

메모리를 공유하지 않는 하나의 process 에서 다른 process로 데이터를 전달하는 보편적인 방법을 알아보며 이때 사용할 부호화 방법과 상위, 하위 호환성에 대해서 알아보았다. 상, 하위 호환성은 한 번에 모든것을 변경할 필요 없이 시스템의 다양한 부분을 독립적으로 업그레이드 하는데 필요한 기능이다.


2.데이터플로 모드(Modes of Dataflow)

소켓 라이브러리는 Socket.io 또는 Websocket과 관련 없음

  • 데이터플로는 매우 추상적인 개념.
  • 하나의 프로세스에서 다른 프로세스로 데이터를 전달하는 방법은 매우 많다.

다음은 가장 보편적인 (메모리를 공유하지 않는) 프로세스 간 데이터 전달 방법이다.

  • 데이터베이스를 통해서
  • 서비스 호출을 통해서
  • 비동기적 메시지 전달을 통해서

데이터 베이스를 통한 데이터 플로

일반적으로 동시에 다양한 프로세스가 데이터베이스를 접근하는 일은 흔하다. 이런 프로세스는 다양한 애플리케이션이나 서비스일 수 있다. 어느 방법이든 애플리케이션이 변경되는 환경에서 데이터 베이스에 접근하는 경우 아마도 일부 프로세스는 새로운 코드로 수행 중잉고 일부 다른 프로세스는 예전 코드로 수행 중일 것이다. 예를 들어 순회식 업그레이드로 현재 새로운 버전을 배포하는 도중이라면 일부 인스턴스는 아직 갱신되지 않았지만 일부 인스턴스는 이미 갱신됐다.

  • 데이터를 부호화하고 데이터베이스에서 읽는 프로세스는 데이터를 복호화함 → 애플리케이션의 변환 과정에서 알지못하는 필드가 유실 될 수 있다.
  • 데이터를 새로운 스키마로 다시 기록(rewriting 마이그레이션)하는 작업은 분명 가능하다. 하지만 대용량 데이터를 마이그레이션 하는 작업은 비싼 비용의 작업이기 때문에 대부분의 관계형 데이터베이스는 기존 데이터를 다시 기록하지 않고 null을 기본값으로 갖는 새로운 칼럼을 추가함
  • 백업 목적이나 데이터 웨어하우스 저장 목적의 데이터 덤프는 복사본을 일관되게 부호화 하는 편이 나음 → 데이터 덤프는 한번에 기록하고 변하지 않으므로 아브로 객체 컨테이너 파일과 같은 형식이 적합하다. 또한 이것은 파케이와 같은 분석 친화적인 칼럼 지향 형식으로 데이터를 부호화할 좋은 기회이다.

서비스 호출을 통한 데이터 플로 : REST와 RPC

네트워크를 통해 통신해야 하는 프로세스가 있을 때 해당 통신을 배치하는 가장 일반적인 방법은 클라이언트와 서버 두 역할로 배치하는 것

클라이언트와 서버

  • 클라이언트 : API로 요청을 만들어 서버에 연결할 수 있다.
  • 서버 : 네트워크를 통해 API를 공개한다.
    • 서비스 : 서버가 공개한 API

요청을 보내는 애플리케이션을 클라이언트, 응답을 보내는 애플리케이션을 서버라 할 수 있다.

웹 동작 방식

  • 클라이언트(웹 브라우저, 모바일 디바이스, 데스크톱 등등 )는 웹 서버로 요청을 보냄 → HTML, CSS, 자바스크립트, 이미지 등을 다운로드하기 위해 GET요청을 보냄
  • API는 표준화된 프로토콜과 데이터 타입(HTTP, URL, SSL/TLS, HTML 등)으로 구성된다.
  • 웹 브라우저 내에서 수행되는 클라이언트 측 자바스크립트 애플리케이션은 XMLHttpRequest를 사용해 HTTP 클라이언트가 될 수 있다. (이 기술을 에이젝스(Ajax)라 칭함.)*에이젝스(Ajax)Ajax는 html페이지 전체가 아니라 필요한 부분만을 갱신할 수 있도록 XMLHttpRequset 객체를 통해서 요청합니다. Json이나 xml 형태로 최소한의 필요한 데이터만 받아서 갱신하게 됨으로 자원낭비가 그만큼 줄어들기에 더 나은 서비스를 구현할 수 있습니다.
  • 이경우 서버 응답은 보통 사람이 볼 수 있게 표시하는 HTML보다는 클라이언트 측 애플리케이션 코드가 이후 처리를 편리하게 할 수 있게 부호화한 JSON, XML과 같은 형태의 파일로 이루어 있다.
  • http 프로토콜은 기본적으로 클라이언트 쪽에서 request를 보내고 server의 response를 받으면 이어졌던 연결이 끊기게 설계가 되어 있습니다. 그래서 화면의 내용을 갱신하기 위해서는 다시 request를 하고 response로 페이지 전체를 다시 받게 됩니다. 하지만 페이지 내용을 일부만을 업데이트 하고자 하는데 페이지 전체를 다시 로드하게 된다면 자원 낭비겠죠?
  • Javascript를 통해 Ajax를 사용하는데 Javascript는 원래 클라이언트 쪽 그러니까 웹 브라우저에서 출발한 언어여서 서버와 교신하는 기능은 없었지만 Ajax를 통해서 가능하게 되었다.
  • 서버 자체가 다른 서비스의 클라이언트가 될 수 있다. 이런 접근 방식은 다른 서비스의 일부 기능이나 데이터가 필요하다면 해당 서비스에 요청을 보낸다.
    • 이러한 애플리케이션 개발 방식을 아래와 같이 칭한다. 전통적 : 서비스 지향 설계 (service-oriented architecture, SOA) 개선 : 마이크로서비스 설계(microservices architecture, MSA)
    • 위 아키텍쳐의 핵심 설계 목표는 서비스를 배포와 변경에 독립적으로 만들어 애플리케이션의 변경과 유지보수를 더욱 쉽게 만드는 것
      • 예전 버전과 새로운 버전의 서버와 클라이언트가 동시에 실행되기를 기대한다
      • 따라서 서버와 클라이언트가 사용하는 데이터 부호화는 서비스 API 버전간 호환이 가능해야한다.

웹 서비스

웹서비스 예시)

  • 서비스와 통신하기 위한 기본 프로토콜로 HTTP 사용할 때 (라고 정의할 수는 있지만 이는 너무 함축적인 표현임)
  • 사용자 디바이스에서 실행하며 HTTP를 통해 서비스에 요청하는 클라이언트 애플리케이션. 보통 공공 인터넷 통해 전달된다. → 모바일 디바이스에서의 기본 앱, 에이젝스를 사용하는 자바스크립트 웹 앱
  • 서비스 지향/마이크로 서비스 아키텍처의 일부로서 대개 같은 데이터 센터에 위치한 같은 조직의 다른 서비스에 요청하는 서비스.(이런 종류의 사용 사례를 지원하는 소프트웨어를 미들웨어라 칭함.)
  • 보통 인터넷을 통해 다른 조직의 서비스에 요청하는 서비스 이것은 다른 조직의 백엔드 시스템 간 데이터 교환을 위해 사용한다. 이 범주에 신용카드 처리 스스템 같은 온라인 서비스가 제공하는 공개 API나 사용자 데이터 공유 접근을 위한 OAuth가 포함된다.

웹서비스 방법 2가지 REST & SOAP

REST와 SOAP는 각기 다른 두 가지의 온라인 데이터 전송 방식입니다. 둘 다 웹 애플리케이션 간 데이터 통신을 허용하는 애플리케이션 프로그래밍 인터페이스(Application Programming Interface, API)를 구축하는 방법을 정의한다.

 

💡 API란?

api란 두 개 이상의 컴퓨터 프로그램이 서로 통신하는 방법

 

API는 “Application Programming Interface”의 준말. 풀이를 하자면, 여러 프로그램들과 데이터베이스, 그리고 기능들의 상호 통신 방법을 규정하고 도와주는 매개체이다. 

즉, 서비스와 서비스끼리의 요청/명령을 받기위한 인터페이스라고 정리할 수 있다.

API는 데이터베이스가 아니지만, 액세스 권한이 있는 앱의 권한 규정과 “서비스 요청”에 따라 데이터나 서비스 기능을 제공하는 메신저 역할을 한다.

API가 필요한 이유?

API의 필요성은 Web의 진화와 밀접한 연관이 있으니 잠깐 살펴보면, 모놀리틱 아키텍처(Monolithic Architecture)가 주도적이었던 Web 1.0 시대에서는 (하지만 현재에도 사실 많이 쓰이고 있는 것이 사실!) 서버와 클라이언트가 분리되지 않고 모두 서버에서 동시에 처리하기 때문에 API 필요성이 그다지 절실하지 않았다.

그러나 2000년경부터 시작된 Web2.0의 “개방, 참여, 공유”의 정신을 바탕으로 정보가 쌍방향으로 소통하고 “사용자가 생성한 데이터”를 위주로 웹 앱의 붐, 그리고 2010년대 들어서 클라우드(Cloud) 기반 인프라와 MSA(Microservices Architecture)의 사용이 확산되면서 API 확산이 가속화되었고 이제 API에서 가장 흔한 구조인 REST 또는 RESTful API가 점차 새로운 웹 생태계의 기반으로 주목된 것이다.

API를 개인적으로는 이렇게 이해한다: “request-to-serve”, 한마디로 “각자 권한 분야에서 각자 필요한 것만 연계하기(철저한 개인주의 따로국밥?)”를 가능하게 해주는 서비스.

API종류

1) Private API

Private API는 내부 API로, 기업이나 연구 단체 등에서 자체 제품과 운영 개선을 위해 단체 내부에서만 사용. 따라서 제삼자에게 노출되지 않는다.

2) Public API

Public API는 말 그대로 public, 즉 개방형 API로, 모두에게 공개된다. Public API 중에서도 접속하는 대상에 대한 제약이 없는 경우를 OpenAPI라 한다.

3) Partner API

Partner API는 특정 비즈니스 파트너 간의 데이터 공유. 그러므로 동의하는 특정인들만 사용할 수 있다.

 

REST (Representational State Transfe)

GET /users          : 모든 사용자의 정보를 가져온다.
GET /users/{id}     : id에 해당하는 사용자의 정보를 가져온다.
POST /users         : 새로운 사용자를 추가한다.
PUT /users/{id}     : id에 해당하는 사용자 정보를 수정한다.
DELETE /users/{id}  : id에 해당하는사용자 정보를 삭제한다.
  • REST는 프로토콜이 아니라 HTTP의 원칙을 토대로 한 설계 철학
  • 간단한 데이터 타입을 강조하며 URL을 사용해 리소스를 식별하고 캐시 제어, 인증, 콘텐츠 유형 협상에 HTTP 기능을 사용
  • REST 원칙에 따라 설계된 API를 RESTful 이라고 부름
  • 코드 생성과 자동화된 도구와 관련되지 않은 간단한 접근 방식을 선호한다.
  • Swagger(스웨거)로 알려진 오픈 API 같은 정의 형식을 사용해 RESTful API와 제품 문서를 기술하는데 사용할 수 있다.

  • 사용예제
(1) API의 헤더 정보와 body 값을 정의한다. 보통 다른 업체의 API를 사용할 때는 고유의 token을 발급 받는다. 토큰 정보는 private하기 때문에 실행 환경에 넣어두는 것이 좋다.
(2) API 통신은 다양한 에러가 발생할 수 있다. 따라서 사전에 try/catch을 사용해 에러를 잡는 것이 좋다. post/get/delete 등의 방식에 따라 HTTP.post()와 같이 사용한다. 첫 번째 인자에는 사용할 API주소, 두 번째 인자에는 option 정보를 넣는다.
(3) API 통신 결과는 string으로 나오기 때문에 parse를 통해 객체 형태로 바꿔준다.

let opt = {		--- (1)
    headers: {
        "content-type"	: "application/json",
        "token"		: "abcdefg123456"
    },
    data: {
        "name" : "minidoo",
        "blog" : "minidoo.log"
    }
};

try {			--- (2)
    let url = "https://velog.io/@minidoo/api/information";
    let result = HTTP.post(url, opt);
    let data = JSON.parse(result.content);	--- (3)
    
    return data;
} catch(err) {
    console.log(err.message);
}

SOAP(Simple Object Access Protocol)

  • HTTP, HTTPS, SMTP 등을 사용하여 XML 기반의 메시지를 컴퓨터 네트워크 상에서 교환하는 통신 프로토콜 → 쉽게말해 HTTP, HTTPS등 통신프로토콜 위에서 XML 메시지를 요청응답 받는 것.
  • HTTP 상에서 가장 일반적으로 사용되지만 HTTP와 독립적이며 대부분 HTTP 기능을 사용하지 않는다. 그 대신 다양한 기능을 추가한 광범위하고 복잡한 여러 표준(WS-*라 하여 Web Service Framework)을 제공한다.
  • 웹서비스 기술 언어(Web Services Description Language) 또는 WSDL(XML 기반 언어)를 사용해 기술한다.
WSDL란?

WSDL(Web Services Description Language) 는 웹서비스가 기술된 정의 파일의 총칭으로 XML을 사용해 기술됩니다.
웹서비스의 구체적인 내용이 기술되어있는 문서이고 서비스 제공장소, 서비스 메세지 포맷, 프로토콜들이 기술되어있습니다.

특징
-XML기반으로 플랫폼에 독립적, 서로 다른 운영체제에서 실행되는 서비스간 통신방법을 제공
-프록시와 방화벽에 구애받지 않고, HTTP, HTTPS등을 통해 메세지를 교환
-확장이 용이
-에러 처리에 대한 내용이 기본적으로 내장
-XML 형태로 메세지를 보내기때문에 다른 기술들에 비해 상대적으로 처리속도가 느림

참고 | 호다닥 공부해보는 SOAP
  • 사람이 읽을 수 없도록 설계되어 도구나 IDE에 크게 의존한다
  • SOAP 벤더 가 지원하지 않는 프로그래밍 언어 사용자의 경우 SOAP 서비스와의 통합은 어렵다.
  • 사용예제
import soapRequest from 'easy-soap-request';

(1) 필수 Parameters 설정
let params = `
    <DATA>
      <NAME>minidoo</NAME>
      <VLOG>minidoo.log</VLOG>
    </DATA>
`;

(1-1) 공백제거
params = params.replace(/>\s*/g, '>').replace(/\s*</g, '<');

(2) xml 스키마 설정
let xml = `
    <?xml version='1.0' encoding='utf-8'?>
    <soap:Envelope xmlns:xsi='' ...>
      <soap:Body> ... </soap:Body>
    </soap:Envelope>
`;

(2-1) 공백제거
xml = xml.replace(/>\s*/g, '>').replace(/\s*</g, '<');

(3) SOAP API 통신
let url = "https://velog.io/@minidoo/api/information";
let soapHeader = {
    "Content-type" : "text/xml";
};

let opt = { url: url, soapHeader: soapHeader, xml: xml };

(async () => {
    const { response } = await soapRequest(opt); 
    const { body } = response;
    
    console.log(body);
})();

 

SOAP는 기업간 통합과 상호 운용성을 이루는데 있어서 가장 단순한 메커니즘이다. SOAP는 다른 언어로 다른 플랫폼에서 빌드된 애플리케이션이 통신할 수 있도록 설계된 최초의 표준 프로토콜이기 때문에 복잡성과 오버헤드를 증가시키는 빌트인 룰을 적용하므로, 페이지 로드 시간이 길어질 수 있다.

 

그러나 이러한 표준은 빌트인 컴플라이언스를 제공한다는 의미이므로, 기업에서 선호하는 방식이기도 합니다. 빌트인 컴플라이언스 표준에는 보안과 안정적인 데이터베이스 트랜잭션의 기본 속성인 원자성, 일관성, 격리성, 내구성(Atomicity, Consistency, Isolation and Durability, ACID)이 포함된다.

 

OAP와 다양한 확장이 표면상으로 표준이 됐지만 다른 벤터의 구현 간 상호운용성은 종종 문제를 일으킨다. 이와 같은 이유로 여전히 많은 대기업에서 SOAP를 사용하지만 대부분의 작은 기업에서는 선호하지 않는다.

SOAP장단점

장점

1.웹 기반 서비스를 생성하는 HTTP프로토콜을 사용하므로 언어와 플랫폼에 독립적이다.

2.WS-Security, SSL 등의 기능을 지원하여 메세지 수준에서 암호화가 가능하고 개인 정보 보호 및 무결성을 제공한다,

3. SOAP은 엄격한 보안성을 가지고있기에 , 금융, 통신, 마케팅, 결제 시스템과 같은 기업용 서비스에 사용되는 장점이있다.

단점

1. XML 형식만 사용할 수 있다.

2. 캐시기능을 사용할 수 없다.

3. 많은 리소스와 대역폭을 필요로 하므로 오버헤드가 큰 편이다.

4 .SOAP API 서버 구축을 위해서는 매우 제한된 규칙에 대해 전문적이고 깊은 이해가 필요하다. & 사용을 위한 복잡한 표준이 정해져 있어, 러닝커브가 큰 편이고 지식의 전문성이 필요하다.

SOAP REST 차이점

차이점 SOAP REST

유형 프로토콜 아키텍처 스타일
기능 기능 위주 : 구조화된 정보 전송 데이터 위주: 데이터를 위해서 리소스에 접근
데이터 포맷 XML만 사용 일반 텍스트, HTML, XML, JSON등 다양한 포맷 허용
보안 WS-Security와 SSL 지원 SSL와 HTTPS 지원
대역폭 상대적으로 더 많은 리소스와 대역폭이 필요 상대적으로 리소스가 적게 필요하고 무게가 가벼움
데이터 캐시 캐시를 사용할 수 없음 캐시 사용 가능
페이로드 처리 엄격한 통신 규약을 갖고 있으며 모든 메세지는 보내기 전에 알려져야함 미리 알릴 필요 없음
ACID 준수 자체적인 ACID기준이 있어서 데이터 손상을 줄여줌 ACID 준수와 관련된 내용이 없음
    웹 프레임 워크
  • Fast API , Phoenix, Gin,Play,Fastify, Express JS, 장고 REST, Flask, Ruby on Rails, Spring Boot |

SOAP는 위의 규약과 WSDL등의 규칙이 존재하기 때문에 데이터 요청을 주고 받을 떄도 SOAP Standard (SOAP Envelope, SOAP Head, SOAP Body 등)를 지켜서 보내야 한다.

 

참고 |

  1. SOAP REST 차이, 두 방식의 가장 큰 차이점은?
  2. RESTful과 SOAP 비교  Roots of the REST/SOAP Debate
  3. 현재까지 SOAP를 쓰는 이유  Why are most of the flight booking providers still using WSDL and SOAP? Why would anyone use them? - Quora

원격 프로시저 호출(RPC) 문제 The problems with remote procedure calls(RPCs)

웹 서비스는 network 상에서 API를 호출하는 여러 기술중 가장 최신의 형상일 뿐이다. 이러한 웹 서비스는 원격 프로시저 호출(Remote procedure call, RPC)의 아이디어를 기반으로 한다.

 

RPC란?

원격 프로시저 호출(영어: remote procedure call, 리모트 프로시저 콜, RPC)은 별도의 원격 제어를 위한 코딩 없이 다른 주소 공간에서 함수나 프로시저를 실행할 수 있게하는 프로세스 간 통신 기술이다. 다시 말해, 원격 프로시저 호출을 이용하면 프로그래머는 함수가 실행 프로그램의 위치가 로컬 위치에 있든 원격 위치에 있든 동일한 코드를 이용할 수 있다.

💡 함수 vs 프로시저

  • 함수(Function) : Input에 따른 Output의 발생을 목적으로 한다. 따라서 Return값을 필수로 가져야 하며, Client단에서 처리되기 때문에 주로 간단한 계산 및 수치 등을 도출할 때 사용한다.
  • 프로시저(Procedure) : Output값 자체에 집중하기보단, '명령 단위가 수행하는 절차'에 집중한 개념이라고 보면 된다.

따라서 Return 값이 있을수도 있고, 없을수도 있으며, Server단에서 처리되기 때문에 함수보다 큰 단위의 실행, 프로세싱 등을 할 때 사용한다.

 

객체 지향의 원칙을 사용하는 소프트웨어의 경우 원격 프로시저 호출을 원격 호출(remote invocation) 또는 원격 메소드 호출(remote method invocation)이라고 일컫는다.


RPC모델은 원격 네트워크 서비스 요청을 같은 프로세스 안에서 특정 프로그래밍 언어의 함수나 메서드를 호출하는 것과 동일하게 사용 가능하게 해준다. 이런 추상화를 위치 투명성(location transparency)라 한다.

RPC 사용 예시
Command API에 사용된다. RPC는 원격 시스템에 명령을 보내기에 매우 적절한 선택이다. 예를 들어, 채널 가입, 채널 탈퇴, 메시지 전송 기능을 제공하는 Slack API는 매우 명령 중심적이다. 그래서 Slack API 설계자들은 작고 단단하며 사용하기 쉬운 RPC 스타일로 Slack API를 모델링했다.

RPC의 장단점

장점

  1. 고유 프로세스 개발에 집중 가능 (하부 네트워크 프로토콜에 신경쓰지 않아도 되기 때문)
  2. 프로세스간 통신 기능을 비교적 쉽게 구현하고 정교한 제어 가능
  3. 다양한 언어를 가진 환경에서 쉽게 확장 가능

단점

  1. 호출 실행과 반환 시간이 보장되지 않음 (네트워크 구간을 통하여 RPC 통신을 하는 경우,  치명적 문제 발생)
  2. 네트워크가 끊겼을 때 보안이 보장되지 않음
  3. 로컬 함수의 경우 예외를 내거나, 값을 반환하지 않을 수 있다.
    1. 네트워크는 timeout으로 결과가 없는것 처럼 만들 수 있지만, 무슨일이 일어났는지 알 방법이 없다.
    2. 정말 요청을 제대로 보낸건지 아닌지를 구분하기도 어려워진다
  4. 실패한 네트워크 요청이 처리를 실행되지만, 응답만 유실된 경우일 수도 있다.
    1. 이때 멱등성 idempotence을 적용하지 않는다면 재시도는 여러 작업이 중복 실행될 수 있다.
  5. 로컬 함수 호출에 비해 훨씬 시간이 많이 소요되고 그 소요시간을 예측할 수 없다.
  6. 로컬함수는 pointer를 효율적으로 전달할 수 있다. 네트워크 요청은 부호화 하여 매개변수로 보내야 한다.
    1. 만약 큰 객체를 보내야 하는 상황이라면?
  7. client와 서비스는 다른 언어로 구현할 수 있다. 따라서 RPC 프레임워크는 하나의 언어에서 다른 언어로 데이터 타입을 변환해야 한다.

이러한 한계와 문제가 있더라도 네트워크 통신을 로컬 함수처럼 사용하려는 수고는 비효율적인 것이 아니다.

RPC REST 차이점

RPC는 내부 마이크로 서비스 제공을 위한 고객별 API 구현에 사용된다. RPC는 단일 공급자와 단일 소비자를 직접 연결하기 위해, REST API 처럼 유선으로 수 많은 메타데이터 주고 받는데 많은 시간을 할애하기를 원하지 않는다.

RPC REST

  RPC RESGT
정의 원격 클라이언트에서 서버의 프로시저를 마치 로컬인 것처럼 직접적으로 호출할 수 있게 하는 시스템 클라이언트와 서버 간의 정형 데이터 교환을 정의하는 일련의 규칙
용도 원격 서버에서 작업을 수행 원격 객체에 대한 생성, 읽기, 업데이트 및 삭제(CRUD) 작업을 수행
활용사례 복잡한 계산이 필요하거나 서버에서 원격 프로세스를 트리거하는 경우 서버 데이터 및 데이터 구조를 균일하게 노출해야 하는 경우
상태유지여부 상태 유지 또는 무상태 무상태
데이터 전달 형식 서버에서 정의하고 클라이언트에 적용되는 일관된 구조 서버에 의해 독립적으로 결정되는 구조. 동일한 API 내에서 여러 형식을 전달할 수 있다.

GraphQL

그래프QL은 API를 위한 쿼리언어로 2012년 페이스북에서 개발하여 REST API를 대체할 수 있습니다.

기존의 REST API의 경우, 정의된 요청에 대한 응답만 가능했다면,

GQL은 필요한 컬럼에 대해서만 선택적으로 요청할수 있어 불필요한 데이터를 받지 않고, 필요한 데이터만 받을 수 있습니다.

GraphQL은 REST와는 다르게 Resource에 대한 엔드포인트가 따로 존재하지 않고, 하나의 엔드포인트만 존재합니다.

또한 해당 엔드포인트로 요청 시, 원하는 리소스와 해당 리소스에서 원하는 필드를 특정하는 GraphQL query를 함께 보냅니다.

/graphql?query={ book(id: "1") { title, author { firstName } } }

{
  "title": "Black Hole Blues",
  "author": {
    "firstName": "Janna",
  }
}

위의 gql을 통해, JSON형식으로 id = 1 인 book의 title과 author의 firstName 만 가져올 수 있습니다.

GraphQL은 여러 Depth의 조인이 필요한 복잡한 요청 데이터도 할 수 있어서, 효율적으로 데이터를 주고 받을 수 있습니다.

비동기적 메시지 전달을 통한 데이터 플로

  • 메시지를 직접 네트워크 연결로 전송하지 않고 임시로 메시지를 저장하는 메시지 브로커라는 중간 단계를 거쳐 전송한다는 점에서 데이터베이스와 유사하다.
  • 클라이언트의 요청(메시지)을 낮은 지연 시간으로 다른 프로세스에 전달한다는 점에서는 RPC와 비슷하지만 메시지 브로커(Message broker, Message Queue) 방식은 RPC 방식보다 여러 장점이 있다.
    • 수신자가 사용 불가 상태에 빠져도 메시지 브로커가 버퍼로 동작할 수 있기 때문에 시스템 안정성이 향상된다.
    • 메시지 브로커가 메시지를 저장하므로 메시지 유실을 방지할 수 있다.
    • 송신자와 수신자가 서로 알 필요가 없으므로 서로간에 의존성이 없다.
      • 논리적으로 송신자와 수신자는 분리됨 → 송신자는 메시지를 게시(publish)할 뿐이고 누가 소비(consume)하는지 상관하지 않음
    • 하나의 메시지를 여러 수신자로 전송할 수 있다.
  • RPC 차이점 : 메시지 전달 방식은 송신 프로세스가 응답을 기대하지 않으므로 단방향이다.
    • 프로세스가 응답을 전송하는 것은 가능하지만 이것은 보통 별도의 채널에서 수행된다. 이러한 통신 패턴을 비동기라 한다.

분산 액터 프레임워크

  • 액터 모델(actor model)은 단일 프로세스 안에서 동시성을 위한 프로그래밍 모델
    • 예시) Akka, Orleans, erlang
    액터 모델은 “모든 것은 액터다(Everything is an actor)”라는 기본 철학을 가지고 갑니다. OOP에서의 “모든 것은 객체다(Everything is an object)”라는 철학과 비슷하지만, 객체 지향 소프트웨어는 기본적으로 순차적 실행을 하지만 액터 모델은 본질적으로 동시성을 제공하는 점이 다릅니다.

    그러면 액터(Actor)란 무엇이냐 라는 질문이 나올 수 있습니다. 액터는 비동기적으로 메세지를 처리할 수 있는 computational entity로 다음과 같은 특징이 있습니다:
    • 다른 액터에게 유한한 개수의 메세지를 보낼 수 있습니다.
    • 유한한 개수의 새로운 액터를 만들 수 있습니다.
    • 다른 액터가 받을 메세지에 수반될 행동(behavior)을 지정할 수 있습니다.
    실제 사람과의 커뮤니케이션을 상상하면 좀 더 이해하기 편합니다. 사람들은 초능력이 존재하지 않기에 타인과 머릿속 생각을 직접 공유하지 못하고, 대화(message)를 통해 대화를 주고받습니다.

    액터 또한 똑같습니다. 메세지를 주고받아 다른 액터와 상호작용을 합니다. 액터가 차지하는 메모리 공간은 독립적이며, 다른 스레드나 액터가 접근할 수 없습니다. 다시 말하면, 메모리 공유 없이 메세지 전달만을 사용하기에 공유 메모리로 인한 교착 상태 등의 골치 아픈 상황들을 피할 수 있습니다.

참고 | Akka 공부하기 - 00.액터 모델이란?