연습 - HttpClient를 통해 REST 서비스 사용

완료됨

여러분은 엔지니어가 고객 사이트를 방문할 때 사용하는 앱의 일부로 엔지니어가 전기 파트의 세부 정보를 조회할 수 있는 기능을 추가해야 합니다. 이 정보는 데이터베이스에 보관되고 REST 웹 서비스를 통해 액세스됩니다. 또한 관리자가 동일한 REST 웹 서비스를 사용하여 데이터베이스에 보관된 파트의 세부 정보를 작성, 제거, 수정할 수 있는 인터페이스를 제공해 달라는 요청을 받았습니다.

이 연습에서는 Azure에 REST 웹 서비스를 배포한 다음, 웹 브라우저를 사용하여 웹 서비스에 액세스할 수 있는지 확인합니다. 그런 다음, REST 웹 서비스를 사용하여 전기 부품의 세부 정보를 검색, 추가, 삭제, 업데이트하는 기능을 기존 앱에 추가합니다.

이 연습은 Azure Portal을 사용하여 수행합니다.

복사 단추를 사용하여 클립보드에 명령을 복사할 수 있습니다. 붙여넣으려면 Cloud Shell 터미널에서 새 줄을 마우스 오른쪽 단추로 클릭하고 붙여넣기를 선택하거나 Shift+Insert 바로 가기 키(macOS의 경우 ⌘+V)를 사용합니다.

Parts REST 웹 서비스 배포

  1. Cloud Shell 창에서 다음 명령을 실행하여 Parts REST 웹 서비스를 비롯한 이 연습의 코드가 포함된 리포지토리를 복제합니다.

    git clone https://github.com/microsoftdocs/mslearn-dotnetmaui-consume-rest-services
    
  2. Consume-REST-services 폴더로 이동

    cd mslearn-dotnetmaui-consume-rest-services/src
    
  3. 다음 명령을 실행하여 Azure Cloud Shell 샌드박스를 사용하여 Parts 웹 서비스를 배포합니다. 이 명령을 사용하면 고유한 URL을 통해 서비스를 사용할 수 있게 됩니다. 이 URL이 표시되면 기록해 두세요. 이 URL을 사용하여 웹 서비스에 연결하도록 앱을 구성할 것입니다.

    bash initenvironment.sh
    

웹 서비스에 대한 코드 검사

참고

로컬 개발 컴퓨터에서 이 연습의 나머지 부분을 수행합니다.

  1. 컴퓨터에서 명령 프롬프트 창을 열고 이 연습을 위한 리포지토리를 복제합니다. 코드는 net-maui-learn-consume-rest-services 리포지토리에 있습니다.

    git clone https://github.com/microsoftdocs/mslearn-dotnetmaui-consume-rest-services
    

    참고 항목

    빌드 생성 파일이 최대 경로 길이를 초과하지 않도록 연습 콘텐츠를 C:\dev와 같은 짧은 폴더 경로에 복제하거나 다운로드하는 것이 가장 좋습니다.

  2. 리포지토리 복제본의 src\webservice\PartsServer 폴더로 이동하고 Visual Studio 또는 Visual Studio Code의 폴더를 사용하여 PartsServer.sln 솔루션을 엽니다. 이 솔루션에는 이전 절차에서 Azure에 배포한 웹 서비스의 코드 복사본이 포함되어 있습니다.

  3. 솔루션 탐색기 창에서 Models 폴더를 확장합니다. 이 폴더에는 다음 두 개의 파일이 포함되어 있습니다.

    • Part.cs. Part 클래스는 부품을 REST 웹 서비스에서 제공한 것으로 나타냅니다. 필드에는 부품 ID, 부품 이름, 부품 유형, 사용 가용 날짜(부품이 처음 공급된 때) 및 공급업체 목록이 포함됩니다. Href 속성은 부품의 상대 URI를 반환합니다. REST 클라이언트는 이 URI를 사용하여 REST 웹 서비스에서 이 특정 부분을 참조할 수 있습니다. Suppliers 속성은 부품 공급업체 목록을 문자열로 반환합니다.

    • PartsFactory.cs. PartsFactory 클래스는 소량의 하드 코딩된 값 세트를 사용하여 서비스에서 제공하는 부품 목록을 초기화합니다. 현실에서는 이 데이터가 데이터베이스에서 검색됩니다.

  4. 솔루션 탐색기 창에서 Controllers 폴더를 확장합니다. 이 폴더에는 다음 파일이 포함되어 있습니다.

    • PartsController.cs. PartsController 클래스는 서비스의 웹 API를 구현합니다. 여기에는 클라이언트 애플리케이션이 모든 부품 목록을 검색(Get)하고, 부품 ID가 지정된 특정 부품의 세부 정보를 찾고(오버로드된 Get 버전), 부품 세부 정보를 업데이트(Put)하고, 목록에 새 파트를 추가(Post)하고, 목록에서 파트를 제거(Delete)할 수 있도록 하는 메서드가 포함됩니다.

    • LoginController.cs. LoginController 클래스는 웹 서비스에 대한 간단한 형태의 인증을 구현합니다. 앱은 권한 부여 토큰을 반환하는 HTTP GET 요청을 이 컨트롤러에 보내야 합니다. 이 권한 부여 토큰은 PartsController로 전송된 요청을 인증하는 데 사용됩니다.

    • BaseController.cs. BaseController 클래스에는 요청을 인증하는 데 사용되는 논리가 포함되어 있습니다. PartsController 클래스는 이 클래스에서 상속합니다. 클라이언트가 유효한 인증 토큰을 제공하지 않고 PartsController 클래스에서 메서드를 호출하려고 시도하면 이 메서드는 HTTP 401(권한 없음) 응답을 반환합니다.

.NET MAUI 클라이언트 앱의 코드 검사

이 모듈에서는 .NET 8.0 SDK를 사용합니다. 기본 설정 터미널에서 다음 명령을 실행하여 .NET 8.0이 설치되어 있는지 확인합니다.

dotnet --list-sdks

다음 예제와 유사한 출력이 표시됩니다.

6.0.317 [C:\Program Files\dotnet\sdk]
7.0.401 [C:\Program Files\dotnet\sdk]
8.0.100 [C:\Program Files\dotnet\sdk]

8으로 시작하는 버전이 나열되어 있는지 확인합니다. 나열되는 버전이 없거나 명령을 찾을 수 없는 경우 최신 .NET 8.0 SDK를 설치합니다.

  1. PartsServer 솔루션을 닫고 복제된 리포지토리의 src\client\PartsClient 폴더에서 PartsClient 솔루션을 엽니다. 이 솔루션에는 PartsServer 웹 서비스를 사용하는 .NET MAUI 클라이언트 앱의 일부 구현이 포함되어 있습니다.

  2. 솔루션 탐색기 창에서 Data 폴더를 확장합니다. 이 폴더에는 다음 두 클래스의 코드가 포함되어 있습니다.

    • PartsManager.cs. PartsManager 클래스는 클라이언트 앱이 REST 웹 서비스와 상호 작용하는 데 사용되는 메서드를 제공합니다. 이 클래스는 아직 미완성입니다. 연습 중에 필요한 코드를 추가해야 합니다. 완성되면 GetClient 메서드가 REST 웹 서비스에 연결합니다. GetAll 메서드는 REST 웹 서비스의 부품 목록을 반환합니다. Add 메서드는 REST 웹 서비스에서 관리하는 부품 목록에 새 부품을 추가합니다. Update 메서드는 REST 웹 서비스에서 저장한 파트의 세부 정보를 수정했고, Delete 메서드는 파트를 제거합니다.

    • Part.cs. Part 클래스는 데이터베이스에 저장된 부품을 모델링합니다. 애플리케이션이 PartID, PartName, PartAvailableDate, PartTypePartSuppliers 필드에 액세스하는 데 사용할 수 있는 속성을 노출합니다. 또한 이 클래스는 애플리케이션이 공급자 이름을 포함하고 있는 서식 있는 문자열을 검색하는 데 사용할 수 있는 SupplierString이라는 유틸리티 메서드를 제공합니다.

  3. 솔루션 탐색기 창에서 Pages 폴더를 확장합니다. 이 폴더에는 다음 두 페이지에 대한 태그와 코드가 포함되어 있습니다.

    • PartsPage.xaml. 이 페이지는 DataTemplate이 있는 CollectionView 레이아웃을 사용하여 가용 부품의 세부 정보를 목록으로 표시합니다. DataTemplate은 데이터 바인딩을 사용하여 표시된 데이터를 웹 서비스에서 검색된 부품에 연결합니다. CollectionView에서 행을 선택하여 AddPartPage에서 파트를 편집하거나, 새 파트 추가 단추를 선택하여 새 파트를 추가할 수 있습니다.

    • AddPartPage.xaml. 이 페이지에서는 사용자가 새 파트에 대한 세부 정보를 입력하고 저장할 수 있습니다. 사용자는 파트 이름, 파트 형식 및 초기 공급자를 지정할 수 있습니다. 부품 ID 및 부품 사용 가능 날짜가 자동으로 생성됩니다.

  4. 솔루션 탐색기 창에서 ViewModels 폴더를 확장합니다. 이 폴더에는 다음 두 가지 클래스가 포함되어 있습니다. AddPartViewModel.csPartsViewModel.cs. 이는 해당 페이지에 대한 뷰 모델이며 페이지가 데이터를 표시하고 조작하는 데 필요한 속성과 논리를 포함합니다.

서비스에 로그인

REST 서비스를 사용하려면 먼저 로그인하여 권한 부여 토큰을 가져와야 합니다. 사용자 인증이 없습니다. 먼저 특정 엔드포인트를 호출하여 권한 부여 토큰을 가져오는 다음 HTTP 헤더의 각 후속 요청에서 토큰을 서버로 다시 보냅니다.

  1. Data 폴더에서 PartsManager.cs 파일을 엽니다.

  2. 다음 코드 조각에 정의된 대로 BaseAddressUrl 정적 필드를 PartsManager 클래스에 추가합니다. URL GOES HERE 텍스트를 앞에서 적어 둔 REST 웹 서비스의 URL로 바꿉니다.

    public class PartsManager
    {
        static readonly string BaseAddress = "URL GOES HERE";
        static readonly string Url = $"{BaseAddress}/api/";
        ...
    }
    
  3. Url 필드 뒤에서 다음 필드를 클래스에 추가합니다. 이 필드는 다음과 같이 사용자가 로그인할 때 반환되는 권한 부여 토큰을 보관합니다.

    private static string authorizationKey;
    
  4. GetClient 메서드를 찾습니다. 이 메서드는 현재 NotImplementedException 예외를 throw합니다. 이 메서드의 기존 코드를 다음 코드로 바꿉니다. 이 코드는 HttpClient 개체를 만든 다음, REST 웹 서비스의 로그인 엔드포인트에 요청을 보냅니다. 서비스는 권한 부여 토큰이 포함된 메시지로 응답할 것입니다. 이 토큰을 역직렬화하고, HttpClient 개체를 사용하여 보낸 후속 요청의 기본 권한 부여 요청 헤더로 추가합니다.

    private static async Task<HttpClient> GetClient()
    {
        if (client != null)
            return client;
    
        client = new HttpClient();
    
        if (string.IsNullOrEmpty(authorizationKey))
        {                
            authorizationKey = await client.GetStringAsync($"{Url}login");
            authorizationKey = JsonSerializer.Deserialize<string>(authorizationKey);
        }
    
        client.DefaultRequestHeaders.Add("Authorization", authorizationKey);
        client.DefaultRequestHeaders.Add("Accept", "application/json");
    
        return client;
    }
    

GET 작업을 수행하여 부품 정보 검색

  1. PartsManager.cs 파일에서 GetAll 메서드를 찾습니다. 이 메서드는 열거 가능한 부품 목록을 반환하는 비동기 메서드입니다. 이 메서드는 아직 구현되지 않았습니다.

  2. 이 메서드에서 NotImplementedException 예외를 throw하는 코드를 삭제합니다.

  3. Connectivity 클래스를 사용하여 디바이스가 인터넷에 연결되어 있는지 확인합니다. 인터넷이 없으면 빈 List<Part>를 반환합니다.

    if (Connectivity.Current.NetworkAccess != NetworkAccess.Internet)
        return new List<Part>();
    
  4. GetClient 메서드를 호출하여 작업할 HttpClient 개체를 검색합니다. GetClient는 비동기 메서드이므로, await 연산자를 사용하여 이 메서드에서 반환된 개체를 캡처합니다.

  5. HttpClient 개체의 GetStringAsync 메서드를 호출하고, REST 웹 서비스에서 부품 배열을 검색하는 기본 URL을 제공합니다. 데이터는 JSON 문자열로 비동기적으로 반환됩니다.

  6. JsonSerializer.Deserialize 메서드를 사용하여 이 메서드에서 반환된 JSON 문자열을 Part 개체 목록으로 역직렬화합니다. 이 목록을 호출자에게 반환합니다.

    완료된 메서드는 다음과 같습니다.

    public static async Task<IEnumerable<Part>> GetAll()
    {
        if (Connectivity.Current.NetworkAccess != NetworkAccess.Internet)
            return new List<Part>();
    
        var client = await GetClient();
        string result = await client.GetStringAsync($"{Url}parts");
    
        return JsonSerializer.Deserialize<List<Part>>(result, new JsonSerializerOptions
            {
                PropertyNameCaseInsensitive = true,
            });                     
    }
    
  7. 앱을 빌드하고 실행합니다. 앱이 시작되면 부품 목록 페이지가 표시되고 GetAll 메서드에서 검색한 파트 목록이 표시됩니다. 다음 이미지는 Android에서 실행되는 앱을 보여줍니다.

    A screenshot of the Parts Client app running on Android showing a list of parts.

  8. 데이터 탐색을 마쳤으면 앱을 닫고 Visual Studio 또는 Visual Studio Code로 돌아갑니다.

POST 작업을 수행하여 데이터베이스에 새 부품 추가

  1. PartsManager 클래스에서 Add 메서드를 찾습니다. 이 메서드는 부품 이름, 공급자 및 부품 유형에 대한 매개 변수를 갖고 있습니다. 이 메서드는 비동기 메서드입니다. 메서드의 목적은 새 파트를 데이터베이스에 삽입하고 새로 만들어진 항목을 나타내는 Part 개체를 반환하는 것입니다.

  2. 이 메서드에서 기존 코드를 삭제합니다.

  3. Connectivity 클래스를 사용하여 디바이스가 인터넷에 연결되어 있는지 확인합니다. 인터넷이 없으면 빈 Part를 반환합니다.

    if (Connectivity.Current.NetworkAccess != NetworkAccess.Internet)
        return new Part();
    
  4. Part 개체를 만듭니다. 다음과 같이 전달된 데이터로 필드를 채웁니다.

    • PartID 필드를 빈 문자열로 설정합니다. 이 ID는 REST 웹 서비스에서 자동으로 생성합니다.
    • 공급업체 이름을 저장할 새 List를 만듭니다.
    • PartAvailableDate 필드를 DateTime.Now로 설정합니다.
    • GetClient 메서드에서 HTTP 클라이언트를 가져옵니다.
    var part = new Part()
    {
        PartName = partName,
        Suppliers = new List<string>(new[] { supplier }),
        PartID = string.Empty,
        PartType = partType,
        PartAvailableDate = DateTime.Now.Date
    };
    
  5. GetClient 메서드를 호출하여 작업할 HttpClient 개체를 검색합니다.

  6. HttpRequestMessage 개체 만들기 이 개체는 웹 서비스로 전송되는 요청을 모델링하는 데 사용됩니다. 사용할 HTTP 동사와 통신할 웹 서비스의 URL을 나타내는 매개 변수를 사용하여 이 개체를 시작합니다.

    var msg = new HttpRequestMessage(HttpMethod.Post, $"{Url}parts");
    
  7. 만들려는 Part 정보를 사용하여 웹 서비스에 페이로드를 보내야 합니다. 이 페이로드는 JSON으로 직렬화됩니다. JSON 페이로드가 HttpRequestMessage.Content 속성에 추가되고 JsonContent.Create 메서드를 통해 직렬화됩니다.

    msg.Content = JsonContent.Create<Part>(part);
    
  8. 이제 HttpClient.SendAsync 함수를 사용하여 웹 서비스에 메시지를 보냅니다. 이 함수는 서버에서 수행된 작업 정보를 보관하는 HttpResponseMessage 개체를 반환합니다. 서버에서 다시 전달되는 HTTP 응답 코드 및 정보를 예로 들 수 있습니다.

    var response = await client.SendAsync(msg);
    response.EnsureSuccessStatusCode();
    

    앞의 내용은 response.EnsureSuccessStatusCode 메서드를 사용합니다. 2xx HTTP 상태 코드 이외의 항목이 반환되면 오류가 throw됩니다.

  9. 웹 서비스가 JSON으로 직렬화된 개체와 같은 정보를 반환하는 경우 HttpResponseMessage에서 정보를 읽을 수 있습니다. 그런 다음, JsonSerializer.Deserialize를 사용하여 JSON을 역직렬화할 수 있습니다.

    var returnedJson = await response.Content.ReadAsStringAsync();
    
    var insertedPart = JsonSerializer.Deserialize<Part>(returnedJson, new JsonSerializerOptions
            {
                PropertyNameCaseInsensitive = true,
            });
    
  10. 마지막으로, 삽입된 새 Part를 반환합니다.

    return insertedPart;
    
  11. 앱을 빌드하고 실행합니다. 새 부품 추가 단추를 선택하고 이름, 유형 및 공급업체를 입력하여 새 부품을 만듭니다. 저장을 선택합니다. PartsManager 클래스의 Add 메서드가 호출되고 웹 서비스에 새 부품이 만들어집니다. 작업이 성공하면 부품 목록 페이지가 목록 맨 아래에 새 부품과 함께 다시 나타납니다.

    A screenshot of the app running after a new part has been added. The new part is at the bottom of the list.

  12. 데이터 탐색을 마쳤으면 앱을 닫고 Visual Studio 또는 Visual Studio Code로 돌아갑니다.

PUT 작업을 수행하여 데이터베이스의 부품 세부 정보를 업데이트합니다.

  1. PartsManager 클래스에서 Update 메서드를 찾습니다. 이 메서드는 Part 개체를 매개 변수로 사용하는 비동기 메서드입니다. 이 메서드는 명시적 반환 값이 없습니다. 그러나 예외가 호출자에게 올바르게 반환되도록 반환 형식은 Task입니다. PUT 기능을 구현해 보겠습니다.

  2. 기존 코드를 삭제합니다.

  3. 이전과 마찬가지로 인터넷 연결을 확인합니다.

    if (Connectivity.Current.NetworkAccess != NetworkAccess.Internet)
        return;
    
  4. HttpRequestMessage를 만듭니다. 이번에는 PUT 작업 및 부품 업데이트 URL을 지정합니다.

    HttpRequestMessage msg = new(HttpMethod.Put, $"{Url}parts/{part.PartID}");
    
  5. JsonContent.Create 함수 및 이 함수에 전달된 part 매개 변수를 사용하여 HttpRequestMessageContent 속성을 설정합니다.

    msg.Content = JsonContent.Create<Part>(part);
    
  6. GetClient 메서드에서 HTTP 클라이언트를 가져옵니다.

    var client = await GetClient();
    
  7. HttpClient를 사용하여 요청을 보낸 다음, 요청이 성공했는지 확인합니다.

    var response = await client.SendAsync(msg);
    response.EnsureSuccessStatusCode();
    
  8. 앱을 빌드하고 실행합니다. 목록에서 파트 중 하나를 선택합니다. AddPart 페이지가 나타납니다. 이번에는 속성이 미리 채워집니다. 원하는 대로 업데이트합니다.

  9. 저장을 선택합니다. 그러면 PartsManager 클래스에서 Update 메서드가 호출되고 변경 내용이 웹 서비스로 전송됩니다. 작업이 성공하면 부품 목록 페이지가 다시 나타나고 변경 내용이 반영됩니다.

    A screenshot of the app running with the first item in the list updated.

    참고 항목

    이전 작업에서 추가한 부품은 부품 목록 페이지에 표시되지 않습니다. 앱이 사용하는 데이터는 앱이 실행될 때마다 미리 정의된 파트 목록으로 다시 설정됩니다. 앱 테스트의 일관성을 보장하기 위한 조치입니다.

DELETE 작업을 수행하여 데이터베이스에서 부품 세부 정보를 제거합니다.

  1. PartsManager 클래스에서 Delete 메서드를 찾습니다. 이 메서드는 partId 문자열을 사용하고 Task를 반환하는 비동기 메서드입니다.

  2. 기존 코드를 삭제합니다.

  3. 인터넷 연결을 확인합니다.

    if (Connectivity.Current.NetworkAccess != NetworkAccess.Internet)
        return;
    
  4. HttpRequestMessage 개체를 만듭니다. 이번에 한해 DELETE HTTP 동사와 부품을 삭제할 URL을 지정합니다.

    HttpRequestMessage msg = new(HttpMethod.Delete, $"{Url}parts/{partID}");
    
  5. GetClient 메서드에서 HTTP 클라이언트를 가져옵니다.

    var client = await GetClient();
    
  6. 웹 서비스에 요청을 보냅니다. 요청이 반환되면 성공 여부를 확인합니다.

    var response = await client.SendAsync(msg);
    response.EnsureSuccessStatusCode();
    
  7. 앱을 빌드하고 실행합니다. 목록에서 파트를 선택한 다음 파트 추가 페이지에서 삭제를 선택합니다. 작업이 성공하면 부품 목록 페이지가 다시 나타나고 삭제한 부품이 더 이상 표시되지 않습니다.