use GenServer

eesope·2025년 1월 28일

elixir

목록 보기
2/5

trouble shootings

“call vs cast” 개념 정리

2-1) GenServer.call(pid, message)
동기 요청(synchronous):
호출한 쪽(클라이언트)은 응답을 받을 때까지 기다립니다.
서버의 handle_call/3가 실행되어 {:reply, result, new_state}를 반환하면, 그 result가 자동으로 클라이언트에게 돌아갑니다.
따라서 직접 send/2를 쓰지 않아도, call을 쓴 쪽에서 결과를 받을 수 있음.
2-2) GenServer.cast(pid, message)
비동기 요청(asynchronous):
즉시 :ok를 반환하고, 서버가 작업을 끝내도 클라이언트에게 자동으로 응답이 돌아가지 않습니다.
서버는 handle_cast/2 안에서 뭔가를 처리할 수 있지만, 클라이언트는 그 결과를 기다리지 않는 구조.
작업 결과를 클라이언트로 다시 알리려면, 추가로 send/2나 또 다른 GenServer.cast(...) 등을 써서 수동으로 전달해야 합니다.

handle_cast/2 콜백은 “비동기 요청”(cast)을 처리하기 위한 함수이기 때문에,
OTP GenServer 규약상 반드시 {:noreply, new_state}(또는 {:stop, reason, new_state} 등) 형태를 반환해야 합니다.
즉, 클라이언트에게 직접 응답을 돌려주지 않기 위해noreply” 라는 튜플 형식을 사용합니다.

반면에 동기 요청(call)을 처리하는 handle_call/3 콜백은 클라이언트가 결과를 기다리므로,
{:reply, response, new_state}처럼 응답(response)을 포함하는 튜플을 반환하게 됩니다.

따라서,

  • handle_cast{:noreply, ...} (또는 {:stop, reason, ...})
  • handle_call{:reply, some_value, ...}
    라는 형식으로 콜백별로 정해진 반환 규칙을 따르는 것입니다.

:ok를 반환하지 않는 이유는, GenServer가 내부적으로 “비동기 메시지”에 대한 응답 흐름이 없다고 간주하기 위해 :noreply를 사용하기 때문입니다.

#PID<0.135.0>Elixir가 콘솔에서 PID를 표시하는 방식일 뿐,
소스 코드 안에서 0.135.0 처럼 숫자 형태로 적을 수 있는 게 아닙니다.

즉,

Server.shuffle(0.135.0)

처럼 입력하면, Elixir는 이를 “소수점이 세 번 들어간 잘못된 숫자 리터럴”로 해석하여 SyntaxError를 내보냅니다.


PID를 함수 인자로 어떻게 넘길까?

  1. PID를 변수에 저장해서 이용하는 방식이 가장 흔합니다. 예:

    iex> {:ok, pid1} = Server.start()
    {:ok, #PID<0.135.0>}
    
    iex> {:ok, pid2} = Server.start()
    {:ok, #PID<0.136.0>}
    
    # shuffle을 호출하고 싶다면
    iex> Server.shuffle(pid1)
    :ok
    
    iex> Server.shuffle(pid2)
    :ok
  2. 다른 방법: IO.inspect(pid, label: "My Pid:") 등으로 출력하면 #PID<0.135.0> 형태로 보이지만, 이 표기는 사람이 확인하기 쉽게 변환된 문자열일 뿐, Elixir 소스에서 사용 가능한 리터럴이 아닙니다.

요약하자면,
PID는 변수 등에 담아두고 그 변수를 Server.shuffle(pid) 형태로 사용해야 합니다.
콘솔에서 {:ok, pid} = Server.start()를 한 뒤 pid 변수를 사용하면 됩니다.

GenServer는
GenServer.cast(비동기) → handle_cast가 처리한 뒤 응답 없이 끝
GenServer.call(동기) → handle_call이 {:reply, result, new_state}로 응답을 주면 Elixir가 내부적으로 클라이언트에게 결과를 반환

GenServer에서 말하는 state 란?

Elixir의 GenServer는 하나의 프로세스로 동작하며, 해당 프로세스가 내부적으로 “현재 보관하고 있는 데이터”나 “작업에 필요한 정보”를 state라는 이름으로 관리합니다.

즉, state는 “이 프로세스가 지금 기억하고 있어야 할 모든 데이터”를 뜻합니다.
예: 사용자 정보를 담을 수도 있고, 지금까지 처리해온 통계 자료, 혹은 다음에 보낼 메시지 인덱스 등 프로세스별로 필요한 모든 것을 담을 수 있습니다.
OTP 철학에서, “프로세스 단위로 상태(state)를 격리”하는 것이 핵심인데,

한 프로세스는 자신의 상태를 직접 관리(GenServer 콜백 init/1에서 초기값 설정 → 이후 handle_call/3, handle_cast/2 등에서 갱신)하고,
다른 프로세스와는 메시지를 통해서만 통신합니다.

atom 은 variable 이 아니므로 넘길 수 없음 -> 변수에 담아서 넘겨야

profile
go simple 🧑🏻‍💻

0개의 댓글