정보
요청한 주제가 아래에 표시됩니다. 그러나 이 주제는 이 라이브러리에 포함되지 않습니다.

Windows Phone 8의 로컬 데이터베이스 모범 사례

2014-06-18

적용 대상: Windows Phone 8 및 Windows Phone Silverlight 8.1 | Windows Phone OS 7.1

 

Windows Phone OS 7.1 부터는 앱의 로컬 폴더에 있는 관계형 데이터를 로컬 데이터베이스에 저장할 수 있습니다. 이 항목에서는 로컬 데이터베이스로 작업할 때 고려해야 하는 기타 모범 사례 컬렉션에 대해 설명합니다. Windows Phone 앱에서 로컬 데이터베이스를 사용하는 방법에 대한 단계별 지침은 Windows Phone 8의 기본 로컬 데이터베이스 앱을 만드는 방법Windows Phone 8의 로컬 데이터베이스 앱(MVVM 포함)을 만드는 방법를 참조하세요.

이 항목에는 다음 단원이 포함되어 있습니다.

 

데이터 가상화 및 Skip/Take 사용

ListBox 컨트롤을 사용하면 사용자가 목록을 스크롤할 때 SkipTake 메서드를 통해 적절한 일괄 처리 항목을 가져올 수 있습니다. 데이터 바인딩된 ListBox 컨트롤에서 데이터 가상화를 사용하도록 설정하는 방법에 대한 자세한 내용은 Windows Phone 7에서 ListBox 성능 향상: 데이터 가상화를 참조하세요.

SkipTake 메서드를 사용하면 ListBox 컨트롤에 표시해야 할 때까지 데이터가 데이터베이스에서 메모리로 로드되지 않습니다. 예를 들어 다음 코드는 데이터베이스에서 501-550 레코드를 검색하는 방법을 보여 줍니다.

return
(from f in App.FeedsDB.Feeds                    
select f).Skip(500).Take(50);

사용자가 목록을 스크롤할 때 이 로드 작업을 수행하는 경우 성능이 저하되기 때문에 이 방법은 데이터 집합이 큰 시나리오(150개 항목 초과)에서만 제한적으로 사용해야 합니다. 작은 데이터 집합의 경우 전체 컬렉션을 메모리에 로드하고 바인딩하는 것이 성능 측면에서 더 낫습니다.

양방향 데이터 바인딩

LINQ to SQL 데이터 컨텍스트의 엔터티는 다른 POCO(Plain Old CLR Object)처럼 UI 컨트롤에 바인딩된 양방향 데이터일 수 있습니다. 엔터티에 데이터 바인딩하는 경우 다음 사항을 고려합니다.

  • 로컬 데이터베이스와의 관계를 유지하려면 앱이 하위 집합 프로젝션이 아니라 전체 엔터티에 바인딩되어야 합니다.

  • 엔터티에 대한 변경 사항을 데이터 컨텍스트에 유지하려면 SubmitChanges 메서드를 사용합니다.

큰 일괄 처리 편집

드물긴 하지만 앱이 로컬 데이터베이스의 많은 레코드나 모든 레코드를 업데이트해야 하는 경우가 있습니다. 예를 들어 10,000명의 고객이 포함된 테이블에 CustomerType이라는 속성이 있습니다. CustomerType 속성에 Standard, Premiere 또는 Exclusive 값을 할당할 수 있습니다. 고객 5,000명은 CustomerTypeExclusive로 지정되었으며 이 카테고리의 이름을 Platinum으로 바꾸어야 한다고 가정합니다.

이때 권장되지 않는 한 가지 방법은 모든 Exclusive 고객을 선택하고 각 고객을 반복하여 해당 유형을 Platinum으로 업데이트하는 것입니다. 데이터 컨텍스트에 모든 변경 사항이 적용된 후 SubmitChanges 메서드가 호출됩니다. 이 시나리오에서는 5,000개 개체가 모두 메모리에 로드되어 단말기에서 문제가 발생할 수 있습니다.

단말기 메모리의 부담을 줄이기 위해 대신 별도의 데이터 컨텍스트를 사용하여 일괄 처리로 업데이트를 수행하는 것이 좋습니다. 이 방법을 사용할 경우 목록을 정렬하고 SkipTake 메서드를 사용하는 쿼리를 활용하여 데이터가 적절하게 페이징되도록 합니다. 일괄 처리가 완료되면 DataContext 개체에서 Dispose를 호출하여 가비지 수집기가 컨텍스트 및 캐시된 관련 개체를 정리하도록 합니다. 또는 using 문에서 DataContext 개체를 캡슐화하여 자동으로 삭제되도록 할 수 있습니다.

버전 열을 통해 빠른 업데이트 사용

테이블에 대한 업데이트 작업의 성능을 최적화하는 가장 쉬운 방법 중 하나는 버전 열을 추가하는 것입니다. 이 최적화는 Windows Phone 의 LINQ to SQL과 관련이 있습니다. 예를 들어 엔터티에서 다음 코드를 추가합니다.

        [Column(IsVersion=true)]
        private Binary _version;

이 최적화를 구현하면 큰 업데이트의 경우 성능이 눈에 띄게 향상될 수 있습니다. 로컬 데이터베이스에서 사용되는 이 최적화의 예는 Windows Phone 8의 로컬 데이터베이스 앱(MVVM 포함)을 만드는 방법를 참조하세요.

데이터베이스에 변경 사항 전송

변경 사항을 데이터베이스로 보내는 경우 앱 시나리오에 따라 최적 일괄 처리 크기와 전송 빈도가 결정됩니다. 다음을 고려하세요.

  • SubmitChanges 메서드는 실제로 변경 사항이 파일 시스템에 저장될 때까지 반환되지 않습니다. 이 작업은 시간이 걸릴 수 있으며 가볍게 수행하면 안 됩니다. 예를 들어 사용자가 CheckBox 컨트롤을 선택하거나 선택을 취소할 때마다 앱이 변경 사항을 전송하면 상당한 오버헤드가 발생합니다.

  • 하지만 변경 사항 수가 많을수록 트랜잭션을 전송하는 데 걸리는 시간이 증가합니다. 앱이 종료 시 너무 큰 일괄 처리를 전송하려고 하면 앱 프로세스가 종료되기 전에 일괄 처리가 완료되지 않을 가능성이 있습니다.

일반적으로 사용자 작업에 의한 변경 사항은 데이터가 손실될 경우 복원할 방법이 없으므로 자주 전송하는 것이 좋습니다. 복구 가능한 변경 사항(예: 클라우드 서비스의 캐시된 데이터)의 경우 변경 집합이 커져도 큰 문제가 되지 않습니다. 종료 시 성공적으로 커밋되지 않은 경우 언제든지 데이터를 복원할 수 있기 때문입니다.

앱이 대량 BLOB(Binary Large Object) 데이터를 저장하고 검색해야 하는 경우 로컬 데이터베이스나 로컬 폴더에 직접 데이터를 저장할 수 있습니다.

binary(n), varbinary(n) 또는 image 데이터 형식을 사용하여 BLOB 데이터를 로컬 데이터베이스에 직접 저장할 수 있습니다. 이 방법을 사용하는 경우 최대 버퍼 크기 값을 다르게 지정하여 BLOB 쿼리 성능을 테스트하는 것이 좋습니다. 연결 문자열에서 max buffer size 매개 변수를 늘리면 BLOB의 검색 성능이 향상되지만 앱의 메모리 사용도 증가합니다. 연결 문자열에 대한 자세한 내용은 Windows Phone 8의 로컬 데이터베이스 연결 문자열을 참조하세요.

또는 BLOB 데이터를 로컬 폴더에 직접 저장할 수 있습니다. 이 방법을 사용하는 경우 응용프로그램이 BLOB 데이터의 경로만 로컬 데이터베이스에 저장할 수 있습니다. 로컬 폴더에도 로컬 데이터베이스 파일이 있으므로 앱이 설치되어 있는 한 로컬 폴더와 로컬 데이터베이스에 저장된 BLOB 데이터가 단말기에서 모두 유지될 수 있습니다. 로컬 폴더 작업에 대한 자세한 내용은 Windows Phone 8의 데이터를 참조하세요.

INotifyPropertyChanging

LINQ to SQL 변경 추적 기능은 각 개체의 복사본을 두 개씩 유지 관리합니다. 개체 복사본 하나는 데이터베이스에서 원래 구체화된 대로 유지됩니다. 다른 복사본은 앱에서 변경됩니다. 변경 사항이 전송되면 LINQ to SQL에서 업데이트된 속성을 확인하고 변경 사항만 데이터베이스 트랜잭션으로 전송할 수 있습니다.

기본적으로 LINQ to SQL은 개체가 구체화될 때 개체의 복사본을 두 개씩 만듭니다. 하지만 실제로 구체화된 컬렉션의 일부 개체만 특정 트랜잭션에서 수정되는 경우도 많습니다. 이 경우 개체의 두 번째 복사본을 유지할 이유가 없습니다.

INotifyPropertyChanging 인터페이스를 사용하면 앱이 데이터베이스에 업데이트로 전송될 속성을 수정하는 경우 이를 DataContext에 알릴 수 있습니다. DataContext는 이 알림을 트리거로 사용하여 복사본을 만들 수 있습니다. 이렇게 하면 실제로 변경되는 항목만 복제하면 됩니다.

엔터티에 다음 코드를 추가하고 값이 변경되기 직전에 각 엔터티 속성의 setter에서 NotifyPropertyChanging 메서드를 호출하여 INotifyPropertyChanging 인터페이스를 구현합니다.

        public event PropertyChangingEventHandler PropertyChanging;

        // Used to notify that a property is about to change
        private void NotifyPropertyChanging(string propertyName)
        {
            if (PropertyChanging != null)
            {
                PropertyChanging(this, new PropertyChangingEventArgs(propertyName));
            }
        }

INotifyPropertyChanging 인터페이스를 사용하는 방법의 예는 Windows Phone 8의 로컬 데이터베이스 앱(MVVM 포함)을 만드는 방법를 참조하세요.

ObjectTrackingEnabled

때때로 모바일 앱에는 데이터를 제공하기만 하고 사용자가 변경할 수 없도록 해야 하는 읽기 전용 쿼리 시나리오가 있습니다. 이 경우 변경 추적 인프라를 완전히 꺼서 메모리를 절약할 수 있습니다. 이렇게 하려면 DataContextObjectTrackingEnabled 속성을 false로 설정합니다.

참고참고:

이 설정은 지정된 DataContext 개체에만 적용됩니다. 다른 DataContext 개체를 사용하여 읽기 전용이 아닌 동일한 행에서 작업을 수행할 수 있습니다.

빠른 앱 전환

사용자가 앱에서 전방 탐색하고 운영 체제가 앱을 유휴 상태로 전환하면 모든 앱의 스레드가 중지되고 추가 처리가 수행되지 않습니다. 하지만 일부 데이터베이스 작업을 비롯하여 완전히 중지할 수 없어 운영 체제에서 앱 대신 수행하는 특정 작업이 있습니다.

특히 상당한 그룹화 및 정렬 작업이 포함된 쿼리와 같이 매우 복잡한 쿼리는 완료하는 데 시간이 걸릴 수 있으며 앱이 일시 중지되기까지 허용되는 시간보다 오래 실행될 수도 있습니다. 이 경우 운영 체제가 완전히 앱에 삭제 표식을 지정합니다. 응용프로그램이 빠른 앱 전환 기능을 완전히 이용할 수 있게 하려면 비활성화하는 동안 복잡한 쿼리를 수행하지 않아야 합니다.

유휴 상태 및 Windows Phone 앱 수명 주기에 대한 자세한 내용은 Windows Phone 8의 앱 활성화 및 비활성화를 참조하세요.

삭제 표시

앱에 삭제 표식을 지정하면 기본 데이터베이스 연결이 닫힙니다. 삭제 표식 지정 후 이전 상태로 돌아가려면 앱에서 삭제 표식 전에 수행한 쿼리를 다시 실행해야 합니다.

인덱싱

기본 키로 지정된 데이터베이스 열에 대해 자동으로 인덱스가 생성됩니다. 그 후에 테이블에 생성된 추가 인덱스를 보조 인덱스라고 합니다. 보조 인덱스는 결과의 정렬 순서를 확인하는 데 사용되는 속성을 비롯하여 자주 쿼리되는 엔터티의 속성에 사용해야 합니다.

자주 정렬되는 속성에는 적절한 정렬 순서(오름차순 또는 내림차순)를 사용해야 합니다. 기본적으로 인덱스에는 오름차순 정렬 순서가 지정됩니다. Index 특성에 정렬 순서를 명시적으로 지정하려면 열 이름 뒤에 ASC 또는 DESC를 사용하여 각각 오름차순 또는 내림차순 정렬을 지정합니다. 예를 들어 다음 엔터티 특성은 OrderID 열의 오름차순 정렬 순서와 Quantity 열의 내림차순 정렬 순서를 사용한 인덱스를 지정합니다.

[Index(Column=”OrderID ASC, Quantity DESC”)]

컴파일된 쿼리

기본적으로 LINQ to SQL은 런타임에 쿼리가 실행될 때마다 LINQ 식 트리를 해당 Transact-SQL 문으로 변환합니다. 자주 실행되는 쿼리(예: 이 ID를 가진 레코드 찾기)의 경우 매번 해당하는 Transact-SQL을 생성하는 오버헤드가 매우 큽니다. 이러한 비효율성을 방지하기 위해 컴파일된 쿼리를 사용할 수 있습니다. 컴파일된 쿼리는 매개 변수가 있는 Transact-SQL 문을 미리 생성하며, 나중에 다른 값으로 이 문을 다시 사용할 수 있습니다.

포그라운드 및 백그라운드 상호 작용

Windows Phone 의 로컬 데이터베이스는 포그라운드 앱 및 관련 백그라운드 에이전트의 동시 사용을 지원합니다. 이 시나리오에서는 다음 사항을 고려하는 것이 중요합니다. 데이터베이스 클라이언트(포그라운드 앱 또는 라이브 에이전트) 중 하나가 일부 데이터베이스 작업을 수행하는 동안 갑자기 종료되면 다른 연결이 무효화될 수 있으며 다시 설정해야 합니다. 이 경우 데이터베이스의 다른 클라이언트에 SqlCeException 예외가 수신됩니다. 이러한 예외가 수신되면 앱이 기존 DataContext 개체를 삭제하고 다시 만들어야 합니다.

팁팁:

서로 다른 스레드 간 로컬 폴더에 대한 액세스를 동기화하려면 Mutex 클래스를 사용하는 것이 좋습니다. 뮤텍스는 공유 리소스에 대한 단독 액세스 권한을 하나의 스레드에만 부여합니다. 스레드가 뮤텍스를 가져오면 첫 번째 스레드가 뮤텍스를 해제할 때까지 해당 뮤텍스를 가져오려는 두 번째 스레드는 일시 중단됩니다. 예를 들어 뮤텍스를 사용하여 포그라운드 및 백그라운드 에이전트에서 실행 중인 Windows Phone 앱 간 폴더에 대한 액세스를 동기화할 수 있습니다.

리소스 제약 조건

포그라운드 앱은 단말기 리소스의 상당 부분을 받습니다(특히 최대 90MB의 RAM). 반면, 백그라운드에서 실행되는 라이브 에이전트는 리소스가 매우 제한됩니다. 일반적으로 앱의 백그라운드 작업을 독립적으로 컴파일할 수 있고 리소스가 제한된 관리 가능한 작은 청크로 나눕니다. 앱에서 데이터베이스를 사용하는 경우 쿼리 및 업데이트 작업을 작은 일괄 처리로 수행하는 것입니다. 예를 들어 RSS 수집기 앱은 작업 전에 데이터베이스의 모든 피드를 구체화하는 대신 캐시의 문서를 업데이트하는 작업을 피드별로 나눌 수 있습니다. 이렇게 하면 앱이 임의의 한 시점에 개체로 구체화되는 데이터베이스 레코드 수를 제한할 수 있으므로 에이전트가 플랫폼에서 설정된 메모리 제한을 초과하지 않습니다.

표시: