|
์ค์ ์์ค์ C++ ์๋ฒ- Unity ํด๋ผ์ด์ธํธ ๋ฉํฐํ๋ ์ด์ด ๊ฒ์ ์๋ฒ ๋ฐ๋ชจ
** ํ์ต์ฉ ์ต์ ์์ ๋ฅผ ๋์ด์ ์ค์ ํ์ฉ ๊ฐ๋ฅํ ๊ตฌ์กฐ**
๋ณธ ํ๋ก์ ํธ๋ ๋จ์ํ ํ์ต์ฉ ์์ ๋ฅผ ๋์ด์ ์ค์ ์์ค์ ๋ฉํฐํ๋ ์ด์ด ๊ฒ์ ์๋ฒ ์๋ฃจ์ ์ ์ ๊ณตํฉ๋๋ค.
- ์ค์ ์์ค์ ํจํท ์์คํ : Move/Chat Broadcasting, Player Management
- C++/C# ์ํคํ ์ฒ: C++ IOCP Server โ C# Unity Client ์๋ฒฝ ํธํ
- ์์ ์๋ํ: ํจํท ์ฝ๋ ์๋ ์์ฑ, ์ฑ๋ฅ ํ ์คํธ ์๋ํ
- ๐ ์ค์๊ฐ ์ฑ๋ฅ ๋ชจ๋ํฐ๋ง: TPS, ์ง์ฐ์๊ฐ, ํจํท ์ฒ๋ฆฌ๋ ์ค์๊ฐ ์์ง
โโโโโโโโโโโโโโโโโโโ Protobuf โโโโโโโโโโโโโโโโโโโ
โ C++ IOCP โโโโโโโโโโโโโโโโบ โ C# Unity โ
โ Game Server โ TCP Socket โ Client โ
โโโโโโโโโโโโโโโโโโโค โโโโโโโโโโโโโโโโโโโค
โ โข Session Mgmt โ โ โข NetworkManagerโ
โ โข Packet Mgmt โ โ โข UI System โ
โ โข Room System โ โ โข Player Mgmt โ
โ โข Memory Pool โ โ โข Packet Queue โ
โ โข ๐ Stats Core โ โ โข ๐ Stats Mgmt โ
โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ
โ โ
โโโโโโโโโโโโ Google Test โโโโโโโโโโโโ
๐ Performance Testing
Visual Studio ํ์ผ ๋น๋ํ
# ์๋ฒ ์คํ
cd iocp_server_cpp/Binary/Release/
GameServer.exe
# Unity ํด๋ผ์ด์ธํธ ์คํ
cd unity_client_csharp/Build/
Client.exe
# C# DummyClient ์คํ
cd iocp_server_cpp/DummyClientCS/bin/Release/net9.0/
DummyClientCS.exe- โ User Registration: ํ์๊ฐ์ ์์คํ (์ด๋ฉ์ผ/ํจ์ค์๋ + Salt/Hash)
- โ Login/Logout: ํ๋ ์ด์ด ์ธ์ฆ ๋ฐ ์ธ์ ๊ด๋ฆฌ
- โ Real-time Movement: ์ค์๊ฐ ์์น ๋๊ธฐํ (.gif ์ฐธ์กฐ)
- โ Live Chat System: ์ค์๊ฐ ์ฑํ ๋ธ๋ก๋์บ์คํ
- โ Player Count Display: ํ์ฌ ์ ์์ ์ ์ค์๊ฐ ํ์
- โ ์๋ฒ/ํด๋ผ์ด์ธํธ ํต๊ณ ์์ง: TPS, ์ง์ฐ์๊ฐ, ํจํท ์ฒ๋ฆฌ๋
- โ ์๋ ํ์ผ ์ ๋ฆฌ: ํ ์คํธ ํ ์์ฝ ํ์ผ ์์ฑ
- โ ์ค์ ๋คํธ์ํฌ ์ฑ๋ฅ ์ธก์ : ๊ฐ์ ์๋ฎฌ๋ ์ด์ ์ด ์๋ ์ค์ TCP/IP ํต์
๐ ์ฑ๋ฅ ํ ์คํธ ๊ฐ์ด๋:
iocp_server_cpp/PerformanceTest/README.md|iocp_server_cpp/LoadTest/README.md
๋ณธ ํ๋ก์ ํธ๋ 3๊ฐ์ง ํต์ฌ ์๋ํ๋ฅผ ํตํด ๊ฐ๋ฐ ์์ฐ์ฑ์ ๊ทน๋ํํฉ๋๋ค:
- ๐ฆ Protobuf ํจํท ์๋ํ: .proto โ C++/C# ํจํท ํด๋์ค ์๋ ์์ฑ
- ๐๏ธ DB ํ๋ก์์ ์๋ํ: XML โ C++ ORM ๋ฐ์ธ๋ฉ ์ฝ๋ ์๋ ์์ฑ
- ๐จ Jinja2 ํ ํ๋ฆฟ ์๋ํ: ํ ํ๋ฆฟ ๊ธฐ๋ฐ ๋ฐ๋ณต ์ฝ๋ ์๋ ์์ฑ
flowchart TD
subgraph Common\Protobuf\bin["Common\Protobuf\bin"]
CPB_A[".proto ํ์ผ ์์ "]
end
subgraph Visual_Studio["VS 2022"]
VS_Build["ํ๋ก์ ํธ ๋น๋"]
VS_PreBuild["ํ๋ก์ ํธ ๋น๋ ์ ์ด๋ฒคํธ"]
VS_CodeGen["์ฝ๋ ์๋ ์์ฑ"]
VS_Impl["์ปจํ
์ธ ์ฝ๋ ๊ตฌํ"]
end
subgraph OS["Window"]
OS_Batch["GenPackets.exe ์๋ ์คํ"]
end
subgraph PacketGenerator["PacketGenerator"]
PG_Revise["PacketGenerator ์์ "]
end
CPB_A --> VS_Build
VS_Build --> VS_PreBuild
VS_PreBuild --> OS_Batch
OS_Batch --> VS_CodeGen
VS_CodeGen --> VS_Impl
PG_Revise -- MakeExe.bat ์คํ ํ, /Templates ํด๋์ GenPackets.Exe๋ฅผ ๋ณต์ฌ --> CPB_A
flowchart TD
subgraph XML_Config["GameDB.xml"]
XML_Define["DB Table or Column or Procedure์ ์"]
end
subgraph Python_Gen["ProcedureGenerator"]
PY_Parse["XmlDBParser.py<br/>XML ํ์ฑ + Jinja2 ํ
ํ๋ฆฟ"]
PY_Template["Templates/Procedure.h<br/>ORM ๋ฐ์ธ๋ฉ ์ฝ๋ ์์ฑ"]
end
subgraph CPP_Code["C++ GameServer"]
CPP_Header["Genprocedures.h<br/>SP::Register ํด๋์ค ์๋ ์์ฑ"]
CPP_Usage["RegisterService.cpp<br/>accountRegister.ParamOut_AccId(accId)"]
end
XML_Define --> PY_Parse
PY_Parse --> PY_Template
PY_Template -- Templates/ ์ .exe ํ์ผ์ Common/ProcedureGenerator๋ก ์ฎ๊ธฐ๊ณ ๋ฐฐ์นํ์ผ ์คํ or ๋น๋--> CPP_Header
CPP_Header --> CPP_Usage
| ์๋ํ ์ ํ | ์ ๋ ฅ | ์ถ๋ ฅ | ํต์ฌ ๊ธฐ๋ฅ |
|---|---|---|---|
| ๐ฆ ํจํท | .proto |
C++/C# ํด๋์ค |
๋คํธ์ํฌ ํต์ ์ฝ๋ |
| ๐๏ธ DB | XML + SQL |
C++ ORM ํด๋์ค |
OUTPUT ํ๋ผ๋ฏธํฐ ์ฒ๋ฆฌ |
| ๐จ ํ ํ๋ฆฟ | Jinja2 |
๋ฐ๋ณต ์ฝ๋ |
์ฝ๋ ์์ฑ ์์ง |
1. XML ์ค์ (GameDB.xml)
<Procedure name="Register">
<Param name="email" type="NVARCHAR(256)"/>
<Param name="pwHash" type="VARBINARY(64)"/>
<Param name="pwSalt" type="VARBINARY(16)"/>
<Param name="accId" type="INT" dir="out"/>
<Param name="result" type="TINYINT" dir="out"/>
<Body>
<![CDATA[
INSERT INTO [dbo].[Account] ([email], [pwHash], [pwSalt]) VALUES(@email, @pwHash, @pwSalt)
SET @accId = SCOPE_IDENTITY();
SET @result = 0;
]]>
</Body>
</Procedure>1.1 ์ค์ ์์ฑ๋ MS-SQL Stored Procedure
CREATE PROCEDURE [dbo].[spRegister] @email nvarchar(256) ,
@pwHash varbinary(64) ,
@pwSalt varbinary(16) ,
@accId int OUTPUT,
@result tinyint OUTPUT AS BEGIN
INSERT INTO [dbo].[Account] ([email], [pwHash], [pwSalt]) VALUES(@email, @pwHash, @pwSalt)
SET @accId = SCOPE_IDENTITY();
SET @result = 0;
END2. ์๋ ์์ฑ๋ C++ ORM ํด๋์ค
class Register : public DBBind<5, 0> {
public:
void ParamIn_Email(const WCHAR* value, int32 len) { /*์๋์์ฑ*/ }
void ParamIn_PwHash(BYTE* value, int32 len) { /*์๋์์ฑ*/ }
void ParamIn_PwSalt(BYTE* value, int32 len) { /*์๋์์ฑ*/ }
void ParamOut_AccId(int32& value) { /*OUTPUT ํ๋ผ๋ฏธํฐ*/ }
void ParamOut_Result(int8& value) { /*OUTPUT ํ๋ผ๋ฏธํฐ*/ }
};3. ์ค์ ์ฌ์ฉ ์ฝ๋
// RegisterService.cpp
SP::Register accountRegister(*conn);
accountRegister.ParamIn_Email(email.c_str(), email.size());
accountRegister.ParamIn_PwHash(pwHash.data(), 64);
accountRegister.ParamIn_PwSalt(salt.data(), 16);
accountRegister.ParamOut_AccId(accId); // OUTPUT ํ๋ผ๋ฏธํฐ
accountRegister.ParamOut_Result(result); // OUTPUT ํ๋ผ๋ฏธํฐ
bool success = accountRegister.Execute(); // SQLMoreResults() ์๋ ํธ์ถcd iocp_server_cpp/LoadTest/scripts/
.\quick_test.bat # 30์ด ๋น ๋ฅธ ํ
์คํธ
.\gradual_load_test.bat # ์ ์ง์ ๋ถํํ
์คํธ (1->5->10->...->30 ํด๋ผ์ด์ธํธ)
.\prometheus_load_test.bat # ๋๊ท๋ชจ ๋ถํํ
์คํธ (๊ธฐ๋ณธ๊ฐ 100๋ช
ํด๋ผ์ด์ธํธ)- ๐ฏ ์ค์๊ฐ ๋ชจ๋ํฐ๋ง: Prometheus + Grafana ๋์๋ณด๋ (http://localhost:3000)
- ๐ ์๊ฐ์ ๋ถ์: TPS, ์ง์ฐ์๊ฐ, ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ ์ค์๊ฐ ์ถ์
- ๐ ์ ์ง์ ์ฆ๊ฐ: ์ฑ๋ฅ ํ๊ณ์ ์๋ ํ์ง
- โก ๋๊ท๋ชจ ํ ์คํธ: prometheus_load_test.bat์ผ๋ก 100๋ช ๋์ ์ ์ ํ ์คํธ
๐ ์์ธ ๊ฐ์ด๋:
iocp_server_cpp/LoadTest/README.md
cd iocp_server_cpp/PerformanceTest/scripts/
.\build_and_test_real_network.bat- ๐ ์ค์ TCP ํต์ : GameServer.exe + DummyClientCS.exe ์ค์ ์คํ
- ๐ ์ฑ๋ฅ ๋ฆฌํฌํธ: CSV/XML/JSON ํ์ ์๋ ์์ฑ
- ๐งช Google Test: ๋จ์ ํ ์คํธ ํ๋ ์์ํฌ ๊ธฐ๋ฐ
- ๐ ํต๊ณ ์์ง: client_stats_*.json (gTest ํ๊ฒฝ์์๋ง)
๐ ์์ธ ๊ฐ์ด๋:
iocp_server_cpp/PerformanceTest/README.md
| ๊ตฌ๋ถ | LoadTest | PerformanceTest |
|---|---|---|
| ๋ชฉ์ | ์ค์๊ฐ ๋ถํ ๋ชจ๋ํฐ๋ง | ์ ํํ ์ฑ๋ฅ ์ธก์ |
| ๋ชจ๋ํฐ๋ง | Grafana ๋์๋ณด๋ | ๋ฆฌํฌํธ ํ์ผ ์์ฑ |
| ํด๋ผ์ด์ธํธ ์ | ์ ์ง์ (1โ30) / ๋๊ท๋ชจ (100๋ช ) | ๊ณ ์ (์ฝ๋ ์ค์ ) |
| JSON ์์ฑ | โ ์์ฑ ์๋จ | โ gTest ๋ชจ๋์์๋ง |
| ์คํ ์๊ฐ | 5๋ถ (์ ์ง์ ) / ๊ฐ๋ณ (๋๊ท๋ชจ) | 45์ด (๊ณ ์ ) |
๋ณธ ํ๋ก์ ํธ๋ [Rookiss๋์ Inflearn ๊ฐ์ ์๋ฆฌ์ฆ]๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ค์ ์์ค์ผ๋ก ํ์ฅํ์ต๋๋ค:
- C++๊ณผ ์ธ๋ฆฌ์ผ๋ก ๋ง๋๋ MMORPG ๊ฒ์ ๊ฐ๋ฐ ์๋ฆฌ์ฆ Part4: ๊ฒ์ ์๋ฒ
- C#๊ณผ ์ ๋ํฐ๋ก ๋ง๋๋ MMORPG ๊ฒ์ ๊ฐ๋ฐ ์๋ฆฌ์ฆ Part4: ๊ฒ์ ์๋ฒ
๐ ๊ฐ์ vs ๋ณธ ํ๋ก์ ํธ
| ๊ตฌ๋ถ | ๊ธฐ์กด ๊ฐ์ | ๋ณธ ํ๋ก์ ํธ |
|---|---|---|
| ํต์ ๊ตฌ์กฐ | C# โ C# ๋๋ C++ โ C++ | C++ โ C# ํฌ๋ก์ค ํ๋ซํผ |
| ํจํท ์ข ๋ฅ | ๊ธฐ๋ณธ์ ์ธ ์์ ํจํท | ์ค์ Move/Chat Broadcasting |
| ์ฑ๋ฅ ์ธก์ | ์์ | ๐ ์ค์๊ฐ ํต๊ณ ์์ง + Google Test |
| ํ ์คํธ ์๋ํ | ์๋ ํ ์คํธ | ๐ ์ํด๋ฆญ ์์ ์๋ํ |
| ๋ณต์ก๋ | ํ์ต์ฉ ์ต์ ์์ | ์ค์ ํ์ฉ ๊ฐ๋ฅํ ๊ตฌ์กฐ |
| DB | ๊ฐ๋จํ I/O | Output ํ๋ผ๋ฏธํฐ ๋ฑ ๋ ๋ณต์กํ DB I/O ๊ฐ๋ฅ |
- Windows / Visual Studio 2022 / WinSock2 IOCP
- Google Protobuf 3.21.12
- OpenSSL: SHA-512 ํจ์ค์๋ ํด์ฑ (Salt ๊ธฐ๋ฐ)
- MSSQL Server
- Unity 6000.1.7f1 / Google.Protobuf 3.21.12
- Google Test 3.21.12 / ์ค์๊ฐ JSON ๋ก๊น
- Python 3.10 (PacketGenerator) / CMake 3.20+
- Prometheus + Grafana: ์ค์๊ฐ ๋ถํํ ์คํธ ๋ชจ๋ํฐ๋ง
- Docker Compose**: ์ํด๋ฆญ ๋ชจ๋ํฐ๋ง ํ๊ฒฝ
iocp-server-unity-client-demo/
โโโ ๐ iocp_server_cpp/ # C++ ์๋ฒ & ํ
์คํธ
โ โโโ ๐ LoadTest/ # ์ค์๊ฐ ๋ชจ๋ํฐ๋ง ๋ถํํ
์คํธ
โ โโโ ๐ PerformanceTest/ # ์ค์ ๋คํธ์ํฌ ์ฑ๋ฅ ์ธก์
โ โโโ ๐ GameServer/ # ๋ฉ์ธ ๊ฒ์ ์๋ฒ
โ โโโ ๐ DummyClientCS/ # C# ํ
์คํธ ํด๋ผ์ด์ธํธ
โโโ ๐ unity_client_csharp/ # Unity ํด๋ผ์ด์ธํธ
โโโ ๐ docs/diagram/ # ์์คํ
๋ค์ด์ด๊ทธ๋จ
โโโ ๐ docs/troubleshooting/ # ํธ๋ฌ๋ธ ์ํ
๊ธฐ๋ก
โโโ ๐ docs/ADR/ # ์์ฌ ๊ฒฐ์ ๊ธฐ๋ก
- ์ปดํฌ๋ํธ ๊ตฌ์กฐ:
docs/diagram/01_Compoenent_diagram.md - ์๋ฒ ์ํคํ
์ฒ:
docs/diagram/02_Server_diagram.md - ํด๋ผ์ด์ธํธ ๊ตฌ์กฐ:
docs/diagram/03_Client_diagram.md - ํจํท ์ํ์ค:
docs/diagram/04_Packet_Sequence_diagram.md
์ด ํ๋ก์ ํธ๋ ์ค์ ์์ค์ ๊ฒ์ ์๋ฒ ํ์ต์ ๋ชฉํ๋ก ํฉ๋๋ค. ๋ฒ๊ทธ ๋ฆฌํฌํธ, ๊ธฐ๋ฅ ์ ์, Pull Request ๋ชจ๋ ํ์ํฉ๋๋ค!
๋ณธ ํ๋ก์ ํธ๋ ํ์ต ๋ชฉ์ ์ผ๋ก ์ ์๋์์ผ๋ฉฐ, Rookiss๋์ ๊ฐ์ ๋ด์ฉ์ ๊ธฐ๋ฐ์ผ๋ก ํ์ฅ๋์์ต๋๋ค.
๐ ์ค์ ์์ค์ ๊ฒ์ ์๋ฒ๋ฅผ ๊ฒฝํํด๋ณด์ธ์!
๐ ์ค์๊ฐ ๋ชจ๋ํฐ๋ง: cd iocp_server_cpp/LoadTest/scripts/ && .\quick_test.bat
โก ๋๊ท๋ชจ ๋ถํํ
์คํธ: cd iocp_server_cpp/LoadTest/scripts/ && .\prometheus_load_test.bat
๐งช ์ฑ๋ฅ ์ธก์ : cd iocp_server_cpp/PerformanceTest/scripts/ && .\build_and_test_real_network.bat
๐ ์์ธํ ๊ฐ์ด๋: LoadTest | PerformanceTest | Diagrams

