이 게시물은 .NET 7에 관해 설명합니다. 다른 버전을 사용할 경우 게시글의 설명과 다를 수 있습니다.
이 글은 마이크로소프트 설명서를 참고하여 작성되었습니다.
목차
1. ASP.NET Core의 구성
Program.cs
- 애플리케이션의 진입점입니다.
- 앱에서 요구하는 서비스가 구성됩니다.
- 앱의 요청 처리 파이프라인이 미들웨어 구성 요소로 정의됩니다.
WebApplication.CreateBuilder
var builder = WebApplication.CreateBuilder(args);
- ASP.NET Core 웹 앱은 위 코드를 생성합니다.
- 이 코드에서 WebApplication.CreateBuilder는 미리 구성된 기본값을 사용하여 클래스의 새 인스턴스를 초기화합니다.
- 초기화된 WebApplicationBuilder는 우선순위 순서대로 앱의 기본 구성을 제공합니다. 그 우선순위는 다음과 같습니다.
- 명령줄 인수
- 접두사 없는 환경 변수
- 사용자 비밀
- 앱이 환경에서 실행되는 경우 해당됩니다. 비밀 정보를 프로젝트와 함께 배포하지 않고, 프로젝트 내부의 환경 변수를 통해 따로 비밀 정보에 접근하는 경우입니다. - JSON 구성 공급자를 사용하는 appsettings.{Environment}.json
- appsettings.Production.json, appsettings.Development.json 등의 예가 있습니다. - JSON 구성 공급자를 사용하는 appsettings.json
라우팅
- 들어오는 HTTP 요청을 일치시켜 앱의 실행 가능 엔드포인트로 디스패치하는 역할을 담당합니다.
- 엔드포인트는 앱의 실행 가능 요청 처리 코드 단위입니다. 엔드포인트는 앱에서 정의되고, 앱 시작 시 구성됩니다. 엔드포인트 일치 프로세스는 요청의 URL에서 값을 추출하고, 요청 처리를 위해 이 값을 제공할 수 있습니다.
- 앱의 엔드포인트 정보를 사용하여 엔드포인트에 매핑되는 URL을 생성할 수 있습니다.
- Controllers, 상태 검사와 같은 엔드포인트 지원 미들웨어, 라우팅에 등록된 대리자 및 람다 등을 사용하여 구성할 수 있습니다.
- WebApplicationBuilder는 UseRouting 및 UseEndpoints를 통해 Program.cs에 추가된 미들웨어를 래핑하는 미들웨어 파이프라인을 구성하나, 앱은 UseRouting 및 UseEndpoints 메서드를 명시적으로 호출하여 실행 순서를 변경할 수 있습니다.
- 앱에서 일치시키고 실행할 수 있는 엔드포인트는 UseEndpoints에서 구성됩니다.
ConfigureLogging
참고: https://github.com/Cysharp/ZLogger
로깅을 설정하기 위해서는 Program.cs에 로깅을 추가하는 코드를 작성해야 합니다. Zlogger로 콘솔 로그를 출력하는 설정을 하기 위해서는 다음과 같은 코드를 추가하면 됩니다.
Host.CreateDefaultBuilder()
.ConfigureLogging(logging =>
{
logging.ClearProviders();
logging.AddZLoggerConsole(options =>
{
options.EnableStructuredLogging = true;
});
});
더 자세한 설정 방법은 https://github.com/Cysharp/ZLogger를 참고하세요. 다양한 형식의 로그를 출력할 수 있습니다.
appsettings.json
{
"AllowedHosts": "*",
"ServerAddress": "http://0.0.0.0:포트 번호",
"logdir": "./log/",
"DbConfig": {
"Redis": "127.0.0.1",
"MySQL": "Server=127.0.0.1;user=root;",
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
- 애플리케이션 설정 파일입니다.
- JsonConfigurationProvider에서 로드합니다.
- appsettings. {Environment}. json값이 appsettings.json 값을 재정의합니다. 개발 환경에서는 appsettings.Development.json, 프로덕션 환경에서는 appsettings.Production.json의 값을 덮어씁니다.
2. 의존성 주입(DI)
참고: https://ko.wikipedia.org/wiki/%EC%9D%98%EC%A1%B4%EC%84%B1_%EC%A3%BC%EC%9E%85
DI를 통해 다른 코드의 변경 없이 Program.cs의 변경만으로 구체적인 구현 형식을 변경할 수 있습니다. 이는 클래스 간 느슨한 결합도를 바탕으로 코드 재사용성 향상에 큰 도움이 됩니다.
쉽게 비유해 보자. 데스노트라는 뮤지컬이 있고, 이 뮤지컬에는 엘과 라이토가 등장합니다. 그리고 각 역할당 2명의 배우가 돌아가며 공연을 합니다. 엘 역할을 맡은 엘 A, 엘 B 배우와 라이토 역할을 맡은 라이토 A, 라이토 B 배우가 있습니다. 엘을 연기하는 두 배우는 라이토가 어떤 배우이든 관계없이 라이토를 연기할 수 있는 배우이기만 하면 공연을 진행할 수 있습니다. 라이토를 연기하는 두 배우도 마찬가지입니다. 뮤지컬 감독은 어떤 배우를 어떤 일정에 무대에 세울지 변경할 수 있습니다.
DI 세계에서 각 역할은 다음과 같습니다.
엘과 라이토: 인터페이스
엘 A, 엘 B, 라이토 A, 라이토 B: 서비스
뮤지컬 감독: 주입자
.NET Core API에서의 의존성 주입은 Program.cs 파일에서 이루어집니다.
Program.cs 파일에 다음과 같은 코드를 작성하는 것으로 의존성 주입을 할 수 있습니다.
builder.Services.AddControllers();
services.AddSingleton<IMyDependency, MyDependency>();
3. 서비스의 생명 주기
참고: https://holjjack.tistory.com/71
Transient
하나의 요청 안에서도 인스턴스가 여러 번 생성됩니다. 상태를 저장하지 않는 인스턴스에 적합합니다.
Scoped
하나의 요청 안에서는 인스턴스가 한 번만 생성됩니다. 요청 내에서 상태를 유지하려는 경우 사용합니다.
Singleton
한 번의 요청으로 인스턴스가 생성되면 앱이 종료될 때까지 유지됩니다. 즉, 해당 서비스를 사용하는 후속 요청은 모두 같은 인스턴스를 재사용합니다.
서비스의 생명 주기는 Program.cs에서 다음과 같이 설정합니다.
builder.Services.AddTransient<IMyDependency, MyDependency1>();
builder.Services.AddScoped<IMyDependency, MyDependency2>();
builder.Services.AddSingleton<IMyDependency, MyDependency3>();
4. 미들웨어
- 요청 및 응답을 처리하는 앱 파이프라인으로 조립되는 소프트웨어입니다.
- 미들웨어에서는 요청을 파이프라인의 다음 구성 요소로 전달할지 여부를 선택합니다. 이 작업은 파이프라인의 다음 구성 요소 전과 후에 수행할 수 있습니다.
- 요청 파이프라인을 빌드하는 데 요청 대리자가 사용됩니다.
- 요청 대리자는 각 HTTP 요청을 처리합니다.
- 요청 대리자는 Run, Map, Use 확장 메서드를 사용하여 구성됩니다.
- 개별 요청 대리자는 무명 메서드로 인라인에서 지정되거나, 재사용 가능한 클래스에서 정의될 수 있습니다. 이러한 개별 요청 대리자를 미들웨어라고 하며, 미들웨어 구성 요소라고 부르기도 합니다.
- 각 미들웨어 구성 요소는 파이프라인의 그다음 구성 요소를 호출하거나 파이프라인을 Short-circuiting하는 역할을 담당합니다. 미들웨어가 Short-circuiting되는 경우 미들웨어에서 더는 요청을 처리하지 못하기 때문에 이를 터미널 미들웨어라고 합니다.
미들웨어 작동 순서
- 미들웨어 구성 요소는 Program.cs에 추가하여 사용합니다.
- 미들웨어 작동 순서는 Program.cs에 추가한 순서와 요청에서 호출되는 순서가 같고, 응답에서 호출되는 순서는 그 역순입니다.
- 미들웨어 작동 순서는 보안, 성능 및 기능에 중요합니다. 또한 특정 미들웨어는 정해진 작동 순서를 지켜야 할 수도 있습니다.
다음은 Program.cs에 미들웨어를 추가한 코드입니다.
if (env.IsDevelopment()) // 앱이 개발 환경에서 실행되는 경우
{
app.UseDeveloperExceptionPage(); // 개발자 예외 페이지 미들웨어가 앱 런타임 오류를 보고합니다
app.UseDatabaseErrorPage(); // 데이터베이스 런타임 오류를 보고합니다
}
else // 앱이 프로덕션 환경에서 실행되는 경우
{
app.UseExceptionHandler("/Error"); // 다음에 실행되는 미들웨어에서 던져진 예외를 잡습니다
app.UseHsts(); // Strict-Transport-Security 헤더를 추가합니다
}
app.UseHttpsRedirection(); // HTTP 요청을 HTTPS로 리다이렉션합니다
app.UseStaticFiles(); // 정적 파일을 반환하고 추가 요청 처리를 단락합니다
app.UseCookiePolicy(); // EU GDPR(일반 데이터 보호 규정)을 준수하게 만듭니다
app.UseRouting(); // 미들웨어가 요청을 라우팅합니다
app.UseAuthentication(); // 보안 리소스에 대한 접근이 허용되기 전에 사용자 인증을 시도합니다
app.UseAuthorization(); // 사용자에게 보안 리소스에 접근할 수 있는 권한을 부여합니다
app.UseSession(); // 세션 상태를 설정 및 유지합니다
5. Attribute
- 특성은 assembly, module, class, struct, enum, constructor, method, property, field, event, interface, parameter, delegate, returnvalue 등에 사용할 수 있습니다.
- 특성을 사용하여 런타임 리플렉션 서비스를 통해 추출할 수 있는 메타데이터에 추가 설명 정보를 배치할 수 있습니다.
- System.Attribute에서 파생되는 특수 클래스 인스턴스를 선언하면 컴파일러에서 특성을 만듭니다.
- 특성은 코드를 컴파일할 때 메타데이터로 내보내지며, 공용 언어 런타임과 사용자 지정 도구 또는 애플리케이션에서 런타임 리플렉션 서비스를 통해 사용할 수 있습니다.
- 모든 특성 이름은 규칙에 따라 'Attribute'로 끝나지만, 런타임을 목적으로 하는 C#과 같은 언어에서는 특성의 전체 이름을 지정할 필요가 없습니다. 예를 들어, System.ObsoleteAttribute를 초기화하려는 경우 Obsolete로만 참조해야 합니다.
특성 적용 프로세스
참고: https://learn.microsoft.com/ko-kr/dotnet/standard/attributes/applying-attributes
- 새 특성을 정의하거나, 기존에 있는 .NET 특성을 사용합니다.
- 코드 요소 바로 앞에 특성을 배치하여, 해당 요소에 맞는 특성을 적용합니다. C#의 경우, 특성은 대괄호('[', ']')로 묶고, 공백으로 요소와 구분합니다. 또한, 줄 바꿈 문자를 사용할 수도 있습니다.
- 특성에 대한 위치 매개 변수와 명명된 매개 변수를 지정합니다.
자주 쓰이는 특성
참고: https://learn.microsoft.com/ko-kr/dotnet/csharp/advanced-topics/reflection-and-attributes/
https://www.geeksforgeeks.org/attributes-in-c-sharp/
Obsolete Attribute
public class Example
{
[ObsoleteAttribute("더 이상 사용하지 않는 프로그램 요소입니다.", true)]
public void CallOldMethod()
{
// 메서드 내용
}
}
더 이상 사용하지 않는 프로그램 요소를 표시하는 특성입니다.
ConditionalAttribute
#define DEBUG
public class Example
{
[Conditional("DEBUG")]
public static void Method1(int x)
{
Console.WriteLine("DEBUG is defined");
}
}
- 메서드 및 클래스에 ConditionalAttribute 특성을 적용할 수 있습니다.
- 지정된 조건부 컴파일 기호가 정의되어 있지 않으면 메서드 호출 또는 특성이 무시되어야 함을 컴파일러에 알립니다.
- void를 반환하지 않는 메서드에 이 특성을 적용하면 Visual Studio 컴파일 오류가 발생합니다.
DllImport Attribute
[System.Runtime.InteropServices.DllImport("user32.dll")]
extern static void SampleMethod();
이 속성은 메서드가 관리되지 않는 DLL에 표시된 정적 진입점임을 나타냅니다.
ApiControllerAttribute
참고: ApiControllerAttribute 클래스
[ApiController]
public class MyControllerBase : ControllerBase
{
}
ApiController 특성을 컨트롤러 클래스에 적용하여 다음과 같은 API 관련 동작을 사용할 수 있습니다.
- 특성 라우팅 요구 사항
- 자동 HTTP 400 응답
- 바인딩 소스 매개 변수 유추
- 다중 파트/폼 데이터 요청 유추
- 오류 상태 코드에 대한 문제 세부 정보
RouteAttribute
특성 라우팅은 규칙 기반 라우팅에 비해 Restful API에서 공통적인 특정 URI 패턴을 지원하기 쉽습니다. 이러한 유형의 URI의 예로는 '/customers/1/orders'가 있습니다.
특성 라우팅을 사용한 예제 코드는 아래와 같습니다.
[Route("customers/{customerId}/orders")]
public IEnumerable<Order> GetOrdersByCustomer(int customerId) {}
경로 접두사
[RoutePrefix("api/books")]
public class BooksController : ApiController
{
// GET api/books
[Route("")]
public IEnumerable<Book> Get() { }
// GET api/books/5
[Route("{id:int}")]
public Book Get(int id) { }
// POST api/books
[Route("")]
public HttpResponseMessage Post(Book book) { }
}
- 컨트롤러의 경로가 모두 동일한 접두사로 시작할 경우에 사용합니다.
- [RoutePrefix] 특성을 사용합니다.
아래는 경로 접두사를 재정의한 코드 예시입니다.
[RoutePrefix("api/books")]
public class BooksController : ApiController
{
// GET /api/authors/1/books
[Route("~/api/authors/{authorId:int}/books")]
public IEnumerable<Book> GetByAuthor(int authorId) { }
}
HTTPAttribute
- Web API는 요청의 HTTP 메서드에 따라 작업을 선택합니다.
- 기본적으로 요청의 HTTP 메서드 이름의 시작 부분과 일치하는 항목을 찾습니다. 이때, 대/소문자는 구분하지 않습니다. 즉, Post 메서드 요청과 PostUser 컨트롤러 메서드는 일치한다고 볼 수 있습니다.
- 아래의 특성을 사용하면 이 규칙을 재정의할 수 있습니다.
- [HttpPost]
- [HttpGet]
- [HttpPut]
- [HttpPatch]
- [HttpDelete]
- [HttpHead]
- [HttpOptions]
다음은 HTTPAttribute를 사용한 예시 코드입니다.
[Route("api/users")]
[HttpPost]
public HttpResponseMessage CreateUser(User user) { }
[Route("api/users/{id:long}")]
[HttpDelete]
public HttpResponseMessage RemoveUser(long id) { }
RequireAttribute
[Required()]
public object MiddleName;
위 코드의 경우, MiddleName이 null이면 유효성 검사 오류 메시지가 표시됩니다.
사용자 정의 Attribute
참고: 사용자 지정 특성 작성
public class MyClass
{
[MyAttribute]
public void MyMethod()
{
// 메서드 내용
}
}
- 사용자 지정 특성은 System.Attribute를 상속받은 클래스입니다.
- 특성 적용 시 대괄호로 특성 클래스를 인스턴스화합니다.
'Framework > .NET' 카테고리의 다른 글
MySQL에 C#의 DateTime.MaxValue를 넣을 수 없는 이유 (0) | 2023.05.08 |
---|---|
ASP.NET에서의 MySQL VS Redis (0) | 2023.05.03 |
[ASP.NET Core로 Web API 만들기] 2. SqlKata로 비동기 CRUD 구현하기 (0) | 2023.04.25 |