토스 / kotlin

2 개의 포스트

toss

LLM을 이용한 서비스 취약점 분석 자동화 #2 (새 탭에서 열림)

AI 기술의 비약적인 발전으로 취약점 분석 자동화가 새로운 국면을 맞이한 가운데, 대규모 소스코드를 효율적으로 분석하기 위한 구체적인 기술적 구현 방법과 보안 관점의 변화가 필요합니다. 본 글은 MCP(Model Context Protocol)를 통한 정밀한 코드 탐색과 SAST 도구를 활용한 분석 후보군 추출을 결합하여 분석의 일관성과 정확도를 높인 사례를 제시합니다. 결과적으로 AI가 단순한 보조 도구를 넘어 복합적인 추론을 수행하는 능동적인 보안 분석 주체로 진화하고 있음을 강조합니다. **MCP를 활용한 효율적인 소스코드 탐색** * 기존의 단순 패턴 매칭 방식은 불필요한 탐색으로 토큰을 낭비하거나 정확한 정의를 찾지 못하는 한계가 있어, 이를 개선하기 위해 ctags와 tree-sitter를 결합한 MCP 서버를 구축했습니다. * AI에게 IDE의 'Go to Definition'과 유사한 능력을 부여하기 위해 `find_references`(참조 검색), `read_definition`(심볼 정의 및 함수 범위 감지), `read_source`(주변 코드 읽기), `get_project_structure`(전체 구조 파악) 등 4가지 핵심 도구를 구현했습니다. * 이 시스템은 AI가 원격 서버 환경에서도 프로젝트의 전체적인 청사진을 이해하고, 분석이 필요한 코드의 맥락을 정확하게 짚어낼 수 있도록 돕습니다. **SAST와 AI의 결합을 통한 분석 범위 확장** * 분석의 일관성을 확보하기 위해 SAST(Semgrep 등)를 취약점 탐지용이 아닌, AI가 반드시 검토해야 할 '모든 입력 경로(Taint Path)'를 추출하는 보조 도구로 활용했습니다. * Spring 프레임워크의 @RequestParam, @RequestBody 등 모든 입력 지점(Source)에서 함수 호출(Sink)까지의 도달 경로를 추출하는 규칙을 설정하여 분석 후보군을 빠짐없이 확보했습니다. * 취약점 유무를 판단하기 어려운 복잡한 비즈니스 로직이나 보안 필터링의 유효성을 AI가 직접 검토하게 함으로써, 기존 정적 분석 도구의 한계를 AI의 문맥 이해 능력으로 보완했습니다. **체계적인 추론 과정(CoT) 설계** * AI가 분석을 시작하기 전 '계획 수립 - 도구 실행 - 검증 - 결과 분석'의 단계를 거치도록 Chain of Thought(CoT) 방식을 적용하여 분석 결과의 신뢰도를 높였습니다. * 단순히 코드를 단편적으로 보는 것이 아니라, MCP 도구를 활용해 연관된 코드와 비즈니스 로직을 충분히 탐색한 후 최종 판단을 내리도록 설계하여 오탐(False Positive)을 획기적으로 줄였습니다. * 이러한 구조화된 추론 과정을 통해 AI는 10개의 취약점 중 일부만 찾는 불완전한 분석에서 벗어나, 정해진 후보군 전체를 일관성 있게 전수 조사할 수 있게 되었습니다. **보안 패러다임의 전환** 현재의 AI는 단순한 챗봇을 넘어 보안 전문가의 사고 과정을 모사하는 에이전트로 진화하고 있습니다. 보안 담당자는 이제 AI에게 효율적인 코드 탐색 도구(MCP)를 제공하고 정밀한 분석 경로(SAST 활용)를 설계해 주는 'AI 오케스트레이터'로서의 역할을 고민해야 합니다. AI가 가진 강력한 추론 능력을 신뢰하되, 이를 올바른 방향으로 이끌 수 있는 환경을 구축하는 것이 보안 자동화의 핵심입니다.

toss

레거시 정산 개편기: 신규 시스템 투입 여정부터 대규모 배치 운영 노하우까지 (새 탭에서 열림)

토스페이먼츠는 20년 동안 운영되어 온 레거시 정산 시스템의 한계를 극복하기 위해 대대적인 개편을 진행했습니다. 거대하고 복잡한 단일 쿼리 기반의 로직을 객체지향적인 코드로 분산하고, 데이터 모델링을 집계 중심에서 거래 단위로 전환하여 정산의 정확성과 추적 가능성을 확보했습니다. 이를 통해 폭발적인 거래량 증가에도 대응할 수 있는 고성능·고효율의 현대적인 정산 플랫폼을 구축하는 데 성공했습니다. ### 거대 쿼리 중심 로직의 분할 정복 * **문제점:** 수많은 JOIN과 중첩된 DECODE/CASE WHEN 문으로 이루어진 거대한 공통 쿼리가 모든 비즈니스 로직을 처리하고 있어 유지보수와 테스트가 매우 어려웠습니다. * **도메인 및 기능 분리:** 거대 쿼리를 분석하여 도메인별, 세부 기능별로 카테고리를 나누는 분할 정복 방식을 적용했습니다. * **비즈니스 규칙의 가시화:** 복잡한 SQL 로직을 Kotlin 기반의 명확한 객체와 메서드로 재구성하여, 코드상에서 비즈니스 규칙이 명확히 드러나도록 개선했습니다. * **점진적 전환:** 기능을 단위별로 분할한 덕분에 전체 시스템 개편 전이라도 특정 기능부터 우선적으로 새 시스템으로 전환하며 실질적인 가치를 빠르게 창출했습니다. ### 데이터 모델링 개선을 통한 추적 가능성 확보 * **최소 단위 데이터 관리:** 기존의 집계(Sum) 기반 저장 방식에서 벗어나 모든 거래를 1:1 단위로 관리함으로써, 오류 발생 시 원인 추적을 용이하게 하고 데이터 재활용성을 높였습니다. * **설정 정보 스냅샷 도입:** 계산 결과와 함께 당시의 계약 조건(수수료율 등)을 스냅샷 형태로 저장하여, 시간이 지나도 과거의 정산 맥락을 완벽히 복원할 수 있게 했습니다. * **상태 기반 재처리:** 거래별로 독립적인 상태를 기록하는 설계를 통해, 장애 발생 시 전체 재처리가 아닌 실패한 건만 선별적으로 복구할 수 있도록 효율화했습니다. ### 고해상도 데이터 대응을 위한 DB 최적화 * **파티셔닝 및 인덱스 전략:** 정산일자 기준의 Range 파티셔닝과 복합 인덱스를 활용해 데이터량 증가에 따른 조회 성능 저하를 방지했습니다. * **조회 전용 테이블 및 데이터 플랫폼 활용:** 실시간 조회가 필요한 핵심 기능은 전용 테이블로 대응하고, 복잡한 어드민 통계는 고성능 데이터 플랫폼에 위임하여 시스템 부하를 분산했습니다. ### 배치 시스템 성능 극대화 * **I/O 횟수 최소화:** 배치 실행 시점에 가맹점 설정 정보를 전역 캐시에 로드하여 반복적인 DB 조회를 제거했습니다. * **Bulk 조회 및 처리:** Spring Batch의 `ItemProcessor`에서 개별 건별로 I/O가 발생하지 않도록 Wrapper 구조를 도입하여 묶음 단위(Bulk)로 조회하도록 개선했습니다. * **멀티 스레드 활용:** `AsyncItemProcessor`와 `AsyncItemWriter`를 도입하여 단일 스레드 제약을 극복하고 처리 속도를 비약적으로 향상시켰습니다. 이번 개편은 단순히 기술적인 스택을 바꾸는 것을 넘어, 레거시 시스템에 숨겨진 복잡한 비즈니스 맥락을 명확한 도메인 모델로 추출해냈다는 점에서 큰 의미가 있습니다. 대규모 트래픽과 복잡한 정산 규칙을 다루는 시스템이라면, 데이터를 최소 단위로 관리하고 I/O 최적화와 캐싱을 적극적으로 활용하는 아키텍처를 검토해볼 것을 추천합니다.