정규화란?

데이터베이스에서의 정규화는 중복 데이터를 최소화하고 무결성을 최대화하기 위한 설계 기법입니다.
정규화를 통해 효과적으로 설계된 데이터베이스는 업데이트 시 발생할 수 있는 문제를 줄이며, 데이터를 효율적으로 저장하게 됩니다.

무결성이란?

무결성(integrity)은 데이터베이스 관리 시스템(DBMS)에서 데이터의 정확성, 일관성 및 신뢰성을 유지하기 위한 속성을 의미합니다.
데이터베이스의 무결성을 유지하는 것은 중요한 설계 원칙 중 하나로, 데이터의 품질을 보장하며 애플리케이션의 신뢰성을 향상시킵니다.

  1. 도메인 무결성(Domain Integrity)
  • 각 필드에 저장될 수 있는 값의 유형, 형식, 범위 및 가능한 값을 정의하며, 이에 따라 데이터가 저장되는지 확인합니다.
  • 예를 들어, 나이 필드에 문자열이 들어가지 않도록하거나 날짜 형식이 'YYYY-MM-DD' 형식을 따르도록 하는 것입니다.
  1. 개체 무결성(Entity Integrity)
  • 각 테이블의 기본 키(primary key)는 고유해야 하며, NULL 값을 허용하지 않아야 합니다.
  • 이를 통해 테이블의 각 레코드를 고유하게 식별할 수 있습니다.
  1. 참조 무결성(Referential Integrity)
  • 외래 키(foreign key)는 다른 테이블의 기본 키를 참조합니다.
  • 참조 무결성은 외래 키 값이 참조하는 테이블의 기본 키 값 중 하나와 일치하거나 NULL 이어야 함을 보장합니다.
  • 즉, 잘못된 참조나 존재하지 않는 데이터를 방지하는 데 중요합니다.
  1. 사용자 정의 무결성(User-defined Integrity)
  • 데이터베이스 설계자나 애플리케이션 개발자가 특정 비즈니스 규칙에 따라 정의한 무결성 규칙입니다.
  • 예를 들어, "고객의 할인율은 0%에서 50% 사이어야 한다"와 같은 규칙이 될 수 있습니다"

정규화 단계

1. 제1정규형 (1NF)

  • 모든 속성은 원자값(더 이상 분해할 수 없는 값)을 가져야 합니다.
  • 각 속성마다 고유한 값을 가져야 합니다.

1) 복합 속성(Composite Attributes)

  • 속성이 여러 하위 속성으로 구성될 수 있는 경우가 있습니다.
  • 예를 들어, '주소'라는 속성이 '도시', '주', '우편번호'와 같은 여러 하위 속성으로 나뉘어진다면, '주소'는 원자값이 아닙니다.
  • 제 1정규형을 충족하기 위해 이러한 복합 속성을 각각의 독립적인 속성으로 분해해야 합니다.

2) 다중값 속성(Multi-valued Attributes)

  • 하나의 속성이 여러 개의 값을 가질 수 있는 경우, 이 속성은 원자값이 아닙니다.
  • 예를 들어, '전화번호'라는 속성이 사용자의 집, 사무실, 핸드폰 등 여러 번호를 가질 수 있다면, 이는 원자값이 아닌 다중값 속성입니다.
  • 이러한 다중값 속성은 제 1정규형을 만족시키기 위해 별도의 테이블로 분리하거나 다른 방법으로 처리해야 합니다.

3) 중복 속성

  • 하나의 엔터티 내에서 같은 정보를 중복해서 저장하는 것은 피해야 합니다.
  • 예를 들어, '제품명'과 '제품 설명'이라는 두 개의 다른 속성에 같은 내용이 저장된다면, 이는 중복성을 초래하므로 제 1정규형을 위반하는 것입니다.

2. 제2정규형 (2NF)

  • 제 1정규형을 만족해야 합니다.
  • 기본 키가 아닌 모든 속성이 기본 키 전체에 함수적 종속되어야 합니다.
  • 즉, 부분적 종속이 존재하지 않아야 합니다.

1) 함수적 종속(Functional Dependency)

  • 속성 A의 값이 속성 B의 값을 유일하게 결정할 때, B는 A에 함수적으로 종속된다고 합니다(기호로는 A -> B)
  • 예를 들면 "학번"이 "학생 이름"을 유일하게 결정하면 "학생 이름"은 "학번"에 함수적으로 종속되어 있습니다.

2) 부분적 종속(Partial Dependency)

  • 복합 기본 키(둘 이상의 속성으로 구성된 키)를 가진 테이블에서 발생하는 종속 형태입니다.
  • 만약 복합 기본 키의 일부만으로 다른 속성이 결정된다면, 그 속성은 부분적으로 종속되어 있다고 말합니다.

예를 들어 학생수강정보라는 테이블이 아래와 같이 있다고 하겠습니다.




이 테이블에서 기본 키는 '학번'과 '과목코드'의 조합입니다.

그런데 이 테이블을 살펴보면

'학생 이름'(StudentName)은 '학번'(StudentID)에만 종속되어 있습니다.
즉, '학번'만 알면 '학생이름'을 알 수 있습니다.

'과목명'(CourseName)은 '과목코드'(CourseCode)에만 종속되어 있습니다.

이러한 관계로 인해 김철수 학생이 두 과목을 수강하는 경우 '학생이름'이 두 번 중복되어 저장됩니다.

이런 부분적 종속을 해결하기 위해 제2정규형(2NF)을 적용하여 테이블을 분해합니다.

  • 학생테이블


  • 과목테이블


  • 학생수강정보테이블

3. 제3정규형 (3NF)

  • 제2정규형을 만족해야 합니다.
  • 기본 키가 아닌 모든 속성이 기본 키에만 함수적 종속되어야 합니다. 이는 전이적 종속이 존재하면 안 된다는 것을 의미합니다.

전이적 종속(Transitive Dependency)

  • 속성 A가 속성 B에 함수적으로 종속되고, 속성 B가 속성 C에 함수적으로 종속될 때, 속성 A도 속성 C에 함수적으로 종속되는 것을 의미합니다.
  • 기호로 표현하면 A -> B, B -> C 가 동시에 참일 때, A -> C 도 참인 경우입니다.

예시) 고객 주문 정보

이 테이블에서 '제품분류'(Category)는 '제품ID'(ProductID)에 함수적으로 종속되어 있습니다.
그러나 '제품ID'는 '주문ID'에 함수적으로 종속되어 있습니다.
따라서 '제품분류'는 '주문ID'에 전이적으로 종속된 상태입니다.

이를 해결하기 위해 제3정규형을 적용하여 테이블을 분리합니다.

  • 주문 테이블

  • 제품 테이블

이렇게 테이블을 분리하면 전이적 종속 문제가 해결되며, 각 테이블의 역할이 명확해집니다.
또한, 제품의 분류정보를 업데이트할 때는 제품 테이블만 변경하면 되므로 데이터 관리가 보다 효율적입니다.

4. BCNF (Boyce-Codd Normal Form)

  • 제3정규형을 만족해야 합니다.
  • 모든 결정자는 후보 키의 부분집합이어야 합니다.
결정자, 후보키, 기본키 설명

예시) 학생과 강의를 관리하는 테이블

1) 결정자(Determinant)

  • '강의코드'는 '강의명'을 결정합니다. 즉, '강의코드'가 주어지면 해당 강의의 이름을 알 수 있습니다.
  • 따라서 '강의코드'는 강의명'의 결정자입니다('강의코드' -> '강의명')

2) 후보키(Candidate Key)

  • '(학번, 강의코드)'의 조합은 테이블의 각 튜플을 유일하게 식별합니다.
  • 다른 조합으로는 튜플을 유일하게 식별할 수 없습니다.
  • 따라서 '(학번, 강의코드)'의 조합이 후보키입니다.

3) 기본키(Pirmarty Key)

  • 위에서 언급한 후보키 '(학번, 강의코드)' 중에서 우리가 실제 데이터베이스 설계시에 기본키로 선택한 키를 말합니다.
  • 여기서는 '(학번, 강의코드)'만이 유일하게 튜플을 식별할 수 있는 후보키이므로 해당 키만이 기본키로 선택할 수 있습니다.
  • 이 기본키에 의해 각 튜플은 고유하게 식별됩니다.
모든 결정자는 후보 키의 부분집합이어야 합니다.

예시) 대학생과 선택한 전공

이 테이블에서는 '전공명'이 '담당 교수'를 결정합니다.
즉 '전공명 -> 담당 교수' 라는 함수적 종속성이 존재합니다.


하지만, '전공명'은 이 테이블의 후보키가 아닙니다. 왜냐하면 '전공명'만으로는 각 튜플(레코드)을 구별할 수 없기 때문입니다.
'학번' 혹은 '학번 + 전공명' 조합이 후보키가 될 수 있습니다.


이 경우, '전공명' 이 결정자이지만 후보 키의 부분집합이 아닙니다.
따라서 이 테이블은 BCNF를 만족하지 않습니다.


BCNF를 만족시키기 위해 분해

1) 학생과 전공 테이블

2) 전공과 담당 교수 테이블

5. 제4정규형(4NF)

  • BCNF를 만족해야 합니다.
  • 다중값 종속성이 없어야 합니다.
다중값 종속성(MVC, Multi-Valued Dependency)
  • 릴레이션 R의 두 속성 A와 B에 대해, A의 어떤 값을 가지는 튜플들이 B의 값을 독립적으로 가질 수 있을 때 발생합니다.
  • 기호로는 A ->> B 라고 표현합니다.

예시) 학생들이 참여하는 동아리와 스포츠 활동

이 테이블에서 주요 포인트는 '학생이름' 속성에 대한 다른 두 속성 ('동아리'와 '스포츠') 사이의 독립적인 관계입니다.


데이터를 보면 '지민'이 '미술' 동아리와 '음악' 동아리에 동시에 속하고 있으며, 동시에 '농구'와 '축구' 스포츠 활동에 참여하고 있습니다.
이때, '지민'의 '미술' 동아리 참여는 '농구'나 '축구' 활동과 직접적인 연관이 없습니다.
즉, '동아리' 참여와 '스포츠' 활동은 독립적인 관계를 가지고 있습니다.

다중값 종속성은 이런 독립적인 관계 때문에 발생합니다.
'학생이름'에 대해 '동아리'와 '스포츠'의 관계가 독립적으로 존재하므로 '학생이름'이 '동아리'에 대한 다중값 종속성과 '스포츠'에 대한 다중값 종속성을 모두 갖게 됩니다.


다시 말하면, '지민'이 참여하는 '동아리'는 '지민'이 참여하는 '스포츠'에 의해 영향을 받지 않습니다.
이는 '학생이름 ->> 동아리'와 '학생이름 ->> 스포츠'의 두 개의 다중값 종속성을 나타냅니다.


이러한 다중값 종속성을 해결하기 위해 테이블을 분리 하면 다음과 같습니다.

1) 학생과 동아리 테이블

2) 학생과 스포츠 테이블

6. 제5정규형(5NF 또는 PJNF, Project-Join Normal Form)

  • 제4정규형을 만족해야 합니다.
  • 조인 종속을 고려하여 분해하였을 때 원래의 관계를 복원할 수 있어야 합니다.
조인 종속(Join Dependency)란?

조인 종속은 릴레이션 R을 두 개 이상의 릴레이션 R1, R2, ..., Rn 으로 분해한 후, 이들을 다시 조인했을 때 원래의 릴레이션 R을 복원할 수 있을 때 발생합니다.
기호로는 R = R1 ⨝ R2 ⨝ ... ⨝ Rn과 같이 나타낼 수 있습니다.

예시) 학생이 참여하는 프로젝트와 해당 프로젝트에서 사용하는 언어를 나타내는 테이블

이 테이블을 다음과 같이 두 개의 릴레이션으로 분해합니다.

  • 릴레이션 R1

  • 릴레이션 R2

이제 R1과 R2를 조인하면 원래의 릴레이션을 복원할 수 있습니다.
이 경우, 원래의 릴레이션에 대한 조인 종속이 발생하며, 이 릴레이션은 5NF를 만족합니다.

제5정규형의 주요 목적은 릴레이션을 적절하게 분해하여 데이터 중복을 제거하면서도 조인 연산을 통해 원래의 릴레이션을 손실 없이 복원할 수 있도록 하는 것입니다.

반응형

Docker와 가상머신(VM)의 차이

 

출처:  https://iancoding.tistory.com/184

 

아래 비교 표에 대한 자세한 설명은 아래에 작성하였습니다.

 

Docker 가상머신(VM)
Docker는 컨테이너화 된 애플리케이션을 실행합니다. 컨테이너는 호스트 OS의 커널을 공유하며 각 컨테이너는 시스템의 나머지 부분으로부터 격리된 환경에서 실행됩니다. VM은 물리적 하드웨어 위에 하이퍼바이저를 사용하여 별도의 전체 운영체제를 실행합니다. 완전히 독립된 가상의 컴퓨터를 의미합니다.
호스트 OS 커널을 공유하기 때문에 추가적인 운영체제를 로드할 필요가 없어 더 적은 리소스를 사용합니다. 각 VM은 전체 운영체제와 그 운영체제를 실행하는 데 필요한 리소스를 포함하므로 일반적으로 더 많은 리소스를 사용합니다.
컨테이너는 빠르게 시작될 수 있습니다. 전체 OS를 부팅해야 하기 때문에 시간이 좀 더 걸립니다.
컨테이너는 호스트 OS의 커널을 공유하기 때문에 VM에 비해 낮은 수준의 격리를 제공합니다. 각 VM은 독립적인 OS를 가지기 때문에 높은 수준의 격리를 제공합니다.
OS를 포함하지 않기 때문에 VM에 비해 이미지 크기가 일반적으로 작습니다. 전체 OS를 포함하기 때문에 디스크 이미지 크기는 대체로 큽니다.
Docker CLI, Docker Compose 등 다양한 도구와 플랫폼을 제공하여 컨테이너화된 애플리케이션의 배포 및 관리를 쉽게 합니다. VMware, VirtualBox, KVM 등 다양한 하이퍼바이저 및 관리 도구가 있습니다.

 

 

표에 대한 자세한 설명(표가 바로 이해되셨다면 넘어가셔도 됩니다)

 

컨테이너는 호스트 OS의 커널을 공유하며 각 컨테이너는 시스템의 나머지 부분으로부터 격리된 환경에서 실행됩니다

컨테이너와 호스트 OS의 관계를 이해하려면 먼저 컨테이너 기술의 핵심 요소와 원칙을 파악해야 합니다.

 

  1. 호스트 OS의 커널 공유

커널은 운영체제의 핵심 부분으로 하드웨어와 소프트웨어 간의 통신을 관리합니다.
이는 프로세스 관리, 메모리 관리, 디바이스 드라이버의 인터페이스 역할 등 다양한 핵심 기능을 포함합니다.

가상 머신(VM)
  • VM은 하이퍼바이저 위에 동작하며, 각 VM은 자체 운영체제(커널 포함)를 가집니다.
  • VM 내부의 애플리케이션은 해당 VM의 커널을 통해 하드웨어 리소스에 액세스합니다. 이 커널은 호스트 시스템의 실제 커널과 독립적입니다.
컨테이너
  • 컨테이너는 호스트 OS 위에서 동작하며, 호스트의 커널을 직접 사용합니다.
  • 컨테이너 내부의 애플리케이션은 호스트 OS의 커널을 통해 하드웨어 리소스에 액세스합니다.

"컨테이너는 호스트 OS의 커널을 직접 사용한다"는 것은, 컨테이너가 호스트 시스템의 실제 커널을 공유하고 이를 바탕으로 동작한다는 것을 의미합니다.

 

  1. 컨테이너의 격리

컨테이너는 '네임스페이스'라는 커널 기능을 사용하여 시스템의 다른 부분으로부터 격리됩니다.
네임스페이스는 프로세스, 네트워크, 사용자, 파일 시스템 등 다양한 리소스에 대한 격리를 제공합니다.

예를 들어, 네트워크 네임스페이스를 사용하면 각 컨테이너는 자체 IP 주소, 포트 번호, 라우트 테이블 등을 가질 수 있습니다.
PID(프로세스 ID) 네임스페이스를 사용하면 컨테이너 내부의 프로세스는 컨테이너 외부에서는 보이지 않습니다.

 

  1. 컨테이너의 이미지 및 파일 시스템

컨테이너는 '이미지'를 기반으로 실행됩니다.
이미지는 응용 프로그램, 라이브러리, 종속성 및 실행에 필요한 다른 모든 파일을 포함합니다.

'오버레이 파일 시스템'을 사용하여 이미지는 여러 계층으로 구성될 수 있습니다.
이를 통해 여러 컨테이너가 동일한 이미지 계층을 공유하면서 독립적인 변경을 수행할 수 있습니다.

VM은 물리적 하드웨어 위에 하이퍼바이저를 사용하여 별도의 전체 운영체제를 실행합니다. 이것은 완전히 독립된 가상의 컴퓨터를 의미합니다.

 

  1. 물리적 하드웨어
  • 컴퓨터의 실제 구성 요소를 의미합니다.
  • 예를 들어, CPU, RAM, 저장 장치(SSD, HDD), 네트워크 카드 등입니다.

 

  1. 하이퍼바이저
  • 하이퍼바이저는 가상화를 가능하게 하는 소프트웨어 또는 펌웨어 컴포넌트입니다.
  • 이를 통해 단일 물리적 하드웨어 위에서 여러 운영체제를 동시에 실행할 수 있습니다.

 

  1. 별도의 전체 운영 체제
  • VM은 자체적인, 독립된 운영체제를 실행합니다. 이 운영체제는 "게스트 운영체제"라고도 불립니다.
  • 게스트 운영체제는 물리적 하드웨어가 아닌, 하이퍼바이저에 의해 제공되는 가상의 하드웨어 위에서 동작합니다. 가상의 CPU, RAM, 디스크 등의 자원을 사용합니다.
  • 각 VM은 다른 VM과 완전히 독립적입니다. 따라서 한 VM에서 발생하는 문제가 다른 VM에 영향을 미치지 않습니다.

 

요약하면, VM 기술은 하이퍼바이저를 사용하여 물리적 하드웨어를 추상화하고, 이 추상화된 환경 위에서 여러 개의 독립적인 운영체제(게스트 OS)를 동시에 실행할 수 있게 해줍니다.
각 운영체제는 자신만의 가상의 자원과 환경을 가지며, 실제 물리적 자원을 하이퍼바이저를 통해 공유합니다.

 

컨테이너는 거의 즉시 시작될 수 있습니다.

  1. 가벼운 구조
  • 컨테이너는 전체 운영체제를 포함하지 않습니다. 대신 호스트 OS의 커널을 공유합니다. 따라서 컨테이너 이미지는 애플리케이션과 그 종속성만을 ㅗ함하게 되므로 사이즈가 작습니다.

 

  1. 직접적인 커널 액세스
  • 컨테이너는 호스트의 커널을 직접 사용하기 때문에 추가적인 추상화 계층이 없습니다. 이로 인해 컨테이너는 자원에 대한 접근이 빠르고 효율적입니다.

 

  1. 최소한의 부팅 프로세스
  • 전통적인 운영체제나 가상 머신은 시작할 때 많은 초기화 과정과 프로세스를 거쳐야 합니다.
  • 반면, 컨테이너는 애플리케이션 실행을 위한 최소한의 프로세스만 시작하므로 시작 시간이 크게 단축됩니다.

 

  1. 이미지 기반의 구조
  • 컨테이너는 미리 준비된 이미지를 바탕으로 실행됩니다.
  • 이 이미지에는 실행에 필요한 모든 파일과 설정이 포함되어 있기 때문에 추가적인 설정 또는 초기화 과정이 필요 없습니다.
반응형

'개발 > Docker' 카테고리의 다른 글

Docker File, Docker Compose 정의 및 사용 방법  (1) 2023.12.28

프로토콜 버퍼(Protocol Buffers, Protobuf)란?

  • Google에서 개발한 데이터 직렬화 도구입니다.
  • Json이나 XML 같은 다른 직렬화 형식에 비해 작고, 빠르며, 효율적입니다.
  • Protobuf는 주로 원격 프로시저 호출(RPC)에서 사용되며, 특히 gRPC에서 중요한 역할을 합니다.

출처: [https://narup.tistory.com/234](https://narup.tistory.com/234)

위 이미지는 첫번째 json 형태를 직렬화 Protobuf를 이용하여 직렬화 되는 과정을 나타내는 이미지 입니다.

프로토콜 버퍼(Protocol Buffers, Protobuf) 특징

1. 언어 중립적

  • 프로토콜 버퍼 정의는 특정 언어와 독립적입니다. 생성된 소스 코드는 다양한 프로그래밍 언어로 제공됩니다.(java, c++, python 등..)

2. 이진 포맷(데이터를 0, 1의 연속으로 표현하는 방식)

  • 데이터는 이진 포맷으로 직렬화되므로 전송 및 저장이 효율적입니다.

3. 확장 가능(필드 번호 이용)

  • 데이터 구조를 변경하더라도 이전 버전의 구조와 호환성을 유지할 수 있습니다.
  • 확장성과 호환성은 특히 분산 시스템, 마이크로서비스 아키텍처, 또는 단순히 시간이 지나면서 시스템의 요구 사항이 변경되는 경우에 중요한 특성입니다.
  • 프로토콜 버퍼(Protobuf)와 같은 이진 직렬화 포맷들은 확장성을 명확하게 지원하도록 설계되었습니다.
  • 이를 통해 개발자들은 시스템이 성장하고 변화해도 서로 다른 버전의 애플리케이션 간에 데이터를 안전하게 교환할 수 있습니다.
필드 번호란?
  • 프로토콜 버퍼에서는 각 필드에 고유한 번호가 할당됩니다.
  • 필드의 식별자로 사용되며, 각 번호는 직렬화와 역직렬화 과정에서 중요한 역할을 합니다.
  • 일단 번호가 할당되면 번호를 변경해서는 안됩니다.
필드 번호 역할 및 사용 방식

1) 식별자로서의 역할

필드 번호는 해당 필드의 고유 식별자로 동작합니다.
직렬화 시, 필드 이름이 아닌 필드 번호가 사용되어 데이터 스트림에 기록됩니다.
이렇게 함으로써, 데이터 전송 시의 크기가 줄어들게 됩니다.

 

2) 직렬화 및 역직렬화

데이터를 이진 형태로 직렬화 할 때, 필드 번호가 이진 스트림에 함께 기록됩니다.
반대로 데이터를 역직렬화 할 때, 이진 스트림에 기록된 필드 번호를 사용하여 해당 필드의 데이터를 올바르게 복원합니다.

 

3) 확장성 및 호환성

데이터 구조가 시간이 지나면서 변경되더라도 필드 번호는 일정하게 유지됩니다.
즉, 새로운 필드를 추가하거나 기존의 필드를 제거해도 해당 필드 번호는 바뀌지 않습니다.
이렇게 하면, 서로 다른 버전의 프로토콜 버퍼 구조 간에도 데이터의 호환성을 유지할 수 있습니다.

예를 들어, 초기 버전의 메시지 구조가 다음과 같다고 가정합니다.

 

message Person{
    string name = 1;
    int32 age = 2;
}

시간이 지나면서 'age' 필드가 더 이상 필요하지 않을 때, 그 필드를 제거할 수 있지만 필드 번호 2는 재사용해서는 안됩니다.

message Person{
    string name = 1;
    reserved 2;
}

또는 새로운 필드를 추가할 수도 있습니다.

message Person{
    string name = 1;
    int32 age = 2;
    string address = 3;
}

기존 'age'에 할당된 필드 번호 2는 변경되지 않았으므로, 오래된 버전과 새로운 버전모두 호환됩니다.

 

4) 필드 순서의 중요성

필드 번호는 필드의 순서나 위치와는 무관하게 동작합니다.
따라서 메시지 정의에서 필드의 순서가 바뀌더라도 필드 번호는 일정하게 유지되어야 합니다.

4. 정의된 스키마

'.proto' 파일에서 데이터 구조를 정의하게 됩니다.
이 파일은 데이터의 구조와 타입 정보를 포함하며, 이를 바탕으로 코드를 생성할 수 있습니다.

.proto 파일
  • 프로토콜 버퍼(Protobuf) 에서 데이터 구조를 정의하는 곳입니다.
  • 이 파일을 이용해서 특정 언어의 코드를 생성할 수 있습니다.
  • 데이터 교환의 규약 또는 계약처럼 동작하며, 다양한 시스템이나 언어 간의 통신에 있어서 일관된 데이터 구조를 보장합니다.

1) 메시지 정의

 

메시지는 여러 필드를 포함하는 구조체와 같습니다.
각 필드에는 데이터 타입, 필드 이름, 고유한 번호가 지정됩니다.

message Person{
    string name = 1;
    int32 age = 2;
}

 

2) 필드 타입

 

a. 정수 타입

  • int32, int64 : 순서대로 32비트, 64비트의 일반적인 정수 타입입니다.

b. 실수 타입

  • float, dobule : 순서대로 32비트, 64 비트의 부동소수점 타입입니다.

c. 문자열 및 바이트

  • string : UTF-8로 인코딩된 문자열 타입입니다.
  • bytes : 임의의 바이트 데이터를 포함하는 타입입니다.

d. bool(참, 거짓)

  • bool : 참 또는 거짓 값을 나타내는 타입입니다.

e. 열거형

  • enum : 명명된 값의 집합을 나타내는 타입입니다.

f. 필드 타입

  • 단일 필드
    기본적인 데이터 타입이나 복합 메시지 타입을 직접 사용하여 필드를 정의할 수 있습니다.
message Sample{
    int32 id = 1;
    string name = 2;
}
  • 반복 필드 (Repeated Fields)
    특정 필드 타입의 여러 값을 배열 또는 리스트 형태로 포함시킬 수 있습니다.
message Sample{
    repeated string tags = 1;
}
  • 옵셔널 필드
    Protobuf v2 에서는 optional 키워드를 사용하여 필드가 선택적으로 포함될 수 있음을 나타내야 했지만 Protobuf v3에서는 모든 필드는 기본적으로 선택적(옵셔널) 입니다.
  • 임베디드 메시지

다른 메시지 타입을 필드 타입으로 사용하여 중첩된 복합 구조를 만들 수 있습니다.

message Address{
    string city = 1;
    string state = 2;
}

message Person{
    string name = 1;
    Address address = 2;
}
  • Oneof

여러 필드 중 하나만 값을 가질 수 있을 때 사용하는 구조입니다.

message ContactInfo{
    oneof method{
        string phone_number = 1;
        string email = 2;
        string address = 3;
    }
}

위의 예에서 ContactInfo 메시지는 method라는 oneof 필드를 가지며, 이 필드는 phone_number, email, address 중 하나만 값을 가질 수 있습니다.

 

oneof를 사용하면 어떤 필드 중에서 하나만 값이 있어야 함을 명확하게 나타낼 수 있습니다.
이는 데이터의 무결성을 보장합니다.

 

oneof 내의 필드들은 공유 메모리를 사용하기 때문에 메모리 사용량이 절약됩니다.
즉, 한번에 하나의 필드만 값이 할당되므로, 여러 필드에 동시에 메모리를 할당할 필요가 없습니다.

 

만약 프로그램이 실수로 두 개 이상의 필드에 값을 할당하려고 하면, 마지막으로 할당된 필드의 값만 유지되며, 그 전의 필드 값들은 모두 삭제됩니다.

이를 통해 오류를 방지할 수 있습니다.

 

3) 서비스 정의

.proto 파일에서는 RPC(Remote Procedure Call) 서비스를 정의할 수도 있습니다.
이를 통해 클라이언트와 서버 간의 통신 규약을 명확하게 지정할 수 있습니다.

service UserService{
    rpc CreateUser (UserRequest) returns (UserResponse);
}

message UserRequest{
    string username = 1;
}

message UserResponse{
    int32 user_id = 1;
}

 

4) 코드 생성

.proto 파일을 정의한 후, 프로토콜 버퍼 컴파일러(protoc)를 사용하여 특정 언어로 코드를 생성할 수 있습니다.
예를 들어, Java 용 코드를 생성하려면

protoc --java_out=./ yourfile.proto

이렇게 생성된 코드는 프로토콜 버퍼 메시지의 직렬화, 역직렬화, 데이터 접근 등의 기능을 제공합니다.

 

5) 확장 및 임포트

.proto 파일 내에서 다른 .proto 파일을 임포트하여 정의된 메시지나 타입을 사용하거나 확장할 수 있습니다.

import "other_definitions.proto";

message ExtendedPerson {
    Person base = 1;
    string address = 2;
}
반응형

'개발 > MSA' 카테고리의 다른 글

ConnectionPool 이란? - 설정 방법  (0) 2023.09.18

ConnectionPool 이란?

연결을 재사용 가능한 형태로 미리 생성해두고, 필요할 때 애플리케이션에서 제공하는 매커니즘입니다.
ConnectionPool을 사용하는 주요 이유는 연결을 맺고 해제하는 과정이 상대적으로 많은 자원과 시간을 소모하기 때문입니다.

Connection Pool 동작 과정

  1. 애플리케이션이 시작될 때, Connection Pool은 지정된 수의 연결을 미리 생성합니다.
  2. 애플리케이션은 연결 작업이 필요할 때, Connection Pool로부터 연결을 요청합니다.
  3. Connection Pool은 사용 가능한 연결을 애플리케이션에 제공합니다.
  4. 애플리케이션은 작업이 끝나면 연결을 닫지 않고, 다시 Connection Pool에 반환합니다. 이 연결은 후속 요청에서 재사용됩니다.

Connection Pool의 장단점

장점

  1. 응답 시간 감소

사용자가 웹 애플리케이션에 로그인 요청을 보낼 때마다 새로운 연결을 생성하는 대신, 미리 생선된 연결을 사용하여 즉시 응답을 처리할 수 있습니다.
연결 설정 시간이 제거되므로 전체 응답 시간이 빨라집니다.

  1. 리소스 최적화

연결 풀을 사용하면 동시에 열려있는 연결 수를 제한할 수 있습니다.
이렇게 하면 서버에 과도한 부하가 걸리는 것을 방지하면서 리소스를 효율적으로 활용할 수 있습니다.

  1. 시스템 안정성 향상

특정 서비스에 대한 급증한 요청으로 서비스가 다운되는 경우, 연결 풀의 크기 제한을 사용하여 특정 임계값을 넘지 않게 할 수 있습니다.
이는 서비스 거부 공격(Denial-of-Service attack)과 같은 상황에서도 서비스의 안정성을 유지하는데 도움이 됩니다.

단점

  1. 초기 구성 복잡성

연결 풀의 최소/최대 크기, 유휴 시간, 연결 유효성 검사 등 다양한 설정 값을 조절하고 최적화해야 합니다.
설정이 적절하지 않으면 성능 문제나 안정성 문제가 발생할 수 있습니다.

  1. 잠재적 리소스 낭비

연결 풀의 크기가 너무 크게 설정되면, 실제로 사용되지 않는 많은 연결이 유휴 상태로 남아 있게 되어 리소스가 낭비될 수 있습니다.

  1. 커넥션 누수 위험

애플리케이션 코드에서 연결을 올바르게 반환하지 않으면, 해당 연결이 풀에서 계속 점유되게 됩니다.
이러한 상황이 반복되면 결국 풀에 사용 가능한 연결이 없게 되어 새로운 요청을 처리할 수 없게 됩니다.
이를 커넥션 누수(connection leak)라고 합니다.

  1. 불완전한 연결 상태

연결이 오래동안 유휴 상태로 있거나 네트워크 문제 등으로 인해 연결이 끊어진 상태일 수 있습니다.
이 경우, 애플리케이션이 해당 연결을 사용하려고 하면 오류가 발생할 수 있습니다.
이를 방지하기 위해 연결 유효성 검사 등의 추가 작업이 필요합니다.

Connection Pool 이 주로 사용되는 4가지

  1. DB
  • DB 연결은 생성과 해제에 비용이 많이 들기 때문에, 연결 풀링을 통해 성능을 향상시킬 수 있습니다.
  1. HTTP 연결
  • 웹 서버 또는 웹 서비스에 대한 HTTP(S) 연결을 풀링하여, 동일한 목적지에 대한 반복적인 요청의 성능을 향상시킬 수 있습니다.
  1. 네트워크 소켓 연결
  • 원격 서비스나 미들웨어(ex: message broker) 와의 통신을 위한 일반적인 TCP 또는 UDP 소켓 연결을 풀링할 수 있습니다.
  1. LDAP 연결
  • LDAP 서버와의 통신을 위한 연결을 풀링하여, 사용자 인증 및 권한 부여 쿼리의 성능을 향상시킬 수 있습니다.

Connection Pool 의 주요 설정 값

  1. maxPoolSize (최대 풀 크기)
  • 연결 풀에 보유할 수 있는 최대 연결 수 입니다.
  • 너무 큰 값은 시스템의 자원을 낭비할 수 있으므로, 실제 시스템의 부하에 따라 조절이 필요합니다.
  1. minPoolSize (최소 풀 크기)
  • 연결 풀이 유지해야 하는 최소한의 연결 수 입니다.
  • 시스템이 비활성 상태일 때도 일정 수의 연결을 유지하게 됩니다.
  1. initialPoolSize (초기 풀 크기)
  • 연결 풀이 처음 생성될 때 만들어져야 하는 연결의 수 입니다.
  • minPoolSize 이상, maxPoolSize 이하의 값이 되어야 합니다.
  1. connectionTimeout (연결 시간 초과)
  • 연결을 얻기 위해 대기할 최대 시간입니다.
  • 지정된 시간 안에 연결을 얻지 못하면 오류가 발생합니다.
  1. idleTimeout (유휴 연결 시간 초과)
  • 연결이 유휴 상태로 있을 수 있는 최대 시간입니다. 이 시간을 초과하면 연결은 닫힙니다.
  • 유휴 상태(Idle State)는 어떤 자원이 활성화되어 있찌만 현재 사용되고 있지 않은 상태(대기 중, 사용되지 않는 상태) 입니다.
  1. maxLifetime (최대 연결 수명)
  • Pool 에 있을 수 있는 연결의 최대 수명입니다.
  • 이 시간을 초과하면 연결은 닫힙니다.
  1. validationTimeout (검증 시간 초과)
  • 연결 검증에 대한 최대 대기 시간입니다.
  1. connectionTestQuery (연결 테스트 쿼리)
  • 연결의 유효성을 확인하기 위한 쿼리입니다.
  • 일반적으로 간단한 쿼리 (ex: 'SELECT 1')가 사용됩니다.

ConnectionPool 의 적절한 설정값 구하기

사실상 ConnectionPool 의 설정값을 "이 값으로 하면 최적이다"라고 하는 값은 없습니다.
그러므로 다음과 같은 절차를 따라 적절한 설정값을 구할 수 있습니다.

  1. 시스템 분석
  • 시스템의 특성, 예상 사용자 수, 트래픽 패턴, 데이터베이스와의 통신 패턴 등을 분석합니다.
  1. 기본값 시작
  • 많은 Connection Pool 라이브러리는 합리적인 기본값을 제공합니다. 이를 시작점으로 사용하고, 이후에 조절할 수 있습니다.
  1. 모니터링
  • 초기 설정을 한 후에는 Connection Pool 의 사용 상태를 모니터링 합니다.
  • 풀에서 사용 가능한 연결의 수, 대기 중인 연결 요청의 수, 유휴 연결의 수 등을 확인할 수 있습니다.
  1. 부하 테스트
  • 시스템에 예상 트래픽 이상의 부하를 가하면서 Connection Pool의 동작을 관찰합니다.
  • 이를 통해 최대 연결 수, 연결 시간 초과 등의 설정값을 조절할 수 있습니다.
  1. 조절 및 반복
  • 모니터링 및 부하 테스트의 결과를 바탕으로 설정값을 조절하고, 다시 테스트와 모니터링을 반복합니다.
  1. 유휴 연결 관리
  • 연결이 유휴 상태로 오래 유지되지 않도록 설정합니다.
  • 일정 시간 이상 사용되지 않는 연결은 자동으로 닫히도록 'idleTimeout' 설정을 조절할 수 있습니다.
  1. 문서화
  • 설정값과 그 이유, 테스트 결과 등을 문서화하여 나중에 참조하거나, 다른 팀원과 공유할 수 있도록 합니다.
  1. 정기적인 리뷰
  • 시스템의 환경이나 요구 사항이 변경될 수 있으므로, 주기적으로 Connection Pool 설정을 리뷰하고 필요에 따라 조절합니다.
반응형

'개발 > MSA' 카테고리의 다른 글

프로토콜 버퍼(Protocol Buffers, Protobuf)란?  (0) 2023.09.19

JQuery와 Java Spring을 이용한 프로젝트에서 JQuery 쪽의 ajax에서 Get Method를 이용한 통신을 Post Method를 이용한 통신으로 변경하였을 때 java에서 설정한 Parameter의 값이 넘어오지 않는 현상 발견!

기존 Get을 이용한 ajax 통신

아래와 같은 코드를 이용하였을 때는 java쪽에 parameter가 정상적으로 처리가 되었습니다.

  • JQuery 코드

let param = $('#inquiry-form').serialize();

$.ajax({
type : "GET",
url : urlPrefix+'/inquiry/sendInquiry',
data : param,
dataType : 'json',
contentType : "application/json; charset=utf-8",
success : function(data){
$('.flex-input-box').removeClass('error')
if(data["code"] == "200"){
window.location.href = urlPrefix+'/inquiry/complete'
}else{
alert(data["message"]);
}
}
})


- java 코드

@RestController
@RequestMapping("${signplus.url-prefix.front}/inquiry")
public class FrontInquiryController {

@Autowired
HttpApiConnectionUtill httpApiConnectionUtill;

@GetMapping()
public String view() {
    return "front/inquiry/inquiry";
}

@GetMapping("/complete")
public String complete() {
    return "front/inquiry/complete";
}

@GetMapping("/sendInquiry")
public ResponseEntity<JSONObject> sendInquiry(@RequestParam Map<String,Object> paramMap) throws URISyntaxException {

    JSONObject jsonObject = httpApiConnectionUtill.sendApiCallClientCredentials(ApiUrlConstants.util+"/sendInquiry", HttpMethod.POST, paramMap);

    JSONObject result = new JSONObject();
    result.put("code", jsonObject.get("code"));
    result.put("message", jsonObject.get("message"));

    return ResponseEntity.status(HttpStatus.OK).body(result);
}

}


### Post를 이용한 ajax 통신으로 변경

처음에 ajax 내의 method를 get -> post, java쪽 @GetMapping -> @PostMapping으로만 변경하면 동작 할 줄 알고 변경하였지만 parameter의 값이 넘어오지 않는 현상이 발견되었습니다.
이부분은 parameter가 전달되지 않은 채로 성공으로 처리되고 있어 발견하기 쉽지 않았습니다.

원인으로는 Get 메소드와 ajax에서 data로 넘겨주는 form 태그의 serialize()의 내용을 제대로 이해하지 못해 나타난 오류였습니다.

@GetMapping에서는 웹 브라우저나 다른 클라이언트가 URL의 쿼리 파라미터를 통해 데이터를 전송합니다.
예를 들면, https://example.com/inquiry?param1=value1&param2=value2와 같은 형태입니다.
@RequestParam은 이러한 쿼리 파라미터를 쉽게 받아올 수 있도록 도와줍니다.
이전 코드에서 ajax의 contentType을 json 형식으로 넘긴다고 정의하였었지만 실제로는 Get 메소드의 특징에 의해 쿼리스트링으로 전달되고 있었던겁니다.

그러나, @PostMapping을 사용할 때는 요청 본문(body)에 데이터가 포함되어 전송됩니다.
@PostMapping을 이용하여 ajax를 통해 본문으로 json 형태로 넘긴다고(contentType) 명시하였지만 실제로는 serialize()를 이용하여 URL-encoded로 보내려고 했으니 해당 부분이 json이 아니어서 @RequestParam 으로 parameter를 받으려고 했지만 값이 제대로 넘어가고 있지 않았던 겁니다.

해결책으로는 2가지가 있습니다.

1. ajax를 이용하여 data를 보낼 때, json 형식으로 변경, java에서 @RequestBody를 이용하여 parameter 받아오기
2. ajax를 이용하여 기존과 동일하게 URL-encoded로 전송, 단, 위의 코드에서 contentType으로 json으로 보낸다는 부분 제거 또는 URL-encoded 명시적으로 설정


#### 수정한 코드

- JQuery

let param = $('#inquiry-form').serialize();

$.ajax({
type : "POST",
url : urlPrefix+'/inquiry/sendInquiry',
data : param,
dataType : 'json',
success : function(data){
$('.flex-input-box').removeClass('error')
if(data["code"] == "200"){
window.location.href = urlPrefix+'/inquiry/complete'
}else{
alert(data["message"]);
}
}
})


- java

@RestController
@RequestMapping("${signplus.url-prefix.front}/inquiry")
public class FrontInquiryController {

@Autowired
HttpApiConnectionUtill httpApiConnectionUtill;


@PostMapping("/sendInquiry")
public ResponseEntity<JSONObject> sendInquiry(@RequestParam Map<String,Object> paramMap) throws URISyntaxException {

    JSONObject jsonObject = httpApiConnectionUtill.sendApiCallClientCredentials(ApiUrlConstants.util+"/sendInquiry", HttpMethod.POST, paramMap);

    JSONObject result = new JSONObject();
    result.put("code", jsonObject.get("code"));
    result.put("message", jsonObject.get("message"));

    return ResponseEntity.status(HttpStatus.OK).body(result);
}

}
```

반응형

+ Recent posts