August 17, 2024
작년 상반기 CS의 밑바닥을 다지고 싶어서 Nand2Tetris 프로젝트와 몇몇 CS도서를 학습했고, 하반기에는 ‘대용량 트래픽 문제해결 경험’을 얻고 싶어서 이를 연습하는 사이드 프로젝트를 구상했고, 성공적으로 마무리했다.
올해 상반기 때는 ‘코드 퀄리티’를 올리고 싶었고, 앞으로 이야기할 ‘코딩철학 만들기’를 통하여 실무 프로젝트에서 월평균 52개의 코드 리뷰
를 작성하며, 굉장히 적극적인 자세로 ‘코드 퀄리티’에 대해 고민하게 되었다. 어떤 코드가 좋은 코드인지 명확히 설명하지 못했던 내가 어떻게 가장 활발한 코드리뷰어가 되었을까?
‘하나의 리뷰 쓰레드’를 생성한 경우만 카운팅에 포함했다. 댓글은 포함하지 않았다. 물런 ‘숫자’자체가 의미가 있는것은 아니다. 하지만 코드리뷰에서 매우 적극적인 자세로 임할 수 있게 되었다는 점에 큰 의미가 있다.
올해 2월경, 평소 즐겨보던 개발 유튜브 채널 에디의 개발 여행기에서 고민상담 신청을 받고 있었고, 나는 바로 신청했다. 무려 한시간 반동안 많은 이야기를 들었지만, 그 중 이 글에서 다루는 것은 코딩 철학
에 대한 이야기다.
코딩 철학이란 코드를 쓰는 본인만의 원칙을 의미한다.
위 코드 중 어떤 코드가 잘 짜여진 코드일까? 당연히 정답은 없다. 따라서, 우리는 자신만의 코딩 철학
을 먼저 세우고 그 철학에 맞는 ‘스타일’을 선택해야 한다.
고민 상담 中 작성한 메모
위 상담 후 얼마 지나지 않아, ‘프로그래머의 뇌’라는 책을 읽었고, 아래와 같은 코딩 철학을 만들었다.
아무리 작성할때 불편하더라도, 읽기 좋은 코드를 우선한다.
책 내용을 소개하는 글은 아니기에, 근거를 간단하게 언급하고 넘어가겠다.
Q. 어떤 코드가 읽기 힘든 코드일까?
인지부하
를 줄여야 한다Q. 왜 읽기 좋은 코드를 우선해야할까?
Q. 빠른 개발을 해야할때는?
이제 철학을 만들었으니 ‘실패와 극복’을 반복 연습하면 된다. 운이 좋게도 당시 실무에서 존재하던 레거시 입출금 시스템
을 신규 입출금 시스템
으로 마이그레이션 하는 과정에 있었고, 매우 많은 실패와 극복
연습을 할 수 있었다. 2년 가까이 직접 메인 유지보수 하던 시스템을 다시 만들어보는 경험은 너무 만족스러웠다.
레거시 유지보수는 어떤 능력을 향상시킬까? ‘운영’ 역량을 향상시키며, 개발자에게 유연한 구조와 코드퀄리티를 고민하게 만든다. (즉 신규개발과 유지보수는 필요한 역량이 다르다)
이제 몇가지 사례를 소개하겠다. 아래 사례들은 모두 실무때 남긴 코드리뷰이다. (가독성을 위해 몇몇 사례는 리뷰 스크린샷을 생략했다) 또한 대부분 ‘프로그래머의 뇌’에 나오는 생각들이다.
당연하게도, 옳은 코딩 스타일은 없으며 각각 장단이 존재한다. 또한 맥락 전체를 제공할 수도 없기 때문에 한계점이 존재하지만 ‘이런 관점도 존재하는구나’정도로 받아들여주면 좋겠다. 🙇♂️
생각이나 결론이 부족하더라도 넓은 이해 부탁한다. 🙇♂️
코딩 철학에 대한 리뷰는 아니지만…
if 조건
을 아주 조금만 변경하면 되는데, 관련 테스트 까지 함께 수정하다 보면 시간을 몇 배로 소모
생각하는 방식
아래 두 예시 중 어떤 코드가 더 읽기 편할까?
function calculateFee(user) {
if (user.age > 20) {
if (user.city === 'Seoul') {
// some code
} else if (user.city === 'Busan') {
// some code
} else {
// some code
}
} else {
// some code
}
}
function calculateFee(user) {
if (user.age <= 20) {
// some code
return;
}
if (user.city === 'Seoul') {
// some code
return;
}
if (user.city === 'Busan') {
// some code
return;
}
// some code
}
나는 2번째 함수(if-return 패턴)가 더 읽기 편하다고 생각한다.
그 이유는 if-else 패턴은 ”하나의 흐름이 너무 길어 읽다가 이전 내용을 잊어버리고, 다시 반복해서 앞 내용을 살펴보는 것“에 대한 가장 대표적 사례라고 생각한다. (else를 기억하며 코드를 읽어야 한다)
그러한 관점으로 개선 방향을 리뷰로 남겼다
function execute() {
if (condition) {
return false;
}
return true
}
execute()
의미없이 Boolean을 리턴하는것은 ‘최소 구현’이 아니다. 다른 개발자로 하여금 “상위에서 true나 false로 어떠한 판단을 하는 로직이 있구나” 등의 오해를 유발한다
즉, Input/Output 모두 필요한만큼만 최소로 구현해야 한다.
// a
await sendSuccessDepositMsg(
accountId,
coin,
amount,
)
// b
await sendMsg(
define.SEND_DEPOSIT,
define.STATUS.SUCCESS,
accountId,
coin,
amount,
)
A는 내부 구현을 ‘숨기고’ 외부에 필요한 최소한의 Input만을 기능 단위로 구분했다.
하지만 B는 이를 외부로 드러냈다. B는 의존성에 따른 ‘수정’이 상위로 전파될 가능성이 큰 코딩 방법이다.
실제로 A,B 방법 모두 레거시 프로젝트에 공존하던 스타일이였고, 레거시 알림 시스템 통합작업
이 발생하며, 전체를 수정할 일이 생겼다. 위와 같이 A 스타일의 코딩 방식은 내부 수정을 통해 어떠한 Input/Output 변화가 없었다. 즉 상위로 ‘코드 수정’이 전파되지 않았다.
당연하게도 B는 상위레이어로 코드 수정이 전파되었다.
예를들면 이러한 코드가 존재했다.
for (;;;) {
// select and validate...
// insert => update db...
// etc...
}
매우 간단한 예시를 사용했지만, 많은 비지니스 로직이 섞여있었다. (한마디로 코드 블록이 상당히 길다. 매일 유지보수해도, 한눈에 이해할 수 없다.)
어떤 코드가 더 읽기 편할까?
const list = [...]
const target = 7
const resultIndex = binarySearch(list, target)
const list = [...]
const target = 7
let start = 0;
let end = list.length-1
let mid
while (start <= end) {
mid = parseInt((start + end) / 2)
if (target === arr[mid]) {
return mid;
} else {
if(target < arr[mid]) {
end = mid - 1
} else {
start = mid + 1
}
}
}
const resultIndex = mid ? mid : -1
첫번째 코드가 더 읽기 편하다고 생각한다.
그 이유는 간단하다, 실제 기능은 같음에도, 우리는 binarySearch
라는 추상화된 함수이름
만을 보고도 해당 코드가 무슨 일을 하는지 추측
할 수 있다. 반면 2번째 코드는 해당 코드를 직접 읽고, 흐름 파악할때까지의 시간이 소요된다.
Q. 만약 binarySearch가 무엇인지 모르는 사람은 코드 깊이
가 늘어났기 때문에 오히려 2번 코드가 더 좋은거 아닐까요?
반복 강조하지만, 이러한 코딩 스타일은 각가의 장단점이 있어서 쉽게 어떤 스타일이 더 좋다고 결론 내리기 힘들다. 그러나, 하나의 ‘코딩 철학’을 통해 우선순위를 매겨 가치판단할 수 있다는 점은 굉장한 것 같다. 애매하게만 느껴지고, ‘막연히 이게 좋지 않을까?’ 라고 생각해오던 내가 코딩 철학을 통해 매우 적극적인 코드 리뷰가 가능해졌다.
지금 글에서 설명한 방법이나 결론은 당연히 언제나 좋은 것도 아니며, 심지어 대부분의 경우 좋지 않을 수도 있다. 또한 내가 최고의 코딩 스타일을 찾았다는 것도 아니다. 다만 더 나은 방법을 찾고, 내 것으로 보다 빠르게 흡수할 수 있게 되었다고 생각한다.
현재 내 다음 목표는 최고의 개발자들의 코드를 보고 트랜드를 읽는 방법 - 잡상훈(오픈소스 코드 읽기)에 있다.
에러가 생기거나 문서에 나오지 않는 기능을 개발해야 할때, 위와 같이 SDK의 버전 태그를 선택하고 해당 버전 소스코드에 필요한 부분을 중심으로 콜스택을 따라 분석하여 문제를 해결한다.
그러나 아직은 ‘익숙한 언어’의 한계와 ‘문제해결 중심 리딩’이라는 한계점이 있다고 생각한다. 올해 오픈소스의 기여하는 도전도 성공했지만, 아직은 그러한 한계점을 깨지 못한것 같다. 앞으로 더욱 더 노력해서 장기적으로 더 좋은 퍼포먼스를 보이고 싶다.