Books/실용주의 프로그래머

7장. 코딩하는 동안

유로띠 2022. 4. 1. 21:52
반응형

안녕하세요 😀

 유로띠 입니다 😉

 

 

 

 

 

 

실용주의 프로그래머


TIL (Today I Learned)

3줄 요약

 

✏️  테스트를 실행할 때가 아니라 테스트에 대해 생각하고, 테스트를 작성할 때 생긴다.

✏️  리팩터링이 필요한 코드는 '종양'이다.

✏️  이름 짓기 정말 어려워...

 

DAY 7

오늘 읽은 범위: 7장. 코딩하는 동안

 

😉 책에서 기억하고 싶은 내용을 써보세요.

🟢  코딩할 때는 매 순간 결정을 내려야 하는데, 프로그램이 정확하게 생산적으로 작동하면서 천수를 누리도록 하려면 사려 깊은 생각과 판단으로 결정을 내려야 한다. (P.273)

🟢  적극적으로 자기 코드에 대해 생각하지 않는 프로그래머는 우연에 맡기는 프로그래밍을 하는 것이다. 코드가 작동하긴 하지만 왜 그렇게 작동하는지 설명은 못한다. (P.274)

🟢  테스트의 긍정적인 효과는 대부분 테스트를 수행할 때 나타나는 것이 아니라, 테스트에 대하여 생각할 때 그리고 테스트를 작성할 때 나타난다는 것이다. (P.274)

 

파충류의 뇌에 귀 기울이기

🟢  개빈 드 베커(Gavin de Becker)는 그가 쓴 <<서늘한 신호>>에서 우리 인간은 더 동물적인 부분인 본능, 즉 파충류의 뇌를 무시하라고 배워 왔다고 한다. (P.275)

🟢  프로그래머로서 경험이 늘어 갈수록 여러분의 뇌에는 암묵적인 지식이 켜켜이 쌓인다. 잘되는 방법, 잘 안 되는 방법, 오류 형태별로 가능한 원인 등 일하는 동안 보고 듣고 느끼는 모든 것이 쌓인다. 여러분이 누군가와 대화를 하다가 멈출 때 본능을 관장하는 뇌는 파일 저장 버튼을 누른다. 일단 본능이 반응하고 있음을 인지하는 것이다. 그리고 왜 그런 느낌이 드는지 알아내야 한다. (P.276)

🟢  새로운 프로젝트를 시작하는 일도 두렵기는 마찬가지다. 많은 사람이 일을 시작하는 첫 발짝을 미루고 싶어한다. 

이런 문제에는 두 가지 원인이 있는데...

첫번째 원인은 파충류의 뇌가 여러분에게 무언가 할 말이 있어서다. 인식의 지평 바로 밑에 도사리고 있는 모종의 의심이 있다. 이런 의심은 중요하다.

다른 원인은 좀 더 진부한데, 여러분은 그저 실수할까 봐 두려운 것일 수 있다.

합리적인 두려움이다. 우리 개발자들은 코드에 많은 것을 투자한다. 그래서 코드의 오류를 자신의 부족한 능력 때문이라고 받아들일 수도 있다. 아마 '가면 증후군'의 요소 또한 있을 것이다. 이 프로젝트는 자신의 능력 밖이라고 생각할 수도 있다. 우리는 이 길의 끝에 무엇이 기다리고 있는지 모른다. 어쩌면 너무 멀리까지 가버린 후에 사실은 길을 잃었다는 것을 인정하게 될지도 모른다. (P.277)

 

🟢  코딩이 진창에서 오르막길을 겉는 것처럼 느껴지는 날도 있다. 한걸음을 떼려면 어마어마한 노력이 필요하고, 세 걸음 나아갔더니 두 걸음 미끄러지기도 한다. 

안타깝지만 진짜로 여러분이 해야 하는 일은 정반대다.

여러분의 코드가 무언가 말하려는 것이다. 지금 하는 작업이 필요 이상으로 힘들다고 말이다. 이유가 무엇이든 코드가 보내는 피드백을 파충류의 뇌가 느끼고 있다. 그래서 여러분의 주의를 끌기 위해 필사적을 노력하는 것이다. (P.278)

🟢  여러분 내면의 파충류에게 귀 기울여라. (P.278)

파충류와 이야기 하는 법
일단, 하고 있는 일을 멈춰라.
뇌가 정리를 좀 할 수 있도록 약간의 시간과 공간을 확보하라.
잠깐 머리를 비운 채로 할 수 있는 일을 하라. 산책을 하고 다른 사람과 수다를 떨어라. 아예 하룻밤 자면서 생각해 봐도 된다.
생각이 저절로 여러분의 뇌 층층이 스며들도록 놔둬라.
언젠가는 다시 생각이 의식의 영역으로 올라와서 '아하!' 하는 순간이 찾아올 것이다.
작성하는 코드에 대한 그림을 그려보라. 동료에게 설명해 보라.
하지만 여러분이 이런 방법들을 시도해 보았는데도 여전히 막혀 있을 수도 있다. 행동해야 할 시간이다.
바로 프로토타이핑을 하면 된다. (279)

 

🟢  놀이 시간이다! 기존코드는 잠시 다른 곳으로 밀어 두고 비슷한 것을 대신 프로토타이핑으로 만들어라 (P.279)

 

1. 포스트잇에 "프로토타이핑 중"이라고 써서 모니터 옆에 붙여라.
2. 프로토타이핑은 원래 실패한다고 자신에게 상기시켜라. 실패하지 않더라도 프로토타입은 버리는 것이라는 점도 함께 상기시켜야 한다. 프로토타이핑으로 손해 볼 일은 없다.
3. 텅 빈 에디터 화면에 여러분이 배우고 싶은 것 혹은 하고 싶은 것을 한 문장의 주석으로 표현해 보라.
4. 코딩을 시작하라. (P.280)

 

🟢  프로토타입을 만드는 도중 콧노래를 흥얼거리는 자신을 발견하고 놀라게 될 것이다. 코드를 만드는 느낌을 즐기고 있는 것이다. 불안함은 사라지고 빨리 해치우고 싶다는 느낌이 그 자리를 대신 채울 것이다. 이 일을 끝내자! (P.280)

🟢  여러분의 코드뿐이 아니다. 다른 사람들이 은연중에 적용한 패턴을 여러분은 의식적으로 적용할 수도 있다. 그 과정에서 여러분이 새로운 것을 배울 수도 있다. (P.281)

 

우연에 맡기는 프로그래밍

🟢  개발자인 우리들 역시 지뢰밭에서 일한다. 하루에도 수백 개가 넘는 함정이 우리가 빠지기를 기다리고 있다. 잘못된 결론을 내리지 않도록 언제나 주의해야 한다. 우리는 우연에 맡기는 프로그래밍,  곧 행운과 우연한 성공에 의존하는 프로그래밍을 하지 않아야 한다. 대신 '의도적으로 프로그래밍'해야 한다. (P.282)

🟢  우연한 행운과 주도면밀한 계획을 착각하기 쉬운 경우도 종종 있다. 몇가지 예를 들어보자.

구현에서 생기는 우연 - 마침내 화면이 그려지더라도 코드로 돌아가 불필요한 호출을 제거하지 않는다. "이제 돌아는 가니까, 그래도 놔두는 편이 더 나을 거야...." 이런 함정에 빠지기 쉽다. 잘 작동하는 데 괜히 건드려서 일을 만들 필요가 있을까? 우리가 보기에는 그래야 할 이유가 몇 가지 있다. (P.284)

🟢  다른 루틴을 호출할 때도 문서화된 동작에만 의존하라. 어떤 이유로든 그럴 수 없다면 추측을 문서로 상세히 남겨라. (P.285)

🟢  가정하지 말라. 증명 하라. (P.286)

🟢  우연에 맡기는 프로그래밍을 하지 말라. 확고한 사실에 근거하지 않은 가정은 어떤 프로젝트에서든 재앙의 근원이 된다. (P.287)

🟢  의도적으로 프로그래밍해야 한다.

의도적 프로그래밍
언제나 여러분이 지금 무엇을 하고 있는지 알아야 한다.
더 경험이 적은 프로그래머에게 코드를 상세히 설명할 수 있는가? 그렇지 않다면 아마 우연에 기대고 있는 것일 터이다.
자신도 잘 모르는 코드를 만들지 말라. 이것이 왜 동작하는 지 잘 모른다면 왜 실패하는지도 알 리가 없다.
계획을 세우고 그것을 바탕으로 진행하라.
신뢰할 수 있는 것에만 기대라. 가정에 의존하지 말라.
가정을 기록으로 남겨라.
코드뿐 아니라 여러분이 세운 가정도 테스트해 보아야 한다. 어떤 일이든 추측만 하지 말고 실제로 시험해 보라.
노력을 기울일 대상의 우선순위를 정하라. 중요한 것에 먼저 시간을 투자하라.
과거의 노예가 되지 말라. 기존 코드가 앞으로 짤코드를 지배하도록 놓아두지 말라. (289)

 

알고리즘의 속도

🟢  알고리즘이 사용하는 자원, 곧 시간, 프로세서, 메모리 등을 추정하는 것이다. (P.291)

🟢  빅오 표기법(big-O notation)은 우리가 측정하는 값-시간, 메모리 등-의 상한을 기술하는 표기법이다. (P.292)

🟢  빅오 표기법(big-O notation)은 수행 시간이든 메모리든, 아니면 다른 무엇을 나타내든 실제 숫자를 알려주지 않는다. 그저 입력의 크기가 바뀜에 따라 이 값이 어떻게 바뀔지를 알려줄 뿐이다. (P.293)

 

big-O notation

O(1) - 상수 (배열의 원소 접근, 단순 명령문)

O(log n) - 로그. (이진 검색) 로그 밑은 중요치 않다.

O(n) - 선형. (순차 검색)

O(n log n) - 퀵 정렬, 힙 정렬의 평균 수행 시간

O(n^2) - 제곱. (선택 정렬과 삽입 정렬)

O(n) - 세제곱. (두 n x n 행렬의 곱)

O(C^n) - 지수. (여행하는 외판원 문제, 집합 분할 문제)

 

🟢  숫자가 외부요인에 따라 달라진다면 잠시 작업을 멈추고 커다란 수가 들어왔을 경우 수행 시간이나 메모리 소모에 어떤 영향을 미칠지 생각해 보는 것이 좋다. (P.296)

🟢  사용하는 알고리즘의 차수를 추정하라 - O(n^2) 알고리즘이 있다면 분할 정복을 사용하여 O(n log n)으로 줄일 수 없는지 시도해 보라 (P.296)

🟢  최고라고 언제나 최고는 아니다 - 성급한 최적화(premature optimization)를 조심하라. 언제나 어떤 알고리즘을 개선하느라 여러분의 귀중한 시간을 투자하기 전에 그 알고리즘이 정말로 병목인지 먼저 확인하는 것이 좋다. (P.298)

 

리팩터링

🟢   소프트웨어 개발은 건축보다 정원 가꾸기에 더 가깝다. 딱딱하기보다는 유기적인 활동이다. (P.301)

🟢  코드 고쳐쓰기, 다시 작업하기, 다시 아키텍처 만들기는 모두 아울러서 '재구성(restructuring)'이라고 부른다. 그런데 그런 활동 중 일부를 따로 떼어 '리팩터링(refactoring)'이라는 이름으로 실천하기도 한다. (P.301)

 

마틴 파울러는 '리팩터링'을 다음과 같이 정의한다.
밖으로 드러러나는 동작은 그대로 유지한 채 내부 구조를 변경함으로써 이미 존재하는 코드를 재구성하는 체계적 기법.
1. 이 활동은 체계적이다. 아무렇게나 하는 것이 아니다.
2. 밖으로 드러나는 동작은 바뀌지 않는다. 기능을 추가하는 작업이 아니다.

 

🟢  코드를 바꾸기 쉽게 유지하는 것이다. 밖으로 드러나는 동작이 바뀌지 않는다는 것을 보장하려면 코드의 동작을 검증하는 좋은 자동화된 단위 테스트가 필요하다. (P.302)

🟢  리팩터링은 언제 하는가? - 리팩터링은 여러분이 무언가를 알게 되었을 때 한다. 여러분이 작년이나 어제, 심지어 10분 전과 비교해서 더 많이 알게 되었다면, 리팩터링을 한다. 주저하지 말고 변경하라. (P.302)

 

코드를 리팩터링할 이유는 많다.
중복
- DRY 원칙 위반을 발견했다.

직교적이지 않은 설계
- 더 직교적으로 바꿀 수 있는 무언가를 발견했다.

더 이상 유효하지 않은 지식
- 코드는 지금 상황에 뒤떨어지지 않아야 한다.

사용 사례
- 어떤 기능은 예전에 생각했던 것보다 더 중요하고, '꼭 필요하다'고  생각했던 기능은 그렇지 않은 경우도 있다는 것을 깨닫게 될 것이다.

성능
- 성능을 개선하려면 시스템의 한 영역에서 다른 영역으로 기능을 옮겨야한다.

테스트 통과
- 여러분이 코드를 조금 추가한 후 추가한 테스트가 통과했을 때가, 방금 추가한 코드로 다시 뛰어들어 깔끔하게 정리하기에 최고의 타이밍이다.

 

🟢  일정의 압박은 리팩터링을 하지 않는 단골 핑계다. 하지만 이는 설득력이 떨어진다. 지금 리팩터링을 하지 않으면 일이 더 진척되었을 때, 즉 신경 써야 할 의존성이 더 많아졌을 때 문제를 고쳐야 하고, 따라서 훨씬 더 많은 시간을 투자해야 한다. 그때가 되면 일정에 더 여유가 생길까? 그럴 리가. (P.304)

🟢  리팩터링이 필요한 코드를 일종의 '종양'이라고 생각하자. (P.304)

 

리팩터링의 몇 가지 조언

리팩터링과 기능 추가를 동시에 하지 말라.
리팩터링을 시작하기 전 든든한 테스트가 있는지 먼저 확인하라. 할 수 있는 한 자주 테스트를 돌려 보라. 이렇게 하면 여러분이 바꾼 것 때문에 무언가 망가졌을 경우 그 사실을 재빨리 알 수 있다.
단계를 작게 나누어서 신중하게 작업하라. 클래스의 필드 하나를 다른 클래스로 옮기기, 메서드 하나 쪼개기, 변수명 하나 바꾸기 같은 작은 단위로 작업해야 한다. 리팩터링에서는 국지적인 변경들이 많이 모여서 커다란 규모의 번화를 낳는 일이 자주 발생한다. 단계를 나누고, 한 단계가 끝날 때마다 테스트를 돌린다면 기나긴 디버깅 작업을 피할 수 있다.

 

🟢  마틴 파울러가 한 조언의 핵심은 탄탄한 회귀 테스트를 유지하는 것이야말로 안전한 리팩터링의 비결이라는 것이다. (P.306)

🟢  다음에 여러분이 기대하는 수준에 못 미치는 코드를 발견하면, 고쳐라. 고통을 관리하라. 지금은 고통스러울지라도 앞으로 더욱 고통스러워질 것 같으면 지금 고치는 편이 낫다. (P.306)

 

테스트로 코딩하기

🟢  테스트는 버그를 찾기 위한 것이 아니다. (P.307)

🟢  우리는 테스트의 주요한 이득이 테스트를 실행할 때가 아니라 테스트에 대해 생각하고, 테스트를 작성할 때 생긴다고 믿는다. (P.308)

🟢  테스트가 코딩을 주도한다. 테스트에 대해 생각함으로써 우리 코드의 결합도는 낮추고 유연성은 올릴 수 있다. 테스트가 코드의 첫 번째 사용자다. (P.309)

 

 

TDD

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

 

🟢  TDD 발상의 핵심은 이 반복 주기가 기껏해야 몇 분 정도로 매우 짧아야 한다는 것이다. 따라서 끊임없이 테스트 작성과 테스트를 통과하게 만들기를 반복하게 된다. (P.311)

🟢  어떻게든 TDD를 실천하라. 하지만 도중에 이따금 멈추어 큰 그림을 살피는 것을 잊지 말라. 초록색 '테스트 통과' 메시지에 중독된 나머지 진짜 문제 해결에는 보탬이 안 되는 코드를 한 무더기나 쓰게 되기 쉽다. (P.312) 

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

🟢  문제가 프로젝트에서 알려지지 않은 채로 숨어 있다가 나중에 곤란한 시점에 터져 버리는 일을 피하고 싶어서다. 계약을 잘 지키는지 확인하는 테스트를 강조함으로써 프로젝트에서 이후에 벌어질지 모를 재앙을 피하려고 노력하는 것이다. (P.317)

🟢  임시 테스트(ad-hoc)는 우리가 직접 코드를 이리저리 찔러보는 것이다. 여러분이 만든 테스트를 그냥 버리지 말고 기존의 단위 테스트 군단에 합류시켜라. (P.318)

🟢  여러분이나 여러분의 팀이 테스트하지 않으면 결과적으로 사용자들이 테스트하게 된다. (P.319)

 

속성 기반 테스트

🟢  코드에서 속성을 찾아내서 테스트 자동화에 사용할 수 있는데, 이것을 속성 기반 테스트라 한다. (P.322)

🟢  속성 기반 테스트가 강력한 까닭은 그저 입력을 생성하는 규칙과 출력을 검증하는 단정문만 설정한 채 제멋대로 작동하도록 놔두기 때문이다. 정확히 어떤 일이 일어날지 절대 알 수 없다.(P.328)

🟢  속성 기반 테스트는 설계에도 도움을 준다. - 우리는 속성 기반 테스트가 단위 테스트를 보완한다고 믿는다. 둘은 서로 다른 문제를 해결하고 각각의 장점이 있다. (P.329)

 

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

🟢  여러분은 90% 완성한 것이다. 하지만 이제는 나머지 90%를 고려해야 한다. 여러분이 다음으로 해야 하는 일은 코드가 잘못될 수 있는 경우를 찾아보고, 각 경우에 대한 단위 테스트를 추가하는 것이다. (P.331)

🟢  조용히 숨어 있는 것으로 보안을 대신하려는 생각은 통하지 않는다. (P.332)

 

기본 보안 원칙

1. 공격 표면을 최소화하라.
2. 최소 권한 원칙
3. 안전한 기본값
4. 민감 정보를 암호화하라.
5. 보안 업데이트를 적용하라.

 

🟢  입력 데이터는 공격 매개체다. - 외부 데이터를 절대 신뢰하지 말라. (P.333)

🟢  인증받은 사용자의 수를 언제나 최소로 유지하라. (P.334)

🟢  출력 데이터는 공격 매개체다. - 정보를 누설하지 말라. (P.335)

🟢  권한이야말로 '적을수록 낫다' (P.336)

🟢  암호나 API 키, SSH 키, 암호화 비밀번호, 그 밖의 다른 인증 정보를 소스 코드용 버전 관리 시스템에 넣지 말라. (P.337)

 

이름 짓기

 

🟢  이름이란 게 무슨 의미가 있나? 프로그래밍에서는 이름이 모든 것이다. (P.341)

🟢  우리의 뇌가 단어를 읽고 이해하는 속도는 엄청나게 빨라서 다른 대부분의 활동보다 빠르다고 한다. 이 말은 무언가를 이해하려고 할 때 단어의 우선순위가 높다는 것이다. (P.342)

🟢  이름을 지을 때는 여러분이 표현하고 싶은 것을 더 명확하게 다듬기 위해 끊임없이 노력해야 한다. 이렇게 명확하게 다듬는 작업이 여러분이 코드를 작성할 때 코드를 더 잘 이해할 수 있도록 도울 것이다. (P.344)

🟢  이름 바꾸기는 더 어렵다 (P.346)

 

 

🧐 오늘 익은 소감은? 떠오르는 생각을 가볍게 적어보세요

✅ 이번 챕터에서는 기억하고 싶은 명언이 많이 있다. 

- 리팩터링이 필요한 코드는 '종양'이다. 🥲

- 우리가 테스트를 하지 않으면 사용자가 테스트를 한다.

- 자기 코드에 대해 생각하지 않은 프로그래머는 우연에 맡기는 프로그래밍이다.

- 개발자인 우리는 지뢰밭에서 일한다. 🤣

- 잘 작동하는 데 괜히 건드려서 일을 만들 필요가 있을까? 그래야 할 이유가 충분히 있다.

 

✅ 개발이 끝나면 정말로 '잘 돌아가니까 괜히 건들지 말자'라는 생각을 하게 되는데 책을 읽고 부끄럽다는 생각이 든다.

리팩터링은 개발에 있어 꼭 필요한 작업이라고 생각한다.

정원처럼 코드를 가꾸다 보면 더욱더 클린 한 코드가 되지 않을까 생각한다.

 

✅ 가면 증후군.. 연차가 쌓이면서 스스로가 아직 부족하다고 느끼지만 연차 때문에 나의 능력이 과대평가받는다고 생각했던 때가 있다.

사람들과 대화하면서 모르는 이야기를 하면 당당하게 물어보지 못하고 아는 척을 했던 적이 있다.

책을 읽으면서 많은 공감을 했고 위로를 받았다.

더욱 노력하여 가면 증후군에서 벗어나도록 하자.

 

✅ 테스트 코드 작성은 제가 작성한 코드가 이렇게 안전한 코드라는 것과 변경에 대해서 버그를 바로 알기 쉽게 하기 위해 작성하는 것이라 생각했는데 책을 읽어보니 테스트는 버그를 찾는 것이 아니라 테스트 코드 작성을 통해 어떻게 처리할 것인지 생각하는 과정이 테스트를 작성하는 진짜 이유라는 점에서 공감이 간다.

생각해보니 내가 작성한 코드를 '어떻게 하면 통화할 것인가?'를 고민하고 생각하는 과정이 중요한 것이라 또 한 번 생각하게 되었다. 

 

✅ 역시나 변수 명 짓기는 제일 어려운 것 같다. 심지어 변수 이름 짓기에 시간을 제일 오래 쓰는 것 같고 무엇보다 영어공부가 꼭 필요하다고 느낀다. 이상한 영어나 전치사를 잘못 사용하면...창피하니까.. 😇

 

 

🙋‍♂️ 궁금한 내용이 있거나, 잘 이해되지 않는 내용이 있다면 적어보세요.

 

⭐️ 여행하는 외판원 문제 : Traveling Salesman problem (TSP) 

 

2098번: 외판원 순회

첫째 줄에 도시의 수 N이 주어진다. (2 ≤ N ≤ 16) 다음 N개의 줄에는 비용 행렬이 주어진다. 각 행렬의 성분은 1,000,000 이하의 양의 정수이며, 갈 수 없는 경우는 0이 주어진다. W[i][j]는 도시 i에서 j

www.acmicpc.net

 

⭐️ 리팩터링

 

리팩터링 2판 - YES24

개발자가 선택한 프로그램 가치를 높이는 최고의 코드 관리 기술마틴 파울러의 『리팩터링』이 새롭게 돌아왔다.지난 20년간 전 세계 프로그래머에게 리팩터링의 교본이었던 『리팩토링』은,

www.yes24.com

 

 

⭐️ Ad-Hoc Testing

임시 테스트는 계획 및 문서화없이 수행되는 소프트웨어 테스트에 일반적으로 사용되는 용어이지만 초기 과학 실험 연구에 적용될 수 있습니다. 결함이 발견되지 않으면 테스트는 한 번만 실행됩니다. 장점은 중요한 결함을 빠르게 찾을 수 있다는 것입니다.

 

반응형