GPT로 썸네일 한번 만들어봤어요🤣
100% 주관적인 이야기로 가득합니다.
프로젝트를 구성하며 개발을 시작할 때는 항상 이상적인 목표를 생각하며 전에 힘들었던/고생했던 기억을 되돌아보며 이번에는 이쁜 코드, 좋은 코드를 작성하려고 하며 시작한다. 아키텍처를 설계하고 공통화 모듈을 개발하며 어떻게 재사용성을 높일지, 가독성은 어떻게 챙겨야 할지, 폴더 구조는 어떻게 가져가야 할지와 같은 고려를 하며 개발하곤 한다.
그러나 이상적인 목표는 말 그대로 이상적인 목표일 뿐 100% 달성하는 것은 실무에서 지키기 어려웠(었)다. 그래서 그 과정 속에서 최선을 선택해 개발을 한다. (그런데도 불구하고 지나고 돌이켜보면 최선이 아닐때가 많았다.)
주저리주저리 써놨지만, 오늘 내가 할 얘기는 문서화와 주석에 대한 것에 초점을 맞춰 작성해 보려고 한다.
클린 코드, 클린 아키텍처에서는 주석에 대한 이야기를 하곤 한다.
- "주석은 나쁜 코드를 보완하지 못한다"
- "코드로 의도를 표현하라!"
- "주석으로 처리한 코드는 쓸모없는 코드가 쌓이게 된다"
틀린 말이 전혀 아니다. 내가 하려는 이야기는 위의 말이 틀렸다는 것이 아닌 어떻게 주석을 처리하면 실무에서 현실적으로 처리할 수 있을지에 대한 이야기를 하고 싶다.
먼저 우리는 코드를 나쁜 코드를 의도하고 만들지 않는다. 그런데 개발하다 보니 나쁜 코드로 변질이 되었거나 더 좋은 방식을 발견했거나 할 수 있다. 그러면 이럴 때 달아놓은 주석은 누구 탓으로 봐야 할까? 코드를 짠 사람?
주석을 달아놓은 사람?
코드 리뷰를 제대로 하지 않은 사람?
결국 코드는 레거시로 변하기 마련이고 이를 계속해서 유지 보수해야 하는 것 또한 개발자의 몫이기에 누구의 탓도 할 수 없다.
다음으로 코드로 의도를 표현해야 한다는 건 당연히 하고 싶은 게 개발자의 마음이다.
그런데 코드로 의도를 표현하는 것은 제한적이다. 왜냐하면 표현의 영역이 개발 언어로 한정되어 있기 때문이다(나는 영어를 잘 못하는 한국인🇰🇷). 코드로 최대한 의도를 표현하려고 하면 그만큼 코드가 장황해질 수 있기도 하고 표현이 불가능할 수도 있다. 또는 도메인 자체가 어려워 변수명 자체가 복잡하거나 변수명이 엄청나게 길어져서 코드로 의도를 표현해도 그것을 이해하는 것조차 어려울 수 있는 것이다.
어려운 변수명 예시)
// 어려워 보이는 용어로 가득한 코드의 예시)
const [liquidation, setLiquidation] = useState();
if (isloanTransaction) {
const TRANSACTION_AMOUNT = 100_000_000;
// ~~~
}
마지막으로 주석으로 처리한 코드에 대해서는 일부로 코드를 더러워지라고 남기진 않았을 것이다. 어떠한 기획 요구사항의 변경, 다음 배포를 위한 임시 주석 처리 등등 어떠한 이유가 있어 주석이 생겼을 가능성도 있다.
위의 예시 말고도 클린 코드에서는 좋은 주석과 나쁜 주석에 대한 설명을 자세히 써놨으니 읽어보는 것도 좋다.
과거의 나는 주석을 정말 최대한 달지 않으려했고, 최대한 코드로만 파악할 수 있게 하려는 편이었다. 최근에는 그렇게 하는 게 좋은 효과만 내는 게 아니라고 생각이 바뀌었다.
정말 쉬운 코드가 아니라면 다른 사람이 작성한 코드를 파악하는 것에는 리소스가 든다.
그 리소스를 줄여주는 게 주석이라고 생각이 된다. 그렇다고 모든 부분에 주석을 하나씩 다 달아주라는 게 아니다.
주석을 달았을 때 도움이 되는 곳들
나는 주석을 남길 때 TODO
, FIXME
, NOTE
를 적극 사용하는 것을 추천한다.
해당 태그를 통해서 카테고리화를 하여 주석의 기준이 명확해지곤 한다.
아래 사진은 react 레포지토리에 주석을 검색한 것이다.
주석을 달았는데 예전 스펙이 담겨있다면 오히려 코드와의 불일치가 발생하여 해를 끼친다. 주석으로 인해 파악한 코드가 의도치 않은 방식으로 작성이 되어있다면 어떤 게 더 최신화된 정보인지 파악하기 어려워진다.
그렇기에 주석에는 쉽게 레거시화되지 않을 정보로 작성되어야 한다. 그런데도 바뀔 정보가 들어갈 수 있다. 위에서 말했듯이 그럴 때는 FIXME
, TODO
와 같은 태그를 활용해 다시 수정할 수 있도록 티를 내야 한다.
유틸, 헬퍼 함수를 작성할 때 주석으로 parameter의 용도, return 값, 해당 함수의 역할 등을 작성하곤 한다. 이렇게 작성된 주석들은 함수를 파악하는데 큰 도움을 준다.
// utils.ts
export const utils = {
/**
* Generates a random integer between min and max (inclusive).
* @param min - Minimum value (inclusive).
* @param max - Maximum value (inclusive).
* @returns A random integer.
*/
getRandomInt(min: number, max: number): number {
return Math.floor(Math.random() * (max - min + 1)) + min;
},
/**
* Capitalizes the first letter of a string.
* @param str - The string to capitalize.
* @returns A string with the first letter capitalized.
*/
capitalize(str: string): string {
if (!str) return '';
return str.charAt(0).toUpperCase() + str.slice(1);
},
/**
* Checks if a given value is an object.
* @param value - The value to check.
* @returns True if the value is an object, false otherwise.
*/
isObject(value: unknown): value is Record<string, unknown> {
return value !== null && typeof value === 'object' && !Array.isArray(value);
},
/**
* Delays execution for a given number of milliseconds.
* @param ms - The delay in milliseconds.
* @returns A promise that resolves after the delay.
*/
delay(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
};
이러한 코드는 어디서나 쓰이지만 어디서 쓰일지 모르기에 주석은 친절하면 할수록 좋다. 그런데 특정 컴포넌트 내부에서만 쓰인다거나 주석이 없어도 파악이 쉽다면 주석을 다는 것조차 리소스가 들고 개발하는 데 괴롭히는 요소로 자리 잡게 된다. 즉, 불필요한 과정이 반복되게 되면서 읽는 사람, 쓰는 사람 모두에게 좋은 영향을 끼치지 못한다.
무엇보다 말로 설명하는 것 대신 변수명(한글 변수명), 가독성을 챙긴 코드를 먼저 생각하는 것이 선행되어야 한다.
복잡도가 올라가면 똑똑하지 않은 사람(=나)은 이해하기 어렵다. 그러면 어딘가에 설명이 필요한데 주석으로는 모든 내용을 포함할 수 없어 다른 외부 공간에 정리가 필요한 순간이 찾아온다. 이때는 주석보다는 문서화를 통해 내용의 설명을 보충하여 해결할 수 있다.
문서화라는 단어에 대해서 잘 쓰인 문서 또는 깊은 내용이 담겨있는 글, 어려운 내용으로 구성된 자세한 설명들이 포함되어 있어야 한다고 생각할 수 있다. 나는 그것보다 문서화의 목적을 생각해 보면 기록하는 데 의의를 둬야 한다고 생각한다. 결국 문서화를 하게 되면 시간이 흘러도 계속해서 공유가 되고 특히나 설계에 관한 내용은 혼자서 개발하는 것보다 여려 명의 의견이 합쳐져야 단단한 설계가 될 것이다. 이를 위해서는 꼭 필요한 과정 중 하나이다.
그렇지만 문서화를 통해 한번 기록을 하게 되면 코드와 문서가 Sync가 되지 않는 상황이 벌어지기 쉬운 구조이다. 코드는 계속해서 변하지만, 문서는 그에 맞춰서 업데이트를 하지 못하는 상황이 벌어질 수 있는 것이다. 그렇기에 문서화는 일회성이 아닌 코드와 같이 함께 가야 할 요소로 꾸준히 관리되어야 한다.
이를 지키기 위한 좋은 방법 중 하나는 모두가 같이 해당 문서를 같이 살펴보고 모두가 코드의 내용을 파악할 수 있게 하고 누구나 수정 및 추가가 가능하도록 환경을 갖추면 된다.
시간이 지나 코드의 수정이 이뤄진 이후에도 수정을 한 사람이 직접 문서를 수정하고 그 문서가 계속해서 최신화가 될 수 있도록 하는 것이다. 수정한 내용에 대해서도 다른 팀원들도 인지하도록 선순환고리을 만들면 된다.
문서화, 주석은 결국에 다같이 잘되려고 하는 일을 위한 과정이라고 생각한다.
일을 하다보면 혼자서 하지 않고 다른 사람과 협업이 필연적이다.
프론트엔드 개발자로 일하다 보면 흔히 접하는 예시 중 하나가 API 문서라고 생각이 된다. API 문서는 백엔드 개발자와 소통하기 위한 하나의 공통된 언어이고, 그 공통된 언어로 서로 대화가 통하는 것이다.
근데 그 API 문서가 제대로 작성이 되어 있지 않고, 최신화되지 않아 오래되었으면 대화가 잘 안 통하는 것은 당연하다.
이 외에도 라이브러리를 사용할 때는 항상 공식 문서 또는 README를 통해 서로 소통을 한다.
라이브러리에서 공식 문서나 리드미를 제공하지 않는다고 생각하면 너무 끔찍하다. 코드를 하나하나 찾아보며 한다면 개발자들은 고통받으면서 사용하거나 차라리 라이브러리를 직접 만들고 말지 하는 생각을 할 수도 있다.
마지막으로 이런 블로그 글을 쓰게 된 이유는 과거의 나를 반성하기 위해서 쓴 글이기도 하다.
내 코드를 남들이 잘 보지 않았던 환경에서는 그렇게 중요하게 생각하지 않았던 부분이었다.
나만 알고 있어도 코드는 돌아갔었고, 내 머릿속에 이미 코드에 대한 모든 히스토리가 다 있으니 크게 중요하게 생각하지 않았던 것 같다.
[trouble-shooting은 언제나 옳지]
회사에 있었던 에피소드 중 하나를 풀면 과거(N년 전)에 발생했던 이슈와 비슷하게 최근에도 비슷한 이슈가 나왔었다. 그런데 그 과거 이슈를 해결하기 위해 작성했던 코드에 매우 자세히 주석이 쓰여있어서 그 코드를 처음 본 사람도 쉽게 파악할 수 있었다. 이걸 보면서 중요하게 생각하게 된 계기로 남게 되었던 것 같다.
[어려운 코드는 언제나 어려워]
그러나 이와는 반대로 정말 복잡한 로직들이 가득했는데 주석은커녕 문서도 찾을 수 없었다. 코드 내부에는 알 수 없는 방어 로직들에 대해서도 따로 설명이 없어서 그 코드들을 지우지도 못하고, 그렇다고 그냥 그대로 사용할 수도 없었던 경험이 있다.
이 글도 어떻게 보면 문서화를 해놓은 글이라고 볼 수 있지 않을까?(최신화는 안.함.) 🥹