원격 이벤트 및 콜백

*이 콘텐츠는 AI(베타)를 사용해 번역되었으며, 오류가 있을 수 있습니다. 이 페이지를 영어로 보려면 여기를 클릭하세요.

Roblox 게임은 기본적으로 멀티플레이어이며, 모든 게임은 본질적으로 서버와 플레이어의 연결된 클라이언트 간에 통신합니다. 가장 간단한 경우로, 플레이어가 캐릭터를 이동할 때 Humanoid 속성 중 일부, 예를 들면 상태가 서버에 전달되며, 이는 다른 연결된 클라이언트에 전송됩니다.

원격 이벤트와 콜백은 클라이언트-서버 경계를 넘어 통신할 수 있게 해줍니다:

  • RemoteEvents는 일방향 통신(요청을 보내고 **응답을 기다리지 않음)을 가능하게 합니다.
  • UnreliableRemoteEvents는 지속적으로 변경되거나 게임의 상태에 중요하지 않은 데이터를 위한 일방향 통신을 가능하게 합니다. 이러한 이벤트는 순서 및 신뢰성을 포기하고 네트워크 성능을 개선합니다.
  • RemoteFunctions는 양방향 통신(요청을 보내고 응답을 받을 때까지 기다림)을 가능하게 합니다.

사용 사례가 너무 다양하여 바인더블 이벤트보다 활용도가 높습니다. 다음은 몇 가지 예입니다:

  • 게임플레이 - 레벨 끝에 도달한 플레이어와 같은 기본적인 게임플레이는 원격 이벤트가 필요할 수 있습니다. 클라이언트 스크립트는 서버에 알리고, 서버 스크립트는 플레이어의 위치를 재설정합니다.
  • 서버 검증 - 플레이어가 물약을 마시려고 할 때, 실제로 그 물약을 가지고 있는가? 공정성을 보장하기 위해, 서버는 게임의 진실의 출처여야 합니다. 클라이언트 스크립트는 서버에 플레이어가 물약을 마시고 있다고 알리는 원격 이벤트를 사용할 수 있으며, 서버 스크립트는 플레이어가 실제로 그 물약을 가지고 있는지, 그리고 어떤 이점을 부여할지 결정할 수 있습니다.
  • 사용자 인터페이스 업데이트 - 게임의 상태가 바뀔 때, 서버 스크립트는 원격 이벤트를 사용하여 점수, 목표 등의 변화에 대해 클라이언트에 알릴 수 있습니다.
  • 게임 내 마켓플레이스 구매 - 원격 함수 사용 예제에 대한 자세한 내용은 프롬프트 구독 구매를 참조하세요.

빠른 참조

다음 표는 RemoteEventsRemoteFunctions를 사용하여 클라이언트와 서버 간 통신하는 방법에 대한 빠른 참조를 제공합니다.

클라이언트 → 서버
클라이언트RemoteEvent:FireServer(args)
서버RemoteEvent.OnServerEvent:Connect(function(player, args))
서버 → 클라이언트
서버RemoteEvent:FireClient(player, args)
클라이언트RemoteEvent.OnClientEvent:Connect(function(args))
서버 → 모든 클라이언트
서버RemoteEvent:FireAllClients(args)
클라이언트RemoteEvent.OnClientEvent:Connect(function(args))

원격 이벤트

RemoteEvent 객체는 응답을 기다리지 않고 클라이언트-서버 경계를 가로지르는 비동기 일방향 통신을 용이하게 합니다.

Studio의 탐색기 창을 통해 새로운 RemoteEvent를 만들려면:

  1. RemoteEvent를 삽입하고자 하는 컨테이너 위에 마우스를 올리십시오. 서버와 클라이언트 모두 접근할 수 있도록 하려면 ReplicatedStorage와 같은 두 측에서 볼 수 있는 장소에 있어야 합니다. 경우에 따라서는 WorkspaceTool 내부에 저장하는 것이 적절할 수 있습니다.
  2. 컨테이너 이름 오른쪽에 나타나는 버튼을 클릭하고 RemoteEvent 인스턴스를 삽입합니다.
  3. 인스턴스의 이름을 목적에 맞게 변경합니다.

RemoteEvent를 생성한 후, 이는 클라이언트에서 서버로, 서버에서 클라이언트로, 또는 서버에서 모든 클라이언트로 일방향 통신을 용이하게 할 수 있습니다.

클라이언트 → 서버
서버 → 클라이언트
서버 → 모든 클라이언트

클라이언트 → 서버

LocalScript를 사용하여 FireServer() 메서드를 호출하여 서버에서 이벤트를 트리거할 수 있습니다. FireServer()에 인수를 전달하면, 특정 제한 사항과 함께 서버의 이벤트 핸들러로 전달됩니다. 서버의 이벤트 핸들러 첫 번째 매개변수는 항상 이를 호출하는 클라이언트의 Player 객체이며, 추가 매개변수는 그 뒤를 따릅니다.

클라이언트RemoteEvent:FireServer(args)
서버RemoteEvent.OnServerEvent:Connect(function(player, args))

다음 ScriptOnServerEvent에 이벤트 핸들러를 연결하여 서버에서 새로운 Part를 생성합니다. 그에 따른 LocalScript는 원하는 ColorPosition으로 RemoteEvent 인스턴스의 FireServer()를 호출합니다.

이벤트 연결 - 스크립트

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Workspace = game:GetService("Workspace")
-- 원격 이벤트 인스턴스에 대한 참조 가져오기
local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")
local function onCreatePart(player, partColor, partPosition)
print(player.Name .. "가 RemoteEvent를 발생시켰습니다.")
local newPart = Instance.new("Part")
newPart.Color = partColor
newPart.Position = partPosition
newPart.Parent = Workspace
end
-- 함수와 이벤트 연결
remoteEvent.OnServerEvent:Connect(onCreatePart)
이벤트 발생 - 로컬스크립트

local ReplicatedStorage = game:GetService("ReplicatedStorage")
-- 원격 이벤트 인스턴스에 대한 참조 가져오기
local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")
-- 원격 이벤트를 발생시키고 추가 인수 전달
remoteEvent:FireServer(Color3.fromRGB(255, 0, 0), Vector3.new(0, 25, -20))

서버 → 클라이언트

Script를 사용하여 FireClient() 메서드를 호출하여 클라이언트에서 이벤트를 트리거할 수 있습니다. FireClient()의 첫 번째 인수는 해당 이벤트에 응답할 클라이언트의 Player 객체이며, 추가 인수는 특정 제한 사항과 함께 클라이언트에 전달됩니다. 이벤트 핸들러는 Players.LocalPlayer를 사용하여 클라이언트에서 플레이어를 확인할 수 있으므로 클라이언트의 첫 번째 인수에 Player 객체를 포함할 필요가 없습니다.

서버RemoteEvent:FireClient(player, args)
클라이언트RemoteEvent.OnClientEvent:Connect(function(args))

다음 LocalScriptOnClientEvent 이벤트에 대한 이벤트 핸들러를 연결합니다. 그에 따른 Script는 서버로 들어오는 플레이어를 수신 대기하고 각 플레이어에 대해 임의의 데이터로 FireClient()를 호출합니다.

이벤트 연결 - 로컬스크립트

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
-- 원격 이벤트 인스턴스에 대한 참조 가져오기
local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")
local player = Players.LocalPlayer
local function onNotifyPlayer(maxPlayers, respawnTime)
print("[클라이언트] 플레이어에 의해 이벤트 수신", player.Name)
print(maxPlayers, respawnTime)
end
-- 함수와 이벤트 연결
remoteEvent.OnClientEvent:Connect(onNotifyPlayer)
이벤트 발생 - 스크립트

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
-- 원격 이벤트 인스턴스에 대한 참조 가져오기
local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")
-- 들어오는 플레이어를 수신 대기하고 각 플레이어에 대해 원격 이벤트를 배포
local function onPlayerAdded(player)
print("[서버] 플레이어에게 이벤트 발생", player.Name)
remoteEvent:FireClient(player, Players.MaxPlayers, Players.RespawnTime)
end
Players.PlayerAdded:Connect(onPlayerAdded)

서버 → 모든 클라이언트

Script를 사용하여 FireAllClients() 메서드를 호출하여 모든 클라이언트에서 이벤트를 트리거할 수 있습니다. FireClient()와 다르게 FireAllClients() 메서드는 모든 클라이언트로 RemoteEvent를 발생시킵니다. 그래서 Player 객체가 필요하지 않습니다.

서버RemoteEvent:FireAllClients(args)
클라이언트RemoteEvent.OnClientEvent:Connect(function(args))

다음 LocalScript는 남은 카운트다운 시간을 출력하는 이벤트 핸들러를 OnClientEvent 이벤트에 연결합니다. 그에 따른 Script는 매 초마다 루프에서 FireAllClients()를 호출하여 모든 클라이언트에 대해 RemoteEvent를 발생시킵니다.

이벤트 연결 - 로컬스크립트

local ReplicatedStorage = game:GetService("ReplicatedStorage")
-- 원격 이벤트 인스턴스에 대한 참조 가져오기
local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")
local function onTimerUpdate(seconds)
print(seconds)
end
-- 함수와 이벤트 연결
remoteEvent.OnClientEvent:Connect(onTimerUpdate)
이벤트 발생 - 스크립트

local ReplicatedStorage = game:GetService("ReplicatedStorage")
-- 원격 이벤트 인스턴스에 대한 참조 가져오기
local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")
local countdown = 5
-- 시간이 만료될 때까지 매 초마다 RemoteEvent를 발생시킵니다.
for timeRemaining = -1, countdown do
remoteEvent:FireAllClients(countdown - timeRemaining)
task.wait(1)
end

원격 콜백

RemoteFunction 객체는 클라이언트-서버 경계를 넘어 동기식 양방향 통신을 용이하게 합니다. 원격 함수의 송신자는 수신자로부터 응답을 받을 때까지 대기합니다.

Studio의 탐색기 창을 통해 새로운 RemoteFunction을 만들려면:

  1. RemoteFunction을 삽입하고자 하는 컨테이너 위에 마우스를 올리십시오. 서버와 클라이언트 모두 접근할 수 있도록 하려면 ReplicatedStorage와 같은 두 측에서 볼 수 있는 장소에 있어야 합니다. 경우에 따라서는 WorkspaceTool 내부에 저장하는 것이 적절할 수 있습니다.
  2. 컨테이너 이름 오른쪽에 나타나는 버튼을 클릭하고 RemoteFunction 인스턴스를 삽입합니다.
  3. 인스턴스의 이름을 목적에 맞게 변경합니다.

RemoteFunction을 생성한 후, 이는 클라이언트와 서버 간 또는 서버와 클라이언트 간의 양방향 통신을 용이하게 할 수 있습니다.

클라이언트 → 서버 → 클라이언트
서버 → 클라이언트 → 서버

클라이언트 → 서버 → 클라이언트

LocalScript를 사용하여 InvokeServer() 메서드를 호출하여 서버에 있는 함수를 호출할 수 있습니다. 원격 이벤트와 달리, RemoteFunction을 호출하는 LocalScript는 콜백이 반환될 때까지 대기합니다. InvokeServer()에 전달하는 인수는 특정 제한 사항과 함께 OnServerInvoke 콜백으로 전달됩니다. 동일한 RemoteFunction에 대해 여러 콜백을 정의하면 마지막 정의만 실행됩니다.

클라이언트RemoteFunction:InvokeServer(args)
서버RemoteFunction.OnServerInvoke = function(player, args)

다음 ScriptOnServerInvoke를 통해 콜백 함수를 정의하고 요청된 Part를 반환 값으로 출력합니다. 그에 따른 LocalScript는 요청된 부품의 색상과 위치를 정의하는 추가 인수로 InvokeServer()를 호출합니다.

콜백 연결 - 스크립트

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Workspace = game:GetService("Workspace")
-- 원격 함수 인스턴스에 대한 참조 가져오기
local remoteFunction = ReplicatedStorage:FindFirstChildOfClass("RemoteFunction")
-- 콜백 함수
local function createPart(player, partColor, partPosition)
print(player.Name .. "가 새 부품을 요청했습니다.")
local newPart = Instance.new("Part")
newPart.Color = partColor
newPart.Position = partPosition
newPart.Parent = Workspace
return newPart
end
-- 함수를 원격 함수의 콜백으로 설정
remoteFunction.OnServerInvoke = createPart
이벤트 호출 - 로컬스크립트

local ReplicatedStorage = game:GetService("ReplicatedStorage")
-- 원격 함수 인스턴스에 대한 참조 가져오기
local remoteFunction = ReplicatedStorage:FindFirstChildOfClass("RemoteFunction")
-- 콜백을 호출할 때 색상과 위치 통과
local newPart = remoteFunction:InvokeServer(Color3.fromRGB(255, 0, 0), Vector3.new(0, 25, -20))
-- 반환된 부품 참조 출력
print("서버가 요청된 부품을 생성했습니다:", newPart)

서버 → 클라이언트 → 서버

Script를 사용하여 InvokeClient() 메서드를 호출하여 클라이언트에서 함수를 호출할 수 있습니다. 하지만 다음과 같은 심각한 위험이 따릅니다:

  • 클라이언트가 오류를 발생시키면 서버도 오류를 발생시킵니다.
  • 클라이언트가 연결 해제를 하는 동안 호출되는 경우, InvokeClient()는 오류를 발생시킵니다.
  • 클라이언트가 값을 반환하지 않으면 서버는 영원히 대기하게 됩니다.

양방향 통신이 필요하지 않은 액션, 예를 들어 GUI를 업데이트하는 경우에는 RemoteEvent를 사용하고 서버에서 클라이언트로 통신하십시오.

인수 제한 사항

RemoteEvent를 발생시키거나 RemoteFunction를 호출할 때, 전달하는 인수를 이벤트나 콜백 함수와 함께 전달합니다. Enum, Instance와 같은 Roblox 객체와 숫자, 문자열, 불리안과 같은 Luau 타입은 전달할 수 있지만, 아래의 제한 사항을 주의 깊게 살펴보십시오.

문자열이 아닌 인덱스

전달된 테이블의 어떤 인덱스Instance, userdata, 또는 function과 같은 문자열이 아닌 타입인 경우, Roblox는 해당 인덱스를 자동으로 문자열로 변환합니다.

이벤트 연결 - 로컬스크립트

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")
local function onEventFire(passedTable)
for k, v in passedTable do
print(typeof(k)) --> string
end
end
-- 함수와 이벤트 연결
remoteEvent.OnClientEvent:Connect(onEventFire)
이벤트 발생 - 스크립트

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
local Workspace = game:GetService("Workspace")
local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")
-- 들어오는 플레이어를 수신 대기하고 각 플레이어에 대해 원격 이벤트를 배포
local function onPlayerAdded(player)
remoteEvent:FireClient(player,
{
[Workspace.Baseplate] = true
}
)
end
Players.PlayerAdded:Connect(onPlayerAdded)

전달된 함수

RemoteEvent 또는 RemoteFunction에 인수로 포함된 함수는 클라이언트-서버 경계를 넘어 복제되지 않으며, 원격으로 함수를 전달하는 것이 불가능합니다. 대신 수신 측의 결과 인수는 nil이 됩니다.

이벤트 연결 - 로컬스크립트

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")
local function onClientEvent(func)
print(func) --> nil
end
remoteEvent.OnClientEvent:Connect(onClientEvent)
이벤트 발생 - 스크립트

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")
local function testFunction()
print("안녕하세요!")
end
-- 함수 인수를 포함하여 원격 이벤트 발생
remoteEvent:FireAllClients(testFunction)

테이블 인덱싱

데이터 테이블을 전달할 경우, 문자열 키와 숫자 키가 혼합된 테이블을 전달하지 마십시오. 대신 완전히 키-값 쌍(사전)으로 구성된 테이블 또는 완전히 숫자 인덱스로 구성된 테이블을 전달하십시오.

이벤트 연결 - 스크립트

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")
local function onEventFire(player, passedTable)
for k, v in passedTable do
print(k .. " = " .. v)
--> 1 = Sword
--> 2 = Bow
--> CharName = Diva Dragonslayer
--> CharClass = Rogue
end
end
-- 함수와 이벤트 연결
remoteEvent.OnServerEvent:Connect(onEventFire)
이벤트 발생 - 로컬스크립트

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")
-- 숫자 인덱스 테이블
local inventoryData = {
"Sword", "Bow"
}
-- 사전 테이블
local characterData = {
CharName = "Diva Dragonslayer",
CharClass = "Rogue"
}
remoteEvent:FireServer(inventoryData)
remoteEvent:FireServer(characterData)

테이블 아이덴티티

원격 이벤트/콜백에 전달된 테이블은 복제됨으로, 해당 이벤트를 발생시키거나 콜백을 호출할 때 제공된 것과 정확히 같지 않습니다. 호출자에게 반환된 테이블도 제공된 것과 정확히 같지 않습니다. 다음 스크립트를 RemoteFunction에서 실행하고 테이블 아이덴티티가 어떻게 다른지 관찰하여 이를 증명할 수 있습니다.

콜백 연결 - 스크립트

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remoteFunction = ReplicatedStorage:FindFirstChildOfClass("RemoteFunction")
-- 콜백 함수
local function returnTable(player, passedTable)
-- 호출 시 테이블 아이덴티티 출력
print(tostring(passedTable)) --> table: 0x48eb7aead27563d9
return passedTable
end
-- 함수를 원격 함수의 콜백으로 설정
remoteFunction.OnServerInvoke = returnTable
이벤트 호출 - 로컬스크립트

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remoteFunction = ReplicatedStorage:FindFirstChildOfClass("RemoteFunction")
local inventoryData = {
"Sword", "Bow"
}
-- 원래 테이블 아이덴티티 출력
print(tostring(inventoryData)) --> table: 0x059bcdbb2b576549
local invokeReturn = remoteFunction:InvokeServer(inventoryData)
-- 반환 시 테이블 아이덴티티 출력
print(tostring(invokeReturn)) --> table: 0x9fcae7919563a0e9

메타테이블

테이블에 메타테이블이 있을 경우, 전송 중 모든 메타테이블 정보가 손실됩니다. 다음 코드 샘플에서 NumWheels 속성은 Car 메타테이블의 일부입니다. 서버가 다음 테이블을 수신할 때 truck 테이블은 Name 속성이 있지만 NumWheels 속성은 없습니다.

이벤트 연결 - 스크립트

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")
local function onEvent(player, param)
print(param) --> {["Name"] = "MyTruck"}
end
-- 함수와 이벤트 연결
remoteEvent.OnServerEvent:Connect(onEvent)
이벤트 발생 - 로컬스크립트

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")
local Car = {}
Car.NumWheels = 4
Car.__index = Car
local truck = {}
truck.Name = "MyTruck"
setmetatable(truck, Car)
-- 메타테이블이 포함된 테이블로 이벤트 발생
remoteEvent:FireServer(truck)

복제되지 않은 인스턴스

RemoteEvent 또는 RemoteFunction이 송신자에게만 보이는 값을 전달하면, Roblox는 클라이언트-서버 경계를 넘어 그것을 복제하지 않으며 그 대신 nil 값을 전달합니다. 예를 들어, ScriptServerStorage의 자손을 전달하면, 그 이벤트를 수신하는 클라이언트는 해당 객체를 사용할 수 없으므로 nil 값을 수신하게 됩니다.

이벤트 발생 - 스크립트

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerStorage = game:GetService("ServerStorage")
local Players = game:GetService("Players")
local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")
-- 클라이언트가 ServerStorage에 접근할 수 없기 때문에 "nil"로 수신됨
local storedPart = Instance.new("Part")
storedPart.Parent = ServerStorage
local function onPlayerAdded(player)
remoteEvent:FireClient(player, storedPart)
end
Players.PlayerAdded:Connect(onPlayerAdded)

마찬가지로, LocalScript에서 부품을 생성하고 이를 Script에 전달하려고 할 경우, 서버는 해당 부품을 알지 못하기 때문에 nil 값을 보게 됩니다.

이벤트 발생 - 로컬스크립트

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Workspace = game:GetService("Workspace")
local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")
-- 서버가 이 부품에 대해 알 수 없기 때문에 "nil"로 수신됨
local clientPart = Instance.new("Part")
clientPart.Parent = Workspace
remoteEvent:FireServer(clientPart)
©2026 Roblox Corporation. Roblox 및 Roblox 로고, 'Powering Imagination'은 미국 및 기타 국가 내 당사의 등록 및 미등록 상표입니다.