CI/CD를 넘어: LangGraph, MCP, A2A를 활용한 에이전트 기반 검증 및 검사 계층 구축
요약
전통적인 CI/CD 파이프라인의 한계를 극복하기 위해 LangGraph, MCP, A2A를 활용한 에이전트 기반 검증 계층 구축 방안을 제시합니다. 단순 자동화를 넘어 맥락적 위험을 추론하고 보안 및 아키텍처 제약 조건을 평가하는 상태 유지 추론 계층의 필요성을 다룹니다.
핵심 포인트
- 전통적 CI/CD는 맥락적 위험(Contextual risk) 판단에 한계가 있음
- LangGraph와 MCP를 활용한 에이전트 기반 검사 계층 도입 제안
- 결정론적 도구와 상태 유지 추론 계층의 결합을 통한 보안 강화
- SDLC 전반에 걸친 에이전트 기반의 검증 및 감사 프로세스 구축
현대의 CI/CD 파이프라인은 자동화 측면에서 매우 뛰어납니다.
코드 컴파일, 테스트 실행, 아티팩트(Artifact) 빌드, 종속성 스캔, 인프라 배포 및 릴리스 승급 등을 수행합니다.
하지만 배포를 자동화하는 것과, 특정 변경 사항이 배포되어야 하는지에 대해 추론(Reasoning)하는 것 사이에는 중요한 차이가 있습니다.
다음과 같은 내용을 포함하는 풀 리퀘스트(Pull Request)를 가정해 봅시다:
DROP TABLE customer_transactions;
또는:
DELETE FROM orders;
또는 다음과 같은 실수로 인한 애플리케이션 변경 사항:
- 인증 미들웨어(Authorization middleware) 제거
- 안전하지 않은 데이터베이스 작업 도입
- 비밀 정보(Secret) 노출
- 인프라 보안 경계 변경
- 아키텍처 컨벤션(Architectural convention) 우회
- 파괴적인 스키마 마이그레이션(Breaking schema migration) 배포
- 또는 예상 범위를 벗어난 프로덕션 리소스 수정
전통적인 CI/CD는 명시적인 규칙이 이미 존재하는 경우 이러한 문제 중 상당수를 감지할 수 있습니다.
더 어려운 문제는 맥락적 위험(Contextual risk)을 결정하는 것입니다.
이 DROP 문이 승인된 마이그레이션의 일부인가?
DELETE 문에 적절한 서술어(Predicate)가 포함되어 있는가?
변경 된 서비스가 여전히 시스템의 아키텍처 제약 조건을 충족하는가?
마이그레이션이 현재 프로덕션에서 실행 중인 애플리케이션과의 호환성을 유지하는가?
겉보기에 무해한 구성 변경이 공격 표면(Attack surface)을 확장하는가?
이 지점이 바로 에이전트 기반 검사 계층(Agentic inspection layer)이 전통적인 CI/CD 보안 제어를 대체하는 것이 아니라 보완할 수 있는 영역입니다.
아이디어는 간단합니다:
프로덕션 배포 전에, 결정론적 도구(Deterministic tools)로부터 증거를 수집하고, 다양한 관점에서 변경 사항을 평가하며, 감사 가능한(Auditable) 배포 결정을 생성하는 상태 유지 추론 계층(Stateful reasoning layer)을 도입하는 것입니다.
실질적인 아키텍처는 다음과 같은 요소들을 결합할 수 있습니다:
- 결정론적 린터(Linters) 및 보안 스캐너
- LangGraph 및 StateGraph
- 특화된 검증 에이전트(Validation agents)
- Model Context Protocol (MCP)
- Agent2Agent (A2A) 통신
- 정책 엔진(Policy engines)
- CI/CD 상태 체크
- 고위험 작업에 대한 인간의 승인
이 글은 제1원칙(First principles)으로부터 해당 아키텍처를 발전시켜 나갑니다.
- 전통적인 CI/CD가 필요하지만 충분하지 않은 이유
전통적인 파이프라인은 흔히 다음과 같은 형태를 띱니다:
개발자 (Developer)
↓
풀 리퀘스트 (Pull Request)
↓
린트 (Lint)
↓
단위 테스트 (Unit Tests)
↓
SAST / 의존성 스캔 (Dependency Scan)
↓
빌드 (Build)
↓
배포 테스트 (Deploy Test)
↓
운영 환경 배포 (Deploy Production)
이러한 아키텍처는 필수적입니다.
NIST의 보안 소프트웨어 개발 프레임워크 (Secure Software Development Framework)는 보안을 최종 단계의 활동으로 취급하기보다 소프트웨어 개발 생명주기 (Software Development Lifecycle, SDLC) 전반에 걸쳐 보안 개발 관행을 통합할 것을 권장합니다.
OWASP 또한 CI/CD 인프라 자체가 보안에 민감하다고 강조합니다. 파이프라인은 소스 코드, 자격 증명 (Credentials), 아티팩트 (Artifacts), 인프라 및 배포 환경에 빈번하게 접근하기 때문입니다.
따라서 문제는 전통적인 CI/CD가 쓸모없어졌다는 것이 아닙니다.
문제는 대부분의 통제 수단 (Controls)이 의도적으로 전문화되어 있다는 점입니다.
SQL 린터 (SQL linter)는 SQL을 이해합니다.
SAST 엔진은 알려진 코드 패턴과 데이터 흐름 (Data flows)을 이해합니다.
의존성 스캐너 (Dependency scanner)는 패키지와 취약점 (Vulnerabilities)을 이해합니다.
단위 테스트 (Unit test)는 예상된 동작을 검증합니다.
하지만 이들 중 어느 것도 전체 배포 컨텍스트 (Deployment context)를 반드시 이해하는 것은 아닙니다.
이러한 차이가 두 번째 계층의 필요성을 부여합니다.
- 결정론적 검증 (Deterministic Validation) 대 에이전트 기반 검사 (Agentic Inspection)
중요한 아키텍처 규칙은 다음과 같습니다:
결정론적 통제 수단 (Deterministic control)이 신뢰성 있게 작업을 수행할 수 있다면, 절대로 LLM에게 그 역할을 대신해 달라고 요청하지 마십시오.
예를 들어, SQL 구문은 일반적으로 SQL 파서 (Parser) 또는 린터 (Linter)를 사용하여 검증해야 합니다.
SQLFluff는 방언을 인식하는 (Dialect-aware) SQL 파싱 및 린팅을 제공하며, 템플릿화된 SQL과도 함께 작동할 수 있습니다.
마찬가지로, 소스 코드 보안 분석은 CodeQL, Semgrep, 의존성 스캐너, 비밀 정보 스캐너 (Secret scanners), 그리고 언어 네이티브 테스트 프레임워크 (Language-native test frameworks)와 같은 도구들을 계속해서 사용해야 합니다.
에이전트 계층 (Agentic layer)은 이러한 도구들의 상위에 위치합니다.
다음과 같이 묻는 대신:
LLM → 이 SQL이 유효한가?
다음 방식을 선호하십시오:
SQL 파서 (SQL Parser)
↓
SQL 린터 (SQL Linter)
↓
정책 규칙 (Policy Rules)
↓
에이전트 기반 리스크 분석 (Agentic Risk Analysis)
처음 세 단계는 증거 (Evidence)를 생성합니다.
에이전트는 더 넓은 배포 컨텍스트 내에서 그 증거를 해석합니다.
이를 통해 우리는 하이브리드 아키텍처를 얻게 됩니다:
결정론적 제어 (Deterministic Controls)
+
맥락적 에이전트 추론 (Contextual Agentic Reasoning)
+
인간 거버넌스 (Human Governance)
그러한 조합은 LLM을 범용 보안 스캐너 (security scanner)로 취급하는 것보다 훨씬 더 안전합니다.
- 에이전트 기반 CI/CD 검사 아키텍처 (The Agentic CI/CD Inspection Architecture)
유용한 상위 수준 아키텍처는 다음과 같습니다:
┌───────────────────┐
│ Pull Request │
└─────────┬─────────┘
...
저는 일반적으로 시스템을 네 가지 논리적 책임으로 나눕니다.
게이트키퍼 (Gatekeeper)
게이트키퍼는 무엇이 변경되었는지, 그리고 어떤 검사 경로 (inspection path)가 필요한지를 결정합니다.
예시:
Python 변경
→ Python 검증 (validation) + 보안 검사 (security inspection)
SQL 변경
→ SQL 파서 (parser) + SQL 정책 검사 (policy inspection)
Terraform 변경
→ IaC 보안 검사 (security inspection)
Dockerfile 변경
→ 컨테이너 보안 검사 (container security inspection)
게이트키퍼는 모든 커밋에 대해 맹목적으로 전체 저장소 (repository)를 분석해서는 안 됩니다.
그의 첫 번째 책임은 범위 축소 (scope reduction)입니다.
- 검증 에이전트 (Validation Agent)
검증 에이전트는 다음 질문에 답합니다:
제안된 변경 사항이 구조적으로 유효한가?
Python의 경우, 다음을 포함할 수 있습니다:
ruff check .
pytest
mypy .
SQL의 경우:
sqlfluff lint migrations/
인프라의 경우:
terraform validate
컨테이너의 경우:
docker build .
중요한 점은 에이전트가 검증을 스스로 만들어내지 않는다는 것입니다.
에이전트는 기성 도구들을 오케스트레이션 (orchestrate)하고 그 결과들을 정규화 (normalize)합니다.
개념적으로는 다음과 같습니다:
validation_result = {
"syntax": "PASS",
"tests": "PASS",
"lint": "PASS",
"violations": []
}
이것은 이후 에이전트들을 위한 증거 (evidence)가 됩니다.
- 보안 검사 에이전트 (Security Inspection Agent)
보안 에이전트는 악용 가능하거나 운영상 위험한 변경 사항에 집중합니다.
입력값에는 다음의 결과들이 포함될 수 있습니다:
CodeQL
Semgrep
Secret Scanner (비밀 정보 스캐너)
Dependency Scanner (의존성 스캐너)
IaC Scanner (IaC 스캐너)
SQL Policy Engine (SQL 정책 엔진)
Container Scanner (컨테이너 스캐너)
예를 들어:
security_result = {
"critical": 0,
"high": 1,
"medium": 3,
"secrets_detected": False
}
그러면 에이전트는 실제 코드 차이 (code diff)와 함께 이러한 발견 사항들을 평가할 수 있습니다.
이러한 구분은 중요합니다.
스캐너는 발견 사항을 탐지합니다.
에이전트는 이러한 변경 사항들이 배포 측면에서 갖는 중요성을 추론하는 데 도움을 줍니다.
- 위험한 SQL은 별도의 게이트(Gate)가 필요합니다
데이터베이스 변경은 특히 흥미로운 주제입니다. 완벽하게 유효한 SQL이라 할지라도 운영 측면에서는 재앙적인 결과를 초래할 수 있기 때문입니다.
다음 사례를 고려해 보십시오:
DELETE FROM customer_orders;
이 문장은 구문론적(syntactically)으로 유효할 수 있습니다.
하지만 WHERE 절의 부재는 이를 매우 위험하게 만들 수 있습니다.
마찬가지로:
DROP DATABASE production;
이 문장 역시 구문론적으로 유효할 수 있지만, 명백히 예외적인 권한 부여가 필요합니다.
정책 검사(policy inspection)를 수행할 가치가 있는 다른 작업들은 다음과 같습니다:
DROP TABLE
DROP SCHEMA
TRUNCATE TABLE
WHERE 절 없는 DELETE
WHERE 절 없는 UPDATE
ALTER TABLE DROP COLUMN
GRANT
REVOKE
CREATE OR REPLACE
하지만 단순한 키워드 차단(keyword blocking)만으로는 불충분합니다.
예를 들어:
DROP TABLE temp_integration_test;
이 명령이 격리된 휘발성 환경(ephemeral environment) 내부에서 실행된다면 완벽하게 허용될 수 있습니다.
따라서 검사 결정은 다음 요소들을 고려해야 합니다:
문장 (Statement)
+
환경 (Environment)
+
객체 분류 (Object classification)
+
마이그레이션 컨텍스트 (Migration context)
+
권한 (Permissions)
+
의존성 (Dependencies)
+
정책 (Policy)
이 지점에서 에이전트 기반 추론(agentic reasoning)이 유용해집니다.
- 정규 표현식(Regex) 보안 시스템이 아닌 AST를 구축하십시오
운영 환경에서 제가 피하고 싶은 실수 중 하나는 다음과 같이 표현식을 사용하여 SQL 보안을 완전히 구현하는 것입니다:
if "DROP" in sql.upper():
reject()
이는 취약합니다.
SQL에는 다음과 같은 요소들이 존재하기 때문입니다:
주석 (comments),
별칭 (aliases),
중첩 문장 (nested statements),
저장 프로시저 (stored procedures),
방언 차이 (dialect differences),
템플릿 (templating),
동적 SQL (dynamic SQL),
따옴표로 묶인 식별자 (quoted identifiers).
더 강력한 아키텍처는 SQL을 추상 구문 트리 (AST, Abstract Syntax Tree)로 파싱합니다.
개념적으로는 다음과 같습니다:
SQL
↓
렉서 (Lexer)
↓
파서 (Parser)
↓
AST
↓
정책 엔진 (Policy Engine)
↓
위험 평가 (Risk Evaluation)
예를 들어:
DELETE
├── 대상(target): customer_orders
└── 조건(where): null
이제 정책 엔진은 구조적으로 추론할 수 있습니다:
if statement.type == "DELETE" and statement.where is None:
risk = "CRITICAL"
이는 부분 문자열 매칭(substring matching)보다 훨씬 더 신뢰할 수 있는 방식입니다.
- 왜 LangGraph가 이 문제에 적합한가
배포 게이트(deployment gate)는 본질적으로 단일 프롬프트(single prompt)가 아닙니다.
그것은 워크플로우 (workflow)입니다.
LangGraph는 다음과 같은 방식을 사용하여 워크플로우를 모델링합니다:
state (상태),
nodes (노드),
edges (엣지),
conditional edges (조건부 엣지).
이는 CI/CD 검사 (inspection)와 놀라울 정도로 잘 매핑됩니다.
우리의 공유 상태 (shared state)는 개념적으로 다음과 같이 보일 수 있습니다:
class PipelineState(TypedDict):
changed_files: list[str]
validation_results: dict
security_results: dict
sql_results: dict
architecture_results: dict
risk_score: int
decision: str
그 다음 노드 (nodes)를 정의합니다:
detect_changes
validate
inspect_security
inspect_sql
review_architecture
calculate_risk
deployment_gate
그리고 이들을 연결합니다:
START
↓
detect_changes
↓
validate
↓
inspect_security
↓
inspect_sql
↓
review_architecture
↓
calculate_risk
↓
deployment_gate
↓
END
하지만 실제 파이프라인 (pipelines)에는 분기 (branching)가 필요합니다.
그 지점에서 StateGraph가 특히 유용해집니다.
- 조건부 라우팅 (Conditional Routing)
문서 (documentation)만 변경되었다고 가정해 봅시다.
데이터베이스 검사 (database inspection)를 실행하는 것은 거의 의미가 없습니다.
우리는 동적으로 라우팅 (route)할 수 있습니다:
def route_change(state):
files = state["changed_files"]
if any(f.endswith(".sql") for f in files):
return "sql_inspection"
...
그러면 그래프는 배포 정책 (deployment policy)을 명시적으로 나타냅니다.
개념적으로:
Change Detector
│
┌──────────┼───────────┐
...
이는 전체 결정 프로세스를 하나의 거대한 시스템 프롬프트 (system prompt) 안에 숨기는 것보다 감사 (audit)하기가 실질적으로 훨씬 쉽습니다.
- 리뷰 에이전트 (The Review Agent)
리뷰 에이전트 (Review Agent)는 다른 질문을 던집니다:
변경 사항이 기술적으로 유효하더라도, 아키텍처 (architectural) 측면에서 타당한가?
이전에 다음과 같은 구조를 사용하던 서비스를 상상해 보십시오:
API
↓
Service Layer
↓
Repository
↓
Database
개발자가 다음과 같은 구조를 도입합니다:
API
↓
Direct Database Query (직접 데이터베이스 쿼리)
이 코드는 다음과 같은 상태일 수 있습니다:
컴파일 (compile) 되고,
테스트 (tests) 를 통과하며,
SQL 린팅 (SQL linting) 을 통과하고,
명백한 취약점 (vulnerability) 이 없을 수 있습니다.
하지만 이는 확립된 아키텍처를 위반할 수 있습니다.
리뷰 에이전트 (Review Agent)는 리포지토리 문서 (repository documentation) 또는 구조화된 정책 리소스 (structured policy resources)에 저장된 아키텍처 정책과 차이점 (diff)을 비교할 수 있습니다.
예를 들어:
architecture/
principles.md
service-boundaries.yaml
database-policy.yaml
security-policy.yaml
출력 결과는 다음과 같을 수 있습니다:
{
"status": "WARN",
"rule": "ARCH-DB-004",
"reason": "API layer directly accesses persistence layer",
"confidence": 0.94
}
결정적으로, 이는 대개 의심할 여지 없는 진실이 아니라 의사결정 지원을 위한 증거 (decision-support evidence)로 취급되어야 합니다.
- MCP: 도구 액세스의 표준화 (Standardizing Tool Access)
에이전트에게는 도구 (tools)가 필요합니다.
SQL 에이전트에게는 다음과 같은 것들이 필요할 수 있습니다:
SQL 파서 (SQL parser)
스키마 메타데이터 (Schema metadata)
의존성 그래프 (Dependency graph)
마이그레이션 이력 (Migration history)
보안 에이전트 (Security Agent)에게는 다음과 같은 것들이 필요할 수 있습니다:
SAST 결과
의존성 스캐너 (Dependency scanner)
비밀 정보 스캐너 (Secret scanner)
리포지토리 메타데이터 (Repository metadata)
리뷰 에이전트 (Review Agent)에게는 다음과 같은 것들이 필요할 수 있습니다:
아키텍처 문서 (Architecture documents)
정책 리포지토리 (Policy repository)
풀 리퀘스트 차이 (Pull-request diff)
서비스 카탈로그 (Service catalog)
모든 통합 사항을 모든 에이전트에 직접 하드코딩하는 방식은 유지보수가 매우 어려워집니다.
이 지점에서 Model Context Protocol (MCP)이 유용해집니다.
MCP는 서버가 다음과 같은 기본 요소 (primitives)를 노출할 수 있는 클라이언트-서버 아키텍처를 정의합니다:
도구 (tools),
리소스 (resources),
프롬프트 (prompts).
개념적인 배포 아키텍처는 다음과 같습니다:
에이전트 (Agent)
│
MCP 클라이언트 (MCP Client)
│
├── 리포지토리 MCP 서버 (Repository MCP Server)
├── SQL MCP 서버 (SQL MCP Server)
├── 보안 MCP 서버 (Security MCP Server)
├── 정책 MCP 서버 (Policy MCP Server)
└── 배포 MCP 서버 (Deployment MCP Server)
예를 들어:
SQL MCP 서버
도구 (tools):
parse_sql
inspect_schema
calculate_dependency_impact
리소스 (resources):
schema://production
migrations://history
이는 다음과 같은 요소들 사이에 깔끔한 분리를 만들어냅니다:
추론 (Reasoning)
그리고:
시스템 액세스 (System Access)
이러한 분리는 프로덕션 에이전트 아키텍처에서 매우 가치 있는 요소입니다.
- MCP가 도구의 안전성을 자동으로 보장하지는 않습니다
이 점은 강조할 가치가 있습니다.
MCP는 상호작용을 표준화합니다.
하지만 노출된 모든 작업이 마법처럼 신뢰할 수 있게 만들어주는 것은 아닙니다.
MCP 명세(specification)는 도구 실행을 보안에 민감한 사항으로 명시적으로 취급하며, 강력한 사용자 제어, 권한 부여 (authorization), 그리고 데이터 보호를 권장합니다.
따라서 프로덕션 CI/CD MCP 서버는 다음과 같은 사항을 강제해야 합니다:
인증 (Authentication)
인가 (Authorization)
최소 권한 (Least privilege)
입력 유효성 검사 (Input validation)
감사 로깅 (Audit logging)
속도 제한 (Rate limiting)
환경 경계 (Environment boundaries)
자격 증명 격리 (Credential isolation)
예를 들어, SQL 검사 에이전트 (SQL inspection agent)는 일반적으로 읽기 전용 메타데이터 액세스 권한만 부여받아야 합니다.
해당 에이전트는 다음과 같은 명령을 실행할 수 있는 자격 증명을 자동으로 부여받아서는 안 됩니다:
DROP DATABASE production;
도구 권한 (Tool permissions)은 최소 권한 원칙 (principle of least privilege)을 따라야 합니다.
- A2A의 역할
MCP와 A2A는 서로 다른 아키텍처 문제를 해결합니다.
유용한 멘탈 모델 (mental model)은 다음과 같습니다:
MCP
에이전트 (Agent) ↔ 도구 (Tools) / 리소스 (Resources)
A2A
에이전트 (Agent) ↔ 에이전트 (Agent)
A2A는 독립적인 에이전트 시스템 간의 상호 운용성 (interoperability)을 위해 설계되었습니다.
이는 검증 책임이 독립적으로 배포된 서비스로 분리될 때 유용해집니다.
예를 들어:
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기