- NASA의 10가지 소프트웨어 개발 규칙에 대한 비판적 분석
- 이 규칙들은 극도로 중요한 임베디드 시스템(예: 우주선 소프트웨어)을 위한 것
- 하지만 이러한 규칙이 다른 개발 환경에서도 적절한지, 또는 다른 언어(C가 아닌 언어)에서도 적용 가능한지에 대해 논의가 필요함
1. 단순한 제어 흐름 유지 (goto, setjmp/longjmp, 재귀 금지)
- 이 규칙은 예외 처리(setjmp()/longjmp())와 재귀를 금지함.
- 재귀가 반드시 비효율적인 것은 아님. 적절한 방법을 사용하면 재귀도 종료를 보장할 수 있음.
- 강제로 재귀를 루프로 변환하면 유지보수가 어려운 코드가 될 위험이 있음.
비판:
- 종료 보장이 중요하지만, 극단적인 제한은 가독성과 유지보수를 저해할 수 있음.
- 무조건적인 재귀 금지는 불필요한 복잡성을 초래할 가능성이 큼.
2. 모든 루프는 명확한 상한을 가져야 함
- 컴파일러가 루프 반복 횟수를 정적으로 분석할 수 있어야 함.
- 그러나 단순히 상한을 설정하는 것만으로는 실제 실행 시간 보장이 어려움.
- 재귀 깊이 제한을 두는 것이 루프 상한을 두는 것만큼 안전할 수 있음.
비판:
- 단순히 상한을 두는 것만으로는 현실적인 실행 가능 시간을 보장하지 못함.
- 상한을 설정해도 너무 큰 값이라면 사실상 무한 루프와 다를 바 없음.
3. 초기화 이후 동적 메모리 할당 금지
- 임베디드 시스템에서는 메모리가 한정적이므로 메모리 부족으로 인한 충돌을 방지하려는 목적.
- 하지만 수동 메모리 관리보다 예측 가능한 동적 할당이 더 안전할 수도 있음.
- 예를 들어, 실시간 가비지 컬렉터(RTGC) 를 사용하면 동적 할당도 예측 가능하게 만들 수 있음.
비판:
- 동적 할당 자체를 금지하는 것보다, 메모리 사용 패턴을 분석하여 안전성을 확보하는 것이 더 나은 접근 방식일 수 있음.
- 현대적인 정적 분석 도구(SPlint 등)를 활용하면 동적 메모리 관련 오류를 사전에 감지할 수 있음.
4. 함수 크기는 A4 용지 한 장 이내로 제한 (약 60줄)
- 함수가 너무 길면 가독성이 떨어진다는 논리.
- 하지만 현대 개발 환경에서는 코드 폴딩 기능이 있어, 함수 길이보다 논리적 단위의 크기가 더 중요함.
비판:
- 물리적인 크기(줄 수)보다 논리적 복잡성을 기준으로 삼아야 함.
- 함수를 작게 쪼개는 것 자체가 목표가 되어서는 안 됨 → 오히려 유지보수를 어렵게 만들 수도 있음.
5. 함수당 최소 두 개의 assert 문 사용
- assert는 디버깅과 문서화에 매우 유용함.
- 하지만 무조건적인 개수 제한은 비효율적일 수 있음.
비판:
- assert의 개수보다 데이터 유효성 검사가 필요한 위치를 명확히 하는 것이 중요함.
- 모든 인자와 외부 입력을 검증하는 것이 더 실용적임.
6. 데이터 객체의 스코프 최소화
- 지역 변수 사용을 권장하는 좋은 원칙.
- 하지만 함수뿐만 아니라 타입과 함수의 스코프도 최소화해야 함.
비판:
- Ada, Pascal, JavaScript, 함수형 언어에서는 타입과 함수도 지역적으로 선언 가능 → NASA 규칙보다 더 나은 접근 방식.
7. 함수 반환 값 및 매개변수 유효성 검증 필수
- 반환 값을 반드시 체크해야 함.
- 하지만 모든 경우를 체크하는 것은 현실적으로 어려움.
비판:
- 실행 오류를 방지하려면 가능한 많은 검사가 필요하지만, 실용적인 한계를 고려해야 함.
- 특히 C에서는 반환 값 체크가 중요하지만, 현대 언어(Java, Rust 등)에서는 타입 시스템을 활용해 더 안전하게 처리 가능.
8. 전처리기 사용 제한 (헤더 포함 및 단순 매크로만 허용)
- 복잡한 매크로, 토큰 결합, 가변 인자 매크로(...) 금지.
- 하지만 가변 인자 매크로는 디버깅 도구로 유용할 수 있음.
비판:
- 전처리기 사용을 제한하는 것보다 가독성 좋은 매크로 스타일을 권장하는 것이 바람직함.
- #ifdef 같은 조건부 컴파일을 막으면, 플랫폼 독립적인 코드 작성이 어려워질 수 있음.
9. 포인터 사용 제한 (이중 포인터 금지, 함수 포인터 금지)
- 함수 포인터 사용 금지 → 높은 안정성을 목표로 함.
- 하지만 함수 포인터는 콜백, 전략 패턴, 디바이스 드라이버 등에 필수적임.
비판:
- 함수 포인터 없이 switch-case로 함수 선택을 강제하면 코드 가독성이 떨어지고 유지보수가 어려워짐.
- 운영체제, 네트워크 스택, 드라이버 개발에서는 함수 포인터가 필수적임.
- 포인터 제한보다 안전한 포인터 사용을 보장하는 방법(C++ 스마트 포인터, Rust 등)이 더 나은 해결책.
10. 모든 코드에 대해 컴파일러 경고를 최대로 설정하고, 정적 분석 도구 사용
- 이 규칙은 매우 좋은 권장 사항.
- 컴파일러 경고 제거 + 정적 분석 도구 사용 = 안정성 향상.
비판:
- NASA의 다른 규칙(예: 포인터 금지, 함수 크기 제한)은 단순히 정적 분석 도구의 한계를 극복하려는 목적이 있음.
- 하지만 현대 정적 분석 도구는 매우 발전했으므로, 지나친 제한보다 더 정교한 분석 기법을 활용하는 것이 유용함.