본문으로 바로가기

if, else 줄이기

category JavaScript 2020. 12. 23. 19:53

if else 줄이기

어디서 읽었는진 모르겠는데 코딩 스타일에 관한 글을 읽다가 'if문과 else문을 난발하는 것을 굉장이 싫어한다'라고 하며

map에 데이터를 넣어두고 코드를 깔끔하게 하는 것을 보고 '오... 코드 깔끔한데?'와 막연하게 고수의 스타일같아서 따라해보기로 했다

왜 줄여야 하는가?

가독성

사방팔방으로 찾아봤지만 가장 큰 이유는 가독성인 것 같다

const input = 'c';

if(input==='a'){
    console.log(1);
} else if(input==='b'){
    console.log(2);
} else if(input==='c'){
    console.log(3);
}

보다는

const map = new Map();
map.set('a',1);
map.set('b',2);
map.set('c',3);

console.log(map.get(input));

가 훨씬 보기편하고 어떻게 작동하는지 한눈에 알 수 있다

성능

해외 레딧에서도 왜 if else문을 map으로 바꾸어야하냐는 질문과 실제로 효율이 있냐고 물어보는 글이 있었고

상황에 따라 다르다고는 하지만 일반적으로 map을 사용하는 것이 나은 것 같다

if else문의 경우 조건문을 돌 때 시간복잡도가 o(n)인 반면

map을 사용할 경우 map에 데이터를 넣을 때 시간 복잡도가 o(n)이다

상황에 따라 다른 것


이 밑으론 해외에서 if else를 줄이기 위해 들여야하는 노력에 대한 글 5 Tips to Write Better Conditionals in JavaScript

 

5 Tips to Write Better Conditionals in JavaScript | DigitalOcean

5 Tips to Write Better Conditionals in JavaScript

www.digitalocean.com

을 번역해오신 분들이 계신데 그 내용을 내가 읽기 위해 매우 간단히 요약해서 적어 보려고 한다

1. 조건문이 반복된다면 includes 메소드를 활용

function test(fruit) {
  if (fruit == 'apple' || fruit == 'strawberry') {
    console.log('red');
  }
}

아무 문제가 없어 보이는 코드.. 그리고 내가 자주 사용하는 스타일의 코드다 (사실 다른 방법이 있는지 조차 몰랐다)

만약 조건이 늘어난다면 어떻게 될까?

function test(fruit) {
    if (fruit === 'apple' || 
        fruit === 'strawberry' ||
        fruit === 'tomato' || 
        fruit === ' cherry'
        ...
        ...
        ...
        ) {
        console.log('red');
    }
}

나였으면 줄만 제대로 맞춰놓고 '이정도면 깔끔해!'라며 좋아했을 것..

function test(fruit) {
    const redFruits = ['apple', 'watermelon', 'strawberry', 'tomato', 'cherry'];
    if(redFruits.includes(fruit)) {
        console.log('red');
    }
}

배열의 includes메소드를 사용해 매우 보기 편해졌고 코드의 중복도 없어졌다

2. 조건문의 중첩은 피하고 리턴을 빨리

function test(fruit, quantity) {
    const redFruits = ['apple', 'watermelon', 'strawberry']

    if(fruit) {
        if(redFruits.includes(fruit)) {
            console.log('red');
            if(quantity > 10) {
                console.log('big quantity');
            }
        }
    } else {
        throw new Error('No fruit!');
    }
}

이 예시는 나도 보고 바로 마음에 들지 않는 곳이 하나 있었다

바로 첫 조건문인 if(fruit)인데 fruit이 없으면 바로 리턴하지 뭐하러 뒤로 길게 빼서 에러를 날리고 있는 걸까?

function test(fruit, quantity) {
    const redFruits = ['apple', 'watermelon', 'strawberry'];
    if (!fruit) throw new Error('Error');

    if(redFruits.includes(fruit)) {
        console.log('red');
        if(quantity > 10) {
            console.log('big quantity');
        }
    }
} 

조건문 3중첩에서 2중첩으로 줄였지만 아직 줄일 곳이 더 있다

function test(fruit, quantity) {
    const redFruits = ['apple', 'watermelon', 'strawberry'];
    if (!fruit) throw new Error('Error');

    if(!redFruits.includes(fruit)) return ;

    console.log('red');

    if(quantity > 10) {
        console.log('big quantity');
    }

}

이 또한 같은 맥락으로 조건이 맞지 않으면 바로 리턴시켜버리는 것

이렇게 리팩토링 해주면 조건문의 중첩은 없어지고 가독성 또한 올라간다

3. 함수의 파라미터, 비구조화를 이용

function test(fruit, quantity) {
    if(!fruit) return;
    const q = quantity || 1; 

    console.log(`We have ${q} ${fruit}!`);
}

위의 코드는 파라미터로 과일의 갯수가 들어오면 갯수로 출력 그렇지 않으면 기본값으로 1을 할당해주고 있다

function test(fruit, quantity = 1) {
    if(!fruit) return;

    console.log(`We have ${q} ${fruit}!`);
}

이렇게 파라미터에 기본값으로 1을 할당해주면 함수 내의 2번째줄에 있는 코드는 필요가 없다

function test(fruit) { 
    if(fruit && fruit.name)  {
        console.log (fruit.name);
    } else {
        console.log('unknown');
    }
}

파라미터가 넘어왔는지 검사하고, 객체에 name이라는 속성이 존재하는지 확인하고 그제서야 객체의 속성을 출력하는 함수

이렇게 객체가 넘어온다면

function test({name} = {}) {
    console.log (name || 'unknown');
}

이렇게 간단히 만들 수 있다

여기서 {name}은 비구조화로 객체의 name 속성값을 가져온 것인데

뒤에 달려있는 = { }는 들어오는 객체가 없을 경우 기본값으로 빈 객체를 받아와 에러를 방지하기 위함이다

4. switch 또한 멀리하고 map과 객체 리터럴을 사용

switch도 사실 if else와 동일한 메커니즘으로 작동하니까.. 리팩토링하는 거겠지?

내가 보기엔 가독성의 차이는 switch나 map이나 비슷한 것 같은데

function test(color) {
    switch (color) {
        case 'red':
            return ['apple', 'strawberry'];
        case 'yellow':
            return ['banana', 'pineapple'];
        case 'purple':
            return ['grape', 'plum'];
        default:
            return [];
    }
}

은 객체 리터럴을 사용하면

function test(color) {
    const object = {
        red : ['apple', 'strawberry'],
        yellow : ['banana', 'pineapple'],
        purple : ['grape', 'plum']
    };

    return object[color] || [];
}

와 같이

map 객체를 사용한다면

function test(color) {
    const fruitColor = new Map()
        .set('red', ['apple', 'strawberry'])
        .set('yellow', ['banana', 'pineapple'])
        .set('purple', ['grape', 'plum']);

    return fruitColor.get(color) || [];
}

와 같이 간소화가 가능하다

원래 위 두 예제에선 map과 객체가 밖으로 빠져있었지만..

나는 이 코드 내에서만 본다면 함수 안에 들어가 있는게 나은 것 같아서 함수안에 넣어 주었다

5. 삼항 연산자 사용

이건 누구나 다 알법한 내용이라 스킵..

6. Array.some , Array.every 활용

const fruits = [
    { name: 'apple', color: 'red' },
    { name: 'banana', color: 'yellow' },
    { name: 'grape', color: 'purple' }
  ];

function test() {
  let isAllRed = true;

  // condition: all fruits must be red
  for (let f of fruits) {
    if (!isAllRed) break;
    isAllRed = (f.color === 'red');
  }

  console.log(isAllRed); // false
}

이 코드는 배열에 있는 과일 객체들의 색을 순회하며 빨강인지 확인하고 그렇지 않다면 거짓을 반환하는 함수이다

function test() {
    const isAllRed = fruits.every(v=> v.color==='red');
    console.log(isAllRed);
}

반대의 하나의 과일이라도 빨강이라면 참을 반환하고 싶다면 every를 사용하면된다

function test() {
    const isAnyRed = fruits.some(f => f.color === 'red');
    console.log(isAnyRed);
}

'JavaScript' 카테고리의 다른 글

자바스크립트의 유니코드  (0) 2021.01.13
논리 연산자  (0) 2020.12.24
비트 연산자  (0) 2020.12.19
history  (0) 2020.12.12
slice() vs substring()  (0) 2020.12.03