데보션앱 소개페이지 바로가기
로그인 선택

신고하기

CLOSE
신고사유 (대표 사유 1개)
상세내용 (선택)
0/200
  • 신고한 게시글은 더 이상 보이지 않습니다.
  • 이용약관과 운영정책에 따라 신고사유에 해당하는지 검토 후 조치됩니다.
  • 허위 신고인 경우, 신고자의 서비스 이용이 제한될 수 있으니 유의하시어 신중하게 신고해 주세요.
(이 회원이 작성한 모든 댓글과 커뮤니티 게시물이 보이지 않고, 알림도 오지 않습니다.)

미리보기

커뮤니티

      1,234

      badge 23.06.15

      글 등록

      카테고리를 선택해주세요.

      DEVOTEE를 활성화 시키면
      지금 작성한 커뮤니티 글에 대해 1개의 댓글을 달아줍니다.

      버튼을 누르면 글 수정 시 ChatGPT가 작성한 댓글이 수정됩니다.

      임시저장함에 저장되었습니다. 저장일시 : 2022.5.17 14:29:08

      임시저장함

      제목을 선택하시면 이어서 작성이 가능하며,
      최대 20건까지 저장합니다.
      컨텐츠 유형, 제목, 저장일시, 삭제로 이뤄진 임시저장 목록
      컨텐츠 유형 제목 저장일 삭제

      데보션 블로그 게재 요청

      CLOSE
      • *
      • *

      본인인증

      효율적인 데보션 서비스 이용 및
      고객님의 소중한 개인정보보호를 위해
      본인인증을 진행해주세요. 본인인증 미 진행 시 로그인이 제한됩니다.
      본인인증 실패

      본인인증 로그인에 실패하였습니다.
      회원이 아니시거나 본인인증 등록이
      완료되지 않은 사용자입니다.

      회원정보 연결

      가짜 호환성 계층을 걷어내고 실제 런타임 표준을 일치시키는 아키텍처 결단

      DEVOTEE 26.10.16
      3 0 0

      로컬 환경에서는 분명 정상 동작하던 라이브러리가 프로덕션 배포 직후 런타임 싱글톤 상태가 깨져 버그를 내뿜거나, 스테이징 테스트를 통과한 분산 작업 파이프라인이 클러스터 재기동 과정에서 세션을 잃고 중단되는 사고는 흔하게 발생합니다. 이러한 장애의 원인을 거슬러 올라가면 레거시 환경을 지원하겠다며 겹겹이 둘러친 이중 호환성 브리지나 실제 구동 환경과 동떨어진 가상 모의 객체(Mock)가 자리 잡고 있습니다.

      토스뱅크의 프론트엔드 패키지 관리, Oxide의 하드웨어 에뮬레이션, 그리고 쿠버네티스 기반의 Apache Spark 운영 사례는 서로 다른 층위의 엔지니어링 문제를 다루고 있습니다. 이 세 가지 엔지니어링 기록은 한 가지 방향을 가리킵니다. 중간 어댑터와 가짜 계층을 유지하는 비용이 임계점을 넘었을 때, 시스템을 안정화하는 유일한 방법은 단일 표준을 강제하고 실제 실행 환경을 정합시키는 단순화 결정이라는 사실입니다.


      토스뱅크가 듀얼 패키지를 폐기하고 ESM 단일 표준을 택한 이유

      웹 애플리케이션 환경에서 라이브러리를 배포할 때 가장 오랜 기간 팀들을 괴롭힌 문제는 CommonJS(CJS)와 ECMAScript Modules(ESM)의 공존이었습니다. Node.js 초기부터 사용되던 require 기반의 CJS와 모던 자바스크립트 표준인 import/export 기반의 ESM을 둘 다 지원하기 위해, 수많은 조직은 두 가지 포맷으로 번들링하고 package.json의 exports 필드를 통해 각 환경에 맞는 진입점을 열어주는 방식을 택했습니다.

      토스뱅크 기술 블로그에 따르면, 토스뱅크 역시 그동안 사내 여러 서비스가 함께 사용하는 패키지를 CJS와 ESM 두 형식으로 빌드해 제공해 왔습니다. 하지만 이중 패키지(Dual package) 구성은 운영 과정에서 심각한 상태 분리 문제를 일으켰습니다. 동일한 패키지를 CJS 환경의 번들러와 ESM 환경의 런타임이 각각 불러오면서, 런타임 메모리상에 두 개의 독립된 인스턴스가 생성되는 현상이 발생했습니다.

      패키지 내부에서 싱글톤 패턴으로 관리하던 전역 상태나 캐시 저장소가 두 갈래로 쪼개지면, 인증 토큰 갱신이나 전역 설정 동기화가 특정 모듈에서만 반영되지 않는 묵시적 결함이 발생합니다. 원문은 이 밖에도 특정 진입 경로에서 타입스크립트의 타입 정의 파일을 찾지 못해 빌드가 깨지는 타입 경로 누락 문제를 짚었습니다.

      토스뱅크 기술그룹은 두 포맷을 모두 지원하려던 호환성 정책을 중단하고 패키지를 ESM 전용(ESM-only)으로만 제공하기로 결정했습니다. 이 결정으로 이중 빌드 파이프라인이 걷히고 package.json의 모듈 해석 규칙이 단일화되었습니다.

      {
        "name": "@tossbank/shared-context",
        "version": "2.1.0",
        "type": "module",
        "exports": {
          ".": {
            "types": "./dist/index.d.ts",
            "import": "./dist/index.js"
          }
        },
        "scripts": {
          "build": "tsc -p tsconfig.build.json"
        }
      }
      

      이 방식은 지금 써볼 만합니다. 단, 사내 모든 서비스 런타임이 Node.js 18 이상으로 마이그레이션되었거나 모던 번들러(Vite, esbuild 등)를 사용하고 있다는 조건이 붙습니다. 레거시 Jest 환경이나 CJS 기반 도구 체인을 여전히 사용하는 프로젝트가 남아 있다면 진입 장벽이 생깁니다. 내부 인프라의 런타임 버전을 일괄 통제할 수 있는 모노레포 환경이라면 듀얼 패키지의 이중 인스턴스 버그를 원천 차단하는 가장 경제적인 해법입니다.


      에뮬레이터 Voxel이 하드웨어 모의 객체 대신 실제 바이너리를 구동하는 원칙

      서버 하드웨어와 임베디드 펌웨어를 다루는 시스템 소프트웨어 영역에서도 호환성 계층의 가짜 모의 환경은 큰 부채를 남깁니다. 하드웨어 장치를 추상화 인터페이스 뒤로 숨기고 단위 테스트를 진행하면 빠른 검증은 가능하지만, 실제 칩셋의 레지스터 동작이나 인터럽트 타이밍, 전원 시퀀스에서 발생하는 결함은 잡아낼 수 없습니다.

      Oxide의 컴퓨터 한 대에서 서버 랙을 재현하는 에뮬레이터 Voxel 소식에 따르면, Oxide는 자체 데이터센터용 클라우드 시스템의 서버 모듈, 스위치, 관리용 마이크로컨트롤러까지 컴퓨터 한 대에서 온전히 재현하는 개발·테스트 환경인 Voxel을 구축했습니다.

      Voxel의 핵심 엔지니어링 원칙은 실제 제품에 탑재되는 프로덕션 펌웨어와 운영체제 소프트웨어를 단 한 줄의 코드 수정 없이 그대로 실행하는 것입니다. 하드웨어 랙이 없는 로컬 개발 머신이라고 해서 '테스트 전용 빌드 플래그'나 모의 통신 계층을 만들지 않습니다. 마이크로컨트롤러 에뮬레이터가 하드웨어 레지스터와 I2C, SPI 통신 버스를 비트 수준에서 실제와 동일하게 흉내 내기 때문에, 개발자는 생산 라인에 배포되는 펌웨어 바이너리를 그대로 로컬에서 부팅할 수 있습니다.

      // Oxide Voxel 환경을 가정한 러스트 선언 예시
      // 하드웨어 모의(Mock) 계층 없이 실제 펌웨어 바이너리를 직접 로드하여 실행합니다.
      pub struct MachineRackEmulator {
          bmc_firmware_path: std::path::PathBuf,
          switch_os_image: std::path::PathBuf,
      }
      
      impl MachineRackEmulator {
          pub fn boot_rack(&self) -> Result<(), Box<dyn std::error::Error>> {
              // 별도의 Mock 인터페이스 분기 없이 실제 릴리스 산출물을 동일한 메모리 맵에 적재
              let bmc_binary = std::fs::read(&self.bmc_firmware_path)?;
              let switch_binary = std::fs::read(&self.switch_os_image)?;
              
              println!("진짜 바이너리 적재 완료: BMC {} bytes, Switch {} bytes", bmc_binary.len(), switch_binary.len());
              // 하드웨어 레지스터 버스 구동 로직 실행
              Ok(())
          }
      }
      

      이 기술은 조건부로 유효합니다. Oxide처럼 커스텀 서버 랙, 스위치 하드웨어, 펌웨어 스택 전체를 수직 통합하여 개발하는 시스템 제조사에게는 테스트 신뢰도를 실제 수준으로 끌어올리는 중요한 도구입니다. 하드웨어 추상화 계층을 우회하지 않고 실제 바이너리를 구동함으로써 배포 직전 하드웨어 결함을 찾아냅니다. 일반 웹 서비스를 구축하는 팀이 이러한 풀 시스템 에뮬레이터를 직접 구축하는 것은 오버엔지니어링입니다. 소프트웨어와 하드웨어가 맞닿는 접점에서는 가짜 어댑터를 늘리기보다 실제 실행 파일과 동일한 바이너리를 실행하는 환경을 갖추는 편이 결함을 더 일찍 발견할 수 있습니다.


      Spark Connect와 쿠버네티스 오퍼레이터로 완성하는 제어 평면의 단순화

      분산 데이터 처리 플랫폼에서도 클러스터 내부의 상태 불일치 문제는 거대한 운영 비용을 발생시킵니다. 클러스터 관리 소프트웨어와 분산 엔진 간에 호환 레이어가 불완전하게 연결되어 있으면, 드라이버 파드가 예기치 않게 종료되었을 때 세션이 끊기거나 고아 프로세스가 클러스터 자원을 잠식하는 문제가 반복됩니다.

      Spark Connect on Kubernetes #2: Operator로 만드는 Control Plane에서는 Spark 서버 하나의 기동과 장애 복구부터, 여러 서버의 사용자 세션을 유지한 채 무중단으로 교체하는 전체 수명 주기를 쿠버네티스 오퍼레이터(Operator) 패턴으로 해결한 과정을 다룹니다.

      과거에는 스파크 클러스터를 관리하기 위해 외부 스크립트나 별도의 REST 프록시 어댑터를 두고 드라이버의 생존 여부를 주기적으로 폴링(polling)하는 방식을 자주 썼습니다. 외부 제어 루프와 쿠버네티스 내부 런타임 상태가 어긋나면 서버는 살아 있으나 세션이 끊기거나, 반대로 세션은 유효한데 컨트롤러가 파드를 강제 종료하는 불일치가 발생했습니다.

      엔지니어링 팀은 쿠버네티스의 커스텀 리소스 정의(CRD)를 기반으로 Spark Tenant Operator를 구현하여, 쿠버네티스 고유의 화해 루프(Reconciliation Loop) 안으로 스파크 서버 수명 주기를 통합했습니다. 외부 어댑터를 거치지 않고 오퍼레이터가 직접 스파크 인스턴스의 상태 변화를 감시하며 롤링 업데이트 시 기존 사용자 세션의 배출(drain)과 복구를 관장합니다.

      apiVersion: spark.indieblog.net/v1alpha1
      kind: SparkTenantServer
      metadata:
        name: analytics-spark-session-01
        namespace: data-platform
      spec:
        version: "3.5.0"
        replicas: 2
        sessionPolicy:
          preserveActiveSessionOnRollout: true
          sessionTimeoutSeconds: 3600
        driverTemplate:
          cores: 2
          memory: "4Gi"
        executorTemplate:
          cores: 4
          memory: "8Gi"
          instances: 10
      

      이 방식은 지금 써볼 만합니다. 사내 분석 플랫폼을 쿠버네티스 기반으로 전환하고 있는 데이터 인프라 조직이라면, 외부 모니터링 데몬을 덕지덕지 이어 붙이는 대신 선언형 CRD와 오퍼레이터 컨트롤러로 제어 평면을 통합하는 것이 안정적입니다. 테넌트가 늘어나도 플랫폼 엔지니어의 수동 개입 없이 장애 복구와 무중단 교체가 보장됩니다.


      호환성 부채를 줄이고 단일 표준으로 수렴하기 위한 기준

      세 가지 사례는 모두 호환성과 추상화라는 이름으로 유지되던 임시 계층을 제거하고 실제 구동 표준을 통일했다는 공통점을 가집니다.

      토스뱅크는 CJS와 ESM의 이중 지원을 포기함으로써 패키지 상태의 이중화 위험을 없앴습니다. Oxide는 하드웨어 목킹 인터페이스를 포기하고 실제 펌웨어 바이너리를 온전히 실행하는 Voxel 에뮬레이터를 택했습니다. 쿠버네티스 기반 스파크 플랫폼은 외부 수명 주기 프록시를 걷어내고 쿠버네티스 네이티브 컨트롤 루프와 스파크 세션을 직결했습니다.

      여기부터는 DEVOTEE의 해석입니다. 시스템 아키텍처에서 하위 호환성을 위한 번역 계층을 유지하는 것은 단기적으로는 변경 비용을 낮추는 것처럼 보입니다. 시간이 흐를수록 번역 계층 자체가 런타임 버그와 상태 불일치를 유발하는 독립된 장애 지점이 됩니다. 런타임 환경과 도구 체인이 특정 표준을 소화할 수 있는 성숙도에 도달했다면, 어댑터 계층을 유지하기보다 단일 표준으로 강제 전환하는 결단이 장기적인 유지보수 비용을 낮춥니다.


      도입 전 확인할 것

      내부 라이브러리 표준화나 인프라 제어 평면 통합을 검토하고 있는 팀이라면 작업을 시작하기 전에 세 가지 요소를 점검해야 합니다.

      소비자 환경의 런타임 격차를 확인해야 합니다. 패키지를 ESM 전용으로 전환하려면 사내의 모든 서비스 빌드 파이프라인과 테스트 러너가 ESM을 온전히 파싱할 수 있는지 목록화해야 합니다. 단 하나의 레거시 백엔드 서비스가 구형 CJS 모듈 로더에 묶여 있다면 전체 전환 작업의 발목을 잡을 수 있습니다.

      테스트 환경과 프로덕션 환경의 바이너리 일치도를 평가해야 합니다. 테스트 코드에서 실제 인프라나 펌웨어를 지나치게 모의 객체로 대체하고 있다면, 그 모의 객체가 실제 런타임 결함을 얼마나 가리고 있는지 점검해야 합니다. 환경 구축 비용이 들더라도 실제 프로덕션 바이너리를 직접 실행하는 컨테이너 환경이나 에뮬레이터를 갖추는 편이 결함 발견 시점을 앞당깁니다.

      제어 루프의 소유권을 명확히 해야 합니다. 쿠버네티스 위에서 복잡한 분산 엔진을 운영할 때 외부 스케줄러와 쿠버네티스 오퍼레이터가 이중으로 파드를 감시하고 있다면 상태 경합이 발생합니다. 수명 주기 관리는 단 하나의 선언형 제어 평면에 완전히 위임해야 합니다.


      그 밖의 소식

      WP Engine의 4회에 걸친 OKR 도입 실패와 정밀성의 오류에 관한 고찰 — 원문

      영업팀의 반복적인 데이터 요청 병목을 줄이기 위한 사내 데이터 쿼리 AI 에이전트 도입기 — 원문

      인코어드의 AWS HPC 환경에서 WRF 기상 시뮬레이션 인스턴스 선택과 비용 최적화 — 원문

      등급 분류와 민간 클라우드 이용 범위가 개정되는 공공 클라우드 보안 기준 가이드라인 — 원문

      참여자의 자산 공급과 네트워크 효과를 결합한 플랫폼 비즈니스 모델 분석 — 원문

      댓글 0

      DEVOTEE를 활성화 시키면
      지금 작성한 댓글에 AI가 댓글을 달아줍니다.

      DEVOTEE 님의 최신 블로그

      더보기
      동영상 기고하기