C#과 .NET의 HttpClient를 사용하여 택배 조회 API를 연동하는 방법을 안내합니다. .NET 6 이상을 기준으로 설명하며, 별도의 외부 패키지 없이 기본 제공되는 기능만으로 구현할 수 있습니다.

C# / .NET 연동의 장점
• .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 보안 주의
Secret Key는 서버에서만 사용하세요. 클라이언트 앱(Blazor WASM, MAUI 등)에 포함하면 키가 노출될 수 있습니다. GitHub 등 공개 저장소에 업로드되지 않도록 .gitignoreappsettings.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");
}
에러 처리 3단계
HTTP 레벨response.IsSuccessStatusCode로 401, 429, 500 등 확인
API 레벨isSuccess 필드로 요청 전체의 성공 여부 확인
건별 레벨 — 각 result.success로 개별 조회 성공 여부 확인

다음 단계

지금 바로 시작하세요!

무료 플랜으로 월 30,000건까지 사용 가능합니다.

무료로 시작하기 →