LLM 시대의 동시성 제어: Colored Petri Nets와 Rust가 답이 될 수 있을까?


최근 LLM을 활용한 소프트웨어 개발(Software Development)이 화두가 되면서, 우리가 직면한 가장 큰 역설 중 하나는 “코드를 짜는 건 쉬워졌지만, 그 코드가 올바른지 검증하는 건 더 어려워졌다” 는 점입니다. 특히 Race Condition이나 Deadlock 같은 동시성(Concurrency) 버그는 LLM이 생성한 코드에서 잡아내기가 정말 까다롭습니다.

그러던 중 흥미로운 아티클 하나를 발견했습니다. 바로 Colored Petri Nets(CPNs) 를 이용해 분산 애플리케이션의 복잡성을 해결하려는 시도입니다. 학부 시절 오토마타 수업에서나 스쳐 지나갔던 Petri Nets가 Rust, 그리고 LLM과 만나 어떻게 현대적인 엔지니어링 도구가 될 수 있는지, Principal Engineer 관점에서 분석해봤습니다.

Colored Petri Nets(CPNs): 잊혀진 기술의 귀환?

기본적인 Petri Nets는 상태(Place)와 전이(Transition)로 이루어진 이분 그래프(Bipartite Graph)입니다. 토큰이 이곳저곳을 옮겨 다니며 시스템의 상태를 표현하죠. 하지만 기본 Petri Nets는 토큰에 ‘데이터’가 없습니다. 단순히 “여기에 토큰이 있다/없다”만 따지기 때문에 유한 상태 머신(FSM)과 다를 바가 없습니다.

Colored Petri Nets 는 여기에 ‘색깔(Color)’, 즉 데이터 타입 을 입힙니다. 토큰이 단순한 점이 아니라, 실제 데이터(Payload)를 가진 객체가 되는 것입니다. 이 지점에서 필자는 무릎을 탁 쳤습니다.

  • 데이터 + 상태 전이: 이것은 정확히 Rust의 Typestate Pattern 과 일치합니다.
  • Guards: 특정 조건(예: 커넥션 풀에 여유가 있을 때만)이 만족되어야 전이가 일어납니다.

이 모델이 매력적인 이유는 동시성 제어의 가장 골치 아픈 부분들—상태 동기화, 충돌 감지, 리소스 잠금—을 애플리케이션 로직에서 분리하여 네트워크 구조 자체로 정의 할 수 있기 때문입니다.

실전 예시: Web Scraper 스케줄링

아티클에서는 Web Scraper를 예로 듭니다. 시니어 엔지니어라면 공감하겠지만, 대규모 스크래퍼를 만들 때 가장 더러운 코드는 HTTP 요청 로직이 아니라 “조율(Coordination)” 로직입니다.

  • “이 도메인은 1초에 5번만 요청해야 해.”
  • “프록시 IP가 다 떨어지면 기다려야 해.”
  • “이미 긁고 있는 URL은 중복으로 큐에 넣으면 안 돼.”

보통 우리는 이걸 Redis Lock이나 DB의 SELECT FOR UPDATE, 혹은 복잡한 인메모리 큐로 해결합니다. 코드는 지저분해지고, 버그가 숨을 곳은 많아집니다.

CPN을 사용하면 이 문제를 “Join” 연산으로 풀 수 있습니다.

Scrape_Target 전이는 Available_Proxies 토큰과 Prioritized_Targets 토큰이 모두 존재할 때만 발생한다.

즉, 프록시와 타겟 URL이 모두 준비된 순간에만 ‘스크래핑’이라는 트랜잭션이 일어나는 겁니다. 개발자가 명시적으로 Lock을 걸고 해제하는 코드를 짤 필요가 없습니다. 네트워크의 구조가 곧 로직이 되니까요.

왜 지금 CPN인가? (feat. LLM & Rust)

솔직히 2024년에 Petri Nets 이야기를 듣게 될 줄은 몰랐습니다. 하지만 저자가 제시하는 비전은 꽤 설득력이 있습니다.

  1. LLM을 위한 가드레일: LLM에게 “동시성 코드를 짜줘”라고 하면 엉망인 코드를 뱉어내지만, “이 CPN 정의에 맞는 핸들러를 짜줘”라고 하면 이야기가 달라집니다. 구조적으로 검증 가능한(Verifiable) 틀 안에서 코드를 생성하게 함으로써 안전성을 확보할 수 있습니다.
  2. Rust와의 궁합: CPN의 시맨틱은 Rust의 소유권(Ownership) 모델 및 타입 시스템과 놀라울 정도로 잘 맞습니다. 컴파일 타임에 많은 동시성 오류를 잡아낼 수 있다는 뜻입니다.

현실적인 한계와 고민

물론 장밋빛 미래만 있는 건 아닙니다. 저 역시 현업에서 이런 Formal Method를 도입하려다 실패한 경험이 있기에 몇 가지 우려되는 점이 보입니다.

  • Persistence (영속성): 아티클에서는 SQLite 스냅샷을 언급하지만, 수백만 개의 토큰이 1초에 수천 번 이동하는 고성능 환경에서 DB가 병목이 되지 않게 만드는 건 매우 어려운 엔지니어링 챌린지입니다.
  • Partitioning: 단일 서버 메모리를 넘어가는 순간, 네트워크를 어떻게 쪼개서 분산 처리할 것인가? 저자도 이 부분은 아직 고민 중인 것으로 보입니다.
  • 러닝 커브: 팀원들에게 “자, 오늘부터 비즈니스 로직을 Petri Net으로 그립니다”라고 했을 때의 반발을 어떻게 감당할 것인가도 문제입니다.

결론: 흥미로운 베팅

저자는 현재 spider-rs라는 Rust 기반 스크래퍼의 코어를 CPN으로 재구현하여 기존의 Ad-hoc 방식과 복잡도를 비교하는 실험을 진행 중이라고 합니다.

개인적으로 이 시도가 “복잡한 분산 시스템의 로직을 선언형(Declarative)으로 바꾸는 키” 가 될 수 있다고 봅니다. 쿠버네티스가 인프라의 상태를 선언적으로 관리하듯, 애플리케이션의 동시성 상태도 선언적으로 관리할 때가 되었습니다. 만약 성공한다면, 우리는 LLM 시대에 걸맞은 새로운 형태의 백엔드 프레임워크를 보게 될지도 모릅니다.

아직 Hacker News에는 댓글이 달리지 않았지만(초기 단계라는 뜻이죠), 백엔드 엔지니어라면 이 databuild와 CPN의 결합을 주목해볼 필요가 있습니다. 저도 주말에 Rust로 간단한 프로토타이핑을 해볼 생각입니다.

References: