🥪 Blazor 미니 프로젝트
지난 시간에는 이전에 만들었던 RankingApp 프로젝트에 게임 결과 및 데이터 저장, 조회 등 다양한 기능을 추가 개발하였다. 이를 통해 CRUD 중 Read를 처리하였다. 이번 시간에는 CRUD중 Create를 직접 구현하는 시간을 가져보도록 하자.
🌅 RankingApp #3 Create
이전에 만들어준 Ranking 레이저 컴포넌트에서 gameResult 데이터 결과를 보여주는 부분을 로그인한 사용자에게만 보여줄 수 있도록 인증 처리를 진행한다. 로그인한 유저에게만 데이터를 보여주고, 그렇지 않은 유저에게는 데이터를 보여주지 않도록 하는 것이다. 해당 부분은 Shared 폴더 내 LoginDisplay 레이저 컴포넌트를 참조하도록 하자.
@page "/ranking"
@using RankingApp.Data.Models
@using RankingApp.Data.Services
@inject RankingService RankingService
<h3>Ranking</h3>
<AuthorizeView>
@* 인증 받은 사용자 *@
<Authorized>
@if (_gameResults == null)
{
<p><em>Loading...</em></p>
}
else
{
<table class="table">
<thead>
<tr>
<th>UsersName</th>
<th>Score</th>
<th>Date</th>
</tr>
</thead>
<tbody>
@foreach (var gameResult in _gameResults)
{
<tr>
<td>@gameResult.UserName</td>
<td>@gameResult.Score</td>
<td>@gameResult.Date.ToString()</td>
</tr>
}
</tbody>
</table>
}
</Authorized>
@* 인증 받지 못한 사용자 *@
<NotAuthorized>
<p>You are not authorized to view this page.</p>
</NotAuthorized>
</AuthorizeView>
@code {
List<GameResult> _gameResults;
...
}
위와 같이 작성하여 처리할 경우, 사용자는 웹 브라우저에 들어가서도 로그인을 하지 않으면 gameResult 데이터 결과를 확인할 수 없다. 따라서 Authorized를 사용하면 인증받은(=로그인한) 사용자에게만 데이터를 보여줄 수 있는 것 까지 알아보았다. 이제 정말로 웹 브라우저에서 데이터를 추가할 때 DB까지 저장될 수 있도록 처리해보자.
...
<AuthorizeView>
@* 인증 받은 사용자 *@
<Authorized>
...
<p>
<button class="btn btn-primary" @onclick="AddGameResult">
Add
</button>
</p>
</Authorized>
@* 인증 받지 못한 사용자 *@
<NotAuthorized>
<p>You are not authorized to view this page.</p>
</NotAuthorized>
</AuthorizeView>
@code {
List<GameResult> _gameResults;
bool _showPopup;
GameResult _gameResult;
protected override async Task OnInitializedAsync()
{
_gameResults = await RankingService.GetGameResultsAsync();
}
private void AddGameResult()
{
_showPopup = true;
_gameResult = new GameResult() { Id = 0 };
}
void ClosePopup()
{
_showPopup = false;
}
}
먼저 데이터 테이블 밑에 GameResult를 추가적으로 등록할 수 있는 addGameResult 함수를 Button에 바인딩 시켜 만들어주도록 하자. 따라서 해당 버튼을 누르면 팝업이 띄워지도록 설정해야 한다.
팝업을 띄우는 것은 일전 시간에도 해본 작업이다. 블레이저 특유의 bool 변수를 통해 이를 처리할 수 있는데 사용자 클릭에 따라 팝업 호출을 위해 bool 변수 _shopPopup을 생성하고 True/False의 값 여부에 따라 해당 UI를 보여줄지 말지 처리하도록 한다.
이 후 새롭게 넣어줄 데이터인 _gameResult를 새롭게 만들어주고 ID를 0으로 설정해주도록 한다. ID를 왜 0으로 하는지 의구심이 들 수 있겠지만, Id가 0이냐, 그렇지 않느냐로 데이터를 새로 생성하는 것인지, 아니면 수정하는것인지 판단하여 처리하기 위해 Id = 0을 명시해준다. 이제 해당 bool 변수를 통해 UI 처리를 진행하도록 하자.
...
<AuthorizeView>
@* 인증 받은 사용자 *@
<Authorized>
<p>
<button class="btn btn-primary" @onclick="AddGameResult">
Add
</button>
</p>
@if (_showPopup)
{
<div class="modal" style="display:block" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div calss="modal-header">
<h3 class="modal-title">Add/Update GameResult</h3>
<button type="button" class="close" @onclick="ClosePopup">
<span aria-hidden="true">X</span>
</button>
</div>
<div class="modal-body">
<label for="UserName">UserName</label>
<input class="form-control" type="text" placeholder="UserName" @bind-value="_gameResult.UserName" />
<label for="Score">Score</label>
<input class="form-control" type="text" placeholder="Score" @bind-value="_gameResult.Score" />
<button class="btn btn-primary" @onclick="SaveGameResult">
Save
</button>
</div>
</div>
</div>
</div>
}
}
</Authorized>
@* 인증 받지 못한 사용자 *@
<NotAuthorized>
<p>You are not authorized to view this page.</p>
</NotAuthorized>
</AuthorizeView>
@code {
List<GameResult> _gameResults;
bool _showPopup;
GameResult _gameResult;
protected override async Task OnInitializedAsync()
{
_gameResults = await RankingService.GetGameResultsAsync();
}
private void AddGameResult()
{
_showPopup = true;
_gameResult = new GameResult() { Id = 0 };
}
void ClosePopup()
{
_showPopup = false;
}
async Task SaveGameResult()
{
if (_gameResult.Id == 0)
{
_gameResult.Date = DateTime.Now;
var result = RankingService.AddGameResult(_gameResult);
}
else
{
// TODO
}
// 데이터 갱신
_gameResults = await RankingService.GetGameResultsAsync();
}
}
일전에 작업했던 modal을 통해 html class를 생성하여 간단한 Form을 생성해주도록 하자. modal-header 부분에서 타이틀은 Add/Update GameResult이며, X버튼을 통해 팝업을 닫도록 설정한다. 이 후 modal-body 부분에서는 UserName, Score를 받아주고, Save 버튼을 누를 경우 SaveGameResult 함수가 동작할 수 있도록 바인딩 해준다.
SaveGameResult는 일전에 작업한 것과 마찬가지로 Id가 0이냐, 0이 아니냐에 따라 값을 분기하여 처리할 것이기 때문에 Id 값을 확인하는 if 분기로 데이터를 확인한다. 이후 var result를 통해 저장이 성공했는지, 실패했는지를 확인할 수 있으므로 반환 값 또한 받아주도록 처리한다.
마지막으로 해당 함수는 DB 저장할 때와 마찬가지로 async 방식을 사용하기 때문에 async와 Task를 통해 처리를 해주도록 한다. 마지막으로 _gameResults를 갱신하는 데, 이 때 모든 데이터를 갱신하는 방법을 사용한다.
이 때 왜 모든 데이터를 갱신해줘야 하지? 추가한 데이터만 갱신하면 되지 않을까? 생각할 수 있지만 우리가 데이터를 리스트에 넣기 전 다른 곳에서 웹 서버에 접근할 경우 다른 데이터가 들어갈 수 있다. 따라서 성능 최적화를 신경써서 하기 보다는 현재 상태에 맞는 데이터를 가져오기전에 데이터를 싸그리 긁어오는 방식으로 리프레시를 진행한다. 마지막으로 RankingService로 이동하여 AddGameResult를 마저 구현하도록 한다.
using RankingApp.Data.Models;
namespace RankingApp.Data.Services
{
public class RankingService
{
ApplicationDbContext _context;
public RankingService(ApplicationDbContext context)
{
_context = context;
}
// CREATE
public Task<GameResult> AddGameResult(GameResult gameResult)
{
// DB에 데이터를 추가한다.
_context.GameResults.Add(gameResult);
_context.SaveChanges();
return Task.FromResult(gameResult);
}
// READ
public Task<List<GameResult>> GetGameResultsAsync()
{
// DB에서 데이터를 가져온다.
List<GameResult> results = _context.GameResults
.OrderByDescending(item => item.Score)
.ToList();
return Task.FromResult(results);
}
}
}
해당 함수는 Task를 통해 GameResult를 그대로 반환처리 한다. 이 후 ApplicationDbContext _context를 통해 Add하여 추가하기로 하는 gameResult 인자 값을 GameResults 리스트에 넣어주도록 한다. 이 후 SaveChanges()를 통해 DB와 메모리의 싱크를 맞춰준다. 이를 SQL로 처리했다면 CREATE, UPDATE든 다양한 함수들을 이용했었어야 하는데 지금은 ORM을 사용하고 있기 때문에 해당 함수를 한 번 사용하면 데이터가 동기화 처리가 된다.
위와 같이 작성했으면 정상적으로 동작하는지 확인하기 위해 실행해보도록 하자.
모든 기능이 다 정상적으로 작동하고 있다. 😎
'공부 > 인프런 - Rookiss' 카테고리의 다른 글
Part 6-7-1. WebAPI와 REST 서비스 : WebApi #1 (2) | 2024.10.11 |
---|---|
Part 6-6-4. Blazor 미니 프로젝트 : Blazor RankingApp #4 (5) | 2024.10.08 |
Part 6-6-2. Blazor 미니 프로젝트 : Blazor RankingApp #2 (5) | 2024.10.01 |
Part 6-6-1. Blazor 미니 프로젝트 : Blazor RankingApp #1 (2) | 2024.09.30 |
Part 6-5-11. Blazor 입문 : Javascript 연동 (3) | 2024.09.20 |