소프트웨어 인식론 | Mark Seemann


tdd #소프트웨어검증 #과학적방법론 테스트주도개발 #인식론

핵심 요약

소프트웨어가 실제로 작동하는지 어떻게 알 수 있을까? 이 질문에 답하기 위해 마크 시만(Mark Seemann)은 철학의 인식론과 과학철학에서 비롯된 과학적 방법론을 소프트웨어 개발에 적용합니다. 과학자들이 가설을 세우고, 예측 가능한 실험을 통해 증거를 모으듯이, **테스트 주도 개발(TDD)**은 이 같은 과학적 검증 프로세스를 소프트웨어 개발에 체계적으로 구현한 방식입니다. 테스트를 먼저 실패시키고, 그 다음 통과시키는 이 과정을 통해서만 코드가 실제로 의도대로 작동하는지 경험적 증거를 수집할 수 있습니다.


상세 요약

1. 철학적 기초: 인식론과 과학적 방법론

1.1 과학에서의 진리에 대한 이해

  • 절대적 확실성의 불가능성: 과학철학의 오랜 명제로, 우리는 어떤 것도 완전히 확실하게 알 수 없습니다
  • 비점근적 접근(Asymptotic Approach): 실험과 관찰을 통해 진실에 점차 더 가까워질 수 있습니다
  • 과학적 방법론의 핵심 구조:
    1. 가설 형성
    2. 예측 수립
    3. 실험 수행
    4. 예측 결과와 실험 결과 비교 (입증 또는 반박)
    5. 필요시 가설 조정
    6. 반복

1.2 갈릴레이 낙체 실험의 예

  • 가설: 질량이 다른 두 물체를 같은 높이에서 동시에 떨어뜨리면 동시에 땅에 닿는다
  • 실험 반복: 수천 번의 실험 (심지어 진공 상태에서도)이 수행됨
  • 과학적 결론: 수천 번의 실험도 이것이 진실임을 증명하지는 못하지만, 매우 가능성 높게 만듭니다

2. 소프트웨어 개발에 적용된 경험적 인식론

2.1 제품 소유자의 관점

  • 비기술 제품 소유자의 위치: 특정 문제를 해결하는 애플리케이션이 필요함
  • 핵심 질문: 목표가 달성되었는지 어떻게 알 수 있는가?
  • 과학자처럼 행동: 가설을 세우고, 예측하고, 실험(테스트)을 수행

2.2 테스트의 중요성

  • 유일한 검증 방법: 테스트만이 소프트웨어가 실제로 작동하는지 판단할 수 있는 유일한 방식입니다
  • 테스트의 다양한 형태:
    • 임시적 vs 계획된 테스트
    • 자동화된 vs 수동 테스트
    • 철저한 vs 대충 한 테스트
  • 각 테스트의 암묵적 가설: “이 단계들을 수행하면, 애플리케이션은 이 특정한 관찰 가능한 방식으로 행동할 것이다”

2.3 테스트 방식의 진화

  • 과거: 전담 테스트 부서

    • 테스트 관리자가 테스트 계획 문서 작성
    • 수동 테스터들이 계획을 따라 실행
    • 이것도 경험적 검증 방식이지만 문제점 존재
  • 수동 테스트의 한계:

    • 느린 속도
    • 실수 발생 가능성
    • 반복 수행 시 세부 사항 간과
  • 자동화된 테스트의 필요성:

    • 속도와 신뢰성 향상
    • 새로운 문제 제기: “테스트 코드 자체도 버그를 포함할 수 있다면?”

3. 소프트웨어에서의 과학적 방법론 (Red-Green-Refactor 사이클)

3.1 첫 번째 단계: 테스트 먼저 작성 (RED)

시나리오: 프로덕션 코드 없이 자동화된 테스트만 작성

암묵적 가설과 예측:

  • “이 테스트를 실행하면 실패할 것이다
  • 이것은 **반박 가능한 예측(Falsifiable Prediction)**입니다

예측 실패 분석 (기대와 다른 결과):

  • 예측이 반박되는 경우 (테스트가 예상과 달리 성공):

    • 테스트 코드가 잘못되었을 가능성
    • 예: 동어반복적 단언(Tautological Assertion) 작성
    • 근본 원인: 테스트 로직 결함
  • 예측이 입증되는 경우 (테스트가 예상대로 실패):

    • 과학적 성공: 가설을 반박하지 못함
    • 테스트 코드가 올바르게 작성되었을 가능성 증가

3.2 두 번째 단계: 최소 코드 작성 (GREEN)

절차: 테스트를 통과할 만큼만 프로덕션 코드 작성

새로운 가설과 예측:

  • “이 테스트를 실행하면 성공할 것이다
  • 역시 반박 가능한 예측

결과 분석:

  • 예측 실패 (테스트가 예상과 달리 실패):

    • 구현 코드의 결함 가능성
    • 테스트 코드의 결함 가능성
    • 심지어 우주선 입자 충돌도 가능(농담이지만 완전히 불가능하지는 않음)
    • 문제의 원인 파악 필요
  • 예측 입증 (테스트가 예상대로 성공):

    • 가설을 반박하지 못함
    • 코드가 의도대로 작동할 가능성 증가

3.3 세 번째 단계: 두 번째 테스트 추가 (RED)

새로운 테스트 작성:

  • “모든 테스트를 실행하면, 새 테스트는 실패할 것이다
  • 반박 가능한 새로운 예측

순환 반복:

  • 테스트 실패 (예측 입증): 테스트가 올바르게 작성되었을 가능성 증가
  • 테스트 성공 (예측 반박): 테스트 로직 재검토

3.4 연속 구현: 모든 테스트 통과 (GREEN)

계속된 최소 코드 작성:

  • “모든 테스트를 실행하면, 모두 통과할 것이다
  • 구현 코드 확장으로 새 테스트 통과 달성

과정의 누적 효과:

  • 테스트 코드 정확성 증거 누적
  • 프로덕션 코드 정확성 증거 누적
  • 반증되지 않은 가설들의 축적

3.5 핵심 원리: 테스트 먼저 작성의 필수성

테스트 먼저 작성하지 않으면 잃게 되는 것:

  • 첫 번째 실험 수행 불가능: 새 테스트가 실패할 것이라는 예측을 확인하지 못함
  • 경험적 증거 수집 불가능: 테스트 코드가 올바른지 증명할 경험적 증거 없음
  • 과학적 근거 상실: 전체 과학적 증거의 절반을 잃음

4. TDD가 과학적 방법론인 이유

4.1 명확한 정의

  • **테스트 주도 개발(TDD)**은 과학적 방법론의 실제 구현입니다
  • 새로운 테스트가 실패하는 것을 보는 것이 프로세스의 필수 부분

4.2 테스트 코드 읽기 vs 실행의 차이

단순히 테스트 코드 읽기의 한계:

  • 과학적 입장: 고대 그리스 철학자들의 내성적 철학과 동등한 수준
  • 고대 그리스의 오류된 이론: 네 가지 체액, 광학 이론(extramission), 원소설 등
  • 결론: 코드를 읽으면 이해한다고 믿을 수 있지만, 실행 없이는 경험적 검증 불가능

실제 경험:

  • 개발자들은 자신이 작성한 코드가 올바르다고 믿었으나 실제로는 오류 포함 경우가 많음

4.3 테스트 후 작성(테스트 마지막)의 문제점

테스트 통과 후 작성 시의 제한된 반박 가능성:

  • 테스트 통과: 학습된 것이 거의 없음

    • 테스트가 실제로 대상 코드를 검증하는지 불명확
    • 동어반복적 단언을 작성했을 가능성
    • 버그를 정확히 포착했을 가능성 (회귀 테스트로 기록됨)
    • 실제 코드 경로를 커버하지 않을 수 있음
  • 테스트 실패: 판단 불가능 상태

    • 테스트가 잘못되었나?
    • 프로덕션 코드가 결함이 있나?
    • 확신 불가능

4.4 레거시 코드의 특성화 테스트(Characterization Test)

전통적 접근:

  • “반드시 실패할 단언을 작성하라”
  • (출처: Michael Feathers의 ‘Working Effectively with Legacy Code’)

권장 변형:

  • 올바르다고 믿는 단언을 작성
  • 테스트를 의도적으로 실패시키도록 시스템 조작
  • 핵심: 테스트 실패를 반드시 확인
  • 이유: 강력한 예측이 반박되지 않은 것이 중요한 경험적 증거

5. 결론: 소프트웨어 개발의 근본적 철학

5.1 핵심 질문의 답

“우리가 개발한 소프트웨어가 의도대로 작동한다는 것을 어떻게 알 수 있나?”

: 더 큰 질문 "우리는 어떻게 무언가를 알 수 있나?"로부터 나옵니다

5.2 과학적 사고의 해답

과학적 방법론 적용:

  1. 가설 수립: “소프트웨어가 주어진 조건에서 특정 방식으로 작동한다”
  2. 예측: 관찰 가능한 동작 예측
  3. 실험(테스트): 예측 검증
  4. 기록: 결과 기록
  5. 개선: 결함 수정
  6. 반복: 계속 과정 반복

5.3 TDD의 위상

  • 매우 과학적인 방법론: 소프트웨어 개발 프로세스 중 가장 엄밀하고 효과적
  • 도전적 현실: 과학도 어렵듯이 TDD도 어렵습니다
  • 가치 제안: 소프트웨어가 의도대로 작동하기를 진정으로 원한다면, 가장 엄밀하고 효과적인 프로세스 중 하나입니다

실용적 팁과 주의사항

팁 1: 항상 테스트 먼저 작성하기

  • : 테스트가 실패하는 것을 보아야만 테스트 코드의 정확성을 경험적으로 증명할 수 있습니다
  • 효과: 전체 과학적 증거의 절반을 상실하지 않기 위해 필수

팁 2: 테스트 실패 확인의 중요성

  • 특성화 테스트 작성 시 테스트가 반드시 실패하도록 의도적 조작
  • 이를 통해 테스트 로직이 실제로 작동함을 증명

팁 3: 코드 읽기와 실행의 차별성 인식

  • 코드를 읽었다고 해서 이해한 것이 아님
  • 반드시 실행하여 경험적 검증 수행

주의사항 1: 동어반복적 단언(Tautology) 회피

  • 테스트가 무조건 통과하도록 작성되면 검증 불가능
  • 테스트 작성 후 실패 여부 확인 필수

주의사항 2: 테스트 코드의 버그 가능성

  • 테스트 코드도 프로덕션 코드 마찬가지로 버그 포함 가능
  • 테스트 실패 원인 분석 시 테스트 코드도 검토 필요

주의사항 3: 레거시 코드 개선 시 주의

  • 기존 버그를 실수로 정상 작동으로 기록하지 않도록 주의
  • 테스트 작성 전에 실제 동작 확인 필수

참고 자료

1개의 좋아요