[들어가기 전에]
길고 길었던 황금 같은 추석 연휴가 이제 끝이 나네요...!
올해 초부터 나름 쉼 없이(?) 달려와 오랫동안 휴식을 취해서 좋았는데 벌써 연휴가 끝나서 너무너무 아쉽지가!! 않네요!!! 😂
어쨌든,,, 오늘은 클린 코드(Clean Code)에 대해 알아볼 겁니다! 개발자라면 클린 코드에 대해 들어봤을 거라고 의심치 않는데요!
저는 대학생 시절 클린 코드를 접했지만 크게 중요하지 않다고 생각했었습니다.
하지만 요즘 대규모 팀 단위로 프로젝트를 하다 보니 다른 팀원의 코드를 보게 되는 경우도 있고 코드 리뷰와 같이 제 코드를 다른 팀원이 보는 경우도 있는데.. 부끄럽지 않은 코드가 되기 위해서는 클린 코드가 중요하다고 생각하여 정리하려고 합니다!
[나쁜 코드]
먼저 나쁜 코드가 무엇인지 알아야 좋은 코드를 알기 쉽겠죠?!
나쁜 코드에 대표적인 예가 스파게티 코드입니다.
스파게티 코드는 구조가 복잡하고 이해하기 어려운 코드를 의미합니다. 말 그대로 스파게티처럼 생긴 코드인 거죠. 정상적으로 작동은 하지만 코드가 무엇을 의미하는지 파악하기 힘들다는 것이 문제입니다.
또한 함수, 변수, 조건문 등이 복잡하게 걸쳐있기 때문에 유지보수가 힘들고 결함이 발생할 확률이 높습니다.
let a = 10;
let b = 20;
function foo() {
if (a > b) {
bar();
} else {
baz();
}
}
function bar() {
a = a * 2;
b = b / 2;
}
function baz() {
a = a / 2;
b = b * 2;
}
예시를 보면 변수와 함수가 어떤 역할을 하는지 한눈에 확인할 수 있을까요? 만약 그렇다고 해도, 시간이 지나고 나서 다시 볼 때 바로 이해할 수 있을까요? 그건 힘들 것입니다.
따라서 스파게티 코드는 클린 코드 원칙에 따라 리팩토링해야 합니다.
[클린 코드]
클린 코드는 이해하기 쉽고, 재사용 가능하며, 유지보수에 용이한 방식으로 작성된 코드를 의미합니다.
그 원칙에 대해서는 몇 가지가 있는데요. 제가 느끼기에 알아두면 좋은 원칙에 대해서 톺아보겠습니다!
[의미 있는 이름을 사용해라]
변수 또는 함수가 존재하는 의미를 나타낼 수 있어야 하고, 어떤 역할을 하는지를 한눈에 알 수 있어야 합니다. 굳이 주석이 없어도 말이죠.
// Bad
let a = 10;
function foo(b) {
return b * 2;
}
// Good
let age = 10;
function calculateDouble(number) {
return number * 2;
}
Bad 표현과 다르게 Good 표현에서는 변수와 함수가 왜 존재하고 하는 역할이 무엇인지 분명하게 알 수 있겠죠? 굳이 주석이 없어도요!
또한, const 키워드를 사용하여 상수를 정의하는 것이 중요합니다.
// Bad
if (user.role === 1) { ... }
// Good
const ADMIN_ROLE = 1;
if (user.role === ADMIN_ROLE) { ... }
상수를 적절하게 사용하면 그 값이 어떤 의미를 가지고 있는지 쉽게 알 수 있습니다.
[함수를 최대한 작게 만들어라]
어떤 프로그램이든 함수는 가장 기본적인 단위입니다.
객체 지향 프로그래밍을 접하셨다면 SOLID 중 하나인 단일 책임 원칙(Single Resopnsibility Principle)에 대해 들어보셨을 겁니다!
"하나의 클래스 혹은 모듈은 하나의 책임만을 가져야 한다."라는 의미이죠.
즉, 함수는 하나의 일만 하면 됩니다. 그 한 가지 일만 하면 되고 그 한 가지 일만 잘하면 되는 것이죠.
// Bad: 함수가 두 가지 역할을 함
function createAndPrintReport(data) {
const report = createReport(data);
console.log(report);
}
// Good: 각 함수가 하나의 역할만 수행
function createReport(data) {
// report 생성 로직
}
function printReport(report) {
console.log(report);
}
[무분별한 주석을 지양해라]
주석은 소스 코드를 더 쉽게 이해하기 위해 사용하는 목적으로 쓰입니다.
이러한 이유로 저는 주석을 많이 사용하는 것이 무조건 좋고, 주석을 많이 사용하는 것이 좋은 개발자가 되는 거다!라고 생각을 했었죠,,,
하지만, 주석은 나쁜 코드를 보완하지 못합니다. 주석으로 코드를 표현하는 게 아니라 코드 자체로 코드의 의도를 표현하는 것이 중요한 거죠!
// Bad
function calculate(a, b) {
// 두 수를 더합니다.
return a + b;
}
// Good
function add(a, b) {
return a + b;
}
[에러를 핸들링해라]
예외 상황을 미리 대비하여 적절한 예외 처리를 구현해야 합니다. 이는 프로그램의 안정성과 에러 처리에 도움이 됩니다.
// Bad (예외 처리 부재)
function divide(a, b) {
return a / b; // b가 0인 경우 에러 발생 가능
}
// Good (예외 처리 추가)
function divide(a, b) {
if (b === 0) {
throw new Error('Divide by zero error');
}
return a / b;
}
[중첩 콜백의 사용을 최소화해라]
비동기 작업을 연속적으로 수행할 때 중첩 콜백이 발생하고 콜백 지옥(Callback Hell)에 빠지게 되죠.
코드의 가독성이 떨어지고 복잡도가 높아지기 때문에 중첩 콜백을 피해야 합니다.
getData(function(a) {
getMoreData(a, function(b) {
getMoreData(b, function(c) {
getMoreData(c, function(d) {
// do something with 'd'
});
});
});
});
이렇게 코드를 작성한다면 중첩이 깊어져서 코드를 이해하기 쉽지 않습니다. 이를 해결하기 위한 방법으로 ES6부터 도입된 Promise를 사용할 수 있습니다.
getData()
.then((a) => getMoreData(a))
.then((b) => getMoreData(b))
.then((c) => getMoreData(c))
.then((d) => {
// do something with 'd'
})
.catch((error) => {
// handle error
});
또한 ES8부터 도입된 async/await를 사용하여 then을 사용하지 않고 더 깔끔하게 비동기 처리할 수 있습니다.
async function processData() {
try {
const a = await getData();
const b = await getMoreData(a);
const c = await getMoreData(b);
const d = await getMoreData(c);
// do something with 'd'
} catch (error) {
// handle error
}
}
processData();
'JavaScript' 카테고리의 다른 글
[JavaScript] toLocaleString() 메서드로 숫자, 날짜 형식 변환하기 (0) | 2024.05.14 |
---|---|
[JavaScript] 프로토타입 기반 프로그래밍 이해하기 (0) | 2024.03.04 |