이론 정리

데이터 중심 애플리케이션 설계 4장 정리

철매존 2024. 11. 20. 17:15
728x90
반응형
  • 4장

    애플리케이션은 시간이 지남에 따라 변한다.
    대부분의 경우 애플리케이션 기능을 변경하려면 저장하는 데이터도 변경해야 한다.
    근데 보통 바로 적용이 안된다(서버/클라쪽에서 바로 적용이 힘들기 때문)
    이를 위해 하위호환/상위호환성을 유지해야 한다.
    여기서는 JSON, XML, 프로토콜 버퍼 등등.. 데이터 부호화를 위한 다양한 형식을 살펴본다.

    데이터 부호화 형식

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

      • 메모리에 객체, 구조체, 리스트, 해시 등등...으로 데이터가 유지되고, 이런 데이터 구조는 CPU에서 효율적으로 접근하고 조작할 수 있게 최적화된다.
      • 데이터를 파일에 쓰거나 네트워크를 통해 전송하려면 스스로를 포함한 일련의 바이트열(예를 들어 JSON)형태로 부호화해야한다. 이런 바이트열은 보통 메모리에서 사용하는 데이터 구조화는 다름
    • 요 두 표현 사이의 변환이 필요한데, (인메모리표현 -> 바이트열 : 부호화/직렬화/마샬링) (고 반대 -> 복호화/파싱/역직렬화/언마샬링) 이라고 한다.

      • 참고로 여기 직렬화는 트랜잭션 그거랑은 다름 - 헷갈릴까봐 여기서는 이제 부호화라고 한다.

      언어별 형식

    • 언어마다 자체적으로 인메모리 객체를 바이트열로 부호화하는 기능을 내장한다. (java의 serializable등등)

      • 근데 이거는 문제가 있는데
        • 보통 특정 프로그래밍 언어랑 묶여있어서 다른 언어에서 데이터 읽기가 힘들다. 즉 이렇게 저장하면 현재 프로그래밍 언어로만 개발해야하고 다른 시스템과 통합에 문제가 있다.
        • 동일한 객체 유형 복원에는 복호화 과정이 임의의 클래스를 인스턴스화할 수 있어야한다. => 이게 근데 왜 문제가 되는지는 모르겠다..
          • 내가 생각할 때에는 사용자가 임의로 값을 삽입해서 보낼 수 있어서일것 같은데 맞을까? 확인필요
        • 버전 관리의 문제
        • 효율성도 문제

      JSON, XML, 이진 변형

    • 수 부호화

      • XML, CSV는 수와 숫자로 구성된 문자열을 구분할 수 없다.
      • JSON은 문자열과 수를 구분하지만 정수와 부동소수점 수를 구별하지 않고 정밀도를 지정하지 않는다.
    • 유니코드 문자열 지원(사람이 읽을 수 있는)

      • JSON, XML은 잘 지원한다.
        • 근데 이진 문자열(문자 부호화가 없는 바이트열) 을 잘 지원하지 않는다.
          • 이게 유용해서 Base64를 쓰기는 한데 정공법이랑 다르고, 데이터 크기 증가
            • 이거 생각해보니 API보낼때 논의되었던 내용중 하나다. 확실히 용도가 좋음
              • But 좀 애매한게 있긴했다.
    • 스키마 지원

      • JSON, XML 모두 스키마 지원
      • CSV는 스키마가 없어서 각 로우와 컬럼 의미 정의는 애플리케이션에서 해줘야 한다.

      이진 부호화

    • JSON이랑 XML은 둘다 공간을 많이 차지함.

      • 그나마 JSON이 XML보다는 덜 장황하지만 그래도 이진 형식보다 훨씬 많은 공간 차지
    • 이진 부호화는 스키마 지정을 안하기 때문에 데이터 안에 모든 객체의 필드 이름을 포함해야 한다.

      스리프트와 프로토콜 버퍼

    • 아파치 스리프트와 프로토콜 버퍼는 같은 원리를 기반으로 한 이진 부호화 라이브러리.

    • 스키마 기술이 필요하다. (IDL : interface definition language)

    • 바이너리프로토콜

      • 타입 주석(이게 문자열인지 정수인지 등등)
      • 필드 태그(필드 이름을 JSON처럼 쓰지 않고 이렇게 하면 나중에 확인가능)
      • 부호화된 데이터
    • 스리프트 컴팩트프로토콜

      • 바이너리프로토콜하고 비슷한데, 필드 타입이랑 태그 숫자를 단일 바이트로 줄이고 가변 길이 정수를 사용해서 부호화

      필드 태그와 스키마 발전

      스키마는 필연적으로 시간이 지남에 따라 변한다. 이를 스키마 발전이라고 부름.
      그래서 이 하위/상위 호환성을 어떻게 유지할까?

    • 부호화된 레코드는 사실상 부호화된 필드의 연결일 뿐이다.

      • 필드 태그를 통해 새로운걸 추가하는건 걍 새로운 번호를 쓰면 거기서 보니까 괜찮다.
        • 상위 호환 : 이러면 예전 코드에서 새로운 코드로 기록한 데이터는 읽지 못하니까 그냥 무시한다.
        • 하위 호환 : 고유 태그 번호가 있다면 그냥 같은 의미이기 떄문에 읽기는 쉽게 가능하다.
          • 다만 새로운 필드 추가의 경우 required추가는 안된다.
            • 하위 호환성 유지를 위해서는 배포 후 추가되는 필드를 optional 혹은 기본값을 주어야 한다.
        • 필드 삭제 : 이거는 optional필드만 삭제가능하고 같은 태그 번호는 절대로 다시 사용 불가능하다.

      데이터타입과 스키마 발전

    • 필드의 데이터타입 변경

      • 값이 정확하지 않거나 짤릴수도 있다.
    • repeated

      • 이거는 required, optional처럼 필드 표시 태그이다.
      • 이전 데이터를 보는 새로운 코드 : 필드 존재 여부에 따라 0이나 1개의 엘리먼트가 있는 목록으로 봄
      • 새로운 데이터를 읽는 예전 코드 : 목록의 마지막 엘리먼트만 봄

      아브로

      프로토콜 버퍼, 스리프트같은 부호화 형식 중 하나이다.
      하둡의 하위 프로젝트로 시작됨(스리프트가 하둡의 사용 사례에는 적합하지 않아서)

    • 부호화할 데이터 구조를 지정하기 위해 스키마 사용

    • 얘는 두개의 스키마 언어가 있는데

      • 사람이 편집할 수 있는 아브로IDL
      • 기계가 더 쉽게 읽을 수 있는 JSON기반 언어
    • 이거는 스키마에 태그 번호가 없다.

      • 그래서 이진 부호화 길이가 32바이트로 모든 부호화 중 길이가 가장 짧다.
    • 부호화된 바이트열을 보면 필드나 데이터타입 식별을 위한 정보가 없다.

      • 즉 단순히 연결된 값으로 구성된다.
    • 아브로를 이용해 이진 데이터 파싱하려면 스키마에 나타난 순서대로 필드를 살펴보고 스키마를 이용해 각 필드의 데이터타입을 미리 파악해야한다.

      • 그니까.. 읽기/쓰기가 정확히 같은 스키마를 사용해야 한다는 것이다.

      쓰기/읽기 스키마

      위의 내용에서 스키마가 정확히 같아야 한다고 했는데, 스키마 발전을 하려면 어떻게 해야할까?

    • 애플리케이션이 파일, DB에 쓰기 위해 혹은 네트워크 전송을 위해 데이터를 아브로 부호화하려면 알고 있는 스키마 버전을 사용해 데이터를 부호화한다. -> 쓰기 스키마

    • 반대로 수신한 데이터를 복호화하길 원한다면 특정 스키마로 복호화되기를 기대한다. -> 읽기 스키마

    • 그래서 이거의 핵심 아이디어는 둘이 꼭 동일하지 않아도 호환 가능하기만 하면 된다는것.

      • 그래서 이거는 데이터 복호화(읽기)때에 쓰기 스키마랑 읽기 스키마 모두를 살펴본 다음 쓰기 스키마에서 읽기 스키마로 변환해 그 차이를 해소한다.

      스키마 발전 규칙

    • 아브로에서

      • 상위 호환성
        • 새로운 버전의 쓰기 스키마와 예전 버전의 읽기 스키마를 가질 수 있음.
      • 하위 호환성
        • 새로운 버전의 읽기 스키마와 예전 버전의 쓰기 스키마를 가질 수 있음.
    • 호환성 유지를 위해서는 기본값이 있는 필드만 추가하거나 삭제할 수 있다.

      • 예를들어 예를들어 기본값만 있는 필드가 새로운 스키마에는 있고 예전 스키마에는 없는 경우
        • 예전 스키마로 기록된걸 새로운 스키마로 읽으면 누락된 필드가 기본값으로 채워짐.
    • 아브로는 널을 허용하려면 유니온타입(Union type)을 사용해야 한다.

    • 얘는 그래서 optional, required표시자가 따로 없다.

      쓰기 스키마는 그래서 뭐지

    • 그래서 읽기는 특정 데이터를 부호화한 쓰기 스키마를 어떻게 알 수 있을까?

    • 상황마다 다름

      • 많은 레코드가 있는 대용량 파일
        • 파일에 시작 부분에 한번 소개함
      • 개별적으로 기록된 레코드를 가진 데이터베이스
        • 모든 부호화된 레코드의 시작 부분에 버전 번호를 포함하고, DB에서는 스키마 버전 목록 유지
        • 읽기의 경우 레코드에서 버전 번호를 추출한 다음 DB에서 해당하는 쓰기 스키마를 가져옴.
      • 네트워크 연결을 통해 레코드 보내기
        • 두 프로세스가 양방향 네트워크 연결로 통신할 떄 연결 설정에서 스키마 버전 합의 가능

      동적 생성 스키마

    • 아브로는 위에서 봤듯이 태그 번호가 포함되어 있지 않다.

    • 이게 동적 생성에 친숙하다는 장점이 있다.

      • 관계형 스키마로부터 아브로 스키마를 쉽게 생성 가능하다. (JSON)
        • 그리고 이 스키마를 통해 DB내용 부호화하고 아브로 객체 컨테이너 파일로 덤프 가능하다.
        • DB테이블에 맞게 레코드 스키마 생성하고 각 컬럼은 해당 레코드의 필드가 된다.
          • 그래서 DB스키마가 변경되면 갱신된 DB스키마로부터 새로운 아브로 스키마를 생성하고 새로운 아브로 스키마로 데이터를 내보낸다.

      코드 생성과 동적 타입 언어

    • 스리프트와 프로토콜 버퍼는 코드 생성에 의존함.

      • 스키마 정의 후 선택한 프로그래밍 언어로 구현 코드 생성가능
      • 복호화된 데이터를 위해 효율적인 인메모리 구조를 사용하고 데이터 구조에 접근하는 프로그램을 작성할 떄 IDE에서 자동완성 가능.
    • js, ruby, python같은 동적 타입 프로그래밍 언어에서는 만족시킬 컴파일 시점의 타입검사기가 없어서 코드 생성이 중요하지 않음.

      • 아브로는 정적 타입을 위해 코드 기술을 제공하기는 하는데, 코드 생성 없이도 사용가능하다.

      스키마의 장점

    • 프로토콜 버퍼, 스리프트, 아브로 모두 스키마를 사용해 이진 부호화 형식을 기술함.

    • XML, JSON보다 훨씬 간단하고 더 자세한 유효성 검사 규칙을 지원함.

    • JSON, XML, CSV같은 텍스트 데이터 타입이 널리 사용되지만 스키마를 기반으로 한 이진 부호화 역시 가능.

      • 필드 이름을 생략 가능해서 이진 JSON변형보다 크기가 작을 수 있다.
      • 스키마는 유용한 문서화 형식인데 복호화를 할 때 스키마가 필요하기 때문에 스키마가 최신 상태인지 확인 가능.
      • 이걸 유지하면 스키마 변경이 적용되기 전에 상위 호환성과 하위 호환성 확인 가능.
      • 정적 타입 프로그래밍 언어 사용자에게 스키마로부터 코드를 생성하는 기능은 유용하다. 컴파일 시점에 타입 체크가 가능해서이다.

      데이터플로 모드

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

    • 누가 데이터를 부호화하고, 누가 그걸 복호화할지?

      • 데이터베이스를 통해
        • 일단 여기서 하위 호환성은 반드시 필요하다. 이전에 기록한 내용을 미래의 자신이 복호화해야 하기 때문이다.
        • 그리고 상위 호환성도 대개 필요한데, 그 이유는 여러 프로세스가 DB에 접근할 것이고 새로운 코드가 기록되어도 예전 코드를 읽을 수도 있기 때문이다.
        • 참고로 중요한것은 예전 코드가 해석이 안돼도 새로운 필드를 유지해야한다는것.
          • 복호화 이후 재부호화 과정에서 손실되면 안되므로.
          • DB는 주로 애플리케이션같은것에 비해 오래 데이터가 유지된다.
            • 바뀌는일은 적지만 마이그레이션은 해야할 수도 있다.
            • 보통은 null을 기본값으로 갖는 새로운 컬럼 추가 스키마 변경이 허용됨.
              • 예전 로우를 읽는 경우 디스크 상의 부호화된 데이터에서 누락된 임의 컬럼은 널값으로 채움.
          • 보관 저장소
            • DB스냅숏 관련
            • 보통 데이터 덤프는 최신 스키마를 사용해 부호화한다.
              • 다양한 시점의 스키마 버전이 섞여 포함되었다고 해도 복사본을 일관되게 부호화하게.
      • 서비스를 통해 : REST, RPC
        • 서버-클라이언트
        • REST, SOAP등이 있고 얘들은 JSON, RPC는 부호화를 쓴다.
        • RPC모델은 원격 네트워크 서비스 요청을 같은 프로세스 안에서 특정 프로그래밍 언어의 함수나 메서드를 호출하는 것과 동일하게 사용 가능하게 해준다.(위치 투명성)
          • RPC는 참고로 결함이 있는데 타임아웃이나 멱등성 등등...
        • RPC 데이터 부호화 발전에 대해
          • 일단 모든 서버를 먼저 갱신하고 나서 모든 클라이언트를 갱신해도 문제가 없다고 가정한다. -> 즉 요청은 하위 호환성만 필요하고 응답은 상위 호환성만 필요
        • 참고로 이게 다른 조직의 것들을 사용할 수 있기 때문에 호환성은 오랜 시간동안 유지돼야 한다.
        • 호환성을 깨는 변경이 필요하면 서비스 제공자는 여러 버전의 서비스 API를 함께 유지한다.
      • 메시지 전달을 통해
        • 위의 두가지를 정리하면
          • DB : 일단 부호화된걸 저장하고 나중에 읽을때 복호화
          • 서비스 : 하나의 프로세스가 네트워크를 통해 다른 프로세스로 요청을 전송하고 가능한 빠른 응답을 기대
        • 이거는 RPC와 DB간 비동기 메시지 전달 시스템
          • 먼저 클라이언트 요청을 빠르게 다른 프로세스로 전달(요거는 RPC랑 비슷함)
          • 그 요청(메시지)를 직접 네트워크 연결로 전송하지 않고 메시지 브로커나 메시지 지향 미들웨어를 통해 전달함(이런거는 DB랑 비슷)
        • 참고로 이거는 단방향이다.
          • 즉 비동기로, 송신 프로세스는 그냥 메시지 전달 후 잊어버림.
        • 여기의 메세지 브로커(rabbitMQ, kafka등)은 특정 데이터 모델을 강요하지 않고 모든 부호화 형식 사용가능.
          • 부호화가 상하위 호환성을 모두 가지면 브로커에서 게시자와 소비자를 독립적으로 변경해서 임의 순서로 배포 가능.
          • 액터 모델은 단일 프로세스 안에서 동시성을 위한 프로그래밍 모델이다.
            • 스레드를 직접 처리하는 대신 로직이 액터에 캡슐화된다.
              • 액터는 보통 하나의 클라이언트나 엔티티를 나타냄.

      정리

    • 많은 서비스가 새로운 버전의 서비스를 동시에 모든 노드에 배포하는 방식보다 한 번에 일부 노드에만 서서히 배포하는 순회식 업그레이드가 필요하다.

      • 애플리케이션 변경을 쉽게 할 수 있는 발전성에 도움이 많이 된다.
    • 시스템을 흐르는 모든 데이터는 하위호환성(새로운 코드가 예전 데이터를 읽을 수 있음)과 상위호환성(예전 코드가 새로운 데이터를 읽을 수 있음)을 제공하도록 부호화해야 한다.

      • 언어별 부호화는 단일 프로그래밍 언어로 제한되며 상위호환/하위호환을 제공하지 못하는 경우가 있음.
      • JSON, XML, CSV같은거는 널리 사용됨. 데이터타입에 다소 모호한 점이 있기 때문에 숫자나 이진 문자열과 같은 항목은 주의해야한다.
      • 스리프트, 프로토콜 버퍼, 아브로같은 이진 스키마 기반 형식은 짧은 길이로 부호화된다.
        • 정적 타입 언어에서 문서와 코드 생성에 유용하지만 사람이 읽기 위해서는 복호화해야 하는 단점이 있음.
    • 그럼 어떻게 부호화할지에 대한 데이터플로우는?

      • DB에 기록하는 프로세스가 부호화하고 DB읽는 프로세스가 복호화하기
      • 클라이언트가 요청을 부호화하고 서버는 요청복호화/응답부호화, 다시 클라이언트는 응답 복호화하는 RPC, REST API
      • 송신자가 부호화하고 수신자가 복호화하는 메시지를 서로 전송하는 비동기메시지 전달
반응형