서버와 클라이언트

# 학습 과정 소개

멀티 플레이를 제공하는 게임의 경우 각 게임의 특성에 따라 사용되는 통신 모델들이 다양합니다. MapleStory World는 다양한 통신 모델 중에서도 서버 - 클라이언트 모델을 사용하고 있습니다. 이번 과정에서는 MapleStory World의 통신 모델인 서버와 클라이언트의 개념과 구조, 그리고 각 공간 간의 정보전달 방법에 대해 알아보도록 하겠습니다.

# 서버와 클라이언트

클라이언트는 게임을 플레이하는 유저들의 모바일이나 PC, 또는 해당 기기에서 개별적으로 돌아가는 프로그램을 의미합니다.주로 유저의 입력을 처리하거나, 서버가 보내온 정보를 내부적으로 처리하여 유저에게 가시적으로 출력하는 역할을 수행합니다. 서버는 다수의 클라이언트가 연결된 단일 컴퓨터, 또는 해당 컴퓨터에서 돌아가는 프로그램을 의미합니다.일반적으로 각 클라이언트에서 보내온 요청에 대한 응답을 주기도 하지만, 게임 진행에 대한 주요 기능과 시스템을 서버에 구축함으로써 모든 클라이언트가 동일한 진행 상황이 될 수 있도록 할 수 있습니다. [png](1637228699029a52751daf6c943c0a46ca3ca575c1f9e)

# 서버와 클라이언트의 관계

서버 - 클라이언트 모델에서 클라이언트는 다른 클라이언트와 통신하지 않으며, 오직 서버하고만 정보를 주고받습니다. 클라이언트는 유저의 입력을 받고, 입력된 내용에 따라 서버에게 정보를 요청하거나 현재 클라이언트의 상황을 서버에게 알립니다. 특정 클라이언트로부터 정보를 받은 서버는 클라이언트에게 다시 정보를 보내주거나, 혹은 전송한 내용을 게임 진행 상황에 업데이트시키고, 업데이트된 내용을 다시 모든 클라이언트에게 전송함으로써 게임을 이용하는 모든 유저가 동일한 상황이 될 수 있도록 맞춰줍니다. [png](1637228723887c6c077c9db624c6cb78924690d141edd)   
서버 - 클라이언트 간 처리 구조는 제작자의 의도에 따라 자유롭게 설계할 수 있지만, 만일 클라이언트에서 모든 로직과 시스템을 구축하고, 서버는 동기화를 위한 통신용으로만 사용하도록 설계한다면 해킹에는 상당히 취약한 구조가 됩니다.예를 들어 다음과 같이 캐릭터의 레벨업을 클라이언트에서 처리 후, 서버를 통해 다른 클라이언트에게 알린다고 가정해봅시다. [png](1637228744217901eadeec18549cd916b580dcb47bcaf) ▲주요 로직을 클라이언트에서 처리할 때의 예   
주요 로직을 개별 클라이언트에서 처리하고 서버는 처리된 결과를 받아 다른 클라이언트에게 전송만 하면 되기 때문에 서버의 부하를 줄일 순 있습니다. 하지만 유저가 본인의 클라이언트를 해킹한 결과를 서버에게 전송한다면, 다른 유저들도 이용하는 게임이 해킹으로 인해 망가지게 됩니다. 예를 들어 위의 예시에서 User A가 몬스터를 잡았을 때 경험치 획득량이 많아지도록 클라이언트를 조작한다면, 서버는 조작된 과정에 의한 결과만 받아들이게 되고, 결국 UserA의 플레이어가 순간적으로 빠르게 레벨업되었다는 정보를 모든 클라이언트에게 전달하게 될 것입니다. 물론 이를 위해 서버에서 검증 로직을 따로 둘 수도 있겠지만, 그렇게 되면 결국 클라이언트와 서버 둘 다 같은 로직이 두 개 생기는 꼴이라 사실 효율적인 구조라 할 순 없습니다. 따라서 MapleStory World에서는 게임의 전반적인 로직과 시스템을 서버에서 구현하고, 클라이언트는 유저의 입력을 받아 서버로 전달하거나, 게임 진행 상황을 내려받아 유저에게 전달하는 역할로 구조를 가져가는 것을 추천합니다. 즉 서버는 게임의 전반적인 흐름을 가져가고, 클라이언트는 유저와 게임의 사이 중계자의 역할을 하는 것입니다. 이와 같은 구조로 레벨업 처리 과정을 변경한다면 다음과 같습니다. [png](1653888266299a18ff1e10049438f981b133b2d61922a)   


# 서버, 클라이언트에서의 프로퍼티와 함수

서버와 클라이언트가 어떻게 상호작용을 하는지를 알아보았으니, 이제 메이커에서 작성한 프로그램이 서버와 클라이언트에서 어떻게 돌아가는지 알아보도록 합니다. 제작자가 스크립트에서 선언한 프로퍼티와 함수는 별도의 설정을 하지 않는 한 기본적으로 서버와 클라이언트 두 개의 공간에 각각 독립적으로 생성됩니다. 이름은 같지만 서로 다른 공간에 존재하기 때문에 별도의 프로퍼티 또는 함수라 보는 것이 좋습니다. [png](1635334094351321c58a15d5a48f2bdb9e10d7fd67482)   
기본적으로 함수의 호출과 프로퍼티의 값 참조 및 할당은 같은 공간 내에서만 이루어집니다. 예를 들어 A라는 프로퍼티에 값을 B라는 함수에서 할당한다 했을 때, 서버에 있는 함수 B가 호출되면 서버에 있는 프로퍼티 A에 값이 할당되지만, 클라이언트에 있는 프로퍼티 A에는 값이 할당되지 않습니다. 반대의 경우도 마찬가지로 클라이언트의 함수 B를 호출하면 클라이언트에 있는 프로퍼티 A에는 값이 할당되지만, 서버에 있는 프로퍼티 A는 값이 할당되지 않습니다. [png](1635334104154192561b773054ed6833d2a775f827572)   
함수 간 호출도 이와 비슷합니다. 예를 들어 A와 B라는 함수를 선언했다 가정했을 때, 클라이언트의 함수 A가 함수 B를 호출하면 클라이언트에 있는 함수 B만 호출되고 서버의 함수 B는 호출되지 않습니다. [png](1635334116615c15afa2e69974975aaf8fea49703fb86)   
종합해보면 서버에서 선언된 것은 서버에서만, 그리고 클라이언트에서 선언한 것은 클라이언트에서만 돌아간다는 말입니다. 그렇다면 멀티 플레이를 위한 서버-클라이언트 간 상호작용은 어떻게 할 수 있을까요?

# 동기화와 실행제어

서버와 클라이언트 간 통신 방법에는 여러 가지가 있습니다. MapleStory World에서는 데이터 또는 상태를 전달하는 방식인 동기화와 특정 상황에 발생해야 할 행위, 행동들을 전달하는 방식인 실행제어를 제공하고 있습니다.

#### 동기화

동기화는 서비스나 프로그램에 따라 의미가 조금씩 다르긴 하지만, MapleStory World에서는 서버와 클라이언트 별도 공간에 있는 프로퍼티 값의 동기화를 의미합니다. 예를 들어, 어느 한쪽의 프로퍼티 값을 변경하면, 다른 한쪽은 동일한 값으로 자동으로 변경되는 것이죠. 따라서 서버와 클라이언트의 프로퍼티가 동일한 값이 될 수 있도록 별도의 처리를 하지 않아도, 자동으로 동기화가 이루어지기 때문에 제작자는 좀 더 편하게 서버와 클라이언트 간 통신을 구현할 수 있습니다. [png](163533412709076a151563ef047af9002af8741822786)   
그렇다고 모든 프로퍼티가 동기화되는 것은 아닙니다. 컴포넌트의 기능과 특성에 따라 프로퍼티 동기화가 될 수도, 혹은 안 될 수도 있습니다. 예를 들어, 주로 클라이언트에서만 작동하는 컴포넌트가 있다고 가정했을 때, 해당 컴포넌트는 서버로 데이터를 보낼 일이 없으니 프로퍼티는 동기화는 이루어지지 않습니다. 스크립트 컴포넌트에서는 프로퍼티 동기화 설정이 가능하도록 기능을 제공하고 있습니다.이는 제작자가 본인이 만든 컴포넌트의 기능과 특성, 또는 제작자의 스타일에 따라 동기화 가능 여부를 설정케 함으로써 불필요한 동작을 줄이고 효율적인 구조로 설계해 나갈 수 있게 합니다. [png](163533413853006f6f98fe3ee4579a26b2c848cf7a551) <프로퍼티 설정에 따라 동기화가 될 수도, 안될 수도 있습니다.>   
일반적으로 동기화는 서버에서 클라이언트 단방향으로 진행됩니다. 프로퍼티 A가 있을 때, 서버에서 특정 조건에 의해 프로퍼티 A에 어떤 값이 할당되면, 클라이언트에 있는 프로퍼티 A는 서버에서 할당된 동일한 값으로 맞춰지게 됩니다. 하지만 반대로 클라이언트의 프로퍼티 A에 값을 할당하면 서버의 프로퍼티 A에는 어떤 변화도 발생하지 않습니다. 이유는 서버와 클라이언트는 일 대 일 관계가 아닌 일 대 다 관계이기 때문입니다. 클라이언트 A와 클라이언트 B가 있을 때, 클라이언트의 프로퍼티 C를 클라이언트 A에서는 1로, 클라이언트 B에서는 2로 할당했다고 가정한다면 서버는 프로퍼티 C의 값을 어떤 클라이언트의 값과 맞춰야 할지 판단이 어려워지게 됩니다.

[{"index":"서버에서 클라이언트로 동기화","0":"[png](16372287876352eedf403ff9a494c9e6bb799f8c5172c)"},{"index":"클라이언트에서 서버로 동기화","0":"[png](1637228806316f799316c1c2540b6bbe5b0e680317a3a)"}]

때에 따라 클라이언트에서 할당한 값을 서버로 동기화하는 경우도 있지만, 특수한 몇몇 컴포넌트를 제외하고는 서버와 클라이언트 간의 관계적 특성으로 인해 주로 서버에서 할당한 값을 클라이언트로 동기화가 이루어지게 됩니다.

#### 실행제어

서버와 클라이언트 간 통신에는 데이터의 전달, 즉 동기화 외에도 공간에 정의된 "액션"을 요청하고 수행하는 과정도 필요합니다. 위의 레벨업 처리 과정을 예시로 들면, 클라이언트가 유저의 입력을 받으면, 서버에게 "몬스터 공격"이라는 "액션"을 요청하게 되고, 서버는 클라이언트의 요청에 따라 "몬스터 공격"이라는 "액션"을 수행하게 되는데요, 이렇듯 각 공간에 정의된 액션을 서로 요청하여 수행케 하는 것을 실행제어라고 합니다. 실행제어는 제작자의 의도에 따라 설정할 수 있습니다. 위에 말한 "액션"은 결국 스크립트에서는 "함수"라 할 수 있는데, 스크립트에서 각 함수의 설정을 어떻게 하느냐에 따라 클라이언트에서 서버로 수행을 요청할 수도 있고, 반대로 서버에서 클라이언트로 수행을 요청할 수도 있습니다. 아래 상황을 예로 들면, 서버에 선언된 함수 B의 실행 공간을 클라이언트로 설정했다면, 함수 B는 서버 내 다른 함수에서 호출이 가능하지만, 클라이언트에서 실행되기 때문에 서버에서 호출할 수 있는 클라이언트 함수처럼 사용이 가능합니다. [png](1635334183953589a1ccd5a874edeaad70b12edca184c)