.NET core 5.0 , Sql server(MS-sql) 2019, Visual Studio 2019
개인 가계부를 웹사이트 형식으로 만들어 볼 예정이다.
마이크로소프트에서 지원하는 프레임워크다. 3.1 까지는 .NET CORE라는 이름으로 서비스 되었지만 이후 버전 부터 .NET 5.0이라는 이름으로 통일했다고 한다. 모바일,웹,데크스톱,클라우드 등엄청나게 다양한 것을 만들 수 있는 프레임워크다.
사용할 .NET의 컴포넌트
Entity Framework Core : 크로스 플랫폼 버전의 오픈소스 Entity 프레임워크다. entity 모델과 ORM으로 데이터베이스와 연결한다.
Identity Core : 사용자에게 역할과 승인을 부여하는 컴포넌트. 이 프로젝트에서는 로그인과 등록을 담당 할거다.
MVC core : Model/View/Controller의 작성을 도와주는 .NET 프레임워크다.
Razoer Core(Razor Pages) : Razor Pages는 .Net 프레임워크의 프로젝트다. Razor Page 자체 내에 Model(모델)과 Controller(컨트롤러) 코드가 포함되어 있다는 것이다. 간단히 말해 MVVM (Model-View-View-Model) 프레임 워크와 비슷하다. 컨트롤러와 컨트롤러 로직을 쓸 필요 없고 라우팅을 View 페이지에서 한다.
SignalIR : 무료 오픈소스 소프트웨어 라이브러리다. 서버코드가 클라이언트-사이드쪽으로 비동기로 처리할 수 있도록 도와준다.
Blazor : 클라이언트-사이드 쪽에서도 C#으로 작성해줄수 있게 해준다. 즉 프론트엔드를 자바스크립 대신 C#으로 처리
이제 VS를 켜서 프로젝트를 생성한다. 템플릿은 ASP.net MVC를 선택하였고 설정 부분에서 HTTPS,Razor Runtime compliation을 꼭 체크해주자. 프로젝트의 이름은 inAndOut으로 정했다.
클릭 몇번으로 멋지게 프로젝트가 생성되었다. inAndOut 프로젝트를 두번 클릭하면 설정과 관련된 부분이 나온다.
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<CopyRefAssembliesToPublishDirectory>false</CopyRefAssembliesToPublishDirectory>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="5.0.5" />
</ItemGroup>
</Project>
TargetFramework를 현재 사용하는 .NET의 버전을 보여주고 CopyRefAssembliesToPublishDirectory는 razor 컴파일 관련된 부분이다. 또한 PackageReference 부분을 보면 현재 프로젝트를 생성하면서 체크했던 설정들이 들어가있다.
namespace InAndOut
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}
다음으로 볼 부분은 Programs.cs다. 여기도 콘솔 어플리케이션 처럼 main 메소드가 정의 되었다. main 메소드 안에서는 CreateHostBuilder를 호출하고 있는데 보면 IHostBuilder 반환하고 있다. 직접적으로 웹 애플리케이션을 만들어주는 부분이라고 보면된다. 보면 webBuilder를 사용해 C#파일들을 모아 웹 애플리케이션으로 만들어 주고있다.
그 다음 파일을 보기전에 DI라는 개념에 대해 정리하고 가자. DI란 스프링에서도 나오는 개념인데, 하나의 테크닉이라고 보면된다. 하지만 스프링과 DI를 하는 방법이 조금 다른데 밑에 설명하도록 하겠다. DI를 설명하자면 하나의 객체 혹은 정적 메소드가 다른 객체에게 의존성을 제공하는거다. 즉 의존성은 사용 될 수 있는 하나의 객체(서비스) 라고 보면 된다.
만약 DI가 없다면 어떨지 생각해보자. 웹사이트에 페이지를 여러개 만든 후 데이터베이스에서 접근하려고 한다. 그렇다면 각 페이지마다 DB를 사용하기위해 DB 객체를 생성해야한다. 그리고 E-mail를 발송하는 메소드가 있다고 치면 그 메소드 또한 모든 페이지에 객체를 생성해야한다.
그러므로 처음에 객체를 생성하고, 이후에 관리하고, 마지막 객체의 폐기까지 전부다 개발자의 몫이다. 만약 데이터베이스나 이메일 클래스가 바뀌었다 생각해보자. 그러면 모든 페이지에 다시 가서 바꾸어야한다.
이러한 불편함을 개선하고자 나온것이 DI다. DI contatiner에 인터페이스로 정의된 IEmail과 IDb 그리고 해당 클래스들을 DI contatiner에 주입한다. 그리고 해당 기능을 원하는 페이지에서 인터페이스를 끌어다 쓰면 DI Container가 마법처럼 모든것을 다 관리해준다.
그리고 만약 수정이 필요하다면 Email 클래스와 DI Container에서만 수정 하면 된다. 스프링에서는 이 방법이 없고, setter() Method를 이용하는 방법과 생성자를 이용하는 방법밖에 없다.
이후 다시 위에 코드를 보면 Program.cs 파일 안에 Main 메소드에서 CreateHostBuilder를 호출하고 있고, CreateHostBuilder에서는 webBuilder를 통해 Startup을 쓰고있다. 그럼 Startup을 봐보도록 하자.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
}
꽤 많은 코드로 작성되어있지만 필요한 부분인 ConfigureServices와 ConfigureServices 메소드만 가져왔다.
ConfigureServices를 먼저 봐보자. ConfigureServices는 런타임 될 때 호출된 다음 Configure 메소드가 호출이 된다. IServiceCollection이라는 여러 서비스의 콜렉션이 들어올 수 있다. 그 뜻은 우리가 서비스를 추가할 수 있다는 뜻이다.
보면 이미 기본적으로 AddControllersWithViews라는 서비스가 호출되고 있는 것을 볼 수 있다. 나중에 우리 또한 이곳에다가 다른 서비스들을 넣을 것이다. 따라서 이 ConfigureServices가 DI container가 된다고 볼 수 있다. 즉 IEmail과 IDB처럼 현재 AddControllersWithViews라는 서비스가 주입되어 있는 상태다.
이제 Configure 메소드를 봐보자. 흥미로운 점은 파라미터가 2개가 있다는 점이다. 뒤에있는 ENV는 예를들어 우리가 현재 개발환경인지 프로덕션환경인지 체크한다. 우선 에러가 발생했을때 IF문에서 개발환경이면 UseDeveloperExceptionPage를 보여주지만 프로덕션 환경일때는 "/Home/Error" 페이지를 보여준다. 그러므로 따라서 저곳에다가 에러페이지를 정의할 필요가 있다.
또한 밑에보면 UseHsts라는 것이있는데, HSTS는 http strict transport security protocol의 약자이다. http의 요청에 의해 정보가 어떻게 전송되는지에 대한 방법이다. 개발 환경에서는 추천되지 않는데 왜냐하면 브라우저에 의해 매우 휘발성이 강하게 되기 때문이다. 개발하면서는 최근에 바뀐 내용들을 봐야하므로 추천 되지 않는다.
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
그 이후 아랫부분이다. 보면 많은 미들웨어들이 있고 마지막에 엔드포인트에서 끝나게 된다. 엔드포인트가 하는일에 대해 정확히 알기 위해선 먼저 라우팅을 봐야한다.
라우팅이란 요청된 URL과 어플리케이션 내에 맞는 컨트롤러와 짝을 맞춰주는 거라고 생각하면 된다. .Net 에서는 여러가지의 라우팅 방법이 있다.
컨트롤러 작업으로 라우팅 하는 방법을 자세히 말하면 다음과 같다.
예제를 보면서 설명하도록 하겠다. "/Appointment/Index" 라는 요청된 URL이 있다고 해보자. 해당 URL은 "Appointment"는 컨트롤러 이름, "Index"라는 이름을 가진 작업(메소드)로 매치가 될 것이다.
두번째 URL을 본다면 "/Appointment/details/1" 이라는 것이 보일 것이다. 마지막 details 까지 매치가 되고 나면 남은 숫자 1이 해당 작업의 파라미터로써 사용된다.
이제 다시 endpoints 부분을 봐보자. 해당 부분을 보면 endpoints로 default를 정의하고 있다. default는 Home/Index/id? 주소로 id는 옵셔널이지만 아무런 추가 url 주소 없이 해당 도메인에 접속한다면 Home 컨트롤러에서 Index 액션으로 보내지게 될 것이다.
엔드포인트를 정의하는 방법은 크게 3가지가 있는데
등이 있다. 현재 써져 있는 방식은 MVC 방법이다.
꽤 많은 실무강의를 들었지만 이렇게 프로젝트의 기본 설정부터 뜯어가며 설명해주는 강의는 처음 듣는거 같다. 대부분의 실무강의들은 코드를 작성하는 법을 가르쳐주지 왜 그렇게 작성 되어있는지는 안 알려준다. 궁금한 점이 있다면 다 개인적으로 찾아보거나 해야한다. 프레임워크 관련 실무강의를 들을때 마치 비행기를 탄 원숭이 같은 느낌이 많이 들었다.
그런 점에서 이 강의는 정말 가려웠던 부분을 속 시원하게 긁어준다. 하지만 영어 강의라 영어로 듣고 한글로 옮기려니 거의 시간이 배로 들고 번역가가 된 거 같은 기분이 든다. 영어로 그냥 들으면서 넘어가면 무슨 뜻인지 이해 하고 넘어갈 수 있는데 그걸 한글로 옮겨쓰는 거는 차원이 다른 이야기인것 같다.
영어 공부도 하고 일석이조라고 생각해야겠다.