Blazor 서버 기본 구조

Dependency Injection, Router, _imports, Layout 등을 알아본다

 

 

 

Dependency Injection

이를 간단하게 알아보기 위해서 이미 존재하고있던 Data폴더에 FoodService.cs를 생성한다.

이후 이 안에 Food class를 생성해주고 프로퍼티값으로 Name, Price를 선언해준다.

그리고 cs파일을 생성하면서 생성된 FoodService class안에

public List<Food> GetFoods()
{
List<Food> foods = new List<Food>()
{
new Food() {Name = "bap", Price = 7000},
new Food() {Name = "KimBap", Price = 3000},
new Food() {Name = "ChoBap", Price = 9000}
};
return foods;
}

위와 같이 List<Food>를 반환해주는 함수를 생성해준다.

이후에 Blazor App을 생성하면서 존재하는 Index.razor파일안에 아래와 같은 코드를 추가해주면

웹 페이지에 하드코딩해서 넣어준 서비스의 목록들이 나오긴 한다.

 

@using BlazorApp.Data;

<div>
@foreach(var food in _foodService.GetFood())
{
<div>@food.Name</div>
<div>@food.Price</div>
}

</div>
@code{
FoodSerive _foodService= new FoodService();
}

다만, 위와 같이 코드를 작성하게 된다면 코드간의 의존성이 너무 심해진다는 문제가 발생한다.

따라서 Interface을 활용하여 이를 해결해주자.

다시 FoodService.cs로 돌아와서 Interface를 선언해주자 이렇게 해주면 이 FoodService를 사용하고싶은

사람들은 이 interface를 구현해야한다는 규칙이 생기게 된다.

public interface IFoodService
{
IEnumeralbe<Food> GetFoods();
}

public class FoodService : IFoodService
{
	public IEnumeralbe<Food> GetFoods()
	{
		List<Food> foods = new List<Food>()
		{
		new Food() {Name = "bap", Price = 7000},
		new Food() {Name = "KimBap", Price = 3000},
		new Food() {Name = "ChoBap", Price = 9000}
		};
		return foods;
	}
}

하지만 아직까지 razor의 컴포넌트들마다 일일이 new FoodService()를 호출해야하는 문제가 존재한다.

이를 해결하기 위해서 Dependency Injection이다.

이는 Startup.cs안에 ConfigureServices 함수안에 service.AddSingleton<IFoodService, FoodService>();로 선언하게 되면

이제 new를 통해서 선언하는 것이아닌

@inject IFoodService foodSerivce를 통해서 FoodService를 new를 통해서 생성하지 않고 사용이 가능하다.

[예시]

@using BlazorApp.Data;

@inject IFoodService foodSerivce

<div>
@foreach(var food in foodSerivce.GetFood())
{
<div>@food.Name</div>
<div>@food.Price</div>
}

</div>

하지만 이러한 방식이 전역적으로 사용되는것은 아니다.

이를 살펴보자.

public class SingletonService : IDisposable
{
public Guid ID {get; set;} //간단하게 ID를 생성하는 인터페이스이다.

public SingletonService() //cotr + tab,tab으로 해당 class의 생성자를 간단하게 생성할 수 있다.
{
ID = Guid.NewGuid();
}

public Dispose()
{
Console.WriteLine("SingletonService Disposed!");
}
}

public class TransientService : IDisposable
{

public Guid ID {get; set;} //간단하게 ID를 생성하는 인터페이스이다.

public TransientService() //cotr + tab,tab으로 해당 class의 생성자를 간단하게 생성할 수 있다.
{
ID = Guid.NewGuid();
}

public Dispose()
{
Console.WriteLine("TransientService Disposed!");
}
}

public class ScopeService : IDisposable
{
public Guid ID {get; set;} //간단하게 ID를 생성하는 인터페이스이다.

public ScopeService() //cotr + tab,tab으로 해당 class의 생성자를 간단하게 생성할 수 있다.
{
ID = Guid.NewGuid();
}

public Dispose()
{
Console.WriteLine("ScopeService Disposed!");
}

}

[생명주기]

3가지 모드를 파악해보자.

 

다음처럼 생성

service.AddSingleton<SingletonService>();

service.AddTransient<TransientService>();

service.AddScoped<ScopedService>();

 

​다음 처럼 사용 (주로 상단에 선언)

@inject SingletonService singleton;

@inject TransientService transient;

@inject ScopedService scoped;

@inject 가 있는 동일페이지에서 다음 코드가 있을때 guid를 통해  id 가 페이지개 갱신 될때마다 재생성 되는 것을 보면 생명 주기를 파악 할 수 있다

<div>

<h1>Singleton</h1>

Guid: @singleton.ID

<h1>Singleton</h1>

Guid: @transient.ID

<h1>Singleton</h1>

Guid: @scoped.ID

<div>

이렇게 하고 생명주기를 살펴보면

singleton와scoped의 id는 변화하지 않고

transient의 id는 변화한다.

하지만 새로고침을 실행하면 singleton의 id는 바뀌지 않지만 나머지 두개는 변화한다.

(Balzor Server 프로젝트의 경우에는 변화가 없지만 클라이언트(Blazor WebAssembly) 사이드같은 경우에는 singleton 방식도 guid 도 변경 된다)

즉, 변동하지 않고 모두에게 똑같이 보여야 한다면 singleton을

유저마다 갱신되어 보여야 한다면 나머지 2개를 선택하여 사용해야 하는데

Transient는 말그래도 웹페이지의 요청이 일어날때마다 바뀌고

Scoped는 웹에 접속할때 마다 변경된다.

 

 

SPA(single Page Application)

SPA는 기존에 존재하던 정적 페이지가 아닌 동적 페이지라고 할 수 있다

기존에는 웹서버에서 전체를 실시간으로 만들어 보내주는 방식이였지만

일부 변경되는 데이터에 따라(Dom) 모습이 달라지는 것을 동적페이지 방식이라 한다=> SPA

 

 

레이아웃

다음 과 같은 레이웃의 범위를 알아보면 아래 코드로 글자가 어디에 들어 가있는지 보면 된다 a, d , c

 

MainLayout.razor

@inherits LayoutComponentBase

<div class="sidebar">
    <p style="color : #be0000;">
        a
    </p>
    
    <NavMenu />
</div>

<div class="main">
    <div class="top-row px-4">
        c
        <a href="https://docs.microsoft.com/aspnet/" target="_blank">About</a>
    </div>

    <div class="content px-4">
        d
        @Body
    </div>
</div>

_imports.razor 알아보기

이 컴포넌트 안에 사용되는 @키워드들은 같은 폴더안에 있는 모든 컴포넌트들에게 적용되게 된다.

imports.razor 의 내용을 ABC 라는 폴더에 넣어놓고
내용을 새로만든 레이아웃 @layout MainLayout2.razor

해놓으면 해당 폴더에 있는 razor 페이지들의 기본 레이아웃은 MainLayout 이 아닌  MainLayout2 으로 변경된다

 

 

기본 레이아웃은 자체는 다음 코드를 상속받아 레이아웃 자체를 구성한다

@inherits LayoutComponentBase

 반드시 하나는 이 것을 상속받는 레잉아웃이 있어야 한다 이것이 적용된 페이지가

MainLayout.razor 이다

 

@inherits를 사용하게되면 class를 상속해서 사용할 수 있게 된다.

@layout키워드를 사용하면 레이아웃을 바꿔서 적용할 수 있다.

키워드를 사용하지 않고 모든 레이아웃을 변경하기 위해서는 App.razor에 DefaultLayout을 변경해주면 된다.

 

 

 

Router : 주소/sports/55  과 같이 주소 뒤에 어떤 페이지를 보여줄지에 대한 것

<Router AppAssembly="@typeof(Program).Assembly">
    <Found Context="routeData">
        <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
    </Found>
    <NotFound>
        <LayoutView Layout="@typeof(MainLayout)">
            <p>Sorry, there's nothing at this address.</p>
        </LayoutView>
    </NotFound>
</Router>

 

<Router 는 다음 처럼 되어 있다 Found 가 RenderFragment 로 내용을 보여주기 위한 형태인것을 알 수있다(템플릿)

그리고 Router는 상대적 기준으로 생각하고 작성해야 한다 즉 현재 경로 위쪽이 무엇인지 기본적으로 절대경로로 생각하면 안된다는것

 

 

 

 

 아래코드에서

<div class="top-row pl-4 navbar navbar-dark">
    <a class="navbar-brand" href="">BlazorApp</a>
    <button class="navbar-toggler" @onclick="ToggleNavMenu">
        <span class="navbar-toggler-icon"></span>
    </button>
</div>

<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
    <ul class="nav flex-column">
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="" Match="NavLinkMatch.All">
                <span class="oi oi-home" aria-hidden="true"></span> Home
            </NavLink>
        </li>
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="counter">
                <span class="oi oi-plus" aria-hidden="true"></span> Counter
            </NavLink>
        </li>
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="fetchdata">
                <span class="oi oi-list-rich" aria-hidden="true"></span> Fetch data
            </NavLink>
        </li>
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="user">
                <span class="oi oi-list-rich" aria-hidden="true"></span> User
            </NavLink>
        </li>
    </ul>
</div>

@code {
    private bool collapseNavMenu = true;

    private string NavMenuCssClass => collapseNavMenu ? "collapse" : null;

    private void ToggleNavMenu()
    {
        collapseNavMenu = !collapseNavMenu;
    }
}

 

 

 이 부분은 

<div class="top-row pl-4 navbar navbar-dark">
    <a class="navbar-brand" href="">BlazorApp</a>
    <button class="navbar-toggler" @onclick="ToggleNavMenu">
        <span class="navbar-toggler-icon"></span>
    </button>
</div>

이 처럼 화면이 작아지는 모바일 환경에서 메뉴표현등으로 나타내기 위한 버튼으로 화면이 큰 윈도우 환경에선 기본적으로는 화면을 줄이지 않는 이상 안보일 수 있다

 

 

NavLink : 이동 시키는 <a href > 와 유사한데 비주얼 적으로 차이가 있는데 버튼을 누를때 스타일 적인 것만 다르다

상단에 Home, FetchData 등의 버튼을 참고하면 된다

 NavLink 는

<a href  를 써서 동일하게 구현할 수도 있고

NavigationManager 를 인젝션 하여

 이렇게 구현하여 구현 할 수도 있다

 

counter 페이지가 뜰때 다음처럼 값을 받을 수도 있다 값은 CurrentCount 로 들어가게 된다

 

 

https://localhost:44337/counter/30  30을 입력하면 CurrentCount 값이 30 이 된다

 

 

 

 

ref : https://blog.naver.com/whro152/223041082396

반응형

+ Recent posts