IT/Programming / / 2023. 4. 19. 10:19

<Code Craft> 1부 코드와 마주보기 : 01 방어하기 (2)

반응형

방어적 프로그래밍을 위한 테크닉

1. 좋은 코딩스타일과 건강한 설계를 채용하라

· 코드에 돌입하기 전에 명료한구조를 만든다.

 

2. 코드 작성을 서두르지 마라

· 프로그램을 작성하면서 각 행에 대해 생각하자, 어떤 에러가 발생할 수 있을까, 발생하지 모르는 모든 에러에 대해 신중히 생각해야 합니다.

· 서둘러 다음일로 넘어가기 전에, 항상 한 코드 단락을 완성하는데 관련된 모든일을 해 놓자. 예를들어 흐름을 작성하는 일을 먼저하고, 에러체크/핸들링을 두번째로 하기로 정하였다면 이 둘을 모두 하는 규율을 반드시 지켜야 합니다.

 

3. 아무도 믿지 마라

 

4. 짧은 코드가 아니라 명료한 코드를 작성하라

· 짧은(하지만 혼란스러울 수 있는) 코드와 명료한(하지만 지루할 수 있는) 코드 중에서 하나를 선택해야 한다면 언제든지 당신이 의도한 대로 읽히는 코드를 선택하세요. 예를 들어 복잡한 계산 오퍼레이션은 여러개의 독립된 명령문으로 만들어서 로직을 더 분명하게 만듭니다.

· 코드를 간단하게 만들어라.

 

5. 어설프게 만지면 안되는 것은 아무도 못만지게 하라

· 내부의 것은 내무에 머물러야 합니다. 사적인 것은 열쇠와 좌물쇠로 잠궈야 합니다.

→ 객체지향 언어에서는 클래스 내부 데이터를 private를 만들어 접근을 만들어라.

→ C++에서는 (cheshire cat이라고도 불리는) pimpl 관용구의 사용을 고려해 보십시오

→ 모든 변수를 그 변수가 속할 수 있는 가장 좁은 범위 안에 두십시오. 쓸데 없이 변수를 전역으로 두지 마세요. 함수 안에서 선언해도 변수를 파일 범위 위치에 두지 마세요, 루프안에 선언해도 될 변수를 함수 범위로 선언하지 마세요

 

6. 경고 스위치를 전부 켜놓고 컴파일하라

· 컴파일러 경고를 항상 사용가능상태로 설정해 두십시오. 그리고 어떤 경고가 발생하면 즉시 코드를 고쳐서 컴파일러의 비명을 잠재우십시오.

 

7. 정적 분석 툴을 이용하라

· 컴파일러가 내는 경고는 코드에 대한 정적 분서을 제한적으로 수행한 결과입니다. 정적 분석이란프로그램이 실행되기 전에 수행되는 코드 점검을 말합니다.

· C언어용 : lint, .NET 어셈블리용 : FxCop.

· 일상적으로 이러한 툴들을 이용하여 코드를 체크해야 합니다. 컴파일러 하나만 가지고 하는 것 보다 이런 툴을 사용했을 때 훨씬 더 많은 에러를 잡을 수 있습니다.

 

8. 안전한 데이터 구조를 사용하라

· 가장 흔한 보안 취약점은 아마도 버퍼 오버런으로 인해 만들어집니다. 버커의 크기를 먼저 체크하지 않고 버퍼에 데이터를 쓰는 식으로 코드를 작성하면 데이터가 버퍼의 끝을 지나서 계속 쓰이게 될 잠재적인 가능성이 항상 있습니다.

char *unsafe_copy(const char *source)
{
    char *buffer = new char[10];
    strcpy(buffer, source);
    return buffer;
}
 

· 위 코드에서 source의 데이터 길이가 10이 넘어가게 된다면 buffer 변수에 확보되어 있는 메모리의 끝을 지나서도 복사한 내용이 계속 쓰일 것입니다. 최악의 경우 악의적인 사용자가 실행가능한 코드를 올려두고 그 코드를 이용하여 프로그램을 마음대로 하이재킹을 할 수 있습니다.

 

· 이러한 취약점을 피하는 방법은 쉽습니다. 프로그램을 손상시키지 않는 더 안전한 데이터 구조를 사용하세요

ctrcpy를 크기 제한이 있는 문자열 복사 오퍼레이션인 strncpy로 교체해서 안전하게 만들 수 있습니다.

char *unsafe_copy(const char *source)
{
    char *buffer = new char[10];
    strncpy(buffer, source, 10);
    return buffer;
}
 

9. 모든 리턴 값을 체크하라

· 함수가 리턴할 때는 이유가 있어서 그렇게 하는 것입니다. 항상 적절한 수준에서 적절한 익셉션을 잡아서 처리하십시오.

 

 

10. 메모리를 조심해서 다뤄라 (그리고 다른 소중한 자원들도)

· 자원관리를 철저히 하고, 실행중에 얻는 모든 자원을 해제하십시오. 메모리는 그 중에서 가장 자주 언급되는 예이지만, 유일한 예는 아닙니다. 파일과 스레드 락도 반드시 주의해서 사용되야하는 귀중한 자원입니다.

 

· 프로그램이 끝날 때 OS가 청소해줄 것이라고 믿고, 파일 닫기나 메모리 해제를 게을리 하면 안됩니다. 코드가 파일 핸들을 모두 잡아 먹거나 메모리를 모두 써버리면서 얼마나 오랫동안 실행중인 상태로 남아있을지 사실상 당신은 알지 못합니다.

 

· JAVA와 .NET에서는 이런 지겨운 일을 모두 말끔하게 처리하는 가비지 컬렉터를 채용하고 있기 때문에 당신은 자원 해제에 대해서 그냥 잊어버릴 수 있습니다. 안전하다고 믿고 안심하지는 마십시오. 더이상 관심이 없는 객체들에 대한 참조를 명시적으로 제거하여 메모리 누수를 막아야합니다.

 

11. 모든 변수를 선언 지점에서 초기화 하라

· 변수를 초기화하면 변수의 목적이 뚜렷해집니다. C와 C++은 이런 문제를 더 복잡하게 만듭니다. 당신이 실수로 초기화되지 않은 변수를 사용하게 되면 프로그램이 실행될 때마다 다른 결과가 나올 것입니다.

 

· 더 안전한 언어(Java, C#)에서는 모든 변수를 초기 값을 정의함으로써 이런 함정을 피해갑니다. 그렇더라도 변수를 선언하면서 초기화 하는것은 좋은 습관이고, 코드의 명료성을 향상시킬것입니다.

 

12. 변수를 가능한 늦게 선언하라

· 변수를 가능한 한 늦게 선언하면, 그 변수는 사용되는 곳에 될 수 있는 한 가까워 질 것이고, 코드의 다른 부분을 혼란스럽게 만들지 않을 것입니다. 그 변수를 사용하는 코드도 명료해질 것입니다. 가까이 있는 선언이 확실히 보여주기 때문에 변수의 타입과 초기화 부분을 찾아서 여기저기 뒤지고 다닐 필요가 없어집니다.

· 하나의 임시변수를 여러곳에서 재사용하지 마십시오. (논리적으로 분리된 영역 포함) 그렇게 하면 나중에 그 코드를 가지고 재작업하는 일이 복잡해집니다. 매번 새로운 변수를 만드세요. 효율성에 관한 문제는 모두 컴파일러가 처리해 줄 것입니다.

 

13. 언어의 표준 설비를 사용하라.

· 언어의 어느 버전을 사용할 것인지 확실하게 정해두십시오. 언어에 정의되어 있지 않은 영역을 특정 컴파일러의 동작에 의존하지 마세요. (C컴파일러가 char를 부호있는 값으로 취급하더라도 다른 컴파일러는 그렇게 하지 않을 수도 있습니다.)

 

14. 좋은 진단 로깅 설비를 사용하라

· 새로운 코드를 작성하면서 무슨일이 일어나고 있는지 체크하기 위해 대개 많은 진단 코드를 포함시킵니다. 그 진단 코드를 제거해야 할까요? 그대로 남겨두는 편이 나중에 돌아올 필요가 있을 때더 편할 것입니다. 특히 진단 코드를 선택적으로 사용 불가능하게 맏들어 놓을 수 있다면요. 조건에 따라 컴파일에서 진단 코드를 제외시킬 수 있는 진단 로깅 시스템들이 있습니다.

 

15. 캐스트는 신중하게

· 대부분의 언어는 데이터를 한타입에서 다른 타입으로 캐스트하는 것을 허용합니다. 정말로 캐스트하고싶다면 신중하게 생각하십시오. 캐스트라는 것은 당신이 컴파일러에게 이렇게 말하는 것 입니다. “너는 타입 체크에 대해 잊으렴. 이 변수가 무엇인지 나는 알고 너는 몰라.” C와 C++은 특히 정확한 데이터 타입이 모호하므로 데이터 타입의 호환성에 대해 추측하면 안됩니다. int와 long타입이 서로 크기가 같고, 서로 대입이 가능할 것이라고 추측하지 마세요.

 

16. 자잘한 테크닉들

· 디폴트 동작 제시하기

→ 대부분의 언어는 switch문을 제공하고 default일 경우 무슨일이 일어날 지에 대해 문서화 하고 있습니다. 디폴트일 경우가 에러상황이면 그것을 코드에 명시적으로 표시하고, 아무일도 일어나지 않으면 그렇다는것을 명시하세요.

→ else가 없는 if문을 작성할 때에도 잠깐 멈추어서 논리적인 디폴트를 처리해야 하는것은 아닌지 잘 생각해 보십시오,.

· 언어의 관용구 따르기

· 수치의 범위 체크하기

→ 가장 기초적인 계산조차도 수치 변수의 오버플로나 언더플로를 일으킬 수 있습니다. 각각의 계산이 완전한지 체크하십시오. 예를 들면 0으로 나누는 에러를 발생시키는 값은 사용할 수 없다는 것을 분명히 하십시오.

· 상수 올바르게 사용하기

→ C/C++ 프로그래머는 정말 조심해야 합니다. const로 만들 수 있는 것은 모두 const로 만들어 허용되지 않은 데이터를 수정할 수 없도록 하세요.

 

 

반응형
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유