GenServer 동작 구현
GenServer를 구현하여 상태를 관리하고 동기 및 비동기 호출을 처리하는 방법을 학습합니다. 이는 대부분의 Erlang 구성 요소를 이루는 기반입니다.
GenServer 동작 구현은(는) CoddyKit의 무료 Erlang OTP: Distributed & Fault-Tolerant Systems Programming 강의입니다. 이것은 4개 중 2번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Erlang OTP: Distributed & Fault-Tolerant Systems Programming 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Erlang OTP: Distributed & Fault-Tolerant Systems Programming 강의에는 총 4개의 강의가 포함되어 있습니다.
이 강의의 일부는 아직 번역되지 않았으며 영어로 표시됩니다.
What is a GenServer?
Welcome back! In the previous lesson, we learned about Erlang's OTP behaviors. One of the most important is the GenServer.
- A GenServer is a generic server that handles requests.
- It manages internal state and processes messages in a sequential manner.
- Think of it as a standardized way to build reliable, fault-tolerant server processes in Erlang.
It's the foundation for many Erlang applications.
GenServer's Core: Callbacks
To implement a GenServer, you define a module that exports specific callback functions. These functions are called by the gen_server behavior at different stages.
init/1: Initializes the server's state.handle_call/3: Handles synchronous client requests (expects a reply).handle_cast/2: Handles asynchronous client requests (fire-and-forget).terminate/2: Cleans up when the server stops.code_change/3: Handles hot code upgrades.
We'll focus on init, handle_call, and handle_cast in this lesson.
Initializing State with `init/1`
Every GenServer starts by initializing its state. This is done in the init/1 callback function.
It takes one argument (typically a list of arguments passed during startup) and should return {ok, State}, where State is the initial data your GenServer will manage.
Here's a basic init function:
-module(my_counter).
-behaviour(gen_server).
-export([init/1]).
init([]) ->
io:format("Counter initialized with state 0.~n"),
{ok, 0}.Starting a GenServer Process
To get our GenServer running, we need to start it. The common way is using gen_server:start_link/3 or gen_server:start_link/4. We'll add a start_link/0 function to our module.
The start_link function creates a new Erlang process and links it to the calling process, making it part of a supervision tree (more on this later!).
-module(my_counter).
-behaviour(gen_server).
-export([start_link/0, init/1]).
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
init([]) ->
io:format("Counter process started!~n"),
{ok, 0}.Synchronous Calls: `handle_call`
When a client needs a reply from the server, it makes a synchronous call using gen_server:call/2 or gen_server:call/3.
The GenServer handles these requests in its handle_call/3 callback. This function takes three arguments:
Request: The message from the client.From: The sender's process ID (Pid) and a tag.State: The current internal state of the GenServer.
It typically returns {reply, Reply, NewState}.
Implementing `handle_call` (Counter)
Let's add an increment function to our counter. This will be a synchronous call, meaning the client waits for the new count.
Try running this code. First, compile it (`c(my_counter).`), then start it (`my_counter:start_link().`). You can then call `my_counter:increment().` to see the counter increase.
-module(my_counter).
-behaviour(gen_server).
-export([start_link/0, increment/0, get_count/0]).
-export([init/1, handle_call/3, handle_cast/2, terminate/2, code_change/3]).
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
increment() ->
gen_server:call(?MODULE, increment).
get_count() ->
gen_server:call(?MODULE, get_count).
init([]) ->
{ok, 0}.
handle_call(increment, _From, State) ->
NewState = State + 1,
{reply, NewState, NewState};
handle_call(get_count, _From, State) ->
{reply, State, State};
handle_call(_Request, _From, State) ->
{reply, {error, bad_request}, State}.
handle_cast(_Msg, State) -> % Placeholder
{noreply, State}.
terminate(_Reason, _State) -> ok.
code_change(_OldVsn, State, _Extra) -> {ok, State}.Asynchronous Calls: `handle_cast`
Sometimes, a client doesn't need a reply and just wants to send a message without waiting. This is an asynchronous call using gen_server:cast/2.
The GenServer handles these messages in its handle_cast/2 callback. It takes two arguments:
Message: The message from the client.State: The current internal state of the GenServer.
It always returns {noreply, NewState} because no reply is sent back to the client.
Implementing `handle_cast` (Reset)
Let's add a reset function to our counter. This will be an asynchronous call, as the client doesn't need to know the new count immediately.
Compile and start the module as before. Call `my_counter:increment().` a few times, then `my_counter:reset().`. You'll notice `reset` returns immediately without a value.
-module(my_counter).
-behaviour(gen_server).
-export([start_link/0, increment/0, get_count/0, reset/0]).
-export([init/1, handle_call/3, handle_cast/2, terminate/2, code_change/3]).
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
increment() ->
gen_server:call(?MODULE, increment).
get_count() ->
gen_server:call(?MODULE, get_count).
reset() ->
gen_server:cast(?MODULE, reset).
init([]) ->
{ok, 0}.
handle_call(increment, _From, State) ->
NewState = State + 1,
{reply, NewState, NewState};
handle_call(get_count, _From, State) ->
{reply, State, State};
handle_call(_Request, _From, State) ->
{reply, {error, bad_request}, State}.
handle_cast(reset, _State) ->
{noreply, 0};
handle_cast(_Msg, State) ->
{noreply, State}.
terminate(_Reason, _State) -> ok.
code_change(_OldVsn, State, _Extra) -> {ok, State}.GenServer State Management
The power of GenServers lies in how they manage state. The State argument is passed into each callback, and the callback returns a NewState.
- This ensures that only one process (the GenServer itself) ever modifies its state, preventing race conditions.
- It makes the server's internal logic easier to reason about.
- The state can be any Erlang term: an integer, a list, a map, a record, or a complex data structure.
This sequential processing of messages and explicit state passing is key to Erlang's concurrency model.
GenServer Call Types
Which of the following statements about GenServer calls are TRUE?
Recap: Implementing GenServer
You've successfully built your first GenServer! Here's what we covered:
- GenServers are standard OTP behaviors for building stateful servers.
- The
init/1callback initializes the server's state. gen_server:start_linkcreates the GenServer process.handle_call/3handles synchronous requests (client waits for reply).handle_cast/2handles asynchronous messages (client doesn't wait).- GenServers manage their state by passing it between callbacks, ensuring sequential updates.
Next, we'll see how supervisors can automatically restart failed GenServers!
자주 묻는 질문
“GenServer 동작 구현” 강의는 무료인가요?
네 — “GenServer 동작 구현” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Erlang OTP: Distributed & Fault-Tolerant Systems Programming 강의 전체를 잠금 해제할 수 있습니다. Erlang OTP: Distributed & Fault-Tolerant Systems Programming 강의에는 총 4개의 강의가 포함되어 있습니다.
“GenServer 동작 구현”에서 뭘 배우나요?
GenServer를 구현하여 상태를 관리하고 동기 및 비동기 호출을 처리하는 방법을 학습합니다. 이는 대부분의 Erlang 구성 요소를 이루는 기반입니다. 브라우저에서 직접 실행하는 실습 코드로 Erlang OTP: Distributed & Fault-Tolerant Systems Programming을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
Erlang OTP: Distributed & Fault-Tolerant Systems Programming을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 Erlang OTP: Distributed & Fault-Tolerant Systems Programming은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 2번째 강의입니다.
“GenServer 동작 구현” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 Erlang OTP: Distributed & Fault-Tolerant Systems Programming 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 Erlang OTP: Distributed & Fault-Tolerant Systems Programming 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.
이 강의의 모든 강의
- OTP 및 동작 이해하기
- GenServer 동작 구현
- 감독자 소개
- OTP 애플리케이션과 릴리스 구축