사람이 만든 코드의 위험성 – NASA와 Apple 사례로 배우는 실수의 심리학
인간은 완벽하지 않습니다.
작은 실수 하나가 수억 달러의 손실로 이어지거나, 전 세계 인터넷에 치명적인 보안 취약점을 남기기도 합니다.
이 글에서는 역사적으로 잘 알려진 두 가지 프로그래밍 실수 사례를 통해, 왜 코드에는 실수가 숨어들기 쉬운지, 그리고 그러한 실수를 어떻게 줄일 수 있을지를 심리학적·기술적 관점에서 살펴보려 합니다.
1. 실수는 언제나 잠복해 있다 - 코드의 위험성
프로그래밍에서 '버그'라는 단어는 너무나 흔하게 사용됩니다. 많은 사람들은 버그를 그저 기능이 정상 동작하지 않는 사소한 오류쯤으로 여기기 쉽습니다. 하지만 현실은 그보다 훨씬 더 심각합니다.
버그는 인명 피해, 수억 원 규모의 금전적 손실, 심지어는 국가 단위의 보안 문제나 사회적 혼란으로 이어질 수 있는 심각한 위험 요소입니다.
✅ 코드는 왜 실수를 유발하는가?
문제의 본질은 코드 자체에 있지 않습니다.
오히려 그 코드를 만드는 인간의 한계, 즉 인지적·심리적 제약에 그 근본적인 원인이 있습니다. 아래는 코드가 실수를 유발하는 대표적인 이유들입니다.
📌복잡성의 폭발
하나의 소프트웨어가 수십만, 수백만 줄의 코드로 구성되며, 수많은 조건 분기(if, switch)와 예외 처리, 의존성들이 얽혀 있습니다.
이처럼 복잡한 구조 안에서는 작은 논리적 실수 하나도 전체 시스템에 심각한 영향을 줄 수 있습니다.
📌자동화에 대한 과신
개발자는 종종 “어차피 컴파일러가 잡아줄 거야”, “테스트가 있으니까 괜찮아”라고 생각합니다.
하지만 자동화된 도구는 모든 실수를 감지하지 못합니다. 특히 논리적 오류나 맥락적 착오처럼 사람만이 인식할 수 있는 실수는 도구가 놓치기 쉽습니다.
📌인간의 인지 한계
인간의 뇌는 한 번에 여러 정보를 완벽히 기억하고 처리할 수 없습니다.
실수는 피할 수 없는 속성이며, 특히 주의력이 분산되거나 피로도가 누적될 때 더욱 쉽게 발생합니다.
즉, 아무리 숙련된 개발자라도 반복되는 업무, 마감 압박, 팀 간 커뮤니케이션 부족 등의 상황에서는 실수를 하기 쉽습니다.
✅ 실수는 어떻게 치명적인 장애로 이어지는가?
아래의 흐름도는 작은 실수가 어떻게 대규모 장애나 사고로 확산되는지를 단순화한 사고 모델입니다.
예를 들어,
• 실수로 잘못된 조건문을 작성했지만
• 코드 리뷰에서 해당 부분이 지나쳤고
• 테스트에서도 우연히 문제가 드러나지 않아
• 그대로 운영에 배포되었고
• 결국 실시간 결제 시스템이 오작동하거나, SSL 인증이 무력화되는 등의 심각한 보안 사고로 이어지는 것입니다.
2. NASA Mars Climate Orbiter - 단위 혼동으로 인한 궤도 이탈
1999년, NASA는 화성의 기후를 관측하기 위한 인공위성 Mars Climate Orbiter를 발사했습니다. 이 위성은 화성 궤도에 진입하여 대기와 기상 정보를 수집할 예정이었지만, 궤도 진입 직후 통신이 두절되었습니다. 이후 NASA는 위성이 화성 대기에 너무 낮은 고도로 진입해 산화되었을 가능성이 높다고 판단했습니다.
놀랍게도 이 사건의 원인은 단순한 기계 고장이 아닌, 프로그램 코드에 숨겨진 ‘단위 변환 오류’였습니다.
✅ 무엇이 문제였을까? – 시스템 간 단위 불일치
이 사건은 두 개의 독립적인 소프트웨어 모듈이 서로 다른 단위계를 사용하고 있었지만, 단위 변환이 명확히 처리되지 않은 채 데이터가 전달된 것에서 비롯됐습니다.
🔸지상 제어 소프트웨어(시스템 A)는 데이터를 파운드초(lbf·s) 단위로 출력했습니다.
🔸반면, 궤도 계산 소프트웨어(시스템 B)는 입력값을 뉴턴초(N·s) 단위로 해석하도록 설계되어 있었습니다.
이처럼 단위가 맞지 않는 데이터를 변환 없이 그대로 사용하면서, 위성의 추진력 계산에 지속적인 오차가 발생했고, 결국 궤도를 제대로 맞추지 못한 채 너무 낮은 고도로 진입하게 된 것입니다.
구분 | 제작사 | 운영기관 | 결과 |
사용 단위 | 영국식 단위 (파운드·초, lbf·s) | 미터법 단위 (뉴턴·초, N·s) | 단위 변환 누락 |
데이터 해석 | 파운드·초로 계산 | 뉴턴·초로 해석 | 약 4.45배의 힘 차이 발생 |
궤도 고도 | 계획 고도: 약 140~150km | 실제 고도: 약 57km | 화성 대기권에 진입하여 탐사선 소실 |
✅ 왜 이런 실수가 발생했을까? – 인간 심리의 영향
단위 불일치는 기술적으로는 간단한 실수처럼 보이지만, 그 배경에는 인간의 심리적 작용이 숨어 있습니다.
🔸1. 기본값 신뢰 편향 (Default Trust Bias)
개발자나 운영자는 종종 "이 정도는 당연히 맞춰져 있겠지"라는 무의식적인 기대를 하게 됩니다.
특히 복잡한 시스템에서는 다른 모듈도 나와 동일한 기준을 쓸 것이라고 가정하는 경향이 강해집니다. 이 편향은 검증을 생략하게 만들고, 결국 중요한 실수를 놓치게 만듭니다.
🔸2. 검증 생략과 자동화의 역설
지속적인 테스트를 통해 시스템이 ‘정상적으로 동작하는 것처럼 보이면’, 실제로는 문제를 내포하고 있어도 검증을 건너뛰게 되는 심리가 작동합니다.
자동화와 반복된 성공이 쌓이면, 사람은 점점 더 의심을 멈추고 자동적 판단에 의존하게 됩니다.
3. Apple의 "goto fail;" - 복붙 실수가 만든 SSL 취약점
2014년 초, Apple은 자사 운영체제인 iOS와 macOS의 SSL(TLS) 구현에서 보안 취약점이 발견되었다고 공식 발표했습니다.
이 문제로 인해, 사용자가 HTTPS 연결을 통해 웹사이트와 통신하는 과정에서 인증서 검증이 제대로 이루어지지 않아, 중간자 공격(Man-in-the-Middle)에 노출될 가능성이 생겼습니다.
이 취약점의 원인은 비교적 단순했습니다.
복잡한 시스템 설계나 알고리즘상의 결함이 아닌, 복사-붙여넣기 과정에서 중복된 goto fail; 문장이 추가된 것이 주요 원인이었습니다.
✅ 코드 분석 - 조건과 무관하게 실행된 goto fail;
아래는 문제가 되었던 코드의 주요 부분입니다.
if ((err = SSLHashSHA1.update(&hashCtx, &serverRandom)) != 0)
goto fail;
goto fail;
이 코드를 보면, goto fail; 문장이 두 번 연속으로 존재합니다.
첫 번째 goto fail;은 조건문에 포함되어 있어 오류가 발생한 경우에만 실행됩니다.
그러나 두 번째 goto fail;은 조건과 관계없이 항상 실행되며, 이후의 중요한 인증 로직이 건너뛰어지는 문제가 발생합니다.
그 결과, SSL 인증서의 유효성을 제대로 검증하지 못하고, 통신이 보안 연결로 잘못 인식될 수 있는 상황이 만들어졌습니다.
// 잘못된 예시
if ((err = ...) != 0)
goto fail;
goto fail; // 조건과 무관하게 항상 실행됨
// 수정된 예시
if ((err = ...) != 0)
goto fail; // 조건이 만족될 때만 실행됨
✅코드 작성 과정에서 발생할 수 있는 인지적 요인
이 문제는 단순한 문법 오류 이상의 의미를 가집니다.
개발자가 코드 작성 과정에서 흔히 겪는 습관적 행동과 인지적 한계가 결합되면서 발생한 실수이기 때문입니다.
🔸코드 복사 후 검토 부족
기존 코드 블록을 복사한 후 내용을 수정하지 않거나, 수정 여부를 재확인하지 않는 경우 발생할 수 있습니다.
🔸주의력의 제한
반복 작업을 수행하다 보면 주의력이 점차 감소하고, 일부 문장의 실행 흐름이나 들여 쓰기를 제대로 인지하지 못할 수 있습니다.
🔸정상 동작에 대한 기대
기존에 잘 작동하던 코드에 대한 암묵적 신뢰로 인해, 세부적인 검토 없이 넘어가는 경향이 있습니다.
이와 같은 요소들이 결합되면, 비교적 단순한 코드 구조에서도 예기치 못한 동작이 발생할 수 있습니다.
4. 반복되는 실수의 심리학 - 우리는 왜 방심할까?
우리는 대부분의 코드를 최선을 다해 검토하고, 테스트도 꼼꼼히 돌린다고 생각합니다. 그런데도 왜 오류는 꾸준히 발생할까요? 그 원인을 단순한 ‘부주의’로 치부해서는 안 됩니다. 실수는 인간의 뇌 구조와 인지 방식에서 비롯된 경우가 많습니다.
✅ 인지 부하 (Cognitive Load) – 한 번에 처리할 수 있는 정보는 제한적이다
인간의 단기 기억은 동시에 처리할 수 있는 정보의 양이 매우 제한적입니다. 일반적으로는 한 번에 5~9개 정도의 항목을 기억하고 판단할 수 있다고 합니다.
하지만 코드 세계에서는 어떤가요?
• 중첩된 조건문 (if, else, switch)
• 복잡한 논리 연산 (&&, ||, !)
• 수십 줄에 걸쳐 연결된 함수 호출
이러한 코드는 개발자에게 과도한 인지 부담을 주게 되고, 그 결과 전체 맥락을 놓치거나 논리적 연결을 잘못 이해하는 실수가 발생합니다.
예로, 조건문이 3중 이상 중첩되었을 때, 마지막 조건이 무슨 의미인지 정확히 기억하기 어렵고, 실수 가능성이 급증합니다.
✅ 자동 사고 (System 1 Thinking) – 빠르지만 부정확한 판단의 위험
심리학자 대니얼 카너먼(Daniel Kahneman)은 인간의 사고방식을 System 1(직관적, 자동적 사고)과 System 2(논리적, 분석적 사고)로 구분했습니다.
대부분의 코딩 작업은 System 2가 필요한 일입니다. 하지만 시간이 부족하거나 익숙한 작업을 반복하다 보면, 우리는 무의식적으로 System 1에 의존하게 됩니다.
• "이전에도 이렇게 했으니 괜찮을 거야"
• "빌드도 되고 테스트도 통과했잖아?"
이러한 자동화된 사고의 유혹은 버그를 지나치게 만듭니다.
특히, 기존 로직을 수정하지 않고 그대로 두는 것이 더 안전하다고 느끼는 경향은 치명적인 실수로 이어질 수 있습니다.
✅ 확증 편향 (Confirmation Bias) – 내가 원하는 정보만 받아들인다
테스트를 돌렸는데 문제없이 통과했습니다. 그러면 우리는 “이제 안전하다”라고 판단하죠. 하지만 이는 논리적으로 잘못된 결론일 수 있습니다.
테스트는 단지 "특정 상황에서는 버그가 드러나지 않았다"는 사실만을 의미할 뿐,
"버그가 없다"는 확증은 절대 아닙니다.
이런 상황에서 우리는 무의식적으로 다음과 같은 확증 편향에 빠지기 쉽습니다:
• “이 테스트 케이스는 통과했으니까 전체도 괜찮겠지”
• “이전 배포에서도 문제없었는데 굳이 고칠 필요 있을까?”
이는 특히 테스트 범위가 협소하거나 시나리오가 제한적일 때 매우 위험합니다.
결국, 테스트가 통과되었다는 사실은 버그가 없다는 증거가 아닙니다.
오히려 그것은 우리가 아직 그 버그를 찾아내지 못했을 가능성을 암시하는 신호일 수 있습니다.
개발자는 코드를 작성하는 기술뿐 아니라, 자신의 사고방식과 판단 과정에도 주의를 기울여야 합니다.
우리는 모두 심리적 맹점과 인지적 편향을 가진 존재이기에, 스스로를 완전히 신뢰할 수 없습니다.
5. 실수를 줄이는 개발 전략 - 도구와 팀의 힘
프로그래머는 누구나 실수합니다. 하지만 중요한 것은 그 실수를 어떻게 줄이고, 다시 반복하지 않도록 시스템화할 것인가입니다. 단순히 개인의 주의력에 의존하기보다는, 정확하고 체계적인 도구 사용과 협업 전략이 실수 예방에 큰 역할을 합니다.
✅ 실수를 줄이는 전략은 "구체적" 이어야 합니다.
모호한 “열심히 확인하자”보다는, 실수의 유형에 따라 명확한 전략과 도구를 갖추는 것이 훨씬 효과적입니다. 다음은 실수를 예방하기 위한 대표 전략과 대응 도구들입니다.
전략 도구/방법 목적
전략 | 도구/방법 | 목적 |
단위 오류 방지 | 타입 안전 언어(Type-safe), 테스트 코드 | 단위 자동 검증 |
복붙 실수 제거 | 코드 템플릿, GitHub Copilot, Snippet | 반복 코딩 방지 |
리뷰 시각 확보 | 페어 프로그래밍(Pair Programming), PR 코드 리뷰 | 외부 시선 확보 |
주의력 보완 | Linter, 정적 분석 도구(Static Analysis) | 습관적 실수 감지 |
📌1. 단위 오류는 코드 수준에서 막아야 한다
NASA의 사례처럼 단위 혼동은 재앙적인 결과를 낳을 수 있습니다. 이를 방지하기 위해 단위 검증이 가능한 타입 시스템을 갖춘 언어(예: Rust, TypeScript)를 활용하거나, 단위 관련 테스트 코드를 명시적으로 작성하는 것이 중요합니다.
📌2. 복붙은 실수의 출발점이다
같은 로직을 반복 작성하는 과정에서 실수가 섞이기 쉽습니다. 특히 goto fail; 같은 실수는 대부분 복사-붙여넣기 후 확인 부족에서 발생합니다.
→ 이를 방지하려면 코드 템플릿을 사용하거나, GitHub Copilot, Snippet 툴을 통해 코드 자동화를 유도해야 합니다.
📌3. 타인의 눈이 실수를 잡는다
본인의 실수는 잘 보이지 않습니다. 이럴 때 페어 프로그래밍이나 Pull Request(PR) 기반 코드 리뷰를 통해 외부 시선의 검증을 받는 것이 중요합니다. 타인의 피드백은 코드뿐 아니라 사고 과정 자체를 점검할 수 있게 해 줍니다.
📌4. 주의력의 한계를 도구로 보완하자
사람은 집중력이 한계가 있습니다. 이를 보완하기 위해 Linter나 정적 분석 도구를 사용하면, 일관성 없는 스타일, 미사용 변수, 조건 누락 등 사소하지만 반복되는 실수를 자동으로 잡아낼 수 있습니다.
예: ESLint, Pylint, SonarQube 등
"본 글은 과거 cericube-it(티스토리)에서 발행했던 콘텐츠를 기반으로, 새롭게 정리한 업데이트 버전입니다."
'4. IT이야기' 카테고리의 다른 글
플랫폼 비즈니스란? 쿠팡과 유튜브로 이해하는 연결의 구조 (1) | 2025.09.07 |
---|---|
핵티비스트란(Hacktivist)? – 해킹으로 세상을 바꾸려는 사람들 (0) | 2025.09.06 |
안전하고 기억하기 쉬운 비밀번호 만들기 – 패턴 전략 (1) | 2025.09.05 |
재사용된 비밀번호, 해킹의 지름길 – 크리덴셜 스터핑이란 무엇인가? (1) | 2025.09.05 |
자동차도 무선 업데이트가 된다? 자동차의 진화: OTA 기술 (0) | 2025.09.05 |