제5장: 통신 프로토콜

弘益人間 (홍익인간) - 널리 인간을 이롭게 하라
안전하고 효율적인 통신 프로토콜을 통해 전 세계 언어 보존 시스템이 원활하게 연결되고, 데이터가 안전하게 전송되며, 모든 연구자가 평등하게 접근할 수 있도록 합니다.

1. 통신 프로토콜 개요

WIA-LANG-001 통신 프로토콜은 분산된 언어 보존 시스템 간의 안전하고 효율적인 데이터 교환을 가능하게 합니다. 전 세계 수천 개의 언어 보존 프로젝트가 서로 다른 환경에서 운영되므로, 표준화된 통신 프로토콜은 상호운용성을 보장하는 핵심 요소입니다.

1.1 프로토콜 스택 구조

WIA-LANG 프로토콜 스택은 OSI 모델을 기반으로 다음과 같은 계층으로 구성됩니다:

계층 프로토콜 역할 예시
응용 계층 WIA-LANG Protocol 언어 데이터 교환 어휘 동기화, 메타데이터 전송
표현 계층 JSON/MessagePack 데이터 직렬화 JSON 인코딩/디코딩
세션 계층 WebSocket/gRPC 연결 관리 지속적 연결, 양방향 통신
전송 계층 TLS 1.3 보안 전송 암호화, 인증
네트워크 계층 TCP/IP, HTTP/3 패킷 라우팅 데이터 전송

2. HTTP/HTTPS 기반 통신

WIA-LANG의 기본 통신 프로토콜은 HTTPS입니다. 모든 데이터 전송은 TLS 1.3 이상으로 암호화되어야 하며, 弘益人間 철학에 따라 개방성과 보안성을 동시에 추구합니다.

2.1 HTTPS 요청/응답 구조

// 표준 HTTPS 요청 헤더
GET /v1/languages/lang-jje-001 HTTP/2
Host: api.wia.org
User-Agent: WIA-LANG-Client/1.0 (Philosophy: 弘益人間)
Accept: application/json
Accept-Encoding: gzip, deflate, br
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...
X-WIA-Request-ID: req-abc123
X-WIA-Client-Version: 1.0.0
X-WIA-Philosophy: 弘益人間

// 표준 HTTPS 응답 헤더
HTTP/2 200 OK
Content-Type: application/json; charset=utf-8
Content-Encoding: gzip
Content-Length: 2048
Cache-Control: public, max-age=3600
ETag: "686897696a7c876b7e"
X-WIA-Request-ID: req-abc123
X-WIA-Server-Version: 1.0.0
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 998
X-RateLimit-Reset: 1735486800
X-WIA-Philosophy: 弘益人間

{
  "status": "success",
  "data": { ... },
  "meta": {
    "philosophy": "弘益人間",
    "version": "1.0"
  }
}

2.2 커스텀 HTTP 헤더

헤더명 설명 필수여부 예시
X-WIA-Request-ID 요청 추적 ID 권장 req-abc123
X-WIA-Client-Version 클라이언트 버전 권장 1.0.0
X-WIA-Philosophy 弘益人間 철학 표명 선택 弘益人間
X-RateLimit-Limit 시간당 요청 한도 응답 시 포함 1000
X-RateLimit-Remaining 남은 요청 수 응답 시 포함 998

3. WebSocket 실시간 통신

실시간 언어 데이터 동기화와 협업을 위해 WIA-LANG은 WebSocket 프로토콜을 지원합니다. 이를 통해 여러 연구자가 동시에 같은 언어 데이터를 편집하고, 변경사항을 즉시 공유할 수 있습니다.

3.1 WebSocket 연결 설정

// WebSocket 클라이언트 연결
import { WIALangWebSocket } from '@wia/lang-client';

const ws = new WIALangWebSocket({
  url: 'wss://realtime.wia.org/v1/stream',
  apiKey: process.env.WIA_API_KEY,
  philosophy: '弘益人間'
});

// 연결 이벤트
ws.on('connect', () => {
  console.log('WIA-LANG WebSocket 연결됨');

  // 특정 언어 채널 구독
  ws.subscribe('language:jje', {
    events: ['word.created', 'word.updated', 'audio.uploaded']
  });
});

// 실시간 데이터 수신
ws.on('word.created', (data) => {
  console.log('새 단어 추가됨:', data);
  /*
  {
    "languageId": "lang-jje-001",
    "wordId": "word-jje-2501",
    "lemma": "고망",
    "contributor": "김제주",
    "timestamp": "2025-12-29T14:30:00Z"
  }
  */
});

// 연결 끊김 처리
ws.on('disconnect', (reason) => {
  console.log('연결 끊김:', reason);
  // 자동 재연결 시도
  setTimeout(() => ws.reconnect(), 5000);
});

// 핑/퐁으로 연결 유지
ws.on('ping', () => {
  ws.pong();
});

3.2 WebSocket 메시지 형식

// 클라이언트 -> 서버 (구독 요청)
{
  "type": "subscribe",
  "channel": "language:jje",
  "events": ["word.created", "word.updated"],
  "filters": {
    "partOfSpeech": "noun"
  },
  "philosophy": "弘益人間"
}

// 서버 -> 클라이언트 (구독 확인)
{
  "type": "subscribed",
  "channel": "language:jje",
  "subscriptionId": "sub-xyz789",
  "timestamp": "2025-12-29T14:30:00Z"
}

// 서버 -> 클라이언트 (실시간 이벤트)
{
  "type": "event",
  "event": "word.created",
  "channel": "language:jje",
  "data": {
    "wordId": "word-jje-2501",
    "lemma": "고망",
    "translation": "고맙다",
    "contributor": "김제주"
  },
  "timestamp": "2025-12-29T14:30:15Z",
  "philosophy": "弘益人間"
}

4. gRPC 고성능 통신

대용량 언어 데이터 전송과 마이크로서비스 간 통신을 위해 WIA-LANG은 gRPC를 지원합니다. gRPC는 HTTP/2 기반의 고성능 RPC 프레임워크로, Protocol Buffers를 사용하여 효율적인 직렬화를 제공합니다.

4.1 Protocol Buffers 정의

// wia_lang.proto
syntax = "proto3";

package wia.lang.v1;

// 언어 서비스 정의
service LanguageService {
  // 언어 목록 조회
  rpc ListLanguages(ListLanguagesRequest) returns (ListLanguagesResponse);

  // 특정 언어 조회
  rpc GetLanguage(GetLanguageRequest) returns (Language);

  // 어휘 목록 조회 (스트리밍)
  rpc StreamLexicon(StreamLexiconRequest) returns (stream Word);

  // 오디오 업로드 (클라이언트 스트리밍)
  rpc UploadAudio(stream AudioChunk) returns (AudioUploadResponse);

  // 실시간 동기화 (양방향 스트리밍)
  rpc SyncLanguageData(stream SyncRequest) returns (stream SyncResponse);
}

// 메시지 정의
message Language {
  string id = 1;
  string code = 2;
  string name = 3;
  string english_name = 4;
  EndangermentLevel endangerment_level = 5;
  int32 speaker_count = 6;
  Region region = 7;
  string philosophy = 8; // "弘益人間"
}

enum EndangermentLevel {
  SAFE = 0;
  VULNERABLE = 1;
  ENDANGERED = 2;
  CRITICALLY_ENDANGERED = 3;
  EXTINCT = 4;
}

message Region {
  string country = 1;
  string province = 2;
  Coordinates coordinates = 3;
}

message Coordinates {
  double latitude = 1;
  double longitude = 2;
}

message Word {
  string id = 1;
  string lemma = 2;
  string translation = 3;
  string part_of_speech = 4;
  Pronunciation pronunciation = 5;
}

message Pronunciation {
  string ipa = 1;
  string audio_url = 2;
}

message ListLanguagesRequest {
  int32 page = 1;
  int32 limit = 2;
  EndangermentLevel endangerment_level = 3;
  string region = 4;
}

message ListLanguagesResponse {
  repeated Language languages = 1;
  Pagination pagination = 2;
  string philosophy = 3; // "弘益人間"
}

message Pagination {
  int32 current_page = 1;
  int32 total_pages = 2;
  int32 total_items = 3;
}

4.2 gRPC 클라이언트 사용 예제

// Node.js gRPC 클라이언트
const grpc = require('@grpc/grpc-js');
const protoLoader = require('@grpc/proto-loader');

// Proto 파일 로드
const packageDefinition = protoLoader.loadSync('wia_lang.proto', {
  keepCase: true,
  longs: String,
  enums: String,
  defaults: true,
  oneofs: true
});

const wiaLangProto = grpc.loadPackageDefinition(packageDefinition).wia.lang.v1;

// gRPC 클라이언트 생성
const client = new wiaLangProto.LanguageService(
  'grpc.wia.org:443',
  grpc.credentials.createSsl()
);

// 언어 목록 조회
client.listLanguages({
  page: 1,
  limit: 20,
  endangerment_level: 'CRITICALLY_ENDANGERED',
  region: 'Asia'
}, (error, response) => {
  if (error) {
    console.error('오류:', error);
    return;
  }

  console.log(`총 ${response.pagination.total_items}개 언어 발견`);
  console.log(`철학: ${response.philosophy}`); // "弘益人間"

  for (const lang of response.languages) {
    console.log(`${lang.name} (${lang.code}): ${lang.speaker_count}명`);
  }
});

// 어휘 스트리밍 조회
const stream = client.streamLexicon({
  language_id: 'lang-jje-001',
  limit: 1000
});

stream.on('data', (word) => {
  console.log(`${word.lemma} (${word.translation})`);
});

stream.on('end', () => {
  console.log('스트리밍 완료');
});

stream.on('error', (error) => {
  console.error('스트리밍 오류:', error);
});

5. 데이터 압축 및 최적화

언어 데이터는 오디오, 비디오, 이미지 등 대용량 파일을 포함하므로, 효율적인 데이터 압축과 전송 최적화가 필수적입니다.

5.1 지원 압축 알고리즘

압축 방식 압축률 속도 사용 사례
Gzip 중간 (70-80%) 빠름 JSON, XML 텍스트 데이터
Brotli 높음 (75-85%) 중간 웹 API 응답
Zstandard 높음 (80-90%) 매우 빠름 대용량 데이터베이스 백업
FLAC 높음 (50-70%) 중간 오디오 파일 (무손실)
MessagePack 중간 (60-70%) 매우 빠름 실시간 바이너리 데이터

5.2 Content-Encoding 협상

// 클라이언트 압축 지원 명시
GET /v1/languages HTTP/2
Host: api.wia.org
Accept-Encoding: br, gzip, deflate

// 서버 응답 (Brotli 압축 사용)
HTTP/2 200 OK
Content-Type: application/json
Content-Encoding: br
Vary: Accept-Encoding

[Brotli 압축된 데이터]

6. 보안 프로토콜

언어 데이터의 무결성과 기밀성을 보장하기 위해 WIA-LANG은 다층 보안 프로토콜을 적용합니다.

6.1 TLS 1.3 암호화

TLS 1.3 핸드셰이크

  1. ClientHello: 클라이언트가 지원하는 암호화 방식 전송
  2. ServerHello: 서버가 선택한 암호화 방식 응답
  3. 키 교환: Diffie-Hellman 키 교환으로 세션 키 생성
  4. 인증: 서버 인증서 검증 (선택적 클라이언트 인증)
  5. 암호화 통신: 모든 후속 데이터 암호화
// Node.js TLS 설정 예제
const https = require('https');
const fs = require('fs');

const options = {
  key: fs.readFileSync('private-key.pem'),
  cert: fs.readFileSync('certificate.pem'),
  ca: fs.readFileSync('ca-cert.pem'),

  // TLS 1.3만 허용
  minVersion: 'TLSv1.3',
  maxVersion: 'TLSv1.3',

  // 안전한 암호화 스위트
  ciphers: [
    'TLS_AES_256_GCM_SHA384',
    'TLS_CHACHA20_POLY1305_SHA256',
    'TLS_AES_128_GCM_SHA256'
  ].join(':'),

  // Forward Secrecy 활성화
  honorCipherOrder: true,

  // 클라이언트 인증 요구 (선택적)
  requestCert: false,
  rejectUnauthorized: true
};

const server = https.createServer(options, (req, res) => {
  res.writeHead(200, {
    'Content-Type': 'application/json',
    'X-WIA-Philosophy': '弘益人間'
  });
  res.end(JSON.stringify({ status: 'secure' }));
});

server.listen(443, () => {
  console.log('WIA-LANG 보안 서버 실행 중 (TLS 1.3)');
});

6.2 데이터 무결성 검증

모든 전송 데이터는 체크섬을 통해 무결성을 검증합니다.

// SHA-256 체크섬 계산 및 검증
const crypto = require('crypto');

function calculateChecksum(data) {
  return crypto
    .createHash('sha256')
    .update(JSON.stringify(data))
    .digest('hex');
}

// 데이터 전송 시 체크섬 포함
const languageData = {
  id: 'lang-jje-001',
  name: '제주어',
  philosophy: '弘익人間'
};

const checksum = calculateChecksum(languageData);

const payload = {
  data: languageData,
  checksum: checksum,
  algorithm: 'sha256'
};

// 수신 측 검증
function verifyChecksum(payload) {
  const receivedChecksum = payload.checksum;
  const calculatedChecksum = calculateChecksum(payload.data);

  if (receivedChecksum !== calculatedChecksum) {
    throw new Error('데이터 무결성 검증 실패!');
  }

  console.log('✓ 체크섬 검증 성공');
  return payload.data;
}

try {
  const verifiedData = verifyChecksum(payload);
  console.log('검증된 데이터:', verifiedData);
} catch (error) {
  console.error('오류:', error.message);
}

7. 오류 복구 및 재시도 메커니즘

네트워크 불안정성을 고려하여 WIA-LANG은 지능적인 재시도 메커니즘을 제공합니다.

7.1 지수 백오프 (Exponential Backoff)

// 지수 백오프 재시도 구현
async function fetchWithRetry(url, options = {}, maxRetries = 5) {
  let lastError;

  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      console.log(`시도 ${attempt + 1}/${maxRetries}`);

      const response = await fetch(url, {
        ...options,
        headers: {
          ...options.headers,
          'X-WIA-Retry-Attempt': attempt.toString(),
          'X-WIA-Philosophy': '弘益人間'
        }
      });

      if (response.ok) {
        return await response.json();
      }

      // 429 (Too Many Requests) 처리
      if (response.status === 429) {
        const retryAfter = response.headers.get('Retry-After');
        const waitTime = retryAfter ? parseInt(retryAfter) * 1000 : calculateBackoff(attempt);

        console.log(`Rate limit 도달. ${waitTime}ms 대기 중...`);
        await sleep(waitTime);
        continue;
      }

      // 5xx 서버 오류는 재시도
      if (response.status >= 500) {
        throw new Error(`서버 오류: ${response.status}`);
      }

      // 4xx 클라이언트 오류는 재시도 안 함
      throw new Error(`클라이언트 오류: ${response.status}`);

    } catch (error) {
      lastError = error;
      console.error(`오류 발생:`, error.message);

      if (attempt < maxRetries - 1) {
        const backoffTime = calculateBackoff(attempt);
        console.log(`${backoffTime}ms 후 재시도...`);
        await sleep(backoffTime);
      }
    }
  }

  throw new Error(`최대 재시도 횟수 초과: ${lastError.message}`);
}

function calculateBackoff(attempt) {
  // 지수 백오프: 2^attempt * 1000ms + 랜덤 지터
  const exponentialTime = Math.pow(2, attempt) * 1000;
  const jitter = Math.random() * 1000;
  return Math.min(exponentialTime + jitter, 60000); // 최대 60초
}

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

// 사용 예제
try {
  const data = await fetchWithRetry('https://api.wia.org/v1/languages', {
    method: 'GET',
    headers: {
      'Authorization': 'Bearer token123'
    }
  });

  console.log('데이터 조회 성공:', data);
} catch (error) {
  console.error('최종 실패:', error.message);
}

7.2 Circuit Breaker 패턴

// Circuit Breaker 구현
class CircuitBreaker {
  constructor(threshold = 5, timeout = 60000) {
    this.failureCount = 0;
    this.threshold = threshold;
    this.timeout = timeout;
    this.state = 'CLOSED'; // CLOSED, OPEN, HALF_OPEN
    this.nextAttempt = Date.now();
  }

  async execute(fn) {
    if (this.state === 'OPEN') {
      if (Date.now() < this.nextAttempt) {
        throw new Error('Circuit breaker OPEN 상태 - 서비스 일시 중단');
      }
      this.state = 'HALF_OPEN';
    }

    try {
      const result = await fn();
      this.onSuccess();
      return result;
    } catch (error) {
      this.onFailure();
      throw error;
    }
  }

  onSuccess() {
    this.failureCount = 0;
    this.state = 'CLOSED';
    console.log('✓ Circuit breaker: CLOSED');
  }

  onFailure() {
    this.failureCount++;
    console.log(`✗ Circuit breaker: 실패 ${this.failureCount}/${this.threshold}`);

    if (this.failureCount >= this.threshold) {
      this.state = 'OPEN';
      this.nextAttempt = Date.now() + this.timeout;
      console.log(`⚠ Circuit breaker: OPEN (${this.timeout}ms 동안)`);
    }
  }

  getState() {
    return this.state;
  }
}

// 사용 예제
const breaker = new CircuitBreaker(3, 30000);

async function callAPI() {
  return breaker.execute(async () => {
    const response = await fetch('https://api.wia.org/v1/languages');
    if (!response.ok) throw new Error('API 오류');
    return response.json();
  });
}

// 여러 번 호출 시도
for (let i = 0; i < 10; i++) {
  try {
    const data = await callAPI();
    console.log('성공:', data.meta?.philosophy); // "弘益人間"
  } catch (error) {
    console.error('실패:', error.message);
  }
  await sleep(1000);
}

8. P2P 분산 통신

중앙 서버 의존도를 낮추고 弘益人間 정신에 따라 분산된 언어 보존을 실현하기 위해, WIA-LANG은 선택적으로 P2P (Peer-to-Peer) 통신을 지원합니다.

8.1 WebRTC 기반 P2P 연결

// WebRTC P2P 연결 설정
import { WIALangP2P } from '@wia/lang-p2p';

const p2p = new WIALangP2P({
  peerId: 'peer-researcher-042',
  signalServer: 'wss://signal.wia.org',
  philosophy: '弘益人間'
});

// 시그널 서버 연결
await p2p.connect();

// 다른 연구자와 P2P 연결
const peer = await p2p.connectToPeer('peer-linguist-123');

// 언어 데이터 직접 전송
peer.send({
  type: 'language_data',
  languageId: 'lang-jje-001',
  data: {
    wordId: 'word-jje-2501',
    lemma: '고망',
    audio: [오디오 바이너리 데이터]
  }
});

// 데이터 수신
peer.on('data', (message) => {
  console.log('P2P 데이터 수신:', message);

  if (message.type === 'language_data') {
    // 로컬 데이터베이스에 저장
    saveLanguageData(message.data);
  }
});

console.log('弘益人間 - P2P로 언어 데이터 공유 중');

장 요약

  1. 다층 프로토콜 스택: WIA-LANG-001은 OSI 모델을 기반으로 응용 계층부터 네트워크 계층까지 표준화된 프로토콜 스택을 정의하여 언어 보존 시스템 간 안정적인 통신을 보장합니다.
  2. HTTPS 기본 통신: TLS 1.3 이상의 암호화를 필수로 하며, 커스텀 HTTP 헤더를 통해 요청 추적, 버전 관리, 속도 제한 등을 효과적으로 수행합니다.
  3. 실시간 WebSocket 지원: 양방향 실시간 통신을 통해 전 세계 연구자들이 동시에 언어 데이터를 편집하고 동기화할 수 있는 협업 환경을 제공합니다.
  4. 고성능 gRPC: Protocol Buffers 기반의 gRPC를 통해 대용량 언어 데이터를 효율적으로 전송하며, 스트리밍 API로 실시간 데이터 처리를 지원합니다.
  5. 지능적 오류 복구: 지수 백오프 재시도와 Circuit Breaker 패턴을 통해 네트워크 불안정성에 대응하고, 시스템 안정성을 높입니다.
  6. P2P 분산 통신: WebRTC 기반 P2P 연결로 중앙 서버 의존도를 낮추고, 弘益人間 철학에 따라 분산된 언어 보존 생태계를 구축합니다.

복습 질문

  1. WIA-LANG 프로토콜 스택의 5개 계층을 나열하고, 각 계층의 역할과 사용되는 프로토콜을 설명하시오.
  2. HTTPS 통신에서 X-RateLimit-Limit과 X-RateLimit-Remaining 헤더가 중요한 이유는 무엇이며, 클라이언트는 이 정보를 어떻게 활용해야 하나요?
  3. WebSocket 프로토콜이 일반 HTTP 요청보다 실시간 협업에 적합한 이유를 설명하고, WIA-LANG에서의 구체적인 사용 사례를 제시하시오.
  4. gRPC와 REST API의 차이점을 비교하고, 언어 데이터 전송 시나리오에서 각각 어떤 경우에 적합한지 논하시오.
  5. 지수 백오프(Exponential Backoff) 재시도 메커니즘에서 "지터(jitter)"를 추가하는 이유는 무엇이며, 이것이 시스템 전체 안정성에 어떻게 기여하나요?
  6. Circuit Breaker 패턴의 세 가지 상태(CLOSED, OPEN, HALF_OPEN)를 설명하고, 각 상태 전환이 일어나는 조건과 의미를 논하시오.
  7. P2P 통신이 중앙 서버 기반 통신과 비교하여 가지는 장단점을 분석하고, 弘益人間 철학과의 연관성을 설명하시오.