Javascript를 이용하여 개발을 하다보면 if, else문이 많아짐에 따라 가독성을 상당히 해치는 코드를 종종 볼 수 있습니다.
특히나 프론트 UI에서 조건에 따라 렌더링 및 데이터를 보여주는 로직이 존재하기때문에 이런 경우가 더 많은 것 같습니다.
저는 backends 언어로 Python을 사용하고 있는데 이때도 마찬가지로 if로 인한 indent가 많아지는 코드를 그다지 선호하지 않습니다.
가장 큰 이유는 가독성이 저하되어 정확히 어떤 기능을 하는 코드인지 파악하기가 힘들어지기 때문입니다.
이러던 도중 좋은 자료를 발견하게 되어 번역하면서 공부해보기로 하였습니다.
https://betterprogramming.pub/stop-putting-so-many-if-statements-in-your-javascript-3b65aaa4b86b
https://www.digitalocean.com/community/posts/5-tips-to-write-better-conditionals-in-javascript
1. 조건문이 반복된다면 includes 메소드를 활용하자
// bad
function test(fruit) {
if (fruit === 'apple' ||
fruit === 'strawberry' ||
...
...
) {
console.log('red');
}
}
위와 같은 코드에서 적은수의 조건이라면 가독성이 괜찮은 것처럼 보입니다. 하지만, 조건이 늘어나면 늘어날수록 매우 지저분해집니다.
// better
function test(fruit) {
const redFruits = ['apple', 'watermelon', 'strawberry']
// 조건들을 배열에 넣음
if(redFruits.includes(fruit)) {
// 배열의 includes 메소드를 활용
console.log('red')
}
}
조건문이 많아진다면 해당 조건들을 배열에 넣은 다음, includes() 메소드를 활용하여 가독성을 높일 수 있습니다. 깔끔하지 않나요?
2. 조건문 중첩을 피하고 빨리 리턴하자 (Early Return)
유효하지 않은 조건이라면 빠르게 return 하는 것이 좋습니다. 예시코드를 보겠습니다.
// bad
function test(fruit, quantity) {
const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];
// 조건 1: fruit의 값이 존재할 경우
if (fruit) {
// 조건 2: 빨강일 경우
if (redFruits.includes(fruit)) {
console.log('red');
// 조건 3: 갯수가 만을 경우
if (quantity > 10) {
console.log('big quantity');
}
}
} else {
throw new Error('No fruit!');
}
}
if문과 else의 중첩이 늘어남에 따라서 코드는 지저분해졌습니다. 유효하지 않은 조건을 Early Return 시켜보겠습니다.
// better
function test(fruit, quantity) {
const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];
// 조건 1: fruit가 없을경우 Early Return
if (!fruit) throw new Error('No fruit!');
// 조건 2: 빨강일 경우
if (!redFruits.inclues(fruit)) return;
console.log('red');
// 조건 3: 갯수가 많을 경우
if (quantity > 10) {
console.log('big quantity');
}
}
위와 같이 변경하면서 불필요한 if, else문을 제거할 수 있게 되었습니다. 상황에 따라서 더 좋은 코드는 바뀔 수 있지만, 굳이 if 중첩문 안으로 조건을 끌고갈 이유는 없습니다.
3. 함수의 파라미터, 비구조화를 이용하자
// bad
function test(fruit, quantity) {
if(!fruit) return;
const q = quantity || 1;
console.log(`We have ${q} ${fruit}!`);
}
위의 코드는 파라미터로 과일의 갯수가 들어오면 갯수로, 그렇지 않다면 기본값을 1로 할당해주고 있습니다.
// better
function test(fruit, quantity = 1) {
if(!fruit) return;
console.log(`We have ${q} ${fruit}!`);
}
파라미터로 기본값을 할당해주는 방법을 이용하면 코드를 1줄 줄일 수 있게 됩니다.
// bad
function test(fruit) {
if(fruit && fruit.name) {
console.log (fruit.name);
} else {
console.log('unknown');
}
}
위의 코드는 파라미터가 존재하고, fruit 객체에 name 속성이 존재하는지 확인합니다.
// better
function test({name} = {}) {
console.log (name || 'unknown');
}
이처럼 간단하게 코드를 만들 수 있습니다. {name}은 비구조화로 객체의 name 속성값을 가져옵니다. 또한, 기본값으로 {} 할당을 해주어 객체가 없을 경우에 대한 에러도 방지하였습니다.
4. switch를 멀리하고 map, 객체 리터럴을 사용하자
// bad
function test(color) {
switch (color) {
case 'red':
return ['apple', 'strawberry'];
case 'yellow':
return ['banana', 'pineapple'];
case 'purple':
return ['grape', 'plum'];
default:
return [];
}
}
일반적인 switch case로 코드를 짠 경우입니다. map, 객체리터럴을 통해 간략하게 표현 가능합니다.
// better1, map
const fruitColor = new Map()
.set('red', ['apple', 'strawberry'])
.set('yellow', ['banana', 'pineapple'])
.set('purple', ['grape', 'plum']);
function test(color) {
return fruitColor.get(color) || [];
}
map을 사용한 방법입니다. map에서 color를 가져오고 값이 없을 시에는 빈 배열을 return 합니다.
// better2, 객체 리터럴
const fruitColor = {
red: ['apple', 'strawberry'],
yellow: ['banana', 'pineapple'],
purple: ['grape', 'plum']
};
function test(color) {
return fruitColor[color] || [];
}
이번에는 객체 리터럴을 사용한 방법입니다. map과 거의 동일한 방법입니다. 코드는 두 방법 모두 간결합니다.
map vs object의 경우에는 사용하는 경우가 약간 다르니 이것에 대해서는 추후의 포스팅에서 다뤄보겠습니다.
5. Array.some, Array.every 활용하기
// bad
const fruits = [
{ name: 'apple', color: 'red' },
{ name: 'banana', color: 'yellow' },
{ name: 'grape', color: 'purple' }
];
function test() {
let isAllRed = true;
// 조건: 모든 과일이 빨강이어야 한다
for (let f of fruits) {
if (!isAllRed) break;
isAllRed = (f.color === 'red');
}
console.log(isAllRed); // false
}
배열을 순회하면서 모든 과일의 색이 red면 True, 아니면 False를 반환하는 함수입니다. 이렇게 작성하면 가독성뿐만 아니라 코드를 이해하기도 어려워집니다. Array.some, Array.every를 사용해서 바꿔보겠습니다.
// better1
function test() {
const isAllRed = fruits.every(v=> v.color==='red');
console.log(isAllRed);
}
every() 메서드를 사용하여 모든 과일의 색이 red 경우에만 True를 반환하도록 변경하였습니다. 상당히 간결해지는 것을 볼 수 있습니다.
// better2
function test() {
const isAnyRed = fruits.some(f => f.color === 'red');
console.log(isAnyRed);
}
some() 메서드를 사용하면 하나의 과일만이라도 red일 경우에 True, 아니면 False를 반환하도록 작성할 수도 있습니다.
6. 삼항연산자를 사용하자
// bad
let action = 'work';
if(isNight) {
action = 'sleep'
}
위와 같은 간단한 조건문은 삼항연산자를 사용하여 1줄로 바꿀 수 있습니다.
// better
const action = isNight ? 'sleep' : 'work'
삼항연산자를 이용하면 좀 더 간단하게 코드를 작성할 수 있습니다. 또한 let 대신 const를 사용하므로 변수의 재할당에 대한 에러를 방지할 수 있습니다. 하지만, 보다 복잡한 조건문의 경우라면 if, else를 활용하는 편이 좋을 수도 있습니다.
7. &&, || (short circuit) 이용하기
// better
const action = isNight && 'sleep' || 'work'
위에서 작성한 코드를 short circuit를 활용하여 변경할 수 있습니다. 다만 이 역시도 너무 많이 사용할 경우 가독성을 해칠 수 있으므로 간단한 조건에만 사용하는 편이 좋습니다.
지금까지, 자바스크립트로 개발을 진행하면서 자연스레 생기는 if, else문을 최소하하는 방법을 알아보았습니다. 이러한 방법들이 정답은 아니지만 구성원들끼리 규칙(컨벤션)을 통일해서 사용한다면 조금 더 가독성 높은 코드를 만들 수 있으리라 생각됩니다.
읽어주셔서 감사합니다.
'SW개발 > Javascript' 카테고리의 다른 글
[Javascript]Javascript 멋지게 쓰기, 프로처럼 쓰기 (0) | 2021.08.31 |
---|---|
[Vue]Computed 와 Watch 언제 사용할까? (feat. computed vs methods) (0) | 2021.05.09 |
[Vue]v-if vs v-show (0) | 2021.05.08 |
[Vue]라이프 사이클 알아보기 (0) | 2021.04.03 |