유니티에서 gRPC 사용하기 4 - 클라이언트 구성과 코드 생성
Unity에서 gRPC 클라이언트(YAHH) 구성, NuGetForUnity, proto 코드 생성 흐름
개요
이전 글에서 서버(ASP.NET Core gRPC + MySQL/Dapper)를 만들어 보았다. 이번에는 유니티 클라이언트에서 gRPC를 실제로 호출하기 위해 환경을 준비하고, proto
를 C#으로 뽑아낸 다음 채널/핸들러를 붙여서 간단히 요청까지 보내보았다.
업데이트 (2025-09-24)
이 글을 포스팅하는 오늘 날짜 기준으로, 유니티 코리아 공식 유튜브에서 관련 언급이 있었다. Unity 6.3 Beta부터 HTTP/2 지원이 추가된다고 밝혔다. 다만 이 지원이 엔진 내부의 어떤 계층까지 포함되는지는 아직 명확하지 않아, 현시점에서 YetAnotherHttpHandler(YAHH)를 완전히 대체할 수 있을지는 미지수다.
1) 패키지 설치
먼저 HTTP/2 통신을 위한 YAHH(YetAnotherHttpHandler)와 ProtoBuf를 설치하기위한 NuGet 패키지를 설치해주었다.
일단 NuGet에서는 아래의 4개 패키지를 설치해 주어야한다.
- Grpc.Net.Client
- Google.Protobuf
- Grpc.Tools
- System.IO.Pipelines
protoc
컴파일러는Grpc.Tools
안에 들어있다.
2) Unity에서 proto 코드 생성 흐름
일반적인 Visual Studio 환경에서는 MSBuild가 proto
변경을 감지해서 자동으로 C# 코드를 만들어 준다. 그런데 Unity는 MSBuild 파이프라인이 아니라서 수동으로 protoc
를 호출해야 한다.
그래서 커뮤니티에서는 이를 어떤방식으로 해결을 했을까가 궁금해서 검색을 해보았다. 배치 파일로 매번 실행을 하거나, 아에 유니티 툴로 만든 오픈소스를 사용하는게 주를 이루었다. 그런데 배치 파일은 매번 파일 탐색기에서 클릭을 해야하고, 오픈소스 툴은 너무 불편하게 만들어 놓았기 때문에 그냥 내가 쓰기 편하게 툴을 하나 만들었다.
단축키 “Ctrl + L”로 서버의 .proto
를 가져와 Unity 프로젝트 안의 Grpc.Tools(protoc)
에 명령을 전달해서 C# 클래스를 생성한다. 서버 프로젝트가 여러 개인 것도 생각해서 소스 경로를 여러 개로 나눠서 관리할 수 있게 해두었다.
최초 한 번 경로만 잡아두면 이후에는 단축키로 계속 수동 컴파일을 돌리면 된다. 잘 되면 아래처럼 C# 코드가 생성된다.
3) 간단한 로그인 UI
간단하게 로그인 UI를 만들어보았다.
4) 채널/핸들러 초기화(YAHH + Grpc.Net.Client)
코드 초기화를 한번 알아보면, 핵심은 목적지와 연결통로에 대한 추상화인 Channel에 YAHH를 넘겨주면 된다는 거다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
private GrpcChannel _channel;
private AccountsClient _client;
private YetAnotherHttpHandler _httpHandler = new YetAnotherHttpHandler { Http2Only = true };
private void Awake()
{
// HTTP/2 설정
AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2Support", true);
AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
// 서버 주소 설정
string address = ServerAddress;
if (string.IsNullOrEmpty(address) == false)
{
if (address.StartsWith("http", StringComparison.OrdinalIgnoreCase) == false)
{
address = $"http://{address}";
}
}
else
{
address = "http://localhost:7777";
}
var channelOptions = new GrpcChannelOptions
{
DisposeHttpClient = true,
HttpHandler = _httpHandler
};
_channel = GrpcChannel.ForAddress(address, channelOptions);
_client = new AccountsClient(_channel);
}
5) 회원가입 요청 예시
이제 생성된 클래스를 이용해 간단하게 요청을 보내봤다. 아래는 회원가입 예시다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/// <summary>
/// 회원가입
/// </summary>
public async Task<RegisterResponse> RegisterAsync(string username, string password)
{
if (string.IsNullOrEmpty(username))
throw new ArgumentException("username");
if (string.IsNullOrEmpty(password))
throw new ArgumentException("password");
var request = new RegisterRequest
{
Username = username,
Id = username,
Password = password
};
try
{
var response = await _client.RegisterAsync(request);
return response;
}
catch (Grpc.Core.RpcException)
{
throw;
}
}
서버에서는 입력을 검증한 뒤 사용자 정보를 DB에 저장한다(비밀번호는 서버 쪽에서 해시). 테스트해 보니 아래처럼 응답이 잘 왔다.
마무리
일단 로그인은 여기까지만 작업을 해볼까 하다가, JWT로 인증 토큰을 발급하고 유효기간까지 확인하는 흐름도 한번 해봐야겠다는 생각이 들었다. 그래서 이 부분은 다음 포스팅에서 다루겠다.