1. 예외 처리 이란 ( Exception Handling)


우리가 인생을 살아가다보면 예상치 못한 일들이 일어나기 마련입니다.

감기에 걸려 출근을 못할수도 있고 물웅덩이에서 물이 튀어 새옷을 버릴수도 있습니다.

이러한 예상치 못한 일들을 겪다보면 자연스럽게 대처하는 방법들이 있습니다.


프로그래밍에서도 마찬가지로 예상치 못한 일들이 일어나기 마련입니다.

더군다나 여러사람이 함께 프로젝트를 진행하다보면 더 많은 일들이 일어나기 마련이지요.

게임을 하던중 예기치 못한 오류가 발생해 프로그램이 종료되는 상황들을 겪어 보셨나요?


이처럼 예외 상황이 발생하였을때 프로그램 종료없이 유연하게 처리하는 것을 예외 처리라 합니다.


2. try~catch, throw, finally


try~catch

앞선 강좌에서 goto 문을 활용하여 어디서든 코드를 점프하여 다음 코드들을 실행하는 것을 접해봤습니다.

이것과 마찬가지로 예외가 발생할 만한 코드들이 있는곳 또는 예외가 발생해서는 안되는 코드에 try 키워드를 

써서 해당 코드들을 블럭( {...} )안에 넣어 놓으면 예외 발생시 예외 상황을 (시도하다) catch 키워드가 있는 곳에서

(받다) 예외상황에 대처할 수 있도록 합니다.


서식 형식

try
{
    //....
}
catch( [예외 객체 1] )
{
}
catch( [예외 객체 2] )
{
}
...


소스코드
using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            int[] arr = { 1, 2, 3 };

            try
            {
                //int[] 배열의 크기를 넘은 인덱스를 사용하여 예외 발생
                arr[4] = 4;
            }
            catch (DivideByZeroException e)
            {
                Console.WriteLine("예외 발생 : {0}", e.Message);
            }
            catch (IndexOutOfRangeException e) // 배열의 크기에 대한 예외 상황 발생시 받는 곳
            {
                Console.WriteLine("예외 발생 : {0}", e.Message);
            }

            try
            {
                //상수를 0으로 나누려하여 예외 발생
                int a = 10;
                a = a / 0;
            }
            catch (IndexOutOfRangeException e)
            {
                Console.WriteLine("예외 발생 : {0}", e.Message);
            }
            catch (DivideByZeroException e) // 0으로 나누기를 하였을때 예외 상황을 받는 곳
            {
                Console.WriteLine("예외 발생 : {0}", e.Message);
            }

            try
            {
                arr[4] = 4;
            }
            //모든 예외상황은 System.Exception 클래스를 상속받아 파생된 클래스입니다.
            //하나의 catch 만으로 모두 처리 할수 있습니다.
            catch(Exception e)
            {
                Console.WriteLine("예외 발생 : {0}", e.Message);
            }
        }
    }
}

결과

예외 발생 : 인덱스가 배열 범위를 벗어났습니다.
예외 발생 : 0으로 나누려 했습니다.
예외 발생 : 인덱스가 배열 범위를 벗어났습니다.


소스코드의 주석의 내용과 같이 모든 예외는 System.Exception으로 부터 파생되어 만들어져 있습니다.

Exception 클래스 만으로 예외상황에 대응 할수 있지만 경우에 따라서는 정확한 처리가 필요할때는 대응이 어려울수

있습니다. 따라서 Exception 만으로 대응 하는것은 자제 하는것이 좋고 올바른 예외 처리를 하기 위해 명확하게 사용하는

것이 좋습니다.


때로는 라이브중인 프로그램에서 최악의 상황(강제 종료)은 피하기위해 예외처리를 설정하고 버그를 수정하기 전까지

유지하는 경우도 있습니다.



throw

catch 키워드로 예외를 받는 다는 것은 어디선가는 예외 상황을 던지고 있다는 말이되고 이는 throw 키워드를 이용하여

직접 예외 상황을 발생 시킬수 있습니다.

다음 소스코드를 참조 하시기 바랍니다.


소스 코드

using System;

namespace ConsoleApp1
{
    class Program
    {
        static int password = 1234;
        static void Main(string[] args)
        {
            try
            {
                CheckPassword(333);
                CheckPassword(1234);
            }
            catch(Exception e)
            {
                Console.WriteLine("예외 발생 : {0}", e.Message);
            }
        }
        static void CheckPassword(int pass)
        {
            if (password != pass)
                throw new Exception("패스워드가 틀립니다.");
            else
                Console.WriteLine("비밀번호 인증완료!");
        }
    }
}

결과

예외 발생 : 패스워드가 틀립니다.


이처럼 프로그래머가 직접 특정 상황에 맞게 예외를 던지고 받을수 있습니다.

 

finally

finally는 try 블럭이 실행된다면 예외가 발생하든 하지 않던 어떤 경우에라도 반듯이 실행되는 키워드 입니다.

할당 받은 100개의 자원들을 사용중에 예외 상황이 발생하여 미처 사용중이던 자원을 해지 하지 못해 사용할수 없는

상황이 발생한다면 사용 할수있는 자원의 갯수는 줄어들고 잠재적 버그를 가지고 사용하게 되는 상황이 발생 할수도

있습니다. 그렇다고 모든 catch 문에서 해지하는 코드를 넣는것 또한 바람직 하지 못한 코드가 됩니다.

이럴때 사용하는 것이 finally 입니다.


소스코드

using System;

namespace ConsoleApp1
{
    class Program
    {
        class NameObject
        {
            bool IsUse = false;
            public bool USE
            {
                get { return IsUse; }
            }
            string sName;

            public NameObject(string name)
            {
                sName = name;
            }
            public void objOpen()
            {
                if (!IsUse)
                {
                    IsUse = true;
                    Console.WriteLine("Open : {0}", sName);
                }
                else
                    throw new Exception("사용중인 오브젝트 " + sName);
            }
            public void objClose()
            {
                if(IsUse)
                {
                    IsUse = false;
                    Console.WriteLine("Close : {0}", sName);
                }
            }
        }
        static void Main(string[] args)
        {
            NameObject[] objs = { new NameObject("A"), new NameObject("B"), new NameObject("C") };
            objs[1].objOpen();

            try
            {
                for (int i = 0; i < objs.Length; i++)
                    objs[i].objOpen();
            }
            catch(Exception e)
            {
                Console.WriteLine("예외 발생 : {0}", e.Message);
            }
            finally
            {
                for(int i = 0; i < objs.Length; i++)
                {
                    if (objs[i].USE)
                        objs[i].objClose();
                }
                Console.WriteLine("모든 오브젝트 해제 완료!");
            }
        }
    }
}

결과

Open : B
Open : A
예외 발생 : 사용중인 오브젝트 B
Close : A
Close : B
모든 오브젝트 해제 완료!


finally는 try를 실행하게되면 어떠한 경우라도 예외가 일어나든 일어나지 않든 간에 반드시 실행됩니다.

위의 예제와 같이 마지막에 해제되는 코드를 넣어 둔다면 자연스럽게 안정성을 가지면서 코드를 정리 할수 있습니다. 


이상으로 예외처리를 마치겠습니다.

수고하셨습니다.





+ Recent posts