REST 클라이언트REST client

이 자습서에서는 .NET Core 및 C# 언어의 다양한 기능에 대해 설명합니다.This tutorial teaches you a number of features in .NET Core and the C# language. 다음에 대해 알아봅니다.You'll learn:

  • .NET Core CLI의 기본 사항The basics of the .NET Core CLI.
  • C# 언어 기능의 개요An overview of C# Language features.
  • NuGet으로 종속성 관리Managing dependencies with NuGet
  • HTTP 통신HTTP Communications
  • JSON 정보 처리Processing JSON information
  • 특성을 사용하여 구성 관리Managing configuration with Attributes.

GitHub에서 REST 서비스에 HTTP 요청을 실행하는 애플리케이션을 빌드합니다.You'll build an application that issues HTTP Requests to a REST service on GitHub. JSON 형식의 정보를 읽은 후 해당 JSON 패킷을 C# 개체로 변환합니다.You'll read information in JSON format, and convert that JSON packet into C# objects. 마지막으로 C# 개체를 사용하는 방법을 배웁니다.Finally, you'll see how to work with C# objects.

이 자습서에는 많은 기능이 있습니다.There are many features in this tutorial. 하나씩 빌드해 보겠습니다.Let's build them one by one.

이 문서의 최종 샘플을 따르려는 경우 해당 샘플을 다운로드할 수 있습니다.If you prefer to follow along with the final sample for this article, you can download it. 다운로드 지침은 샘플 및 자습서를 참조하세요.For download instructions, see Samples and Tutorials.

사전 요구 사항Prerequisites

.NET Core를 실행하려면 머신을 설정해야 합니다.You'll need to set up your machine to run .NET core. .NET Core 다운로드 페이지에서 설치 지침을 찾을 수 있습니다.You can find the installation instructions on the .NET Core Downloads page. Windows, Linux, macOS에서나 Docker 컨테이너에서 이 애플리케이션을 실행할 수 있습니다.You can run this application on Windows, Linux, or macOS, or in a Docker container. 선호하는 코드 편집기를 설치해야 합니다.You'll need to install your favorite code editor. 아래 설명에서는 오픈 소스 플랫폼 간 편집기인 Visual Studio Code를 사용합니다.The descriptions below use Visual Studio Code, which is an open source, cross platform editor. 그러나 익숙한 어떤 도구도 사용 가능합니다.However, you can use whatever tools you are comfortable with.

애플리케이션 만들기Create the Application

첫 번째 단계에서는 새 애플리케이션을 만듭니다.The first step is to create a new application. 명령 프롬프트를 열고 애플리케이션에 대한 새 디렉터리를 만듭니다.Open a command prompt and create a new directory for your application. 해당 디렉터리를 현재 디렉터리로 지정합니다.Make that the current directory. 콘솔 창에 다음 명령을 입력합니다.Enter the following command in a console window:

dotnet new console --name WebAPIClient

이렇게 하면 기본 "Hello World" 애플리케이션에 대한 시작 파일이 만들어집니다.This creates the starter files for a basic "Hello World" application. 프로젝트 이름은 "WebAPIClient"입니다.The project name is "WebAPIClient". 새 프로젝트이므로 어떤 종속성도 없습니다.As this is a new project, none of the dependencies are in place. 첫 번째 실행에서는 .NET Core 프레임워크를 다운로드하고, 개발 인증서를 설치하며, NuGet 패키지 관리자를 실행하여 누락된 종속성을 복원합니다.The first run will download the .NET Core framework, install a development certificate, and run the NuGet package manager to restore missing dependencies.

수정하기 전에 “WebAPIClient” 디렉터리에 cd하고 명령 프롬프트에서 dotnet run(참고 참조)를 입력하여 애플리케이션을 실행합니다.Before you start making modifications, cd into the "WebAPIClient" directory and type dotnet run (see note) at the command prompt to run your application. 환경에 누락된 종속성이 있으면 dotnet run이 자동으로 dotnet restore를 수행합니다.dotnet run automatically performs dotnet restore if your environment is missing dependencies. 애플리케이션을 다시 빌드해야 하면 dotnet build도 수행합니다.It also performs dotnet build if your application needs to be rebuilt. 최초 설치 후에는 프로젝트에 해당할 때만 dotnet restore 또는 dotnet build를 실행하면 됩니다.After your initial setup, you will only need to run dotnet restore or dotnet build when it makes sense for your project.

새 종속성 추가Adding New Dependencies

.NET Core의 주요 디자인 목표 중 하나는 .NET 설치의 크기를 최소화하는 것입니다.One of the key design goals for .NET Core is to minimize the size of the .NET installation. 애플리케이션에 일부 기능을 위해 추가 라이브러리가 필요한 경우 C# 프로젝트(*.csproj) 파일에 해당 종속성을 추가합니다.If an application needs additional libraries for some of its features, you add those dependencies into your C# project (*.csproj) file. 현재 예제에서는 애플리케이션이 JSON 응답을 처리할 수 있도록 System.Runtime.Serialization.Json 패키지를 추가해야 합니다.For our example, you'll need to add the System.Runtime.Serialization.Json package, so your application can process JSON responses.

이 애플리케이션에는 System.Runtime.Serialization.Json 패키지가 필요합니다.You'll need the System.Runtime.Serialization.Json package for this application. 다음 .NET CLI 명령을 실행하여 패키지를 프로젝트에 추가합니다.Add it to your project by running the following .NET CLI command:

dotnet add package System.Text.Json

웹 요청 수행Making Web Requests

이제 웹에서 데이터 검색을 시작할 준비가 되었습니다.Now you're ready to start retrieving data from the web. 이 애플리케이션에서는 GitHub API에서 정보를 읽게 됩니다.In this application, you'll read information from the GitHub API. .NET Foundation 상위 항목 아래에서 프로젝트에 대한 정보를 읽어 보겠습니다.Let's read information about the projects under the .NET Foundation umbrella. 먼저 GitHub API에 대해 요청을 수행하여 프로젝트에 대한 정보를 검색합니다.You'll start by making the request to the GitHub API to retrieve information on the projects. 사용할 엔드포인트는 https://api.github.com/orgs/dotnet/repos입니다.The endpoint you'll use is: https://api.github.com/orgs/dotnet/repos. 이러한 프로젝트에 대해 모든 정보를 검색하려고 하므로 HTTP GET 요청을 사용합니다.You want to retrieve all the information about these projects, so you'll use an HTTP GET request. 브라우저도 HTTP GET 요청을 사용하므로 해당 URL을 브라우저에 붙여 넣어 수신되고 처리 중인 정보를 볼 수 있습니다.Your browser also uses HTTP GET requests, so you can paste that URL into your browser to see what information you'll be receiving and processing.

HttpClient 클래스를 사용하여 웹 요청을 수행합니다.You use the HttpClient class to make web requests. 모든 최신 .NET API와 마찬가지로 HttpClient 는 장기 실행되는 API에 대해 비동기 메서드만 지원합니다.Like all modern .NET APIs, HttpClient supports only async methods for its long-running APIs. 먼저 비동기 메서드를 만들어 보겠습니다.Start by making an async method. 애플리케이션의 기능을 빌드할 때 구현을 채웁니다.You'll fill in the implementation as you build the functionality of the application. 먼저 프로젝트 디렉터리에서 program.cs 파일을 열고 Program 클래스에 다음 메서드를 추가합니다.Start by opening the program.cs file in your project directory and adding the following method to the Program class:

private static async Task ProcessRepositories()
{
}

C# 컴파일러에서 Task 형식을 인식하도록 using 지시문을 Main 메서드 맨 위에 추가해야 합니다.You'll need to add a using directive at the top of your Main method so that the C# compiler recognizes the Task type:

using System.Threading.Tasks;

이때 프로젝트를 빌드하면 이 메서드에는 await 연산자가 없어서 동기적으로 실행되므로 경고가 생성됩니다.If you build your project at this point, you'll get a warning generated for this method, because it does not contain any await operators and will run synchronously. 지금은 이 경고를 무시하세요. 메서드를 입력할 때 await 연산자를 추가할 것입니다.Ignore that for now; you'll add await operators as you fill in the method.

다음으로 Main 메서드를 업데이트하여 ProcessRepositories 메서드를 호출합니다.Next, update the Main method to call the ProcessRepositories method. ProcessRepositories 메서드는 작업을 반환합니다. 이 작업이 완료되기 전에 프로그램을 종료하지 않아야 합니다.The ProcessRepositories method returns a task, and you shouldn't exit the program before that task finishes. 따라서 Main의 서명을 변경해야 합니다.Therefore, you must change the signature of Main. async 한정자를 추가하고 반환 형식을 Task로 변경합니다.Add the async modifier, and change the return type to Task. 그런 다음 메서드의 본문에서 ProcessRepositories에 대한 호출을 추가합니다.Then, in the body of the method, add a call to ProcessRepositories. 해당 메서드 호출에 await 키워드를 추가합니다.Add the await keyword to that method call:

static async Task Main(string[] args)
{
    await ProcessRepositories();
}

이제 아무 작업도 수행하지 않지만 비동기적으로는 수행하는 프로그램이 되었습니다.Now, you have a program that does nothing, but does it asynchronously. 이것을 개선해보겠습니다.Let's improve it.

먼저 웹에서 데이터를 검색할 수 있는 개체가 필요합니다. 이를 위해 HttpClient를 사용할 수 있습니다.First you need an object that is capable to retrieve data from the web; you can use a HttpClient to do that. 이 개체는 요청 및 응답을 처리합니다.This object handles the request and the responses. Program.cs 파일 내의 Program 클래스에서 해당 형식의 단일 인스턴스를 인스턴스화합니다.Instantiate a single instance of that type in the Program class inside the Program.cs file.

namespace WebAPIClient
{
    class Program
    {
        private static readonly HttpClient client = new HttpClient();

        static async Task Main(string[] args)
        {
            //...
        }
    }
}

다시 ProcessRepositories 메서드로 돌아가 첫 번째 버전을 채워 보겠습니다.Let's go back to the ProcessRepositories method and fill in a first version of it:

private static async Task ProcessRepositories()
{
    client.DefaultRequestHeaders.Accept.Clear();
    client.DefaultRequestHeaders.Accept.Add(
        new MediaTypeWithQualityHeaderValue("application/vnd.github.v3+json"));
    client.DefaultRequestHeaders.Add("User-Agent", ".NET Foundation Repository Reporter");

    var stringTask = client.GetStringAsync("https://api.github.com/orgs/dotnet/repos");

    var msg = await stringTask;
    Console.Write(msg);
}

컴파일을 위해 파일 맨 위에 2개의 새 using 지시문도 추가해야 합니다.You'll need to also add two new using directives at the top of the file for this to compile:

using System.Net.Http;
using System.Net.Http.Headers;

이 첫 번째 버전은 dotnet foundation 조직의 모든 리포지토리 목록을 읽으라는 웹 요청을 수행합니다.This first version makes a web request to read the list of all repositories under the dotnet foundation organization. (.NET Foundation의 GitHub ID는 dotnet임) 첫 몇 줄은 이 요청에 대해 HttpClient를 설정합니다.(The GitHub ID for the .NET Foundation is dotnet.) The first few lines set up the HttpClient for this request. 먼저 GitHub JSON 응답을 수락하도록 구성됩니다.First, it is configured to accept the GitHub JSON responses. 이 형식은 단순히 JSON입니다.This format is simply JSON. 다음 줄은 이 개체의 모든 요청에 사용자 에이전트 헤더를 추가합니다.The next line adds a User Agent header to all requests from this object. 이러한 두 가지 헤더는 GitHub 서버 코드에서 확인되며 GitHub에서 정보를 검색하는 데 필요합니다.These two headers are checked by the GitHub server code, and are necessary to retrieve information from GitHub.

HttpClient 를 구성한 후에는 웹 요청을 수행하고 응답을 검색합니다.After you've configured the HttpClient, you make a web request and retrieve the response. 이 첫 번째 버전에서는 HttpClient.GetStringAsync(String) 편의 메서드를 사용합니다.In this first version, you use the HttpClient.GetStringAsync(String) convenience method. 이 편의 메서드는 웹 요청을 수행하는 작업을 시작한 다음, 요청이 반환될 때 응답 스트림을 읽고 해당 스트림에서 콘텐츠를 추출합니다.This convenience method starts a task that makes the web request, and then when the request returns, it reads the response stream and extracts the content from the stream. 응답의 본문은 String 으로 반환됩니다.The body of the response is returned as a String. 작업이 완료되면 이 문자열을 사용할 수 있습니다.The string is available when the task completes.

이 메서드의 마지막 두 줄은 해당 작업을 대기하고 콘솔에 응답을 출력합니다.The final two lines of this method await that task, and then print the response to the console. 앱을 빌드한 다음 실행합니다.Build the app, and run it. ProcessRepositories에는 await 연산자가 포함되므로 이제 빌드 경고는 사라집니다.The build warning is gone now, because the ProcessRepositories now does contain an await operator. 길게 표시되는 JSON 형식 텍스트를 볼 수 있습니다.You'll see a long display of JSON formatted text.

JSON 결과 처리Processing the JSON Result

지금까지 웹 서버에서 응답을 검색하고 해당 응답에 포함된 텍스트를 표시하는 코드를 작성했습니다.At this point, you've written the code to retrieve a response from a web server, and display the text that is contained in that response. 다음에는 JSON 응답을 C# 개체로 변환해 보겠습니다.Next, let's convert that JSON response into C# objects.

System.Text.Json.JsonSerializer 클래스는 개체를 JSON으로 직렬화하고 JSON을 개체로 역직렬화합니다.The System.Text.Json.JsonSerializer class serializes objects to JSON and deserializes JSON into objects. 먼저 GitHub API에서 반환된 repo JSON 개체를 나타내도록 클래스를 정의합니다.Start by defining a class to represent the repo JSON object returned from the GitHub API:

using System;

namespace WebAPIClient
{
    public class Repository
    {
        public string name { get; set; }
    }
}

'repo.cs'라는 새 파일에 위 코드를 넣습니다.Put the above code in a new file called 'repo.cs'. 이 버전의 클래스는 JSON 데이터를 처리하는 가장 간단한 경로를 나타냅니다.This version of the class represents the simplest path to process JSON data. 클래스 이름 및 멤버 이름은 다음 C# 규칙 대신 JSON 패킷에 사용된 이름과 일치합니다.The class name and the member name match the names used in the JSON packet, instead of following C# conventions. 나중에 몇 가지 구성 특성을 제공하여 수정할 것입니다.You'll fix that by providing some configuration attributes later. 이 클래스에서는 JSON serialization 및 deserialization의 또 다른 중요한 특징을 보여 줍니다. 즉, JSON 패킷의 모든 필드가 이 클래스에 속하는 것은 아닙니다.This class demonstrates another important feature of JSON serialization and deserialization: Not all the fields in the JSON packet are part of this class. JSON serializer는 사용되는 클래스 형식에 포함되지 않은 정보를 무시합니다.The JSON serializer will ignore information that is not included in the class type being used. 이 특징 때문에 JSON 패킷의 필드 일부에만 작용하는 형식을 보다 쉽게 만들 수 있습니다.This feature makes it easier to create types that work with only a subset of the fields in the JSON packet.

이제 형식을 만들었으므로 역직렬화를 수행해 보겠습니다.Now that you've created the type, let's deserialize it.

다음으로, serializer를 사용하여 JSON을 C# 개체로 변환합니다.Next, you'll use the serializer to convert JSON into C# objects. ProcessRepositories 메서드의 GetStringAsync(String) 호출을 다음 줄로 바꿉니다.Replace the call to GetStringAsync(String) in your ProcessRepositories method with the following lines:

var streamTask = client.GetStreamAsync("https://api.github.com/orgs/dotnet/repos");
var repositories = await JsonSerializer.DeserializeAsync<List<Repository>>(await streamTask);

새 네임 스페이스를 사용하고 있으므로 파일의 맨 위에도 추가해야 합니다.You're using new namespaces, so you'll need to add it at the top of the file as well:

using System.Collections.Generic;
using System.Text.Json;

현재 GetStringAsync(String) 대신 GetStreamAsync(String)를 사용하고 있습니다.Notice that you're now using GetStreamAsync(String) instead of GetStringAsync(String). serializer는 해당 소스로 문자열 대신 스트림을 사용합니다.The serializer uses a stream instead of a string as its source. 앞의 코드 조각 두 번째 줄에서 사용되는 C# 언어의 몇 가지 기능에 대해 설명해 보겠습니다.Let's explain a couple features of the C# language that are being used in the second line of the preceding code snippet. JsonSerializer.DeserializeAsync<TValue>(Stream, JsonSerializerOptions, CancellationToken)에 대한 첫 번째 인수는 await 식입니다.The first argument to JsonSerializer.DeserializeAsync<TValue>(Stream, JsonSerializerOptions, CancellationToken) is an await expression. (다른 두 매개 변수는 선택 사항이며 코드 조각에서 생략됩니다.) 지금까지는 대입문의 일부로만 볼 수 있었지만 Await 식은 코드의 거의 모든 위치에 나올 수 있습니다.(The other two parameters are optional and are omitted in the code snippet.) Await expressions can appear almost anywhere in your code, even though up to now, you've only seen them as part of an assignment statement. Deserialize 메서드는 제네릭 입니다. 즉, JSON 텍스트에서 만들어야 하는 개체 종류에 대한 형식 인수를 제공해야 합니다.The Deserialize method is generic, which means you must supply type arguments for what kind of objects should be created from the JSON text. 이 예제에서는 다른 제네릭 개체 System.Collections.Generic.List<T>List<Repository>로 역직렬화합니다.In this example, you're deserializing to a List<Repository>, which is another generic object, the System.Collections.Generic.List<T>. List<> 클래스는 개체의 컬렉션을 저장합니다.The List<> class stores a collection of objects. 형식 인수는 List<>에 저장된 개체의 형식을 선언합니다.The type argument declares the type of objects stored in the List<>. JSON 텍스트는 리포지토리 개체의 컬렉션을 나타내므로 형식 인수는 Repository입니다.The JSON text represents a collection of repo objects, so the type argument is Repository.

이 섹션이 거의 완료되었습니다.You're almost done with this section. JSON을 C# 개체로 변환했으므로 각 리포지토리의 이름을 표시해 보겠습니다.Now that you've converted the JSON to C# objects, let's display the name of each repository. 다음 줄을Replace the lines that read:

var msg = await stringTask;   //**Deleted this
Console.Write(msg);

다음으로 바꿉니다.with the following:

foreach (var repo in repositories)
    Console.WriteLine(repo.name);

애플리케이션을 컴파일하고 실행합니다.Compile and run the application. .NET Foundation에 포함된 리포지토리의 이름이 출력됩니다.It will print the names of the repositories that are part of the .NET Foundation.

serialization 제어Controlling Serialization

더 많은 기능을 추가하기 전에 [JsonPropertyName] 특성을 사용하여 name 속성을 다뤄 보겠습니다.Before you add more features, let's address the name property by using the [JsonPropertyName] attribute. repo.cs에서 name 필드의 선언을 다음과 같이 변경합니다.Make the following changes to the declaration of the name field in repo.cs:

[JsonPropertyName("name")]
public string Name { get; set; }

[JsonPropertyName] 특성을 사용하려면 using 지시문에 System.Text.Json.Serialization 네임스페이스를 추가해야 합니다.To use [JsonPropertyName] attribute, you will need to add the System.Text.Json.Serialization namespace to the using directives:

using System.Text.Json.Serialization;

이렇게 변경할 경우 program.cs에서 각 리포지토리의 이름을 쓰는 코드를 변경해야 합니다.This change means you need to change the code that writes the name of each repository in program.cs:

Console.WriteLine(repo.Name);

dotnet run을 실행하여 매핑이 올바르게 수행되었는지 확인합니다.Execute dotnet run to make sure you've got the mappings correct. 이전과 동일한 출력이 표시되어야 합니다.You should see the same output as before.

새로운 기능을 추가하기 전에 한 가지 더 변경해 보겠습니다.Let's make one more change before adding new features. ProcessRepositories 메서드는 비동기 작업을 수행하고 리포지토리 컬렉션을 반환할 수 있습니다.The ProcessRepositories method can do the async work and return a collection of the repositories. 이 메서드에서 List<Repository>로 돌아가 정보를 쓰는 코드를 Main 메서드로 이동해 보겠습니다.Let's return the List<Repository> from that method, and move the code that writes the information into the Main method.

ProcessRepositories의 시그니처를 변경하여 Repository 개체의 목록을 해당 결과로 표시하는 작업을 반환합니다.Change the signature of ProcessRepositories to return a task whose result is a list of Repository objects:

private static async Task<List<Repository>> ProcessRepositories()

다음에는 JSON 응답을 처리한 후 리포지토리를 반환합니다.Then, just return the repositories after processing the JSON response:

var streamTask = client.GetStreamAsync("https://api.github.com/orgs/dotnet/repos");
var repositories = await JsonSerializer.DeserializeAsync<List<Repository>>(await streamTask);
return repositories;

이 개체를 async로 표시했으므로 컴파일러는 해당 반환에 대한 Task<T> 개체를 생성합니다.The compiler generates the Task<T> object for the return because you've marked this method as async. 그런 다음 해당 결과를 캡처하고 각 리포지토리 이름을 콘솔에 쓰도록 Main 메서드를 수정해 보겠습니다.Then, let's modify the Main method so that it captures those results and writes each repository name to the console. Main 메서드는 이제 다음과 같이 표시됩니다.Your Main method now looks like this:

public static async Task Main(string[] args)
{
    var repositories = await ProcessRepositories();

    foreach (var repo in repositories)
        Console.WriteLine(repo.Name);
}

추가 정보 읽기Reading More Information

GitHub API에서 전송되는 JSON 패킷에 있는 속성을 몇 가지 더 처리하는 것으로 마무리해 보겠습니다.Let's finish this by processing a few more of the properties in the JSON packet that gets sent from the GitHub API. 모든 기능을 알 수는 없겠지만 일부 속성을 추가하면 C# 언어의 몇 가지 기능이 추가로 확인됩니다.You won't want to grab everything, but adding a few properties will demonstrate a few more features of the C# language.

먼저 Repository 클래스 정의에 몇 가지 간단한 형식을 더 추가해 보겠습니다.Let's start by adding a few more simple types to the Repository class definition. 해당 클래스에 다음 속성을 추가합니다.Add these properties to that class:

[JsonPropertyName("description")]
public string Description { get; set; }

[JsonPropertyName("html_url")]
public Uri GitHubHomeUrl { get; set; }

[JsonPropertyName("homepage")]
public Uri Homepage { get; set; }

[JsonPropertyName("watchers")]
public int Watchers { get; set; }

이러한 속성은 기본적으로 문자열 형식(JSON 패킷에 포함된 형식)을 대상 형식으로 변환합니다.These properties have built-in conversions from the string type (which is what the JSON packets contain) to the target type. Uri 형식은 생소할 수 있습니다.The Uri type may be new to you. 이 형식은 URI 또는 이 경우에는 URL을 나타냅니다.It represents a URI, or in this case, a URL. Uriint 형식의 경우 JSON 패킷에 대상 형식으로 변환되지 않는 데이터가 포함되어 있으면 serialization 작업은 예외를 throw합니다.In the case of the Uri and int types, if the JSON packet contains data that does not convert to the target type, the serialization action will throw an exception.

이러한 데이터를 추가한 경우에는 해당 요소를 표시하도록 Main 메서드를 업데이트합니다.Once you've added these, update the Main method to display those elements:

foreach (var repo in repositories)
{
    Console.WriteLine(repo.Name);
    Console.WriteLine(repo.Description);
    Console.WriteLine(repo.GitHubHomeUrl);
    Console.WriteLine(repo.Homepage);
    Console.WriteLine(repo.Watchers);
    Console.WriteLine();
}

마지막 단계로, 최종 밀어넣기 작업을 위한 정보를 추가해 보겠습니다.As a final step, let's add the information for the last push operation. 이 정보는 JSON 응답에서 다음 방식으로 형식이 지정됩니다.This information is formatted in this fashion in the JSON response:

2016-02-08T21:27:00Z

해당 형식은 UTC(협정 세계시)이므로 Kind 속성이 UtcDateTime 값을 얻습니다.That format is in Coordinated Universal Time (UTC) so you'll get a DateTime value whose Kind property is Utc. 표준 시간대로 표시되는 날짜를 선호하는 경우 사용자 지정 변환 메서드를 작성해야 합니다.If you prefer a date represented in your time zone, you'll need to write a custom conversion method. 먼저 Repository 클래스에서 날짜 및 시간을 UTC로 표현하는 public 속성을 정의하고 현지 시간으로 변환된 날짜를 반환하는 LastPush readonly 속성을 정의합니다.First, define a public property that will hold the UTC representation of the date and time in your Repository class and a LastPush readonly property that returns the date converted to local time:

[JsonPropertyName("pushed_at")]
public DateTime LastPushUtc { get; set; }

public DateTime LastPush => LastPushUtc.ToLocalTime();

방금 정의한 새 구문을 살펴보겠습니다.Let's go over the new constructs we just defined. LastPush 속성은 get 접근자에 대한 식 본문 멤버 를 사용하여 정의됩니다.The LastPush property is defined using an expression-bodied member for the get accessor. set 접근자가 없습니다.There is no set accessor. set 접근자를 생략하는 것이 바로 C#에서 읽기 전용 속성을 정의하는 방식입니다.Omitting the set accessor is how you define a read-only property in C#. (C#에서 쓰기 전용 속성을 만들 수 있지만 해당 값은 제한됩니다.)(Yes, you can create write-only properties in C#, but their value is limited.)

마지막으로 콘솔에 output 문을 하나 더 추가하면 이 앱을 빌드하고 다시 실행할 준비가 됩니다.Finally, add one more output statement in the console, and you're ready to build and run this app again:

Console.WriteLine(repo.LastPush);

이제 해당 버전이 완성된 샘플과 일치해야 합니다.Your version should now match the finished sample.

결론Conclusion

이 자습서에서는 웹 요청을 수행하고, 결과를 구문 분석하고, 해당 결과의 속성을 표시하는 방법을 알아보았습니다.This tutorial showed you how to make web requests, parse the result, and display properties of those results. 또한 프로젝트에 종속성으로 새 패키지를 추가했습니다.You've also added new packages as dependencies in your project. 개체 지향 기술을 지원하는 C# 언어의 일부 기능을 확인했습니다.You've seen some of the features of the C# language that support object-oriented techniques.

dotnet new, dotnet build, dotnet run, dotnet test, dotnet publishdotnet pack 등 복원이 필요한 모든 명령에 의해 암시적으로 실행되므로 dotnet restore를 실행할 필요가 없습니다.You don't have to run dotnet restore because it's run implicitly by all commands that require a restore to occur, such as dotnet new, dotnet build, dotnet run, dotnet test, dotnet publish, and dotnet pack. 암시적 복원을 사용하지 않으려면 --no-restore 옵션을 사용합니다.To disable implicit restore, use the --no-restore option.

dotnet restore 명령은 Azure DevOps Services의 연속 통합 빌드 또는 복원 발생 시점을 명시적으로 제어해야 하는 빌드 시스템과 같이 명시적으로 복원이 가능한 특정 시나리오에서 여전히 유용합니다.The dotnet restore command is still useful in certain scenarios where explicitly restoring makes sense, such as continuous integration builds in Azure DevOps Services or in build systems that need to explicitly control when the restore occurs.

NuGet 피드를 관리하는 방법에 대한 자세한 내용은 dotnet restore 설명서를 참조하세요.For information about how to manage NuGet feeds, see the dotnet restore documentation.