ݺߣ

ݺߣShare a Scribd company logo
염재현
코드 갶독성
갶독성
2015 나는 프로그래머다 컨퍼런스 (11) 염산악 - 갶독성에 대하여
갶독성? 왜?
프로그램은 자주 읽힌다. 쓰는 것보다 읽는 것이 더 빈번하다.
남들이 이해하기 쉬워야 한다.
나만 보기 위한 코드도 언젠가 사용될 수 있다.
... 잔소리 잔소리 ...
예... 예... 예...
거기에 제 생각은요
...
이유 하나. 프로페셔널리즘
갶독성 떨어지는 코드는...
아마추어 같다... 정말로...
초보자의 코드를 보세요. 쉬운 코드인가?
초보 운전이 어떤지도 생각해 봅시다.
이유 둘. 큰 기쁨
아름다운 코드는 큰 기쁨을 줍니다.
내 코드가 이렇게 아름다울 리가 없어!
예쁘게 나오니까 재미도 있네!
이유 셋. 성장과 기회의 필수 조건
코드가 읽기 쉬워야 남들이 ___(을/를) 써 줍니다.
코드가 읽기 쉬워야 ___(이/가) 개선점을 찾을 수 있습니다.
코드가 읽기 쉬워야 배울 수 있습니다.
갶독성이 뛰어난 코드를 작성하면...
프로처럼 보이게 한다. 아니, 그게 프로다.
큰 기쁨을 준다.
성장할 수 있게 해 준다.
갶독성을 버리면...
아마추어를 벗어나지 못한다.
코딩이 재미가 없어진다. 이직하고 싶어진다.
성장 없이 정체된다.
갶독성이 대세다
프로그래밍 기법 발전은 복잡도를 줄이기 위한 것으로 모아
짐.
제어 구조, 서브루틴, 클래스, 패키지, 구조화, 객체지향, 함수
형, 추상화, 가비지 컬렉션, 디자인 패턴...
갶독성 향상 역시 복잡도(뇌의 로드)를 줄입니다.
평생을 수련해야 하는 갶독성
그렇기 때문에 우리는 평생을 걸쳐 갶독성을 향상시키기 위
한 수련에 수련을 거듭해야 하는 것입니다.
옳거니!
갶독성 수련 시작
목표:
코드를 읽는 사람이 코
드를 이해하는데 드는
시간과 노력을 최소화
눈에 보이는 것
소중한 것들은 눈에 보이지 않는 것
들이야
-어린 왕자
눈에 보이는 것도 소중하단다.
-염산악
레이아웃: 기본 중에 기본
좋은 구현, 충분한 주석에도 레이아웃이 엉망이면 이해 불가
실수를 막기 위한 규칙들
자동화된 도구로 강제 가능
야심찬 도전과제
오른쪽의 코드를 우리 한 번 열심히 잘
이해하도록 해 봅시다.
하기 싫습니다.
하지 맙시다.
google.j.b=(!!location.hash&&!!location.hash.match('[#&]((q|fp)=|tbs
=rimg|tbs=simg|tbs=sbi)'))||(google.j.qbp==1);(function(){google.c={
c:{a:true,d:false,i:false,m:true,n:false}};google.sn='webhp';(functi
on(){function
f(a,b,c){g.push({o:a,v:b,w:c});a.addEventListener?a.addEventListener
(b,c,!1):a.attachEvent&&a.attachEvent("on"+b,c)}function
e(a,b,c){a.addEventListener?a.removeEventListener(b,c,!1):a.attachEv
ent&&a.detachEvent("on"+b,c)}var
g=[];google.timers={};google.startTick=function(a,b){var
c=b?google.timers[b].t.start:google.time();google.timers[a]={t:{star
t:c},e:{},it:{},m:{}};(c=window.performance)&&c.now&&(google.timers[
a].wsrt=Math.floor(c.now()))};google.tick=function(a,b,c){google.tim
ers[a]||google.startTick(a);c=c||google.time();b instanceof
Array||(b=[b]);for(var
d=0;d<b.length;++d)google.timers[a].t[b[d]]=c};google.c.e=function(a
,b,c){google.timers[a].e[b]=c};google.bit=function(a,b){google.timer
s[a]||google.startTick(a);var
c=google.timers[a].it[b];c||(c=google.timers[a].it[b]=[]);var
d=c.push({s:google.time()})-1;return
function(){c[d]&&(c[d].e=google.time())}};google.c.b=function(a){var
b=google.timers.load.m;b[a]&&google.ml(Error("a"),!1,{m:a});b[a]=!0}
;google.c.u=function(a){var
b=google.timers.load.m;if(b[a]){b[a]=!1;for(a in
b)if(b[a])return;google.csiReport()}else
google.ml(Error("b"),!1,{m:a})};google.rll=function(a,b,c){var
d=function(b){c(b);e(a,"load",d);e(a,"error",d)};f(a,"load",d);b&&f(
a,"error",d)};google.ull=function(){for(var
실수 방지
빠르게 코드를 읽어봅시다.
다시 찬찬히 봅시다.
에너지가 더 소모됩니다.
사실 Submit은 한 번만 수행됩니
다.
에너지를 아낍시다.
for (i = 0; i < size; ++i)
if (a[i] > b[i]) {
a[i] = b[i];
last = i;
}
Submit(a);
return size;
자동화된 도구
가능하면 자동화된 도구로 최대한
간단한 포매팅 문제를 해결합시다
.
for (i = 0; i < size; ++i) {
if (a[i] > b[i]) {
a[i] = b[i];
last = i;
}
}
Submit(a);
return size;
레이아웃: 합의가 필요
들여쓰기 규칙
줄을 나누는 규칙, 블럭을 나누는 규칙
순서: #include, 입출력 변수 등
덤: 읽기 쉬운 조건문
if (size < MAX_SIZE) { ... }
if (MAX_SIZE > size) { ... }
if (0 <= index && index < size) { ... }
if (index >= 0 && index < size) { ... }
코딩 컨벤션
유명한 코딩 컨벤션들을 답습하기
언어에서 권장하는 컨벤션 익히기
주변의 코드들과 일관성 유지하기 (개성을 죽이는 교육)
문단 나누기
관련 있는 것들끼리 뭉치자. 줄을 맞추자.
=> 루틴이 하는 일을 하나로 하자.
=> 클래스에 연관있는 메소드들만 있게 하고 더 가까이 관련 있는 것들끼리 묶자.
=> 파일 이름 잘 짓고 끼리끼리 묶자.
클래스/파일/패키지 이름
클래스, 파일, 패키지 이름이 그냥 Util, utils.cc, helper.cc 같은 것이고 수많은 루
틴들이 들어가 있지는 않는가? What about foobar, then?
의미 있는 것들끼리 모으고 묶고 이름을 짓자. 이름이 지어지지 않으면 분리하자.
이 클래스가 무엇을 하는지, 너무 많은 것을 하지는 않는지 살피자.
시대와 환경의 변화에 따른 권장 사항의 변화
강제적인 헝가리안 표기법
타입 검사가 잘 되지 않고 IDE 지원이 열악하던 시절 좋은 표기법이었음.
하나의 종료점
수동으로 메모리 할당과 해제를 하던 시절에는 유용한 규칙이었음.
GOTO exit;
스코프 등을 이용한 자원 획득/해제(RAII), with등으로 제약, try-finally로 해제 코드를 넣을 수 있
Self
Documenting
말하지 않아도 알아요. 그냥 바라보
면
- 모 마쉬멜로와 초콜릿 간식
적당히 눈치를 줘야 알아준다.
- 염산악
코드가 말한다
아마추어의 코드에는 지나치게 주석이 많음.
줄 단위의 주석... 주석을 상세히 쓰라니 배운대로
열심히 i에 0을 넣는다고 주석 씀.
상세한 주석에도 불구하고 읽기는 매우 어려움.
읽어도 의도를 알기 어려움. (e.g. // Skip first
row)
// Remove first 6 characters
query = request[6:]
프로의 코드에는 주석이 적당함.
문서화에 필요한 주석이 대부분. 변수나 함수 이
름이 스스로 설명함.
주석이 거의 없지만 읽기는 매우 쉬움.
코드로 알기 어려운 의도 등을 주석으로 밝힘.
(e.g. // Skip header row)
const string pathPrefix = "/api/?";
query = request[len(pathPrefix):]
함축과 압운: 언어 영역을 배운 이유
이름에 정보를 최대한 많이 넣어야 하지만 특히 긴 이름 사용이 반복되면 짜증난
다.
따라서 함축이 필요하다!
압운은 두운, 요운, 각운으로 나뉜다. 요~ 롸임 있는 코드.
max, min, num, cnt, hex, unsafe, enc, has, is, ...
함수 이름
서브루틴은 호출하고 반환받을 수 있는 코드의 덩어리에 이름을 붙인 것.
이 이름을 잘 붙이는 것으로 독자들이 그 코드가 무엇을 하는지 더 잘 알 수 있다.
언어 영역/외국어 영역에서 배운 포괄하는 제목 짓기를 생각하자.
함축
프로그래머는 국어 실력도 좋아야 한다.
GetResults(); // 결과들을 리턴한다.
함축
프로그래머는 국어 실력도 좋아야 한다. (아니, 영어...?)
GetResults()? FetchResults()? ExtractResults()? GenerateResults()?
ComputeResults()?
함축
아, 파라미터가 있었다..
GetResults({"min_score": 2.5}); // score가 2.5점 이상인 결과들을 리턴한다.
=> LoopOverResultsAndReturnOnlyThoseWithScoreHigherThan(2.5);
??? ??? 중산정왕의 N대손이자 황숙이자 예주목이고 좌장군인 유비
그래서 한마디로 그것이 무엇이옵니까?
함축
GetResults({"min_score": 2.5}); // score가 2.5점 이상인 결과들을 리턴한다.
FilterResults({"min_score": 2.5}); // 2.5점 이상의 결과만 필터한다.
함축
GetResults({"min_score": 2.5}); // score가 2.5점 이상인 결과들을 리턴한다.
FilterResults({"min_score": 2.5})? // 2.5점인 이상은 버리나요? 이하를 버리나요?
함축
GetResults({"min_score": 2.5}); // score가 2.5점 이상인 결과들을 리턴한다.
FilterResults({"min_score": 2.5}); // 2.5점 이상의 결과만 버리나? 취하나?
SelectResults({"min_score": 2.5});
ExcludeResults({"min_score": 2.5});
압운
매개변수 이름을 잘 짓자. @Nullable 같은 것이 지원되지 않으면 OrNull, NotNull
을 변수 이름이나 함수 이름에 붙이면 더 읽기 쉬워지는지 살펴보자.
문자열은 경우에 OrEmpty 같은 것이 붙으면 더 읽기 쉬워지는지 살펴보자.
시간에 단위 _sec, _ms, _us, _ns 같은 것이 붙으면 더 읽기 쉬워지는지 보자.
폰트 크기가 font_size인 것보다 _pt, _px가 붙으면 도움이 되는지 보자.
font_size_px = PointToPixel(font_size_pt);
변수 이름을 지어주세요
// If needs recalculation
if (xxx && yyy && zzz) {
... code ...
}
bool needs_recalc = xxx && yyy && zzz;
if (needs_recalc) {
... code ...
}
과정 설명
여러 스텝으로 나눌 수 있는 함수
이미 이 정도라도 나눠진 코드를
기반으로 작업할 수 있는 것을 감
사하게 생각하자.
// Step 1. Read input.
... code ..
// Step 2. Generate results.
... code ...
... code ...
// Step 3. Update results.
... code ...
... code ...
// Step 4. Write output.
... code ...
함수 이름으로
이름이 곧 어떤 순서로 무엇을 하
는지 설명한다.
ReadInput();
GenerateResults();
UpdateResults();
WriteOutput();
이미 좋지 않았나요?
=> 협업으로 변화되는 코드의 미래를 봅시다.
왜 굳이?
흐름의 단순화
들어올 땐 마음대로지만 나갈 땐 아
니란다~
- ???
마음대로 들어오시면 곤란합니다.
그리고 볼 일 다 보셨으면 어서 나가
주시죠.
- 염산악
일찍 리턴하기 패턴
무엇을 리턴해야 할 지 아는 순간 바로 리턴해 버림. (multiple exit, return early)
중요한 코드 부분의 양이 줄어들고 예외 상황이 줄어들어 흐름이 단순해 짐.
기억해야 할 변수도 줄어들게 되어 뇌의 부담을 줄여줌.
루프에서 일찍 리턴하기
흐름이 단순해지고 리턴 값이 분명해진다.
One Exit Result* result = nullptr;
for (int i = 0; i < n; ++i) {
Message msg = getMessage();
if (!msg.empty()) {
... actual code ...
result = msg.AsResult();
break;
}
}
return result;
Return Early
for (int i = 0; i < n; ++i) {
Message msg = getMessage();
if (!msg.empty()) {
... actual code ...
return msg.AsResult();
}
}
return nullptr;
루프와 함수 분리
기억해야 할 변수도 줄어든다.
루프가 무엇을 하는지가 분명해진다. (함수 이름을 잘 지으면...)
루프와 함수 분리
루프와 상태 변수를 이용해야 하
는 경우가 있다면 함수를 분리하
면 더 읽기 쉬워지는지 살펴보자.
이번에도 이미 이런 코드를 갖고
작업할 수 있음을 감사하게 생각
하자.
bool found = false;
for (const auto& item : items) {
if (...) {
found = true;
break;
}
}
if (found) {
...
}
루프와 함수 분리
return을 이용하면 코드가 간단함.
물론 functional한 함수들을 이용
하는 방법도 있겠음.
bool ContainsItem(...) {
for (const auto& item : items) {
if (...) {
return true;
}
}
return false;
}
if (ContainsItem(...)) {
...
}
GOTO와 break label이 사용되는 경우
여러 루프를 빠져나와야 하는 경우 break label을 쓰거나 조건 변수를 써야 하는
경우
함수를 분리하면 (혹은 클로저를 쓰면) 더 읽기 쉬워지는지 살펴보자.
return은 함수 안에 있는 모든 루프를 한 번에 빠져나오므로...
루프와 함수 분리
goto를 쓰거나 break label을 써야
한다.
코드가 길어지면 흐름이 더욱 복
잡해진다.
bool found = false;
for (const auto& item : items) {
for (const auto& attr: item.attrs()) {
if (...) {
found = true;
... (1) code ...
goto outer; // break outer;
}
}
}
outer:
if (found) {
... (1) code ...
}
... (2) more code ...
루프와 함수 분리
return을 이용하면 코드가 간단함.
물론 functional한 함수들을 이용
하는 방법도 있음.
bool ContainsItem(...) {
for (const auto& item : items) {
for (const auto& attr : item.attrs())
{
if (...) {
return true;
}
}
}
return false;
}
if (ContainsItem(...)) {
... (1) code ...
}
... (2) more code ...
예외 상황 쳐 내기
예외 상황을 안고 코드를 읽을 때 심란해진다.
일찍 쳐 내면 이후의 코드가 간단해지는 경우가 많다.
One Exit Result* result = nullptr;
if (condition) {
Message msg = getMessage();
if (!msg.empty()) {
... some code ...
... some code ...
result = msg.AsResult();
}
}
return result;
Return Early if (!condition) {
return nullptr;
}
Message msg = getMessage();
if (msg.empty()) {
return nullptr;
}
... some code ...
... some code ...
return msg.AsResult();
Return Early
이 부분이 초기 조건 검사 부분이
된다.
if (!condition) {
return nullptr;
}
Message msg = getMessage();
if (msg.empty()) {
return nullptr;
}
... some code ...
... some code ...
return msg.AsResult();
예외 조건 검사
조건없이 묻지도 따지지도 않고...
- 보험/대부 업계
저기 저 그렇게 쉬운 사람 아니거든
요?
- 염산악
Design by Contract (DbC)
추상 자료형에 다음을 덧붙임
precondition
invariant
postcondition
Design by Contract
Eiffel, D, Ada 등의 언어에서 지원함
지원하지 않는 언어도 써드 파티로 지원하거나 assert를 지
원하는 경우가 많음
Defensive Programming vs DbC
방어 운전에서 따온 것으로, "입력이
무엇이 들어올지 알 수 없으니, 입력을
믿지 말고 조심해서 어떻게든 잘 가 봅
시다." 와 같은 자세
가능하면 죽지 않고 감.
어떤 입력이 들어와야 하는지 상세히
명시하고 잘못된 입력이 들어오면, "계
약 위반이오! 그러지 않기로 했잖소!"
하면서 죽어버리는 자세
프로덕션 코드에서는 성능을 위하여
검사하지 않기 때문에 위반 시 정의되
지 않은 행동을 하게 됨
Precondition
조건이 만족하지 않을 때, 로그를
남기고 에러를 리턴하거나, 죽어
버리거나 할 수 있다. 디버그 모드
인지 릴리즈 모드인지에 따라 달
라질 수 있다.
if (!Precondition(src)) {
// ...
}
for (i = 0; i < size_; ++i) {
if (min_[i] > src[i]) {
min_[i] = src[i];
last = i;
}
}
return last;
Postcondition
if (!Precondition(src)) {
// ...
}
last = Private(src);
if (!Postcondition(last)) {
// ...
}
return last;
주석 백일장
승고월하문이 좋을지 승퇴월하문이
좋을지 고민하는 중이었습니다.
- 당나라 시인 가도
둘 중에 가능하면 오해 없는 쪽으로
해 주시고 이유를 써 주세요. 아니면
TODO로 달아주세요.
- 염산악
퇴고
주석을 열심히 퇴고 하면 교통사고 가해자라도 용서받을 수 있다.
시작이 반이다
일단 먼저 써 놓고 본다. // 헉! 여기 잘못하면 뻗음.
침착하게 그게 무슨 뜻인지를 다시 고쳐쓴다.
영어가 불편하면 일단 머리에 있는 걸 잊기 전에 한국어라도 쓰고 침착하게 고쳐
쓴다.
오해하지 말고 들어...
오해할만한 문장으로 쓰지 않는다.
내 주석을 어떻게 오해할 수 있을지 삐딱하게 바라보자.
다시 고쳐 써서 더 좋은 문장으로 만든다.
보고 드리겠습니다!
보고 드리겠습니다!
코드를 하나 하나 설명하지 않는다. 이런 걸로 영어 공부 하고 싶어도 참는다.
보고 드리겠습니다!
코드를 하나 하나 설명하지 않는다. 이런 걸로 영어 공부 하고 싶어도 참는다.
필요 없는 주석은 지운다. 영어로 써 놓은 주석이 아까워도 할 수 없다.
보고 드리겠습니다!
코드를 하나 하나 설명하지 않는다. 이런 걸로 영어 공부 하고 싶어도 참는다.
필요 없는 주석은 지운다. 영어로 써 놓은 주석이 아까워도 할 수 없다.
슬라이드에 필요 없는 내용들도 좀 지우시지?
상세한 것만이 능사가 아니다
길게 쓰는 것보다 단어 하나의 파워가 더 강력할 수 있다.
// 이 함수들은 따로 하는 일에 대한 구현이 들어 있는 것이 아니라 좀 더 쓰기 어
려운 복잡한 함수를 호출하기 위한 준비 작업을 하고, 매개 변수의 순서도 우리
프로젝트 표준에 맞게 바꾸어서 구현이 들어 있는 함수들을 호출해 준다.
상세한 것만이 능사가 아니다
길게 쓰는 것보다 단어 하나의 파워가 더 강력할 수 있다.
// 이 함수들은 따로 하는 일에 대한 구현이 들어 있는 것이 아니라 좀 더 쓰기 어
려운 복잡한 함수를 호출하기 위한 준비 작업을 하고, 매개 변수의 순서도 우리
프로젝트 표준에 맞게 바꾸어서 구현이 들어 있는 함수들을 호출해 준다.
// 이 함수들은 래퍼들이다.
어떤 것을 쓸까요?
왜 이렇게 했는가?
TODO, FIXME, ...
버그 같아 보이지만 버그가 아님, 핵 구현, 속도 때문에 정확도를 조금 버림, 이 방
법보다 그 방법이 더 빠를 거라 생각하겠지만 이게 더 빨랐음.
커맨드라인 사용 예제
정리
레이아웃 및 형식화는 자동화된 도구와 열정으로. 그리고 일관성을 유지하자
의미 있는 것들끼리 모아서 추상화하자
변수/함수 이름만 잘 붙여도 갶독성이 증가한다
흐름을 단순화하자
주석을 잘 쓰고 퇴고하자.
Final point
갶독성을 좋게 하기 위한 노력을
계속 하자.
Thanks!
<jae.yeom@gmail.com>

More Related Content

2015 나는 프로그래머다 컨퍼런스 (11) 염산악 - 갶독성에 대하여

  • 4. 갶독성? 왜? 프로그램은 자주 읽힌다. 쓰는 것보다 읽는 것이 더 빈번하다. 남들이 이해하기 쉬워야 한다. 나만 보기 위한 코드도 언젠가 사용될 수 있다. ... 잔소리 잔소리 ... 예... 예... 예...
  • 6. 이유 하나. 프로페셔널리즘 갶독성 떨어지는 코드는... 아마추어 같다... 정말로... 초보자의 코드를 보세요. 쉬운 코드인가? 초보 운전이 어떤지도 생각해 봅시다.
  • 7. 이유 둘. 큰 기쁨 아름다운 코드는 큰 기쁨을 줍니다. 내 코드가 이렇게 아름다울 리가 없어! 예쁘게 나오니까 재미도 있네!
  • 8. 이유 셋. 성장과 기회의 필수 조건 코드가 읽기 쉬워야 남들이 ___(을/를) 써 줍니다. 코드가 읽기 쉬워야 ___(이/가) 개선점을 찾을 수 있습니다. 코드가 읽기 쉬워야 배울 수 있습니다.
  • 9. 갶독성이 뛰어난 코드를 작성하면... 프로처럼 보이게 한다. 아니, 그게 프로다. 큰 기쁨을 준다. 성장할 수 있게 해 준다.
  • 10. 갶독성을 버리면... 아마추어를 벗어나지 못한다. 코딩이 재미가 없어진다. 이직하고 싶어진다. 성장 없이 정체된다.
  • 11. 갶독성이 대세다 프로그래밍 기법 발전은 복잡도를 줄이기 위한 것으로 모아 짐. 제어 구조, 서브루틴, 클래스, 패키지, 구조화, 객체지향, 함수 형, 추상화, 가비지 컬렉션, 디자인 패턴... 갶독성 향상 역시 복잡도(뇌의 로드)를 줄입니다.
  • 12. 평생을 수련해야 하는 갶독성 그렇기 때문에 우리는 평생을 걸쳐 갶독성을 향상시키기 위 한 수련에 수련을 거듭해야 하는 것입니다.
  • 15. 목표: 코드를 읽는 사람이 코 드를 이해하는데 드는 시간과 노력을 최소화
  • 16. 눈에 보이는 것 소중한 것들은 눈에 보이지 않는 것 들이야 -어린 왕자 눈에 보이는 것도 소중하단다. -염산악
  • 17. 레이아웃: 기본 중에 기본 좋은 구현, 충분한 주석에도 레이아웃이 엉망이면 이해 불가 실수를 막기 위한 규칙들 자동화된 도구로 강제 가능
  • 18. 야심찬 도전과제 오른쪽의 코드를 우리 한 번 열심히 잘 이해하도록 해 봅시다. 하기 싫습니다. 하지 맙시다. google.j.b=(!!location.hash&&!!location.hash.match('[#&]((q|fp)=|tbs =rimg|tbs=simg|tbs=sbi)'))||(google.j.qbp==1);(function(){google.c={ c:{a:true,d:false,i:false,m:true,n:false}};google.sn='webhp';(functi on(){function f(a,b,c){g.push({o:a,v:b,w:c});a.addEventListener?a.addEventListener (b,c,!1):a.attachEvent&&a.attachEvent("on"+b,c)}function e(a,b,c){a.addEventListener?a.removeEventListener(b,c,!1):a.attachEv ent&&a.detachEvent("on"+b,c)}var g=[];google.timers={};google.startTick=function(a,b){var c=b?google.timers[b].t.start:google.time();google.timers[a]={t:{star t:c},e:{},it:{},m:{}};(c=window.performance)&&c.now&&(google.timers[ a].wsrt=Math.floor(c.now()))};google.tick=function(a,b,c){google.tim ers[a]||google.startTick(a);c=c||google.time();b instanceof Array||(b=[b]);for(var d=0;d<b.length;++d)google.timers[a].t[b[d]]=c};google.c.e=function(a ,b,c){google.timers[a].e[b]=c};google.bit=function(a,b){google.timer s[a]||google.startTick(a);var c=google.timers[a].it[b];c||(c=google.timers[a].it[b]=[]);var d=c.push({s:google.time()})-1;return function(){c[d]&&(c[d].e=google.time())}};google.c.b=function(a){var b=google.timers.load.m;b[a]&&google.ml(Error("a"),!1,{m:a});b[a]=!0} ;google.c.u=function(a){var b=google.timers.load.m;if(b[a]){b[a]=!1;for(a in b)if(b[a])return;google.csiReport()}else google.ml(Error("b"),!1,{m:a})};google.rll=function(a,b,c){var d=function(b){c(b);e(a,"load",d);e(a,"error",d)};f(a,"load",d);b&&f( a,"error",d)};google.ull=function(){for(var
  • 19. 실수 방지 빠르게 코드를 읽어봅시다. 다시 찬찬히 봅시다. 에너지가 더 소모됩니다. 사실 Submit은 한 번만 수행됩니 다. 에너지를 아낍시다. for (i = 0; i < size; ++i) if (a[i] > b[i]) { a[i] = b[i]; last = i; } Submit(a); return size;
  • 20. 자동화된 도구 가능하면 자동화된 도구로 최대한 간단한 포매팅 문제를 해결합시다 . for (i = 0; i < size; ++i) { if (a[i] > b[i]) { a[i] = b[i]; last = i; } } Submit(a); return size;
  • 21. 레이아웃: 합의가 필요 들여쓰기 규칙 줄을 나누는 규칙, 블럭을 나누는 규칙 순서: #include, 입출력 변수 등
  • 22. 덤: 읽기 쉬운 조건문 if (size < MAX_SIZE) { ... } if (MAX_SIZE > size) { ... } if (0 <= index && index < size) { ... } if (index >= 0 && index < size) { ... }
  • 23. 코딩 컨벤션 유명한 코딩 컨벤션들을 답습하기 언어에서 권장하는 컨벤션 익히기 주변의 코드들과 일관성 유지하기 (개성을 죽이는 교육)
  • 24. 문단 나누기 관련 있는 것들끼리 뭉치자. 줄을 맞추자. => 루틴이 하는 일을 하나로 하자. => 클래스에 연관있는 메소드들만 있게 하고 더 가까이 관련 있는 것들끼리 묶자. => 파일 이름 잘 짓고 끼리끼리 묶자.
  • 25. 클래스/파일/패키지 이름 클래스, 파일, 패키지 이름이 그냥 Util, utils.cc, helper.cc 같은 것이고 수많은 루 틴들이 들어가 있지는 않는가? What about foobar, then? 의미 있는 것들끼리 모으고 묶고 이름을 짓자. 이름이 지어지지 않으면 분리하자. 이 클래스가 무엇을 하는지, 너무 많은 것을 하지는 않는지 살피자.
  • 26. 시대와 환경의 변화에 따른 권장 사항의 변화 강제적인 헝가리안 표기법 타입 검사가 잘 되지 않고 IDE 지원이 열악하던 시절 좋은 표기법이었음. 하나의 종료점 수동으로 메모리 할당과 해제를 하던 시절에는 유용한 규칙이었음. GOTO exit; 스코프 등을 이용한 자원 획득/해제(RAII), with등으로 제약, try-finally로 해제 코드를 넣을 수 있
  • 27. Self Documenting 말하지 않아도 알아요. 그냥 바라보 면 - 모 마쉬멜로와 초콜릿 간식 적당히 눈치를 줘야 알아준다. - 염산악
  • 28. 코드가 말한다 아마추어의 코드에는 지나치게 주석이 많음. 줄 단위의 주석... 주석을 상세히 쓰라니 배운대로 열심히 i에 0을 넣는다고 주석 씀. 상세한 주석에도 불구하고 읽기는 매우 어려움. 읽어도 의도를 알기 어려움. (e.g. // Skip first row) // Remove first 6 characters query = request[6:] 프로의 코드에는 주석이 적당함. 문서화에 필요한 주석이 대부분. 변수나 함수 이 름이 스스로 설명함. 주석이 거의 없지만 읽기는 매우 쉬움. 코드로 알기 어려운 의도 등을 주석으로 밝힘. (e.g. // Skip header row) const string pathPrefix = "/api/?"; query = request[len(pathPrefix):]
  • 29. 함축과 압운: 언어 영역을 배운 이유 이름에 정보를 최대한 많이 넣어야 하지만 특히 긴 이름 사용이 반복되면 짜증난 다. 따라서 함축이 필요하다! 압운은 두운, 요운, 각운으로 나뉜다. 요~ 롸임 있는 코드. max, min, num, cnt, hex, unsafe, enc, has, is, ...
  • 30. 함수 이름 서브루틴은 호출하고 반환받을 수 있는 코드의 덩어리에 이름을 붙인 것. 이 이름을 잘 붙이는 것으로 독자들이 그 코드가 무엇을 하는지 더 잘 알 수 있다. 언어 영역/외국어 영역에서 배운 포괄하는 제목 짓기를 생각하자.
  • 31. 함축 프로그래머는 국어 실력도 좋아야 한다. GetResults(); // 결과들을 리턴한다.
  • 32. 함축 프로그래머는 국어 실력도 좋아야 한다. (아니, 영어...?) GetResults()? FetchResults()? ExtractResults()? GenerateResults()? ComputeResults()?
  • 33. 함축 아, 파라미터가 있었다.. GetResults({"min_score": 2.5}); // score가 2.5점 이상인 결과들을 리턴한다. => LoopOverResultsAndReturnOnlyThoseWithScoreHigherThan(2.5); ??? ??? 중산정왕의 N대손이자 황숙이자 예주목이고 좌장군인 유비 그래서 한마디로 그것이 무엇이옵니까?
  • 34. 함축 GetResults({"min_score": 2.5}); // score가 2.5점 이상인 결과들을 리턴한다. FilterResults({"min_score": 2.5}); // 2.5점 이상의 결과만 필터한다.
  • 35. 함축 GetResults({"min_score": 2.5}); // score가 2.5점 이상인 결과들을 리턴한다. FilterResults({"min_score": 2.5})? // 2.5점인 이상은 버리나요? 이하를 버리나요?
  • 36. 함축 GetResults({"min_score": 2.5}); // score가 2.5점 이상인 결과들을 리턴한다. FilterResults({"min_score": 2.5}); // 2.5점 이상의 결과만 버리나? 취하나? SelectResults({"min_score": 2.5}); ExcludeResults({"min_score": 2.5});
  • 37. 압운 매개변수 이름을 잘 짓자. @Nullable 같은 것이 지원되지 않으면 OrNull, NotNull 을 변수 이름이나 함수 이름에 붙이면 더 읽기 쉬워지는지 살펴보자. 문자열은 경우에 OrEmpty 같은 것이 붙으면 더 읽기 쉬워지는지 살펴보자. 시간에 단위 _sec, _ms, _us, _ns 같은 것이 붙으면 더 읽기 쉬워지는지 보자. 폰트 크기가 font_size인 것보다 _pt, _px가 붙으면 도움이 되는지 보자. font_size_px = PointToPixel(font_size_pt);
  • 38. 변수 이름을 지어주세요 // If needs recalculation if (xxx && yyy && zzz) { ... code ... } bool needs_recalc = xxx && yyy && zzz; if (needs_recalc) { ... code ... }
  • 39. 과정 설명 여러 스텝으로 나눌 수 있는 함수 이미 이 정도라도 나눠진 코드를 기반으로 작업할 수 있는 것을 감 사하게 생각하자. // Step 1. Read input. ... code .. // Step 2. Generate results. ... code ... ... code ... // Step 3. Update results. ... code ... ... code ... // Step 4. Write output. ... code ...
  • 40. 함수 이름으로 이름이 곧 어떤 순서로 무엇을 하 는지 설명한다. ReadInput(); GenerateResults(); UpdateResults(); WriteOutput();
  • 41. 이미 좋지 않았나요? => 협업으로 변화되는 코드의 미래를 봅시다. 왜 굳이?
  • 42. 흐름의 단순화 들어올 땐 마음대로지만 나갈 땐 아 니란다~ - ??? 마음대로 들어오시면 곤란합니다. 그리고 볼 일 다 보셨으면 어서 나가 주시죠. - 염산악
  • 43. 일찍 리턴하기 패턴 무엇을 리턴해야 할 지 아는 순간 바로 리턴해 버림. (multiple exit, return early) 중요한 코드 부분의 양이 줄어들고 예외 상황이 줄어들어 흐름이 단순해 짐. 기억해야 할 변수도 줄어들게 되어 뇌의 부담을 줄여줌.
  • 44. 루프에서 일찍 리턴하기 흐름이 단순해지고 리턴 값이 분명해진다.
  • 45. One Exit Result* result = nullptr; for (int i = 0; i < n; ++i) { Message msg = getMessage(); if (!msg.empty()) { ... actual code ... result = msg.AsResult(); break; } } return result;
  • 46. Return Early for (int i = 0; i < n; ++i) { Message msg = getMessage(); if (!msg.empty()) { ... actual code ... return msg.AsResult(); } } return nullptr;
  • 47. 루프와 함수 분리 기억해야 할 변수도 줄어든다. 루프가 무엇을 하는지가 분명해진다. (함수 이름을 잘 지으면...)
  • 48. 루프와 함수 분리 루프와 상태 변수를 이용해야 하 는 경우가 있다면 함수를 분리하 면 더 읽기 쉬워지는지 살펴보자. 이번에도 이미 이런 코드를 갖고 작업할 수 있음을 감사하게 생각 하자. bool found = false; for (const auto& item : items) { if (...) { found = true; break; } } if (found) { ... }
  • 49. 루프와 함수 분리 return을 이용하면 코드가 간단함. 물론 functional한 함수들을 이용 하는 방법도 있겠음. bool ContainsItem(...) { for (const auto& item : items) { if (...) { return true; } } return false; } if (ContainsItem(...)) { ... }
  • 50. GOTO와 break label이 사용되는 경우 여러 루프를 빠져나와야 하는 경우 break label을 쓰거나 조건 변수를 써야 하는 경우 함수를 분리하면 (혹은 클로저를 쓰면) 더 읽기 쉬워지는지 살펴보자. return은 함수 안에 있는 모든 루프를 한 번에 빠져나오므로...
  • 51. 루프와 함수 분리 goto를 쓰거나 break label을 써야 한다. 코드가 길어지면 흐름이 더욱 복 잡해진다. bool found = false; for (const auto& item : items) { for (const auto& attr: item.attrs()) { if (...) { found = true; ... (1) code ... goto outer; // break outer; } } } outer: if (found) { ... (1) code ... } ... (2) more code ...
  • 52. 루프와 함수 분리 return을 이용하면 코드가 간단함. 물론 functional한 함수들을 이용 하는 방법도 있음. bool ContainsItem(...) { for (const auto& item : items) { for (const auto& attr : item.attrs()) { if (...) { return true; } } } return false; } if (ContainsItem(...)) { ... (1) code ... } ... (2) more code ...
  • 53. 예외 상황 쳐 내기 예외 상황을 안고 코드를 읽을 때 심란해진다. 일찍 쳐 내면 이후의 코드가 간단해지는 경우가 많다.
  • 54. One Exit Result* result = nullptr; if (condition) { Message msg = getMessage(); if (!msg.empty()) { ... some code ... ... some code ... result = msg.AsResult(); } } return result;
  • 55. Return Early if (!condition) { return nullptr; } Message msg = getMessage(); if (msg.empty()) { return nullptr; } ... some code ... ... some code ... return msg.AsResult();
  • 56. Return Early 이 부분이 초기 조건 검사 부분이 된다. if (!condition) { return nullptr; } Message msg = getMessage(); if (msg.empty()) { return nullptr; } ... some code ... ... some code ... return msg.AsResult();
  • 57. 예외 조건 검사 조건없이 묻지도 따지지도 않고... - 보험/대부 업계 저기 저 그렇게 쉬운 사람 아니거든 요? - 염산악
  • 58. Design by Contract (DbC) 추상 자료형에 다음을 덧붙임 precondition invariant postcondition
  • 59. Design by Contract Eiffel, D, Ada 등의 언어에서 지원함 지원하지 않는 언어도 써드 파티로 지원하거나 assert를 지 원하는 경우가 많음
  • 60. Defensive Programming vs DbC 방어 운전에서 따온 것으로, "입력이 무엇이 들어올지 알 수 없으니, 입력을 믿지 말고 조심해서 어떻게든 잘 가 봅 시다." 와 같은 자세 가능하면 죽지 않고 감. 어떤 입력이 들어와야 하는지 상세히 명시하고 잘못된 입력이 들어오면, "계 약 위반이오! 그러지 않기로 했잖소!" 하면서 죽어버리는 자세 프로덕션 코드에서는 성능을 위하여 검사하지 않기 때문에 위반 시 정의되 지 않은 행동을 하게 됨
  • 61. Precondition 조건이 만족하지 않을 때, 로그를 남기고 에러를 리턴하거나, 죽어 버리거나 할 수 있다. 디버그 모드 인지 릴리즈 모드인지에 따라 달 라질 수 있다. if (!Precondition(src)) { // ... } for (i = 0; i < size_; ++i) { if (min_[i] > src[i]) { min_[i] = src[i]; last = i; } } return last;
  • 62. Postcondition if (!Precondition(src)) { // ... } last = Private(src); if (!Postcondition(last)) { // ... } return last;
  • 63. 주석 백일장 승고월하문이 좋을지 승퇴월하문이 좋을지 고민하는 중이었습니다. - 당나라 시인 가도 둘 중에 가능하면 오해 없는 쪽으로 해 주시고 이유를 써 주세요. 아니면 TODO로 달아주세요. - 염산악
  • 64. 퇴고 주석을 열심히 퇴고 하면 교통사고 가해자라도 용서받을 수 있다.
  • 65. 시작이 반이다 일단 먼저 써 놓고 본다. // 헉! 여기 잘못하면 뻗음. 침착하게 그게 무슨 뜻인지를 다시 고쳐쓴다. 영어가 불편하면 일단 머리에 있는 걸 잊기 전에 한국어라도 쓰고 침착하게 고쳐 쓴다.
  • 66. 오해하지 말고 들어... 오해할만한 문장으로 쓰지 않는다. 내 주석을 어떻게 오해할 수 있을지 삐딱하게 바라보자. 다시 고쳐 써서 더 좋은 문장으로 만든다.
  • 68. 보고 드리겠습니다! 코드를 하나 하나 설명하지 않는다. 이런 걸로 영어 공부 하고 싶어도 참는다.
  • 69. 보고 드리겠습니다! 코드를 하나 하나 설명하지 않는다. 이런 걸로 영어 공부 하고 싶어도 참는다. 필요 없는 주석은 지운다. 영어로 써 놓은 주석이 아까워도 할 수 없다.
  • 70. 보고 드리겠습니다! 코드를 하나 하나 설명하지 않는다. 이런 걸로 영어 공부 하고 싶어도 참는다. 필요 없는 주석은 지운다. 영어로 써 놓은 주석이 아까워도 할 수 없다. 슬라이드에 필요 없는 내용들도 좀 지우시지?
  • 71. 상세한 것만이 능사가 아니다 길게 쓰는 것보다 단어 하나의 파워가 더 강력할 수 있다. // 이 함수들은 따로 하는 일에 대한 구현이 들어 있는 것이 아니라 좀 더 쓰기 어 려운 복잡한 함수를 호출하기 위한 준비 작업을 하고, 매개 변수의 순서도 우리 프로젝트 표준에 맞게 바꾸어서 구현이 들어 있는 함수들을 호출해 준다.
  • 72. 상세한 것만이 능사가 아니다 길게 쓰는 것보다 단어 하나의 파워가 더 강력할 수 있다. // 이 함수들은 따로 하는 일에 대한 구현이 들어 있는 것이 아니라 좀 더 쓰기 어 려운 복잡한 함수를 호출하기 위한 준비 작업을 하고, 매개 변수의 순서도 우리 프로젝트 표준에 맞게 바꾸어서 구현이 들어 있는 함수들을 호출해 준다. // 이 함수들은 래퍼들이다.
  • 73. 어떤 것을 쓸까요? 왜 이렇게 했는가? TODO, FIXME, ... 버그 같아 보이지만 버그가 아님, 핵 구현, 속도 때문에 정확도를 조금 버림, 이 방 법보다 그 방법이 더 빠를 거라 생각하겠지만 이게 더 빨랐음. 커맨드라인 사용 예제
  • 74. 정리 레이아웃 및 형식화는 자동화된 도구와 열정으로. 그리고 일관성을 유지하자 의미 있는 것들끼리 모아서 추상화하자 변수/함수 이름만 잘 붙여도 갶독성이 증가한다 흐름을 단순화하자 주석을 잘 쓰고 퇴고하자.
  • 75. Final point 갶독성을 좋게 하기 위한 노력을 계속 하자.