Skip to main content

๋ณต์žกํ•œ ์•Œ๊ณ ๋ฆฌ์ฆ˜์ด๋‚˜ ๋…ผ๋ฆฌ ์„ค๋ช…

Copilot Chat์€ ๋ณต์žกํ•œ ์•Œ๊ณ ๋ฆฌ์ฆ˜์ด๋‚˜ ๋…ผ๋ฆฌ์— ๋Œ€ํ•œ ๋ช…ํ™•ํ•˜๊ณ  ๊ฐ„๊ฒฐํ•œ ์„ค๋ช…์„œ๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ฝ”๋“œ์—์„œ ๋ณต์žกํ•œ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ๋˜๋Š” ๋…ผ๋ฆฌ๋ฅผ ์„ค๋ช…ํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํŠนํžˆ ๋‹ค๋ฅธ ์‚ฌ๋žŒ์ด ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค๋ช…ํ•˜๋ ค๊ณ  ํ•˜๋Š” ๊ฒฝ์šฐ ๋” ์–ด๋ ค์šธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Copilot Chat์€ ๋ช…ํ™•ํ•˜๊ณ  ๊ฐ„๊ฒฐํ•œ ๋ฐฉ์‹์œผ๋กœ ์•Œ๊ณ ๋ฆฌ์ฆ˜์ด๋‚˜ ๋…ผ๋ฆฌ๋ฅผ ์„ค๋ช…ํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•œ ์ œ์•ˆ์„ ์ œ๊ณตํ•˜์—ฌ ์ด ์ž‘์—…์— ๋„์›€์ด ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์˜ˆ์ œ ์‹œ๋‚˜๋ฆฌ์˜ค

์•„๋ž˜ C# ์ฝ”๋“œ์—๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๊ณ  ์˜ค๋ฅ˜๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ ๋‹ค์‹œ ์‹œ๋„ํ•˜๊ณ  ์ƒํƒœ ๋ ˆ์ด๋ธ”์„ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๋ฉ”์„œ๋“œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ฝ”๋“œ์˜ ์ฃผ์„์—์„œ ๋ฉ”์„œ๋“œ์˜ ์ž‘๋™ ๋ฐฉ์‹ ๋ฐ ์žฌ์‹œ๋„์™€ ์ทจ์†Œ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์„ค๋ช…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

private static readonly HttpClient _client = new HttpClient();

public async Task<string> FetchDataFromApiWithRetryAsync(string apiUrl, CancellationToken cancellationToken, int maxRetries, int cancellationDelay, Label statusLabel)
{
    var retryCount = 0;
    using var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);

    while (retryCount < maxRetries)
    {
        try
        {
            cts.CancelAfter(cancellationDelay);
            return await FetchDataFromApiAsync(cts.Token, statusLabel);
        }
        catch (Exception ex) when (!(ex is OperationCanceledException))
        {
            if (retryCount < maxRetries - 1) {
                retryCount++;
                int delay = (int)Math.Pow(2, retryCount) * 1000;
                await Task.Delay(delay, cancellationToken);
                UpdateStatusLabel($"Retrying ({retryCount}/{maxRetries})...", statusLabel);
            } else {
                throw new Exception($"Failed to fetch data after {maxRetries} retries: {ex.Message}", ex);
            }
        }
    }

    throw new OperationCanceledException("The operation was cancelled.", cts.Token);
}

private async Task<string> FetchDataFromApiAsync(string apiUrl, CancellationToken cancellationToken, Label statusLabel)
{
    UpdateStatusLabel("Fetching data...", statusLabel);

    HttpResponseMessage response = await _client.GetAsync(apiUrl, cancellationToken);
    response.EnsureSuccessStatusCode();
    string data = await response.Content.ReadAsStringAsync();

    UpdateStatusLabel("Data fetched.", statusLabel);
    return data;
}

private void UpdateStatusLabel(string message, Label statusLabel)
{
    if (statusLabel.InvokeRequired) {
        statusLabel.BeginInvoke(new Action<string, Label>(UpdateStatusLabel), message, statusLabel);
    } else {
        statusLabel.Text = message;
    }
}

์˜ˆ์‹œ ํ”„๋กฌํ”„ํŠธ

Copilot Chat์—๊ฒŒ ๋ฉ”๋ชจ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ด ์ฝ”๋“œ๋ฅผ ๋” ์‰ฝ๊ฒŒ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋„๋ก ์š”์ฒญํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Add comments to this code to make it more understandable

์˜ˆ์ œ ์‘๋‹ต

์ฐธ๊ณ  ํ•ญ๋ชฉ

๋‹ค์Œ ์‘๋‹ต์ด ์˜ˆ์ œ์ž…๋‹ˆ๋‹ค. Copilot Chat ์‘๋‹ต์€ ๋น„๊ฒฐ์ •์ ์ด๋ฏ€๋กœ ์—ฌ๊ธฐ์— ํ‘œ์‹œ๋œ ์‘๋‹ต๊ณผ ๋‹ค๋ฅธ ์‘๋‹ต์„ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Copilot๋Š” ์ œ์•ˆ๋œ ์—…๋ฐ์ดํŠธ์˜ ๊ฐœ์š”์™€ ์ฃผ์„์ด ์ถ”๊ฐ€๋œ ์ฝ”๋“œ ์กฐ๊ฐ์œผ๋กœ ์‘๋‹ตํ•ฉ๋‹ˆ๋‹ค.

// Shared HttpClient instance for making HTTP requests (best practice for reuse)
private static readonly HttpClient _client = new HttpClient();

/// <summary>
/// Fetches data from an API with retry mechanism and timeout functionality
/// </summary>
/// <param name="apiUrl">The URL to fetch data from</param>
/// <param name="cancellationToken">Token to cancel the operation</param>
/// <param name="maxRetries">Maximum number of retry attempts</param>
/// <param name="cancellationDelay">Timeout duration in milliseconds</param>
/// <param name="statusLabel">UI label to show status updates</param>
public async Task<string> FetchDataFromApiWithRetryAsync(string apiUrl, CancellationToken cancellationToken, int maxRetries, int cancellationDelay, Label statusLabel)
{
    var retryCount = 0;
    // Create a linked cancellation token that combines user cancellation and timeout
    using var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);

    while (retryCount < maxRetries)
    {
        try
        {
            // Set timeout for this attempt
            cts.CancelAfter(cancellationDelay);
            return await FetchDataFromApiAsync(cts.Token, statusLabel);
        }
        catch (Exception ex) when (!(ex is OperationCanceledException))
        {
            // If not the last retry attempt, implement exponential backoff
            if (retryCount < maxRetries - 1) {
                retryCount++;
                // Exponential backoff: 2^retryCount seconds (1s, 2s, 4s, 8s, etc.)
                int delay = (int)Math.Pow(2, retryCount) * 1000;
                await Task.Delay(delay, cancellationToken);
                UpdateStatusLabel($"Retrying ({retryCount}/{maxRetries})...", statusLabel);
            } else {
                // If all retries failed, throw exception with details
                throw new Exception($"Failed to fetch data after {maxRetries} retries: {ex.Message}", ex);
            }
        }
    }

    // This is reached if maxRetries is 0 or some other unexpected case
    throw new OperationCanceledException("The operation was cancelled.", cts.Token);
}

/// <summary>
/// Makes the actual API call to fetch data
/// </summary>
private async Task<string> FetchDataFromApiAsync(string apiUrl, CancellationToken cancellationToken, Label statusLabel)
{
    UpdateStatusLabel("Fetching data...", statusLabel);

    // Perform HTTP GET request with cancellation support
    HttpResponseMessage response = await _client.GetAsync(apiUrl, cancellationToken);
    response.EnsureSuccessStatusCode(); // Throws if response is not successful
    string data = await response.Content.ReadAsStringAsync();

    UpdateStatusLabel("Data fetched.", statusLabel);
    return data;
}

/// <summary>
/// Updates UI label text safely across threads
/// </summary>
/// <param name="message">The message to display</param>
/// <param name="statusLabel">The label control to update</param>
private void UpdateStatusLabel(string message, Label statusLabel)
{
    // Check if we need to invoke on UI thread
    if (statusLabel.InvokeRequired) {
        // Use BeginInvoke to update label on UI thread
        statusLabel.BeginInvoke(new Action<string, Label>(UpdateStatusLabel), message, statusLabel);
    } else {
        // Direct update if already on UI thread
        statusLabel.Text = message;
    }
}

์ถ”๊ฐ€ ์ฐธ๊ณ  ์ž๋ฃŒ