로그인 처리를 위한 Account Server는 하나의 웹서버로 만들 수 있다. C#에서 웹서버를 만들기 위해서 ASP.NET Core를 사용할 수 있는데 사용자 계정에 대한 DB모델을 정의하고 일종의 RESTful API를 구축해 로그인 기능을 구현할 수 있다.
'ASP.NET Core'는 마이크로소프트에서 제공하는 프레임워크로 이를 이용해 웹 어플리케이션이나 API를 만들 수 있다.
Visual Studio를 사용한다면, Visual Studio에는 ASP.NET Core에서 제공하는 여러가지 웹 어플리케이션 템플릿이 있다. 웹앱, 웹 API, MVC모델구조 등 여러가지를 지원한다.
public class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
}
}
처음 생성되는 Main함수는 크게 두 부분으로 나눌 수 있다.
builder는 서비스외의 실행환경이나 구성, 로깅, 호스트 설정같은 어플리케이션의 다양한 부분을 설정하는 객체로 사용자가 정의한 Controller에 대한 규칙도 정의할 수 있다.
app은 HTTP 요청 파이프라인을 구성하는데 사용되는 객체로 미들웨어나 라우팅등 사용자 요청이 어디로 연결되는지에 대한 환경을 설정할 수 있다.
[C# 서버] DB - EFC, CRUD에서 Entity를 정의하는 법과 DbContext에 대한 내용을 간략하게 얘기했었는데 Account Server에서 사용할 Account 테이블은 아래와 같이 정의할 수 있다.
[Table("Account")]
public class AccountDb
{
public int AccountDbId { get; set; }
public string AccountName { get; set; }
public string Password { get; set; }
}
public class CreateAccountPacketReq
{
public string AccountName { get; set; }
public string Password { get; set; }
}
public class CreateAccountPacketRes
{
public bool CreateOk { get; set; }
}
public class LoginAccountPacketReq
{
public string AccountName { get; set; }
public string Password { get; set; }
}
public class ServerInfo
{
public string Name { get; set; }
public string Ip { get; set; }
public int CrowdedLevel { get; set; }
}
public class LoginAccountPacketRes
{
public bool LoginOk { get; set; }
public List<ServerInfo> ServerList { get; set; } = new List<ServerInfo>();
}
웹서버는 실시간 상호작용이 주 기능이 아니지만 게임서버처럼 클라이언트-서버가 주고 받을 패킷에 대한 정의가 필요하다. 로그인, 회원가입의 요청-응답에 대한 패킷정의는 위와 같이 할 수 있다.
서버는 클라이언트가 보낸 요청을 해석, 처리해야하기 때문에 요청패킷의 구조와 데이터에 대해서 알고 있어야하고 반대로 클라이언트 역시 서버가 전달한 응답을 해석, 처리하기 위해 응답패킷의 구조와 데이터에 대해서 알고 있어야 한다.
[Route("api/[controller]")]
[ApiController]
public class AccountController : ControllerBase
{
AppDbContext _context;
public AccountController(AppDbContext context)
{
_context = context;
}
[HttpPost]
[Route("create")]
public CreateAccountPacketRes CreateAccount([FromBody] CreateAccountPacketReq req)
{
CreateAccountPacketRes res = new CreateAccountPacketRes();
AccountDb account = _context.Accounts
.AsNoTracking()
.Where(a => a.AccountName == req.AccountName)
.FirstOrDefault();
if(account == null)
{
_context.Accounts.Add(new AccountDb()
{
AccountName = req.AccountName,
Password = req.Password
});
bool success = _context.SaveChangesEx();
res.CreateOk = success;
}
else
{
res.CreateOk = false;
}
return res;
}
[HttpPost]
[Route("login")]
public LoginAccountPacketRes LoginAccount([FromBody] LoginAccountPacketReq req)
{
LoginAccountPacketRes res = new LoginAccountPacketRes();
AccountDb account = _context.Accounts
.AsNoTracking()
.Where(a=>a.AccountName == req.AccountName && a.Password==req.Password)
.FirstOrDefault();
if (account == null)
{
res.LoginOk = false;
}
else
{
res.LoginOk = true;
res.ServerList = new List<ServerInfo>()
{
new ServerInfo() { Name = "아메리카노", Ip = "127.0.0.1", CrowdedLevel = 0},
new ServerInfo() { Name = "민트초코라떼", Ip = "127.0.0.1", CrowdedLevel = 3}
};
}
return res;
}
}
ASP.NET Core의 Controller 폴더에 Controller를 생성하면 ControllerBase를 상속받는 사용자 정의 Controller를 구현할 수 있다.
[Route("api/[controller]")]
[ApiController]
public class AccountController : ControllerBase
AccountController
의 Account Table에 접근하기 위해 AppDbContext를 가지고 있다. AppDbContext _context;
public AccountController(AppDbContext context)
{
_context = context;
}
[HttpPost]
[Route("create")]
public CreateAccountPacketRes CreateAccount([FromBody] CreateAccountPacketReq req)
매개변수의 [FromBody]
는 패킷 Body에 있는 값을 파싱할 것을 의미한다. 입력값과 반환값을 보면 요청패킷(~Req)를 받고 반환값은 응답패킷(~Res)인 것을 알아두자.
[HttpPost]
[Route("login")]
public LoginAccountPacketRes LoginAccount([FromBody] LoginAccountPacketReq req)
회원가입과 마찬가지로 입력값, 반환값의 패킷차이를 알아두자. 함수에서 DB에 접근하는 코드는 아래와 같다.
AccountDb account = _context.Accounts
.AsNoTracking()
.Where(a=>a.AccountName == req.AccountName && a.Password==req.Password)
.FirstOrDefault();
Account Server의 설정을 위해 메인함수에 다음과 같은 builder 설정을 해준다. Json 파싱과정의 Policy를 초기화 시키고 DBMS의 연결정보는 Configuration의 내용을 참고하라는 설정이다.
builder.Services.AddControllers().AddJsonOptions(options =>
{
options.JsonSerializerOptions.PropertyNamingPolicy = null;
options.JsonSerializerOptions.DictionaryKeyPolicy = null;
});
builder.Services.AddDbContext<AppDbContext>(options =>
{
options.UseSqlServer(builder.Configuration["ConnectionStrings:DefaultConnection"]);
});