Skip to main content

Posts

통합 회원 관리(SSO/OIDC)와 글로벌 인증 구조

통합 회원 관리(SSO/OIDC)와 글로벌 인증 구조 1. 배경 — "로그인은 하나, 서비스는 전 세계" 이전까지는 각 국가별로 별도의 회원 시스템을 운영했다. 태국, 한국, 중국, 말레이시아의 서비스가 모두 다른 사용자 DB를 가졌고, 글로벌 공급사는 국가별로 중복 계정을 등록해야 했다. 로그인 토큰도 서버별로 상이해 SSO(Single Sign-On)가 불가능했다. 이 구조가 만들어낸 문제는 명확했다. 하나의 공급사가 태국과 한국에서 모두 활동하려면 두 개의 계정이 필요하고, 각각 별도로 로그인해야 하며, 프로필 정보도 따로 관리해야 한다. 5개 플랫폼(Trade, Logistics, Workflow, Document AI, Delivery)이 각자 독립된 인증 시스템을 가지고 있다면 파트너 수가 4,300개를 넘어선 시점에서 운영은 사실상 불가능하다. 6월에 이 구조를 완전히 개편하며, 단일 로그인으로 모든 국가의 모든 서비스에 접근할 수 있는 통합 인증 체계를 구축했다. 2. 왜 SSO와 OIDC인가 SSO는 한 번의 로그인으로 여러 서비스에 접근하는 것을 의미한다. OIDC(OpenID Connect)는 OAuth 2.0 위에 구축된 인증 표준으로, 사용자의 신원을 검증하고 ID Token을 발급하는 프로토콜이다. REINDEERS가 OIDC를 선택한 이유는 세 가지다. 첫째, 각 플랫폼이 독립적으로 배포되더라도 하나의 인증 서버만 바라보면 된다. 둘째, Google, LINE, WeChat, Kakao 등 소셜 로그인 제공자와의 연동이 표준화된 방식으로 가능하다. 셋째, ID Token의 서명 검증만으로 인증 상태를 확인할 수 있어 ...

데이터 품질관리, CI/CD 검증, Telegram 알림 및 Rollback 구조

데이터 품질관리, CI/CD 검증, Telegram 알림 및 Rollback 구조 1. 배경 — "데이터가 코드다" REINDEERS 플랫폼은 다국가·다통화 데이터를 실시간으로 처리한다. 그러나 시스템이 복잡해질수록 코드보다 더 위험한 것은 "데이터 불일치"였다. 6월 초, PO 테이블의 통화 코드 누락으로 결제 금액 계산 오류가 발생했다. 개발 로직에는 문제가 없었지만, 데이터 무결성이 깨져 있었다. 코드는 테스트를 통과했지만 데이터는 검증 대상에 포함되지 않았던 것이다. 이 사건을 계기로 REINDEERS는 데이터 품질관리(DQM, Data Quality Management)를 DevOps 파이프라인에 통합하기로 했다. 코드가 통과해야 하는 테스트가 있듯이, 데이터도 통과하지 못하면 배포되지 않는 구조를 만들겠다는 것이 핵심 원칙이었다. CI/CD 파이프라인에 데이터 검증 단계를 삽입하고, 실패 시 자동 차단과 Telegram 알림, 그리고 원격 Rollback까지 연결하는 전체 흐름을 설계했다. 2. 품질관리 체계의 원칙 모든 품질검증은 SQL 기반 선언형 규칙으로 정의된다. 코드를 수정할 필요 없이 규칙만 등록하면 즉시 적용된다. 검증은 CI/CD 파이프라인 단계에서 자동으로 실행된다. 수동 확인은 존재하지 않는다. 오류 발생 시 Telegram으로 실시간 보고된다. 운영자는 어디서든 모바일로 상황을 파악한다. Telegram 명령을 통해 승인·롤백을 원격 수행할 수 있다. CLI나 서버 접속이 필요 없다. 이 네 가지 원칙이 적용되면서, "사람이 직접 확인하는 검증"은 사라졌다. 시스템이 데이터의 정합성을 자동으로 감시하고, 문제...

i18n·다국어·다통화 엔진 및 DTS/Redis 일관성 구조

i18n·다국어·다통화 엔진 및 DTS/Redis 일관성 구조 1. 문제 정의 — "언어, 통화, 그리고 시간" 글로벌 플랫폼을 설계할 때 가장 어려운 점은 단순히 번역이 아니다. 사용자의 언어와 통화, 그리고 거래가 발생하는 시간대(timezone)가 동시에 일관성을 가져야 한다 는 것이다. 태국 바이어가 THB(바트)로 주문을 생성하고, 한국 공급사가 KRW(원화) 기준으로 송장을 발행하면, 모든 계산은 "실제 결제일 환율"을 기준으로 변환되어야 한다. 시스템은 태국어·영어·한국어·중국어로 각각 동일한 상품 정보를 제공해야 하며, 어떤 리전에서 접속하더라도 동일한 데이터가 지연 없이 노출되어야 한다. 이 복잡성의 핵심은 세 가지 축이 서로 연결되어 있다는 점이다. 언어가 바뀌면 표시할 텍스트가 달라지고, 통화가 바뀌면 가격 계산 로직이 달라지며, 리전이 바뀌면 캐시 원본 위치가 달라진다. 하나라도 어긋나면 바이어가 보는 가격과 공급사가 받는 정산액이 불일치하는 사고로 이어진다. REINDEERS는 6월에 i18n + Currency + DTS + Redis 통합 구조를 완성하여 이 세 축을 하나의 파이프라인으로 묶었다. 2. i18n 엔진 — 언어 데이터의 추상화와 저장 전략 REINDEERS의 다국어(i18n) 엔진은 단순한 문자열 번역이 아니다. 언어 데이터는 모든 비즈니스 엔터티(Product, Category, Notice 등)와 독립적으로 관리된다. 상품 테이블에 직접 name_th , name_ko 컬럼을 두는 방식은 언어가 추가될 때마다 스키마를 변경해야 하므로 확장성이 없다. 대신 i18n_text 테이블을 도입하여 키-값 기반으로 모든 다국어 텍스트를 관리한다. CRE...

글로벌 DB 레이어링과 데이터 구조 재설계

1. 데이터 구조 개편의 배경 기존 구조는 AWS 싱가포르 단일 리전에 집중되어 있었고, 데이터 테이블은 약 50~60개 수준이었다. 문제는 명확했다. 각국의 통화, 언어, 세율, 운송 단가 등이 모두 하드코딩 되어 있었고, 국가별 운영 정책에 맞춘 데이터 확장이 사실상 불가능했다. 특히, PO(구매 주문)와 Invoice(송장) 간 데이터 일관성이 보장되지 않아, 회계/정산 단계에서 오류가 빈번하게 발생했다. 예를 들어, 태국 바트화로 생성된 PO에 한국 원화 기준의 환율이 잘못 적용되거나, 말레이시아 링깃의 세율 계산에 태국 VAT 7%가 적용되는 경우가 있었다. 이런 문제들은 코드 레벨의 패치로는 해결할 수 없었다. CEO가 제시한 요구사항은 간단했지만 깊었다. "데이터의 출발점이 한국이 아니라, 세계 어디서든 동일한 구조 로 작동해야 한다." 이 한 문장이 전체 리디자인의 기준이 되었다. REINDEERS는 한국, 태국, 말레이시아, 중국 4개국에 법인을 두고 있고, 4,300개 이상의 파트너사가 이 플랫폼을 사용한다. 바이어 2,500개 이상, 공급사 1,800개 이상, 포워더 30개 이상이 각기 다른 통화와 언어로 거래를 진행하는 상황에서, 단일 국가 기준의 데이터 구조는 한계에 도달해 있었다. 2. 6-Layer Architecture: 데이터의 논리적 분리 DB 구조는 기능적 목적에 따라 6개의 계층으로 나뉘었다. 각 계층은 물리적으로 같은 MySQL 인스턴스 내에 존재하지만, 논리적으로는 독립된 스키마로 분리되어 있다. 이 분리의 핵심 원칙은 "변경 빈도가 다른 데이터는 같은 계층에 두지 않는다"는 것이었다. ...

개발 환경과 문화의 표준화 — 코드, 협업, 그리고 사람의 일하는 방식

1. 개발 환경의 목표 — '사람 없이도 일관된 결과' REINDEERS의 개발 환경은 단 하나의 목표를 갖고 설계되었다. "사람이 없어도 코드 품질과 배포 결과가 동일해야 한다." 이를 위해 모든 개발 환경은 완전히 동일한 컨테이너 기반으로 세팅되었고, 환경 편차나 로컬 의존성 문제는 제거되었다. 개인의 환경은 존재하지 않으며, 모든 환경은 dev , staging , production 세 단계로 통합 관리된다. 이 목표가 생긴 배경이 있다. 팀이 네 번 교체되면서 매번 겪었던 문제가 "내 PC에서는 되는데 서버에서는 안 된다"였다. 운영체제 버전, 런타임 버전, 로컬에 설치된 라이브러리의 차이가 빌드 결과의 불일치를 만들어냈다. 특히 4개국(태국, 한국, 중국, 말레이시아)에 분산된 개발자들이 각자 다른 환경에서 작업하면 이 문제는 기하급수적으로 커진다. 컨테이너화된 개발 환경은 이 문제를 원천적으로 해결한다. 누가, 어디서 빌드하든 결과가 동일하다. 표준 개발 환경 구조 # Dockerfile (공용 개발환경) FROM node:20-bullseye RUN apt-get update && \ apt-get install -y python3 python3-pip vim curl git && \ pip install pre-commit flake8 WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . CMD ["npm", "run", "dev"] 모든 개발자는 동일한 컨테이너 이미지를 사용하며, 코드 수정 시 Dr...

데이터와 자동화 — 요구조건을 만족시키는 '새로운' 플랫폼만이 해답이었다

1. 요구조건에서 시작한 '새 플랫폼' 선언 Quote → PO → Invoice → Delivery → Settlement 전체 흐름이 단일 데이터 체인 으로 연결될 것 국가/언어/통화/세율/물류 규칙을 데이터 레이어 에서 통합 관리할 것 데이터 변화가 이벤트(AMQP) 를 통해 실시간 전파되고 캐시와 UI가 자동 갱신될 것 사람이 문서/툴을 조작하지 않아도 AI 워크플로우 가 명세→스키마→API→배포를 자동 생성할 것 이 요구는 곧 플랫폼의 재정의 였다. "코드가 데이터를 설명"하던 과거에서, " 데이터가 코드를 지배 하는" 구조로의 전환. 이 관점에서 전 계층을 다시 설계했다. 이 요구조건이 왜 기존 시스템으로는 충족될 수 없었는지를 구체적으로 짚어보자. 기존 시스템에서 견적(Quote)과 주문(PO)은 별개의 데이터 저장소에 관리되고 있었다. 견적서에 적힌 단가와 실제 주문에 반영된 단가가 일치하는지 확인하려면 두 개의 테이블을 수동으로 조인해야 했고, 그 과정에서 환율 변환 시점이 달라 금액 불일치가 발생하는 경우가 잦았다. 4개국(태국, 한국, 중국, 말레이시아)의 거래에서 THB, KRW, CNY, MYR, USD라는 5개 통화가 오가는 환경에서 이런 불일치는 단순한 기술 버그가 아니라 비즈니스 신뢰 문제였다. 2. 기존 시스템이 막혀 있던 구체적 지점들 패치로는 해결할 수 없었던 구조적 한계를 정리하면 다음과 같다. 다국어/다통화 하드코딩: i18n 텍스트가 프런트엔드 컴포넌트에 직접 삽입되어 있었다. 새 언어를 추가하려면 수백 개 파일을 수정해야 했다. 단일 리전 종속: 모든 데이터가 싱가포르 리전에 있어서, 태국 바이어와 한국...

MCP 아키텍처 설계기 — 글로벌 인프라의 뼈대를 세우다

MCP 아키텍처 설계기 — 글로벌 인프라의 뼈대를 세우다 2025년 5월, REINDEERS 플랫폼의 인프라 재설계가 본격적으로 시작되었다. 4월 한 달 동안 우리는 기존 시스템을 전면 점검했고, AWS 싱가폴 리전에 산재된 비효율적인 구조를 확인했다. CI/CD는 단순 스크립트 수준에 머물렀고, 운영 스테이지도 명확히 구분되지 않았다. 소스 관리조차 통합되지 않아 빌드가 환경마다 달랐다. 결국 우리는 아키텍처를 완전히 다시 세워야 했다. MCP가 무엇인가 — 아키텍처적 정의 MCP는 Multi-Commerce Platform의 약자다. 단일 서비스가 아니라 5개의 독립 플랫폼이 하나의 메시지 브로커를 중심으로 연결된 구조를 가리킨다. 각 플랫폼은 고유한 도메인을 담당하면서도, 무역 거래의 전체 흐름 안에서 하나의 시스템처럼 동작해야 한다. 5개 플랫폼이 독립적으로 동작하면서도 거래 데이터를 일관되게 공유해야 하는 요구사항은 기존의 모놀리식 구조로는 해결할 수 없었다. 견적 플랫폼에서 생성된 PQ(Price Quotation)가 공급사 플랫폼의 QA(Quote Answer)와 연결되고, 이것이 다시 QB(Quote Back) - PO(Purchase Order) - DO(Delivery Order) - FWD(Forwarding) - Pay - Settlement으로 이어지는 무역 거래의 흐름은 각 플랫폼 간의 정교한 이벤트 전달 없이는 성립하지 않는다. 왜 이벤트 드리븐인가 — Request-Response를 버린 이유 초기 설계에서 플랫폼 간 통신을 REST API 기반 동기 호출로 구현하는 안을 검토했다. 거래 플랫폼이 공급사 플랫폼의 API를 직접 호출하고, 응답을 받아 다음 단계로 진행하는 방식이다. 이 구조의 문제는 무역 거래의 특성에서 드러난다. PO가 확정되면 동시에 여러 일이 발생해야 한다. 공급사에 생산 지시가 전달되고, 포워딩 플랫폼에서 물류 견적 준비가 시작되며, 결제 플랫폼에서 선수금 요청이 생성되고,...