SQLite를 사용하여 로컬에 데이터 저장

완료됨

관계형 데이터가 있을 때는 SQLite가 유용합니다. 소셜 미디어 앱을 빌드한다고 가정해 봅니다. 구독자에 대한 정보를 앱에 저장해야 합니다. 이 데이터에는 각 사용자에 대한 고유 ID와 해당 이름이 포함되어 있습니다. 이러한 종류의 관계를 SQLite 데이터베이스에서 쉽게 모델링할 수 있습니다.

이 단원에서는 SQLite-net을 사용하여 .NET MAUI 애플리케이션에서 SQLite를 사용하는 방법을 알아봅니다.

SQLite란?

SQLite는 경량의 플랫폼 간 로컬 데이터베이스이며, 모바일 애플리케이션을 위한 업계 표준이 되고 있습니다. SQLite에는 서버가 필요하지 않습니다. 데이터베이스는 디바이스의 파일 시스템에 있는 단일 디스크 파일에 저장됩니다. 모든 읽기 및 쓰기 작업은 SQLite 디스크 파일을 대상으로 직접 실행됩니다.

SQLite 네이티브 라이브러리가 Android와 iOS에 기본 제공됩니다. 다만, 엔진은 C/C++ API만 지원합니다. 이 시나리오는 SQLite 및 .NET과 상호 작용하는 방법을 원하는 .NET 개발자에게는 적합하지 않습니다.

SQLite-net이란?

원시 SQLite 엔진에는 .NET 개발자가 사용할 수 있는 여러 가지 C# 래퍼가 있습니다. 많은 .NET 개발자가 SQLite-net라는 인기 C# 래퍼를 사용합니다.

SQLite-net은 개체 관계형 매퍼입니다. 이를 사용하면 프로젝트에서 정의한 모델을 스키마로 사용할 수 있기 때문에 데이터베이스 스키마를 정의하는 프로세스를 간소화할 수 있습니다.

Diagram showing how SQLite-net provides a .NET wrapper and the SQLite C/C++ engine.

예를 들어 User을(를) 모델링하는 다음 클래스를 고려해 보겠습니다.

class User
{
    public int Id { get; set; }
    public string Username { get; set; }
    ...
}

개체 관계형 매퍼를 사용하면 이 초기 User 클래스를 가져와 클래스에 IdUsername 필드에 대한 열이 있는 User(이)라는 데이터베이스 테이블을 만들 수 있습니다.

SQLite-net은 NuGet 패키지로 제공됩니다. 사용하려면 sqlite-net-pcl 패키지를 앱에 추가해야 합니다. Visual Studio에서 NuGet 패키지 관리자를 사용합니다. 또한 Android애서 앱을 실행하려면 SQLitePCLRaw.provider.dynamic_cdecl 패키지도 추가해야 합니다.

SQLite 데이터베이스에 연결하는 방법

앱에서 SQLiteConnection 개체를 통해 SQLite 데이터베이스에 대한 연결을 설정할 수 있습니다. 이 클래스는 SQLite에서 제공하는 다른 형식 및 메서드와 함께 SQLite 네임스페이스에 정의됩니다. 이 개체를 인스턴스화할 때 데이터베이스 파일의 이름을 전달합니다. 그러면 생성자가 파일이 있으면 파일을 열고 없으면 파일을 만듭니다.

다음 코드에서는 예제를 보여 줍니다.

using SQLite;
...
string filename = ...
SQLiteConnection conn = new SQLiteConnection(filename);

filename은(는) 앱 샌드박스의 위치를 가리킵니다.

테이블을 만드는 방법

앞서 언급했듯이 SQLite-net은 개체 관계형 매퍼입니다. 따라서 C# 클래스에서 데이터베이스 스키마를 작성할 수 있습니다. SQLite-net은 일반 C# 클래스에서 데이터베이스 테이블을 빌드할 수 있지만 추가 메타데이터를 제공하기 위해 클래스에 추가할 수 있는 많은 특성이 있습니다. 이 메타데이터는 SQLite가 고유성과 같은 기능을 적용하고 데이터에 제약 조건을 적용하는 데 도움이 됩니다.

사용 가능한 특성은 다음과 같습니다.

  • Table: 테이블 이름을 클래스 이름이 아닌 다른 이름으로 하려면 테이블의 이름을 지정합니다.
  • PrimaryKey: 열이 기본 키임을 지정합니다.
  • AutoIncrement: 새 행을 삽입하면 열 값이 자동으로 증가하도록 지정합니다.
  • Column: 열 이름을 속성 이름이 아닌 다른 이름으로 하려면 열 이름을 지정합니다.
  • MaxLength: 열에 사용할 수 있는 최대 문자 수를 지정합니다.
  • Unique: 열의 값이 다른 모든 행에서 고유해야 한다는 것을 지정합니다.

다음 코드는 이러한 특성을 적용하는 User 클래스의 업데이트된 버전을 보여줍니다.

[Table("user")]
public class User
{
    // PrimaryKey is typically numeric 
    [PrimaryKey, AutoIncrement, Column("_id")]
    public int Id { get; set; }

    [MaxLength(250), Unique]
    public string Username { get; set; }
    ...
}

C# 클래스를 정의한 후 SQLiteConnection 클래스에서 CreateTable 제네릭 메서드를 호출하여 데이터베이스에 테이블을 생성합니다. 클래스를 형식 매개 변수로 지정합니다. 예를 들면 다음과 같습니다.

SQLiteConnection conn = new SQLiteConnection(filename);
conn.CreateTable<User>();

테이블이 데이터베이스에 이미 있으면 CreateTable 메서드가 스키마 클래스를 검사하여 변경 내용이 있는지 확인합니다. 있는 경우 작업은 데이터베이스 스키마를 업데이트하려고 시도합니다.

기본 읽기 및 쓰기 작업을 수행하는 방법

테이블이 생성되고 나면, 테이블과 상호 작용을 시작할 수 있습니다. 행을 추가하려면 SQLiteConnection 인스턴스에서 Insert 메서드를 사용하고 삽입할 데이터를 보유하는 적절한 형식의 개체를 제공합니다. 다음 코드는 User 테이블에 새 행을 추가하는 방법을 보여줍니다.

public int AddNewUser(User user)
{
    int result = conn.Insert(user);
    return result;
}

Insert 메서드는 int을(를) 반환하며, 이는 테이블에 삽입된 행 수를 나타냅니다. 이 경우에 이 숫자는 1입니다.

테이블에서 행을 검색하려면 Table 메서드를 사용합니다. 이 메서드는 개체 컬렉션(비어 있을 수도 있음)을 반환합니다.

public List<User> GetAllUsers()
{
    List<User> users = conn.Table<User>().ToList();
    return users;
}

Table 메서드는 TableQuery\<T> 개체를 반환합니다. List을(를) 얻으려면 앞의 예와 같이 ToList 메서드를 사용합니다.

LINQ를 사용하여 SQLite 쿼리 실행

Table 메서드는 테이블에서 모든 행을 검색합니다. 대부분의 경우 지정된 조건 집합과 일치하는 행의 하위 집합만 반환하려 합니다. 이러한 작업에 SQLite-net과 LINQ를 함께 사용합니다.

SQLite-net은 다음을 비롯한 일반적인 LINQ 쿼리를 많이 지원합니다.

  • Where
  • Take
  • Skip
  • OrderBy
  • OrderByDescending
  • ThenBy
  • ElementAt
  • 첫째
  • FirstOrDefault
  • ThenByDescending
  • 개수

이 메서드를 사용하면 확장 메서드 구문이나 LINQ C# 구문을 사용할 수 있습니다. 예를 들어 지정된 사용자의 세부 정보를 검색할 수 있는 코드 조각은 다음과 같습니다.

public User GetByUsername(string username)
{
    var user = from u in conn.Table<User>()
               where u.Username == username
               select u;
    return user.FirstOrDefault();
}

행 업데이트 및 삭제

SQLiteConnection 개체의 Update 메서드를 사용하여 행을 업데이트합니다. 새 값으로 업데이트할 행을 정의하는 개체를 제공합니다. Update 메서드는 제공된 개체와 기본 키 값이 동일한 행을 수정합니다. 반환되는 값은 변경된 행 수입니다. 이 값이 0이면 일치하는 기본 키가 있는 행을 찾을 수 없으며 항목이 업데이트되지 않습니다. 다음 코드 조각은 이 메서드의 작동 방식을 보여 줍니다.

public int UpdateUser(User user)
{
    int result = 0;
    result = conn.Update(user);
    return result;
}

SQLiteConnection 개체의 Delete 메서드를 사용하여 테이블에서 행을 제거합니다. 이 메서드의 가장 간단한 형식은 아래 예와 같이 삭제할 항목의 기본 키를 매개 변수로 사용합니다. 이 Delete 메서드 형식은 제네릭이며 형식 매개 변수가 필요합니다. 반환되는 값은 테이블에서 제거된 행 수입니다.

public int DeleteUser(int userID)
{
    int result = 0;
    result = conn.Delete<User>(userID);
    return result;
}