실무 팀 투입 후 2주 정도가 되었을 때, 팀장님으로부터 책을 추천 받아 읽게되었습니다.
책을 읽고 책에 나오는 팁들에 대한 정리와 저의 견해를 남겨두었습니다.

1장. 실용주의 철학
Tip1. 자신의 기여(craft)에 관심을 가져라.
소프트웨어 개발을 잘하고 싶지 않다면 다른 업계를 가라.
Tip2. 자기 일에 대해 생각하라.
본인이 무엇을 하고 있는지 항상 생각해라.
비판적으로 생각하고 기계적으로 일하면 안 된다.
Tip3. 당신에게는 에이전시(agency)가 있다.
여기서 에이전시는 영어 사전에 의하면 "the ability to take action or to choose what action to take", 즉 주체적으로 원하는 행동을 고르고 행할 수 있는 능력을 의미합니다.
문제를 고치기 위해 생각하고 뒤쳐지는 기분이 든다면 여가 시간을 쪼개어 재밌어 보이는 것을 공부합니다.
IT 업계는 이직이 자유로운 곳이므로 환경(원격 근무, 디지털 노마드 등)이 마음에 들지 않는다면 이직도 생각하면 좋습니다.

Tip4. 어설픈 변명 말고 대안을 제시하라.
어떠한 작업을 완료하기 위해 자원이 더 필요하다면, 도움이 필요하다는 것을 요청해야 합니다.
(여기서 자원이란 시간, 기술을 더 깊게 배워야 할 필요, 교육의 필요 등을 말합니다)
어떤 일을 할 수 없거나 늦어질 때는 변명을 하는 것이 아닌 이 상황을 타개할 대안을 제시해야 합니다.

Tip5. 깨진 창문을 내버려두지 말라.
눈에 뜨일 때마다 나쁜 설계, 잘못된 결정, 좋지 않은 코드를 고쳐라.
나쁜 설계, 잘못된 결정, 형편없는 코드 모두 깨진 창문입니다.
발견하면 바로 고치고 고칠 시간이 없다면 일단 판자로 덮는 것만이라도 해야 합니다.(불쾌한 코드 주석 처리, //TODO, // 아직 구현되지 않음 등의 주석 표시, dummy 데이터 작성)
하지만, 창문을 깨는 것보다 일단 잘 만들면 됩니다.
프로젝트의 코드가 아름답고 잘 설계되었다면, 일단 망가뜨리면 안됩니다.

오타니도 쓰레기를 보면 줍는 것처럼 프로그래머인 저희도 쓰레기 코드가 보이면 항상 줍고 처리하는 습관을 들여야 합니다.
Codex, Claude, Gemini에게 쓰레기 코드를 던져 두면 그대로 사용할 경우 더욱 쓰레기가 될 수도..?
Tip6. 변화의 촉매가 되라.
사람들에게 변화를 강요할 수는 없다. 대신 미래가 어떤 모습일지 보여주고, 미래를 만드는 일에 그들이 참여하도록 하라.

Tip7. 큰 그림을 기억하라.
주변에 무슨 일이 일어나는지 점검하는 걸 잊을 정도로 세부사항에 빠지지 말라.

코딩도 좋지만 팀에서의 일도 잘 살펴보는 것이 중요하다.
Tip8. 품질을 요구 사항으로 만들어라.
완벽한 소프트웨어를 만드는 것은 불가능합니다.
사용자의 요구 사항에 따른 기본 조건은 갖추되 SW 자체 품질에 대해선 타협점을 찾아야 합니다.
또, 돌아가는 소프트웨어라면 적당히 멈출 줄도 알아야 합니다.

기획자의 요구 사항에 대해 적당한 타협점을 맞춰가며 프로젝트를 진행해봅시다.
Tip9. 지식 포트폴리오에 주기적으로 투자하라.
학습을 습관으로 만들어라.
- 새로운 언어 배우기, 기술 서적 매달 읽기, 그냥 책 읽기, 모임 참여하기, 수업 듣기, 다른 환경에서 실험하기, 요즘 흐름 놓치지 않기
책에서는 위와 같은 제안을 합니다. 요즘에는 ai 발전을 통해 빠르게 발전하는 산업 속에서 ai와 함께 일하는 능력을 발전시키는 것이 중요하다고 생각합니다.
저는 개발자가 높은 연봉을 받는 이유 중 하나가 발전하는 기술 산업에서 배우지 않으면 뒤쳐지고 항상 새롭거나 발전된 기술을 받아들일 준비를 해야 한다고 생각합니다.
AI가 발전함에 따라 오히려 학습하기 쉬운 환경이 되었고 AI를 이용하여 작업을 하기도 좋은 환경이 되었다고 생각합니다.(물론 인터넷 강의나 책들도 분명 좋지만) 주기적인 학습과 사이드 프로젝트를 통해 업무에서도 적용해보고 개인적인 성장도 도모한다면 좋은 개발자가 되지 않을까 생각합니다.
Tip10. 읽고 듣는 것을 비판적으로 분석하라.
벤더, 매체들의 야단 법석, 도그마에 흔들리지 말라. 여러분과 여러분 프로젝트의 관점에서 정보를 분석하라.
책에서는 다음과 같은 질문들에 먼저 생각해보라고 제안합니다.
- 왜냐고 5번 묻기
- 누구에게 이익이 되나?
- 어떤 맥락인가?
- 언제, 어디서 효과가 있을까?
- 왜 이것이 문제인가?
개발의 관점에서 AI가 발전함에 따라 AI의 답변 품질도 좋아지곤 있지만 아직 맹신할 순 없는 도구라고 생각합니다.
AI를 적절하게 활용하고 AI 답변에 대한 비판적인 사고를 통해 도움이 되는 것들만 쏙쏙 골라 참고한다면 생산성이 더욱 올라갈 것입니다.
물론 이 팁은 단순 개발의 관점 뿐 아니라 인생을 살아감에 있어서도 중요한 태도라고 생각합니다.
역사와 정치의 관점에서도 하나의 사건에 대한 입장이 분분하니 비판적 사고를 통해 그대로 받아들이지 않는 습관이 좋을 것입니다.
Tip11. 한국어든 영어든 하나의 프로그래밍 언어일 뿐이다.
개발자로서 코드를 작성하여 기계에 우리의 의도를 전달하듯 우리의 생각을 언어로 작성하여 다음 세대의 개발자들에게 전달하고 제안서와 보고서를 통해 자원을 요청하고 팀 내의 소통 모두 언어를 통해 하오니 '소통'도 결국 중요한 개발자의 소양입니다.
Tip12. 무엇을 말하는가와 어떻게 말하는가 모두 중요하다.
효과적으로 전달하지 못하면 좋은 생각이 있어 봐야 소용없다.
1. 청중 알기
- 영업, 운영 부서, 개발 부서 등 각 그룹에 맞는 접근을 통해 모든 사람들이 만족할 수 있는 프로젝트를 하자.
2. 말하고 싶은 게 무언지 알라
- 말하고자 하는 것이 정확히 무엇인지를 생각하고 소설가가 책을 쓰기 전 미리 줄거리를 생각하는 것처럼 문서를 쓸 때 개요를 쓰고 자문하라. 그리고 듣는 사람에게 통하는 방법으로 잘 전달할 수 있게 다듬자.
3. 때를 골라라
- 금요일 퇴근 전에 하지 마라.
4. 스타일을 골라라
- 격식 or 본론 전 일상적인 이야기 등 선택
- 또, 상대방의 기술 수준이나 경험에 따라 요약할지 어디까지 설명할지 생각해라.
5. 멋져 보이게 하라
- 마크 다운을 이용해서 예쁘게 꾸미자.
6. 청중을 참여시켜라(경청하라, 응답하라 포함)
- 다른 사람들의 말을 잘 듣고 질문에 대한 응답을 통해 의사소통을 하자.

Tip13. 문서를 애초부터 포함하고, 나중에 집어넣으려고 하지 말라.
코드와 별개로 만든 문서가 정확하거나 최신 정보를 잘 반영하기는 힘들다.
소스 코드에 주석을 다는 것도 사실 좋지 않다. -> 소스 코드를 바꾸면 주석도 바꿔야하기 때문에 두 군데를 바꿔야 하기 때문.
어떻게 동작하는지 잘 보여주는 코드를 쓰는 것에 더 집중하자.
2장 실용주의 접근법
Tip14. 좋은 설계는 나쁜 설계보다 바꾸기 쉽다.
잘 설계된 코드는 바뀜으로써 사용하는 사람에게 맞춰져야 한다.
ETC 원칙(Easier To Change)
- 우리가 하는 모든 설계 원칙은 ETC의 특수한 경우이다.
단일 책임 원칙(Single Responsibility Principle)이 유용한 이유는 요구 사항이 바뀌더라도 모듈 하나만 바꿔서 반영할 수 있기 때문이다.
이름 짓기가 중요한 이유는 코드가 읽기 쉬워지고 코드를 바꾸려면 코드를 읽어야 하기 때문이다.
ETC는 규칙이 아니라 가치이다.
선택의 갈림길에서 도움을 주는 안내자이다.
1. 코드를 작성할 때, '바꾸기 쉽게' 라는 길을 선택한다.
- 교체 가능하다는 의미는 결합도는 낮추고 응집도는 높이라는 이야기이다.
2. 직관을 발전시키는 기회로 삼아라.
- 엔지니어링 일지와 현재 상황과 우리의 선택, 그리고 이후의 추측을 작성해두고 나중에 코드를 바꿔야하는 시점에 다시한번 보자. 이를 통해 스스로에 대한 피드백을 할 수 있을 것이다.

Tip15. DRY: 반복하지 말라(Don’t Repeat Yourself).
모든 지식은 시스템 내에서 단 한 번만, 애매하지 않고, 권위 있게 표현되어야 한다.
이때, 전형적인 중복들은 다음과 같은 예시가 있다.
1. 코드의 중복

코드가 중복된다면 함수로 바꿔서 작성하자.
모든 코드 중복이 지식 중복은 아니다.
void validate_age(int value)
{
validate_type(value, _integer);
validate_min_integer(value, 1);
}
void validate_quantity(int value)
{
validate_type(value, _integer);
validate_min_integer(value, 1);
}위와 같은 사례에서는 내용이 동일하기 때문에 DRY 원칙 위배라고 생각할 수 있지만, 두 함수가 표현하는 지식이 다르므로 우연히 규칙이 같은 것이지 중복은 아닙니다.
문서화 중복
위에서도 설명했지만, 주석도 결국 코드의 의도를 설명하는 것으로 수정할 경우 두 군데를 수정해야 합니다.
결국 잘 쓴 코드는 주석을 대체할 수 있습니다.
데이터의 중복
class Line
{
double length; // double length() { return start.DistanceTo(end); } 로 바꾸면 더 낫다.
Point start;
Point end;
};위의 예시에서 length는 시작점과 끝점이 정해지면 길이도 결정되므로 계산되는 필드로 만드는 편이 낫습니다.
또, 이후에는 개발을 하다보면 성능상의 이유로 DRY 원칙을 위배하는 경우가 생길 수 있습니다.(비용이 많이 드는 연산을 여러 번 수행하지 않기 위해 캐싱할 때 등)
이때 요령은 바깥에는 DRY 원칙 위배를 노출하지 않고 클래스 내부 메소드를 고생시키면 됩니다.
class Line
{
double length;
Point start;
Point end;
Line(Point start, Point end) : start(start), end(end)
{
CalculateLength();
}
void CalculateLength()
{
this->length = start.DistanceTo(end);
}
public:
void SetStart(Point p) { this->start = p; CalculateLength(); }
void SetEnd(Point p) { this->end = p; CalculateLength(); }
Point GetStart() { return start; }
Point GetEnd() { return end; }
double GetLength() { return length; }
};모듈의 자료 구조를 노출하면 모듈의 구현과 그 자료 구조를 사용하는 코드 사이 결합이 생깁니다.
그러므로 get, set 등의 함수를 이용하면 이후 기능 추가가 더 쉬워집니다.
개발자간의 중복
발견하고 없애기 가장 어려운 중복입니다.
똑같은 일을 하는 코드가 중복으로 추가될 수도 있고 이러한 중복은 수년동안 발견되지 않을 수도 있습니다.
결국 유지 보수의 문제로 귀결되므로 개발자 간의 중복에 대처하기 위해 의사소통을 잘하는 튼튼하고 유대가 돈독한 팀을 만들어야 합니다.
책에서는 팀원 한 사람을 프로젝트의 사서로 임명하여 지식 교환을 도우라고 합니다.
일상적으로 코드 리뷰를 통해 다른 사람의 소스 코드와 문서를 읽고 배우며 기억하는 것입니다.

Tip16. 재사용하기 쉽게 만들어라.
재사용하기 쉽다면 사람들이 재사용할 것이다. 재사용을 촉진하는 환경을 만들어라.
위에서의 코드 중복처럼 재사용 하기 쉽게 만드는 것이 중요합니다.

중복을 막기 위한 함수가 여러 요구 사항과 예외로 인해 재사용이 불가능해질 수도 있으므로 재사용이 용이하도록 만드는 것이 중요합니다.
Tip17. 관련 없는 것들 간에 서로 영향이 없도록 하라.
컴포넌트를 자족적이고, 독립적이며 단 하나의 잘 정의된 목적만 갖도록 설계하라.
직교성(orthogonality)
: 그래프의 축과 같이 두 직선이 직각으로 만나는 경우를 직교한다고 말합니다. -> 이때 두 직선은 독립적입니다.
소프트웨어 개발에서도 관련 없는 모듈간의 독립성을 보존하고 결합도를 줄이는 것을 의미합니다.
직교성의 장점
1. 생산성 향상
- 변화를 국소화해서 테스트와 개발 시간이 줄어듭니다.
- 느슨하게 결합되어 새로운 컴포넌트와 결합하기 쉽습니다.
2. 리스크 감소
- 일부분을 바꾸더라도 문제점은 모듈 내로 한정됩니다.
- 특정 플랫폼이나 제품에 덜 종속적입니다.
설계
설계를 직교적으로 확인하기 가장 쉬운 질문은 '특정 기능에 대한 요구 사항을 대폭 변경하는 경우 몇 개의 모듈이 영향 받는가?' 이다. 직교적인 시스템에서는 '하나'여야 합니다.

게임 엔진 구조와 같은 계층 단위 설계는 하위 계층이 제공하는 추상화만을 사용하여 유연성이 높아집니다.
또한 모듈 간에 의존성이 폭증할 위험을 줄일 수 있습니다.
Tip18. 최종 결정이란 없다.
모든 결정은 바닷가의 모래 위에 쓰인 글씨라 생각하고 변화에 대비하라.
프로젝트를 진행하면서 기술 스택이나 사용하는 아키텍처 패턴 등이 달라질 수 있습니다.
여기서 DRY 원칙, 결합도 줄이기, 외부 설정 사용하기를 따른다면 중요하면서도 되돌릴 수 없는 결정의 수를 가능한 한 줄일 수 있습니다.(가역성)
이러한 결정을 줄여야 하는 이유는 프로젝트 초기에 최선의 결정을 항상 내리지 못하기 때문입니다.

Tip19. 유행을 좇지 마라.
유행이 아니라 본질을 보고 아키텍처(k8s 기반 서버를 할지, 한 컴퓨터 쓸지 등)를 선택하라. 그리고 바꾸기 쉽게 만들어라.
Tip20. 목표물을 찾기 위해 예광탄을 써라.
시도해 보고 목표와 얼마나 가까운지 확인하라.
익숙하지 않은 알고리즘, 기술, 언어, 라이브러리를 사용하면서 미지의 것과 맞닥트리게 됩니다.
전형적으로는 스펙을 세세히 계산하고 명세화 하지만 실용주의 프로그래머는 요구 사항으로부터 최종 시스템의 일부 측면까지 빨리, 눈에 보이게, 반복적으로 도달하게 해 줄 무언가를 찾습니다. 시스템을 정의하는 중요한 요구 사항을 찾고 의문이 드는 부분이나 가장 위험이 커 보이는 곳을 먼저 코드를 작성하여 우선순위를 정합니다.
이러한 접근 방법은 '사용자가 뭔가 작동하는 것을 일찍부터 보게 된다' 는 장점이 있습니다.
또, 항상 정확한 일을 하는 게 아닐 수 있습니다. 단, 이러한 과정을 통해 목표물에 더 가까워지게 바꿀 수 있고 더 정확하게 바꿀 수도 있습니다.
프로토타이핑과 유사해 보이지만 프로토타이핑은 대충 끼워 맞춰 구현한 것으로 모두 버려야 합니다. 그리고 실험 과정을 통해 얻은 교훈을 바탕으로 새로운 코드를 작성합니다.
반면, 예광탄 접근 방법은 전체 SW가 어떻게 연결되고 어떻게 상호작용하는지, 코드를 붙일 아키텍처를 제시합니다. 그리고 대강 구현한 알고리즘과 단순한 사용자 인터페이스로 구성됩니다. 그리고 이 토대를 바탕으로 새로운 기능을 차근차근 추가합니다. 이때 이 프레임워크는 그대로 남아있으며 앞으로도 그대로 동작합니다.
즉, 프로토타이핑은 나중에 버리는 코드를 만들지만 예광탄 코드는 기능은 별로 없지만 완결된 코드이며 최종 시스템의 일부가 됩니다.
Tip21. 프로토타이핑으로 학습하라.
프로토타이핑은 위험 요소를 분석하고 노출시킨 후, 매우 저렴한 비용으로 바로잡을 기회를 얻는 것입니다.
프로토타이핑을 꼭 코드로만 작성하지 않아도 포스트잇이나 화이트보드 등을 통해 그림을 그려서 할 수도 있고 그림판 등의 앱을 이용해서 할 수도 있습니다. 만약 세부 사항을 진행해야 한다면 프로토타이핑보다는 예광탄 접근 방법을 사용하는 것이 나을 수 있습니다.
프로토타이핑을 통해 조사할 수 있는 것들은 아래와 같습니다.
- 아키텍터
- 기존 시스템에 추가할 새로운 기능
- 외부 데이터의 구조 혹은 내용
- 외부에서 가져온 도구나 컴포넌트
- 성능 문제
- 사용자 인터페이스 설계
프로토타이핑의 가치는 코드가 아니라 그 과정에서 배우는 교훈에 있습니다.
Tip22. 문제 도메인에 가깝게 프로그래밍하라.
실용주의 프로그래머라면 어떤 경우에는 한 차원 더 나아가서 그 도메인의 실제 어휘와 문법, 의미론(즉, 그 도메인의 언어)를 사용해서 프로그래밍할 수도 있습니다.
내부 도메인 언어는 궁극적으로는 어떤 형태로 만들더라도 호스트 언어의 문법을 벗어날 수 없습니다.가능하다면 YAML, JSON, CSV처럼 널리 통용되는 외부 언어를 사용하라.
단, 외부 언어의 도입은 애플리케이션의 사용자가 직접 도메인 언어로 코드를 작성하는 경우에만 추천합니다.
저는 이 부분을 읽으면서 도메인 언어(DSL)라는 개념을 처음 접했습니다.
처음에는 이해가 잘 가지 않았지만 아래 위키를 참고해서 보고 이해가 되었습니다.
https://ko.wikipedia.org/wiki/%EB%8F%84%EB%A9%94%EC%9D%B8_%ED%8A%B9%ED%99%94_%EC%96%B8%EC%96%B4
Tip23. 추정으로 놀람을 피하라.
추정을 하고 추정하는 능력을 계발하는 것은 이후 직관적으로 판단이 가능하게 된다.
(ex. 어떤 서브시스템을 최적화 할지? 어떤 서브시스템은 그대로 나둬도 될지?)
누군가 추정치를 물었을 때 스스로 물어보아야 할 첫 번째 질문은 답변이 사용될 상황이 무엇인지다.
-> 이때 답변을 할 때 125근무일 or 6개월 등 전달하려는 정확도를 고려하여 답변 단위를 선택해야 합니다.
모든 추정치는 모델에 기반합니다.
(아래 방법을 통해 어떻게 추정을 하는 것이 좋을 지 알 수 있습니다.)
- 어떤 종류의 추정을 하건 첫 단계는 상대방이 무엇을 묻고 있는지 이해하는 것입니다.(질문에 조건이 명시적이지 않은 경우에 어떤 조건이 있을지 미리 생각하는 습관이 좋다고 합니다. 간혹 이런 조건이 답변의 일부가 되기도 합니다.)
- 기본적인 것만 갖춘 개략적인 모델을 만듭니다.(모델을 통해 밑그림을 제공하고 드러나지 않던 문제들을 발견하는 경우도 있습니다.)
- 다 만든 모델은 컴포넌트로 분해할 수 있어야 합니다. 또,이 컴포넌트가 어떻게 상호작용 하는지 수식으로 기술해야 합니다.
- 각 컴포넌트가 어떻게 시스템에 영향을 주는지에 대한 매개변수를 찾고 매개변수에 값을 할당합니다.
추정치에 대한 이야기를 하면서 이해 되지 않는 사례들이 많았지만, 개발한 경험에선 직관과 추정이 앞으로의 개발을 진행하는 데 크게 작용한다는 것에 공감되었고 이를 발전시켜야 한다는 것을 항상 느끼고 있습니다.
Tip24. 코드와 함께 일정도 반복하며 조정하라.
구현하면서 얻는 경험으로 소요 시간을 재조정하라.
프로젝트에서는 수많은 반복되는 주기가 필요합니다.
이때, 초기 기능의 구현과 테스트를 마친 기간을 첫 반복 주기의 끝으로 삼아서 이후 계획을 추측하는 것을 추천한다고 합니다.
물론, 일정 관리를 위해 M/M, PERT 등의 기법이 물론 존재하지만 정확하진 않다고 생각합니다.
책에서는 일단 나중에 연락드릴게요 라고 피하고 생각 후에 답변 하는 걸 추천한다고 합니다.
3장. 기본 도구
Tip25. 지식을 일반 텍스트로 저장하라.
우리가 저장하는 텍스트는 사람이 이해할 수 있어야 합니다.
Field169=467abe위처럼 표현하면 467abe가 무슨 의미인지 알 수 없기 때문에 쓸모없는 일반 텍스트가 됩니다.
일반 텍스트를 사용하는 이유는 다음과 같습니다.
1. 지원 중단에 대한 보험
- 레거시 모듈의 파싱 지원 종료 등에 대한 방지를 합니다.
2. 기존 도구의 활용
- VC툴, 에디터 등의 도구에서 일반 텍스트를 사용합니다.
3. 더 쉬운 테스트
- 데이터를 일반 텍스트로 표현하면 간단하게 테스트 데이터를 수정하거나 추가할 수 있습니다.
Tip26. 명령어 셸의 힘을 사용하라.
GUI로는 할 수 없는 일을 셸로 해결하라.
GUI 인터페이스의 장점은 WYSIW-YG(What You See is What you Get), 즉, 여러분이 보는 것이 여러분이 얻는 것이라는 것이다. 그러나 단점은 WYSUAYG(What You see Is All You Get) 즉, 여러분이 보는 것이 여러분이 보는 전부라는 것이다.
GUI환경은 일반적으로 설계자의 의도에 따른 제약을 받는다.
때문에 명령어 쉘의 힘을 활용하는 것은 너의 사고를 넓히는 첫번째 발자국입니다.


프로젝트를 하면서 git bash보다는 github desktop이나 fork 같은 GUI를 사용하여 버전 관리를 하였던 기억이 나서 이번 챕터의 내용이 크게 와닿았습니다.
특히, GUI를 사용하면 cherry-pick 과 같은 cli에서만 제공하는 기능들을 사용하지 못하는 것이 불편했는데, 이후에는 cli 환경에서도 자신만의 환경을 만들어 생산성을 높일 수 있도록 시도해봐야겠습니다.
Tip27. 에디터를 유창하게 쓸 수 있게 하라.
프로그래머에게 에디터는 가장 중요한 도구이며 에디터에 능숙해지는 것은 결국 에디터 사용법에 대한 생각을 하지 않고 프로그래밍에 집중할 수 있습니다.
에디터를 유창하게 쓸 수 있다는 기준은 무엇일까?에 대한 질문들이 있습니다.
아래 글에서 본인이 자주 쓰는 에디터의 기능을 알고 있는지 테스트할 수 있게 따로 접은글로 넣어뒀으니 확인해보세요.
이때, 아래 적은 과제들은 마우스나 트랙패드 없이 수행할 수 있어야 합니다.
더보기
- 텍스트를 편집할 때 문자, 단어, 줄, 문단 단위로 커서를 이동하거나 내용을 선택할 수 있다.
- 코드를 편집할 때 반대쪽 괄호로 이동하거나, 함수, 모듈 등 다양한 문법 단위로 커서를 이동할 수 있다.
- 변경한 코드의 들여쓰기를 자동으로 맞출 수 있다.
- 여러 줄의 코드를 명령 하나로 주석 처리했다가 다시 주석 해제할 수 있다.
- 실행 취소를 여러 번 했다가 취소한 명령을 재실행 기능으로 다시 수행할 수 있다.
- 에디터 창을 여러 구역으로 쪼개고 각 구역 사이를 이동할 수 있다.
- 특정 줄 번호로 이동할 수 있다.
- 여러 줄을 선택 후 가나다순으로 정렬할 수 있다.
- 문자열로, 또 정규 표현식으로 검색한다. 이전에 검색했던 것을 다시 검색할 수 있다.
- 선택 영역이나 패턴 검색을 이용하여 일시적으로 여러 개의 커서를 만든 다음, 동시에 여러 곳의 텍스트를 편집할 수 있다.
- 현재 프로젝트의 컴파일 오류를 표시할 수 있다.
- 현재 프로젝트의 테스트를 실행할 수 있다.
질문의 답변은 https://chatgpt.com/share/6992bcf0-a560-8011-9115-927921fee84d
위 링크 페이지를 참고해주시면 감사하겠습니다.
아직 저도 많이 익숙하진 않지만 실무를 하며 특히 유용했던 부분은 Ctrl+Shift+F 로 솔루션 내 코드를 검색하고 Ctrl+T로 솔루션 내 파일명 검색이 가장 유용했던 것 같습니다.
또, 실무팀에서는 VS 확장 기능을 이용하여 생산성을 더욱 높이는 자동화 기능과 편의 기능 등이 있는데, 이러한 부분들이 실용주의 프로그래머에 걸맞다고 생각합니다.
Tip28. 언제나 버전 관리 시스템을 사용하라.
개인 프로젝트도 VCS를 사용하여 브랜치, 롤백 등을 사용하는 것이 중요하다.
Tip29. 비난 대신 문제를 해결하라.
누가 낸 버그인 것이 중요한 게 아닌 해결할 사람이 우리라는 것에 집중해야 합니다.
Tip30. 당황하지 말라.
디버깅 제 1원칙은 당황하지 않는 것입니다.
오류가 난 것은 결국 실제로 어떤 곳에 문제가 있기 때문에 발생한 것이며 근시안적으로 상황을 보는 것이 아닌 문제 발생의 근본적인 원인을 파악해야 합니다.

Tip31. 코드를 고치기 전에 실패하는 테스트부터.
디버깅 전략- 버그 재현하기
버그를 고치기 가장 좋은 것은 버그를 재현할 수 있게 만드는 것입니다.
그래서 디버깅의 가장 중요한 규칙 중 하나는 '코드를 고치기 전 실패하는 테스트부터' 하는 것입니다.
게임 개발에서도 적용이 가능할지..?
Tip32. 그놈의 오류 메시지 좀 읽어라.
예외는 실패 원인을 알려준다.
Tip33. “select”는 망가지지 않았다.
OS, 컴파일러, 외부 라이브러리 등에 대한 버그일 수 있겠다는 생각보다는 본인이 작성한 코드에서 잘못 호출하고 있거나 잘못 활용하고 있을 확률이 높습니다.

아무 관련 없어 보이는 코드 한 줄을 수정하더라도 어떤 버그가 고쳐지면서 새로운 버그가 발생할 수도 있습니다.
Tip34. 가정하지 말라. 증명하라.
실패를 했을 때, 본인이 세운 가정이 적어도 하나는 잘못됐다는 것을 받아들이고
코드가 제대로 동작한다는 것을 맥락 안에서, 데이터로, 경계 조건 하에서 증명해야 합니다.

또, 버그를 마주쳤을 때 단순히 고치는 것을 넘어 왜 이 문제가 일찍 발견되지 않았을까 생각해 봐야 합니다.
버그를 미리 잡을 수 있도록 단위 테스트나 다른 테스트를 수정할 필요가 있나 고민해야 합니다. 그리고, 비슷한 버그가 다른 코드에서 일어날법 한지도 확인해야 합니다.
버그가 누군가의 잘못된 가정으로 발생했다면 팀과 함께 이 문제에 대해 토론해야 합니다.
(한 명이 아닌 다른 사람들도 그럴 수 있기 때문)
Tip35. 텍스트 처리 언어를 익혀라.
일부 작업은 컴퓨터에게 맡겨라.
C/C++은 오래 걸리지만, Python/Ruby 등의 언어는 텍스트 처리를 빠르게 할 수 있습니다.
간단한 텍스트 처리 언어를 통해 자동화, 테스트 제작, 상호 작용 등을 진행할 수 있습니다.
저도 python을 이용해서 웹크롤링을 하고 대학 시험 점수 결과표를 토대로 등수 계산, 학점 비율 계산 등을 미리 할만큼 편리한 기능 등을 빠르게 만들 수 있습니다.
프로젝트에서 사용되는 언어 외에도 추가적인 텍스트 처리 언어를 통해 생산성을 높일 수 있습니다.
<+ 추가 팁: 엔지니어링 일지>
일기를 써라.(종이에)
4장. 실용주의 편집증
Tip36. 여러분은 완벽한 소프트웨어를 만들 수 없다.
위 말에 상처받지 말고 공리로 받아들이라고 합니다.

Tip37. 계약으로 설계하라.
DbC(Design by Contract) 개념이 등장합니다.
프로그램의 정확성을 위해 SW 모듈의 권리와 책임을 문서화하고 합의하는 데 초점을 맞추는 방식입니다.
선행 조건(precondition)
- 루틴(메서드)이 호출되기 전, 호출자(Caller)가 보장해야 하는 참(True)인 조건입니다.
후행 조건(postcondition)
- 루틴(메서드)이 실행된 후, 루틴이 호출자에게 보장하는 참(True)인 상태입니다.
클래스 불변식(class invariant) = 상태(state)
- 클래스의 인스턴스(객체) 생명 주기 동안 항상 참이어야 하는 조건입니다.
루틴과 그 루틴을 호출하려는 코드 간의 계약은 다음과 같습니다.'만약 호출자가 루틴의 모든 선행 조건을 충족한다면 해당 루틴은 종료 시 모든 후행 조건과 불변식이 참이 되는 것을 보장한다.'
#include <iostream>
#include <stdexcept> // std::invalid_argument 사용을 위해 필요
class BankAccount {
private:
int balance;
// 클래스 불변식 확인용 프라이빗 메서드 (디버깅 시 유용)
void check_invariant() const {
if (balance < 0) {
throw std::logic_error("Invariant violated: balance must be non-negative");
}
}
public:
// 생성자: 초기 잔액도 0 이상이어야 함
BankAccount(int initial_balance = 0) : balance(initial_balance) {
if (balance < 0) throw std::invalid_argument("Initial balance cannot be negative");
}
// 선행조건: amount > 0
// 후행조건: balance = oldBalance + amount
void deposit(int amount) {
// 1. 선행조건 위반 확인
if (amount <= 0) {
throw std::invalid_argument("Deposit amount must be positive");
}
// 후행조건 확인을 위해 이전 상태 저장 (선택 사항)
int old_balance = balance;
// 2. 상태 업데이트
balance += amount;
// 3. 후행조건 및 불변식 확인
if (balance != old_balance + amount) {
throw std::logic_error("Postcondition failed");
}
check_invariant();
}
// 현재 잔액 조회 (상태를 변경하지 않으므로 const 선언)
int get_balance() const {
return balance;
}
};gemini가 만들어준 간단한 DbC 적용 코드 예시.
Tip38. 일찍 작동을 멈춰라.
예외가 발생하는 순간부터 해당 프로그램이 더이상 유효하지 않을 수 있습니다.
이는, 문제가 발생했을 때 어설프게 수습하려 하지 말고, 그 즉시 실행을 중단하여 더 큰 피해를 막는 것입니다.
Tip39. 단정문으로 불가능한 상황을 예방하라.
"그런 일은 절대 일어나지 않을 거야" 라는 생각이 든다면 그런 일을 확인하는 코드를 추가하라고 합니다.
이때, 단정문(assertion)을 사용합니다. 매개 변수나 결과가 절대 null이어서는 안된다면 명시적으로 검사합니다.
-> C++에서는 보통 매크로로 구현됩니다.
assert (result != null);어떤 my_sort 알고리즘을 작성했다면, 제대로 작동하는지 확인해라.
그러나, 진짜 오류 처리를 해야하는 곳에는 단정을 대신 사용해선 안 됩니다.
(단정은 결코 일어나면 안 되는 것들을 검사합니다.)
Tip40. 자신이 시작한 것은 자신이 끝내라.
리소스를 할당하는 함수나 객체가 리소스를 해제하는 책임 역시 져야 합니다.
책에서의 예시에서는 여러 함수 간의 결합(coupling)되어 있는 문제에 대한 예시가 있습니다.
이를 해결하기 위해 인스턴스 변수로 저장하지 않고 매개변수로 전달하여 리소스에 대한 책임을 하나의 함수에 위임합니다.
책에는 나오지 않았지만, C++에서는 RAII 패턴을 구현한 스마트 포인터가 있습니다.
void pragmatic_example() {
// 시작(할당)과 동시에 끝내기(해제)를 보장하는 스마트 포인터
std::unique_ptr<BankAccount> account = std::make_unique<BankAccount>(1000);
account->deposit(500);
}혹은 파일 시스템에서는 아래처럼 적용할 수 있습니다.
class FileHandler {
std::ofstream file;
public:
FileHandler(const std::string& filename) : file(filename) {
if (!file.is_open()) throw std::runtime_error("파일을 열 수 없습니다.");
}
void write(const std::string& msg) { file << msg << "\n"; }
~FileHandler() {
if (file.is_open()) {
file.close();
}
}
};
void use_file() {
FileHandler myFile("log.txt");
myFile.write("파라미터로 전달하기");
}Tip41. 지역적으로 행동하라.
유효 범위를 짧고 명확하게 유지하라.
C++의 경우에는 변수 스코프를 이용하여 함수나 블록 종료, 예외 등으로 변수가 스코프를 벗어나면 변수의 메모리가 해제됩니다.
변수 스코프를 활용하여 예외에 따른 리소스 해제에 대한 예방을 할 수 있습니다.
Tip42. 작은 단계들을 밟아라. 언제나.
소프트웨어 개발에서 너무 먼 미래를 내다볼 수 없고, 정면에서 벗어난 부분은 어둡게 보입니다. 그래서 항상 작은 단계들을 밟아가며 피드백을 확인하고 조정하는 것이 중요합니다.
여기서 너무 먼 미래는 몇 달 후 완료 일정 추정하기, 미래 요구 사항 예측하기, 미래에 어떤 기술 쓸 수 있을지 추측하기, 미래의 유지보수나 확장 가능성을 미리 고려하여 설계하기 등이 있습니다.
물론, 미래의 유지 보수와 확장성을 고려해서 설계해야 하지만 볼 수 있는 미래까지만 고려해야 합니다. 불확실한 미래까지 대비한 설계는 틀릴 가능성이 높아지고 설계에만 너무 많은 에너지를 씁니다. 대신 DRY 원칙을 기억하고 대체하기 쉽게 작성합니다.
Tip43. 예언하지 말라.
볼 수 있는 만큼만 내다보라.

5장. 구부러지거나 부러지거나
Tip44. 결합도가 낮은 코드가 바꾸기 쉽다.
다리나 탑 같은 단단하게 설계하는 것과 달리 소프트웨어 설계에서 SW의 구조는 유연해야 합니다.
결합도가 낮게 하기 (=적게 연결하기)
- 열차 사고(train wreck): 연쇄 함수 호출
- 글로벌화: 정적(static)인 것의 위험함
- 상속: 왜 클래스 상속이 위험한가?
하지만, 위 목록들은 인위적이며 이후의 내용에서 근본적인 패턴을 찾아보고 적용해야 합니다.
Tip45. 묻지 말고 말하라(Tell, Don’t Ask).
객체가 스스로 처리하게 하라.
이 원칙은 다른 객체의 내부 상태에 따라 판단을 내리고 그 객체를 갱신해서는 안 된다는 것입니다.
객체의 내부 상태를 묻는 것으로 인해 캡슐화의 장점은 사라지고 그 과정에서 구현에 대한 지식이 퍼져버립니다.
따라서, 열차 사고를 고치는 첫 발짝은 처리를 객체에 위임하는 것입니다.
// 나쁜 예시❌: 열차 사고(Train Wreck) 발생
void applyDiscount(Customer& customer, double discount) {
// 1. 데이터를 묻고(Ask)
double currentTotal = customer.order.total_amount;
// 2. 외부에서 계산하고
if (currentTotal > 50.0) {
currentTotal -= discount;
}
// 3. 다시 설정한다.
customer.order.total_amount = currentTotal;
}
// 좋은 예시 ✅
class Order {
private:
double total_amount;
public:
Order(double amount) : total_amount(amount) {}
// 객체에게 직접 명령(Tell)을 내림
void applyDiscount(double discount) {
if (total_amount > 50.0) {
total_amount -= discount;
}
}
};
class Customer {
private:
Order order;
public:
Customer(double amount) : order(amount) {}
// 상위 객체도 하위 객체에게 처리를 위임함
void discountOrder(double discount) {
order.applyDiscount(discount);
}
};
int main() {
Customer myCustomer(100.0);
// "할인해줘!"라고 말만 하면 끝
myCustomer.discountOrder(10.0);
}Tip46. 메서드 호출을 엮지 말라.
마침표를 하나만 쓰려고 노력하라.
무언가에 접근할 때 "."을 딱 하나만 쓰려고 노력하는 것이 좋습니다.
// 좋지 않은 방식
amount = customer.orders.last().totals().amount;위의 경우는 여러 번 접근을 하는 방식으로 좋지 않습니다. 그러나, 엮여있는 것들이 절대로 바뀌지 않는 것 같다면 이 규칙을 지키지 않아도 됩니다. (ex. 언어에 기본으로 포함된 라이브러리 등)
Tip47. 전역 데이터를 피하라.
전역은 모든 곳에 영향을 준다.
전역 데이터 하나하나는 애플리케이션의 모든 함수에 갑자기 매개 변수가 추가된 것과 같은 효과를 냅니다.
전역 데이터는 여러 가지 방법으로 코드의 결합도를 높입니다. 전역 데이터의 구현을 변경할 때 시스템 코드 전체에 영향을 줄 수 있음이 분명합니다.
싱글톤(singleton)도 전역 데이터입니다.
Tip48. 전역적이어야 한다면 API로 감싸라.
외부 리소스도 전역 데이터입니다.
수정 가능한 외부 리소스는 모두 전역 데이터입니다. 데이터베이스, 저장소, 파일 시스템, 서비스 API 등을 사용한다면 전역 데이터의 함정에 빠질 위험이 있으니 이 리소스들을 우리가 작성하는 코드로 모두 감싸는 것이 해결 방법입니다.
<+ Topic 29 실세계를 갖고 저글링하기 >
이벤트(event)는 무언가 정보가 있다는 것을 의미합니다. 정보는 사용자가 버튼을 클릭하거나, 주가 정보가 갱신될 때처럼 외부에서 올 수 있습니다. 어디에서 온 것이든 이런 이벤트에 반응하도록, 그리고 그에 기반해서 하는 일을 조절하도록 만들면 세상에서 더 잘 작동하는 앱을 만들 수 있습니다. 그렇다면 이 앱을 어떻게 만들 수 있을지에 대한 전략 4가지가 있습니다.
1. 유한 상태 기계(FSM)
2. 감시자(observer) 패턴
3. 게시-구독(PUB-SUB)
4. 반응형 프로그래밍과 스트림
Tip49. 프로그래밍은 코드지만 프로그램은 데이터다.
데이터 변환 중심으로 설계하라.
ex) 폴더 안 파일 중에 줄 수가 가장 긴 파일 5개를 찾는 프로그램을 작성할 때 어떤 식으로 진행되느냐.
디렉토리 이름 -> 파일명 목록 -> 줄 수와 파일명 목록 -> 줄 수로 정렬된 목록 -> 마지막 다섯 개와 전체 줄 수 -> 마지막 다섯 개
위와 같이 데이터는 변환됩니다.
즉, 입력으로부터 출력을 하기 위해 데이터가 어떻게 변환되는지를 중심으로 생각하는 능력이 필요하다고 합니다.
Tip50. 상태를 쌓아 놓지 말고 전달하라.
데이터를 흘려보내라.
// 책에선 Elixir를 사용했으나 크게 와닿지 않아 약간 변경했습니다.
const content = File.read(file_name);
const lines = Find_Matching_Lines(content, pattern);
const result = Truncate_Lines(lines);객체 지향 프로그래밍에서는 데이터를 숨기고 객체 안을 캡슐화 해야 한다고 느끼겠지만, 객체들이 서로 상태를 변경한다면 결합을 많이 만들어내고 결국 객체 지향 시스템이 바꾸기 어려워지는 큰 요인이 됩니다.
위처럼 공장의 컨베이어 벨트 같이 움직이는 모습을 함수형 프로그래밍의 순수성을 표현한 것이 변환 프로그래밍이라고 이해했습니다.
관련 내용이 어렵게 적혀있지만, 코드를 일련의 중첩된 변환으로 생각하는 접근 방식을 가지는 것이 이후 프로그래밍에 도움이 될 것이라고 적혀있습니다.
Tip51. 상속세를 내지 말라.
인터페이스나 위임을 고려하라.
상속을 하는 이유 2가지는 코드의 공유, 타입의 정의이다.
이때, 코드의 공유를 위해 사용하는 상속은 결합도가 너무 높아지고 새로운 타입을 정의하기 위한 상속은 다중 상속 등의 문제가 발생할 수 있습니다.
이러한 문제를 내는 것보단 아래 대안을 사용하는 것이 좋습니다.
1. 인터페이스와 프로토콜(일부 언어에서 인터페이스를 프로토콜로 부른다고 합니다.)
2. 위임
3. 믹스인과 트레이트
Tip52. 다형성은 인터페이스로 표현하라.
상속 없이도 다형성을 만들 수 있다.
C#, Java 등의 언어에서는 interface를 지원하지만 C++에서는 따로 interface를 지원하지 않습니다.
class IDrivable
{
public:
virtual double GetSpeed() = 0;
virtual void Stop() = 0;
};C++에선 위처럼 순수 가상 함수를 이용하여 인터페이스처럼 만들 수 있습니다.
std::vector<IDrivable> v;
v.push_back(new Car(...));
v.push_back(new Bicycle(...));
v.push_back(new Motorcycle(...));위처럼 다형성을 인터페이스를 이용하여 표현하는 것이 좋습니다.
Tip53. 서비스에 위임하라.
Has-A가 Is-A보다 낫다.
책에서는 위임의 예시를 Ruby 언어로 드는데, c++로 바꿔서 예시를 들어보겠습니다.
class Account {
private:
std::string accountName;
std::unique_ptr<Persister> repo;
public:
Account(std::string name) : accountName(name)
{
repo = std::make_unique<Persister>();
}
void save()
{
repo->save(accountName);
}
};위처럼 클래스 내부에서 객체를 가지고 있는 상태에서 클래스 메소드 발생 시 해당 객체에게 처리를 위임합니다.
Tip54. 믹스인으로 기능을 공유하라.
상속 없이 기능을 추가하라.
믹스인(mixin)은 다른 클래스의 부모클래스가 되지 않으면서 다른 클래스에서 사용할 수 있는 메서드를 포함하는 클래스입니다.(C++에서는 mixin 기능을 제공하지 않습니다.)
믹스인은 is-a 관계(상속 관계)를 만들지 않고 원하는 기능을 하위 클래스로 전달할 수 있습니다.
이를 통해 상황에 맞는 전문화된 클래스를 만들 수 있습니다.
Tip55. 외부 설정으로 애플리케이션을 조정하라.
변경 가능한 값은 외부에서 관리하라.
애플리케이션 출시 후에 바뀔 수도 있는 값에 코드가 의존하고 있다면 그 값을 애플리케이션 외부에서 관리할 수 있도록 해야 합니다. 이를 주로 설정 데이터라고 하며 아래와 같은 예시가 있습니다.
- 데이터베이스나 외부 API 같은 외부 서비스 인증 정보
- 라이선스 키
- 지역에 따른 세부 서식
- 애플리케이션이 사용하는 포트 번호, IP 주소, 클러스터 이름
- 외부에서 지정하는 매개 변수
설정 방법에는 2가지가 있습니다.
1. 정적(static) 설정
YAML 이나 JSON 파일을 이용한 일반 파일이나 데이터베이스 테이블로 관리하는 설정 방식입니다.
보통 애플리케이션 모든 부분에서 해당 설정 정보에 쉽게 접근할 수 있도록 전역에서 접근할 수 있도록 하는데, 이렇게 하지 않고 API 뒤로 정보를 숨겨 놓는 것을 추천합니다.
2. 서비스형 설정(Configuration-As-A-Service)

책에서 설명하는 내용은 잘 이해되지 않아서 추가적으로 찾아보았습니다.
설정을 중앙 서버에서 관리하고 각 애플리케이션은 실행 혹은 런타임 중에 설정을 받아옵니다.
(ex. AWS AppConfig 등의 서비스가 존재합니다.)
이를 통해, 설정 데이터를 동적으로 관리할 수 있고 여러 애플리케이션이 설정 정보를 공유할 수 있습니다.
6장 동시성
동시성 vs 병렬성
- 동시성(Concurrency): 둘 이상의 코드 조각이 실행될 때 동시에 실행중인 것처럼 행동하는 것
- 병렬성(Parallelism): 실제로 동시에 실행되는 것
동시성은 보통 스레드, 프로레스 등을 이용하여 구현하고 병렬성은 CPU의 여러 코어나 연결된 여러 개의 컴퓨터가 필요합니다.
Tip56. 작업 흐름 분석으로 동시성을 개선하라.
동시에 일어나도 되는 게 무엇이고 반드시 순서대로 일어나야 하는 게 어떤 것인지 활동 다이어그램 등을 이용해 작업 흐름을 기록하는 것이 좋습니다.

동시 작업의 기회
8번 믹서를 돌리는 과정에서 10번, 11번 동작을 진행할 수 있습니다.
프로젝트에서도 주로 우리 코드가 아닌 곳에서 시간이 걸리는 활동에서 동시성을 고려할 수 있습니다.
예를 들어, 데이터베이스를 조회하거나 외부 서비스에 접근할 때, 사용자 입력을 기다릴 때 같이 다른 작업이 먼저 끝나기를 기다리는 상황에서 CPU가 쉬지 않고 더 생산적인 일을 할 수 있을 것입니다.
병렬 작업의 기회
병렬 작업은 하드웨어가 하는 것이므로 비교적 독립적인 부분 작업들이 나누기 좋습니다.(특히, 다른 부분 작업을 기다릴 필요 없이 진행할 수 있는 부분)
Tip57. 공유 상태는 틀린 상태다.
공유 상태는 버그를 만든다.
비-원자적 갱신으로 인해 메모리의 상태가 공유될 수 있으며 여러 프로세스(스레드)에서 접근할 때 버그가 발생할 수 있습니다.
그렇다면 원자적(atomic)으로 바꾸기 위해선 어떤 방법이 있는지 책에선 아래와 같이 설명을 하고 있습니다.
1. 세마포어 및 상호 배제 방법
Semaphore/mutex를 이용하는 방법
다만, 위 방법은 어떠한 개발자가 위 방법을 사용하지 않으면 다시 제대로 동기화가 되지 않을 수 있습니다.
2. 리소스를 트랜잭션으로 관리
데이터 제어를 중앙에서 관리하고 사용 입장에선 api로 호출하도록 변경합니다. 이때, 중앙에서는 세마포어 등을 이용하여 보호해야 합니다. 이때, deadlock 등에 주의합니다.
+ 이밖에도 monitor나 다른 언어(Rust)를 사용하는 방법도 있습니다.

Tip58. 불규칙한 실패는 동시성 문제일 가능성이 크다.
재현이 어렵다면 동시성을 의심하라.
코드에서 둘 이상의 인스턴스가 파일, DB, 외부 서비스 등의 리소스에 동시에 접근할 수 있다면 잠재적인 문제를 안고 있다는 의미입니다.
이러한 경우에서 주로, 여러 스레드에서 공유하는 메모리에 접근할 때 동시성 문제일 가능성이 큽니다.
Tip59. 공유 상태 없는 동시성을 위해 액터를 사용하라.
외부 동기화 없이 상태를 관리하라.
액터(Actor)는 자신만의 비공개 지역 상태를 가진 독립적인 가상 처리 장치(virtual process)입니다. 각 액터는 우편함을 하나씩 보유하고 있습니다. 액터가 잠자고 있을 때 우편함에 메시지가 도착하면 액터가 깨어나면서 메시지를 처리합니다. 처리가 끝나면 우편함에 다른 메시지를 처리하고 만약 우편함이 비어있다면 다시 잠듭니다.
- 이때, 액터는 다른 액터를 생성하거나 알고 있는 다른 액터에게 메시지를 보내거나 새로운 상태를 생성할 수 있습니다.
- 시스템이 저장하는 상태는 메시지와 각 액터의 지역 상태 뿐입니다.
- 모든 메시지는 일방향이고 답장은 없습니다.
- 각 메시지를 처리하는 중간에 다른 일을 하지 않습니다.
이 결과로, 액터들은 아무것도 공유하지 않으면서 비동기적으로 동시에 실행됩니다.
사실 이해는 잘 되지 않았지만, 외부에 상태를 공유하지 않고 해당 액터만 자신의 상태를 제어할 수 있으면서 메시지로만 상호작용하는 시스템을 의미하는 것 같습니다.
Tip60. 칠판으로 작업 흐름을 조율하라.
독립성을 유지하며 협력하라.
여러 작업을 요청해야할 때 하나씩 응답을 기다리기보다 끝나는 순서대로 하나씩 하나씩 정보들을 완성해 나가는 것이 칠판 시스템(작업 흐름 시스템)입니다. 이 시스템은 데이터의 도착 순서와 관계 없이 어떤 사실이 칠판에 올라가면 적절한 규칙이 발동되도록 하면 됩니다.
칠판(blackboard) 시스템은 액터와 다르게 공유 공간을 중심으로 협업하여 문제를 해결한다는 아키텍처를 의미하는 것 같습니다.
7장. 코딩하는 동안
Tip61. 내면의 파충류에게 귀 기울여라.
직감이 문제를 알려준다.
코드를 보고 작성하려고 할 때, 막히는 부분이 있거나 힘든 날이 있을 것입니다. 그런 날은 무의식 속에서 지금까지 개발하며 축적해온 경험과 지혜가 동작한 것일 수도 있습니다. 혹은 새로운 프로젝트나 프로젝트의 새로운 기능을 추가하려고 할 때 두려움이 생기는 경우도 있을 것입니다.
이럴 땐 하고 있는 일을 멈추고 뇌가 정리를 할 수 있도록 약간의 시간과 공간을 확보하는 것이 좋다고 합니다.
머리를 비울 수 있도록 산책을 하거나 수다를 떨거나 하룻밤 자고 나서 생각해봐도 좋고, 생각이 저절로 뇌 층층이 스며들도록 놔두다 보면 어느 순간 생각이 의식의 영역으로 올라와서 '아하' 하는 순간이 찾아올 것입니다.
이러한 방법이 잘 되지 않는다면, 그림을 그려보고 동료에게 설명도 하는 등 뇌의 다른 부위에 문제를 노출하는 방법을 하면 좋습니다.
그럼에도 되지 않을 경우엔 프로토타이핑을 하는 것입니다.
프로토타이핑을 통해서 새로운 것을 배울 수도 있고 불안감을 제거할 수도 있습니다.

Tip62. 우연에 맡기는 프로그래밍을 하지 말라.
계획된 복잡성만 받아들여라.
코드를 수정하다가 갑자기 안 되는 경우에서 왜 코드가 망가졌는지 모르는 것은 애초에 코드가 왜 잘 동작하는지도 몰랐기 때문입니다. 우리는 우연에 맡기는 프로그래밍을 하지 않고 왜 이렇게 동작하는지에 대해 명확히 알고 의도적인 프로그래밍을 해야 합니다.
책에서는 의도적인 프로그래밍을 위해 아래와 같은 방법을 설명합니다.
- 더 경험이 적은 프로그래머에게 코드를 상세히 설명하기
- 자신도 잘 모르는 코드를 만들지 않기.
- 계획을 세우고 그것을 바탕으로 진행하기.
- 신뢰할 수 있는 것에만 기대기.
- 가정을 기록으로 남기고 테스트하기.

Tip63. 알고리즘의 차수를 측정하라.
실행 시간을 예측하라.
알고리즘의 수행 시간이 얼마나 걸릴지 예측해야 합니다. 주로 Big O 표기법에 따라 분석을 진행합니다.
주로, 정렬이나 탐색 등의 알고리즘에서 수행 시간을 예측해야 합니다.(시간과 더불어 메모리 사용량도 추측할 수 있으면 좋습니다.)
O(log n) -> 이진 검색 (주로 반씩 자르는 경우)
O(n) -> 선형적이고 순차 탐색
O(n log n) -> 퀵 정렬, 힙 정렬 등(주로 분할 정복으로 이루어짐)
O(n^2) -> 제곱 (중첩 반복문)

Tip64. 추정을 테스트하라.
실제 환경에서 성능을 측정하라.
잠재적인 문제점을 해결하기 위해 O(n^2) 알고리즘을 분할 정복을 사용하여 O(n lon n)으로 줄일 수 없는지 시도해봐야 합니다. 코드의 실행 시간이 얼마나 될지 또는 메모리를 얼마나 사용할지 확실하지 않다면 직접 실행해 봐야 합니다.
그리고 추정을 이미 했더라도 실제 서비스에서 실제 데이터로 돌아가는 코드의 수행 시간만이 정말로 의미있는 수치입니다.
성급한 최적화(premature optimization)
만약 입력 데이터가 작은 경우에는 삽입 정렬과 퀵 정렬이 걸리는 시간은 비슷합니다. 그러나, 디버깅하는 것은 퀵 정렬이 더 오래 걸립니다. 입력 데이터의 규모가 작으면 데이터를 준비하는 데 걸리는 시간이 알고리즘을 돌리는 시간보다 더 길어질 수 있습니다. 이런 경우는 알고리즘의 적절한 선택이 아닐 것입니다.
그래서, 성급한 최적화를 조심해야 하며 어떤 알고리즘을 개선하느라 생각하기 전 병목을 생각해봐야 합니다.

Tip65. 일찍, 자주 리팩터링하라.
근본 문제를 해결하라.
리팩토링이란 밖으로 드러나는 동작은 그대로 유지한 채 내부 구조를 변경함으로써 이미 존재하는 코드를 재구성하는 체계적 기법이라고 정의합니다. 이러한 ㅍ리팩토링은 무질서하게 대규모로 코드를 다시 쓰는 것이 아니라 정확한 목적을 가지고 정밀하게 접근하는 활동입니다. 그래서 코드를 바꾸기 쉽게 유지하는 것입니다.
리팩토링은 언제 하는가?
- 중복 (DRY 원칙 위배)
- 직교적이지 않은 설계
- 더 이상 유효하지 않은 지식
- 사용 사례 (실제 시스템 사용 사례를 통해 중요한 부분과 그렇지 않은 부분에 따라 리팩토링)
- 성능 개선
- 테스트 통과 (테스트 통과 후 작은 부분부터 리팩토링을 한다.)
-> 책에서는 리팩토링을 일정의 압박 때문에 미루면 이후 리팩토링을 할 때 더 많은 비용이 들 수 있다고 경고합니다.
리팩토링을 어떻게 하는가?
리팩토링의 본질은 재설계입니다. 설계에 대한 새로운 사실이 밝혀지거나 문제에 대한 이해가 더 깊어지거나 요구 사항이 바뀐다면 언제라도 재설계의 대상이 될 수 있습니다. 그렇지만 거대한 규모의 코드를 닥치는 대로 헤집어 놓으면, 나중에는 리팩토링 전보다 더 안 좋은 처지에 놓일 수도 있습니다. 이런 경우를 위해 책에선 아래와 같은 리팩토링 조언을 줍니다.
1. 리팩토링과 기능 추가를 동시에 하지 않기
2. 리팩토링을 시작하기 전 테스트가 있는지 확인하고 자주 테스트 해보기.(이를 통해, 바꾼 것으로 인해 망가진 것을 빠르게 파악)
3. 단계를 작게 나누어 신중하게 작업하기. 클래스의 필드 하나를 다른 클래스로 옮기기, 메소드 하나 쪼개기, 변수명 하나 바꾸기 같이 작은 단위로 작업해야 합니다.

Tip66. 테스트는 버그만 찾는 것이 아니다.
설계에 대한 피드백을 준다.
테스트를 실행할 때 이득이 생기는 것이 아니라 테스트에 대해 생각하고 테스트를 작성할 때 생깁니다.
Tip67. 테스트가 코드의 첫 번째 사용자다.
테스트 피드백으로 방향을 잡아라.
테스트 주도 개발(TDD)
추가하고 싶은 작은 기능 하나를 결정한다.
그 기능이 구현되었을 때 통과하게 될 테스트를 하나 작성한다.
테스트를 실행한다. 다른 테스트는 통과하고 방금 추가한 테스트 딱 하나만 실패해야 한다.
실패하는 테스트를 통과시킬 수 있는 최소한의 코드만 작성한다. 그리고 이제는 모든 테스트가 통과하는지 확인한다.
코드를 리팩터링한다. 방금 작성한 테스트나 함수를 개선할 수 있는 부분이 없는지 살펴본다. 개선한 후에도 테스트가 계속 통과하는지 확인한다.
TDD를 사용할 때는 목표가 어디인지 알아야 합니다. 그렇지 않으면 테스트 커버리지를 달성하기 위해 과도하게 많은 시간을 투자할 수 있습니다.
Tip68. 끝에서 끝까지 만들어라.
상향식/하향식이 아닌 end to end 방식으로 기능을 만드는 과정을 통해 문제에 대해 배우고 코드를 채워가면서 배운 것을 적용하는 것이 중요합니다.
Tip69. 테스트할 수 있도록 설계하라.
테스트를 먼저 생각하라.
단위 테스트(unit test)를 통해 각 모듈의 동작이 계약을 잘 따르는지 확인해야 합니다.
Tip70. 소프트웨어를 테스트하라.
사용자에게 맡기지 말라.
개발팀에서 테스트하지 않으면 사용자들이 테스트하게 됩니다. 그러니 테스트 계획을 세우는 것이 좋습니다.
Tip71. 속성 기반 테스트로 가정을 검증하라.
예상치 못한 경우를 발견하라.
코드에 존재하는 계약과 불변식(invariant)을 뭉뚱그려서 속성(property)이라고 부릅니다. 코드에서 속성을 찾아내서 테스트 자동화에 사용할 수 있는데, 이를 속성 기반 테스트(property-based testing)라 합니다.
속성 기반 테스트는 즉, 항상 성립해야 하는 성질인 속성을 정의하고 무작위 입력을 대량 생성해서 그 성질이 깨지지 않는지 확인하는 테스트 방식입니다.
Tip72. 단순함을 유지하고 공격 표면을 줄여라.
기본 보안 원칙
외부 공격자가 우리가 남겨 놓은 어떤 틈이든 벌리고 들어와 시스템을 망가트리려 할 것입니다. 그렇기에 실용주의 프로그래머라면 지켜야 하는 기본 원칙이 있습니다.
1. 공격 표면을 최소화하라.
2. 최소 권한 원칙.
3. 안전한 기본값.
4. 민감 정보를 암호화하라.
5. 보안 업데이트를 적용하라.
공격 표면 최소화
시스템의 공격 표면(attack surface)영역은 공격자가 데이터를 입력하거나, 데이터를 추출하거나 서비스를 실행시킬 수 있는 모든 접근 지점을 합친 것입니다.
1. 코드의 복잡성은 공격 매개체(attack vector)를 유발한다. -> 복잡한 코드는 예상 외 부작용이 일어날 확률을 높이고 결과적으로 공격 표면을 넓힙니다. 복잡한 코드보다 단순하고 작은 코드가 더 낫습니다. 코드가 적으면 버그도 적고 보안 구멍도 줄어듭니다.
2. 입력 데이터는 공격 매개체다. -> 외부의 데이터를 절대 신뢰하지 않아야 합니다. DB나 렌더링 그밖의 다른 처리 루틴에 전달하기 전에 언제나 나쁜 내용을 제거해야 합니다.
3. 인증이 없는 서비스는 공격 매개체다. -> 인증 없는 서비스는 누구든 호출이 가능합니다. 따라서,별도로 처리하거나 제한을 두는 조치를 하지 않으면 DoS(Denial-of-Service) 공격이 가능해집니다.
4. 인증을 요구하는 서비스도 공격 매개체다. -> 인증 받은 사용자 수를 언제나 최소로 유지해야 합니다. 쓰이지 않거나 오래되고 유효하지 않은 사용자나 서비스를 정리해야 합니다.
5. 출력 데이터는 공격 매개체다. -> 응답에 들어있는 데이터가 사용자의 권한에 적절한지 늘 확인하고 주민번호 같은 위험도가 높은 정보는 일부만 노출하거나 알아볼 수 없게 변형해야 합니다.
6. 디버깅 정보는 공격 매개체다. -> 테스트에서 노출하는 정보나 실행 시점 예외 정보가 훔쳐보는 이들의 눈에 띄지 않도록 잘 보호해라.(라이브 게임에서 로그가 암호화되어 있는 이유가 이런 것들 때문인 것
최소 권한 원칙
운영 체제의 권한 체계처럼 민감한 리소스를 종류 별로 분류한 다음 각 사용자에게 필요한 종류만 권한을 부여합니다.
안전한 기본값
앱이나 웹의 사용자 기본 설정은 가장 안전한 값이어야 합니다.
ex) 비밀번호를 입력할 때 기본값은 입력한 글자를 별표로 바꾸어서 비밀번호를 숨기는 것이어야 합니다.
민감 정보를 암호화하라
개인 식별 정보나 비밀번호, 다른 인증 정보를 일반 텍스트로 남기지 말아야 합니다. 암호나 API키, SSH 키 등 인증 정보를 VCS에 넣지 않고 빌드나 배포 프로세스 내에서 설정 파일이나 환경 변수로 관리합니다.
Tip73. 보안 패치를 신속히 적용하라.
보안 업데이트를 적용하라
보안 패치는 필요하지만 부작용으로 일부 시스템이 망가질 수 있다. 그래서 기다리고 업데이트를 미룰 수도 있습니다.
저는 아직 윈도우10 쓰고 있는데, 조만간 업데이트를 해야겠다고 생각이 들었습니다.
Tip74. 이름을 잘 지어라, 필요하면 바꿔라.
의도를 표현하라.
프로그래밍에서 이름은 '모든 것'입니다.
애플리케이션, 서브시스템, 모듈, 함수, 변수 등 새로운 것을 끊임없이 만들고 그것에 이름을 부여합니다. 그리고 이름은 개발자의 의도를 드러내기 때문에 아주 중요합니다.
우리는 코드에서 하는 역할에 따라 이름을 지어야 한다고 믿습니다. 이 말은 즉, 무언가를 만들 때 이것을 왜 만드는지에 대한 생각을 해야 한다는 의미입니다.
좋은 이름을 짓더라도 리팩토링 되고 사용 방식이 변하고 의미는 미묘하게 달라집니다. 그래서 부지런히 이름을 바꾸는 것도 중요합니다.

8장. 프로젝트 전에
Tip75. 자신이 뭘 원하는지 정확히 아는 사람은 없다.
전체 방향만 알 뿐이다.
요구 사항이 땅 위에 놓여 있는 경우는 드물다. 보통 깊숙이 묻혀 있고 아예 존재하지 않을 때도 있습니다.
Tip76. 프로그래머는 사용자가 원하는 바를 깨닫도록 돕는다.
공동 창작이다.
프로그래머는 사람들이 자신이 원하는 바를 깨닫도록 돕는 것입니다.

Tip77. 요구 사항은 피드백으로 알게 된다.
탐험과 반복이 필요하다.
기존 시스템을 변경하거나 새로운 것을 요청받았을 때, 신입 개발자들이 자주 범하는 실수는 이런 요청 사항을 받았을 때 바로 해결책을 구현해버리는 것이다. 그런데 경험상 최초의 요청 사항은 궁극적인 요청 사항이 아니다.
예를 들어 '5만 원 이상인 모든 주문은 배송비가 무료입니다.' 라고 했을 때 어떤 생각이 드시나요?
- 어떤 종류의 배송이 무료인가? 당일 배송? 일반 배송? 국제 배송은 어떤지?
- 5만 원에 현재 고객이 선택한 배송 방법의 배송비도 포함인가? 5만 원에 세금도 포함인가? 5만 원 기준이 바뀔 수도 있는가? 등등
물론 의뢰인이 이미 생각해 본 문제일 수 있지만 이러한 것들을 질문으로 정보를 끄집어내야 합니다. 또, 의뢰인이 미처 고려해 보지 못한 문제도 있을 것입니다. 이 부분에서, 좋은 개발자라면 협상 능력을 키울 수도 있는 부분입니다.

Tip78. 사용자와 함께 일하라.
진짜 사용 방식을 이해하라.
의뢰인의 머릿속 깊숙이 들어갈 수 있는 방법은 바로 의뢰인이 되어 보는 것입니다. 함께 일하면서 신뢰를 구축하고 의사소통의 기반을 다지는 데 많은 도움이 될 것입니다. 또 짧은 주기로 만나면서 직접 피드백을 받을 수 있습니다. 피드백 수집은 의뢰인과의 관계를 쌓아가는 시작점입니다.

Tip79. 정책은 메타데이터다.
코드에 고정하지 말라.
정책이 바뀔 때 시스템의 메타데이터만 업데이트하면 잘 분리된 시스템을 만들 수 있습니다.
ex) 권한이 있는 사용자만 기록에 접근할 수 있다. -> 권한 정책을 메타데이터로 관리합니다.
Tip80. 프로젝트 용어 사전을 사용하라.
용어를 한곳에서 관리하라.
프로젝트 용어 사전을 만들고 관리하여 사용자가 개발자가 동일한 용어를 사용할 수 있도록 해야 합니다.

Tip81. 생각의 틀을 벗어나지 말고, 틀을 찾아라.
진짜 제약 조건을 발견하라.
프로젝트 진행 중에 정말 어려운 문제를 만날 수 있습니다.
이럴 때, 모든 선입견을 의심하고 그것이 진짜 바꿀 수 없는 제약인지 가늠해 봐야 합니다. 이것은 틀을 벗어나고 벗어나지 않고의 문제가 아니라 틀을 찾는 것 즉, 진짜 제약을 찾는 일입니다. 먼저 생각해 볼 수 있는 모든 해결 경로 후보를 눈 앞에 나열해봅니다. 그리고 모든 경로에 대해 점검하면서 왜 안 되는지 설명해봅니다. 아니면 잠시 문제에서 벗어나 산책하고 오거나 내일로 미루는 것이 좋습니다. 그래도 문제에서 손을 놓고 싶지 않다면, 문제를 놓고 함께 이야기할 사람을 찾는 것입니다.
Tip82. 코드에 혼자 들어가지 말라.
함께 프로그래밍하라.
페어 프로그래밍
사람은 서로 다른 배경과 경험을 가지고 있고 문제를 푸는 데도 다른 기법과 접근 방법을 사용합니다. 주어진 문제에 집중하는 정도나 주의력도 제각각입니다. 한 개발자가 세부 사항에 집중하며 다른 개발자가 전체적인 관점에서 본다면 더 나은 소프트웨어를 만들 수 있습니다.
몹 프로그래밍
둘이 아닌 여러명이 동시에 문제 하나에 달려드는 것입니다. 셋 이상의 사람이 참여하는 페어 프로그래밍의 확장판입니다.
늘 혼자서 프로그래밍 했다면 페어 프로그래밍을 시도해도 좋을 수도 있습니다.
그리고 다른 사람의 관점을 듣고 이해하려고 노력합니다.

Tip83. 애자일은 방식이다.
형용사다.
애자일(agile): 기민하다 는 뜻의 형용사입니다. 즉, 애자일은 일하는 방식을 말하는 것이지 우리가 애자일이 아닙니다.
애자일이란 것은 변화에 대응하는 것, 일을 시작한 후 맞부딪히는 미지의 것에 대응하는 것이 전부입니다.
애자일하게 일하는 방법은 다음과 같습니다.
- 여러분이 어디에 있는지 알아내라.
- 도달하고 싶은 곳을 향하여 의미 있는 발걸음을 가능한 한 작게 옮겨라.
- 어디에 도착했는지 평가하고, 망가트린 것이 있으면 고쳐라.
위 과정을 끝날 때까지 반복하고 위 과정을 모든 일의 모든 층위에서 재귀적으로 사용합니다.
애자일이 전반적으로 작동하게 하려면 좋은 설계를 만들어야 합니다. 좋은 설계는 무언가를 바꾸기 쉽게 하기 때문이고 바꾸기 쉽다면 모든 층위에서 아무런 주저 없이 문제를 바로잡을 수 있을 것입니다.

특히 Riot Games의 League of Legends 를 개발할 때 애자일 방식을 적용했다고 합니다.
9장. 실용주의 프로젝트
Tip84. 작고 안정적인 팀을 유지하라.
실용주의 팀은 대략 10명~12명 이하여야 하고 구성원이 추가되거나 빠지는 일은 드물어야 합니다.
모두가 서로 잘 알고, 신뢰하며, 의존해야 합니다.
Tip85. 실현하려면 계획하라.
회고와 학습을 계획하라.
지식 포트폴리오를 계획하라
성공을 원하는 팀이라면 자신들의 지식과 기술에 투자하는 것을 고려해야 합니다. 여러분의 팀이 진정 개선하고 혁신하고 싶다면 계획을 세워야 합니다. "시간이 나면 그때" 하겠다는 것은 "영원히 하지 않겠다"는 것입니다. 할 일을 업무 목록이나 업무 흐름 도구를 사용하든 간에 기능 개발로만 채우지 말고 해야 할 일들이 있습니다. 예를 들면 아래와 같습니다.
- 구형 시스템 유지보수
- 프로세스 회고와 개선 (지속적인 개선이 일어나려면 주위를 둘러보고 무엇이 잘되고 안되는지를 파악하고 계획하고 고쳐야 합니다.)
- 새로운 기술 탐험 (새로운 기술이나 라이브러리를 "다들 쓰니까" 라는 이유로 또는 컨퍼런스에서 봤다는 이유로 도입하지 않고 프로토타이핑을 해보고 신중하게 조사해야 합니다.)
- 학습 및 기술 갈고 닦기 (팀원들을 전도할 계획을 세우고 점심 먹으며 가볍게 얘기하거나 스터디 시간을 잡아 공부하자.)

Tip86. 모든 기능을 갖춘 팀을 조직하라.
처음부터 끝까지 만들 수 있게 하라.
팀의 존재를 소통하라
훌륭한 프로젝트팀은 뚜렷한 특성이 있습니다.
사람들은 이 팀과의 회의를 기대합니다. 모든 사람이 좋아할 만한 잘 준비된 퍼포먼스를 보게 될 걸 알기 때문이고 문서는 깔끔하고 정확하며 일관적이고 팀은 한 목소리로 얘기합니다. 심지어 유머 감각도 있을 것입니다.
프로젝트를 시작할 때 이름을 지어주면 팀이 하나로 의사소통 하게 도와주는 마케팅 역할을 할 수 있고 정체성을 확립할 수 있습니다.
팀 예광탄
프로젝트팀은 프로젝트의 여러 분야에서 수많은 기술을 섭렵하고 다양한 과제를 해결해야 합니다. 요구 사항을 이해하고, 아키텍처를 설계하고, 프론트엔드와 서버 코드를 쓰고, 테스트를 돌리는 모든 일을 해내야 합니다. 그런데, 이런 활동들과 과제들을 독립적으로 따로따로 수행할 수 있는 일은 불가능합니다.
그래서 설명했었던 예광탄 기법을 이용하여 처음에는 작고 제한적일지라도 시스템의 끝에서 끝까지 전체에 걸쳐 있는 단일 기능을 개발할 것을 추천합니다. 처음에는 아무리 작고 제한적인 기능밖에 만들지 못하더라도 말입니다. 이 말은 작업에 필요한 기술을 모두 팀 안에 갖추어야 한다는 의미입니다. (게임 기준) 클라이언트, UI/UX, 서버, QA 등이 모두 함께 일하는 것이 편안하고 익숙해야 합니다.

Tip87. 유행이 아니라 맞는 것을 사용하라.
환경에 맞는 방법을 선택하라.
특정한 개발 방법(애자일)이나 프레임워크 테스트 기법을 굳이 사용하는 이유가 정말 지금 하는 일에 잘 맞아서 인지? 아니면 ㄱ단순히 최근에 유행하는 기법이라 그런지 생각해봐야 합니다.
Tip88. 사용자에게 필요할 때 제공하라.
기다리게 하지 말라.
사용자가 필요로 할 때마다, 사업적으로 배포가 의미 있을 때마다 배포하는 것이 중요합니다.
Tip89. 버전 관리로 빌드, 테스트, 릴리스를 운용하라.
커밋이 자동화를 시작하게 하라.
대부분 Jenkins를 이용해 CI/CD를 하는 것 같습니다.
Tip90. 일찍, 자주, 자동으로 테스트하라.
자동 테스트가 가장 효과적이다.
코드를 작성하자마자 테스트를 해야 합니다.
일부 프로젝트에서는 제품 코드 보다 테스트 코드가 더 많을 수도 있습니다.
Tip91. 모든 테스트가 끝날 때까지 코딩은 끝난 게 아니다.
당연하다.
빌드 과정에서 단위 테스트, 통합 테스트, 유효성 평가 및 검증, 성능 테스트 등을 진행해야 합니다.
Tip92. 버그를 심어 놓고 테스트를 테스트하라.
버그를 심어 검증하라.
완벽한 소프트웨어를 작성할 수 없는 것처럼 완벽한 테스트 소프트웨어 역시 작성할 수 없으므로 테스트를 테스트할 필요가 있습니다. 의도적으로 버그를 만든 테스트를 이용합니다.

Tip93. 코드 커버리지보다 상태 조합을 테스트하라.
중요 상태를 검증하라.
코드 커버리지만 하지 말고 속성 기반 테스트 등을 이용해 상태 조합을 테스트합니다.
Tip94. 버그는 한 번만 잡아라.
이후에는 자동 테스트가 맡아야 한다.
그물 조이기
테스트에서 가장 중요한 개념으로 버그가 기존 테스트의 그물을 빠져나갔다면 다음 번에는 그걸 잡아낼 수 있도록 새 테스트를 준비해야 합니다.
한번 인간 테스터가 버그를 찾았다면 더는 인간 테스터가 그 버그를 만나서는 안 됩니다. 그 순간 후에는 해당 버그를 확인할 수 있게 자동화 테스트를 수정해야 합니다.(어차피 또 일어날 거기 때문에 자동화 테스트가 우리를 대신해 찾아 줄 수 있는 버그까지 쫓아다닐 시간이 없습니다.)
Tip95. 수작업 절차를 사용하지 말라.
자동화는 반복적이고 수동적인 작업(빌드, 테스트, 배포)을 자동화 도구를 통해 처리하여 실수를 줄이고 개발 생산성을 높이는 핵심 실천하는 방법입니다.

Tip96. 사용자를 기쁘게 하라.
개발자로서 목표는 사용자를 기쁘게 하는 것입니다.
사용자가 진짜로 원하는 것은 코드가 아니라 목적과 예산에 맞추어 풀어야 하는 사업상의 문제가 있습니다. 그리고 우리의 팀과 일하면서 문제를 풀어내야 합니다.
사용자는 프로젝트의 고객 잔존율, 데이터의 품질, 절감한 비용 등을 기준으로 프로젝트의 성공 기준을 판단할 것입니다. 이러한 목적을 파악하여 사용자의 기대를 어떻게 충족시킬 수 있을지 고민을 시작할 수 있습니다.
도메인에 대한 지식이 늘어남에 따라 근본적인 사업 문제를 해결하기 위해 우리가 맡지 않은 부분에 대해서도 더 좋은 제안을 할 수 있습니다. 조직의 여러 측면을 경험한 개발자가 이런 방법을 더 잘 찾아낼 수 있다고 굳게 믿습니다.
고객을 기쁘게 하고 싶다면 고객이 문제를 풀 때 적극적으로 도와줄 수 있는 관계를 구축해야 합니다.
실용주의 프로그래머는 "문제 해결사"입니다.

Tip97. 자신의 작품에 서명하라.
자부심을 가져라.
Tip98. 먼저, 해를 끼치지 말라.
실패로 누구도 고통받지 않게 하라.
Tip99. 쓰레기 같은 인간을 돕지 말라.
여러분도 그렇게 된다.
Tip100. 결국 당신의 삶이다. 삶을 사람들과 나누고, 삶을 축하하고, 삶을 만들어가라. 그리고 그걸 즐겨라!

책을 읽고 나서
책에서는 기초적인 컴퓨터 과학 지식을 전제로 설명하는 부분이 있었고, 용어가 갑자기 바뀌는 경우도 있었습니다.
그래도 전공 시간에 배웠던 개념과 지식을 바탕으로 이해하려고 하니, 용어가 바뀌는 부분에서도 검색이나 스스로의 생각을 통해 빠르게 내용을 따라갈 수 있었습니다.
후반부로 갈수록 앞에서 다뤘던 내용이 다시 등장하기도 하고 집중력이 다소 떨어지기도 했지만, 학부 시절 프로젝트 경험을 떠올리며 실용주의 프로그래머로서의 역할을 이해하기 쉬웠고, 앞으로 어떻게 성장해 나갈 수 있을지에 대한 방향도 배울 수 있는 기회였습니다.
개발자로서 일하지 않는 시간에도 자기계발을 통해 끊임없이 변화하는 산업에 적응하고 발전해 나가는 자세가 중요하다고 느꼈습니다. 특히 게임 개발을 하면서, 평소 좋아하던 게임을 가까이에서 접하고 직접 만들어 보는 과정 자체가 앞으로의 커리어를 발전시키는 원동력이 되고 있다고 생각합니다.
AI의 발전으로 또 한 번의 큰 변화가 일어나고 있는 지금, 이러한 흐름에 잘 적응하고 생산성과 성장을 동시에 이뤄내는 개발자가 되기 위해 계속 노력해야겠다고 다짐하게 되었습니다.
'CS > 소프트웨어 책 후기' 카테고리의 다른 글
| 클린 코드(Clean Code) 책 후기 (0) | 2026.03.01 |
|---|