CodeGym /행동 /C# SELF /파일 예외 처리 ( try-catch

파일 예외 처리 ( try-catch)

C# SELF
레벨 38, 레슨 1
사용 가능

1. 소개

파일 작업은 거의 항상 프로그램 외부의 상황과 얽혀 있습니다: 파일이 삭제되거나 이동될 수 있고, 다른 프로그램이 잠가둘 수 있으며, 사용자의 권한이 부족하거나 디스크에 빈 공간이 없을 수 있습니다. 이런 상황은 예외를 발생시킬 수 있습니다 — 프로그램 실행이 중단되며 예외 객체(Exception)가 "던져집니다". 이런 예외를 잡아 처리하지 않으면 프로그램은 오류로 종료되고 콘솔에 무서운 빨간 텍스트를 보여주거나(더 나쁘게는) 조용히 다운됩니다.

C#에서는 이런 상황을 처리하기 위해 try-catch 블록(간단히 말해 "에러 트랩")을 사용합니다. 이 블록은 오류를 "잡는" 것뿐 아니라, 이후에 무엇을 할지 결정할 수 있게 해줍니다: 친절한 메시지를 보여주거나 다른 파일을 선택하게 하거나, 로그에 기록하거나 단순히 재시도할 수 있습니다.

실제 애플리케이션에서

콘솔 계산기 같은 단순한 프로그램이라면 파일 작업에서 오류를 잡지 않아도 될 때가 있지만, 회사에서 문서를 처리하는 프로그램이나 진행 상태를 저장하는 게임이라면 이런 오류를 처리하지 않는 건 안전장치 없이 줄타기하는 것과 같습니다: 언젠가 반드시 문제가 생깁니다.

2. 예외 처리 복습

기본 문법 try-catch

파일과 관련된 구체적 상황으로 들어가기 전에 try-catch 구문의 기본 문법을 복습해봅시다(이 구문은 이미 예외 강의에서 봤을 겁니다):


try
{
    // 여기에 예외를 "던질" 수 있는 코드가 옵니다
}
catch (Exception ex)
{
    // 여기서 모든 예외를 잡습니다... 하지만 이렇게 하면 안 좋아요!
    Console.WriteLine("무언가 잘못됐습니다: " + ex.Message);
}

try 안에는 잠재적으로 위험한 코드를 쓰고, catch 안에는 문제가 생겼을 때 무엇을 할지 적습니다.

프로그래머 유머: "try-catch는 오류용 투명 망토 같은 거야. 오류는 있지만 너는 못 봐."

파일 관련 예외들

.NET으로 파일 작업을 할 때 보통 만나는 예외들 — 예를 들어 스트림을 만들거나 읽기/쓰기를 할 때 발생하는 것들 — 은 다음과 같습니다:

  • 파일을 찾을 수 없음 → FileNotFoundException
  • 파일/디렉터리에 대한 접근 권한 없음 → UnauthorizedAccessException
  • 경로 문제(예: 허용되지 않는 문자, 너무 긴 경로) → PathTooLongException, ArgumentException
  • 파일이 다른 프로세스에 의해 사용 중 → IOException
  • 디스크에 빈 공간 없음 → IOException

파일 작업과 관련된 예외들은 보통 IOException에서 파생된 것들입니다. 이들은 모두 기본 타입인 System.Exception을 상속합니다.

3. 실습: 파일 작업 중 오류 잡기 및 처리

우리의 "성장하는 애플리케이션" 예제로 몇 가지 전형적인 경우를 살펴봅시다: 인사말을 파일에서 읽어 처리해 사용자에게 출력한다고 가정하고, try-catch로 오류를 체크합니다.

예제 1: "파일 없음" 처리


string filePath = "hello.txt";

try
{
    string greeting = File.ReadAllText(filePath);
    Console.WriteLine("파일 내용: " + greeting);
}
catch (FileNotFoundException ex)
{
    Console.WriteLine("파일을 찾을 수 없습니다! 파일 " + filePath + " 가 존재하는지 확인하세요.");
    // 추가로 상세 정보를 출력할 수 있습니다
    Console.WriteLine("기술적 세부사항: " + ex.Message);
}

파일 "hello.txt"가 없으면, 프로그램은 오류로 종료되지 않고 친절한 메시지를 출력합니다. 이렇게 하면 사용자의 실수에 좀 더 강해집니다.

예제 2: 접근 권한 문제

이번에는 파일은 있지만 읽을 권한이 없는 상황을 보겠습니다(예: 누군가가 읽기 권한을 막아두었거나 파일이 보호된 폴더에 있음).


try
{
    string secret = File.ReadAllText("C:\\Windows\\System32\\config.txt");
}
catch (UnauthorizedAccessException ex)
{
    Console.WriteLine("파일에 접근할 수 없습니다! 관리자 권한으로 프로그램을 실행해 보세요.");
    Console.WriteLine("기술적 원인: " + ex.Message);
}

예제 3: 일반적인 IOException

다른 프로세스가 파일을 잡고 있거나, 디스크 공간 부족 또는 하드웨어 문제와 관련된 오류들은 보통 IOException으로 처리합니다:


try
{
    File.WriteAllText("important.txt", "중요한 정보!");
}
catch (IOException ex)
{
    Console.WriteLine("파일 작업 중 오류가 발생했습니다: 아마도 다른 프로그램이 파일을 사용 중이거나 디스크에 여유 공간이 부족합니다.");
    Console.WriteLine("기술적 원인: " + ex.Message);
}

4. 여러 예외 처리: 선택적 대응

때로는 예외 타입에 따라 다르게 대응해야 합니다. .NET에서는 여러 개의 catch 블록을 순서대로(특정한 것에서 일반적인 것 순으로) 적을 수 있습니다(그렇지 않으면 컴파일러가 지적합니다).


try
{
    string content = File.ReadAllText("file.txt");
    Console.WriteLine(content);
}
catch (FileNotFoundException ex)
{
    Console.WriteLine("파일을 찾을 수 없습니다.");
}
catch (UnauthorizedAccessException ex)
{
    Console.WriteLine("파일에 접근 권한이 없습니다!");
}
catch (IOException ex)
{
    Console.WriteLine("기타 입출력 오류: " + ex.Message);
}

중요: 만약 catch (Exception ex)를 맨 앞에 두면 이후 블록들은 의미가 없어집니다. 기본 타입이 모든 것을 잡아버리기 때문이죠!

5. 중첩된 try-catch와 재시도

가끔은 오류를 처리하는 것뿐만 아니라 사용자가 "수정"할 기회를 주고 싶을 때가 있습니다 — 예를 들어 존재하는 파일 경로를 다시 입력하도록 유도하는 경우:


string filePath;
string content;
int attempts = 0;
const int maxAttempts = 3;

do
{
    Console.Write("파일 경로를 입력하세요: ");
    filePath = Console.ReadLine();

    try
    {
        content = File.ReadAllText(filePath);
        Console.WriteLine("파일 내용:\n" + content);
        break;
    }
    catch (FileNotFoundException)
    {
        Console.WriteLine("파일을 찾을 수 없습니다! 다시 시도하세요.");
    }
    catch (UnauthorizedAccessException)
    {
        Console.WriteLine("파일에 접근 권한이 없습니다! 다른 파일을 시도하세요.");
    }
    attempts++;
}
while (attempts < maxAttempts);

if (attempts == maxAttempts)
    Console.WriteLine("너무 많은 실패한 시도입니다.");

이 코드는 파일 경로를 잊어버린 사용자에게 친절한 작은 "인터페이스" 역할을 합니다.

6. 흔한 실수와 주의할 점: 게으름에서 책임감으로

초보자(심지어 경험 있는 개발자도)의 흔한 실수는 모든 예외를 무분별하게 잡는 것입니다: 단순히 catch (Exception) 를 쓰고 "오류가 발생했습니다!"만 출력하는 식이죠. 이런 방식은 문제가 여러 가지 이유로 나쁩니다. 첫째, 실제 비즈니스 로직의 결함을 숨깁니다. 둘째, 파일과 무관한 다른 오류들(예: 코드 오타나 수학적 오류)이 무심코 무시되어 진짜 원인 찾는 데 시간이 오래 걸립니다.

더 나은 방법은 당신이 의미 있게 처리할 수 있는 예외만 명시적으로 잡는 것입니다. 어떤 오류가 났는지 확실하지 않다면, 잡지 않고 예외가 터지게 두는 편이 낫습니다: 프로그램이 실패하면 호출 스택을 보고 무엇을 고쳐야 하는지 바로 알 수 있습니다.

특이사항: 일부 예외는 내부 원인(InnerException)을 가지고 있을 수 있습니다. 로그를 작성할 때 이걸 분석하면 문제를 더 잘 진단할 수 있습니다.

또 다른 포인트 — catch 블록 이후에 프로그램이 계속 정상적으로 동작할 수 없다면(예: 주요 설정 파일을 열지 못한 경우) return으로 종료하거나 예외를 다시 던져서(throw;) "반쪽짜리" 기능만 가진 좀비 프로그램이 돌아다니지 않게 하는 것이 낫습니다.

2
과제
C# SELF, 레벨 38, 레슨 1
잠금
여러 예외 처리: 파일에 쓰기
여러 예외 처리: 파일에 쓰기
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION