Lazy Initialization
Lazy Initialization이란, 사용자가 실제로 필요할 때만 로딩을 하여 데이터 낭비를 줄이게 하는 방법입니다. 직역하면 개체를 처음 사용할 때까지 생성을 지연시킨다는 의미입니다.
예를 들어, C#이나 Java로 구현한 HMI 화면이 전환될 때 사용자가 요구치 않은 데이터까지 로드를 해버리면 로드될 때마다 응답속도가 느려지게 되어서 효율적으로 데이터를 사용하고 있다고는 보기 어렵습니다.
Web에서도 사용자가 특정 웹 페이지를 방문을 했는데, 해당 페이지에 총 80장의 이미지가 있다고 가정을 합니다.
이미지의 크기는 한 5~6장 정도면 사용자가 보는 화면에 꽉 차는 상황이에요.
이 80장의 이미지를 전부 한꺼번에 로딩을 하게 되면 효율성이 있다고 볼 수 있을 까요?
실제로 사용자가 보는 화면은 80장에서 5장 밖에 되지 않을 텐데 말이죠.
사용자가 마우스 휠을 사용해 아래로 화면을 전환했을 때, 나머지 이미지를 순서대로 로딩을 하게 되면 서버에 부담이 덜 가게 될겁니다.
JS에서는 Scroll감지 기법으로 getBoundingClientRect 메소드를 이용해서 Lazy Loading을 구현하기도 합니다.
보통은 객체를 모두 초기화를 하는 것이 많지만 때에 따라 메모리 효율성을 높이기 위해 사용자가 필요로 하는 시점까지 데이터 로드를 지연하고 있다가 사용자의 요청이 오면 그때 객체를 초기화해 응답하는 Lazy Loading 방법을 사용하기도 합니다.
반대의 개념으로 사용하는 시점에 바로 초기화 하는 것을 Eager Loading이라고 합니다.
지연 초기화에 대해서는 c# 코드를 기반으로 예를 들겠습니다.
// Initialize by using default Lazy<T> constructor. The
// Orders array itself is not created yet.
Lazy<Orders> _orders = new Lazy<Orders>();
// Initialize by invoking a specific constructor on Order when Value
// property is accessed
Lazy<Orders> _orders = new Lazy<Orders>(() => new Orders(100));
위와 같은 Lazy<T> 제네릭 컬렉션을 이용해서 지연 객체를 초기화를 할 수 있는데, 위 코드에서는 Orders라는 클래스 기반으로 지연 개체를 생성했습니다.
이러한 지연 객체를 생성하게 되면 읽기 전용 속성인 Value를 사용하지 않는 한 Orders의 인스턴스가 생성되지 않습니다. 즉, 초기화가 지연이되고 있다는 이야기죠.
// We need to create the array only if displayOrders is true
if (displayOrders == true)
{
DisplayOrders(_orders.Value.OrderData);
}
else
{
// Don't waste resources getting order data.
}
위와 같이 DisplayOrders 함수에 _orders 지연 객체의 Value을 호출함으로 인스턴스가 초기화되어 OrderData = 100이었던 값을 출력해줍니다.
보통은 편의를 위해 Orders myOrders = _orders.Value로 myOrders에 객체를 초기화해서 사용합니다.
_orders = new Lazy<Orders>(() => new Orders(10));
만약, Lazy 객체를 다르게 초기화를 하고 싶으면 람다식을 이용해 100이라는 값 대신 10으로 새로 초기화를 할 수 있습니다.
class Customer
{
private Lazy<Orders> _orders;
public string CustomerID {get; private set;}
public Customer(string id)
{
CustomerID = id;
_orders = new Lazy<Orders>(() =>
{
// You can specify any additional
// initialization steps here.
return new Orders(this.CustomerID);
});
}
public Orders MyOrders
{
get
{
// Orders is created on first access here.
return _orders.Value;
}
}
}
위의 코드를 분석해보자면 특정 고객이 음식을 주문할 때, 이 음식을 주문한 고객의 ID를 초기화하는 구조, 라고 보시면 됩니다.
당연히 주문내역에 있는 음식은 고객이 가지고 있어야 하는 것이죠?
그리고 그 주문한 음식은 누구의 음식인지 고객의 ID를 알고 있어야 구분을 할 수 있겠죠?
다만, 지연 초기화가 사용된 것은 이러한 주문 과정이 고객이 주문을 했을 때만 필요하다는 것입니다.
위의 코드에서는 Customer 클래스의 생성자가 id를 받아서 Lazy객체를 초기화하는데, Lazy<T>의 Value 속성은 읽기전용이기 때문에 set 메서드를 사용할 수가 없어서 Customer의 생성자에서 Orders를 초기화하는 것이구요.
기타 다른 방법으로도 Lazy Loading을 구현할 수 있습니다.
보통 불필요한 메모리의 소모가 크게 발생하는 데이터에서 이러한 구조를 사용하는데, 예를 들자면 클라이언트가 서버에 데이터를 요청하는 곳에서 사용하게 됩니다.
관리 프로그램과 서버, 주문 어플리케이션 3가지의 프로그램이 있다고 가정했을 때, Orders는 고객의 주문 어플리케이션을 통해 서버의 데이터베이스에 Orders의 상세한 내용이 적재가 됩니다.
이를 브로커 패턴을 사용해 Subcribe하는 방법을 사용하여 관리 프로그램에 Alarm을 주는 방법을 사용하기도 하지만, 관리 프로그램에서 새로고침이라던가, 화면 전환이 일어났을 때, A음식, B음식, C음식의 주문현황을 모두 로딩하게 되면 어떻게 될까요?
관리자는 A음식의 주문현황만 확인하고 싶을 뿐이었는데, 나머지 B음식과 C음식의 주문현황이 로드된 메모리는 낭비가 되게 됩니다.
이때 초기화 지연을 사용해서 화면 전환이 일어났을 때 A음식 화면에서 A음식을 서버의 데이터베이스에서 가져오는 것과 같은 과정으로 메모리 낭비를 줄일 수가 있는 기법입니다.
참고 사이트 :: https://docs.microsoft.com/ko-kr/dotnet/framework/performance/lazy-initialization
'C# > WPF' 카테고리의 다른 글
WPF Fody 사용 방법 (0) | 2020.09.16 |
---|---|
[C#] nuget package manager console 사용 방법 (0) | 2020.09.03 |
[WPF] Text String Format Binding 사용 방법 (0) | 2020.08.31 |
[WPF] UserControl에 Binding하기 위한 DependencyProperty 사용 방법 (11) | 2020.07.03 |
[WPF] UserControl 에서 Property 속성 추가 방법 (2) | 2020.06.10 |