이 자습서에서는 GitHub의 REST 서비스에 대한 HTTP 요청을 발급하는 앱을 빌드합니다. 앱은 JSON 형식으로 정보를 읽고 JSON을 C# 개체로 변환합니다. JSON에서 C# 개체로 변환하는 것을 역직렬화라고 합니다.
이 자습서에서는 다음 방법을 보여줍니다.
- HTTP 요청을 보냅니다.
- JSON 응답을 역직렬화합니다.
- 특성을 사용하여 역직렬화를 구성합니다.
이 자습서의 마지막 샘플 과 함께 수행하려는 경우 다운로드할 수 있습니다. 다운로드 지침은 샘플 및 자습서참조하세요.
필수 조건
- 최신 .NET SDK
- Visual Studio Code 편집기
- C# 개발 키트
클라이언트 앱 만들기
명령 프롬프트를 열고 앱에 대한 새 디렉터리를 만듭니다. 현재 디렉터리로 설정하십시오.
콘솔 창에서 다음 명령을 입력합니다.
dotnet new console --name WebAPIClient이 명령은 기본 "Hello World" 앱에 대한 시작 파일을 만듭니다. 프로젝트 이름은 "WebAPIClient"입니다.
"WebAPIClient" 디렉터리로 이동하여 앱을 실행합니다.
cd WebAPIClientdotnet rundotnet run는 앱에 필요한 모든 종속성을 복원하기 위해 자동으로 실행됩니다dotnet restore. 필요한 경우dotnet build이(가) 실행되기도 합니다. 앱 출력"Hello, World!"이 표시됩니다. 터미널에서 Ctrl+C 를 눌러 앱을 중지합니다.
HTTP 요청하기
이 앱은 GitHub API 를 호출하여 .NET Foundation 우산 아래의 프로젝트에 대한 정보를 가져옵니다. 엔드포인트는 .입니다 https://api.github.com/orgs/dotnet/repos. 정보를 검색하기 위해 HTTP GET 요청을 만듭니다. 또한 브라우저는 HTTP GET 요청을 하므로 해당 URL을 브라우저 주소 표시줄에 붙여넣어 수신 및 처리할 정보를 확인할 수 있습니다.
클래스를 HttpClient 사용하여 HTTP 요청을 만듭니다. HttpClient 는 장기 실행 API에 대한 비동기 메서드만 지원합니다. 따라서 다음 단계에서는 비동기 메서드를 만들고 Main 메서드에서 호출합니다.
Program.cs프로젝트 디렉터리에서 파일을 열고 해당 내용을 다음으로 바꿉니다.await ProcessRepositoriesAsync(); static async Task ProcessRepositoriesAsync(HttpClient client) { }이 코드:
-
Console.WriteLine문을ProcessRepositoriesAsync키워드를 사용하는await호출로 바꿉니다. - 빈
ProcessRepositoriesAsync메서드를 정의합니다.
-
Program클래스에서 HttpClient을 사용하여, 다음 C# 코드로 콘텐츠를 바꾸어 요청 및 응답을 처리하십시오.using System.Net.Http.Headers; using HttpClient client = new(); client.DefaultRequestHeaders.Accept.Clear(); client.DefaultRequestHeaders.Accept.Add( new MediaTypeWithQualityHeaderValue("application/vnd.github.v3+json")); client.DefaultRequestHeaders.Add("User-Agent", ".NET Foundation Repository Reporter"); await ProcessRepositoriesAsync(client); static async Task ProcessRepositoriesAsync(HttpClient client) { }이 코드:
- 모든 요청에 대해 HTTP 헤더를 설정합니다.
-
AcceptJSON 응답을 수락하는 헤더 -
User-Agent헤더입니다. 이러한 헤더는 GitHub 서버 코드에서 확인되며 GitHub에서 정보를 검색하는 데 필요합니다.
-
- 모든 요청에 대해 HTTP 헤더를 설정합니다.
ProcessRepositoriesAsync메서드에서 .NET Foundation 조직 아래의 모든 리포지토리 목록을 반환하는 GitHub 엔드포인트를 호출합니다.static async Task ProcessRepositoriesAsync(HttpClient client) { var json = await client.GetStringAsync( "https://api.github.com/orgs/dotnet/repos"); Console.Write(json); }이 코드:
- 호출 HttpClient.GetStringAsync(String) 메서드에서 반환된 작업을 기다립니다. 이 메서드는 HTTP GET 요청을 지정된 URI로 보냅니다. 응답 본문은 작업이 완료되면 String로 반환됩니다.
- 응답 문자열
json이 콘솔에 인쇄됩니다.
앱을 빌드하고 실행합니다.
dotnet runProcessRepositoriesAsync연산자가 이제await에 포함되어 있으므로 빌드 경고가 없습니다. 출력은 JSON 텍스트의 긴 표시입니다.
JSON 결과 역직렬화
다음 단계에서는 데이터를 가져오고 처리하는 방법을 간소화합니다. GetFromJsonAsync 일부인 확장 메서드를 사용하여 📦 JSON 결과를 개체로 가져오고 역직렬화합니다.
Repository.cs 파일을 만들고 다음 코드를 추가합니다.
public record class Repository(string Name);앞의 코드는 GitHub API에서 반환된 JSON 개체를 나타내는 클래스를 정의합니다. 이 클래스를 사용하여 리포지토리 이름 목록을 표시합니다.
리포지토리 개체의 JSON에는 수십 개의 속성이
Name포함되지만 속성만 역직렬화됩니다. serializer는 대상 클래스에 일치하는 항목이 없는 JSON 속성을 자동으로 무시합니다. 이 기능을 사용하면 큰 JSON 패킷에 있는 필드의 하위 집합만 사용하는 형식을 더 쉽게 만들 수 있습니다.GetFromJsonAsync다음 지점에서 사용할 메서드는 속성 이름에 대해 대/소문자를 구분하지 않는 이점이 있지만 C# 규칙은 속성 이름의 첫 글자를 대문자로 사용하는 것입니다.이 메서드를 HttpClientJsonExtensions.GetFromJsonAsync 사용하여 JSON을 C# 개체로 가져오고 변환합니다. GetStringAsync(String) 메서드에서
ProcessRepositoriesAsync의 호출을 다음 줄로 대체하십시오.var repositories = await client.GetFromJsonAsync<List<Repository>>("https://api.github.com/orgs/dotnet/repos");업데이트된 코드는 GetStringAsync(String)을 HttpClientJsonExtensions.GetFromJsonAsync으로 바꿉니다.
메서드의
GetFromJsonAsync첫 번째 인수는 식입니다await.await표현식은 코드 내 거의 모든 위치에 나타날 수 있지만, 지금까지는 할당문에서만 본 적이 있을 것입니다. 다음 매개 변수requestUri는 선택 사항이며 개체를 만들client때 이미 지정된 경우 제공할 필요가 없습니다. 요청을 보낼 URI를 개체에 제공하지client않았으므로 이제 URI를 지정했습니다. 마지막 선택적 매개 변수CancellationToken인 코드 조각에서 생략됩니다.이
GetFromJsonAsync메서드는 제네릭입니다. 즉, 가져온 JSON 텍스트에서 만들어야 하는 개체의 종류에 대한 형식 인수를 제공합니다. 이 예제에서는List<Repository>를 제네릭 개체로 역직렬화하고, 이는 또 다른 제네릭 개체인 System.Collections.Generic.List<T>입니다. 클래스는List<T>개체 컬렉션을 저장합니다. 형식 인수는 에 저장된 개체의 형식을List<T>선언합니다. JSON 텍스트는Repository리포지토리 개체의 컬렉션을 나타내므로 형식 인수는 해당 레코드입니다.각 리포지토리의 이름을 표시하는 코드를 추가합니다. 다음과 같이 읽히는 줄을 교체합니다.
Console.Write(json);다음 코드를 사용합니다.
foreach (var repo in repositories ?? Enumerable.Empty<Repository>()) Console.WriteLine(repo.Name);다음
using지시문은 파일 맨 위에 있어야 합니다.using System.Net.Http.Headers; using System.Net.Http.Json;앱을 실행합니다.
dotnet run출력은 .NET Foundation의 일부인 리포지토리의 이름 목록입니다.
코드 리팩터링
메서드는 ProcessRepositoriesAsync 비동기 작업을 수행하고 리포지토리의 컬렉션을 반환할 수 있습니다. 해당 메서드를 Task<List<Repository>>을 반환하도록 변경하고, 그 호출자 근처로 콘솔에 쓰는 코드를 옮기세요.
ProcessRepositoriesAsync의 서명을Repository개체 목록 결과를 반환하는 작업으로 변경합니다.static async Task<List<Repository>> ProcessRepositoriesAsync(HttpClient client)JSON 응답을 처리한 후 리포지토리를 반환합니다.
var repositories = await client.GetFromJsonAsync<List<Repository>>("https://api.github.com/orgs/dotnet/repos"); return repositories ?? new();컴파일러는 이 메서드를
Task<T>로 표시했으므로 반환 값의async개체를 생성합니다.Program.cs 파일을 수정하고 호출을
ProcessRepositoriesAsync다음으로 바꿔 결과를 캡처하고 각 리포지토리 이름을 콘솔에 씁니다.var repositories = await ProcessRepositoriesAsync(client); foreach (var repo in repositories) Console.WriteLine(repo.Name);앱을 실행합니다.
출력은 동일합니다.
더 많은 속성 역직렬화
다음 단계에서는 GitHub API에서 반환된 JSON 페이로드에서 더 많은 속성을 처리하도록 코드를 확장합니다. 모든 속성을 처리할 필요는 없지만 몇 가지를 추가하면 추가 C# 기능이 표시됩니다.
클래스의
Repository내용을 다음record정의로 바꿉니다. 네임스페이스System.Text.Json.Serialization를 가져오고[JsonPropertyName]특성을 적용하여 JSON 필드를 C# 속성에 명시적으로 매핑해야 합니다.using System.Text.Json.Serialization; public record class Repository( string Name, string Description, [property: JsonPropertyName("html_url")] Uri GitHubHomeUrl, Uri Homepage, int Watchers, [property: JsonPropertyName("pushed_at")] DateTime LastPushUtc );Uri 및
int형식에는 문자열 표현으로 변환하거나 문자열 표현으로부터 변환할 수 있는 기본 제공 기능이 있습니다. JSON 문자열 형식에서 해당 대상 형식으로 역직렬화하는 데 추가 코드가 필요하지 않습니다. JSON 패킷에 대상 형식으로 변환되지 않는 데이터가 포함된 경우 serialization 작업은 예외를 throw합니다.JSON은
lowercase또는snake_case을(를) 속성 이름으로 자주 사용합니다. 필드html_url및pushed_at는 C#의 PascalCase 명명 규칙을 따르지 않습니다.[JsonPropertyName]을 사용하면 JSON 키가 대/소문자나 밑줄을 포함하는 경우에도 해당 C# 속성에 정확하게 바인딩됩니다. 이 방법은 C#에서 PascalCase 속성 이름을 허용하면서 예측 가능하고 안정적인 역직렬화를 보장합니다.GetFromJsonAsync또한 메서드는case-insensitive속성 이름을 일치시킬 때이므로 더 이상 변환할 필요가 없습니다.foreachProgram.cs 파일의 루프를 업데이트하여 속성 값을 표시합니다.foreach (var repo in repositories) { Console.WriteLine($"Name: {repo.Name}"); Console.WriteLine($"Homepage: {repo.Homepage}"); Console.WriteLine($"GitHub: {repo.GitHubHomeUrl}"); Console.WriteLine($"Description: {repo.Description}"); Console.WriteLine($"Watchers: {repo.Watchers:#,0}"); Console.WriteLine(); }앱을 실행합니다.
이제 목록에 추가 속성이 포함됩니다.
날짜 속성 추가
마지막 푸시 작업의 날짜는 JSON 응답에서 다음과 같이 형식이 지정됩니다.
2016-02-08T21:27:00Z
이 형식은 협정 세계시(UTC)를 위한 것이므로, 역직렬화의 결과는 DateTime이며, 이 값의 Kind 속성은 Utc입니다.
표준 시간대에 표시되는 날짜 및 시간을 얻으려면 사용자 지정 변환 방법을 작성해야 합니다.
Repository.cs 날짜 및 시간의 UTC 표현에 대한 속성과 현지 시간으로 변환된 날짜를 반환하는 읽기 전용
LastPush속성을 추가하면 파일은 다음과 같이 표시됩니다.using System.Text.Json.Serialization; public record class Repository( string Name, string Description, [property: JsonPropertyName("html_url")] Uri GitHubHomeUrl, Uri Homepage, int Watchers, [property: JsonPropertyName("pushed_at")] DateTime LastPushUtc ) { public DateTime LastPush => LastPushUtc.ToLocalTime(); }LastPush속성은 식 본문 멤버를 사용하여get접근자를 정의합니다.set접근자가 없습니다. 접근자를set생략하는 것은 C#에서 읽기 전용 속성을 정의하는 한 가지 방법입니다. (예, C#에서 쓰기 전용 속성을 만들 수 있지만 해당 값은 제한됩니다.)Program.cs에 다시 출력 문을 추가합니다.
Console.WriteLine($"Last push: {repo.LastPush}");전체 앱은 다음 Program.cs 파일과 유사해야 합니다.
using System.Net.Http.Headers; using System.Net.Http.Json; using HttpClient client = new(); 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 repositories = await ProcessRepositoriesAsync(client); foreach (var repo in repositories) { Console.WriteLine($"Name: {repo.Name}"); Console.WriteLine($"Homepage: {repo.Homepage}"); Console.WriteLine($"GitHub: {repo.GitHubHomeUrl}"); Console.WriteLine($"Description: {repo.Description}"); Console.WriteLine($"Watchers: {repo.Watchers:#,0}"); Console.WriteLine($"{repo.LastPush}"); Console.WriteLine(); } static async Task<List<Repository>> ProcessRepositoriesAsync(HttpClient client) { var repositories = await client.GetFromJsonAsync<List<Repository>>("https://api.github.com/orgs/dotnet/repos"); return repositories ?? new List<Repository>(); }앱을 실행합니다.
출력에는 각 리포지토리에 대한 마지막 푸시의 날짜와 시간이 포함됩니다.
다음 단계
이 자습서에서는 웹 요청을 만들고 결과를 구문 분석하는 앱을 만들었습니다. 이제 앱 버전이 완성된 샘플과 일치해야 합니다.
.NET에서 JSON을 직렬화 및 역직렬화(마샬링 및 언마샬링)하는 방법을 구성하는 방법에 대해 자세히 알아봅니다.
.NET