티스토리 뷰



[프로그래밍 방법론] 객체와 자료구조 – Clean Code


이 글은 Clean Code 책의 내용을 정리한 것입니다.


오류 처리는 반드시 프로그램에 필요한 요소중 하나입니다. 입력이 이상하거나 디바이스가 실패할지도 모르기 때문입니다. 간단히 말해 무엇인가가 잘못될 가능성은 언제든지 존재합니다. 


1. 오류코드 보다는 예외를 사용하라.


과거에는 예외를 지원하지 않는 언어들이 많았지만, 현재는 C#, Java와 같은 언어들이 모두 예외를 지원하고 있습니다. 예외를 지원하지 않으면 오류처리를 하는데 많은 한계가 있고, 호출자 코드가 복잡해지는 문제를 가지지만 예외를 사용하면 이러한 문제를 해결 할 수 있습니다.


2. Try-Catch-Finally 코드부터 구현하라.


예외에서 프로그램 안에다 범위를 정의한다는 사실을 미리 파악하고 있어야 합니다. try-catch 구문을 사용하면 try 구문에서 언제든 에러가 발생을 해도 catch 구문으로 예외를 처리할 수 있습니다.


테스트 시 강제로 예외를 일으키는 테스트 케이스를 작성한 후 테스트를 통화하게 코드를 작성하는 방법을 권장합니다.


3. 미확인(Unchecked) 예외를 사용하라


과거에는 확인된 예외(모든 예외 상황을 구현) 하는 것이 옳다고 판단이 되었지만, 현재는 그렇지 않습니다. 확인된 예외를 사용하지 않더라도 안정적인 소프트웨어를 제작하는데는 문제가 될것이 없습니다. 실제로 C#, C++, 루비, 파이썬은 확인된 예외를 지원하지 않습니다. 


확인된 예외를 사용하면 OCP(Open Closed Principle)을 위반하게 됩니다. 메소드에서 확인된 예외를 던졌는데 catch 블록이 세 단계 위에 있다면 그 사이 메소드 선언부를 고쳐야 합니다. 이는 캡슐화를 깨버리는 현상이 발생하는 원인이 됩니다. 


4. 예외에 의미를 제공하라


예외를 던질 때는 전후 상황을 충분히 덧붙입니다. 그러면 오류가 발생한 원인과 위치를 찾기가 훨씬 쉬워지는 경향이 있습니다.


5. 호출자를 고려해 예외 클래스를 정의하라


오류를 분류하는 방법은 수없이 많습니다. 발생하는 컴포넌트로 구분하던지, 유형으로 분류가 가능합니다. 어플리케이션에서 오류를 정의할 때 프로그래머에게 가장 중요한 점은 오류를 잡아내는 방법이 되어야 합니다. 


6. null을 반환하거나, 인수로 전달하지 말라


과거에는 예외를 지원하지 않는 언어들이 많았지만, 현재는 C#, Java와 같은 언어들이 모두 예외를 지원하고 있습니다. 예외를 지원하지 않으면 오류처리를 하는데 많은 한계가 있고, 호출자 코드가 복잡해지는 문제를 가지지만 예외를 사용하면 이러한 문제를 해결 할 수 있습니다.


public void registerItem(Item item) {

if(item != null) {

ItemRegistry reg = GetRegistry();

if(reg != null)  {

// blablabla

}

}

}


위의 코드는 매번  null을 체크해야만 합니다. null 체크가 많으면 개발자에게 많은 부하를 줄 수 있습니다. 이런 경우는 예외를 던지는 것이 옳은 방법입니다. null을 반환하는 방식도 나쁘지만, 전달하는 방법은 더욱더 좋지 않습니다. 차라리 NullPointException을 던지는 코드를 구현하는것이 조금은 더 옳은 방법이지만 완벽한 해결책은 안됩니다. Assert를 이용하는 것이 더 좋다고 생각합니다. 


7. 오류 처리에 대한 마무리


깨끗한 코드는 읽기도 좋아야 하지만 안정성도 높아야 합니다. 오류 처리를 프로그램 논리와 분리해 독자적인 사안으로 고려하면 튼튼하고 깨끗한 코드를 작성할 수 있고, 코드 유지보수성도 크게 높아집니다.


8. C#에서의 예외처리


C#에서는 예외처리를 위해 try-catch-finally 구문을 이용할 수 있습니다.


class FileRead
{
    public void ReadAll(FileStream fileToRead)
    {
        // This if statement is optional 
        // as it is very unlikely that 
        // the stream would ever be null. 
        if (fileToRead == null)
        {
            throw new System.ArgumentNullException();
        }

        int b;

        // Set the stream position to the beginning of the file.
        fileToRead.Seek(0, SeekOrigin.Begin);

        // Read each byte to the end of the file. 
        for (int i = 0; i < fileToRead.Length; i++)
        {
            b = fileToRead.ReadByte();
            Console.Write(b.ToString());
            // Or do something else with the byte.
        }
    }
}


또한 throw를 이용해서 예외를 발생시킬 수 있습니다.


throw new Exception();


마지막으로 C#에서 사용자 정의 예외 클래스를 만드는 방법에 대해서 이야기 해볼려고 합니다.


어플리케이션 기반의 Exception을 만들기 위해서는 ApplicationException을 상속받습니다. CLR 처리와 관련된 예외 처리는 Exception을 상속받습니다. ApplicationExcpetion과 Exception 중 무엇을 사용해야 하는 내용은 아직까지도 개발자 입장에서 많은 토론이 발생하고 있는게 현실입니다. 


다만 ApplicationException은 CLR과 전혀 관계가 없이 처리가 되므로 이 부분에 대한 단점이 많이 보여 Exception을 쓰는게 좋다는게 대세 의견이라고 보시면 될것 같습니다. 


사용자 정의 예외 클래스 만드는 방식에 대한 좋은 글이 있어 가지고 왔습니다.

참고하시기 바랍니다.


Designing Custom Exceptions

In some cases, it will not be possible to use existing exceptions. In those cases, you’ll need to define custom exceptions. The guidelines in this section provide help on doing that.


ý Avoid deep exception hierarchies. 

þ Do derive exceptions from System.Exception or one of the other common base Exceptions.

þ Do end exception class names with the ‘Exception’ suffix.

þ Do make exceptions serializable. An exception must be serializable to work correctly across application domain and remoting boundaries.

þ Do provide (at least) these common constructors on all exceptions. Make sure the names and types of the parameters are exactly as in the example below.


public class SomeException: Exception, ISerializable {

   public SomeException();

  public SomeException(string message);

  public SomeException(string message, Exception inner);

 

  // this constructor is needed for serialization.

   protected SomeException(SerializationInfo info, StreamingContext context);

}


þ Do report security sensitive information through an override of ToString only after demanding an appropriate permission.

If the permission demand fails, return a string excluding the security sensitive information.


Annotation (Rico Mariani):

Do not store the results of ToString in any generally accessible data structure unless that data structure suitably secures the string from untrusted code.  This advice applies to all strings but since exception strings frequently contain sensitive information (such a file paths) I reiterate the advice here.


þ Do store useful security sensitive information in private exception state. Ensure only trusted code can get the information.

þ Consider providing exception properties for programmatic access to extra information (besides the message string) relevant to the exception.



이 글이 도움이 되셨나요?

그렇다면 아래의 그림을 클릭해주세요.


댓글