박종훈 기술블로그

안전하고 아름다운 소프트웨어 만들기 (이성호 교수님)

10월 10일 스타트업 리딩클래스 라는 프로그램을 통해 충남대학교의 이성호 교수님께서 우리 회사(lucentblock)에 방문해주셔서 강연을 해주셨다. 우리 회사 사람들 외에도 다양한 분들이 오셔서 강연을 들으러 오셨었다.

그동안 계속 정리해야겠다 마음은 먹었는데, 시간이 나지 않아서 정리하지 못하고 있다가 이제서야 정리를 할 수 있게 되었다.

사실 솔직히 이야기 해서 처음에는 강연자에 대한 정보도 없이 제목만 전달 받았을 때는 크게 기대하고 있지 않았다. “안전하고 아름다운 소프트웨어 만들기” 라는 제목만 들었을 때는 ‘도대체 안전하고 아름다운 소프트웨어 라는게 무엇을 의미하는가?’ 라는 생각이 들었었다. (참여의 강제성이 있었기 때문에 그 부분에서 조금 더 부정적인 인상이 있었던것 같다.)

근데 막상 들으니 그런 부정적인 인상이 싹 사라질 정도로 정말 유익했던 이야기를 많이 해주셨다. 구글에서 겪은 이야기들도 해주셔서 재미있었다.

그 중 정리한 내용들을 간단하게 기록을 위해 남긴다.


소프트웨어 결함과 비용

소프트웨어 결함은 늦게 발견될수록 더 많은 비용을 초래한다.

graph of 'cost to repair'

Applied Software Measurement: global analysis of productivity and quality

고객에게는 실망을, 조직원에게는 어려움을 유발하여 충성도가 하락되게 한다. 특히, 배포 후에는 결함으로 인한 생명, 재산 피해가 발생하여 브랜드 이미지에 타격이 생긴다.

소프트웨어 오류를 방지하는 두 가지 방법

  • Fault tolerance (Recovery)
    • 결함 극복 메카니즘을 통해 결함 발생 시 스스로 회복하여 올바른 동작을 수행하도록 함.
      • rollback : 특정 지점부터 다시 실행
      • redundancy : 중복 신호 송출
      • mirroring : 복사본 유지
  • Fault prevention (avoidance)
    • 소프트웨어 배포 전 결함을 탐지하여 수정
      • code review
      • testing & analysis
      • verification

소프트웨어 품질 보장을 위한 방법들

방법AssuranceCost
formal verification높음높음
static & dynamic analysis중간중간
testing and code review낮음낮음

소프트웨어 검사 기법 (수동 vs 자동)

수동 분석

  • 사람이 직접 프로그램을 확인하고 옮고 그름 판단
  • 비용이 저렴하거나 (e.g. code review) 혹은 아주 비쌈 (e.g. formal verification)
  • 사람의 노력이 요구되므로 확인하는 소스코드의 양 대비 많은 시간 소요
  • 검사하는 사람의 전문성에 의존적

정형 검증 (formal verification)

  • 프로그램의 동작을 논리식을 활용하여 증명

    • 프로그램을 논리식으로 변환 (사람이)
    • 프로그램이 만족해야 할 스펙을 theorem(정리)으로 정의 (사람이)
    • 논리식이 스펙을 만족하는지 증명 (사람이)
    • 증명이 옳은지 그른지는 증명 보조 도구가 자동으로 검증
      • Coq, HOL, Isabelle, …
  • ‘수학적 증명’ 이므로 프로그램이 항상 옳다는 것을 엄밀하게 검증

    • ‘정형 검증을 거친 프로그램은 절대 결코 잘못 동작하지 않는다’
  • 고도로 훈련된 사람이 많은 시간과 노력을 쏟아야 함.

    • 논리식 및 증명 기법에 대한 충분한 지식이 필요
    • 큰 프로그램에 대해 적용이 어려움

코드 리뷰

  • 구현 또는 변형된 프로그램을 동료가 확인하여 검증

    • 누가? : 동료 또는 제 3의 리뷰어
    • 무엇을? : 작성된 코드의 올바름을 평가하고 스타일 기준 준수 여부를 확인
      • 피드백은 개발자의 코드 리팩터링으로 이어짐
      • 피대븍 반영 후, 리뷰어가 반영 여부 승인 (LGTM)
    • 언제? : 개발자가 코드를 작성한 후 코드베이스에 반영을 원할 때
      • 코드의 변화가 너무 크거나 작지 않아야 좋다.
      • 코드베이스에 반영되기 이전에 수행.
  • 주로 코드 리뷰 도구의 도움을 받아 수행

    • 코드 리뷰 도구는 코드의 변화(diff)를 보여주고 코멘트를 달 수 있는 기능 제공
    • 코멘트 반영 후 의견과 함께 ‘resolved’ 하여 반영 결과 회신
    • 도구는 기법과 활용할 수 있으며 개발 workflow에 쉽게 통합되어야 함.
      • IDE 또는 버전 관리 도구에 통합
  • 리뷰의 품질과 신속성이 개개인의 리뷰어에 의존적인 단점

    • 고품질의 코드 리뷰를 위해 개발자 대상 교육이 필요
    • 개발자 상호간의 신뢰와 한 팀이라는 공동체 의식이 중요
  • 코드리뷰를 통해 개발을 진행하였을 때 80% 이상의 오류 감소 효과가 있었다고 함.

  • 대다수의 소프트웨어 회사에서 사용중

자동 분석

  • 프로그램이 프로그램을 확인하여 옳고 그름 판단
  • 분석 도구의 개발 비용은 비싸나, 활용 비용은 저렴
  • 자동화 또는 준 자동화 된 방법으로 많은 양의 코드를 비교적 빠른 시간에 확인 가능

동적 분석

  • 프로그램을 적절한 입력과 함께 실행하여 동작을 분석
  • 실행 환경에 의존적
  • 제약사항 : 프로그램에 모든 입력을 넣어볼 수는 없음
    • 다수의 ‘미탐 (false negative)’ 발생 가능

정적 분석

  • 프로그램을 실행하지 않고, 가능한 실행 동작을 어림잡아 분석
  • 실행 환경에 독립적
  • 제약사항 : 프로그램을 실행하지 않기 때문에 완벽하게 어림잡기는 불가능
    • 오탐(false positive) 또는 설계에 따라 미탐(false negative)도 발생 가능

소프트웨어 분석 기법의 특성

  • 안전성 (soundness)

    • 프로그램의 실행 가능한 모든 행동의 분석
    • 실행 불가능한 행동도 가능하다고 분석해 낼 수 있음. 오탐 (false positive)
  • 완벽성 (completeness)

    • 프로그램의 실행 가능한 행동만 분석
    • 실행 가능한 행동도 분석해 내지 못할 수 있음. 미탐 (false negative)

soundness vs completeness

출처 : https://courses.cs.washington.edu/courses/cse403/16au/lectures/L15.pdf

프로그램 테스팅

  • 프로그램에 입력을 주어 실행을 통해 분석
    • ‘특정 입력’에 대해 올바름 또는 올바르지 않음을 확인
    • 프로그램에 오류가 있음을 밝혀낼 수 있으나, 오류가 없음을 증명할 수는 없음
  • 불안전 하지만 완벽 (미탐은 있으나 오탐은 없음.)
  • 테스팅을 수행하기 위해서는 test suite, oracle, test harness가 필요
    • test suite : test case의 집합
    • test case : 프로그램에 주어질 각 입력
    • oracle : 각 입력에 대한 프로그램의 올바른 수행 결과
    • test harness : 각 test case를 대상 프로그램에 대해 실행 후 실행 결과 관찰

좋은 테스트 작성하기

  • 테스트는 그 자체로 완전한 프로그램이며, 다른 테스트와의 의존성이 없어야 함.
  • 테스트 작성 시 아래와 같은 사항은 지향
    • 테스트 순서에 대한 의존성 : Test A 실행 후 Test B 가 실행되어야 하는 경우
    • 테스트 간의 상호 호출 : Test A 가 Test B 를 호출
    • 테스트 간의 변경 가능한 자료의 공유 : Test A, Test B가 mutable object를 공유할 경우 테스트 간의 순서 의존성 발생

테스트 품질 평가하기

  • 테스트가 프로그램을 얼마나 속속들이 검사하는지에 대한 지표
  • 주로, 테스트 커버리지 (test coverage)를 통해 평가
    • 하지만 휴리스틱
    • 품질을 평가하는 하나의 지표는 될 수 있겠지만 높은 커버리지가 보품질을 보장하는 것은 아님.

휴리스틱 불충분한 시간이나 정보로 인하여 합리적인 판단을 할 수 없거나, 체계적이면서 합리적인 판단이 굳이 필요하지 않은 상황에서 사람들이 빠르게 사용할 수 있게 보다 용이하게 구성된 간편추론의 방법 - 위키 -

퍼즈 테스트 (fuzzing)

  • 자동으로 생성한 다수의 입력으로 프로그램을 실행시켜 행동을 관찰하는 테스트 기법
    • 주로 개발자가 예상하지 못한 입력을 넣어 프로그램의 비정상 행동을 유도
  • 참고 : https://www.fuzzingbook.org/

프로그램 정적 분석

  • 프로그램을 분석하기 좋은 형태(IR)로 바꾸어 실행 없이 행동을 어림잡아 분석
    • 컴파일러의 프로그램 오류 검사
    • Linter
  • 명시적 입력 없이 모든 경우의 수를 고려하여 가능한 행동을 분석
  • 다양한 유료, 무료 도구들이 있음

Google 소프트웨어 개발 적용 사례

  • 디자인 문서, 발표 및 상호 리뷰 : 팀원 뿐 아니라 관심있는 누구나 참여 가능
  • monolithic codebase (google3)
    • 모든 코드를 하나의 저장소에 관리
      • 장점: 통합 버전 관리, 코드 공유, 의존성 관리, 협업 등
      • 단점: 큰 사이즈의 코드 → 이를 개발, 실행, 관리할 수 있는 도구 개발해야 함.
  • 웹 IDE (Cider) 사용 : 클라우드 기반 스토리지 (CitC) 와 연동 - no files in local
  • Changelist(CL) 를 만들어 Piper에 변경 요청
    • 변경된 파일들, 설명, 메타데이터 포함
    • 반드시 변경에 대한 테스트와 함께 제출해야 함
      • 높은 유닛 테스트 커버리지를 달성하도록 강력하게 권장
        • 일일이 확인하지 않으나, 모두가 노력하고 있음
    • CL 생성 시 코드가 바로 반영 되지 않고
      • 정적 분석 도구 (Tricoder)에 의해 코딩 스타일, 결함 등 자동으로 분석
      • “precommit”이 수행되어 변경된 파일 포함 관련 파일들의 모든 테스트 자동 수행
      • 코드 품질, 테스트 커버리지, 분석 미 테스트 겨로가는 코드 리뷰 도구로 전달
  • 코드 리뷰는 Critique 라는 도구를 통해 수행됨.
    • 개발자는 CL에 대한 code review 요청
    • Reviewer 는 diff를 확인하고 코멘팅
    • 개발자는 코멘트를 반영하여 수정
    • LGTM을 받을 때 까지 반복
    • 두 단계의 코드 리뷰 절차가 있음.
      • First round : 설계부터 기능, 테스트 까지의 전반적인 검토
        • 설계 : 코드가 잘 설계 되었는가?
        • 기능 : 코드가 개발자의 의도대로 동작하는가?
        • 복잡고 : 코드가 가능한 간단하게 작성되었는가?
        • 테스트 : 적절한 테스트가 수립되었고 자동으로 동작하는가
        • 팀원 또는 관련 부분을 잘 알고있는 동료가 수행
      • Second round : 코드가 ‘읽기 쉬운가’? (readability)
        • 이름 : 변수, 클래스, 함수, 파일의 이름은 직관적이고 명확한가?
        • 주석 : 함수, 클래스, 불명확한 변수에 유용하고 명확한 주석이 있는가?
        • 스타일 : 지정된 코드 스타일 가이드를 준수하여 작성되었는가?
        • 문서화 : 개발자는 관련 문서를 적절하게 수정하였는가?
        • 별도의 자격을 가진 readability reviewer가 수행
  • 소프트웨어 골개 및 외부 라이브러리 활용을 제한함
    • 내부용과 외부용을 별도로 관리
    • 외부 라이브러리는 별도의 통합 디렉토리에 관리하여 보안 위협에 대처함.
  • Nightly 테스트, 정적 분석, 그리고 퍼징
    • 시간이 오래 걸리는 시스템 테스트는 코드 수정이 일어나지 않는 시간대에 수행 (개발에 방해되지 않도록)
    • 여러 정적 분석 도구를 함께 활용하여 게이트 키퍼로 활용
    • 소프트웨어 배포 전 퍼징을 통해 프로그램 안정성 검사

마무리

  • 소프트웨어 개발은 복잡하고 어렵고 조심스러운 협동 과정이다.
    • 소프트웨어가 다양한 곳에 다양한 목적으로 활용되어 그 중요도가 크게 증가하였다.
    • 안전하고 아름다운 소프트웨어는 재산, 인명, 그리고 브랜드 가치를 보호하는데 중요하다.
  • 소프트웨어의 안정성 및 가독성 검사는 개발 비용 증가 및 번거로움을 초래하기도 한다.
    • 그러나, 소프트웨어의 신뢰도를 높이고 유사상황 발생을 억제하는 장치이다.
    • 소프트웨어 결함으로 인해 발생할 수 있는 잠재적 비용 감소에 크게 기여한다.
  • 적용 가능한 다양한 기법을 최대한 활용하여 안전하고 아름다운 소프트웨어 개발을 하자.
    • 개발자 개인 뿐 아니라 회사까지 모두가 관심을 가져야 할 사항이라 생각된다.