시스템 로그를 데이터베이스에 저장하는 ASP.NET Core 구현 가이드

  • 4 minutes to read

이 가이드는 Blazor Web App 프로젝트인 Azunt.Web에서 시스템 예외를 ErrorLogs 테이블에 저장하기 위한 전 과정을 설명합니다. MVC 또는 Blazor 환경 모두에서 사용할 수 있습니다.

1. 데이터베이스 테이블 생성

SQL Server에서 다음 스크립트를 실행하여 로그 테이블을 생성합니다.

CREATE TABLE ErrorLogs (
    Id INT PRIMARY KEY IDENTITY,
    LogLevel NVARCHAR(50),
    Message NVARCHAR(MAX),
    StackTrace NVARCHAR(MAX),
    Source NVARCHAR(255),
    CreatedAt DATETIME DEFAULT GETDATE()
);

2. EF Core 모델 정의

Models/ErrorLog.cs:

public class ErrorLog
{
    public int Id { get; set; }
    public string LogLevel { get; set; } = "Error";
    public string Message { get; set; } = string.Empty;
    public string? StackTrace { get; set; }
    public string? Source { get; set; }
    public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
}

3. DbContext에 포함

ApplicationDbContext.cs:

public class ApplicationDbContext : IdentityDbContext
{
    public DbSet<ErrorLog> ErrorLogs { get; set; }

    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    {
    }
}

4. 로그 저장 서비스 구현

Services/ErrorLogService.cs:

public class ErrorLogService
{
    private readonly ApplicationDbContext _db;

    public ErrorLogService(ApplicationDbContext db)
    {
        _db = db;
    }

    public async Task LogErrorAsync(Exception ex, string? source = null)
    {
        var log = new ErrorLog
        {
            Message = ex.Message,
            StackTrace = ex.StackTrace,
            Source = source,
            LogLevel = "Error"
        };

        _db.ErrorLogs.Add(log);
        await _db.SaveChangesAsync();
    }
}

5. DI 등록

Program.cs:

builder.Services.AddScoped<ErrorLogService>();

6. 마이그레이션 적용

패키지 관리자 콘솔 또는 CLI에서 실행:

dotnet ef migrations add AddErrorLogs
dotnet ef database update

7. 로그 테스트 예제

Blazor Web App에서는 서버 측 예외를 구성 요소 내부에서 try-catch로 잡아 처리할 수 있습니다. 아래는 MVC 컨트롤러와 Blazor Server 페이지에서 로그를 저장하는 예제입니다.

(1) MVC Controller에서 사용 예시

public class DemoController : Controller
{
    private readonly ErrorLogService _errorLogService;

    public DemoController(ErrorLogService errorLogService)
    {
        _errorLogService = errorLogService;
    }

    public async Task<IActionResult> Index()
    {
        try
        {
            throw new InvalidOperationException("MVC 테스트 예외입니다.");
        }
        catch (Exception ex)
        {
            await _errorLogService.LogErrorAsync(ex, nameof(DemoController));
            return Json(new { error = "문제가 발생했습니다.", details = ex.Message });
        }
    }
}

(2) Blazor Server 구성 요소에서 사용 예시

Pages/TestError.razor:

@page "/test-error"
@inject ErrorLogService ErrorLogService

<h3>Blazor 테스트</h3>
<button class="btn btn-danger" @onclick="TriggerError">예외 발생</button>

@code {
    private async Task TriggerError()
    {
        try
        {
            throw new NullReferenceException("Blazor 테스트 예외입니다.");
        }
        catch (Exception ex)
        {
            await ErrorLogService.LogErrorAsync(ex, nameof(TestError));
        }
    }
}

8. 관리자 로그 리스트 페이지 (QuickGrid 및 페이징 UI 포함)

Blazor Server 프로젝트의 Pages 폴더에 ErrorLogList.razor 파일을 생성합니다. 이 페이지는 QuickGrid 컴포넌트를 사용하여 로그를 페이징 처리해 보여주며, 페이지 이동을 위한 간단한 UI도 포함합니다.

Pages/ErrorLogList.razor:

@page "/admin/error-logs"
@inject ApplicationDbContext DbContext
@using Microsoft.AspNetCore.Components.QuickGrid
@using System.Linq

<PageTitle>Error Logs</PageTitle>

<h3>Error Log List</h3>

<QuickGrid Items="pagedLogs">
    <PropertyColumn Property="@nameof(ErrorLog.Id)" Title="ID" />
    <PropertyColumn Property="@nameof(ErrorLog.LogLevel)" Title="Level" />
    <PropertyColumn Property="@nameof(ErrorLog.Message)" Title="Message" />
    <PropertyColumn Property="@nameof(ErrorLog.Source)" Title="Source" />
    <PropertyColumn Property="@nameof(ErrorLog.CreatedAt)" Title="Created" />
</QuickGrid>

<div class="mt-3">
    <button class="btn btn-secondary" @onclick="PrevPage" disabled="@(!HasPrev)">이전</button>
    <span class="mx-2">페이지 @currentPage / @totalPages</span>
    <button class="btn btn-secondary" @onclick="NextPage" disabled="@(!HasNext)">다음</button>
</div>

@code {
    private List<ErrorLog> pagedLogs = new();
    private int currentPage = 1;
    private int pageSize = 10;
    private int totalCount = 0;

    private int totalPages => (int)Math.Ceiling((double)totalCount / pageSize);
    private bool HasPrev => currentPage > 1;
    private bool HasNext => currentPage < totalPages;

    protected override async Task OnInitializedAsync()
    {
        await LoadLogs();
    }

    private async Task LoadLogs()
    {
        totalCount = await DbContext.ErrorLogs.CountAsync();
        pagedLogs = await DbContext.ErrorLogs
            .OrderByDescending(e => e.CreatedAt)
            .Skip((currentPage - 1) * pageSize)
            .Take(pageSize)
            .ToListAsync();
    }

    private async Task PrevPage()
    {
        if (HasPrev)
        {
            currentPage--;
            await LoadLogs();
        }
    }

    private async Task NextPage()
    {
        if (HasNext)
        {
            currentPage++;
            await LoadLogs();
        }
    }
}

결론

이제 Azunt.Web 프로젝트에서 MVC와 Blazor Server 모두에서 발생한 예외를 ErrorLogs 테이블에 기록하고, 관리자 페이지에서 QuickGrid와 수동 페이저 UI를 통해 페이징된 로그 리스트를 실시간으로 조회할 수 있습니다. 이 구조는 유지보수와 장애 대응에 실질적인 도움을 줄 수 있습니다.

VisualAcademy Docs의 모든 콘텐츠, 이미지, 동영상의 저작권은 박용준에게 있습니다. 저작권법에 의해 보호를 받는 저작물이므로 무단 전재와 복제를 금합니다. 사이트의 콘텐츠를 복제하여 블로그, 웹사이트 등에 게시할 수 없습니다. 단, 링크와 SNS 공유, Youtube 동영상 공유는 허용합니다. www.VisualAcademy.com
박용준 강사의 모든 동영상 강의는 데브렉에서 독점으로 제공됩니다. www.devlec.com