ASP.NET
.NET을 사용하여 웹 및 서비스를 빌드하기 위한 Microsoft의 오픈소스 웹 프레임워크
RESTful 웹 서비스 구축 지원, MVC(Model-View-Controller) 아키텍처, 내장 보안 기능 등 다양한 기능 및 도구 포함
- .NET : 다양한 유형의 애플리케이션을 빌드하기 위한 도구 / 프로그래밍 언어 / 라이브러리로 구성된 개발자 플랫폼
- MVC : Model(사용자 정의 프로퍼티 등) / View(html 등의 인터페이스) / Controller(DB 통신 등) 으로 구별하는 형식
- 코드가 섞이는 것을 방지하고 유지보수가 용이함
- Model -> Controller -> View 또는 View -> Controller -> Model로 전달 가능
ASP.NET Core에서 필요한 개념들
Program.cs : 웹 애플리케이션의 시작 코드를 포함한 파일
앱에서 요구하는 서비스가 구성되고, 앱의 요청 파이프라인이 일련의 미들웨어 구성 요소로 정의됨
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddRazorPages();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapRazorPages();
app.Run();
- WebApplication.CreateBuilder(args)로 빌더 생성
- 빌더에 서비스 추가
- builder.Build()를 호출하여 애플리케이션을 빌드
- app 변수를 통해 미들웨어 추가
- app.Run()을 호출하여 애플리케이션 실행
DI(Dependency Injection, 종속성 주입)
CreateBuilder()로 생성되는 WebApplicationBuilder 객체에는 DI(종속성 주입) 프레임워크가 포함됨
따라서 Builder.Services를 사용해 DI 컨테이너에 서비스를 추가할 수 있음
public class IndexModel : PageModel
{
private readonly MyDependency _dependency = new MyDependency();
public void OnGet()
{
_dependency.WriteMessage("IndexModel.OnGet");
}
}
위 코드를 보면 IndexModel 개체에서 MyDependency 개체가 사용됨
따라서 MyDependency 클래스는 IndexModel 클래스에 종속된 종속성을 가졌다고 할 수 있음
이 경우 종속성을 가진 MyDependency 클래스를 다른 구현으로 변경할 때, MyDependency가 다른 클래스들에도 종속되어있는 상태라면 다른 클래스들 모두에게도 각각 수정을 가해주어야함
이러한 문제를 해결하기 위해 종속성 주입을 통해 종속성 생성과 동작을 분리할 수 있음
public interface IMyDependency
{
void WriteMessage(string message);
}
public class MyDependency : IMyDependency
{
public void WriteMessage(string message)
{
Console.WriteLine($"MyDependency.WriteMessage Message: {message}");
}
}
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddScoped<IMyDependency, MyDependency>();
var app = builder.Build();
위 코드와 같이 종속성 주입을 활용할 경우 MyDependency 클래스를 다른 구현으로 변경한다 하더라도 이 기능을 제공하는 IMyDependency 인터페이스 자체는 변경되지 않음
- 클래스간 결합도가 약해져 리펙토링이 쉬워짐
- 인터페이스 기반 설계로 확장이 용이함
- 생명주기별로 Container를 관리할 수 있게 되어 리소스의 낭비를 막을 수 있음
종속성 주입시 서비스의 라이프사이클에 따라 수명 주기(Lifetime)을 선택하여 사용할 수 있음
- Transient : 각각의 요청, 서비스마다 새로운 인스턴스를 생성
매번 새로운 인스턴스를 생성하므로 서비스의 상태가 유지되지 않음 - Scoped : 요청마다 새로운 인스턴스 생성, 요청 종료시 인스턴스 소멸
한 번의 요청동안에는 동일한 인스턴스를 공유 - Singleton : 처음 요청이 발생할 때 생성, 애플리케이션이 종료될 때까지 유지
애플리케이션 전체에서 단 하나의 인스턴스를 제공
미들웨어(MiddleWare)
요청 및 응답을 처리하는 앱 파이프라인으로 조립되는 소프트웨어 즉, 양쪽을 연결하여 데이터를 주고받을 수 있도록 중간에서 매개 역할을 하는 소프트웨어를 뜻함
미들웨어는 요청을 파이프라인의 다음 구성 요소로 전달할지 여부를 선택하고, 파이프라인의 다음 구성 요소 전후에 작업을 수행할 수 있음
- 파이프라인을 빌드하는데는 요청 대리자가 사용되며, 요청 대리자가 각 HTTP 요청을 처리
- 요청 대리자는 Run / Map / Use 확장 메서드를 사용하여 구성됨
- 각각의 요청 대리자는 무명 메서드(인라인 미들웨어) 또는 재사용 가능한 클래스로 정의될 수 있으며 해당 메서드들을 미들웨어 또는 미들웨어 구성요소라고 함
- 각 미들웨어 구성요소는 파이프라인의 다음 구성 요소를 호출하거나 파이프라인을 단락(short-circuiting)함
- 단락(short-circuiting)될 경우 더이상 요청을 처리하지 못하도록 하기 때문에 이를 터미널 미들웨어라고 함
- Use : 미들웨어를 등록하고 요청 파이프라인에서 미들웨어를 실행
체인 형태로 사용 가능하며 여러 개의 미들웨어를 순차적으로 등록하여 처리 순서를 조절할 수 있음 - Map : 요청 경로를 기준으로 미들웨어를 등록하고 해당 경로에 매칭되는 요청만 해당 미들웨어를 실행
중첩될 수 있으며, 경로 매칭이 가장 구체적인 미들웨어가 실행됨 - Run : 미들웨어 파이프라인의 마지막단계로 미들웨어를 등록
Run으로 등록시 더이상 다음 미들웨어로 요청이 넘어가지 않음
미들웨어 구성요소가 추가되는 순서에 의해 호출 순서가 결정되고, 응답시에는 역순으로 호출됨
- UseExceptionHandler()
다음 미들웨어들에서 발생하는 예외를 잡는 역할, 가장 먼저 작성되어야함 - UseHsts()
HSTS(HTTP Strict Transport Security) 기능으로 Strict-Transport-Security 헤더를 추가하여 HTTPS를 강제
신뢰할 수 없거나 잘못된 인증서를 사용하지 못하도록 차단 - UseHttpsRedirection()
HTTP 요청을 HTTPS로 리디렉션 - UseStaticFiles()
정적 파일(.css, .js, .png, .jpg 등)을 반환하고 추가 요청 처리를 단락함
매개 변수가 없을 경우 웹 루트(정적 리소스 파일의 기본 경로, 기본적으로 {contetn root}/wwwroot로 설정됨)에 있는 파일을 제공 가능으로 표시 - UseRouting()
라우팅, 호출되지 않을경우 라우팅 미들웨어는 기본적으로 파이프라인의 시작 부분에서 실행됨 - UseCors()
동일 원본 정책을 완화할 수 있게 해주는 W3C 표준인 CORS(원본 간 리소스 공유)를 사용하도록 설정 - UseAuthentication()
보안 리소스에 대한 액세스가 허용되기 전에 사용자 인증을 시도 - UseAuthorization()
사용자에게 보안 리소스에 액세스할 수 있는 권한 부여 - UseEndpoint()
요청 파이프라인에 엔드포인트(앱의 실행 가능 요청 처리 코드 단위) 실행 추가
라우팅(Routing)
들어오는 HTTP 요청을 일치시켜 앱의 실행 가능 엔드포인트로 디스패치(파라미터로 메소드를 호출)하는 역할을 담당
UseRouting() 및 UseEndpoint()를 통해 등록된 미들웨어 쌍을 사용, 앱에 정의된 엔드포인트 집합을 확인하여 요청을 기반으로 가장 일치하는 항목을 선택함
- UseRouting() : 요청받은 URL 경로에 해당하는 핸들러 메서드나 미들웨어로 요청을 라우팅
즉, 요청된 URL 경로를 해석 - UseEndpoints() : 라우팅된 요청을 처리하기 위한 메서드나 미들웨어를 등록
즉, 요청에 따라 어떤 핸들러 메서드나 미들웨어가 실행될지를 결정
// Location 1: before routing runs, endpoint is always null here.
app.Use(async (context, next) =>
{
Console.WriteLine($"1. Endpoint: {context.GetEndpoint()?.DisplayName ?? "(null)"}");
await next(context);
});
app.UseRouting();
// Location 2: after routing runs, endpoint will be non-null if routing found a match.
app.Use(async (context, next) =>
{
Console.WriteLine($"2. Endpoint: {context.GetEndpoint()?.DisplayName ?? "(null)"}");
await next(context);
});
// Location 3: runs when this endpoint matches
app.MapGet("/", (HttpContext context) =>
{
Console.WriteLine($"3. Endpoint: {context.GetEndpoint()?.DisplayName ?? "(null)"}");
return "Hello World!";
}).WithDisplayName("Hello");
app.UseEndpoints(_ => { });
// Location 4: runs after UseEndpoints - will only run if there was no match.
app.Use(async (context, next) =>
{
Console.WriteLine($"4. Endpoint: {context.GetEndpoint()?.DisplayName ?? "(null)"}");
await next(context);
});
/ URL을 사용하여 위 코드를 실행할 경우
1. Endpoint: (null)
2. Endpoint: Hello
3. Endpoint: Hello
다른 URL을 사용하여 코드를 실행할 경우
1. Endpoint: (null)
2. Endpoint: (null)
4. Endpoint: (null)
- UseRouting()을 호출하기 전의 엔드포인트는 항상 null
- 일치하는 항목이 발견될시 엔드포인트가 설정되며, UseEndpoints() 미들웨어가 터미널이됨
- UseEndpoints() 뒤의 미들웨어는 일치 항목이 없는 경우에만 실행됨
호스트(Host)
애플리케이션을 실행하는 환경을 의미, 애플리케이션의 라이프사이클 관리, HTTP 요청 처리, 로깅, 설정 및 서비스 등을 처리
애플리케이션의 실행 / 구성 / 배포 및 관리를 담당하기 때문에 다음과 같은 설정과 기능을 캡슐화함
- HTTP 서버 구현
- 미들웨어 구성 요소
- 로깅
- DI(종속성 주입) 서비스
- Configuration
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
WebApplicationBuilder.Build() 메서드를 통해 WebApplication 호스트를 인스턴스화
해당 메서드는 다음과 같은 옵션으로 호스트를 구성함
- Kestrel을 웹 서버로 사용, IIS 통합을 사용하도록 설정
- Kestrel : 플랫폼 간 웹서버, IIS를 사용하여 역방향 프록시 구성에서 실행
- appsettings.json, 환경변수, 명령줄 인수, 기타 구성 소스에서 구성을 로드
- 콘솔 및 디버그 공급자에 로깅 출력을 보냄
로깅(Logging)
애플리케이션의 동작과 상태를 기록하여 디버깅, 모니터링, 문제 해결 등을 지원
로깅에는 레벨이 존재
- 트레이스(Trace, 0) : 가장 낮은 로그 레벨, 매우 상세한 정보를 기록
- 디버그(Debug, 1): 디버깅 목적으로 상세한 정보를 기록
- 정보(Information, 2): 애플리케이션의 상태와 동작에 관한 정보를 기록
- 경고(Warning, 3): 경고 수준의 이벤트나 오류를 기록
- 오류(Error, 4): 애플리케이션에서 발생한 오류와 예외를 기록
- 치명적 오류(Critical, 5): 치명적인 오류를 기록
- None(6) : 로그 비활성화
#1
var builder = WebApplication.CreateBuilder(args);
builder.Logging.ClearProviders();
builder.Logging.AddConsole();
#2
var builder = WebApplication.CreateBuilder();
builder.Host.ConfigureLogging(logging =>
{
logging.ClearProviders();
logging.AddConsole();
});
위 두 코드는 모두 로깅을 구성하는 예시로 동일하게 작동
logging.ClearProvicders()로 기존 로깅 공급자를 제거하고, logging.AddConsole()로 콘솔 로깅 공급자를 추가
이를 통해 콘솔에 로그 메세지가 출력되도록 설정됨
- 로깅 공급자 : 로그 메시지를 수집하고 처리하는 기능을 제공하는 컴포넌트
ASP.NET Core에는 기본 로깅 공급자로 콘솔, 디버그, 이벤트 원본, 이벤트 로그가 존재
- 콘솔(Console) : 로그 메시지를 콘솔에 출력
- 디버그(Debug) : 로그 메시지를 System.Diagnostics.Debug.WriteLine을 호출하여 Debug 공급자에 기록
- 이벤트 원본(EventSource) : 로그 메시지를 Microsoft-Extensions-Logging인 플랫폼 간 이벤트 원본에 기록
- 이벤트 로그(EventLog) : 로그 메시지를 Windows 이벤트 로그에 저장
- 파일에 로그를 기록하기 위한 로깅 공급자는 기본으로 포함되어있지 않으므로 타사 로깅 공급자를 사용해야함
Log4Net, NLog, Serilog 등이 존재
public class AboutModel : PageModel
{
private readonly ILogger _logger;
public AboutModel(ILogger<AboutModel> logger)
{
_logger = logger;
}
public void OnGet()
{
_logger.LogInformation("About page visited at {DT}",
DateTime.UtcNow.ToLongTimeString());
}
}
위 코드와 같이 DI의 ILogger<TCategoryName> 개체를 사용하여 로그를 생성
LogInformation()을 호출하여 Information 수준으로 로깅
{
"Logging": {
"LogLevel": { // All providers, LogLevel applies to all the enabled providers.
"Default": "Error", // Default logging, Error and higher.
"Microsoft": "Warning" // All Microsoft* categories, Warning and higher.
},
"Debug": { // Debug provider.
"LogLevel": {
"Default": "Information", // Overrides preceding LogLevel:Default setting.
"Microsoft.Hosting": "Trace" // Debug:Microsoft.Hosting category.
}
},
"EventSource": { // EventSource provider
"LogLevel": {
"Default": "Warning" // All categories of EventSource provider.
}
}
}
}
위와 같이 appsettings.{ENVIRONMENT}.json 파일의 Logging 섹션에서 로깅 속성을 구성
- LogLevel : 선택한 범주에 대해 로그할 최소 수준 지정
지정하지 않을시 Information 수준으로 기본 설정 - 특정 공급자와 범주마다 개별적인 설정이 가능함
ConfigureLogging() 메서드는 호스트 레벨에서 로깅 구성을 변경하고자 할 때 사용
일반적으로 애플리케이션 수준의 로깅 구성에는 appsettings.json 파일을 수정하거나 코드 내에서 로깅 빌더를 직접 사용
구성(Configuration)
WebApplicationBuilder 초기화시 앱의 기본 구성을 제공
따라서 appsettings.json 파일은 WebApplicationBuilder를 사용하여 애플리케이션 구성시 기본적으로 수행되는 내부작업에 의해 구성에 추가됨
var builder = WebApplication.CreateBuilder(args);
builder.Host.ConfigureAppConfiguration((hostingContext, config) =>
{
config.SetBasePath(Directory.GetCurrentDirectory());
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
});
위와 같이 ConfigureAppConfiguration() 메서드를 사용하여 호스트 레벨에서 애플리케이션 구성을 설정할 수도 있음
- SetBasePath
- 설정파일 등을 검색할 기본 위치 설정
- 설정하지 않을시 실행파일이 존재하는 위치에 설정됨
- AddJsonFile()
- optional이 true일시 설정파일이 존재하지 않아도 예외가 발생하지 않고 계속 진행됨
- reloadOnChange가 true일시 파일 변경이 감지되면 자동으로 구성이 다시 로드됨
'개인공부 > Web API 게임 서버 공부' 카테고리의 다른 글
배경지식 - Redis (0) | 2023.04.20 |
---|---|
배경지식 - ORM (1) | 2023.04.20 |
배경지식 - C# (0) | 2023.04.20 |
배경지식 - API (0) | 2023.04.19 |
배경지식 - HTTP (1) | 2023.04.19 |
댓글