재우니 개발자 블로그

 

 

아래는 최신 트렌드(.NET 8 기준)의 ASP.NET Core 애플리케이션 성능 최적화 방법을 단계별로 정리한 블로그 글 예시입니다. 각 단계별로 설명과 코드 샘플을 풍부히 담았으니, 필요에 따라 추가·변경하여 활용하세요.

 


1단계: 불필요한 서비스 등록 줄이기

ASP.NET Core의 미들웨어·서비스는 필요할 때만 등록해야 애플리케이션이 가벼워집니다. Program.cs에서 기본 템플릿이 등록해 주는 모든 것을 다 쓰는 것이 아니라, 실제 사용하는 것만 골라서 추가하세요.

var builder = WebApplication.CreateBuilder(args);

// 예: Identity나 Razor Pages를 안 쓰면 제거
// builder.Services.AddRazorPages();
// builder.Services.AddDefaultIdentity<IdentityUser>().AddEntityFrameworkStores<ApplicationDbContext>();

// 필요한 서비스만 등록
builder.Services.AddControllersWithViews();
builder.Services.AddDbContext<MyDbContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("Default")));

// 캐싱, 압축, 인증·권한 등은 필요 시에만
builder.Services.AddMemoryCache();           // Tip 2에서 사용
builder.Services.AddResponseCompression();    // Tip 5에서 사용

var app = builder.Build();

// 미들웨어도 필요한 것만
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();

app.MapDefaultControllerRoute();
app.Run();

이렇게 구성 요소를 슬림화하면 애플리케이션 시작 속도가 빨라지고 메모리 사용량도 줄어듭니다.


2단계: 메모리 캐싱으로 DB 호출 횟수 줄이기

DB 호출은 디스크 I/O로 느립니다. 자주 읽지만 자주 변경되지 않는 데이터는 IMemoryCache(혹은 서버 팜 환경에는 Redis 등 분산 캐시)로 보관하세요.

// 1) Repository 클래스
public class CustomerRepository
{
    private readonly IMemoryCache _cache;
    public CustomerRepository(IMemoryCache cache) => _cache = cache;

    public async Task<Customer> GetByIdAsync(string id, bool forceReload = false)
    {
        if (forceReload)
            _cache.Remove(id);

        return await _cache.GetOrCreateAsync(id, async e =>
        {
            e.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(30);
            return await LoadCustomerFromDbAsync(id);
        });
    }

    private Task<Customer> LoadCustomerFromDbAsync(string id)
        => _dbContext.Customers.FirstOrDefaultAsync(c => c.Id == id);
}

// 2) Controller에서 사용
public class HomeController : Controller
{
    private readonly CustomerRepository _repo;
    public HomeController(IMemoryCache cache)
        => _repo = new CustomerRepository(cache);

    public async Task<IActionResult> Index(string id, bool force = false)
    {
        var customer = await _repo.GetByIdAsync(id, force);
        return View(customer);
    }
}
  • 분산 캐시: 서버 팜을 쓰면 Redis, NCache, SQL Server 분산 캐시를 활용하세요

3단계: 클라이언트로 전송하는 데이터 최소화

초기 페이지 로드 시 불필요한 데이터를 모두 내려주면 렌더링 지연이 발생합니다.

  • 페이징(paging): 첫 페이지 데이터만 내려주고, 사용자가 요청할 때 추가 로드
  • Infinite Scroll: 화면에 보이는 영역 + α 만큼만 로드
<!-- View: 최소 데이터만 렌더링 -->
<div id="grid"></div>
<script>
  let page = 1, pageSize = 20;
  async function loadPage() {
    const res = await fetch(`/api/items?page=${page}&size=${pageSize}`);
    const data = await res.json();
    renderGrid(data);
  }
  loadPage();
  // 스크롤 이벤트로 추가 로드...
</script>
// API: 페이징 지원
[HttpGet("api/items")]
public async Task<IActionResult> GetItems(int page = 1, int size = 20)
{
    var items = await _dbContext.Items
        .AsNoTracking()
        .Skip((page - 1) * size)
        .Take(size)
        .ToListAsync();
    return Ok(items);
}

페이징이나 무한 스크롤은 사용자 경험을 크게 개선합니다.


4단계: CSS·JS는 CDN에서, 정적 파일은 최적화된 경로로

  • Bootstrap, React, Vue 등 산업 표준 라이브러리는 CDN 사용
  • 회사 로고·커스텀 스크립트 등 자체 정적 파일도 Azure CDN 같은 서비스에 배포
<!-- _Layout.cshtml -->
<head>
  <!-- Bootstrap CSS from CDN -->
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.4.0/dist/css/bootstrap.min.css" rel="stylesheet" />
  <!-- WebOptimizer로 번들된 커스텀 CSS -->
  <link rel="stylesheet" href="~/css/bundle.css" />
</head>
<body>
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.4.0/dist/js/bootstrap.bundle.min.js"></script>
  <script src="~/js/bundle.js"></script>
</body>

CDN으로 파일을 제공하면 지리적으로 더 가까운 노드에서 빠르게 다운로드됩니다.


5단계: 파일 번들링·압축(WebOptimizer 연동)

WebOptimizer를 사용하면 런타임에 자동으로 CSS/JS를 번들링·최소화(minify) 해주고, GZIP 압축까지 처리합니다.

// Program.cs
builder.Services.AddWebOptimizer(pipeline =>
{
    pipeline.AddCssBundle("/css/bundle.css", "css/*.css");
    pipeline.AddJsBundle("/js/bundle.js", "js/*.js");
});

var app = builder.Build();
app.UseWebOptimizer();
<!-- 번들 파일 참조 -->
<link rel="stylesheet" href="/css/bundle.css" />
<script src="/js/bundle.js"></script>

요청 수와 파일 크기를 동시에 줄여 초기 로드 속도를 높입니다.


6단계: ResponseCache로 서버 호출 횟수 줄이기

자주 요청되는 페이지에는 [ResponseCache]를 걸어 브라우저·프록시 캐싱을 활용하세요.

[ResponseCache(Duration = 600, VaryByQueryKeys = new[] { "country" })]
public IActionResult PriceList(string country)
{
    var list = _dbContext.Prices.Where(p => p.Country == country).ToList();
    return View(list);
}

같은 쿼리 문자열 요청 시 10분간 서버 호출 없이 캐시된 응답을 내려줍니다.


보너스: 전체 비동기(async/await) 활용

동기 메서드는 쓰레드를 블록해 요청 처리량을 제한합니다. 데이터베이스·HTTP 호출은 async 패턴으로 작성해 쓰레드 풀 풀링 효율을 극대화하세요.

public async Task<IActionResult> GetDataAsync()
{
    var data = await _httpClient.GetFromJsonAsync<List<MyDto>>("https://api.example.com/data");
    return View(data);
}

비동기를 통해 높은 동시성(concurrency)을 확보할 수 있습니다.


마무리

  • 필요한 것만 등록
  • DB 호출 줄이기 → 캐싱
  • 클라이언트 전송 최소화
  • CDN·번들링·압축
  • 캐싱 및 비동기

 

이 여섯 가지+보너스 비동기를 적용하면 사용자 체감 속도가 크게 개선됩니다.
여기에 프로파일링(BenchmarkDotNet, MiniProfiler)이나 GC 튜닝(Workstation vs Server mode), Kestrel 옵션 조정 등 추가 최적화 기법을 더하면 더 좋은 성능을 얻을 수 있습니다.

 

 

참고사이트

 

https://www.telerik.com/blogs/make-run-faster-optimizing-aspnet-core-application

 

Make It Run Faster: Optimizing Your ASP.NET Core Application

Your users always like it when their applications run faster. Here are six things you can do right now to make that happen.

www.telerik.com