📌 개요

참고할 점
- UI 제작 및 콘텐츠 관련 구현 코드는 다루지 않습니다.
- 학습용 프로젝트이기 때문에 잘못된 부분이 있을 수 있습니다.
만약 잘못된 부분이 있다면 댓글로 알려주시면 감사하겠습니다.

다시 한번 이번 학습의 목표를 살펴보도록 하겠습니다.
이전 포스터에서 MySQL 패키지로 게임 DB를 구축하였습니다.
그래서 이번에는 MySQL에 접속하여 로그인/회원가입을 처리하는 gRPC C# 서버를 개발하겠습니다.

오늘은 위 부분을 구현할 예정이며, gRPC C# 클라이언트는 다음 포스터에서 다루도록 하겠습니다.
※ C# 서버는 MySqlConnector 패키지를 통해 가상머신 로컬에서 MySQL과 통신합니다.

What is gRPC?
New to gRPC? Start with the following pages
grpc.io
gPRC가 궁금하신 분은 위 링크를 통해 자세히 살펴볼 수 있습니다.
📌 gRPC C# 프로젝트 생성 및 구현

먼저 Visual Studio Installer을 열어줍니다.
※ 저는 Visual Studio 2022을 사용할 것이며, 설치는 별도로 진행해주세요.

Visual Studio Installer가 열렸다면 수정 버튼을 누릅니다.

ASP.NET 및 웹 개발을 선택하고 설치합니다.

설치를 마쳤다면 Visual Studio로 들어갑니다.

새 프로젝트 만들기를 누릅니다.

템플릿 검색에 gRPC를 검색하면 ASP.NET Core gRPC 서비스 탬플릿을 확인할 수 있습니다.
gRPC를 사용하려면 NuGet을 통해 gRPC 패키지를 설치해야 하지만,
템플릿으로 시작하면 필요한 패키지를 설치하기 때문에 손쉽게 gRPC 개발을 시작할 수 있습니다.

솔루션을 생성하면 위와 같이 .proto 파일을 확인할 수 있습니다.
이것은 클라이언트와 서버가 서로 통신하기 위한 protobuf 파일입니다.

proto 파일에서 중요한 부분은 service와 message입니다.
message: 요청/응답을 위한 데이터 구조 정의
- Type과 ID로 필드를 구성 (ID는 중복 X)
service: 서버가 제공하는 원격 함수를 정의한 블록
- 하나의 Service는 여러 개의 RPC 메서드 보유
- 입력과 출력을 message로 정의
💡 추가 키워드 설명
1. syntax: proto 파일 문법 버전 정의
2. package: 패키지 네임스페이스로, 자동 생성된 코드에서 네임스페이스로 사용됨
3. option: 컴파일러 설정 옵션으로, 언어별 설정이 가능함

위 규칙에 의거하여 user.proto 파일을 만들었습니다.
이번에는 proto 파일에 정의된 service를 구현할 차례입니다.
하지만 그전에 proto 파일을 기반으로 자동 생성된 코드가 필요합니다.

user.proto 파일의 속성으로 들어갑니다.

위 사진과 동일하게 설정하고 확인을 누릅니다.

솔루션을 빌드합니다.

그러면 proto 파일을 기반으로 C# 코드가 자동으로 생성된 것을 확인할 수 있습니다.
Grpc.Tools을 통해 proto 파일을 컴파일하면 자동으로 추상 클래스(Base)를 생성한 것으로,
이 클래스를 상속받아 로직을 구현해주면 됩니다.
저희는 요청이 들어오면 MySQL DB에 접속하여 Task을 처리할 것이며, 이때 필요한 패키지가 MySqlConnector입니다.
📌 MySqlConnector로 서버와 DB 통신 구현

MySqlConnector는 C#에서 MySQL과 연결하고 쿼리를 실행하는 비동기 드라이버입니다.
Visual Studio NuGet을 통해 설치할 수 있습니다.
using MySqlConnector;
위 구문을 스크립트에 선언하여 사용할 수 있습니다.
1. MySql 접속하기
private const string connStr = "Server=localhost;Database=mygame;User ID=gameapi;Password=gameapi_pw;";
var conn = new MySqlConnection(connStr);
await conn.OpenAsync();
connStr은 연결 문자열로,
- Server: MySQL 주소 (VM 로컬에서 동작하므로 localhost 명시)
- Database: 사용할 DB
- User ID: DB에 접속할 사용자 이름
- Password: 사용자 계정의 비밀번호 (secret = User ID 계정의 비밀번호)
MySqlConnection을 생성하고, OpenAsync 비동기 함수로 MySql에 접속한다.
2. Command 생성 및 인자 설정
var insertCmd = new MySqlCommand("INSETT INTO users (user_id, user_pw, nickname) VALUES (@user_id, @user_pw, @nickname)", conn);
insertCmd.Parameters.AddWithValue("@user_id", request.UserId);
insertCmd.Parameters.AddWithValue("@user_pw", request.UserPw);
insertCmd.Parameters.AddWithValue("@nickname", request.Nickname);
위 코드처럼 MySqlCommand 객체를 통해 쿼리를 생성하고 인자를 설정할 수 있다.
그 후 Execute 계열 함수를 통해 MySQL로 쿼리를 전송하고, 쿼리에 대한 응답을 받습니다.
💡 Execute 계열 함수들 정리
ExecuteNonQuery : 삽입/수정/삭제 (반환값: 영향을 받은 튜플 수)
ExecuteScalar : 결과가 하나의 값일 때 (반환값: object, 형변환 필요)
ExecuteReader : 조회 결과를 여러번 읽을 때 (반환값 : MySqlDataReader)
3. 로그인 및 회원가입 기능 구현
MySQL을 사용하는 방법을 알아봤으니, DB를 활용한 로그인 및 회원가입을 구현해보도록 하겠습니다.
※ 소스 코드가 길기 때문에 접는 글로 정리하겠습니다.
using grpc;
using Grpc.Core;
using MyGame.Grpc.Auth;
using MySqlConnector;
using System.Reflection.PortableExecutable;
namespace grpc.Services
{
public class AuthService : Auth.AuthBase
{
private const string connStr = "Server=127.0.0.1;Database=mygame;User ID=gameapi;Password=gameapi_pw;";
public override async Task<RegisterResponse> Register(RegisterRequest request, ServerCallContext context)
{
using var conn = new MySqlConnection(connStr);
await conn.OpenAsync();
var checkCmd = new MySqlCommand("SELECT COUNT(*) FROM users WHERE user_id = @user_id", conn);
checkCmd.Parameters.AddWithValue("@user_id", request.UserId);
var count = (long)await checkCmd.ExecuteScalarAsync();
if (count > 0)
{
return new RegisterResponse
{
Succese = false,
Message = "이미 존재하는 사용자입니다."
};
}
var insertCmd = new MySqlCommand("INSERT INTO users (user_id, user_pw, nickname) VALUES (@user_id, @user_pw, @nickname)", conn);
insertCmd.Parameters.AddWithValue("@user_id", request.UserId);
insertCmd.Parameters.AddWithValue("@user_pw", request.UserPw);
insertCmd.Parameters.AddWithValue("@nickname", request.Nickname);
await insertCmd.ExecuteNonQueryAsync();
return new RegisterResponse
{
Succese = true,
Message = "회원가입 성공입니다."
};
}
public override async Task<LoginResponse> Login(LoginRequest request, ServerCallContext context)
{
using var conn = new MySqlConnection(connStr);
await conn.OpenAsync();
var cmd = new MySqlCommand("SELECT * FROM users WHERE user_id = @user_id", conn);
cmd.Parameters.AddWithValue("@user_id", request.UserId);
using var reader = await cmd.ExecuteReaderAsync();
string passwordInDb = string.Empty;
string nicknameInDb = string.Empty;
if (await reader.ReadAsync())
{
passwordInDb = reader["user_pw"].ToString();
nicknameInDb = reader["nickname"].ToString();
// 필요한 다른 컬럼(예: created_at)도 읽을 수 있다
// DateTime createdAt = reader.GetDateTime("created_at");
}
else
{
// 사용자 ID에 해당하는 데이터가 없는 경우
return new LoginResponse
{
Success = false,
Message = "사용자를 찾을 수 없습니다.",
Token = string.Empty
};
}
// 비밀번호 검증
if (passwordInDb != request.UserPw)
{
return new LoginResponse
{
Success = false,
Message = "비밀번호가 일치하지 않습니다.",
Token = string.Empty
};
}
return new LoginResponse
{
Success = true,
Message = "로그인 성공입니다.",
Token = $"{request.UserId}_{nicknameInDb}"
};
}
}
}
4. (번외) 포트 설정하기

만약 포트를 기본값(5000)이 아닌, 직접 지정해주고 싶다면 위처럼 설정해주면 됩니다.
추가된 부분은 Endpoints 블록이며, 수동 포트 설정을 위한 블록이다.
- Grpc : 이름 (자유롭게 가능)
- Url : 어떤 IP/포트에서 수신할지 (0.0.0.0:7777 = 모든 IP 허용/7777포트 사용)
- Protocols : gRPC는 반드시 HTTP/2 지정
📌 C# gPRC 서버 배포 및 실행

위 과정을 수행하고 Visual Studio 빌드를 하면 위 사진처럼 파일들을 얻을 수 있습니다.

저는 서버를 오라클 가상머신(VM)에서 돌릴 것이기 때문에 MobaXterm 프로그램을 사용했습니다.
빌드된 파일들을 가상머신에 업로드하였습니다.


C# 서버에 접근할 수 있도록 gRPC가 사용하는 포트(5000번)을 TCP 포트를 열어줍니다.
그래야 나중에 클라이언트(Unity)에서 서버와 통신할 수 있습니다.
※ 저는 클라우드 컴퓨팅이라 위 사진처럼 포트를 설정했습니다.
만약 다른 디바이스 혹은 클라우드를 사용하신다면 거기에 맞는 방법으로 포트를 열어주세요.
설정이 끝났다면 리부트해줍니다.

이제 가상머신으로 돌아와 gRPC 서버를 실행할 수 있도록 환경을 설치합니다.
sudo apt install -y dotnet-runtime-8.0 : .NET 8 런타임 설치
sudo apt install -y aspnetcore-runtime-8.0 : ASP.NET Core 8 설치
위 패키지를 모두 설치했다면 gRPC 파일들이 위치한 폴더로 이동합니다.
cd {폴더 위치} : gRPC 파일들이 위치한 폴더로 이동
dotnet {grpc.dll 파일 이름} : grpc 서버 dll 실행

위 사진처럼 출력된다면 성공적으로 gRPC 서버가 실행 중이라는 의미입니다.
만약 오라클 클라우드로 C# 서버를 실행하고 있다면?
오라클 클라우드는 AWS와 다르게 방화벽을 추가로 설정해줘야 합니다.
sudo iptables -I INPUT 1 -p tcp --dport 5000 -j ACCEPT
sudo netfilter-persistent save
sudo netfilter-persistent reload
위 명령어를 순차적으로 실행하여 TCP 5000번 포트에 대한 방화벽을 설정해줍니다.
그래야 클라이언트(Unity)가 가상머신에 접근하여 C# 서버와 gRPC 통신을 할 수 있기 때문입니다.
(추가: 설정 해두면 편리한 기능)
1. systemd 서비스 등록 및 백그라운드 실행
sudo nano /etc/systemd/system/grpc.service
위 명령어를 입력하면 service 설정 파일 편집기로 이동합니다.
[Unit]
Description=My gRPC Server
After=network.target
[Service]
WorkingDirectory=/home/ubuntu/grpc
ExecStart=/usr/bin/dotnet /home/ubuntu/grpc/grpc.dll
Restart=always
RestartSec=5
User=ubuntu
Environment=DOTNET_ENVIRONMENT=Production
[Install]
WantedBy=multi-user.target
설정에서 WorkingDirectory와 ExecStart를 수정해줍니다. (폴더 위치 및 파일 이름이 다를 수 있음)
sudo systemctl daemon-reload
위 명령어로 서비스 변경 사항을 저장합니다.
2. 서비스 등록 및 실행
sudo systemctl enable grpc.service
위 명령어를 입력하면 서버를 재부팅하면 자동으로 grpc 서버를 실행하도록 합니다.
sudo systemctl start grpc.service
설정이 끝났다면 위 명령어로 grpc.service를 백그라운드로 실행해봅니다.
3. 상태 및 로그 확인
sudo systemctl status grpc
journalctl -u grpc-f
grpc.service가 제대로 동작하고 있는지 로그를 확인합니다.
이로써 C# gRPC 서버는 준비 완료입니다.
다음 포스터에서는 C# gRPC 클라이언트 개발에 대해 살펴보겠습니다. (Unity 활용)
'유니티(Unity) > 툴 개발' 카테고리의 다른 글
| [Unity gPRC 서버 개발(4/4)] gRPC C# 클라이언트 개발 (4) | 2025.07.16 |
|---|---|
| [Unity gRPC 서버 개발(2/4)] MySQL 게임 DB 구축 (2) | 2025.07.13 |
| [Unity gRPC 서버 개발(1/4)] REST API & PRC 개념 정리 (2) | 2025.07.12 |
| [Unity TCP Socket 서버 개발(2/2)] 서버와 클라이언트 분리 (2) | 2025.07.04 |
| [Unity TCP Socket 서버 개발(1/2)] TcpListener & TcpClient로 채팅 구현하기 (0) | 2025.07.01 |