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 빈 프로젝트를 생성

asp.net 프로젝트 생성

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를 클릭하면 요청을 보낼 수 있는 인터페이스가 나타납니다. Swagger UI

2. SQLite 연결 및 회원가입 API 구현, JWT

1. 로그인 정보를 저장할 User 테이블 스키마를 위한 클래스 정의

  • ASP.Net에서는 DB와의 연결을 순간순간 유동적으로 처리합니다.
  • 테이블 구조, 스키마 또한 ASP.Net에서 편하게 Model이라는 클래스 집단을 통해 데이터 구조를 편리하게 C#으로 정의할 수 있습니다.
  • 우선 유저들의 회원정보를 저정하기 위한 데이터 구조를 작성합니다.

저장할 회원 정보

  • 학습용 프로젝트이므로 매우 간단한 구조를 지향합니다.
  1. 유저 번호(기본키)
  2. 유저 id(string)(unique)
  3. password(string(hash로 저장))
  4. password_salt(salt 값)
  5. 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

  1. /api/Auth/signup: 회원가입
  2. /api/Auth/login: 로그인
  3. /api/User/info: 회원 정보(회원 번호, 회원 id)

4. next.js 웹사이트 제작

  • fetch를 활용해 클라이언트 사이드 렌더링(CSR)을 통한 동적 기능이 있는 정적 사이트를 제작합니다.
  • gemini cli을 활용하여 제작하였습니다.
  • 간단하게 회원가입 페이지, 로그인 페이지, 회원정보 조회 할 수 있는 get api사용하는 대시보드 페이지가 있습니다.
  • 학습용 프로젝트이니만큼, 전체 유저 아이디를 쉽게 볼 수 있도록 그냥 가입된 사람들의 모든 아이디 정보를 조회할 수 있게 했습니다.

사진

사진 사진 사진 사진

마무리

  • 이로써 ASP.Net을 활용하여 웹 API서버를 만들어 보았고, Next.js의 정적 웹사이트에서 활용할 수 있음을 확인했습니다.
  • 이제 이를 확장하여 유니티 프로젝트에서의 웹 통신 구현을 학습하도록 합니다.

2. 유니티 클라이언트 제작 및 웹 API, gRPC 통신 확인