Azure Table에서 Go용 Azure SDK를 사용하는 방법

적용 대상: 테이블

이 문서의 내용은 Azure Table Storage와 Azure Cosmos DB for Table에 적용됩니다. API for Table은 처리량 최적화 테이블, 글로벌 배포 및 자동 보조 인덱스를 제공하는 테이블 스토리지용 프리미엄 제품입니다.

이 문서에서는 Go용 Azure SDK를 사용하여 Azure Tables 및 Table 엔터티를 만들고 나열하고 삭제하는 방법을 알아봅니다.

Azure Table은 스키마 없이 디자인된 키 특성 저장소를 제공하여 클라우드에 정형 NoSQL 데이터를 저장할 수 있게 해줍니다. Azure Table Storage는 스키마가 없기 때문에 진화하는 애플리케이션의 요구 사항에 따라 데이터를 쉽게 적응시킬 수 있습니다. 테이블의 데이터 및 API에 대한 액세스는 많은 애플리케이션에서 빠르고 비용 효율적인 솔루션입니다.

Table 스토리지 또는 Azure Cosmos DB를 사용하여 웹 애플리케이션의 사용자 데이터, 주소록, 디바이스 정보와 같은 유연한 데이터 세트를 저장할 수 있습니다. 또는 서비스에 필요한 다른 유형의 메타데이터를 저장할 수 있습니다. 테이블에 저장할 수 있는 엔터티 수에는 제한이 없으며, 스토리지 계정에 포함할 수 있는 테이블의 수에는 스토리지 계정의 최대 용량 한도까지 제한이 없습니다.

이 문서에 따라 Go용 Azure SDK를 사용하여 Azure Table Storage를 관리하는 방법을 알아봅니다.

필수 조건

환경 설정

이 자습서를 진행하려면 Azure 리소스 그룹, 스토리지 계정 및 테이블 리소스가 필요합니다. 다음 명령을 실행하여 환경을 설정합니다.

  1. Azure 리소스 그룹을 만듭니다.

    az group create --name myResourceGroup --location eastus
    
  2. 다음으로 새 Azure Table에 대한 Azure Storage 계정을 만듭니다.

    az storage account create --name <storageAccountName> --resource-group myResourceGroup --location eastus --sku Standard_LRS
    
  3. 테이블 리소스를 만듭니다.

    az storage table create --account-name <storageAccountName> --account-key 'storageKey' --name mytable
    

패키지 설치

Go를 사용하여 Azure Table을 관리하려면 azidentityaztables라는 두 개의 패키지가 필요합니다. azidentity 패키지는 Azure에 인증하는 방법을 제공합니다. 그리고 aztables 패키지는 Azure에서 테이블 리소스를 관리하는 기능을 제공합니다. 다음 Go 명령을 실행하여 이러한 패키지를 설치합니다.

go get github.com/Azure/azure-sdk-for-go/sdk/data/aztables
go get github.com/Azure/azure-sdk-for-go/sdk/azidentity

Azure에 인증하는 방법에 대한 자세한 내용은 Go용 Azure SDK를 사용하여 Azure 인증을 확인하세요.

샘플 애플리케이션 만들기

패키지가 설치되면 Go용 Azure SDK를 사용하여 Azure Table을 관리하는 샘플 애플리케이션을 만듭니다. go mod 명령을 실행하여 azTableSample이라는 새 모듈을 만듭니다.

go mod init azTableSample

다음으로, main.go라는 파일을 만든 다음, 아래에 복사합니다.

package main

import (
    "context"
    "encoding/json"
    "fmt"
    "os"

    "github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
    "github.com/Azure/azure-sdk-for-go/sdk/azidentity"
    "github.com/Azure/azure-sdk-for-go/sdk/data/aztables"
)

type InventoryEntity struct {
    aztables.Entity
    Price       float32
    Inventory   int32
    ProductName string
    OnSale      bool
}

type PurchasedEntity struct {
    aztables.Entity
    Price float32
    ProductName string
    OnSale bool
}

func getClient() *aztables.Client {
    accountName, ok := os.LookupEnv("AZURE_STORAGE_ACCOUNT")
    if !ok {
        panic("AZURE_STORAGE_ACCOUNT environment variable not found")
    }

    tableName, ok := os.LookupEnv("AZURE_TABLE_NAME")
    if !ok {
        panic("AZURE_TABLE_NAME environment variable not found")
    }

    cred, err := azidentity.NewDefaultAzureCredential(nil)
    if err != nil {
        panic(err)
    }
    serviceURL := fmt.Sprintf("https://%s.table.core.windows.net/%s", accountName, tableName)
    client, err := aztables.NewClient(serviceURL, cred, nil)
    if err != nil {
        panic(err)
    }
    return client
}

func createTable(client *aztables.Client) {
    //TODO: Check access policy, Storage Blob Data Contributor role needed
    _, err := client.Create(context.TODO(), nil)
    if err != nil {
        panic(err)
    }
}

func addEntity(client *aztables.Client) {
    myEntity := InventoryEntity{
        Entity: aztables.Entity{
            PartitionKey: "pk001",
            RowKey:       "rk001",
        },
        Price:       3.99,
        Inventory:   20,
        ProductName: "Markers",
        OnSale:      false,
    }

    marshalled, err := json.Marshal(myEntity)
    if err != nil {
        panic(err)
    }

    _, err = client.AddEntity(context.TODO(), marshalled, nil) // TODO: Check access policy, need Storage Table Data Contributor role
    if err != nil {
        panic(err)
    }
}

func listEntities(client *aztables.Client) {
    listPager := client.List(nil)
    pageCount := 0
    for listPager.More() {
        response, err := listPager.NextPage(context.TODO())
        if err != nil {
            panic(err)
        }
        fmt.Printf("There are %d entities in page #%d\n", len(response.Entities), pageCount)
        pageCount += 1
    }
}

func queryEntity(client *aztables.Client) {
    filter := fmt.Sprintf("PartitionKey eq '%v' or RowKey eq '%v'", "pk001", "rk001")
    options := &aztables.ListEntitiesOptions{
        Filter: &filter,
        Select: to.StringPtr("RowKey,Price,Inventory,ProductName,OnSale"),
        Top:    to.Int32Ptr(15),
    }

    pager := client.List(options)
    for pager.More() {
        resp, err := pager.NextPage(context.Background())
        if err != nil {
            panic(err)
        }
        for _, entity := range resp.Entities {
            var myEntity PurchasedEntity 
            err = json.Unmarshal(entity, &myEntity)
            if err != nil {
                panic(err)
            }
            fmt.Println("Return custom type [PurchasedEntity]")
            fmt.Printf("Price: %v; ProductName: %v; OnSale: %v\n", myEntity.Price, myEntity.ProductName, myEntity.OnSale)
        }
    }
}

func deleteEntity(client *aztables.Client) {
    _, err := client.DeleteEntity(context.TODO(), "pk001", "rk001", nil)
    if err != nil {
        panic(err)
    }
}

func deleteTable(client *aztables.Client) {
    _, err := client.Delete(context.TODO(), nil)
    if err != nil {
        panic(err)
    }
}

func main() {

    fmt.Println("Authenticating...")
    client := getClient()

    fmt.Println("Creating a table...")
    createTable(client)

    fmt.Println("Adding an entity to the table...")
    addEntity(client)

    fmt.Println("Calculating all entities in the table...")
    listEntities(client)

    fmt.Println("Querying a specific entity...")
    queryEntity(client) 

    fmt.Println("Deleting an entity...")
    deleteEntity(client) 

    fmt.Println("Deleting a table...")
    deleteTable(client)
}

Important

인증한 계정에 Azure Storage 계정을 관리하는 데 필요한 적절한 액세스 정책이 있는지 확인합니다. 위의 코드를 실행하려면 계정에 최소한 스토리지 Blob 데이터 기여자 역할과 스토리지 테이블 데이터 기여자 역할이 있어야 합니다.

코드 예제

클라이언트 인증

// Lookup environment variables
accountName, ok := os.LookupEnv("AZURE_STORAGE_ACCOUNT")
if !ok {
  panic("AZURE_STORAGE_ACCOUNT environment variable not found")
}

tableName, ok := os.LookupEnv("AZURE_TABLE_NAME")
if !ok {
  panic("AZURE_TABLE_NAME environment variable not found")
}

// Create a credential
cred, err := azidentity.NewDefaultAzureCredential(nil)
if err != nil {
  panic(err)
}

// Create a table client
serviceURL := fmt.Sprintf("https://%s.table.core.windows.net/%s", accountName, tableName)
client, err := aztables.NewClient(serviceURL, cred, nil)
if err != nil {
  panic(err)
}

테이블 만들기

// Create a table and discard the response
_, err := client.Create(context.TODO(), nil)
if err != nil {
  panic(err)
}

엔터티 만들기

// Define the table entity as a custom type
type InventoryEntity struct {
    aztables.Entity
    Price       float32
    Inventory   int32
    ProductName string
    OnSale      bool
}

// Define the entity values
myEntity := InventoryEntity{
    Entity: aztables.Entity{
        PartitionKey: "pk001",
        RowKey:       "rk001",
    },
    Price:       3.99,
    Inventory:   20,
    ProductName: "Markers",
    OnSale:      false,
}

// Marshal the entity to JSON
marshalled, err := json.Marshal(myEntity)
if err != nil {
    panic(err)
}

// Add the entity to the table
_, err = client.AddEntity(context.TODO(), marshalled, nil) // needs Storage Table Data Contributor role
if err != nil {
    panic(err)
}

엔터티 가져오기

// Define the new custom type
type PurchasedEntity struct {
    aztables.Entity
    Price       float32
    ProductName string
    OnSale      bool
}

// Define the query filter and options
filter := fmt.Sprintf("PartitionKey eq '%v' or RowKey eq '%v'", "pk001", "rk001")
options := &aztables.ListEntitiesOptions{
    Filter: &filter,
    Select: to.StringPtr("RowKey,Price,Inventory,ProductName,OnSale"),
    Top:    to.Int32Ptr(15),
}

// Query the table for the entity
pager := client.List(options)
for pager.More() {
    resp, err := pager.NextPage(context.Background())
    if err != nil {
        panic(err)
    }
    for _, entity := range resp.Entities {
        var myEntity PurchasedEntity
        err = json.Unmarshal(entity, &myEntity)
        if err != nil {
            panic(err)
        }
        fmt.Println("Return custom type [PurchasedEntity]")
        fmt.Printf("Price: %v; ProductName: %v; OnSale: %v\n", myEntity.Price, myEntity.ProductName, myEntity.OnSale)
    }
}

엔터티 삭제

_, err := client.DeleteEntity(context.TODO(), "pk001", "rk001", nil)
if err != nil {
  panic(err)
}

테이블 삭제

_, err := client.Delete(context.TODO(), nil)
if err != nil {
  panic(err)
}

코드 실행

애플리케이션을 실행하는 일만 남았습니다. 하지만 그렇게 하기 전에 환경 변수를 설정해야 합니다. 다음 명령을 사용하여 두 환경 변수를 만들고 적절한 값으로 설정합니다.

export AZURE_STORAGE_ACCOUNT=<YourStorageAccountName> 
export AZURE_TABLE_NAME=<YourAzureTableName>

다음으로 다음 go run 명령을 실행하여 앱을 실행합니다.

go run main.go

리소스 정리

다음 명령을 실행하여 리소스 그룹 및 나머지 리소스를 모두 삭제합니다.

az group delete --resource-group myResourceGroup

다음 단계

이 빠른 시작에서, Azure Cosmos DB 계정을 만들고, 데이터 탐색기를 사용하여 테이블을 만들고, 앱을 실행하는 방법을 알아보았습니다. 이제 테이블용 API를 사용하여 데이터를 쿼리할 수 있습니다.