C# 6편: 예외 처리와 디버깅으로 안정적인 프로그램 작성하기
1. 예외(Exception)란?
프로그램을 실행하다 보면 의도치 않은 오류가 발생할 수 있습니다. 이러한 오류를 **예외(Exception)**라고 부르며, 예외가 발생하면 프로그램이 비정상적으로 종료될 수 있습니다. C#에서는 이러한 예외를 처리하여 프로그램이 안전하게 종료되거나, 적절한 대응을 할 수 있도록 도와줍니다.
예외 처리는 try-catch-finally 블록을 사용하여 구현할 수 있습니다.
일반적인 예외 상황
- 사용자가 입력한 값이 잘못된 경우
- 네트워크 연결 실패
- 파일을 찾을 수 없는 경우
- 데이터베이스 오류 등
2. try-catch 구문
try-catch 구문은 프로그램에서 발생할 수 있는 예외를 처리하기 위한 구조입니다.
- try: 예외가 발생할 수 있는 코드를 포함합니다.
- catch: 예외가 발생했을 때의 처리 코드를 작성합니다.
기본 구문
try
{
// 예외가 발생할 가능성이 있는 코드
}
catch (Exception ex)
{
// 예외가 발생했을 때 실행될 코드
Console.WriteLine(ex.Message);
}
예시: 0으로 나누기 예외 처리
try
{
int number = 10;
int divisor = 0;
int result = number / divisor; // 0으로 나누기 예외 발생
}
catch (DivideByZeroException ex)
{
Console.WriteLine("0으로 나눌 수 없습니다: " + ex.Message);
}
위 코드는 0으로 나누려 할 때 DivideByZeroException 예외가 발생하고, 이를 catch 블록에서 처리합니다.
3. 여러 예외 처리
C#에서는 다양한 예외 타입을 처리할 수 있도록 다중 catch 블록을 사용할 수 있습니다. 각 catch 블록에서 다른 유형의 예외를 처리할 수 있습니다.
예시: 여러 예외 처리
try
{
int[] numbers = { 1, 2, 3 };
Console.WriteLine(numbers[10]); // IndexOutOfRangeException 발생
}
catch (IndexOutOfRangeException ex)
{
Console.WriteLine("인덱스 범위를 벗어났습니다: " + ex.Message);
}
catch (DivideByZeroException ex)
{
Console.WriteLine("0으로 나눌 수 없습니다.");
}
catch (Exception ex)
{
Console.WriteLine("예상치 못한 오류가 발생했습니다: " + ex.Message);
}
여기서 IndexOutOfRangeException과 DivideByZeroException을 각각 처리하고, 그 외의 예외는 마지막 catch 블록에서 처리합니다.
4. finally 블록
finally 블록은 예외 발생 여부와 상관없이 항상 실행되는 코드를 작성할 수 있는 영역입니다. 주로 파일, 네트워크 연결 해제 또는 자원 해제와 같은 작업을 할 때 사용합니다.
예시: finally 블록 사용
try
{
int number = 10;
int divisor = 0;
int result = number / divisor;
}
catch (DivideByZeroException ex)
{
Console.WriteLine("0으로 나눌 수 없습니다.");
}
finally
{
Console.WriteLine("프로그램이 종료됩니다.");
}
finally 블록은 예외가 발생하든 그렇지 않든 실행됩니다.
5. 사용자 정의 예외
C#에서는 기본적으로 제공되는 예외 클래스 외에, 필요에 따라 사용자 정의 예외를 만들 수 있습니다. 사용자 정의 예외는 Exception 클래스를 상속받아 만듭니다.
사용자 정의 예외 클래스
class InvalidAgeException : Exception
{
public InvalidAgeException(string message) : base(message)
{
}
}
사용자 정의 예외 사용 예시
class Person
{
public int Age { get; set; }
public void SetAge(int age)
{
if (age < 0)
{
throw new InvalidAgeException("나이는 음수가 될 수 없습니다.");
}
Age = age;
}
}
class Program
{
static void Main(string[] args)
{
Person person = new Person();
try
{
person.SetAge(-1); // 사용자 정의 예외 발생
}
catch (InvalidAgeException ex)
{
Console.WriteLine(ex.Message);
}
}
}
이 코드는 나이를 음수로 설정하려고 할 때 InvalidAgeException을 던집니다.
6. 예외 던지기(throw)
throw 키워드를 사용하여 예외를 명시적으로 발생시킬 수 있습니다. 예외를 발생시켜 호출자에게 처리하도록 하거나, 사용자 정의 예외를 던질 때 사용됩니다.
예시: 예외 던지기
int Divide(int a, int b)
{
if (b == 0)
{
throw new DivideByZeroException("0으로 나눌 수 없습니다.");
}
return a / b;
}
try
{
int result = Divide(10, 0);
}
catch (DivideByZeroException ex)
{
Console.WriteLine(ex.Message);
}
7. 디버깅(Debugging)
디버깅은 프로그램에서 발생하는 문제를 찾아 수정하는 과정입니다. C#에서는 Visual Studio와 같은 개발 도구를 통해 디버깅 기능을 제공합니다.
디버깅 기법
- 중단점(Breakpoint): 프로그램 실행 중 특정 지점에서 중지하여 변수 값을 확인하고, 코드의 흐름을 추적할 수 있습니다.
- 단계별 실행(Step Over/Into/Out): 코드를 한 줄씩 실행하거나, 함수 내로 들어가거나 빠져나오는 등의 제어를 할 수 있습니다.
- 워치(Watch): 특정 변수의 값을 모니터링하고, 그 변수가 어떻게 변하는지 추적합니다.
- 로컬 변수 창: 현재 스코프 내의 모든 변수를 모니터링하고, 그 값을 실시간으로 확인할 수 있습니다.
중단점 설정
Visual Studio에서 중단점을 설정하려면 코드 왼쪽 줄 번호를 클릭하면 됩니다. 실행 중 해당 줄에 도달하면 프로그램이 일시 중단됩니다.
8. 실습 예제: 예외 처리와 디버깅
다음은 사용자 입력을 받아 나누기 연산을 수행하고, 예외 처리 및 디버깅 기능을 사용하는 예제입니다.
using System;
class Program
{
static void Main(string[] args)
{
try
{
Console.Write("첫 번째 숫자를 입력하세요: ");
int num1 = Convert.ToInt32(Console.ReadLine());
Console.Write("두 번째 숫자를 입력하세요: ");
int num2 = Convert.ToInt32(Console.ReadLine());
int result = Divide(num1, num2);
Console.WriteLine($"결과: {result}");
}
catch (FormatException ex)
{
Console.WriteLine("숫자가 아닌 값을 입력했습니다. " + ex.Message);
}
catch (DivideByZeroException ex)
{
Console.WriteLine("0으로 나눌 수 없습니다. " + ex.Message);
}
finally
{
Console.WriteLine("프로그램이 종료됩니다.");
}
}
static int Divide(int a, int b)
{
return a / b;
}
}
- FormatException: 사용자가 숫자가 아닌 값을 입력했을 때 처리.
- DivideByZeroException: 0으로 나누려 할 때 처리.
- finally: 예외와 상관없이 항상 프로그램 종료 메시지 출력.
9. 디버깅 실습
- 중단점 설정: Divide 메서드가 호출되는 줄에 중단점을 설정합니다.
- 단계별 실행: 디버깅 모드에서 프로그램을 한 줄씩 실행하여 Divide 메서드의 입력 값과 결과를 추적합니다.
- 변수 확인: 로컬 변수 창 또는 워치를 사용해 a와 b 변수의 값을 실시간으로 확인합니다.
10. 요약
- try-catch-finally 구문을 사용하여 프로그램의 예외를 안전하게 처리할 수 있습니다.
- throw 키워드를 사용하여 명시적으로 예외를 발생시키거나 사용자 정의 예외를 던질 수 있습니다.
- 디버깅은 코드의 오류를 찾고 수정하는 중요한 과정으로, 중단점과 변수 추적 등을 통해 프로그램 흐름을 분석할 수 있습니다.
다음 편에서는 C#에서 **파일 입출력(IO)**을 다루며, 파일을 읽고 쓰는 방법에 대해 학습하겠습니다.