푸터 콘텐츠로 바로가기
Iron Academy Logo
C# 및 .NET의 새로운 기능

Async C#: Tim Corey와 함께 비동기 Zip에 대한 심층 탐구

Tim Corey
24m 49s

파일 압축 및 추출은 데스크톱 앱, 웹 서버 작업이든, 또는 데이터를 아카이브해야 할 필요가 있든 간에 많은 C# 개발자에게 필수적인 작업입니다. 수년간 .NET 개발자들은 동기적으로 zip 파일을 생성, 업데이트 및 추출 할 수 있었으며, 이는 UI 스레드를 차단하거나 다른 작업을 지연시켰습니다. Tim Corey가 그의 비디오 "Async Zip in .NET 10 – One Line Create or Extract And More"에서 설명했듯이, .NET 10은 비동기 zip 파일 작업을 소개하여 개발자들이 메인 애플리케이션을 얼리지 않고 I/O 바운드 작업을 수행하게 하는 효율적인 코드를 작성할 수 있게 했습니다.

C#에서의 비동기 프로그래밍은 주로 Task 및 Task 객체를 기반으로 하며, Task 클래스는 진행 중인 작업을 모델링하고 동시에 실행할 수 있게 하는 핵심 요소로 작용합니다. Task 비동기 프로그래밍(TAP) 모델은 일반적인 비동기 코딩에 대한 추상화 계층을 제공하여 비동기 작업 및 예외를 처리하는 것을 더 쉽게 만듭니다. System.Threading.Tasks.Task 클래스 및 관련 유형은 C#에서 작업 기반 비동기 프로그래밍 모델을 구현하는 데 사용됩니다.

이번 기사에서는 Tim의 설명을 따라 비동기 작업을 수행하고, C#에서 zip 파일 생성, 추출 및 선택적 압축 방법을 보여드릴 것입니다. 이 과정에서 async/await 패턴, Task 객체 및 적절한 예외 처리를 사용합니다.

Asynchronous Programming 및 Async Zip in .NET 10 소개

Tim은 C#이 zip 파일을 생성 및 추출할 수 있는 긴 역량을 강조하며 시작합니다. 그러나 .NET 10을 사용하면 개발자는 호출 스레드를 차단하지 않고 이러한 작업을 처리하기 위해 비동기 코드를 사용할 수 있습니다. 비동기 메서드와 await 키워드를 사용하면, 대용량 zip 파일을 생성하거나 추출하는 작업이 백그라운드 스레드에서 실행될 수 있어 메인 UI 스레드가 반응성을 유지할 수 있습니다.

async 키워드는 C# 컴파일러에게 해당 메서드가 최소 한 번의 await 키워드를 포함하고 있음을 나타내는 장식자에 불과합니다. await 키워드는 비동기 메서드 내부에서만 사용할 수 있으며, Task 또는 Task객체가 뒤따라야 합니다.

Tim이 설명하듯, 이는 특히 다중 작업(예: 네트워크 호출, 데이터베이스 쿼리, 파일 I/O)을 수행하는 애플리케이션에서 중요합니다. 여기서 대기 중인 작업은 호출 스레드를 차지하지 않고 CPU 바운드 작업이 계속되도록 합니다. .Result 또는 .Wait()로 비동기 코드를 차단하면 비동기 코드가 동기 코드로 변환되며, 이는 교착 상태로 이어질 수 있습니다.

비동기 메서드는 Task 또는 Task객체를 반환하며, 이를 반환함으로써 적절한 작업 구성 및 동시성을 가능하게 합니다. await이 없는 메서드는 동기적으로 실행됩니다. 비동기 메서드에서 Task 객체를 반환함으로써 여러 진행 중인 작업을 조정하고 예외를 더 효과적으로 처리할 수 있습니다.

프로젝트 설정

Tim은 Visual Studio 2026을 열어 ZipFilesApp이라는 콘솔 애플리케이션을 생성하면서 시작합니다. 심지어 static async Task Main 메서드조차 비동기 작업을 수행하여 프로그램의 진입점에서 메인 스레드를 차단하지 않도록 사용할 수 있다고 말합니다. 기존 코드를 조정할 때는 종종 동기 코드를 Task로 감싸거나 비동기 버전을 구현하여 전체 코드베이스를 다시 작성하지 않고도 비동기 동작을 도입할 수 있습니다.

그 후 필수적인 using 지시문을 추가합니다:

using System.IO.Compression;

이를 통해 CreateFromDirectoryAsync 및 ExtractToDirectoryAsync와 같은 비동기 메서드에 액세스할 수 있습니다. 메서드를 만들 때 비동기 메서드에 'Async' 접미사를 추가하여 메서드가 동기인지 비동기인지 명확히 구분하는 명명 규칙을 사용하는 것이 중요합니다. 일관성 없는 메서드 명명은 메서드가 동기인지 비동기인지 혼란을 초래할 수 있습니다.

다음으로 Tim은 zip 작업에 필요한 경로를 정의하기 위해 세 개의 문자열 변수를 준비합니다:

  1. 원본 디렉터리 – 압축할 폴더입니다.

  2. 대상 zip 파일 – zip 파일을 저장할 전체 경로입니다.

  3. 대상 디렉터리 – 추출된 파일이 저장될 폴더입니다.

Tim은 문자열 리터럴에서 @ 기호를 사용하여 백슬래시를 피하는 것이 Windows 경로 작성 시 동기 코드에서 흔히 발생하는 문제를 피한다고 언급합니다. 이 기사 내내 C#에서 비동기 프로그래밍을 구현하는 방법을 설명하기 위해 코드 샘플과 코드 조각이 제공됩니다.

비동기 메서드를 사용한 Zip 파일 생성하기

3:47경, Tim은 static async Task 메서드를 사용한 단일 행의 비동기 작업을 시연합니다:

await ZipFile.CreateFromDirectoryAsync(
    sourceDirectory, 
    destinationZipFile, 
    CompressionLevel.SmallestSize, 
    includeBaseDirectory: false
);

여기서 Tim은 여러 중요한 측면을 설명합니다:

  • async 키워드는 이 비동기 메서드를 표시하여 await를 사용할 수 있게 합니다.

  • await는 이 비동기 메서드의 실행을 중지하여 작업이 완료될 때까지 기다리게 하며, 다른 작업이나 메인 UI 스레드가 계속 실행될 수 있게 합니다. 이는 await가 어떻게 작동하고, 비동기 작업(예: 파일 I/O)에서 비차단 동작을 보장하기 위해 실행을 중지하는지를 보여줍니다.

  • CompressionLevel.SmallestSize는 효율적인 압축을 보장합니다.

  • includeBaseDirectory 매개변수는 루트 폴더가 zip에 포함될지 여부를 제어합니다.

이 예제는 C#에서 비동기 워크플로가 async 및 await 키워드에 의해 관리되어, 코드가 더 유지보수 가능해지고 예외 처리가 간소화되는 방법을 보여줍니다. await 키워드는 비동기 메서드 내부에서만 사용할 수 있으며, 공용 비동기 Task 메서드와 같은 비동기 메서드에서만 사용할 수 있습니다. await을 만나면, C# 컴파일러는 상태 기계를 생성하여 실행 흐름을 관리하며, 실행을 중지하고, 비동기 작업을 시작하고, 완료되면 이어서 실행합니다. 비동기 메서드가 값을 반환하면, 결과는 나중에 사용할 수 있도록 int result와 같은 변수에 캡처될 수 있습니다. async/await를 사용하여 개발자는 백그라운드 스레드나 스레드 풀 작업을 수동으로 생성할 필요가 없습니다. 컴파일러가 필요한 상태 기계와 지원 코드를 자동으로 생성합니다.

Await Task를 사용하여 비동기로 Zip 파일 추출하기

Tim은 비동기 작업을 사용한 zip 파일 추출로 이동하여 또 다른 비동기 메서드를 사용합니다:

await ZipFile.ExtractToDirectoryAsync(
    destinationZipFile, 
    destinationDirectory, 
    overwriteFiles: false
);

그는 overwriteFiles 매개변수가 폴더가 이미 존재할 경우 예외 처리를 수행한다고 밝힙니다. 기본적으로, 파일이 이미 존재하면 추출이 실패하지만, 이를 true로 설정하면 비동기 작업이 기존 파일을 대체할 수 있게 합니다.

비동기 코드 작업 시, 신뢰성 있는 예외 처리가 필수적입니다. Tim은 비동기 메서드에서 예외를 처리하기 위해 try-catch 블록을 사용하는 것이 중요하다고 강조합니다. 비동기 코드에서 예외를 제대로 처리하지 않으면 비동기 작업에서 발생한 예외가 즉시 보이지 않아 조용한 실패 또는 예기치 못한 충돌로 이어질 수 있습니다. 항상 예외를 처리하여 신뢰성 있는 애플리케이션 동작을 보장하세요.

Tim은 이 비동기 코드가 여러 작업에 대해 안전하다고 언급합니다. 즉, 동시에 발생하는 다른 요청이나 네트워크 호출을 차단하지 않음을 의미합니다. await 키워드를 사용하여 호출 스레드는 작업이 완료될 때까지 해제됨으로써 반응성 있는 프로그램 흐름을 만듭니다.

또한 비동기 메서드를 정의할 때는 async void를 주로 이벤트 핸들러, 예를 들어 GUI 애플리케이션의 버튼 클릭에만 사용해야 합니다. 이 경우, 메서드 시그니처에는 대개 (object sender, EventArgs e)가 포함되며, 여기서 object sender는 이벤트의 출처를 식별합니다. 이벤트 핸들러 외부에서 async void를 사용하는 것은 예측할 수 없는 동작과 해결하기 어려운 버그로 이어질 수 있으므로 대부분의 비동기 메서드에는 async Task를 선호하세요.

Zip에 파일 선택적으로 추가하기

때때로, 개발자는 전체 폴더를 압축하는 것보다 더 많은 제어가 필요합니다. Tim은 FileStream을 생성하여 선택적으로 비동기 작업을 수행하는 방법을 시연합니다:

await using FileStream zipStream = new FileStream(
    destinationZipFile, 
    FileMode.Create, 
    FileAccess.Write, 
    FileShare.None, 
    bufferSize: 4096, 
    useAsync: true
);

파일을 비동기로 추가할 때, 각 파일 추가는 특정 작업을 나타냅니다. 파일 추가 작업을 동시에 시작하고, Task.WhenAll과 같은 메서드를 사용하여 완료를 기다림으로써 성능을 크게 향상시킬 수 있습니다. 이 방법으로 실행 중인 작업을 관리하는 것은 한 번에 여러 요청을 처리해야 하는 서버와 같은 고부하 동시 처리 시나리오에서 필수입니다. 이 방법은 원격 서버와 상호작용할 때 종종 사용되며, 데이터를 기다리는 동안 애플리케이션이 반응성을 유지할 수 있게 합니다.

Tim은 각 세부 사항을 설명합니다:

  • FileMode.Create – 새로운 zip 파일을 생성하거나 덮어씁니다.

  • FileAccess.Write – 파일 쓰기를 허용합니다.

  • FileShare.None – 다른 작업이 파일을 접근하지 못하게 합니다.

  • useAsync: true – 비동기 파일 쓰기를 가능하게 하며, 핵심 비동기 작업입니다.

그는 이 문맥에서 비동기 프로그래밍이 주 작업이 완료될 때까지 메인 스레드를 차단하는 것을 피하게 하여 특히 여러 작업을 처리하는 웹 서버에 유용하다고 언급합니다. 고부하 동시 서버는 비동기 프로그래밍을 활용하여 수많은 동시 클라이언트 요청을 효율적으로 처리하며, 각 연결마다 새로운 스레드를 생성하지 않습니다. 그러나 비동기 코드를 순서대로 작성하면 비동기 작업이 순서대로 끝나지 않으므로 읽기와 디버깅을 어렵게 만들 수 있습니다.

Zip 보관 파일 생성 및 파일 추가하기

그다음 Tim은 ZipArchive를 비동기로 생성합니다:

using ZipArchive archive = await ZipArchive.CreateAsync(
    zipStream, 
    ZipArchiveMode.Create, 
    leaveOpen: false, 
    entryNameEncoding: null
);
  • await 표현은 작업이 끝날 때까지 실행이 멈추는 것을 보장하며, 파일을 추가하는 것에 대한 안전한 진행을 만듭니다. Async와 await는 비동기 작업 관리를 쉽게 하므로 전통적인 콜백 기반 접근법과 비교할 때 코드가 더 읽기 쉽고 유지보수가 용이합니다.

  • 동기화 컨텍스트에 따라 동일한 코드가 다르게 작동할 수 있음을 주의하세요. 예를 들어, WPF 또는 WinForms 애플리케이션에서는 await 호출 후의 연결이 메인 UI 스레드에서 실행될 수 있는 동기화 컨텍스트가 존재하지만, 콘솔 애플리케이션에서는 그런 컨텍스트가 없습니다. 이는 비동기 코드 실행에 영향을 미칠 수 있습니다.

  • 라이브러리 코드를 작성할 때는 await 호출 후 ConfigureAwait(false)를 사용하여 불필요한 컨텍스트 전환을 방지하고 성능을 개선하는 것을 고려하세요.

  • Path.GetRelativePath를 사용하여 Tim은 여러 파일을 사용하는 비동기 워크플로에서 폴더 구조를 zip 내부에 유지하는 방법을 설명합니다:
foreach (string filePath in Directory.GetFiles(sourceDirectory, "*", SearchOption.AllDirectories))
{
    string relativePath = Path.GetRelativePath(sourceDirectory, filePath);
    await archive.CreateEntryFromFileAsync(filePath, relativePath).ConfigureAwait(false);
}

이것은 비동기 작업에서 각 파일 추가가 비동기 호출이라는 것을 보여주며, 여전히 비차단 I/O를 수행하면서 await 키워드가 적절한 순서를 보장합니다. 비동기 코드는 현재 스레드를 차단하지 않게 함으로써 반응성을 개선하고, I/O 작업이 완료되는 동안 스레드가 다른 작업을 수행할 수 있게 합니다.

파일 접근 및 예외 처리 다루기

Tim은 일반적인 예외 처리 시나리오에 대해 설명합니다: 원래 FileStream 작업이 완료되기 전에 zip을 추출하려고 시도할 때입니다. 파일 범위의 비동기 void 메서드 또는 적절한 범위의 using 문이 없는 비동기 Task 메서드를 사용할 때 발생할 수 있음:

해당 파일에 대한 접근이 거부되었습니다. 다른 프로세스가 파일을 사용 중입니다.

솔루션은 비동기 작업을 전통적인 범위의 using 블록으로 감싸 자원을 해제한 후 파일을 추출하는 것입니다:

await using (FileStream zipStream = new FileStream(...))
{
    using ZipArchive archive = await ZipArchive.CreateAsync(zipStream, ...);
    // 파일을 비동기적으로 추가합니다
}
// 이제 안전하게 추출할 수 있습니다

이는 작업 객체가 완료되고 동기화 컨텍스트를 해제하여 파일 잠금 오류를 방지합니다.

결론: .NET 10에서의 효율적인 비동기 Zip

팁은 .NET 10에서 비동기 zip 작업이 개발자가 비동기 작업을 효율적으로 수행하고, 메인 스레드를 차단하지 않으며, 네트워크 호출이나 데이터베이스 쿼리와 같은 다른 작업과 원활하게 통합할 수 있게 한다고 결론지었습니다.

정적 비동기 Task 메서드, async 수정자 및 await 표현식을 사용하여 개발자는 다음을 수행할 수 있습니다:

  • UI 스레드를 멈추지 않고 zip 파일을 압축하거나 추출합니다.

  • 수동 스레드 풀 관리 없이 여러 스레드를 안전하게 처리합니다.

  • 폴더 구조를 유지하며 선택적으로 압축합니다.

  • 적절한 예외 처리와 리소스 정리를 보장합니다.

팁의 예시는 파일 작업에서 async/await가 편리할 뿐만 아니라 C#의 현대 비동기 프로그래밍의 필수 요소임을 보여줍니다.

그의 비디오를 따라 개발자는 async 메서드, await 호출 및 비동기 워크플로를 사용하여 복잡하고 간단한 파일 압축 작업을 원활하게 처리할 수 있는 효율적인 코드를 작성할 수 있습니다.

Hero Worlddot related to Async C#: Tim Corey와 함께 비동기 Zip에 대한 심층 탐구
Hero Affiliate related to Async C#: Tim Corey와 함께 비동기 Zip에 대한 심층 탐구

사랑하는 것을 공유하여 더 많은 수익을 얻으세요

당신은 .NET, C#, Java, Python, 또는 Node.js를 다루는 개발자를 위한 콘텐츠를 만드나요? 당신의 전문성을 추가 수입으로 전환하세요!

아이언 서포트 팀

저희는 주 5일, 24시간 온라인으로 운영합니다.
채팅
이메일
전화해