─━ IT ━─

자물쇠(Lock)와 싸우지 마세요! ⚔️ 동시성 문제의 우아한 해법, 액터 모델 (The Actor Model)

DKel 2025. 9. 21. 22:32
반응형

은행 계좌에서 1만 원을 출금하는 동시에, 다른 쪽에서는 5천 원을 입금하는 코드가 있다고 상상해 봅시다. 만약 이 두 작업이 정확히 같은 시간에 '계좌 잔액'이라는 하나의 데이터를 동시에 수정하려고 하면 어떻게 될까요? 데이터가 꼬이거나(Race Condition), 서로가 끝나기만을 기다리다 영원히 멈춰버리는(Deadlock) 끔찍한 버그가 발생할 수 있습니다.

이런 문제를 막기 위해 개발자들은 지난 수십 년간 '락(Lock)' 이나 '뮤텍스(Mutex)' 같은 복잡한 '자물쇠'를 사용하며 데이터 주변을 지키는 힘겨운 싸움을 해왔습니다. 하지만 이 자물쇠를 거는 타이밍을 조금이라도 잘못 맞추면, 시스템 전체가 멈추는 대참사가 일어나기 일쑤였죠.

액터 모델(Actor Model) 은 이 문제에 대해 "애초에 싸울 일을 만들지 않으면 어떨까?"라는 완전히 다른 해답을 제시합니다.

액터 모델이란? 완벽한 자율 근무 회사 🏢

액터 모델은 시스템 전체를 '액터(Actor)' 라는 독립적인 일꾼들의 집합으로 보는 개념입니다. 이 모델을 이해하는 가장 좋은 방법은 시스템을 하나의 '회사'로, 모든 액터를 그 회사의 '직원'으로 비유하는 것입니다. 이 회사에는 세 가지 철칙이 있습니다.

  1. 직원은 완벽히 독립적이다 (상태 분리): 모든 직원(액터)은 각자 자신의 책상과 자신의 머릿속 생각(Private State)을 가지고 있습니다. 다른 직원은 절대 남의 책상을 뒤지거나 머릿속을 들여다볼 수 없습니다. 즉, 액터 간에는 메모리를 절대 공유하지 않습니다.
  2. 모든 소통은 이메일로만 한다 (비동기 메시지 전달): 다른 직원에게 일을 시키고 싶으면, 어깨를 툭툭 치며 말을 거는 대신, 그 직원의 메일함(Mailbox)에 이메일(Message) 을 보냅니다. 직원은 자신의 메일함에 쌓인 이메일을 순서대로, 자신의 페이스에 맞춰 하나씩 처리합니다. 이메일을 보낸 사람은 상대방이 즉시 답장하리라 기대하지 않습니다. (비동기)
  3. 직원은 새로운 직원을 고용할 수 있다 (액터 생성): 한 직원은 자신의 업무를 돕게 하기 위해 새로운 직원(자식 액터)을 직접 고용하고 관리할 수 있습니다.

이 세 가지 규칙을 지키면 어떤 일이 벌어질까요? 직원 두 명이 동시에 '회사 매출 장부'라는 하나의 노트를 고치려 다툴 일이 원천적으로 사라집니다. 장부는 오직 '회계팀'이라는 액터만 접근할 수 있고, 다른 모든 액터는 그에게 "매출을 500만 원 추가해 줘"라는 이메일을 보내야만 하니까요.

은행 계좌 이체의 예시

위에서 언급한 계좌 이체 문제를 액터 모델로 풀어보면 이렇습니다.

코드 스니펫
 
[상황] 'A계좌 액터' (잔액: 10,000원)가 'B계좌 액터'에게 1,000원을 이체한다.

1.  '송금 담당 액터'가 'A계좌 액터'의 메일함으로 메시지를 보낸다.
    - To: A계좌 액터
    - Message: { type: 'WITHDRAW', amount: 1000, target: 'B계좌 액터' }

2.  'A계좌 액터'는 자신의 메일함에서 메시지를 꺼내 처리한다.
    - 자신의 잔액을 10,000원에서 9,000원으로 변경한다. (오직 자신만이 자신의 잔액을 만질 수 있다!)
    - 작업이 끝나면 'B계좌 액터'의 메일함으로 메시지를 보낸다.
    - To: B계좌 액터
    - Message: { type: 'DEPOSIT', amount: 1000 }

3.  'B계좌 액터'는 나중에 자신의 메일함에서 메시지를 꺼내 처리한다.
    - 자신의 잔액에 1,000원을 더한다.

4.  끝.

어떤 순간에도 '잔액'이라는 데이터를 두고 경쟁하거나 충돌할 가능성이 없습니다. 모든 것은 순서가 보장된 메시지를 통해 이루어지기 때문입니다. 복잡한 '락'이 코드에서 완전히 사라졌습니다!

액터 모델의 초능력: 장애 복구와 확장성

이 모델의 진정한 힘은 장애 복구 능력(Fault Tolerance) 에서 나옵니다. 한 직원(액터)이 갑자기 아파서 쓰러지면, 그를 고용한 상사(슈퍼바이저 액터)가 이를 감지하고 "이 직원을 잠시 쉬게 하고 다시 깨울까?" 혹은 "아예 해고하고 새로운 직원으로 대체할까?"를 결정할 수 있습니다. 시스템 일부가 죽어도 전체가 멈추지 않는, 놀랍도록 튼튼한 서비스를 만들 수 있죠. (WhatsApp 같은 통신 시스템이 사용하는 Erlang/Elixir 언어의 핵심 철학입니다.)

또한, 액터들은 서로 완벽히 독립적이기 때문에, 수천 개의 CPU 코어나 수백 대의 서버에 액터들을 분산시켜도 시스템은 아무 문제 없이 돌아갑니다. 엄청난 확장성(Scalability) 을 기본으로 갖추게 되는 셈이죠.

마치며

액터 모델은 동시성이라는 어려운 문제를 정면으로 돌파하는 대신, "애초에 공유하지 않으면 문제가 생기지 않는다"는 발상의 전환을 통해 우회하는 우아한 해법입니다.

복잡한 락과 스레드 동기화 코드 대신, 격리된 행위자와 메시지만으로 시스템을 설계하는 것. 이는 단순한 기술을 넘어, 분산 시스템을 바라보는 하나의 철학에 가깝습니다. 동시성 문제로 고통받아 본 개발자라면, 이 아름다운 모델의 세계를 탐험해 볼 가치는 충분합니다. 🎭

반응형