C#과 .NET의 HttpClient를 사용하여 택배 조회 API를 연동하는 방법을 안내합니다. .NET 6 이상을 기준으로 설명하며, 별도의 외부 패키지 없이 기본 제공되는 기능만으로 구현할 수 있습니다.
• .NET 기본
HttpClient만으로 연동 가능 — 별도 HTTP 라이브러리 불필요•
System.Text.Json 기본 제공으로 별도 NuGet 패키지 불필요•
async/await으로 비동기 처리 — UI 블로킹 없이 API 호출
1. NuGet 패키지
.NET 6 이상에서는 System.Text.Json이 기본 포함되어 있으므로 추가 패키지가 필요 없습니다. JSON 직렬화/역직렬화가 모두 내장되어 있습니다.
선택 사항으로 Newtonsoft.Json을 사용할 수도 있지만, 대부분의 경우 기본 제공되는 System.Text.Json으로 충분합니다.
2. API 키 설정
API 키는 환경 변수 또는 appsettings.json에서 관리합니다. 코드에 직접 작성하지 마세요.
환경 변수 방식
var apiKey = Environment.GetEnvironmentVariable("DELIVERY_API_KEY");
var secretKey = Environment.GetEnvironmentVariable("DELIVERY_SECRET_KEY");
var authHeader = $"Bearer {apiKey}:{secretKey}"; appsettings.json 방식
// appsettings.json
{
"DeliveryApi": {
"ApiKey": "pk_live_xxxxxxxxxxxxxxxx",
"SecretKey": "sk_live_xxxxxxxxxxxxxxxx"
}
} Secret Key는 서버에서만 사용하세요. 클라이언트 앱(Blazor WASM, MAUI 등)에 포함하면 키가 노출될 수 있습니다. GitHub 등 공개 저장소에 업로드되지 않도록
.gitignore에 appsettings.Development.json을 추가하세요.
3. 단건 조회
HttpClient로 택배 조회 API를 호출하는 기본 예제입니다. items 배열에 조회할 택배 정보를 넣어 POST 요청을 보냅니다.
using System.Net.Http;
using System.Text;
using System.Text.Json;
var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", authHeader);
var payload = new
{
items = new[]
{
new { courierCode = "cj", trackingNumber = "1234567890" }
}
};
var content = new StringContent(
JsonSerializer.Serialize(payload),
Encoding.UTF8,
"application/json"
);
var response = await client.PostAsync(
"https://api.deliveryapi.co.kr/v1/tracking/trace",
content
);
var json = await response.Content.ReadAsStringAsync();
Console.WriteLine(json); 4. 응답 파싱
JsonDocument를 사용하면 별도 모델 클래스 없이도 응답을 파싱할 수 있습니다. 빠르게 테스트하거나 간단한 스크립트에서 유용합니다.
using System.Text.Json;
var doc = JsonDocument.Parse(json);
var root = doc.RootElement;
if (root.GetProperty("isSuccess").GetBoolean())
{
var results = root.GetProperty("data").GetProperty("results");
foreach (var result in results.EnumerateArray())
{
if (result.GetProperty("success").GetBoolean())
{
var data = result.GetProperty("data");
var status = data.GetProperty("deliveryStatusText").GetString();
var trackingNumber = data.GetProperty("trackingNumber").GetString();
Console.WriteLine($"[{trackingNumber}] {status}");
}
else
{
var error = result.GetProperty("error");
Console.WriteLine($"조회 실패: {error.GetProperty("code").GetString()}");
}
}
} 5. 다건 조회
items 배열에 여러 택배를 넣으면 한 번의 API 호출로 최대 50건까지 조회할 수 있습니다. clientId에 주문번호를 넣으면 응답에서 쉽게 식별할 수 있습니다.
var payload = new
{
items = new[]
{
new { courierCode = "cj", trackingNumber = "1111111111", clientId = "order_001" },
new { courierCode = "lotte", trackingNumber = "2222222222", clientId = "order_002" },
new { courierCode = "hanjin", trackingNumber = "3333333333", clientId = "order_003" },
}
};
var content = new StringContent(
JsonSerializer.Serialize(payload),
Encoding.UTF8,
"application/json"
);
var response = await client.PostAsync(
"https://api.deliveryapi.co.kr/v1/tracking/trace",
content
);
var json = await response.Content.ReadAsStringAsync();
Console.WriteLine(json); 6. 타입 정의 (모델 클래스)
프로덕션 코드에서는 record 또는 class로 응답 타입을 정의하여 타입 안전한 역직렬화를 사용하는 것을 권장합니다.
// Models/TrackingApiResponse.cs
using System.Text.Json.Serialization;
public record TrackingApiResponse(
[property: JsonPropertyName("isSuccess")] bool IsSuccess,
[property: JsonPropertyName("data")] TrackingData? Data,
[property: JsonPropertyName("message")] string? Message
);
public record TrackingData(
[property: JsonPropertyName("results")] TrackingResult[] Results,
[property: JsonPropertyName("summary")] TrackingSummary Summary
);
public record TrackingResult(
[property: JsonPropertyName("success")] bool Success,
[property: JsonPropertyName("data")] TrackingInfo? Data,
[property: JsonPropertyName("error")] TrackingError? Error,
[property: JsonPropertyName("clientId")] string? ClientId
);
public record TrackingInfo(
[property: JsonPropertyName("trackingNumber")] string TrackingNumber,
[property: JsonPropertyName("courierCode")] string CourierCode,
[property: JsonPropertyName("courierName")] string CourierName,
[property: JsonPropertyName("deliveryStatus")] string DeliveryStatus,
[property: JsonPropertyName("deliveryStatusText")] string DeliveryStatusText,
[property: JsonPropertyName("isDelivered")] bool IsDelivered
);
public record TrackingError(
[property: JsonPropertyName("code")] string Code,
[property: JsonPropertyName("message")] string Message
);
public record TrackingSummary(
[property: JsonPropertyName("total")] int Total,
[property: JsonPropertyName("successful")] int Successful,
[property: JsonPropertyName("failed")] int Failed
); 모델을 사용한 역직렬화
var options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
};
var result = JsonSerializer.Deserialize<TrackingApiResponse>(json, options);
if (result?.IsSuccess == true && result.Data != null)
{
foreach (var item in result.Data.Results)
{
if (item.Success && item.Data != null)
{
Console.WriteLine($"[{item.Data.TrackingNumber}] {item.Data.DeliveryStatusText}");
}
}
} 7. ASP.NET Core 연동
ASP.NET Core에서는 IHttpClientFactory를 DI로 주입받아 사용합니다. HttpClient를 직접 생성하는 것보다 커넥션 풀 관리가 효율적입니다.
// Program.cs (Minimal API)
var builder = WebApplication.CreateBuilder(args);
// Register HttpClient via DI
builder.Services.AddHttpClient("DeliveryApi", client =>
{
var config = builder.Configuration.GetSection("DeliveryApi");
var apiKey = config["ApiKey"];
var secretKey = config["SecretKey"];
client.BaseAddress = new Uri("https://api.deliveryapi.co.kr");
client.DefaultRequestHeaders.Add("Authorization", $"Bearer {apiKey}:{secretKey}");
});
var app = builder.Build();
app.MapPost("/track", async (TrackRequest req, IHttpClientFactory factory) =>
{
var client = factory.CreateClient("DeliveryApi");
var payload = new { items = new[] { new { req.CourierCode, req.TrackingNumber } } };
var content = new StringContent(
JsonSerializer.Serialize(payload),
Encoding.UTF8,
"application/json"
);
var response = await client.PostAsync("/v1/tracking/trace", content);
var json = await response.Content.ReadAsStringAsync();
return Results.Content(json, "application/json");
});
app.Run();
public record TrackRequest(string CourierCode, string TrackingNumber); 8. 에러 처리
API 연동 시 네트워크 오류, HTTP 오류, API 레벨 오류, 건별 오류를 모두 처리해야 합니다. 아래 예제는 각 단계별 에러 처리를 보여줍니다.
try
{
var response = await client.PostAsync(
"https://api.deliveryapi.co.kr/v1/tracking/trace",
content
);
// Check HTTP status
if (!response.IsSuccessStatusCode)
{
var errorBody = await response.Content.ReadAsStringAsync();
Console.WriteLine($"HTTP {(int)response.StatusCode}: {errorBody}");
return;
}
var json = await response.Content.ReadAsStringAsync();
var result = JsonSerializer.Deserialize<TrackingApiResponse>(json);
// Check API-level success
if (result?.IsSuccess != true)
{
Console.WriteLine($"API error: {result?.Message}");
return;
}
// Check per-item success
foreach (var item in result.Data!.Results)
{
if (!item.Success)
{
Console.WriteLine($"[{item.ClientId}] Failed: {item.Error?.Code}");
}
}
}
catch (HttpRequestException ex)
{
Console.WriteLine($"Network error: {ex.Message}");
}
catch (TaskCanceledException)
{
Console.WriteLine("Request timed out");
} • HTTP 레벨 —
response.IsSuccessStatusCode로 401, 429, 500 등 확인• API 레벨 —
isSuccess 필드로 요청 전체의 성공 여부 확인• 건별 레벨 — 각
result.success로 개별 조회 성공 여부 확인