Unity Project
개요
- Unity 프레임워크 학습과 웹통신을 같이 활용해 실제 서비스를 만들어보는 연습을 하고자하는 프로젝트입니다.
- 간단한게 웹로그인 + 채팅 + 간단한 미니 게임 시스템만 구현한 뒤 점차 시스템 추가해나가면서 유지보수 할 수 있는 장기 프로젝트로 끌어가는게 목적입니다.
- 2026/03에 시작해 학기 중(6월까지)에 어느정도 완성하는걸 목표로 하고있습니다.
구현 목표
- 웹에서 로그인, 유니티 클라이언트에서 로그인. 친구기능. 길드 기능. 채팅 기능. 미니게임(가위바위보)
- 웹 로그인 - 프론트는 Next.js, 웹 서버는 asp.net 사용. 회원 정보는 SQLite 저장
- 게임 화면에서 로그인 - 웹 서버로 요청 - 로그인 성공시 정보를 받아서 게임 서버(asp.net)에 연결
기술 스택
- C#, Unity, ASP.Net Core, Next.js, gRPC, SQLite,
Ⅰ. 웹 API 서버 제작 asp.net core api server
1. ASP.Net 프로젝트 초기 설정
- API 웹 서버를 만들기 위한 프로젝트 초기 설정을 수행합니다.
ASP.NET Core 빈 프로젝트를 생성
Swagger 패키지 설치
- Swagger는 웹서버 api를 손쉽게 테스트할 수 있게 도와주는 프레임워크입니다.
- 웹서버에 연결된 api 목록들을 자동으로 화면에 띄워줍니다.
- post요청 같이 데이터를 같이 보내야하는 api의 경우 Swagger가 도움을 줍니다. Swashbuckle.AspNetCore NuGet 패키지를 설치하여 API 문서를 자동으로 생성하고 테스트할 수 있도록 합니다.
dotnet add package Swashbuckle.AspNetCore
- Nuget 패키지 매니저에서도 설치 가능
API 컨트롤러 추가
- 웹 요청을 처리하는 API 컨트롤러를 추가합니다. (api 컨트롤러 예시 코드)
c#
// Controllers/HomeController.cs
[Route("api/[controller]")]
[ApiController]
public class HomeController : ControllerBase
{
public class TestRequest
{
public required string testMessage { get; set; }
public required int testNumber { get; set; }
}
[HttpGet("/")]
public string Index()
{
return "hello, world!!";
}
[HttpPost("test")]
public string Test(TestRequest request)
{
return $"test message: {request.testMessage}. testNum: {request.testNumber}";
}
}Program.cs 설정
- swagger 서비스 등록, 컨트롤러 사용 등록 등과 같은 웹서버 구동 설정을 해줍니다.
c#
// Program.cs
namespace aspdotnet
{
public class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
// 컨트롤러 서비스 등록
builder.Services.AddControllers();
// Swagger 서비스 등록
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// 개발 환경에서 Swagger UI 활성화
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
// HTTPS 리디렉션 활성화
app.UseHttpsRedirection();
// 컨트롤러 라우팅 활성화
app.MapControllers();
app.Run();
}
}
}API 테스트
- /swagger 주소로 접근하면 서버에 연결된 API 목록이 보이고, 각 API를 클릭하면 요청을 보낼 수 있는 인터페이스가 나타납니다.
2. SQLite 연결 및 회원가입 API 구현, JWT
1. 로그인 정보를 저장할 User 테이블 스키마를 위한 클래스 정의
- ASP.Net에서는 DB와의 연결을 순간순간 유동적으로 처리합니다.
- 테이블 구조, 스키마 또한 ASP.Net에서 편하게 Model이라는 클래스 집단을 통해 데이터 구조를 편리하게 C#으로 정의할 수 있습니다.
- 우선 유저들의 회원정보를 저정하기 위한 데이터 구조를 작성합니다.
저장할 회원 정보
- 학습용 프로젝트이므로 매우 간단한 구조를 지향합니다.
- 유저 번호(기본키)
- 유저 id(string)(unique)
- password(string(hash로 저장))
- password_salt(salt 값)
- date_created(DateTime)
- 유저 아이디와 유저 비밀번호만을 가지고 로그인하게 합니다.
- 또한, 아이디는 test로 시작하고 비밀번호는 qwer로만 시작하게 설정하도록 할 것입니다.
User.cs Model 클래스
- C#에서는 DataAnnotations패키지를 통해 데이터베이스 스키마를 표현할 수 있습니다.
- UserNo, UserID, Password(hashed), PasswordSalt, CreatedAt 스키마를 가진 클래스를 작성합니다.
코드
c#
// Model/User.cs
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace aspdotnet.Model
{
public class User
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int UserNo { get; set; }
public required string UserID { get; set; }
public required byte[] PasswordHash { get; set; }
public required byte[] PasswordSalt { get; set; }
public DateTime CreatedAt { get; set; } = DateTime.Now;
}
}2. SQLite nuget 패키지 설치
- SQLite는 따로 DB 설정 없이도 파일로 생성이 되어 간단하게 다룰 수 있다는 장점이 있습니다.
- SQLite를 사용할 수 있게 해주는 nuget 패키지를 설치합니다.
dotnet add package Microsoft.EntityFrameworkCore.Sqlite
3. DatabaseContext 구현
- DatabaseContext는 실제 데이터베이스(SQLite)와의 연결을 및 전체적인 관리를 담당합니다.
c#
// Data/AppDbContext.cs
using aspdotnet.Model;
using Microsoft.EntityFrameworkCore;
namespace aspdotnet.Data
{
public class AppDbContext: DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<User>()
.HasIndex(u => u.UserID)
.IsUnique();
}
}
}4. main.cs에서 SQLite 서비스 연결
- 이제 dB와의 연결을 관리할 context 클래스와 해당 db에서 사용할 테이블의 스키마를 구현하였습니다.
- 이제 이를 사용하기 위해 service에 추가합니다. SQLite db명은 app.db입니다.
코드
c#
using aspdotnet.Data;
using Microsoft.EntityFrameworkCore;
namespace aspdotnet
{
public class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
// 컨트롤러 서비스 등록
builder.Services.AddControllers();
// Swagger 서비스 등록
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
// SQLite 서비스 등록
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseSqlite(builder.Configuration.GetConnectionString("app")));
var app = builder.Build();
// 애플리케이션 시작시 DB 자동 생성
using (var scope = app.Services.CreateScope())
{
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
db.Database.EnsureCreated();
}
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.MapControllers();
app.Run();
}
}
}appsettings.json 설정파일 DB이름 설정
options.UseSqlite(builder.Configuration.GetConnectionString("app")));DB이름을 설정파일에서 가져오게 돼있습니다. 이를 위해 설정파일에 DB이름을 넣어주어야 합니다.
txt
// appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"app": "Data source=app.db"
}
}
5. Controller에서 DBContext 사용
- main.cs에서 builder service에 컨텍스트를 추가했다면, 컨트롤러 생성시 dbcontext가 같이 넘어옵니다.
- 해당 context를 받아서 컨틀로러 내부에서 DB를 자유롭게 사용하면 됩니다.
AuthController 코드
- 생산자에서 AppDbContext를 받아서 _context에 할당합니다.
- Signup API에서 _context.Users 테이블에 마음껏 접근해서 테이블을 확인, 추가하고 있습니다. SaveChange()로 커밋도 합니다.
c#
using aspdotnet.Data;
using aspdotnet.Model;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System.Security.Cryptography;
namespace aspdotnet.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class AuthController : ControllerBase
{
private readonly AppDbContext _context;
public AuthController(AppDbContext context)
{
_context = context;
}
public class SignupRequest
{
public required string UserID { get; set; }
public required string Password { get; set; }
public required string PasswordConfirm { get; set; }
}
[HttpPost("signup")]
public IActionResult Signup(SignupRequest request)
{
if (request.Password != request.PasswordConfirm)
return BadRequest("Passwords do not match");
// for Test api
{
if (request.UserID == "string")
{
return Ok("signup api running well");
}
if (!TestCode(request))
{
return BadRequest("Please use test credentials starting with 'test' for UserID and 'qwer' for Password");
}
}
// check existing user
if (_context.Users.Any(u => u.UserID == request.UserID))
return Conflict("Username already exists");
CreatePasswordHash(request.Password, out var hash, out var salt);
var user = new User
{
UserID = request.UserID,
PasswordHash = hash,
PasswordSalt = salt
};
_context.Users.Add(user);
_context.SaveChanges();
return Ok("Signup successful");
}
bool TestCode(SignupRequest request)
{
if (!request.UserID!.StartsWith("test"))
{
return false;
}
if (!request.Password!.StartsWith("qwer"))
{
return false;
}
return true;
}
private static void CreatePasswordHash(string password, out byte[] hash, out byte[] salt)
{
salt = RandomNumberGenerator.GetBytes(16);
hash = Rfc2898DeriveBytes.Pbkdf2(password, salt, 100_000, HashAlgorithmName.SHA256, 32);
}
}
}6. Swagger에서 api 테스트, 동작 확인
- 현재 signup api에서 id를 string으로 입력하면(Swagger 기본값) api가 잘 동작한다는 메시지를,
이외 test로 시작하는 아이디와 qwer로 시작하는 비밀번호를 입력하면 회원가입이 되는 모습을 볼 수 있습니다. - 이외에도 비밀번호 불일치의 경우, 조건에 안 맞는 아이디, 비밀번호, 중복된 아이디 같은 회원가입을 시도한 뒤 결과값을 확인해봅니다.
3. ASP.Net API 웹서버 기능 구현
- 앞에서 기본적인 asp.net api구현 방법 및 DB 연결에 대해 정리하였습니다.
- 이제 이를 기반으로 로그인 시스템 구현, 인증 시스템 및 추가 API 구현에 대해 정리하겠습니다.
1. 비밀번호 해시 시스템
- SHA256을 사용하여 비밀번호를 해시로 저장함. 회원가입시 랜덤으로 생선되는 salt값(16바이트)을 이용
- 해시를 100'000번 돌리면서 32바이트 출력을 받음
hash = Rfc2898DeriveBytes.Pbkdf2(password, salt, 100_000, HashAlgorithmName.SHA256, 32);- 로그인 시 입력받은 비밀번호와 salt를 이용해 100'000번 해시 돌리고선 해시된 비밀번호랑 같은지 확인
2. 쿠키 인증 시스템(JWT)
- 로그인 성공 시(비밀번호 검증 성공) JWT 발급.
- JWT는 유저번호와 유저ID, 만료시간(+1시간), 사인 정보를 가지고 있음.
- 서버가 요청을 받았을 때 쿠키값 jwt에 있는 서명을 확인하고서 인증된유저/아닌 유저로 판단.
- 컨트롤러나 api에서 인증된 유저의 요청만 받는다면, 인증 안 된 유저의 요청을 거부.
3. 추가 설정
- CORS 정책, next.js 웹사이트 측 제한 풀기 위해 localhost:3000은 제외돼있음. 나중에 github.io 화이트리스트 추가 예정
- https 리다이렉션
웹서버 api
/api/Auth/signup: 회원가입/api/Auth/login: 로그인/api/User/info: 회원 정보(회원 번호, 회원 id)
4. next.js 웹사이트 제작
- fetch를 활용해 클라이언트 사이드 렌더링(CSR)을 통한 동적 기능이 있는 정적 사이트를 제작합니다.
- gemini cli을 활용하여 제작하였습니다.
- 간단하게 회원가입 페이지, 로그인 페이지, 회원정보 조회 할 수 있는 get api사용하는 대시보드 페이지가 있습니다.
- 학습용 프로젝트이니만큼, 전체 유저 아이디를 쉽게 볼 수 있도록 그냥 가입된 사람들의 모든 아이디 정보를 조회할 수 있게 했습니다.
사진
마무리
- 이로써 ASP.Net을 활용하여 웹 API서버를 만들어 보았고, Next.js의 정적 웹사이트에서 활용할 수 있음을 확인했습니다.
- 이제 이를 확장하여 유니티 프로젝트에서의 웹 통신 구현을 학습하도록 합니다.