🔎 유용한 정보
1. 강의 개요
이번 강의에서는 Task와 BackgroundWorker를 활용해 비동기 파일 처리를 구현합니다.
비동기 프로그래밍은 긴 작업(예: 대용량 파일 읽기, 복사, 다운로드 등)이 실행되는 동안
UI가 멈추지 않고 응답성을 유지하도록 도와줍니다.
실습에서는 대용량 파일 복사를 예제로 들어 비동기 처리의 장점을 배워봅니다.
2. 학습 목표
- Task와 async/await를 사용한 비동기 작업 구현
- BackgroundWorker를 활용한 비동기 작업 구현
- 작업 중 진행 상황(Progress)을 ProgressBar로 표시
- 비동기 작업 중 UI 응답성 유지
3. 기능 요구사항
필수 기능
1️⃣ 파일 복사 작업:
- 원본 파일에서 대상 파일로 비동기 복사
2️⃣ 진행 상황 표시:
- ProgressBar를 통해 복사 진행 상황 표시
3️⃣ UI 응답성 유지:
- 파일 복사 작업 중에도 버튼 클릭 등 UI 작업 가능
4️⃣ 취소 기능:
- 작업 중 취소 버튼으로 파일 복사 중단
4. 실습: 비동기 파일 복사 애플리케이션 제작
1️⃣ 폼 구성
- 폼(Form) 이름: Form1
- 컨트롤 배치
컨트롤 타입 이름 위치 크기
TextBox | txtSourcePath | 폼 상단 왼쪽 | (300 x 30) |
Button | btnBrowseSource | 폼 상단 오른쪽 | (100 x 30) |
TextBox | txtDestinationPath | 폼 중앙 왼쪽 | (300 x 30) |
Button | btnBrowseDestination | 폼 중앙 오른쪽 | (100 x 30) |
Button | btnStart | 폼 하단 왼쪽 | (100 x 30) |
Button | btnCancel | 폼 하단 오른쪽 | (100 x 30) |
ProgressBar | progressBar | 폼 하단 중앙 | (400 x 30) |
📌 폼 디자인 예시:
--------------------------------------------------
| [Source Path: TextBox] [Browse Source 버튼] |
--------------------------------------------------
| [Destination Path: TextBox] [Browse Dest 버튼] |
--------------------------------------------------
| [Start 버튼] [Cancel 버튼] |
--------------------------------------------------
| [ProgressBar] |
--------------------------------------------------
2️⃣ 코드 작성
(1) 파일 복사를 위한 Task 구현
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApp_AsyncFileCopy
{
public partial class Form1 : Form
{
private CancellationTokenSource _cancellationTokenSource;
public Form1()
{
InitializeComponent();
}
// 파일 복사 시작
private async void btnStart_Click(object sender, EventArgs e)
{
string sourcePath = txtSourcePath.Text.Trim();
string destinationPath = txtDestinationPath.Text.Trim();
if (string.IsNullOrEmpty(sourcePath) || string.IsNullOrEmpty(destinationPath))
{
MessageBox.Show("소스 및 대상 경로를 입력하세요.", "오류", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
_cancellationTokenSource = new CancellationTokenSource();
progressBar.Value = 0;
try
{
await CopyFileAsync(sourcePath, destinationPath, _cancellationTokenSource.Token);
MessageBox.Show("파일 복사가 완료되었습니다.", "완료", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
catch (OperationCanceledException)
{
MessageBox.Show("파일 복사가 취소되었습니다.", "취소됨", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
catch (Exception ex)
{
MessageBox.Show($"오류 발생: {ex.Message}", "오류", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
finally
{
_cancellationTokenSource.Dispose();
_cancellationTokenSource = null;
}
}
// 비동기 파일 복사
private async Task CopyFileAsync(string sourcePath, string destinationPath, CancellationToken cancellationToken)
{
const int bufferSize = 81920; // 80KB 버퍼
var buffer = new byte[bufferSize];
using (var sourceStream = new FileStream(sourcePath, FileMode.Open, FileAccess.Read))
using (var destinationStream = new FileStream(destinationPath, FileMode.Create, FileAccess.Write))
{
long totalBytes = sourceStream.Length;
long totalCopied = 0;
int bytesRead;
while ((bytesRead = await sourceStream.ReadAsync(buffer, 0, buffer.Length, cancellationToken)) > 0)
{
await destinationStream.WriteAsync(buffer, 0, bytesRead, cancellationToken);
totalCopied += bytesRead;
// ProgressBar 업데이트
int progress = (int)((double)totalCopied / totalBytes * 100);
progressBar.Invoke(new Action(() => progressBar.Value = progress));
cancellationToken.ThrowIfCancellationRequested(); // 취소 요청 확인
}
}
}
// 복사 취소
private void btnCancel_Click(object sender, EventArgs e)
{
_cancellationTokenSource?.Cancel();
}
}
}
(2) 파일 경로 선택 기능
// 소스 파일 선택
private void btnBrowseSource_Click(object sender, EventArgs e)
{
using (OpenFileDialog openFileDialog = new OpenFileDialog())
{
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
txtSourcePath.Text = openFileDialog.FileName;
}
}
}
// 대상 경로 선택
private void btnBrowseDestination_Click(object sender, EventArgs e)
{
using (SaveFileDialog saveFileDialog = new SaveFileDialog())
{
if (saveFileDialog.ShowDialog() == DialogResult.OK)
{
txtDestinationPath.Text = saveFileDialog.FileName;
}
}
}
(3) Designer 코드
private void InitializeComponent()
{
this.txtSourcePath = new TextBox();
this.btnBrowseSource = new Button();
this.txtDestinationPath = new TextBox();
this.btnBrowseDestination = new Button();
this.btnStart = new Button();
this.btnCancel = new Button();
this.progressBar = new ProgressBar();
// Source Path TextBox 설정
this.txtSourcePath.Location = new System.Drawing.Point(10, 10);
this.txtSourcePath.Size = new System.Drawing.Size(300, 30);
// Browse Source Button 설정
this.btnBrowseSource.Location = new System.Drawing.Point(320, 10);
this.btnBrowseSource.Size = new System.Drawing.Size(100, 30);
this.btnBrowseSource.Text = "Browse Source";
this.btnBrowseSource.Click += new EventHandler(this.btnBrowseSource_Click);
// Destination Path TextBox 설정
this.txtDestinationPath.Location = new System.Drawing.Point(10, 50);
this.txtDestinationPath.Size = new System.Drawing.Size(300, 30);
// Browse Destination Button 설정
this.btnBrowseDestination.Location = new System.Drawing.Point(320, 50);
this.btnBrowseDestination.Size = new System.Drawing.Size(100, 30);
this.btnBrowseDestination.Text = "Browse Destination";
this.btnBrowseDestination.Click += new EventHandler(this.btnBrowseDestination_Click);
// Start Button 설정
this.btnStart.Location = new System.Drawing.Point(10, 100);
this.btnStart.Size = new System.Drawing.Size(100, 30);
this.btnStart.Text = "Start";
this.btnStart.Click += new EventHandler(this.btnStart_Click);
// Cancel Button 설정
this.btnCancel.Location = new System.Drawing.Point(120, 100);
this.btnCancel.Size = new System.Drawing.Size(100, 30);
this.btnCancel.Text = "Cancel";
this.btnCancel.Click += new EventHandler(this.btnCancel_Click);
// ProgressBar 설정
this.progressBar.Location = new System.Drawing.Point(10, 150);
this.progressBar.Size = new System.Drawing.Size(400, 30);
// Form 설정
this.ClientSize = new System.Drawing.Size(450, 200);
this.Controls.Add(this.txtSourcePath);
this.Controls.Add(this.btnBrowseSource);
this.Controls.Add(this.txtDestinationPath);
this.Controls.Add(this.btnBrowseDestination);
this.Controls.Add(this.btnStart);
this.Controls.Add(this.btnCancel);
this.Controls.Add(this.progressBar);
this.Text = "비동기 파일 복사";
}
3️⃣ 실행 결과
1️⃣ 파일 선택 및 경로 설정
- 소스 및 대상 파일 경로를 선택
2️⃣ 비동기 파일 복사
- "Start" 버튼 클릭 시 파일 복사 시작 → ProgressBar로 진행 상황 표시
3️⃣ 복사 취소
- "Cancel" 버튼 클릭 시 복사 작업 중단
4️⃣ UI 응답성 유지
- 복사 중에도 UI 작업 가능
5. 주요 개념 요약
- Task: 비동기 작업 처리를 위한 클래스
- CancellationToken: 비동기 작업 취소를 위한 토큰
- ProgressBar: 작업 진행 상황을 시각적으로 표시
- FileStream: 파일 읽기/쓰기 스트림
'📁 [4] 개발자 정보 & 코드 노트 > C#' 카테고리의 다른 글
C# Windows Forms 강의 72편: WebSocket을 활용한 실시간 양방향 통신 (0) | 2025.04.15 |
---|---|
C# Windows Forms 강의 71편: 비동기 네트워크 통신 - HTTP 요청 및 응답 처리 (0) | 2025.04.14 |
C# Windows Forms 강의 69편: 파일 탐색기 애플리케이션 제작 (TreeView와 ListView 활용) (0) | 2025.04.12 |
C# Windows Forms 강의 67편: CSV 데이터를 읽고 쓰기 - 테이블 형식 데이터 관리 (0) | 2025.04.11 |
C# Windows Forms 강의 66편: JSON 데이터를 읽고 쓰기 - 파일 관리 애플리케이션 구현 (0) | 2025.04.10 |
🔎 유용한 정보