5 minute read

20장: 소프트웨어 품질

20.1 소프트웨어 품질의 특성

  • 유저가 바라보는 외적 특성, 프로그래머가 바라보는 내적 특성이 있으며, 어떤 수준에서는 내적 특성이 외적 특성에 영향을 미친다.
  • 특성들은 서로 긍정적, 부정적 관계를 미치기도 한다(내구성이 높으면 정확성이 떨어진다던지).
  • 외적 특성 : 정확성, 유용성, 효율성, 신뢰성, 무결성, 적응성, 정밀성, 견고성
  • 내적 특성 : 유지보수성, 유연성, 이식성, 재사용성, 가독성, 테스트 용이성, 이해성

    20.2 소프트웨어의 품질을 향상시키기 위한 기법들

  • 테스트 전략, 코드 리뷰, 프로세스 등이 다 중요하지만 가장 중요한 것은 품질의 목표이다.
  • 품질의 특성 중 어떤 것에 가장 집중할 것인지 명확하게 정한다. 모든 목표를 동일하게 달성할 수 없다는 것을 명심한다.
  • p654. 이 놀라운 결과의 의미는, 사람들이 실제로 자기에게 주어진 일을 한다는 점이다. 프로그래머는 높은 성취동기를 갖고 있으며, 그들은 지정된 목표를 위해서 일을 할 것이다. 하지만 목표가 무엇인지 반드시 알려주어야 한다.

    20.3 품질 향상 기법의 상대적 효과성

  • 결함 감지 기법에는 여러 가지가 있으나, 단독적으로 사용하는 것만으로는 큰 효과를 보지 못한다. 가장 많이 쓰는 유닛테스트와 통합테스트도 최빈수 비율이 각각 30%, 35%이다.
  • 일반적인 조직의 결함제거 효율(평균 85%)보다 익스트림 프로그래밍의 감지비율이 더 높다(90-97%)
  • 사람이 코드를 읽고 결함을 발견하는 것이 테스트보다 훨씬 효과적이라는 연구가 매우 많다. 코드 리뷰가 매우 중요하다.
  • 추천할만한 기법 조합
    • 모든요구 사항, 아키텍처, 그리고 시스템의 주요 부분에 대한 설계의 형식적 조사
    • 모델링이나 프로토타이핑
    • 코드 읽기나 조사
    • 수행 테스트

      20.4 품질 보증 활동 시기

  • 정해진 시기는 없다. 결함은 소프트웨어의 모든 단계에 들어있다.

    20.5 소프트웨어 품질의 일반적인 원칙

  • 품질의 향상이 개발 비용을 줄인다.
  • 디버깅과 리팩토링, 수정 작업이 전체 시간의 50%를 차지한다. 오류를 예방하여 디버깅을 줄이면 생산성은 향상된다.

21장: 협력 구현

21.1 협력 개발 방법 개요

  • 다른 프로그래머가 자신의 작업을 지켜본다는 사실은 여러 방면에서 도움이 된다. 함께 코드를 봄으로써 결함 감지율이 줄고, 좀 더 주의깊게 코드를 작성할 수 있으며, 코딩 문화를 만들고, 훌륭한 개발자로부터 스킬을 전수받을 수 있는 기회이다.

    21.2 짝 프로그래밍

  • 페어 프로그래밍은 감시가 아니다. 강요해서도 안된다. 페어프로그래밍을 하는 대부분의 조직들은 결국 부분적으로만 페어프로그래밍을 한다.
  • 성격이 맞지 않는 짝, 초보자들끼리 짝을 짓지 않는다.

    21.3 형식적인 정밀 검토

  • 수정이 아니라 발견에 초점을 두는 활동이다. 대안을 찾거나 옳고 그름을 판단하거나, 작성자를 비판해서는 안 된다.
  • 모든 참석자가 각자의 역할이 있으며, 각자의 역할에 맞게 코드를 혼자 검토하는 시간을 갖는다.
  • 검토자는 결함을 고칠 결정권이 작성자에게 있음을 기억해야 한다.

    21.4 여러 가지 협력 개발 방법

  • 코드 읽기 : 두 명 이상의 사람이 코드를 각자 읽고 모두 모여 논의한다. 개별적인 검토 시간이 주어지므로 좀 더 유연하다.

22장: 개발자 테스트

22.1 소프트웨어 품질에서 개발자 테스트의 역할

  • 테스트 자체는 품질을 향상시키지 않는다. 테스트를 더 많이 할 게 아니라 더 잘 개발하려고 노력해야 한다.
  • 테스트에는 ‘오류를 발견할 수 있을 거라는 가정’이 요구된다. 따라서 반드시 오류를 찾을 수 있기를 희망하고 테스트를 작성해야 한다.

    22.2 개발자 테스트에 대한 바람직한 접근 방법

  • 반드시 이 내용들을 확실하게 이해해야 한다,
  • 각각의 연관된 요구사항과 설계사항들이 구현되었는지를 보장하기 위해 테스트를 하라.
  • 요구 사항에서 자주 빠뜨리는 사항에 대한 테스트도 고려하라. 보안, 저장소, 설치절차, 신뢰성 수준 등이 그렇다.
  • 기초 테스트(??)를 사용하라.
  • 최소한 코드의 모든 줄을 테스트해야 한다.
  • 테스트 먼저 짜라.
  • 다음 한계를 주의한다.
    • 개발자는 코드가 깨지는 상황을 테스트하기보다는 코드의 동작을 테스트하는 경향이 있다.
    • 커버리지를 낙관적으로 보지 마라. 보통 95% 정도를 생각하지만, 실제로는 50-60%를 달성하고 있다. 보다 나은 기준은 100%를 달성하는 것이다.

22.3 여러 가지 교묘한 테스트 방법

  • 모든 케이스를 테스트하는 건 불가능하므로, 오류가 발생할 것 같은 케이스를 테스트하라. edge case 등.
  • 구조적 기초 테스트는 모든 코드라인을 hit하는 테스트케이스를 말한다. if/while 등의 조건문을 모두 테스트하려면 케이스가 매우 많아진다. 따라서 테스트 대상이 되는 함수는 짧을수록 좋다.

    22.4 전형적인 오류

  • 파레토의 법칙을 따른다 - 오류의 80%는 클래스나 루틴의 20%에서 발견되었다. 오류의 50%는 클래스의 5%에서 발견되었다.
  • 오류를 유발할 가능성이 있는 루틴은 프로그래밍에서 가장 비싼 엔티티였고, 개발자들은 그 복잡한 루틴을 수정하기를 피한다.
  • 오류의 16-19%는 설계에 대한 잘못된 이해에서 비롯되었다. 설계를 잘 이해해야 한다.
  • 테스트 자체의 오류보다 테스트 데이터가 잘못된 경우가 더 많다. 요구사항에 대한 요청을 받았을 때 테스트에 대한 계획을 함께 세우라.

    22.5 테스트 지원 도구

  • N/A

    22.6 테스트를 향상시키는 방법

  • 초기부터 테스트 계획을 세운다. 자동화된 회귀 테스트를 고려한다.

    22.7 테스트 기록을 보존하는 방법

  • N/A

23장: 디버깅

23.1 디버깅 이슈 소개

  • 최고의 프로그래머는 결함도 적고 디버깅하는 시간도 적었다.
  • 결함이 있다 = 프로그램의 동작을 완전히 이해하지 못하고 있다 = 시행착오를 통해 프로그래밍을 하고 있다.
  • 결함을 수정하는 것이 아니라, 결함을 피하는 법을 먼저 배워야 한다.
  • 디버깅은 가독성, 설계, 코드 품질과 같은 구현 관련 사항이 집중되어 있는, 자신을 발전시킬 수 있는 비옥한 땅이다.
  • 겉으로 보이는 것을 수정하지 말고, 문제를 완벽하게 이해하기 위해 시간을 들이고 노력한다.

    23.2 결함 발견

  • 다양한 테스트케이스를 기반으로 문제를 reliable 하게 만들고, 분석을 통해 결함에 대한 가설을 세워 증명하는 형태가 되어야 한다.
  • 결함이 잘 안 찾아진다면 코드가 잘 작성되지 않았기 때문일 수 있다. 테스트 케이스를 개선하고, 유닛 테스트를 만들어라.
  • 시도해 볼 목록을 만들고, 안 되면 넘어가야 한다. 막다른 길로 가지 않도록 유의할 것.

    23.3 결함 수정

  • 문제를 수정하기 전에 반드시 이해하기.
  • 빨리 해결하려고 하지 마라.
  • 증상이 아니라 문제를 해결해라.
  • ‘이렇게 하면 되지 않을까?’라는 주술적 프로그래밍을 피해라.

    23.4 디버깅에서 심리학적으로 고려해야 할 사항

  • 디버깅은 가설을 세우고 가설을 방법론적으로 부인하는 과정이다. 쉽지 않다.
  • 디버깅도 정신적인 활동을 요구한다. 비평적이고 엄격한 사고.
  • 코드를 쓰면서 디버깅을 한다면 설계를 위한 창조적 사고와 디버깅을 위한 비판적 사고를 동시에 해야 한다.

    23.5 디버깅 도구 ? 분명한 도구와 그렇지 않은 도구

  • 실행 프로파일러 : 작성한 코드가 적당한 시간을 소모하는가 ?

24장: 리팩터링

24.1 소프트웨어 진화의 종류

  • (품질이 향상되느냐/떨어지느냐) * (구현중이냐/유지보수중이냐) c## 24.2 리팩터링 소개
  • 복붙은 설계 오류이다. DRY.

    24.3 구체적인 리팩터링

  • 리팩터링 책 참고

    24.4 안전한 리팩터링 방법

  • 간단한 변경도 심각하게 검토해라. 대개 1-5줄의 변경이 그 이상의 변경보다 더 많은 에러를 낳는다. 너무 사소해서 테스트도 리뷰도 안 하기 때문에.

    24.5 리팩터링 전략

  • 리팩터링 책 참고

25장: 코드 최적화 전략

25.1 성능이란?

  • 코드의 속도와 성능의 연관성은 결코 크지 않다. 코드를 최적화하려 노력하는 만큼 제품의 다른 품질 특성에 대해서는 작업을 하지 않게 된다. 대체로는 품질 특성이 높은 것이 유저에게 더 좋은 평판을 받으니 주의할 것.

    25.2 코드 최적화 소개

  • 코드를 최적화하는 것보다 아키텍쳐/알고리즘/하드웨어 등을 바꾸는 것이 더 쉽다. 그러나 효율적인 코드를 짠다는 것은 훌륭한 프로그래머라는 것을 의미한다.
  • 효율적인 코드가 항상 좋은 코드는 아니라는 것에 주의한다.
  • 파레토의 법칙 : 프로파일러를 활용해 가장 많이 사용되는 소수의 부분을 최적화하는 데 집중할 것.
  • 프로그램을 올바로 만들고 최적화해라. 나중에 작업하기 쉽도록 모듈화하고, 정확히 만들고 나서 성능을 확인해라. 초기 단계부터 최적화하려고 들면 큰 숲을 보며 중요한 부분을 최적화하는 대신 못하고 엉뚱한 부분에 시간을 쏟게 된다.

    25.3 느리고 비대한 부분

  • 입출력, 페이징, 컨텍스트 스위칭, 수학함수 등

    25.4 측정

  • 추정말고 측정해라. 프로파일러를 써라.

    25.5 반복

  • 개별적인 최적화로 10배씩 빨라지지 않는다. 그러나 최적화가 누적되면 충분히 성능이 개선된다.

    25.6 코드 최적화 단계 요약

  • N/A

26장: 코드 최적화 기법

  • 정의상, 코드 최적화는 성능을 향상시키는 대신 내부 구조를 손상시킨다. 책에서는 내부 구조를 손상시키지 않는다면 최적화로 생각하지 않는다. (리팩토링<->최적화)

    26.1 논리 구조

  • 복잡한 논리구조를 테이블 참조로 바꾼다. bool a, b, c 의 조합에 따라 값이 바뀌어야 한다면 if 문을 늘어놓지 말고 테이블로 만들자.

    26.2 반복문

  • 가장 빈번한 루프를 안쪽에 작성한다.

    26.3 데이터 변환

  • 부동소수점보다는 정수, 다차원 배열 최소화, 배열 참조 최소화

    26.4 표현식

  • 덧셈을 곱셈으로, 멱승을 곱셈으로, 작은 형으로(longlong->long/int, double->float), n*2 / n/2 는 쉬프트로.

    26.5 루틴

  • 인라인

    26.6 저급 언어를 이용한 재구성

  • N/A

    26.7 변경이 많을수록 상태는 그대로

  • N/A