실용주의 프로그래머 - 2022.04.02 - 7장.코딩하는 동안

moontag·2022년 4월 2일
0

북클럽 TIL

목록 보기
7/12

DAY 16 (p.299-374 전자책기준)

7장.코딩하는 동안

📚 오늘 TIL 3줄 요약

  • 철저한 테스트 계획은 유지 보수 비용과 고객지원실에 걸려오는 전화 횟수📞를 줄일 수 있다.
  • 성급한 최적화는 모든 악의 근원이다.
  • refactoring은 잡초 제거처럼 위험하지 않은 작은 단계를 밟는 일상활동이다


기억하고 싶은 내용


  • 적극적으로 자기 코드에 대해 생각하지 않는 프로그래머는 우연에 맡기는 프로그래밍을 하는 것이다

파충류의 뇌에 귀 기울이기

  • 본능적인 뇌가 내는 소리에 귀 기울이기
  • 백지의 공포
  • 작성한 코드에 대한 걸 설명해보고 문제를 생각해보기

<빈 에디터 화면 앞에서>

  • 프로토타이핑은 원래 실패한다고 자신에게 상기시켜라
  • 하고싶은 것을 한 문장의 주석으로 표현하고 코딩을 시작하라


우연에 맡기는 프로그래밍

구현에서 생기는 우연

  • 정말로 돌아가는 게 아니라 그저 돌아가는 듯이 보이는 것일 수도...
  • 화면해상도나 CPU코어가 더 많은 경우 등 다른상황에서는 이상하게 작동할지도..
  • 문서화안된 동작은 라이브러리 다음 릴리즈에서 변경될 수도 있다
  • 불필요한 추가 호출은 코드를 느리게 만든다
  • 추가 호출한 루틴에 새 버그 생길 수도 있다
  • 문서화된 동작에만 의존하라

*협정 세계시 UTC가 괜히 있는 것이 아니다. UTC를 사용하라

  • 우연에 맡기는 프로그래밍을 하지 마라
  • 가정하지 말라. 증명하라

의도적으로 프로그래밍하기

  • 후배 프로그래머에게 코드를 상세히 설명할 수 있는가? 못한다면 우연이다
  • 왜 동작하는지도 잘 모르는 코드를 만들지 말라
  • 계획을 세우고 이를 토대로 진행하라
  • 가정에 의존하지 말고 신뢰할 만한 것에 의존해라. 어렵다면 최악의 가정을 해라
  • 가정을 기록으로 남겨라
  • 가정도 테스트해봐라. 단정문 작성하라
  • 우선순위대로 시간투자하라
  • 기존코드가 앞으로 짤 코드를 지배하지 못하도록하라. 리팩터링


알고리즘의 속도

알고리즘을 추정한다는 말의 의미

입력의 크기가 늘어날수록 알고리즘 수행시간이 길어지거나 사용하는 메모리 양이 늘어난다.
수행시간과 필요한 메모리 양을 계산하는, 상세한 분석을 해야하는 경우 O 표기법이 유용하다

대문자 O 표기법

근삿값을 다루는 수학적 방법으로 O()와 같이 표기한다
정렬루틴이 원소 n개를 정렬하는데 O(n²)시간이 걸린다고 할때,
최악의 경우 시간이 n의 제곱에 비례하여 늘어난다
시간의 최댓값이 n²보다 더 빨리 늘어나진 않는다
최상위 차수를 제외난 나머지 차수는 모두 제거하고 상수인 계수도 표기안한다
O(n²/2 + 3n)은 O(n²/2)와 같고 O(n²)과도 같다

O( )Big-O notation
O(1)상수. (배열의 원소 접근, 단순 명령문)
O(lgn)로그. (이진 검색) 로그의 밑은 중요치 않다. 따라서 O(logn)과 동일하다
O(n)선형. (순차 검색)
O(nlogn)선형보다는 안 좋지만 그래도 많이 나쁘진 않다 (퀵 정렬quicksort와 힙 정렬heapsort의 평균 수행시간)
O(n²)제곱. (선택 정렬과 삽입 정렬)
O(n³)세제곱. (두 n Χ n 행렬의 곱)
O(Cⁿ)지수. (여행하는 외판원 문제, 집합 분할 문제)



상식으로 추정하기

  • 단순 반복문
    반복문 하나가 1부터 n까지 돌아간다면 O(n)일 가능성이 크다
    대표적 예) 소진탐색exhaustive search 배열에서 최댓값찾기, 체크섬 생성하기 등

  • 중첩 반복문
    반복문이 중첩되면 알고리즘은 O(m Χ n)이 되며, m,n은 두 반복문의 반복 횟수다. 보통 버블정렬처럼 간단한 정렬 알고리즘에서 나타나는데, 바깥 반복문의 배열의 각 원소를 차례대로 방문하고, 안쪽 반복문은 그 원소를 정렬된 결과 중 어디에 둘 것인지 찾는다. 이런 정렬 알고리즘은 O(n²)이 되기 쉽다.

  • 반씩 자르기
    반복문을 돌 때마다 작업 대상의 수를 반으로 줄이는 알고리즘이면 O(lgn)이 될 가능성이 크다.
    예) 정렬된 목록의 이진 검색, 이진 트리의 탐색, 정수의 2진수 표현에서 첫 번째 1인 비트를 찾는 문제 등

  • 분할 정복 divide and conquer
    입력 데이터를 둘로 나눠서 각 독립적으로 작업한 후, 결과를 합치는 알고리즘은 O(nlogn)일 수 있다.
    예) 퀵 정렬
    퀵 정렬은 데이터를 반으로 나누고 각 반쪽에서 재귀적으로 정렬을 수행한다. 이미 정렬된 입력값이 들어로 때는 성능이 떨어지기 때문에 엄밀하게는 O(n²)이지만, 퀵 정렬의 평균 수행 시간은 O(nlogn)이다.

  • 조합적 combinatoric
    알고리즘이 항목의 순열을 다루기 시작하면 대부분 수행 시간은 걷잡을 수 없이 늘어난다. 순열엔 계승이 따라오기 때문이다.
    (1부터 5까지의 숫자로 이루어진 순열은 5!=5x4x3x2x1=120가지나 있다)
    원소 5개를 처리하는 조합적 알고리즘의 시간을 재 보자. 원소 6개 처리는 시간이 6배나 더 걸릴 것이다. 7개면 시간이 42배다.
    난해한 문제를 푸는 알고리즘이 대개 여기에 속한다. 종종 한정된 문제 도메인에서 이런 알고리즘의 수행 시간을 줄이기 위해 휴리스틱을 동원하기도 한다.
    예) 여행하는 외판원 문제, 상자에 물건을 최적으로 집어넣는 문제, 숫자 십합을 분할해서 각 부분 집합의 원소 합을 모두 같게 만드는 문제 등

실전에서의 알고리즘 속도

  • O(n²)알고리즘이 있다면 분할정복으로 O(nlogn)으로 줄일 수 없는 지 시도해봐라

  • 입력값 n이 작다면, 단순한 O(n²) 코드가 복잡한 O(nlogn)보다 더 좋은 성능내기도 한다

  • 이론과 실무적 요인을 모두 고려하려고 노력하라

  • 여러분의 추정을 테스트하라

  • 코드 프로파일러를 사용하여 알고리즘 돌아갈때 각 단계의 실행 횟수를 센 다음 입력값 크기별 실행 횟수를 그래프로 그려보라.

  • 성급한 최적화는 모든 악의 근원이다.

  • 알고리즘 개선하느라 귀한 시간 투자하기 전에 그 알고리즘이 정말 병목인지 먼저 확인하가

책 추천

  • 로버트 세지윅 - 알고리즘, 알고리즘 분석 입문
  • 도널드 커누스 - The Art of Computer Pro-gramming <#1, 2, 3, 4A>


리팩터링

밖으로 드러나는 동작은 그대로 유지한 채 내부 구조를 변경함으로써 이미 존재하는 코드를 재구성하는 체계적 기법
- 마틴 파울러 'refactoring'

  • 리팩토링은 잡초 제거처럼 위험하지 않은 작은 단계를 밟는 일상활동이다

언제 하는가?

  • 중복
  • 직교적이지 않은 설계
  • 더 이상 유효하지 않은 지식
  • 이 기능은 사실 더 중요했었던 or 필요하다고 생각했었는데 아니였던 경우
  • 성능 개선 필요성
  • 테스트 통과했을 때, 추가한 코드로 가서 다시 정리하려고

기능을 옮기고 결정을 바꾸는 것은 고통스럽다..코드 열기가 무섭지만 그래도,,,

  • 일찍 리팩터링하고, 자주 리팩터링하라
  • 시간을 더 끌면 환자는 생명을 잃을 지도 모른다
  • 일정에 리팩터링할 시간을 확실히 포함시켜둬라

어떻게 하는가?

  • 리팰터링과 기능추가를 동시에 하지마라
  • 자주 테스트를 해봐라
  • 단계를 나눠서 작업하기
    메서드 하나 쪼개기, 변수명 하나 바꾸기 등 작은 단위로 작업
    단계끝날 때마다 테스트 돌린다면 긴 디버깅 작업을 피할 수 있다


테스트로 코딩하기

  • 테스트는 버그를 찾기 위한 것이 아니다
  • 테스트에 대해 생각하기
  • 테스트가 코딩을 주도한다

TDD test-driven development 테스트 주도 개발

테스트를 먼저 작성하는 것
반복 주기가 몇 분 주기로 짧아야 한다

1. 추가하고 싶은 작은 기능 하나 결정한다
2. 그 기능 구현됐을 때 통과하게 될 테스트를 하나 작성한다
3. 테스트 실행. 다른 테스트는 통과하고 방금 추가한 테스트 하나만 실패해야한다
4. 실패하는 테스트를 통과하는 최소 코드만 작성. 
   그리고나서 모든 테스트가 통과하는지 확인한다
5. 코드 리팩터링. 방금 작성한 테스트나 함수를 개선할 수 있는 부분이 없는지 살펴본다. 
   개선 후에도 테스트가 계속 통과하는지 확인한다

TDD: 목표가 어디인지 알아야 한다

  • 하향식 - 문제 전체에서 시작해서 조각으로 쪼개기를 반복하여 작게 만들기
  • 상향식 - 바닥에서 시작해 점차 코드를 쌓아올린다
  • 상향식, 하향식이 아니라 끝에서 끝까지 만들어라
  • 목적지를 떠올리지 않으면 같은 자리에서만 뱅뱅 맴돌 수도 있다
  • 맨 처음부터 테스트가 가능하게 만들고, 코드 연결 전에 하나씩 철저하게 테스트해야한다

단위 테스트 unit test

: 어떤 모듈에게 이것저것을 시켜보는 코드

  • 인위적 환경을 구축한 다음 테스트할 모듈의 루틴들을 호출한다
  • 그 다음 반환된 결과들을 이미 알고 있는 값과 비교하거나,
    똑같은 테스트를 이전에 돌렸을 때 나온 값과 비교하여 올바른지 검사한다
  • 회귀테스트regression testing : 동일한 테스트를 코드 수정 후 다시 돌려보는 것

계약을 지키는지 확인하기

  • 단위테스트는 계약을 잘 지키는지 보는 테스트다

임시 테스트 ad-hoc test

  • console.log(), 디버거나 IDE환경, REPL(Read Eval Print Loop)에 직접 실행하면서 입력하는 코드 조각일 수도 있다
  • 디버깅 작업이 끝나면 임시테스트를 정식 테스트의 형태로 만들어둬야 한다
  • 그냥 버리지 말고 기존의 단위 테스트 군단에 합류시켜라

테스트 접점 만들기

p.344

  • 기능 스위치를 활용하여 특정 사용자나 사용자 집답은 진단 메시지를 더 많이 남기게 할 수 있다

테스트 문화

  • 여러분이 테스트하지 않으면 사용자가 테스트하게 된다
  • 철저한 테스트 계획은 유지 보수 비용과 고객지원실에 걸려오는 전화 횟수를 줄일 수 있다.
  • 테스트 먼저
    최상의 선택지. 테스트 주도 설계. 때에 따라 먼저 쓰기 어렵거나 의미가 없을 수도 있다
  • 코드와 테스트를 함께
    코드를 조금 작성한 후 테스트 작성하고 다시 코드로 넘어간다
  • 나중에 테스트
    최악. 사실 "테스트 하지 않음" 이란 뜻이다
  • 테스트코드도 결합도 낮추고, 깨끗하고 견고하게 유지하라
  • 테스트, 설계, 코딩 모든 것이 프로그래밍이다


속성 기반 테스트

  • 코드에 존재하는 계약과 불변식을 뭉뚱그려 '속성'이라고 부른다
  • 속성기반 테스트 라이브러리로 가정을 검증하라
  • 속성기반 테스트가 실패했으면, 어떤 매개 변수를 사용한지 알아낸 다음 그 값을 이용해 별도의 단위 테스트를 정식으로 추가하는 것이 좋다.
  • 속성기반 테스트는 설계에도 도움을 준다


바깥에서는 안전에 주의하라

  • 코드가 잘못될 수 있는 경우 찾기
    경우에 대한 단위테스트 추가
    잘못된 매개변수 넘기거나 리소스 흘리거나 모자라는 경우 생각해보기

기본 보안 원칙

  1. 공격 표면을 최소화하라
  2. 최소 권한 원칙
  3. 안전한 기본값
  4. 민감 정보 암호화하기
  5. 보안 업데이트 적용하기
  • 공격 표면을 최소화하라
    공격 표면 : 공격자가 데이터를 입력, 추출하거나 서비스 실행시킬 수 있는 모든 접근 지점
    - 코드의 복잡성은 공격 매개체를 유발한다
    - 입력 데이터는 공격 매개체다. 외부입력은 오염됐으니 제거해라
    - 인증없는 서비스는 공격 매개체다. 전 세계 누구든지 호출할 수 있어서
    - 인증을 요구하는 서비스도 공격 매개체다
    - 출력 데이터는 공격 매개체다. 정보를 누설하지 마라
    - 디버깅 정보는 공격 매개체다.
  • 최소권한원칙 : 최소한의 권한만 꼭 필요한 시간만큼만 제일 짧에 부여하는 원칙
  • 안전한 기본값 ex)비번입력 - ****
  • 민감 정보를 암호화하라
    암호나 API키, SSH키, 암호화 비밀번호, 인증 정보를 버전관리 시스템에 넣지마라
    보통 빌드나 배포 프로세스 내에서 설정파일이나 환경 변수로 관리한다
  • 보안 업데이트를 적용하라

상식 대 암호

  • 암호를 절대 직접 만들지 말라
  • 라이브러리, 프레임웤, 오픈소스를 사용해라
  • MD5나 SHA1을 더는 쓰면 안 되는 지 문제를 이해해야한다


이름 짓기

  • 문화를 존중하라
    낙타표기법 camelCase
    뱀 표기법 snake_case

  • 일관성
    모든 팀원이 각 단어의 뜻을 알고 일관된 의미로 사용해야 한다
    프로젝트 용어로 자리 잡아가게 한다. (디자인 패턴같은 '패턴 언어'가 하는일)

  • 이름 바꾸기는 더 어렵다
    의도를 표현하지 못하거나 오해부를만한 헷갈리는 이름이면 고쳐라
    이름 바꾸기 쉽게 만들어라 etc

  • 이름을 잘 짓고 필요하다면 이름 바꿔라



소감

단위테스트, TDD, 리팩토링의 중요성에 대해서 배워봤다. 나중에 유지 보수 비용과 고객 전화 빈도를 낮추기 위해서 필요하다는 말이 크게 와닿았다.. 앞으로 같이 살아갈 코드와 나를 위해서 테스트는 필수라고 생각했다. 그리고 알고리즘 속도와 추정 부분은 아직 잘 모르겠다. 빅 O 표기법 관련해서 다시 복습해야겠다.

  • 리팩터링2도 읽어봐야겠다
    2판은 리팩터링 카탈로그를 자바스크립트 코드로 제시했다고 한다. 그래서 이해하는데 더 쉬울 것같다



궁금한 내용, 잘 이해되지 않는 내용은?



오늘 읽은 다른 사람의 TIL

profile
터벅터벅 나의 개발 일상

0개의 댓글