토스

72 개의 포스트

toss.tech

태그로 필터

toss5분 읽기큐레이션 요약

AI에게 투자정보를 말하게 하기까지

LLM을 활용한 금융 투자 정보 서비스의 핵심은 문장을 잘 생성하는 데 있지 않고, 생성 전후의 근거 선별·검증·관찰 체계를 설계하는 데 있습니다. 토스증권은 이를 위해 세 가지 관문을 제시합니다. 즉, 말할 정보를 고르고, 생성 과정을 통제하며, 결과를 평가 가능한 구조로 만드는 것입니다. ## 금융 투자 정보가 일반 요약보다 어려운 이유 - **적시성**: 시장 상황은 빠르게 변하므로 늦은 설명은 부정확한 정보가 될 수 있습니다. - **정확성**: 기사에 기업명이 등장했다는 사실만으로 해당 기업의 주가 변동을 설명할 수 없습니다. - 자회사 관련 내용인지 - 유사한 이름의 다른 기업인지 - 단순 홍보성 기사인지 구분해야 합니다. - **검증 가능성**: 모든 설명에 근거를 남기고, 결과를 평가하며, 오류 발생 시 재현할 수 있어야 합니다. - **비정상성**: 실적 시즌, 금리 이벤트, 선거, 지정학적 이슈 등에 따라 데이터 분포와 시장 반응이 달라집니다. ## LLM과 에이전트의 불확실성 - LLM은 비정형 텍스트를 자연어로 재구성하는 데 강하지만, 근거가 부족하면 유창한 오답을 생성할 수 있습니다. - 에이전트 구조에서는 다음과 같은 오류가 여러 단계로 전파될 수 있습니다. - 검색 단계에서 잘못된 근거 선택 - 툴 호출 결과의 오해 - 이전 단계의 잘못된 상태를 다음 판단에 사용 - 따라서 투자 정보 서비스에서는 에이전트의 자율성을 무조건 확대하기보다, 필요한 부분은 제한하고 생성 전후의 통제를 강화해야 합니다. ## 첫 번째 관문: 말할 정보 고르기 ### 입수 단계에서 메타데이터 구축 - 뉴스·공시·재무 데이터를 수집할 때 BERT 기반 분류 모델로 미리 분류합니다. - 데이터에는 다음과 같은 정보를 함께 저장합니다. - 산업·시장·콘텐츠 유형 등의 `taxonomy_tags` - 관련 기업인 `related_entities` - 벡터 검색을 위한 `embedding` - 검색 시점에 매번 분류하는 대신, 데이터가 들어올 때부터 검색과 검증에 필요한 구조를 갖춥니다. ### 후보를 넓게 검색한 뒤 단계적으로 축소 - 하이브리드 리트리버로 후보를 넓게 확보해 재현율을 우선합니다. - 이후 다음 절차로 부적절한 정보를 제거합니다. - **중복 제거**: 의미 유사도 기반으로 같은 이벤트를 클러스터링하고 대표 출처만 남깁니다. - **리랭킹·필터링**: 기업 주가 움직임과의 직접적 관련성을 기준으로 순위를 조정합니다. - **설명 유형 분류**: 실적, 가이던스, 기업 행동 등 주요 설명 패턴을 분류합니다. - **실패 사유 분류**: 광고성, 홍보성, 근거 부족 등의 라벨을 붙여 필터링합니다. - 최종 근거는 다음 순서로 배치합니다. - 무슨 일이 있었는가 - 대상 기업과 어떻게 연결되는가 - 주가 방향과 근거의 방향성이 일치하는가 - 근거가 충분하고 최신인가 이 과정은 LLM에 전달할 정보를 압축하고, 비즈니스 요구에 맞게 배치하는 **컨텍스트 엔지니어링**입니다. ## 두 번째 관문: 생성 과정 통제하기 ### 절차형 태스크 그래프 - 검색, 관련성 판단, 중복 제거, 근거 구성, 응답 생성 등을 독립된 단계로 나눕니다. - 각 단계의 입력·출력 스키마를 명확히 정의하면 다음 효과가 있습니다. - 단계별 디버깅과 평가 가능 - 비용과 레이턴시 예측 - 실패 지점 추적 - 단계별 폴백 설계 ### 자율형 에이전트와 절차형 오케스트레이션의 구분 - 탐색 과정 자체가 중요한 업무에는 자율형 에이전트가 적합합니다. - 투자 아이디어 발굴 - 시장 이벤트의 잠재 시나리오 탐색 - 응답 형식과 판단 절차가 명확한 업무에는 절차형 그래프가 유리합니다. - 특정 기업의 주가 등락 원인 설명 - 정해진 근거 검증과 방향성 판단 - 긴 ReAct 루프는 툴 호출, 토큰, 레이턴시와 실행 경로를 늘리므로 제품 요건이 명확할 때는 과도할 수 있습니다. - 이 경우 LLM은 검색과 판단을 모두 자율적으로 수행하기보다, 요약·재작성·근거 기반 설명에 집중시키는 편이 안정적입니다. ### 절차형 그래프의 재사용성 - 절차형 그래프는 단순한 운영 안정화 수단을 넘어 다른 에이전트가 호출할 수 있는 기능 인터페이스가 됩니다. - 예를 들어 다음과 같은 입력과 출력의 도구로 제공할 수 있습니다. - 입력: 종목, 주가 방향, 시간 범위 - 처리: 검색 → 관련성·방향성 판단 → 중복 제거 → 근거 정렬 → 설명 생성 - 출력: 설명, 근거 목록, 추론 유형 등 - 이렇게 구성하면 상위 에이전트가 복잡한 절차를 직접 계획하지 않고 검증된 기능을 재사용할 수 있습니다. ## 세 번째 관문: 평가 가능한 결과 만들기 ### 범주형 루브릭과 구조화된 출력 - 자연어 답변만 생성하지 않고, 답변과 함께 이벤트 유형·실패 사유 등의 분류값도 생성합니다. - 이를 통해 다음 지표를 측정할 수 있습니다. - 관련성 오탐 감소 여부 - 주가 방향성과 근거 방향성의 불일치 비율 - 부적절한 이슈의 통과율 - 정밀도, 재현율, F1-score - 시장 국면이 바뀌면 새로운 실패 유형과 이벤트 유형을 추가할 수 있도록 분류 체계를 유연하게 운영해야 합니다. - 운영 중 발견된 문제, 평가 데이터셋, 프롬프트 버전, 모델 버전을 연결해야 개선 효과를 재현하고 수치로 확인할 수 있습니다. ### 맥락 기반 Few-shot Retrieval - 고정된 Few-shot 예시는 다양한 금융 이벤트와 시장 국면을 충분히 대표하지 못합니다. - 대신 운영 샘플에 다음 정보를 저장합니다. - 원문 - 판단 결과 - 실패 유형 - 유사도 검색용 임베딩 - 새로운 판단 요청이 들어오면 유사한 과거 사례를 검색해 포지티브·네거티브 예시를 함께 프롬프트에 넣습니다. - 성공 사례와 실패 사례를 동시에 제공하면 모델이 판단의 경계와 오류 패턴을 더 잘 파악할 수 있습니다. - 실제 관련성 검증 태스크에서 재현율을 유지하면서 정확도와 정밀도가 개선되었으며, 특히 False Positive 감소에 효과적이었습니다. ## 프롬프트와 모델 학습을 넘어 필요한 것 - 금융 AI 서비스 품질은 프롬프트와 모델만으로 결정되지 않습니다. - 운영을 위해 다음 요소가 함께 필요합니다. - 적절한 임베딩 모델과 리트리빙 전략 - 별도 분류 모델을 활용한 데이터 구조화 - 근거 검증과 실패 유형 관리 - 단계별 추적 및 평가 - 시장 국면 변화에 따른 평가셋·프롬프트 업데이트 투자 정보 서비스에서는 LLM의 자율성을 최대화하기보다, 근거를 선별하고 검증하는 절차를 명확히 설계하는 것이 중요합니다. 검색·분류·검증은 통제 가능한 그래프로 구성하고, LLM은 구조화된 근거를 바탕으로 설명을 생성하도록 제한하는 방식이 안정성과 확장성을 함께 확보하는 현실적인 접근입니다.

원문 읽기(새 탭에서 열림)
toss6분 읽기큐레이션 요약

토스의 속도와 품질, 상용 도구로 충분한가 — 토션(Tossion)

토션(Tossion)은 흩어진 자동화·수동 테스트 결과와 테스트 케이스를 하나의 플랫폼에서 연결하고, QA 조직이 필요한 기능을 직접 빠르게 확장하기 위해 만든 테스트 관리 플랫폼입니다. 테스트 런에는 당시의 테스트 케이스와 판정 근거를 스냅샷으로 보존해 과거 결과의 신뢰성을 확보하고, AI·PR 분석·실기기 자동화까지 하나의 흐름으로 통합했습니다. 토스는 상용 TCM의 기능을 사용하는 데 그치지 않고, 빠른 개발 속도와 품질 기준에 맞춰 플랫폼 자체를 계속 진화시키는 것을 목표로 합니다. ## 흩어진 테스트 정보를 하나의 기록으로 통합 - 기존에는 자동화 테스트 결과, 매뉴얼 테스트 결과, 테스트 케이스와 판단 근거가 서로 다른 곳에 흩어져 과거 결과를 확인하는 데 시간이 걸렸습니다. - 토션의 기본 구조는 **프로젝트 → 스위트 → 섹션 → 테스트 케이스**이며, 섹션은 트리 구조로 관리됩니다. - 테스트 케이스는 제품 변화에 따라 수정·삭제되지만, 테스트 런은 당시 검증 내용을 보존하기 위해 별도로 축적됩니다. - 테스트 런을 생성할 때 테스트 케이스를 단순 참조하지 않고 다음 정보를 복사해 독립적인 행으로 저장합니다. - Assignee - Test Step - Description - 테스트 런의 각 행에는 상태 변경 이력이 쌓이며, 누가 언제 어떤 Version에서 어떤 판단을 했는지 확인할 수 있습니다. - 섹션을 직접 선택한 테스트 케이스에는 Type·Platform 등의 필터를 적용하지 않습니다. 명시적인 선택이 자동 조건보다 우선하기 때문입니다. ## 테스트 런의 스냅샷과 변경 이력 - 테스트 런은 **Active → Completed → Closed** 상태로 진행됩니다. - Closed 시점에 테스트 케이스, 코멘트, 자동화 결과를 스냅샷으로 저장합니다. - 이후 원본 테스트 케이스가 수정되거나 삭제되어도 종료된 테스트 런의 화면과 리포트는 변하지 않습니다. - 이를 통해 “지난달에는 무엇으로 검증했는가”라는 질문에 당시 상태 그대로 답할 수 있습니다. ## 빠른 피드백을 반영하는 협업 기능 - Assignee별로 전체 테스트 수와 남은 테스트 수를 보여 주는 진척도 차트를 제공합니다. - Status, Type, Assignee, Version, Platform, RNR, History 등 실제로 필요한 필드만 추가·유지합니다. - 여러 사용자가 같은 테스트 런을 동시에 사용할 수 있도록 다음 기능을 제공합니다. - 현재 접속 중인 사용자 아바타 표시 - 사용자별 색상 구분 - 편집 중인 테스트 케이스와 Description 잠금 - 창을 닫거나 연결이 끊기면 잠금 자동 해제 - 다른 사용자의 Status 변경을 새로고침 없이 반영 - 핵심은 기능의 규모보다 사용자 요청을 개발·배포·활용하는 시간이 짧다는 점입니다. ## 상용 TCM 대신 직접 만든 플랫폼 - 토스의 빠른 개발 속도에서는 품질 검증도 같은 속도로 변화해야 하며, 품질이 속도의 희생양이 되어서는 안 됩니다. - 상용 도구는 제공 업체가 정한 기능과 로드맵 안에서만 사용할 수 있습니다. - 토스가 필요로 한 것은 정해진 기능을 제공하는 도구가 아니라, 새로운 요구를 즉시 추가할 수 있는 플랫폼이었습니다. - 이를 기반으로 다음 기능을 직접 추가했습니다. - 릴리즈 PR 분석 - AI 기반 테스트 케이스 생성 - 실기기 회귀 테스트 실행 - 자동화 결과를 수동 테스트 케이스별로 기록 ## 릴리즈 PR 분석과 QA 범위 결정 - RC 빌드나 릴리즈 마일스톤에 포함된 PR을 모두 수집해 QA 라벨이 있는 PR과 없는 PR로 나누어 분석합니다. - QA 라벨이 있는 PR은 검증 관점을 정리하고, 라벨이 없는 PR은 정말 QA 검증이 필요 없는지 다시 확인합니다. - 분석 목적은 기능 요약이 아니라 “이번 릴리즈에서 반드시 확인해야 할 항목”을 찾는 것입니다. - QA 서버의 Agent가 토션에 등록된 작업을 주기적으로 확인하고, 작업을 받으면 서버에 로그인된 AI를 실행합니다. - 수백 개의 PR을 한 번에 처리하지 않고 여러 묶음으로 나누어 병렬 분석합니다. - AI 결과는 다음과 같은 규칙으로 검증합니다. - 화면명이나 구체적인 조건이 없는 모호한 문장 - PR 제목을 그대로 옮긴 요약 - 함수명이 그대로 남은 설명 - 재현 단계·기대 결과·실패 증상·판단 근거가 빠진 테스트 케이스 - 부적합한 결과는 AI가 다시 분석합니다. - 병합된 PR 수와 분석 결과 수를 대조해 누락된 PR이 있으면 해당 항목만 재처리합니다. - 과거 장애가 발생한 파일 목록과 이번 PR의 변경 파일을 비교해 위험도를 조정합니다. - 결과는 묶음 단위로 토션에 저장해 중단 시에도 완료된 분석을 보존하고, 재실행할 때 이미 처리한 PR은 건너뜁니다. - 최종적으로 추려진 항목은 Sprint 테스트 런의 범위와 검증 근거가 됩니다. ## AI 기반 테스트 케이스 생성 - 기능 개발 속도를 사람이 따라가기 어렵기 때문에 AI가 테스트 케이스를 생성해 토션에 등록합니다. - AI는 “자산 > 계좌 연결 > 은행 선택”처럼 경로를 출력하고, 토션이 이를 실제 섹션 트리로 변환합니다. - 기존 섹션이 있으면 재사용하고, 없으면 중간 단계를 포함해 새로 생성합니다. - 결과의 신뢰성을 확보하기 위해 세 겹의 검증을 적용합니다. 1. AI가 누락된 분기·에러 상황·경계값을 스스로 재검토 2. 표기 규칙, 테스트 케이스 번호, 화면 누락, 요구사항 반영 여부를 스크립트로 검증 3. 별도의 AI가 테스트 계획을 작성해 범위와 위험 요소, 적용할 테스트 기법을 정의 - 테스트 계획과 실제 테스트 케이스를 비교해 다음을 확인합니다. - 계획에는 있지만 테스트 케이스에 없는 항목은 누락 - 테스트 케이스에는 있지만 계획에 없는 항목은 범위 이탈 - 화면 중심으로만 테스트하면 상태 전이처럼 화면에 드러나지 않는 테스트 축을 놓칠 수 있습니다. - 따라서 테스트 계획에서 상태 전이, 경계값 등 필요한 테스트 기법을 먼저 지정하고, 테스트 케이스가 이를 모두 포함하는지 확인합니다. - AI의 토션 접근은 화면이 아닌 CLI로 제한하고, 환경 차이로 인한 설치·런타임·경로 문제를 줄이기 위해 단일 실행 파일로 배포합니다. ## 토션에서 실기기 회귀 테스트 실행 - 신규 기능은 사람이 직접 검증하고, 안정화된 테스트 케이스는 회귀 자동화 대상으로 편입합니다. - 토션의 실행 화면에서 다음 항목을 선택해 바로 테스트를 시작합니다. - 대상 기기 - 빌드 - 실행 범위 - 결과를 연결할 테스트 런 - QA 서버의 러너는 Android·iOS 실기기를 관리하며, 스스로 토션에 등록되지만 관리자 승인 전에는 작업을 받지 않습니다. - 러너는 주기적으로 연결된 기기 상태를 보고하므로 실행 가능한 기기를 화면에서 확인할 수 있습니다. - 토션이 발급한 빌드를 설치해 실행함으로써 어떤 빌드에서 나온 결과인지 명확히 유지합니다. - 전체 회귀 또는 특정 섹션만 선택해 실행할 수 있습니다. - 테스트 중에는 시나리오별 통과·실패 여부, 실행 시간, 오류 메시지가 실시간으로 기록됩니다. - 결과는 시나리오가 아니라 **스텝 단위**로 저장됩니다. - 상태 - 소요 시간 - 오류 메시지 - 해당 시점의 스크린샷 - 시나리오 단위 영상 - 실행 결과를 특정 테스트 런에 연결하면 자동화 결과가 수동 테스트 기록의 각 테스트 케이스에 직접 반영됩니다. ## 자동화 결과를 테스트 케이스와 연결 - 별도 자동화 리포트에 “200건 중 3건 실패”라고만 표시하면 어떤 수동 테스트 케이스가 실패했는지 사람이 다시 대조해야 합니다. - 이를 해결하려면 양방향 연동이 필요합니다. - 테스트 케이스를 자동화 코드로 변환 - 자동화 결과를 다시 테스트 케이스별 기록으로 저장 - 토션은 자동화 결과를 테스트 케이스 한 건 단위까지 내려보내 수동 검증 기록과 자동화 실행 결과를 같은 맥락에서 확인할 수 있도록 설계되었습니다. - 제공된 글은 이 자동화 코드 생성 기능의 상세 구현 설명 직전에서 끝납니다. 토션의 핵심은 테스트 관리, AI 분석, 테스트 생성, 실기기 자동화를 각각 분리하지 않고 하나의 테스트 런과 테스트 케이스 흐름으로 연결한 데 있습니다. 유사한 플랫폼을 구축할 때도 먼저 결과의 스냅샷·이력 보존을 설계하고, 이후 AI와 자동화를 기존 기록 구조에 연결하는 방식이 실용적입니다.

원문 읽기(새 탭에서 열림)
toss5분 읽기큐레이션 요약

모노리포 희망편, 절망의 리포가 희망의 리포로 부활하기까지 걸린 1년

토스는 100명 이상의 프론트엔드 엔지니어가 여러 제품을 개발하면서도 React 19, Next.js 15 등 동일한 개발환경을 유지하기 위해 모노리포와 의존성 카탈로그를 활용하고 있습니다. 단순히 모노리포를 사용하는 것만으로는 서비스별 의존성 버전 파편화와 느린 설치 속도 문제를 해결할 수 없었기 때문에, 핵심 라이브러리 버전을 표준화하는 카탈로그 전략을 도입했습니다. 그 결과 의존성 규모와 설치 시간이 크게 줄었고, 플랫폼 변경과 최신 기술 도입도 더 안전하고 빠르게 진행할 수 있게 되었습니다. ## 모노리포가 제공한 개발 일관성 - 토스의 모바일 제품 코드는 하나의 모노리포로 통합되어 있습니다. - 모든 서비스가 React, Next.js, TypeScript, 번들러, Linter 등 유사한 버전을 사용하도록 관리되었습니다. - 이를 통해: - React Concurrent Mode, React Server Components 같은 최신 기능을 여러 서비스에서 활용할 수 있습니다. - 서비스 간 공통 코드와 플랫폼 라이브러리를 쉽게 공유할 수 있습니다. - 플랫폼 변경사항을 전체 서비스에 일관되게 전파할 수 있습니다. - 제품이 많아도 동일한 개발환경을 유지해 사용자 경험과 개발자 경험을 함께 개선하는 것이 목표였습니다. ## 모노리포만으로 해결되지 않은 문제 - 서비스마다 React와 각종 라이브러리의 버전이 달라 의존성 트리가 복잡했습니다. - 오래된 서비스는 낡은 개발환경을 계속 사용하게 되어 개발 서버 속도와 API 사용성에서 큰 차이가 났습니다. - 의존성 종류와 버전이 많아 설치에 캐시가 있어도 1분 이상 걸리는 경우가 있었습니다. - 플랫폼 팀은 다양한 React 및 라이브러리 조합을 모두 테스트해야 했기 때문에 공통 라이브러리 변경이 어려웠습니다. - 서비스 개발자도 업데이트 후 문제가 발생할 가능성을 우려해 플랫폼 라이브러리 업데이트를 기피했습니다. - 결과적으로 오래된 의존성이 고착되고, 서비스와 플랫폼 양쪽의 유지보수 비용이 커졌습니다. ## 폴리리포의 한계 - 모노리포를 여러 개의 독립적인 리포지토리로 나누면 각 저장소의 의존성과 설치 부담은 줄어듭니다. - 그러나 다음 문제는 오히려 남거나 심해질 수 있습니다. - 서비스별 개발환경 파편화 - 공통 코드 공유와 업데이트 비용 증가 - 서비스마다 다른 개발 경험 - 플랫폼 변경사항의 일관된 전파 어려움 - 토스는 지속적으로 플랫폼을 유지보수하고 최신화해야 하므로, 폴리리포보다는 기존 모노리포의 의존성 문제를 해결하는 방향을 선택했습니다. ## 핵심 해결책: 의존성 버전 표준화 - 가장 근본적인 문제를 “서비스마다 핵심 의존성 버전이 모두 다르다”는 점으로 정의했습니다. - React, 컴포넌트 라이브러리(TDS), 상태 관리 라이브러리(Jotai), TypeScript, ESLint 등 약 10~20개의 주요 라이브러리를 표준화 대상으로 삼았습니다. - 핵심 의존성 버전을 통일하면: - 설치해야 할 의존성의 종류와 개수가 줄어듭니다. - 모든 서비스에서 비슷한 개발 경험을 제공합니다. - 플랫폼 라이브러리의 테스트 환경이 단순해집니다. - Breaking change에 대응하는 코드 변환 스크립트나 호환성 레이어를 만들기 쉬워집니다. - 서비스 개발자가 검증된 최신 라이브러리로 업데이트할 유인이 커집니다. - 대부분의 개발자는 “React가 필요하다”고 결정할 뿐 특정 버전을 직접 선택할 필요는 없다는 점도 표준화의 근거가 되었습니다. ## 카탈로그를 통한 버전 관리 - 토스는 서비스에서 권장하는 표준 라이브러리 버전 집합을 **카탈로그(Catalog)**라고 정의했습니다. - pnpm이나 Yarn의 카탈로그 기능을 사용해 모노리포의 공통 버전을 선언합니다. ```yaml catalog: react: ^18.2.0 jotai: ^2.18.1 ``` - 각 서비스는 `catalog:` 프로토콜로 버전을 참조합니다. ```json { "dependencies": { "react": "catalog:", "jotai": "catalog:" } } ``` - 안정 버전과 실험 버전을 별도 카탈로그로 관리할 수도 있습니다. ```yaml catalogs: stable: react: ^18.2.0 jotai: ^2.18.1 beta: react: ^19.1.0 jotai: ^2.20.1 ``` - 이후 서비스는 `catalog:stable`처럼 특정 카탈로그를 선택할 수 있습니다. - React, Next.js, TypeScript, TDS, 토스 앱 SDK 등 핵심 개발 라이브러리부터 카탈로그에 편입했습니다. ## 카탈로그 도입과 운영 방식 - 카탈로그 패키지는 릴리즈 전에 주요 사용 사례를 테스트 페이지로 검증했습니다. - 신규 서비스는 최신 카탈로그를 자동으로 참조하도록 스캐폴딩했습니다. - 개발자가 `yarn add`로 직접 패키지를 추가해도 카탈로그 버전을 사용하도록 했습니다. - 실수로 카탈로그를 사용하지 않는 경우를 CI에서 자동 검출했습니다. - 기존 서비스는 코드 오너와 함께 의존성을 카탈로그 참조 방식으로 일괄 마이그레이션했습니다. - 카탈로그 버전을 변경할 때는 기존 카탈로그를 직접 수정하지 않고 새 버전을 발행했습니다. - 일부 서비스에서 먼저 검증 - 안정성 확인 - 전체 서비스가 새 카탈로그로 수동 마이그레이션 - 업그레이드 비용을 낮추기 위해 코드 수정 스크립트와 AI Skill도 제공했습니다. ## 카탈로그 적용 후의 성과 - 서비스별 의존성 버전이 통일되면서 전체 의존성 규모가 크게 감소했습니다. - Yarn PnP의 `.pnp.cjs` 파일 크기: - 96MB → 15MB - 약 84% 감소 - 개발 서버 실행 시간: - 26.7초 → 20.3초 - 약 23% 개선 - 전체 의존성 설치 시간: - 528.4초 → 249.9초 - 약 52% 감소 ## 검증된 의존성과 구조적 개선 - 카탈로그에 포함된 패키지는 최소 한 개 이상의 서비스에서 동작을 검증해야 하므로 사용 신뢰도가 높아졌습니다. - 패키지 간 의존성도 엄격하게 관리할 수 있게 되었습니다. - 예를 들어 A 패키지가 B의 v1에 의존하는데 서비스가 B의 v2를 사용하는 식의 불일치를 예방할 수 있습니다. - 어떤 서비스가 어떤 버전을 사용하는지 파악하기 쉬워져 패키지 개발자가 대규모 구조 개선을 추진하기 수월해졌습니다. - 그 결과 RSC, TypeScript 7, Rspack, E2E 테스트 같은 급진적인 기술 개선도 비교적 빠르고 안정적으로 도입할 수 있었습니다. - 플랫폼 패키지의 개선사항이 서비스에 전달되는 경로가 표준화되어, 서비스가 최신 플랫폼의 혜택을 더 빠르게 받을 수 있는 기반도 마련되었습니다. ## 실용적인 결론 모노리포의 효과를 극대화하려면 저장소를 하나로 합치는 것만으로는 부족합니다. 핵심 의존성의 버전을 카탈로그로 표준화하고, CI 검증·자동 마이그레이션·단계적 릴리즈를 함께 운영해야 서비스 간 일관성, 설치 성능, 플랫폼 업데이트 속도를 동시에 개선할 수 있습니다.

원문 읽기(새 탭에서 열림)
toss4분 읽기큐레이션 요약

DS와 MLE가 함께 일하는 법

토스뱅크는 DS와 MLE 사이의 역할 경계를 사람이나 파일이 아닌 명시적인 인터페이스로 정의하면서 ML 모델 배포 협업을 개선했습니다. 노트북 전달 방식에서 `.py` 파일 공유를 거쳐, 전처리·추론·후처리를 구현한 모델 패키지를 `pip install`로 배포하는 구조로 발전했습니다. 여기에 모노레포, 공통 추상화, CI, AI 코딩 스타일 규칙을 결합해 배포 속도와 일관성을 높였습니다. ## 노트북 전달 방식의 한계 - Phase 0에서는 DS가 주피터 노트북에서 학습과 추론 코드를 모두 작성하고, MLE가 이를 바탕으로 서빙 코드를 처음부터 다시 작성했습니다. - 라이브러리, 설정 파일, 소스 코드가 흩어져 있어 실행 환경을 재현하기 어려웠습니다. - 노트북에서 동작하던 전처리나 설정을 MLE가 다르게 해석하는 문제가 발생했습니다. - 모델 수가 늘어날수록 파일 요청과 커뮤니케이션 비용이 크게 증가했습니다. - 역할의 경계가 코드가 아니라 사람 사이에 있었기 때문에 책임과 작업 범위가 불명확했습니다. ## `.py` 파일로 추론 로직 분리 - Phase 1에서는 DS가 노트북에서 핵심 추론 로직을 별도의 `.py` 파일로 분리했습니다. - 노트북은 학습과 실험에 집중하고, 실제 모델 로직은 코드 파일로 관리했습니다. - MLE 리뷰와 CI 검증을 거치도록 하면서 DS의 의도를 더 정확히 보존할 수 있었습니다. - 그러나 모델마다 함수 이름이 `predict()`, `run()`, `inference()` 등으로 달라 인터페이스가 통일되지 않았습니다. - 서빙 환경으로 코드를 옮길 때 환경 차이로 수정이 필요했고, 로깅·메트릭·에러 처리를 공통으로 적용하기도 어려웠습니다. - 노트북에서 전역 설정을 변경한 코드가 여러 모델이 실행되는 서빙 프로세스에 영향을 주는 문제도 있었습니다. ## 인터페이스를 통한 역할 분리 - Phase 2에서는 `commons-ml-model` 패키지에 모델의 표준 구조를 정의했습니다. - 추상화 클래스가 다음 세 가지 인터페이스를 제공합니다. - `pre_process`: 입력 데이터 전처리 - `inference`: 모델 추론 - `post_process`: 결과 후처리 - DS는 위 메서드의 구현체를 작성하고 모델을 하나의 패키지로 배포합니다. - MLE는 해당 패키지를 설치해 서비스에 연결하므로 코드를 직접 복사하거나 재작성하지 않습니다. - 추상화 클래스가 추론 전후에 공통으로 다음 기능을 처리합니다. - 요청 추적을 위한 `trace_id` - 추론 시작·완료 로그 - 실행 시간 측정 - 메트릭 기록 - 결과적으로 DS는 모델 동작에 집중하고, MLE는 서비스 인프라와 운영 기능을 담당하게 됐습니다. - 공통 관측 기능을 추상화 클래스 한 곳에서 수정하면 모든 모델에 일괄 적용할 수 있습니다. ## 모노레포와 `uv` 워크스페이스 - 여러 모델 패키지와 공통 추상화 패키지를 하나의 저장소에서 관리했습니다. - `uv` 워크스페이스를 사용해 DS와 MLE가 같은 코드베이스에서 작업하고 리뷰할 수 있도록 했습니다. - 공통 인터페이스 변경과 모델 패키지 수정이 하나의 PR에서 함께 이뤄졌습니다. - CI, 버전 관리, 배포 정책을 저장소 단위로 통일할 수 있었습니다. - 공통 패키지 변경이 모든 모델에 영향을 줄 수 있다는 위험도 존재합니다. - 모델과 패키지가 늘면서 빌드가 느려졌고, Poetry에서 `uv`로 전환해 빌드 속도를 약 3~5배 개선했습니다. ## AI 시대의 코드 스타일 통일 - AI가 코드를 작성하면서 같은 기능도 예외 처리, 네이밍, enum 사용 방식 등이 사람마다 달라지는 문제가 생겼습니다. - 인터페이스가 같더라도 코드의 세부적인 작성 방식이 달라 리뷰 비용이 증가했습니다. - 팀 규칙 모음인 `pfmls-stylepack`을 도입해 AI가 코드 작성 단계부터 팀 컨벤션을 따르도록 했습니다. - Hook을 활용해 네이밍, 예외 처리, 고정값과 enum 사용 기준 등을 자동 적용했습니다. - 규칙이 적용된 코드에는 그 이유를 표시해 리뷰어가 변경 의도를 쉽게 파악하도록 했습니다. - 협업 표준을 두 층위로 나눴습니다. - 구조 표준화: 인터페이스로 담당 범위와 코드 형태 통일 - 스타일 표준화: 컨벤션으로 구현 방식과 코드 결 통일 ## 도입 과정에서 얻은 교훈 - 인터페이스는 너무 엄격하면 DS의 모델별 커스터마이징을 막고, 너무 느슨하면 다시 구현 방식이 제각각이 될 수 있습니다. - 초기에는 가이드 문서를 제공하고 DS와 MLE가 첫 모델을 페어로 함께 만드는 방식이 효과적입니다. - 공통 라이브러리는 한 번의 수정으로 전체 모델에 개선을 적용할 수 있지만, 반대로 전체 모델에 장애를 전파할 수도 있습니다. - 기존 모델 패키지를 참고 코드로 제공하면 새로운 구성원의 러닝 커브를 줄일 수 있습니다. - AI 활용이 늘어날수록 기능의 책임뿐 아니라 코드 작성 방식까지 명시적으로 관리해야 합니다. 실무적으로는 모델 배포 과정에서 “누가 무엇을 한다”를 문서로만 정의하기보다, 추상화 클래스와 패키지 구조로 강제하는 것이 효과적입니다. 먼저 전처리·추론·후처리 같은 최소 인터페이스를 정하고, 공통 로깅·메트릭·CI를 그 바깥에 배치하는 방식부터 시작하는 것을 추천합니다.

원문 읽기(새 탭에서 열림)
toss5분 읽기큐레이션 요약

LLM은 똑똑한데, 왜 우리 회사 일은 모를까

LLM이 사내 질문에 정확히 답하려면 단순히 관련 문서를 검색하는 것만으로는 부족하다. 문서·코드·메신저의 최신성, 상충 여부, 실제 구현과의 일치 여부까지 관리하는 신뢰 가능한 컨텍스트 계층이 필요하며, Topic은 이를 구축하기 위한 시스템이다. Topic은 원본을 의미 단위로 정규화하고, 개념과 관계를 연결한 뒤, 변경된 부분만 선택적으로 검증한다. ## 검색만으로는 신뢰를 보장할 수 없는 이유 - 검색은 질문과 관련된 텍스트를 찾아줄 뿐, 해당 정보가 최종 결정인지 판단하지 못한다. - 문서, 미팅, 코드가 서로 다른 정책을 설명할 수 있다. - 메신저 논의가 실제 결론인지, 문서가 오래된 것인지, 코드 변경이 의도된 것인지 추가 판단이 필요하다. - 에이전트가 각자 원문을 검색하면 자료 선택과 충돌 해석이 달라져 답변 일관성이 떨어진다. - Topic은 출처, 관계, 최신성, 충돌 상태를 공통 계층에서 관리해 사람과 LLM이 같은 근거를 사용하도록 한다. ## 신뢰를 구성하는 여섯 가지 축 - **Granularity**: 독립적으로 관리할 수 있는 적절한 크기와 의미의 컨텍스트인지 판단한다. - **Faithfulness**: 컨텍스트의 주장이나 설명이 원문 근거로 뒷받침되는지 확인한다. - **Staleness**: 정보가 현재도 유효한지 검사한다. - **Canonicality**: 서로 다른 이름이나 표현이 같은 대상을 가리키는지 판단한다. - **Consistency**: 여러 출처의 내용이 서로 양립하는지 확인한다. - **Coverage**: 중요한 근거와 관점이 누락되지 않았는지 살핀다. - 모든 문제를 하나의 신뢰도 점수로 합치지 않고, 규칙·LLM·사람 검토를 각각 적합한 판단에 사용한다. ## Ingest: 출처별 의미 단위를 보존한 정규화 Topic은 문서·코드·메신저의 원본을 공통 `ContentUnit`으로 변환한다. - 주요 필드: - `source_type`: document, code, messenger - `unit_type`: 문서 섹션, 메신저 스레드 등 - `source_uri`: 원문으로 돌아가는 주소 - `content_hash`: 변경 감지용 해시 - `created_at_src`, `updated_at_src` - 출처별 식별자와 구조를 담은 `metadata` - 공통 형식은 후속 추출·검증을 일관되게 만든다. - 출처별 구조는 의미 경계와 증거의 원천을 보존하기 위해 유지한다. ### 문서는 제목 계층 단위로 분할 - Markdown 문서를 고정 길이가 아니라 제목 구조에 따라 나눈다. - 상위 제목 경로를 함께 저장해 문장이 어떤 정책이나 기능에 속하는지 보존한다. - 섹션이 지나치게 긴 경우에만 추가 분할한다. - 문서 경로, 원문 URL, 작성·수정 시각도 검증 정보로 남긴다. ### 메신저는 개별 메시지보다 스레드 단위로 처리 - 메시지 하나만 보면 질문인지 결론인지 알기 어렵기 때문에 스레드 전체를 하나의 의미 단위로 삼는다. - 요약 시: - 함수명, 에러 클래스, 파일 경로 등 기술 식별자를 원문 그대로 보존한다. - 질문, 검토한 선택지, 최종 결과를 구분한다. - 확정된 내용과 미결정 내용을 나눈다. - 대화에 없는 합의를 만들어내지 않는다. - 잡담만 있는 스레드는 컨텍스트로 만들지 않는다. ### 코드는 심볼과 비즈니스 동작을 함께 표현 - 파서로 함수·클래스 등 코드 심볼을 추출한다. - 파일 경로, 심볼 종류, 시작·종료 줄, import 관계는 규칙 기반으로 수집한다. - 여러 심볼을 가로지르는 업무 동작은 `CodeSemanticCard`로 묶는다. - semantic card에는 다음 정보가 포함된다. - 업무 대상과 실제 동작 - 도메인 용어와 코드 식별자 - 저장소·파일·줄·심볼 단위의 근거 span - 기준이 된 `commit_sha` - LLM이 만든 카드는 파일·줄이 실제 존재하는지, 설명을 뒷받침하는 span이 있는지 규칙 기반으로 재검사한다. - 최종 검증에서는 카드의 설명만 믿지 않고 현재 코드의 실제 span을 다시 읽는다. ## Extract: 개념과 관계를 원문 근거와 연결 - 각 `ContentUnit`에서 개념 후보와 이를 뒷받침하는 문장을 추출한다. - 함께 등장한 후보를 중심으로 관계를 제안하고, 표현·의미가 유사한 후보의 중복 가능성을 계산한다. - 충분한 근거가 있을 때만 대표 개념으로 통합한다. - 문서·메신저·코드처럼 출처가 다른 unit 사이에도 연결 후보를 만든다. - 개념과 관계에는 원문 위치, 인용, 판단 상태를 함께 저장한다. ### 사내 용어는 사람 검토를 포함 - 띄어쓰기·대소문자 차이는 정규화와 임베딩으로 자동 탐지할 수 있다. - 사내 약어와 별칭은 잘못 합치면 검색·검증 전체를 오염시킬 수 있다. - 애매한 동의어는 즉시 병합하지 않고 `Synonym Proposal`로 등록한다. - 사람이 승인한 별칭만 관리되는 관계로 반영하며, 거절된 후보는 반복 제안하지 않는다. ### 문서와 코드 관계를 유형별로 구분 - `supported_by`: 코드가 문서의 설명을 뒷받침한다. - `contradicted_by`: 코드와 문서의 동작이 충돌한다. - `mentions`: 같은 기능을 언급하지만 일치·충돌 여부를 판단할 근거가 부족하다. - 임베딩으로 후보를 제한한 뒤, 의미 판단이 필요한 후보만 배치 검증한다. - 관계에는 유형뿐 아니라 신뢰도, 판단 이유, 원문 인용, 검증 상태를 기록한다. - 검증 실패나 낮은 신뢰도는 관계를 저장하지 않는다. 관계가 없다는 것은 무관하다는 뜻이 아니라 아직 확인되지 않았다는 의미다. ## Verify: 변경된 부분만 선택적으로 재검증 - 각 unit의 안정적인 식별자와 `content_hash`를 이용해 변경 범위를 추적한다. - 변경되지 않은 unit의 추출·관계 결과는 재사용한다. - 새로 생성되거나 변경된 unit만 다시 처리한다. - 원본이 삭제되면 해당 원본을 참조하던 관계도 정리한다. - 코드 anchor에는 검증 당시의 commit과 span hash를 저장한다. - 현재 코드에서 anchor가 사라졌으면 orphaned 상태로 표시한다. - anchor의 span hash가 같으면 의미 검증을 생략한다. - span hash가 달라졌으면 변경된 코드에 대해 faithfulness를 다시 검증한다. - 해시와 참조 무결성은 규칙 기반으로 검사하고, 실제 의미가 달라진 경우에만 LLM을 호출한다. - 이 방식은 비용을 줄이면서도 변경 원인과 컨텍스트 상태 변화를 추적하게 해준다. ## 실용적인 결론 사내 LLM의 품질을 높이려면 검색 성능만 개선하기보다, 의미 단위·원문 근거·출처 간 관계·최신성·충돌 상태를 함께 관리해야 한다. 특히 자동화는 명확한 변경 감지와 관계 추출에 사용하고, 사내 용어 통합이나 중요한 정책 판단처럼 맥락 의존적인 문제는 근거를 제시한 뒤 사람의 승인을 받는 방식이 안전하다.

원문 읽기(새 탭에서 열림)
toss6분 읽기큐레이션 요약

토스의 디바이스 팜 만들기

네뷸라는 팀별로 흩어져 운영하던 실기기 테스트 환경을 중앙 플랫폼으로 통합해, 누구나 API 호출 한 번으로 실제 스마트폰을 제어할 수 있도록 만든 사내 디바이스 팜입니다. Appium 대신 자체 드라이버를 개발해 속도와 확장성을 높였고, 실시간 미러링·보안·24시간 운영 안정성까지 직접 구축했습니다. 그 결과 15대에서 시작한 팜은 100대를 넘어 수백 대 규모로 확장되며 전사 공용 테스트 인프라로 자리 잡았습니다. ## 팀별 디바이스 팜의 한계 - 각 팀이 맥북이나 맥미니에 5~10대의 기기를 직접 연결하고 관리했습니다. - Appium 설정, 기기 인식, OS 버전 대응, 연결 장애 복구를 팀마다 반복해야 했습니다. - 기기 관리와 테스트, 보안·컴플라이언스까지 개발자가 함께 맡아야 했습니다. - 팀별 자원이 격리되어 회사 전체의 기기를 효율적으로 공유하기 어려웠습니다. - 네뷸라는 이러한 운영 부담을 중앙화하고 전문 플랫폼으로 이전하기 위해 시작됐습니다. - 맥미니 5대와 기기 15대, 개발자 1명으로 시작해 1년간 100대 이상으로 성장했습니다. ## API 한 번으로 실기기 제어 - 사용자는 기기가 어느 호스트에 연결됐는지, ADB나 Xcode를 어떻게 설정했는지 알 필요가 없습니다. - `occupy` API로 조건에 맞는 기기를 점유한 뒤 액션 API를 호출하면 됩니다. - 예를 들어 다음과 같은 흐름으로 기기를 사용할 수 있습니다. - Android 기기 점유 - 좌표 `(540, 1200)` 클릭 - 테스트 종료 후 기기 반환 - 예약, 케이블 연결, 로컬 환경 설정을 숨기고 단순한 인터페이스를 제공하는 것이 네뷸라의 핵심 가치입니다. ## 네뷸라의 4계층 아키텍처 - **클라이언트** - 웹 프런트엔드, SDK·CLI, 직접 API 호출 등 다양한 접근 방식을 제공합니다. - **서버** - 기기 발견·점유·할당·테스트 실행을 관리하는 오케스트레이션 계층입니다. - 테스트 요청은 Kafka로 전달되고 여러 Runner가 분산 처리합니다. - `occupy · assign · release` 기반 분산 락으로 한 기기를 여러 테스트가 동시에 사용하는 문제를 방지합니다. - **에이전트** - iOS용 Mac mini와 Android용 Linux 호스트에서 실행됩니다. - ADB·Xcode로 연결된 기기를 자동 발견하고 서버 요청을 로컬 기기로 전달합니다. - **기기 계층** - 기기마다 controller server와 controller runner가 동작합니다. - 실제 화면 클릭, 텍스트 입력 등의 동작은 이 계층에서 수행됩니다. ## Appium 대신 개발한 Nebula Driver ### 빠른 명령 처리 - Appium과 Android 기기에서 명령 지연을 비교한 결과, 클릭·입력 작업에서 네뷸라가 10배 이상 빠른 경우가 있었습니다. - Appium은 동작 전 화면이 안정될 때까지 기다리는 `waitForIdle`을 사용해 견고성을 높입니다. - 네뷸라는 화면이 진행 중이어도 노드에 바로 명령을 전달하고 즉시 반환하는 속도 우선 방식을 택했습니다. - `waitForIdle`을 끄면 성능 격차가 2~3배 수준으로 줄어들지만, 실시간 조작이 중요한 네뷸라에는 속도 중심 설계가 적합했습니다. ### Stateless 구조 - Appium은 세션 기반이라 세션 생성에 약 15~40초가 걸릴 수 있습니다. - 기기 수가 늘수록 세션 생성 실패와 세션 관리 비용도 증가합니다. - 네뷸라는 기기 컨트롤러를 미리 실행해 두고, 상태 없는 HTTP 호출을 받는 구조를 사용합니다. - 세션 시작 비용과 세션 장애를 줄이고, 대규모 기기 운영에 유리한 구조를 만들었습니다. ### 사내 환경에 맞춘 확장 - 자체 인터페이스를 소유하므로 필요한 기능을 직접 추가할 수 있습니다. - 한글·이모지 입력을 지원하는 자체 IME를 구현했습니다. - 토스 앱 전용 신호 트리거를 추가할 수 있습니다. - 사내 앱센터와 연동해 pre-release 빌드를 기기에 바로 설치할 수 있습니다. - 보안 정책도 드라이버 규격 안에서 강제할 수 있습니다. - Android는 ADB·UiAutomation, iOS는 Swift·XCTest를 기반으로 구현하고, OpenAPI 스펙으로 Go·TypeScript 코드를 자동 생성했습니다. ## 실시간 화면 미러링 - 네뷸라는 정해진 테스트 스텝만 실행하는 도구가 아니라, 사용자가 화면을 보면서 동시에 조작할 수 있어야 했습니다. - 따라서 실시간 조작과 실시간 영상 스트리밍을 Android·iOS 모두에서 해결해야 했습니다. ### Android 미러링 - scrcpy는 Android 화면을 데스크톱 앱에 보여주는 데 적합하지만, 서버를 거쳐 여러 브라우저에 배포하는 구조에는 맞지 않았습니다. - 네뷸라는 scrcpy의 인코딩 방식을 참고하되 자체 미러링 경로를 구현했습니다. - `SurfaceControl`로 가상 디스플레이를 만들고 `MediaCodec`으로 H.264 영상을 인코딩합니다. - 인코딩된 영상은 브로드캐스터를 통해 여러 브라우저 시청자에게 전달됩니다. ### iOS 미러링 - iOS는 Android처럼 화면을 자유롭게 추출하기 어렵고 USB 사용 방식에도 제약이 있습니다. - 기존 QVH·Appium MJPEG 방식은 화면을 보면서 동시에 조작하는 요구를 충족하지 못했습니다. - QuickTime Player의 iOS 화면 캡처 방식과 유사한 경로를 USB 독점 없이 내재화했습니다. - 그 결과 조작과 미러링을 동시에 수행할 수 있게 됐습니다. - Android와 iOS 모두 실시간 H.264·브로드캐스팅 경로로 통일해 브라우저에서 여러 기기를 한 번에 볼 수 있습니다. ## 중앙화로 강화한 보안과 컴플라이언스 - 팀별 운영에서는 개발자가 테스트와 기기 관리, 보안 준수를 모두 책임져야 했습니다. - 네뷸라 팀은 사내 보안팀과 협력해 모바일 기기 팜 운영 기준을 정의했습니다. - 중앙 플랫폼에 보안 정책을 적용해 모든 기기에 동일한 기준을 일괄 반영할 수 있게 했습니다. - 사용자는 보안 요건이 적용된 환경에서 테스트에만 집중할 수 있습니다. ## 24시간 운영을 위한 안정성 ### 하드웨어 운영 - USB 연결 안정성, 케이블·허브 선택, 전원 공급, 서버실 설계를 직접 검증했습니다. - 물리적 장애를 완전히 제거할 수 없기 때문에 사람의 대응 체계와 이중화를 함께 준비하고 있습니다. ### 소프트웨어 운영 - 호스트별 컨트롤러와 미러링 프로세스를 오케스트레이션합니다. - 프로세스가 종료되더라도 자동으로 복구되도록 설계했습니다. - 서버·에이전트·컨트롤러·미러링 구성요소에 무중단 배포를 적용했습니다. - 기기 상태, 프로세스 상태, 서버 성능을 함께 관측하는 모니터링 체계를 구축하고 있습니다. - 여러 팀이 상시 사용하는 공용 인프라이므로 안정성을 핵심 기능으로 취급합니다. ## API 위에 만들어진 테스트 생태계 - 기기 15대에서 100대 이상, 수백 대 규모로 확장 중이며 24시간 운영됩니다. - 웹페이지에서는 미러링 화면을 보며 클릭으로 테스트 스텝을 만들 수 있습니다. - SDK로 E2E 테스트 코드를 작성하고, CLI로 터미널·CI/CD·AI 에이전트에서 기기를 제어할 수 있습니다. - 제품 로그가 기대대로 기록되는지 검수하는 시스템에도 활용됩니다. - AI 에이전트가 API를 호출해 테스트 스텝을 직접 판단하고 실행할 수도 있습니다. - 하나의 공개 API를 기반으로 여러 도구와 활용 사례가 자연스럽게 확장됐습니다. - 사용자 사례에 따르면 Appium 기반 테스트를 이전한 뒤 실행 속도가 크게 향상됐고, 수동 검수 시간이 30~40분에서 10분 이내로 줄었습니다. 네뷸라의 사례는 기기 수를 늘리는 것보다, 복잡한 하드웨어와 운영 문제를 단순한 API 뒤로 숨기는 것이 중요하다는 점을 보여줍니다. 비슷한 플랫폼을 구축한다면 초기부터 기기 점유 모델, 실시간 미러링, 보안 정책, 자동 복구와 무중단 배포를 함께 설계하고, 내부 사용자가 쉽게 확장할 수 있는 단일 API를 중심에 두는 것이 좋습니다.

원문 읽기(새 탭에서 열림)
toss4분 읽기큐레이션 요약

누군가는 토스를 테스트하는 동안, 우리는 테스트하는 법을 만듭니다.

토스 QA Platform 팀은 매주 수백 건의 변경이 포함된 앱을 안정적으로 배포하기 위해, 테스트와 품질 관리의 표준화를 추진하고 있습니다. 단순히 테스트 도구를 제공하는 데 그치지 않고, AI와 자체 플랫폼을 활용해 테스트 실행부터 결함 분석, 출시 후 대응까지 효율화하려 합니다. 궁극적으로는 사람이 중요한 판단에 집중하고, 반복적인 검증은 자동화하는 것이 목표입니다. ## 매주 반복되는 릴리즈 검증 - 토스는 매주 새로운 버전을 배포하며, 한 번의 릴리즈마다 평균 300~400건의 코드가 변경됩니다. - 릴리즈 후보가 올라오면 다음 순서로 검증합니다. - **토스닥터(Toss Doctor)**: 로그인부터 탈퇴까지 핵심 기능을 빠르게 확인하는 스모크 테스트 - **PRCheck**: 변경된 코드와 영향 범위, 버그 위험도, 테스트 우선순위 분석 - **토스체커(Toss Checker)**: 기존 기능이 손상되지 않았는지 확인하는 전사적 리그레션 테스트 - 배포 후에는 크래시 지표를 모니터링하고, 문제가 발생하면 핫픽스를 즉시 배포할지 다음 릴리즈에서 해결할지 판단합니다. - 핫픽스는 사용자에게 추가 업데이트를 요구하므로, 단순히 빠른 대응보다 재발 가능성과 해결의 안전성을 함께 고려합니다. ## 토스 전체의 품질을 지원하는 QA - QA Platform 팀의 역할은 특정 제품의 테스트에 국한되지 않습니다. - QA를 처음 시작하는 팀에 테스트 방향을 제시하고, 사내 도구의 품질을 보증하며, 조직 단위의 QA 프로세스 설계를 지원합니다. - 목표는 누구나 쉽게 테스트 케이스를 만들고, 빠르고 정확하게 테스트할 수 있는 환경을 구축하는 것입니다. - 이를 통해 개별 팀이 아닌 토스 전체의 품질 수준을 끌어올리려 합니다. ## 토스 품질의 세 가지 표준 - **매번 신뢰할 수 있는 배포** - 한 번 성공하는 것이 아니라 매주 일정한 품질과 신뢰성을 유지하는 것이 중요합니다. - **결함을 정확히 발견하는 테스트** - 테스트의 양보다 실제 사고로 이어질 가능성이 높은 결함을 놓치지 않는 것이 핵심입니다. - **효율적인 품질 보증** - 반복 작업을 사람의 수작업만으로 처리하지 않고 자동화해, 지속 가능한 방식으로 품질을 유지해야 합니다. - 올해는 여기에 AI를 활용해 자동으로 수행되는 테스트의 범위를 넓히고 있습니다. 다만 모든 판단을 AI에 맡기기보다, 사람은 사람의 판단이 필요한 영역에 집중하도록 역할을 나눕니다. ## 자체 QA 플랫폼 ‘토션’ - 상용 도구는 토스의 빠른 배포 주기와 업무 방식에 맞게 유연하게 바꾸기 어려웠기 때문에 자체 플랫폼 **토션(Tossion)**을 개발했습니다. - 토션은 처음에 TestRail을 대체하는 플랫폼으로 시작했습니다. - 테스트 케이스 작성 - 테스트 실행 - 결과 기록 - 테스트 관련 봇 통합 - 여러 봇은 **토스버틀러(Toss Butler)**라는 하나의 봇으로 통합해 토스의 업무 흐름에 맞췄습니다. - 이후 다음 기능들이 추가됐습니다. - **PRCheck**: PR 변경 사항을 분석하고 테스트가 필요한 영역을 제시 - **tcgen**: PRD, 디자인 문서 등 여러 맥락을 바탕으로 테스트 케이스 초안 자동 생성 - **자동화 테스트 플랫폼**: 매뉴얼 테스트와 자동화 테스트 결과를 한 화면에서 비교 - **Crash Trend 대시보드**: 크래시의 발생 추세와 토스에 적합한 지표를 분석 - **핫픽스 대시보드**: 장애 원인 분류와 재발 방지 대책 관리 ## 도구 제공만으로는 부족했던 이유 - 팀은 테스트 케이스를 쉽게 만들면 사람들이 테스트를 더 적극적으로 수행할 것이라고 예상했습니다. - 하지만 tcgen을 공개한 뒤 기대만큼 사용되지 않았습니다. - 실제 사용자가 원한 것은 테스트 도구가 아니라 다음과 같은 지원이었습니다. - 누군가 테스트를 빠르고 정확하게 대신 수행할 것 - 테스트 결과의 품질까지 책임질 것 - 도구를 제공하는 것은 사용자 입장에서 업무를 줄이는 것이 아니라 새로운 업무를 넘기는 일이 될 수 있었습니다. - 이에 따라 QA Platform 팀은 도구를 제공하는 데서 나아가, 직접 테스트를 처리하고 품질까지 책임지는 방향으로 전략을 바꿨습니다. ## AI와 빠른 방향 전환 - AI는 빠르게 발전하기 때문에 어제 효과적이었던 방식이 오늘에는 낡을 수 있습니다. - QA 도구가 품질 향상을 돕기보다 변화 속도를 늦추지 않도록, 지속적인 검토와 폐기가 필요합니다. - 실제로 API 테스트를 위한 **API Labs**는 방향이 맞지 않다고 판단해 개발 8시간 만에 폐기했습니다. - 토션, 토스닥터, 토스체커, 자체 스킬들도 완성된 제품이 아니라 필요하면 언제든 교체할 수 있는 시스템으로 설계됐습니다. - AI가 도구를 만드는 속도는 높여도 다음 문제를 대신 결정하지는 못합니다. - 무엇을 품질로 정의할 것인가 - 어떤 기준을 끝까지 지킬 것인가 - 어떤 테스트를 사람에게 맡길 것인가 - 따라서 품질 기준을 세우고 도구의 방향을 조정하는 일은 여전히 QA 팀의 핵심 역할입니다. ## 앞으로 이어질 이야기 - 이후 시리즈에서는 다음 주제를 구체적으로 다룰 예정입니다. - 토션이 어떻게 시작됐는지 - 토스닥터가 배포 전 무엇을 검증하는지 - 토스체커가 증가하는 회귀 테스트를 어떻게 자동화하는지 - 지능형 AI 봇이 여러 도구를 어떻게 연결하는지 - 토스 QA Platform 팀은 매주 반복되는 변화 앞에서 “정말 배포해도 괜찮은가”를 확인하며, 테스트를 수행하는 방법 자체를 만들어가고 있습니다. 실용적으로는 테스트 도구를 도입할 때 기능 수보다 사용자의 실제 부담을 줄이는지 먼저 검증해야 합니다. 또한 AI 기반 QA 시스템은 완성품으로 보기보다, 품질 기준과 업무 방식의 변화에 맞춰 빠르게 교체·개선할 수 있도록 설계하는 것이 중요합니다.

원문 읽기(새 탭에서 열림)
toss4분 읽기큐레이션 요약

es-toolkit: 작은 내부 라이브러리가 글로벌 프로젝트가 된 이야기

es-toolkit은 레거시 브라우저 지원과 비효율적인 구현으로 무거워진 lodash를 대체하기 위해 Toss 내부 유틸리티 라이브러리에서 출발했습니다. 최신 브라우저 API와 ECMAScript Modules를 활용해 불필요한 코드를 제거한 결과, 함수별 성능이 최소 2배에서 10배 이상 향상되고 번들 크기도 일부 경우 30배 이상 줄었습니다. 이후 국내외 개발자들의 참여와 오픈소스 생태계의 지원을 바탕으로 크게 성장했으며, 기존 lodash 사용자가 쉽게 전환할 수 있도록 `es-toolkit/compat`도 개발했습니다. ## lodash를 대체할 현대적인 유틸리티 라이브러리의 필요성 - 프론트엔드 개발에서는 `throttle`, `debounce`, `uniq` 같은 유틸리티 함수가 자주 필요했습니다. - 널리 사용되던 lodash는 다음과 같은 한계가 있었습니다. - 오래된 코드 구조를 유지함 - `Array#map`처럼 브라우저가 기본 제공하는 기능을 직접 재구현함 - Internet Explorer 등 레거시 브라우저를 위한 방어 로직을 포함함 - ECMAScript Modules를 지원하지 않아 tree-shaking이 어려움 - `lodash-es`는 ESM을 지원했지만 lodash의 오래된 내부 구현과 비효율성은 그대로였습니다. - Toss는 자체 라이브러리인 `@toss/utils`를 운영했지만, 모든 함수를 직접 구현하고 다양한 엣지 케이스를 관리하는 데 큰 부담이 있었습니다. ## es-toolkit의 시작과 성능 개선 - Toss 팀은 현대적인 웹 환경에 맞는 효율적인 유틸리티 라이브러리를 만들기로 했습니다. - 핵심 목표는 lodash의 불필요한 로직을 제거하고, 브라우저 내장 API를 적극 활용하는 것이었습니다. - 구현 결과: - 함수에 따라 성능이 최소 2배, 최대 10배 이상 향상 - 레거시 브라우저 지원 코드와 중복 구현을 제거해 일부 번들 크기가 30배 이상 감소 - 최신 모듈 시스템을 활용해 필요한 코드만 포함할 수 있는 기반 마련 ## 국내외 오픈소스 커뮤니티의 참여 - Toss Frontend의 소셜 미디어에 초기 결과를 공유한 뒤 예상보다 많은 사용자가 유입되었습니다. - 커뮤니티 구성원들은 다음과 같은 방식으로 프로젝트에 기여했습니다. - 누락된 함수 구현 - 버그 수정 - 미완성된 코드 최적화 - lodash를 es-toolkit으로 교체하는 번들러 플러그인 제작 - 유명 라이브러리의 의존성 교체 - Reddit 공유 이후 100개 이상의 추천과 수만 명의 저장소 방문이 발생했습니다. - 해외 블로그와 뉴스레터가 프로젝트를 소개하면서 국제적인 참여가 더욱 확대되었습니다. ## 오픈소스 기여가 개발자 성장으로 이어진 사례 - Dayong Lee는 es-toolkit을 계기로 한국에서도 영향력 있는 오픈소스 프로젝트가 나올 수 있다는 가능성에 주목했습니다. - Toss 직원이 아니었지만 공개 저장소에 작은 Pull Request부터 제출하며 기여를 시작했습니다. - 지속적인 코드 리뷰와 기여를 통해 프로젝트의 두 번째로 많은 기여자가 되었습니다. - 이 과정에서 다음을 학습했습니다. - 인터페이스 설계 원칙 - JavaScript 언어의 세부 동작 - 협업과 코드 리뷰 방식 - es-toolkit에서 쌓은 경험은 이후 Toss Bank에 합류하는 계기가 되었습니다. ## 기존 사용자의 전환 장벽 - 라이브러리가 빠르게 발전했지만, 기존 lodash 사용자가 es-toolkit으로 전환하는 속도는 상대적으로 느렸습니다. - lodash는 코드베이스 곳곳에서 다양한 함수를 사용하기 때문에 함수별로 하나씩 교체하는 작업은 큰 부담이었습니다. - 또한 lodash는 가능한 많은 입력과 예외 상황을 처리하는 반면, es-toolkit은 주요 사용 사례에 집중했습니다. - 따라서 동일한 이름의 함수를 단순히 교체하면 일부 상황에서 동작 차이로 런타임 오류가 발생할 수 있었습니다. ## `es-toolkit/compat`을 통한 점진적 마이그레이션 - es-toolkit 팀은 import 문만 바꿔도 사용할 수 있는 lodash 호환 계층의 필요성을 발견했습니다. - `es-toolkit/compat`은 lodash의 인터페이스와 실제 동작을 최대한 유지하면서 내부 구현만 현대화하는 방식으로 설계되었습니다. - 이를 통해 사용자는 대규모 코드 수정 없이도 다음 효과를 얻을 수 있습니다. - 기존 lodash 사용 방식 유지 - 더 빠른 내부 구현 활용 - 점진적으로 es-toolkit 표준 API로 마이그레이션 - 즉, 완전한 재작성보다 낮은 비용으로 성능과 번들 크기 개선을 먼저 경험하게 하는 전략입니다. ## 실용적인 결론 레거시 라이브러리를 교체할 때는 단순히 더 빠른 구현을 제공하는 것만으로는 충분하지 않습니다. 기존 API와 동작을 유지하는 호환 계층을 함께 제공하면 대규모 코드베이스의 전환 장벽을 크게 낮출 수 있습니다. 새로운 프로젝트에는 es-toolkit을 직접 사용하고, 기존 lodash 프로젝트에는 `es-toolkit/compat`을 활용한 단계적 마이그레이션을 고려할 수 있습니다.

원문 읽기(새 탭에서 열림)
toss3분 읽기큐레이션 요약

5. Technical Writer, 사라질 결심

AI 시대에 문서는 조직의 맥락을 AI에 전달하는 핵심 수단이므로, AI가 문서를 잘 만들고 관리하도록 문서화 원칙과 사례를 학습시켜야 한다. 토스는 소수의 Technical Writer(TW)만으로 수천 명의 문서를 관리할 수 없다는 문제를 해결하기 위해, TW의 역할을 AI Skill로 자동화하려 했다. 하지만 Skill을 만들어 공개하는 것만으로는 사용률이 높아지지 않았고, 사용자가 직접 설치·호출하고 자료를 준비해야 하는 불편함이 주요 장애물로 드러났다. ## AI에게 TW의 암묵지 전달하기 - 기존 TW의 리뷰 코멘트를 분석해 문서를 바라보는 관점과 테크니컬 라이팅 원칙을 추출했다. - 기존 가이드를 AI가 기계적으로 적용하지 않도록 각 원칙에 다음을 함께 제공했다. - 잘못된 예시 - 올바른 예시 - 왜 그렇게 작성해야 하는지에 대한 설명 - 자주 작성하는 문서 유형별 템플릿을 만들었다. - ADR 템플릿에는 다음과 같은 필수 섹션을 명시했다. - 개요 - 맥락 - 고려한 선택지와 장단점 - 최종 결정 - 결정 근거 - 반드시 들어가야 하는 섹션에는 `(required)`를 붙여 AI가 핵심 정보를 누락하지 않게 했다. - 문서 유형과 템플릿을 함께 제공해 AI가 구조와 작성 목적을 이해하도록 했다. ## 문서 작성 Skill 구축 TW가 문서 작성을 지원하는 과정을 네 단계로 분해해 AI Skill에 반영했다. - **목적과 배경 확인** - 서비스·프로젝트명 - 문서 목적 - 대상 독자 - 필요한 상세 수준 - 참고 자료 - 예상 문서 구조를 질문한다. - **문서 구조 결정** - 템플릿이 없으면 개요, 핵심 내용, 부가 정보 순서로 기본 구조를 만든다. - 적합한 템플릿이 있으면 온보딩 가이드, 회의록, PRD 등 문서 유형별 템플릿을 참고한다. - **본문 작성** - 테크니컬 라이팅 원칙과 MDX 규칙에 따라 내용을 채운다. - 템플릿은 문서의 목적과 유형에 맞을 때 보조적으로 사용한다. - **점검** - 어색한 표현이나 누락된 정보를 확인한다. - 필수 정보가 부족하면 추측하지 않고 질문이나 주석으로 남긴다. - 선택 항목은 근거 자료가 없을 경우 빈 섹션으로 만들지 않는다. 사용자는 AI가 묻는 질문에 답하기만 하면 되므로, TW와 대화하듯 문서 초안을 완성할 수 있도록 설계했다. ## 문서 리뷰 Skill의 시행착오 처음에는 기존 리뷰 코멘트를 체크리스트로 바꿔 AI가 모든 항목을 점검하게 했다. 그러나 AI가 중요한 문제는 놓치고, 실제로 필요하지 않은 코멘트를 억지로 생성하는 문제가 발생했다. - 잘 작성된 문서의 기준은 어느 정도 정형화할 수 있다. - 반면 잘못된 문서의 문제는 문서마다 다르게 나타난다. - 목적은 명확하지만 논리 흐름이 어색한 경우 - 논리는 자연스럽지만 독자에게 전달할 가치가 빠진 경우 - 따라서 고정된 체크리스트만으로는 다양한 문서 문제를 효과적으로 찾기 어려웠다. 이를 해결하기 위해 AI가 원칙을 참고해 자율적으로 판단하는 리뷰 워크플로를 만들었다. - 테크니컬 라이팅 원칙 파일을 먼저 읽는다. - 문서를 원칙에 비추어 스스로 검토한다. - 문제라고 판단한 이유와 수정 초안을 코멘트로 작성한다. - 마지막에 체크리스트로 누락을 한 번 더 확인한다. 기존 리뷰 코멘트는 단순 점검 목록이 아니라, 원칙이 실제 문서에 어떻게 적용되는지 보여주는 예시로 활용했다. 예를 들어 `date: string`처럼 이름과 타입만 적는 대신, 의미·허용 형식·사용 예시까지 함께 작성하도록 가르쳤다. ## Skill만 공개해서는 충분하지 않았다 두 가지 Skill을 만들어 사내에 공개했지만, 기대만큼 사용되지 않았다. - 사용자가 직접 Skill을 다운로드하고 설치해야 했다. - 비개발자에게 CLI 기반 설치 과정이 낯설고 어려웠다. - Skill을 설치한 뒤에도 문서를 작성할 때마다 사용자가 AI Skill을 떠올리고 직접 호출해야 했다. - 문서 작성에 필요한 코드, 기획서, 기존 문서, Slack 링크 등의 자료도 사용자가 직접 찾아 AI에게 전달해야 했다. - 결국 자동화된 기능이 있어도 실제 업무 흐름과 분리되어 있으면 사용자가 추가로 수행해야 하는 일이 많았다. 따라서 문서 자동화의 핵심은 좋은 프롬프트나 Skill을 만드는 데서 끝나지 않는다. 사용자가 별도로 설치하거나 기억하거나 자료를 수집하지 않아도, 실제 업무 과정에서 자연스럽게 AI가 문서 작성과 리뷰를 지원하도록 연결해야 한다.

원문 읽기(새 탭에서 열림)
toss4분 읽기큐레이션 요약

6. 도구를 넘어, 기준과 책임으로

커머스 조직의 지식 관리는 문서를 많이 쓰거나 자동화 도구를 도입하는 것만으로 완성되지 않는다. 무엇을 지식으로 남길지, 누가 책임질지, 어떤 문서를 신뢰할지에 대한 기준과 거버넌스가 함께 있어야 한다. 궁극적으로는 개인의 기억과 흩어진 기록을 조직의 업무 흐름 속에서 생성·검증·갱신되는 시스템으로 바꿔야 한다. ## 혼자 문서를 작성하는 방식의 한계 - 커머스 위키에 용어사전, 온보딩 문서, 정책 문서를 정리하자 팀마다 다르게 쓰던 용어를 통일하고 다른 팀의 기능을 이해하는 출발점을 만들 수 있었다. - 하지만 제품과 정책의 변화 속도가 문서 작성 속도보다 빨랐다. - 담당자가 바뀐 정책, 일시적인 실험, 메신저에서 논의된 결정까지 한 사람이 모두 추적하기는 불가능했다. - 지식이 현장에서 먼저 생기고 TW가 뒤늦게 정리하는 구조로는 최신성을 유지하기 어려웠다. ## 참여를 유도하는 문화만으로 부족했던 이유 - 주간 뉴스레터, 정책 질문봇, 문서화 워크숍, 길드 등을 통해 구성원의 참여를 높였다. - 문서 요청과 위키 인용은 늘었지만, 첫 기여가 지속적인 기여로 이어지지는 않았다. - 문서 작성은 업무 우선순위에서 밀렸고, 작성된 문서도 시간이 지나며 갱신되지 않았다. - 문서의 적절한 깊이와 대상 독자가 정해져 있지 않아 작성자가 매번 혼자 판단해야 했다. - 실무자는 상세한 구현 정보가 필요하지만, 다른 팀에는 불필요한 노이즈가 될 수 있다. - 개발자에게 유용한 변수명과 기술 세부사항은 비개발자의 이해를 방해할 수 있다. - 문제는 구성원이 문서화에 무관심해서가 아니라, 무엇을 어디에 어느 수준으로 남기고 누가 검토할지 정해져 있지 않았다는 데 있었다. - 문서화가 업무 흐름에 포함되고 팀의 책임으로 인정되어야 지속될 수 있다. ## AI 자동화가 보여준 구조적 문제 - 매일 밤 AI가 두 가지 신호를 바탕으로 문서 초안을 작성한다. - 배포·정책 변경 공지에서 문서 갱신이 필요한 내용을 추출한다. - 정책 질문봇이 답하지 못한 질문을 찾아 관련 자료를 바탕으로 새 문서 초안을 만든다. - 사람은 빈 화면에서 처음부터 작성하는 대신, AI 초안의 근거를 확인하고 승인하는 역할을 맡는다. - 자동화로 작성 부담은 줄었지만 새로운 문제가 드러났다. - 비슷한 문서가 중복 생성됐다. - 최신 문서가 무엇인지 판단하기 어려웠다. - 종료된 실험이나 오래된 정책을 AI가 현행 정책처럼 답하는 경우가 생겼다. - 자동화는 지식 수집과 초안 작성은 돕지만, 문서의 신뢰성·최신성·책임자를 결정하지는 못한다. ## 지식 거버넌스와 책임의 필요성 - 질문의 초점이 “문서를 어떻게 만들까?”에서 “어떻게 믿을 수 있는 지식을 만들까?”로 바뀌었다. - 정책 담당자 변경, 오래된 결정의 폐기, 중복 문서 간 우선순위 같은 문제는 도구가 아니라 운영 기준이 해결해야 한다. - 토스는 문서와 지식을 누가, 언제, 어떤 기준으로 만들고 관리하고 폐기할지 이해관계자가 함께 정하는 ‘커머스 문서·지식 거버넌스’를 제안했다. - 거버넌스는 한 번 정하고 끝나는 규칙이 아니라, 실제 적용 결과를 확인하고 지속적으로 보완하는 체계다. ## 토스 팀의 지식 관리 기준 - **아는 것은 조직에 남긴다** - 반복해서 묻는 질문 - 중요한 의사결정 - 새로 온 구성원이 알아야 하는 내용 - **남긴 지식은 찾을 수 있게 정리한다** - 사람이 검색하거나 AI가 참조할 수 있도록 분류·구조화한다. - 조직 특성에 따라 다음 기준을 선택할 수 있다. - 기술 레이어: 데이터나 시스템의 처리 단계 - 서비스 도메인: 담당 서비스 영역 - 기능 단위: 시스템 또는 기능별 구분 - **필요한 순간에 사용할 수 있게 연결한다** - 위키에 저장하는 데 그치지 않고 질문봇, GitHub 등 실제 업무 도구와 연결한다. - **정확한 정보를 최신 상태로 유지한다** - 문서 책임자와 검토 주기를 정한다. - 실험 정책과 확정 정책을 구분하고, 종료된 정책은 폐기하거나 기록용으로 분류한다. - 조직의 지식은 단순히 글로 남은 모든 정보가 아니라, 구성원이 상황을 이해하고 더 나은 결정을 내리는 데 도움이 되며 검증된 정보다. ## Knowledge Committee의 역할 - Knowledge Committee는 전사 문서 운영 기준을 정의하고 유지하며, 조직 간 기준 충돌을 조정하는 협의체다. - 자발적 모임인 길드와 달리 공식적인 의사결정 권한과 실행력을 가진다. - 운영은 두 층으로 나뉜다. - **TW 챕터**: 문서의 정의, 상태, 출처, 책임자 등 전사 공통 기준을 관리한다. - **각 도메인·챕터**: 현장 특성에 맞춰 문서의 책임자, 갱신·폐기 시점, 운영 방식을 정한다. - 중앙에서 모든 것을 통제하면 현장 변화에 느리고, 전사 기준이 없으면 조직마다 지식 관리 방식이 달라진다. - 예를 들어 커머스 조직에서 실험 배포와 확정 배포를 구분하지 않으면 종료된 실험 정책이 현행 정책처럼 남을 수 있다. - 이런 예외와 시행착오를 커미티가 기준에 반영하고 전사에 공유하면, 개별 조직의 경험이 전체 조직의 운영 노하우가 된다. ## 개인의 기억을 조직의 자산으로 전환하기 - 목표는 지식이 특정 개인이나 메신저 기록에 머무르지 않고 조직 안에서 계속 축적되고 재사용되는 구조를 만드는 것이다. - 지식을 남기고 검증하고 다시 사용하는 과정이 업무의 기본 흐름에 포함되어야 한다. - TW의 역할도 문서 작성에 머무르지 않고 지식 시스템, 제품, 거버넌스를 설계하는 방향으로 확장된다. - 궁극적으로는 문서화가 별도의 숙제가 아니라 자연스러운 업무 방식이 되어, TW의 개입 없이도 조직 지식이 순환하는 상태를 지향한다. 실무적으로는 도구를 도입하기 전에 먼저 “무엇을 남길 것인가”, “누가 검토하고 책임질 것인가”, “언제 최신성을 확인하고 폐기할 것인가”를 정하는 것이 우선이다. 이후 자동화와 AI를 초안 작성·검색·질의응답에 연결해야 지식 관리 시스템이 지속적으로 작동할 수 있다.

원문 읽기(새 탭에서 열림)
toss4분 읽기큐레이션 요약

우리 팀의 문서화는 왜 실패할까? (2)

두 조직의 문서화 경험은 자율적 기여만으로는 지식이 지속적으로 축적되기 어렵다는 점을 보여준다. 문서화의 핵심은 흩어진 지식을 한곳에 모으고, 질문과 공유에 대한 심리적 부담을 낮추며, 조직의 상태에 맞는 구조와 운영 방식을 만드는 데 있다. AI는 문서 작성과 지식 전파를 쉽게 할 뿐 아니라, 질문·문서 증가량·답변 품질 등을 지표로 파악하게 해 문서화 상태를 진단하는 도구가 되고 있다. ## 자율적 문서화의 한계 - 커머스에서는 구성원이 자율적으로 참여하는 ‘커머스 위키’를 만들기 위해 워크숍과 길드를 운영했다. - 첫 문서를 작성하게 만드는 데는 성공했지만, 두 번째·세 번째 기여로 이어지게 하기는 어려웠다. - 문서화가 개인의 의지와 자발성에만 의존하면 지속 가능한 운영 구조를 만들기 어렵다. - 반면 이미 문서가 잘 갖춰진 애즈 도메인에서는 새 플랫폼을 만들기보다 기존 컨벤션을 존중하고, 지식의 위치와 연결 관계를 파악하기 쉽게 만드는 데 집중했다. - 문서가 거의 없는 조직과 이미 충분한 문서가 있는 조직은 출발점과 우선순위가 달라야 한다. ## 지식 공유를 막는 심리적 부담 - 질문을 적게 하는 이유는 단순히 관심이 부족해서가 아니라, “내가 모른다”는 사실을 공개하는 것이 부담스럽기 때문이다. - 문서를 작성할 때도 “내 지식이 틀리면 어떡하지”라는 불안 때문에 좋은 자료를 공유하지 못하는 경우가 많다. - 이를 해결하기 위해 ‘개발 상담 주간’을 열어 질문 자체를 자연스러운 행동으로 만들었다. - 특정 전문가에게 자유롭게 질문하도록 유도 - 다른 사람의 질문에 공감하도록 장려 - 전문가가 답하지 못한 질문에는 팀원들이 대신 답변하도록 독려 - 매일 짧은 서버 개발 지식을 전달하는 봇도 운영한다. - 구성원이 직접 문서를 찾지 않아도 지식에 노출된다. - 완성된 문서를 처음부터 작성하는 대신, 공유된 내용에 한마디를 보태거나 수정하는 방식으로 참여 장벽을 낮춘다. ## AI가 낮춘 문서화의 진입장벽 - AI를 이용하면 문서 초안을 빠르게 만들 수 있어 문서 작성에 필요한 부담이 줄어든다. - 챗봇은 매일 지식을 전달하거나 질문에 답하면서 지식 공유를 일상적인 활동으로 만든다. - AI는 문서화 현황을 정량적으로 확인하는 데도 활용된다. - 챗봇에 올라온 질문 수 - 사람이 대신 답변한 사례와 답변 내용 - 일주일 동안 새로 작성된 문서 수 - 지난주 대비 문서 증가량 - 새로 추가된 문서 목록 - 이를 통해 어떤 지식이 부족한지, 구성원이 무엇을 궁금해하는지, 지식이 실제로 순환하고 있는지를 파악할 수 있다. ## 사람용 문서와 AI용 세부 문서의 분리 - AI가 문서를 읽게 되면서 사람에게는 불필요한 세부 맥락까지 기록해야 하는 상황이 생겼다. - 커머스에서는 문서를 두 영역으로 나누었다. - 중앙 문서: Technical Writer가 관리하며 사람이 읽기 쉽고 조직 전체에 공유할 만한 내용 중심 - 팀 저장소 문서: 업무 과정에서 자동으로 쌓이며 팀 내부 AI가 활용할 수 있는 세부 정보와 맥락 포함 - 문서의 독자가 사람뿐 아니라 AI까지 확장되면서, 문서의 목적과 공개 범위를 구분하는 구조가 필요해졌다. ## 도메인과 챕터의 차이 - 공통 원칙은 지식을 한곳에 모으고, 문서가 흩어지지 않도록 통로를 단순화하는 것이다. - 도메인 문서 - 제품과 코드에 직접 연결된다. - 제품 출시와 변화가 빠르므로 문서 업데이트 주기도 짧다. - 용어, 기능, 정책, 지표처럼 업무와 직접 관련된 구조가 중요하다. - 독자가 다양하므로 비개발자도 이해할 수 있는 수준으로 작성하는 것이 효과적이다. - 챕터 문서 - 특정 직군을 위한 컨벤션, 업무 방식, 생산성 지식이 중심이다. - 코드와 직접 관련되지 않은 추상적인 내용이 많다. - 변화가 느린 만큼 지속적인 업데이트와 참여를 유도하는 방식이 과제다. - 독자가 비교적 명확해 목적에 맞춘 문서 작성이 쉽다. ## 문서 유형과 독자 구분 - 하나의 문서에 모든 정보를 담기보다 독자와 목적에 따라 문서를 분리해야 한다. - 활용 예시는 다음과 같다. - 가이드: 업무를 수행하는 방법 설명 - 기능 단위 정책: 제품이나 기능의 동작 원칙 정리 - 용어 사전: 조직 내 공통 언어 정의 - 지표 문서: 기능이나 정책을 측정하는 기준 설명 - 문서 유형별 역할을 명확히 하면 독자가 필요한 정보를 더 빠르게 찾을 수 있다. ## 문서화 수준 진단 방법 - 업무 중 막혔을 때 무엇을 먼저 찾는지 관찰하면 조직의 문서화 수준을 파악할 수 있다. - 사람이나 사내 메신저를 찾는 경우 - 문서가 거의 없는 상태다. - 업무에 가장 자주 필요한 정보부터 하나씩 정리해야 한다. - 문서를 검색하는 경우 - 원하는 정보를 찾지 못한다면 부족한 문서를 보완해야 한다. - 검색이 잘 된다면 문서는 충분히 쌓인 상태이며, AI를 연결해 접근성을 높일 수 있다. - 문서 기반 AI나 봇에게 질문하는 경우 - 답변이 부정확하면 원인을 분석해야 한다. - 관련 문서가 없으면 새로 작성해야 한다. - 정보가 여러 곳에 흩어져 있으면 한곳으로 통합해야 한다. - 문서는 있지만 엉뚱한 답을 하면 내용이 오래됐거나 맥락이 부족할 가능성이 크다. ## 문서화의 구체적인 시작점 - “문서화를 해야 한다”는 막연한 목표보다 실제 문제와 니즈를 먼저 정의해야 한다. - 예를 들어: - 팀마다 용어가 달라 소통이 어렵다면 용어 사전부터 만든다. - 다른 팀이나 외부에 공유할 레퍼런스가 없다면 공통 가이드를 만든다. - 반복적으로 질문이 발생한다면 해당 업무의 절차와 판단 기준을 문서화한다. - 문제를 하나로 좁히고 그 문제를 해결하는 문서부터 시작해야 지속 가능성이 높다. 결국 효과적인 문서화는 구성원의 의지에만 기대지 않고, 지식을 한곳에 모으고 자연스럽게 공유되도록 만드는 운영 구조에서 출발한다. 먼저 조직의 현재 상태와 가장 큰 문서화 니즈를 진단한 뒤, 하나의 구체적인 문제를 해결하는 문서와 자동화부터 시작하는 것이 좋다.

원문 읽기(새 탭에서 열림)
toss4분 읽기큐레이션 요약

es-toolkit, 사내 작은 라이브러리가 전세계적인 라이브러리가 되기까지

es-toolkit은 오래되고 비효율적인 lodash를 현대 JavaScript 환경에 맞게 대체하기 위해 시작된 유틸리티 라이브러리입니다. 불필요한 호환 코드를 제거하고 핵심 사용 사례에 집중한 결과, 함수 성능은 최대 10배 이상 향상되고 번들 크기는 최대 30배 이상 줄었습니다. 이후 `es-toolkit/compat`과 오픈소스 커뮤니티의 기여를 통해 Yarn, Recharts, Storybook 등 다양한 프로젝트에 채택되며 주간 NPM 다운로드 2천만 회를 넘어섰습니다. ## lodash의 한계와 es-toolkit의 시작 - lodash는 오래된 브라우저와 Internet Explorer를 지원하기 위한 방어적 코드가 많이 포함되어 있습니다. - `Array#map`처럼 현대 브라우저가 기본 제공하는 기능도 직접 구현해 코드가 불필요하게 커졌습니다. - ECMAScript Modules를 지원하지 않아 Tree-shaking으로 필요한 코드만 포함하기 어려웠습니다. - `lodash-es`는 ESM만 추가했을 뿐, 기존 lodash의 낡고 비효율적인 구현 문제는 그대로였습니다. - 토스 내부에서도 `@toss/utils`를 직접 운영했지만, 유틸리티 함수의 다양한 엣지 케이스를 관리하는 데 부담이 있었습니다. - 이에 따라 “lodash의 불필요한 로직을 제거해 더 빠르고 작은 라이브러리를 만들자”는 목표로 es-toolkit이 시작되었습니다. ## 성능과 번들 크기 개선 - lodash의 핵심 함수인 `throttle`, `debounce`, `uniq` 등을 현대적인 방식으로 다시 구현했습니다. - 함수별로 차이는 있지만 불필요한 로직을 제거한 결과 성능이 최소 2배에서 최대 10배 이상 향상되었습니다. - 오래된 브라우저 지원 코드와 중복 구현을 제거해 번들 크기가 최대 30배 이상 감소했습니다. - 현대적인 모듈 구조를 활용해 사용하는 함수만 번들에 포함할 수 있도록 했습니다. - 핵심 유스케이스에 집중해 모든 예외 상황을 처리하는 대신, 일반적인 사용 환경에서 작고 빠르게 동작하도록 설계했습니다. ## 오픈소스 커뮤니티의 확산 - 토스 프론트엔드 SNS를 통해 첫 버전을 공개한 뒤 국내 개발자들의 관심과 기여가 이어졌습니다. - 기여자들은 누락된 함수 구현, 버그 수정, 성능 최적화 등을 Pull Request로 보완했습니다. - 해외 개발자 커뮤니티에 소개된 후 수만 명이 저장소를 확인하고, 구현 방식과 개선점에 대해 활발히 논의했습니다. - 커뮤니티에서는 lodash를 es-toolkit으로 바꾸는 번들러 플러그인과 다른 라이브러리의 의존성을 교체하는 작업도 자발적으로 진행했습니다. - 외부 기여자로 참여한 이다용 개발자는 지속적인 코드 리뷰와 기여를 통해 JavaScript 및 API 설계 경험을 쌓았고, 이후 토스뱅크 입사로 이어졌습니다. ## `es-toolkit/compat`을 통한 마이그레이션 - es-toolkit은 주요 사용 사례에 집중했기 때문에 lodash와 함수 동작이 다른 경우가 있었습니다. - 단순히 import 경로만 바꾸면 런타임 오류가 발생할 수 있어 기존 프로젝트의 마이그레이션 부담이 컸습니다. - 이를 해결하기 위해 lodash의 인터페이스와 동작을 최대한 호환하는 중간 계층인 `es-toolkit/compat`을 제공했습니다. - 기존 코드를 크게 수정하지 않고 import만 변경해도 내부 구현의 현대화 효과를 얻을 수 있도록 했습니다. - 이 접근 방식으로 Storybook, Mermaid, Yarn Berry, Recharts 등 대규모 오픈소스 프로젝트의 채택이 늘었습니다. - 결과적으로 es-toolkit은 주간 NPM 다운로드 2천만 회 이상을 기록했습니다. ## 앞으로의 확장 방향 - 더 많은 JavaScript 라이브러리가 es-toolkit을 사용해 번들 크기와 실행 성능을 개선하도록 지원할 계획입니다. - `Map`과 `Set`의 필터링처럼 기존 내장 자료구조에서 다루기 불편한 기능을 추가하려고 합니다. - Promise 기반 비동기 코드에서 자주 사용하는 `delay` 같은 실용적인 함수도 제공합니다. - 브라우저뿐 아니라 Node.js, Deno, Bun 등 서버 환경을 위한 유틸리티로 영역을 넓히고 있습니다. - 최근 추가된 `exec`처럼 필요한 기능은 유지하면서도 경쟁 구현보다 작은 함수를 제공하는 것을 지향합니다. - 앞으로도 “80% 이상의 유스케이스에 최적화된 작고 빠른 구현”이라는 원칙을 유지할 계획입니다. 기존 lodash를 사용 중인 프로젝트라면 먼저 `es-toolkit/compat`으로 점진적인 교체를 검토하는 것이 현실적인 접근입니다. 이후 호환성이 필요 없는 코드부터 순수 es-toolkit 함수로 전환하면 성능과 번들 크기를 줄이면서도 마이그레이션 위험을 낮출 수 있습니다.

원문 읽기(새 탭에서 열림)
toss4분 읽기큐레이션 요약

우리 팀의 문서화는 왜 실패할까? (1)

문서화가 실패하는 이유는 구성원의 의지 부족보다 문서 작성이 개인의 결심에만 의존하는 구조에 있습니다. 무엇을 어디까지 써야 하는지 기준이 없고, 지식의 정확성을 확신하기 어렵고, 문서화가 업무 프로세스에 자연스럽게 포함되지 않기 때문입니다. 도메인과 챕터 모두에서 출발점은 흩어진 지식을 한곳에 모아 실제 업무에 활용되도록 만드는 것이었습니다. ## 도메인과 챕터의 문서화 차이 - **도메인** - 커머스·광고처럼 특정 사업이나 제품을 목표로 여러 직군이 협업하는 조직입니다. - 정책, 용어, 제품 지식, 실험 결과, API 등 업무와 직접 연결된 정보를 다룹니다. - **챕터** - 서버·프론트엔드처럼 같은 직군이 모인 기능 조직입니다. - 컨벤션, 도구 사용법, 기술 표준, 운영 경험 등 직군 공통 지식을 공유합니다. ## 조직별 문서화 활동 - 동진 님은 커머스와 애즈 도메인에 흩어진 내부 지식을 연결하고, 개발자센터와 문서 업데이트 프로세스를 운영합니다. - 혜빈 님은 전사 문서화 기준과 문서 시스템 ‘토독’을 만들고, 서버 챕터에서는 문서화 길드를 운영합니다. - 문서 작성뿐 아니라 문서 리뷰, 자동화, 지식 공백 탐색까지 문서화의 범위를 넓히고 있습니다. ## 조직의 기대와 실제 반응 - 커머스 도메인에서는 구성원들이 이미 구체적인 문서화 요구를 갖고 있었습니다. - 용어가 통일되지 않아 불편함 - 제품에 적용 중인 정책을 찾기 어려움 - 실험 문서와 API 문서가 제대로 정리되지 않음 - 서버 챕터에서는 처음에 문서화의 효용보다 작성에 드는 수고를 더 크게 느꼈습니다. - 문서를 기반으로 답변하는 AI 챗봇도 원천 문서가 부실해 효과가 제한적이었습니다. - 이후 문서화 길드가 챗봇의 답변을 모니터링하고 부족한 문서를 보완하면서, 문서가 실제 도구의 품질을 높인다는 점을 구성원들이 체감하게 됐습니다. ## 인터뷰로 확인한 실제 문제 - 동진 님은 구성원 인터뷰를 통해 도메인에서 필요한 지식과 문서 공백을 파악했습니다. - 혜빈 님은 온보딩 문서 리뷰가 작성자에게 부담이 되는지 확인하기 위해 인터뷰를 진행했습니다. - 예상과 달리 구성원들은 리뷰를 긍정적으로 받아들였습니다. - 공개 전 다른 관점에서 검토할 수 있음 - 초안보다 문서 품질이 향상됨 - 문서화의 핵심 장애물은 의지 부족이 아니라 작성 방법의 불확실성이었습니다. - 무엇을 다뤄야 하는지 모름 - 어느 수준까지 작성해야 하는지 모름 - 자신의 지식이 정확한지 확신하기 어려움 - 틀린 정보를 공유할까 봐 공개를 꺼림 ## 문서가 없을 때 발생하는 협업 비용 - 도메인에서는 정책과 정보가 여러 팀에 흩어져 협업이 지연됩니다. - 예를 들어 B팀이 정산 기능을 수정하려면 먼저 기존 정산 정책의 위치부터 찾아야 합니다. - 온보딩 과정에서도 체계적인 문서가 없으면 자신이 무엇을 모르는지조차 파악하기 어렵습니다. - 챕터에서는 코드만으로 이해하기 어려운 운영상의 예외나 설계 배경을 찾는 데 많은 시간이 듭니다. - 과거 메신저 스레드를 검색하거나 - 여러 검색어로 반복해서 찾거나 - 최종적으로 코드를 작성한 사람에게 직접 물어봐야 합니다. - 개인이 해결한 오류와 업무 지식을 공유하지 않으면 같은 문제를 구성원들이 반복해서 해결하게 됩니다. - “이 정도는 모두 알겠지”, “나만 모르는 것 같다”는 심리적 장벽이 지식 공유를 막습니다. ## 문서화가 개인의 의지에 의존하는 구조 - 문서화는 당장 효과가 나타나기보다 6개월 후, 1년 후에 가치가 커지는 미래를 위한 투자입니다. - 현재 업무와 직접 연결되지 않으면 부수적인 일로 밀리기 쉽습니다. - 문서를 한 번 작성하는 데서 끝나지 않고 지속적인 업데이트 책임도 필요합니다. - 따라서 작성자는 문서화를 가치 있는 자산보다 추가적인 책임이나 부담으로 느낄 수 있습니다. - 작성과 수정이 업무 프로세스에 포함되어 있지 않으면, 바쁜 상황에서 문서화는 쉽게 중단됩니다. - AI는 초안 작성과 정리를 도와 문서화의 도구적 장벽을 낮추지만, 문서화 기준과 운영 구조 자체를 대신 만들지는 못합니다. ## 문서화의 출발점 - 조직에 필요한 문서를 먼저 파악하려면 구성원 인터뷰와 실제 업무의 불편을 관찰해야 합니다. - 완벽한 문서를 한 번에 만들기보다 흩어진 지식을 우선 한곳에 모으는 것이 중요합니다. - 이후 검색, 리뷰, AI 챗봇 등 실제 사용 사례를 통해 부족한 문서를 발견하고 개선해야 합니다. - 문서화가 지속되려면 작성·리뷰·업데이트가 개인의 의지가 아니라 업무 흐름에 자연스럽게 포함되어야 합니다.

원문 읽기(새 탭에서 열림)
toss4분 읽기큐레이션 요약

세상에 없던 직무를 만들어가기

토스의 Technical Writing Chapter는 문서를 작성하는 역할을 넘어 조직의 지식 시스템을 설계하는 조직으로 확장되었습니다. 코드만으로는 설명할 수 없는 의사결정의 맥락과 암묵지를 문서화하고, 이를 사람과 AI가 업무 중 바로 활용할 수 있게 만드는 것이 핵심입니다. 궁극적으로는 문서 작성 자체를 자동화해 TW가 없어도 조직 안에서 지식이 지속적으로 쌓이는 구조를 만드는 것을 목표로 합니다. ### 코드만으로는 완성되지 않는 SSoT - 코드는 시스템이 무엇을 하는지는 보여주지만, 왜 그렇게 설계했는지와 어떤 의사결정을 거쳤는지는 담기 어렵습니다. - 담당자가 자리를 비우거나 퇴사하면 과거 메신저 대화와 개인의 기억에 의존해 맥락을 찾아야 합니다. - AI 역시 일반적인 지식은 잘 활용하지만 조직 고유의 역사와 의사결정 맥락은 알 수 없습니다. - 따라서 진정한 Single Source of Truth는 코드뿐 아니라 코드 주변의 맥락과 히스토리까지 포함해야 합니다. - AI에게 업무를 맡기려면 조직 구성원이 알고 있는 내용을 먼저 구조화해 남겨야 합니다. ### 문서를 쓰는 일에서 지식이 찾아가게 만드는 일로 - 초기에는 프론트엔드 챕터의 온보딩 문서처럼 필요한 내용을 정리하는 데 집중했습니다. - 그러나 좋은 문서도 사람들이 처음부터 읽지 않으며, 필요한 부분만 찾아보는 경우가 많다는 한계가 있었습니다. - 문서 링크를 전달하는 대신, 사람들이 실제로 일하는 메신저와 IDE 안에서 문서 내용을 활용하도록 챗봇 ‘박씨’를 만들었습니다. - 박씨는 기존 문서를 근거로 답변하고 출처까지 제시해, 사용자가 직접 문서를 검색하지 않아도 필요한 지식에 접근하게 했습니다. - 이 시스템을 계기로 문서에 관심이 없던 팀들도 반복 질문을 줄이고 암묵지를 시스템화하기 위해 지식 시스템을 요구하기 시작했습니다. ### 문서에서 지식 시스템으로 - TW의 역할은 문서를 잘 쓰는 사람에서 조직에 맞는 지식 시스템을 설계하는 사람으로 확장되었습니다. - 지식은 특정 맥락에서 문제를 이해하고 더 나은 결정을 내리도록 돕는, 검증된 정보입니다. - 지식 시스템은 코드·대화·배포 기록 등에 흩어진 지식을 한곳에 모으고, 사람이 읽거나 AI가 이해할 수 있는 형태로 구조화합니다. - 이렇게 축적된 지식은 검색뿐 아니라 질문 응답과 업무 자동화에도 활용됩니다. - 조직이 커질수록 반복 질문과 커뮤니케이션 비용이 증가하므로, 지식 시스템은 생산성과 온보딩을 지원하는 조직 인프라가 됩니다. ### Technical Writing Chapter가 하는 네 가지 일 - **지식 플랫폼 개발** - 사내 지식 관리 플랫폼 ‘토독’을 직접 만들고 운영합니다. - **조직별 문서화 주도** - 각 조직의 업무 방식과 필요한 지식에 맞춰 흩어진 정보를 수집하고 활용 구조를 설계합니다. - **문서 작성의 자동화** - AI 워크플로와 자동화를 활용해 구성원이 비슷한 품질의 문서를 작성하고 리뷰받도록 합니다. - 장기적으로 사람이 직접 수행하는 Technical Writing을 줄이는 것이 목표입니다. - **문서화 문화 조성** - AI가 잘 읽을 수 있는 문서 작성법 등을 주제로 전사 세션을 진행합니다. - 조직별 문서화 길드와 지식 커미티를 운영해 지식을 생산하고 관리하는 방식을 바꿉니다. ### 궁극적인 목표: Technical Writer의 소멸 - 올해 목표는 사람이 직접 문서를 작성하지 않아도 조직의 지식이 축적되는 구조를 만드는 것입니다. - TW가 모든 문서를 대신 작성하는 것이 아니라, 조직 구성원과 AI가 지속적으로 지식을 생산·관리하도록 시스템과 문화를 구축합니다. - 최종적으로는 Technical Writing Chapter가 없어도 각 조직이 스스로 지식 시스템을 운영할 수 있도록 만드는 것을 지향합니다. - 이는 직무가 사라진다는 의미보다, 특정 직무에 의존하지 않는 지식 생산 구조를 만든다는 의미에 가깝습니다. ### 다른 직무에도 적용되는 확장 방식 - 이 사례는 정해진 직무를 수행한 결과가 아니라, 조직의 문제를 해결하는 과정에서 직무의 범위를 새롭게 정의한 사례입니다. - AI가 업무 방식을 바꾸는 상황에서는 기존 직무 설명에 머무르기보다 반복되는 문제와 조직의 필요를 따라 역할을 확장할 수 있습니다. - Technical Writer의 전문성은 글쓰기 자체뿐 아니라 지식을 발견하고, 구조화하고, 전달하며, 자동화하는 능력으로 넓어지고 있습니다. 조직의 지식을 개인의 기억이나 메신저 기록에만 남겨두지 않으려면 코드와 맥락을 함께 관리해야 합니다. 문서를 단순한 참고 자료가 아니라 업무 흐름 속에서 바로 활용되는 시스템으로 설계하고, 반복적인 작성과 관리를 자동화하는 방향이 실용적인 접근입니다.

원문 읽기(새 탭에서 열림)
toss6분 읽기큐레이션 요약

Spark Connect on Kubernetes #1: 견고한 Spark Connect 만들기

토스증권은 여러 사용자가 안정적으로 사용할 수 있는 Production급 Spark Connect를 Kubernetes에서 운영하고 있습니다. Spark Connect는 Driver를 애플리케이션마다 실행하는 대신 장기 실행 서버로 분리해 가벼운 클라이언트와 빠른 세션 생성을 제공하지만, 여러 세션이 하나의 SparkContext를 공유하면서 장애 전파와 리소스 경합 문제가 발생합니다. 이를 해결하기 위해 글로벌 장애 카운터를 사실상 비활성화하고, 결과 크기를 제한하며, 여러 Replica로 Driver와 SparkContext를 분리하는 전략을 사용합니다. ## Classic Spark의 구조와 Spark Connect의 등장 - Spark는 작업을 계획·지휘하는 **Driver**와 실제 연산을 수행하는 **Executor**로 구성됩니다. - Classic Spark의 배포 방식은 다음과 같습니다. - **Client mode**: 클라이언트 프로세스가 Driver 역할을 수행합니다. - **Cluster mode**: 작업 제출 시 클러스터에 Driver가 생성되고 작업 종료 후 사라집니다. - 두 방식 모두 애플리케이션마다 Driver가 하나씩 생성되고, 애플리케이션의 수명과 함께 종료됩니다. - Spark Connect는 Spark 3.4부터 도입됐으며, 4.0에서는 기존 Dataset/DataFrame API와 거의 동등한 수준에 도달했습니다. - 글의 구현과 설정은 Spark 4.1을 기준으로 합니다. ## Spark Connect의 동작 방식 - Spark Connect에서는 Driver를 애플리케이션별 프로세스가 아니라 **미리 실행해 둔 서버**로 운영합니다. - 클라이언트는 Spark 라이브러리와 JVM을 직접 포함하지 않는 Thin Client입니다. - 클라이언트의 DataFrame·SQL 연산은 다음 과정으로 처리됩니다. - 연산을 Unresolved Logical Plan으로 변환 - Protocol Buffer로 인코딩 - gRPC를 통해 서버로 전송 - 서버가 분석, 최적화, 스케줄링, 실행 수행 - 결과를 Arrow 기반으로 클라이언트에 스트리밍 - 구조적으로는 JDBC 클라이언트가 데이터베이스 서버에 질의하는 모델과 유사합니다. ## Spark Connect의 장점 - 클라이언트에 무거운 Spark 의존성이나 JVM이 없어도 됩니다. - Python, SQL, 노트북, BI 도구 등 다양한 클라이언트가 같은 서버에 접속할 수 있습니다. - Driver가 이미 실행 중이므로 매번 프로세스를 생성하고 리소스를 협상할 필요가 없습니다. - 클라이언트가 종료되거나 네트워크가 끊겨도 서버에서 실행 중인 작업은 보호할 수 있습니다. - 반면 하나의 장기 실행 서버에 여러 사용자가 접속하면서, Spark의 기존 “애플리케이션 하나에 워크로드 하나”라는 전제가 깨집니다. ## 공유 Driver가 만드는 단일 장애점 - 여러 세션이 하나의 SparkContext와 Driver JVM을 공유합니다. - Driver가 장애를 일으키면 해당 서버의 모든 세션, 실행 중인 Job, 캐시가 함께 사라집니다. - `spark.executor.maxNumFailures`는 Executor 실패를 애플리케이션 전체 단위로 누적합니다. - 기본 임계값은 `max(3, 2 × executor 수)`입니다. - 임계값을 초과하면 `stopApplication()`이 호출되고, 결과적으로 `sys.exit(11)`로 서버 전체가 종료됩니다. - 이 카운터는 다음 이유로 멀티세션 환경에서 위험합니다. - 개별 쿼리의 Task 실패가 아니라 Executor 실패를 전역적으로 집계합니다. - 시간이 지나도 실패 기록이 계속 누적됩니다. - 서로 다른 사용자의 실패가 합산됩니다. - 문제가 없는 세션도 장애를 함께 겪게 됩니다. ## 세션 격리와 리소스 경합의 한계 - `newSession()`은 SQL 네임스페이스 등 세션 상태만 분리합니다. - CPU, 메모리, Executor, Task 슬롯은 모든 세션이 공유합니다. - 한 사용자가 대규모 Job을 제출하면 다른 사용자의 쿼리 응답도 느려질 수 있습니다. - 기본 FIFO 스케줄링에서는 먼저 제출된 작업이 우선하며, 선점이 없어 이미 실행 중인 Task를 중단할 수 없습니다. - Fair Scheduler를 사용해도 Task 슬롯을 배분하는 순서만 조정할 뿐, 사용자별 CPU·메모리 격리는 제공하지 않습니다. - Spark Connect에서는 `spark.scheduler.pool`이 기본적으로 제대로 전파되지 않아 모든 쿼리가 Default Pool에 들어갑니다. - Classic Spark에서는 `setLocalProperty()`가 Driver 스레드에 직접 적용됩니다. - Spark Connect에서는 클라이언트와 Driver가 분리되어 서버의 요청 처리 스레드에 값을 별도로 설정해야 합니다. - 토스증권은 서버 스레드에 사용자별 Pool을 직접 설정하는 방식으로 이 문제를 보완했습니다. - 사용자별 Pool을 적용하려면 먼저 요청의 사용자를 식별해야 하며, 인증·인가와 연결됩니다. - 궁극적인 CPU·메모리 격리는 Spark 스케줄러가 아니라 Spark 외부의 리소스 관리 계층에서 해결해야 합니다. ## 고정된 서버 스케일 문제 - Spark Connect 서버는 이미지, Driver·Executor 리소스, Spark 설정이 고정된 상태로 실행됩니다. - Dynamic Resource Allocation으로 Executor 수는 조절할 수 있지만, 서버 자체의 기본 스펙은 실행 중 바뀌지 않습니다. - 서버를 필요에 따라 생성·교체하거나 팀 단위로 격리하는 문제는 후속 글에서 다룹니다. ## 글로벌 장애 카운터 비활성화 - 서버 전체를 종료시키는 Executor 실패 경로를 차단하기 위해 다음과 같이 설정합니다. - `spark.executor.maxNumFailures`: 사실상 무한대로 설정해 글로벌 종료 조건을 비활성화 - `spark.executor.failuresValidityInterval`: 오래된 실패 기록을 주기적으로 제거 - `spark.task.maxFailures`: 동일 Task의 반복 실패를 제한 - `spark.stage.maxConsecutiveAttempts`: Shuffle Fetch 실패로 Stage가 반복 실행되는 상황을 제한 - `task.maxFailures`는 OOM이나 예외처럼 동일 Task가 반복 실패하는 경우를 담당합니다. - `stage.maxConsecutiveAttempts`는 Shuffle Fetch 실패로 Stage 전체가 반복되는 경우를 담당합니다. - 이 방식으로 문제가 있는 쿼리만 실패시키고 서버와 다른 사용자의 세션은 유지할 수 있습니다. - 다만 실패 허용 횟수를 지나치게 낮추면 일시적인 장애에도 정상 쿼리가 실패할 수 있으므로 워크로드에 맞춰 여유를 둬야 합니다. ## Driver 메모리 보호와 결과 크기 제한 - Spark Connect에서는 쿼리 결과가 Driver를 거쳐 클라이언트로 스트리밍됩니다. - 사용자가 대규모 테이블을 `collect`하면 Driver 메모리가 고갈될 수 있습니다. - `spark.driver.maxResultSize`는 한 액션에서 반환되는 Task 결과의 누적 크기를 제한합니다. - 제한을 초과하면 Driver가 결과를 모두 가져오기 전에 Job을 중단하므로, 대규모 결과가 Driver 메모리에 유입되는 것을 막을 수 있습니다. - 기본값인 1GB는 애플리케이션 하나만 실행하는 환경의 값이므로, 여러 세션이 동시에 결과를 가져가는 멀티세션 서버에서는 더 보수적으로 설정해야 합니다. - 이 설정만으로 Driver OOM이나 노드 장애까지 막을 수는 없습니다. ## 여러 Replica를 통한 장애 영향 축소 - Driver 자체의 OOM이나 노드 소실처럼 설정으로 막을 수 없는 장애에 대비해 Spark Connect 서버를 여러 Replica로 구성합니다. - 각 Replica는 독립적인 다음 요소를 갖습니다. - SparkContext - Driver - Executor - 한 Replica가 장애로 종료되어도 장애 범위가 해당 Replica에 한정되고, 다른 Replica가 새로운 세션 요청을 처리할 수 있습니다. - 단일 서버의 장애가 Spark Connect 전체로 확산되는 구조를 여러 독립 실행 단위로 나누는 것이 핵심입니다. ## 실용적인 운영 방향 - 멀티세션 Spark Connect에서는 전역 장애 카운터를 그대로 두지 말고, Task·Stage·Job 단위의 실패 제한으로 문제 쿼리를 격리하는 것이 안전합니다. - `spark.driver.maxResultSize`를 동시 세션 수와 쿼리 특성에 맞게 보수적으로 설정해야 합니다. - 스케줄러 Pool만으로는 CPU·메모리 격리가 불가능하므로, 강한 격리가 필요하면 Replica나 Kubernetes 리소스 정책을 활용해야 합니다. - 단일 Driver를 그대로 공유하기보다 여러 Replica를 운영해 장애의 영향 범위를 줄이는 것이 Production 환경에 적합합니다.

원문 읽기(새 탭에서 열림)