본문 바로가기

Language/JavaScript

[번역글] JS에서 조건문을 더 낫게 쓰기 위한 꿀팁 5가지

이 글은 Jecelyn Yeen의 5 Tips to Write Better Conditionals in JavaScript라는 글을 한국어로 번역한 글입니다. 원문 링크

JavaScript를 쓸 때는 많은 조건문을 사용합니다. 조건문을 더 보기 좋고, 깔끔하게 쓰는 방법을 알려드리겠습니다.

  1. 여러 조건에 함께 맞아야 할 때는 Array.includes를 사용하자.
  2. 중첩은 최대한 적게, Return은 최대한 빨리.
  3. 기본 파라미터와 비구조화를 사용하자.
  4. Switch 대신 Map과 오브젝트를 사용하자.
  5. 배열의 전체 조건에는 Array.every, 부분 조건에는 Array.some을 사용하자.

여러 조건에 함께 맞아야 할 때는 Array.includes를 사용하자.

아래의 예제를 봅시다.

// 조건문으로 되어있습니다.
function test(fruit) {
if (fruit == '사과' || fruit == '딸기') {
console.log('빨갛습니다.');
}
}

view rawno array.include.js hosted with ❤ by GitHub

첫눈에는 괜찮을 수도 있습니다. 하지만 우리가 '체리'나 '크랜베리' 같은 또 다른 빨간 과일을 추가해야 할 때는 어떻게 해야할까요? ||보다 더 좋은 방법이 없는 걸까요?

function test(fruit) {
// 조건들을 배열로 뺐습니다.
const redFruits = ['사과', '딸기', '체리', '크랜베리'];
 
if (redFruits.includes(fruit)) {
console.log('빨갛습니다.');
}
}

view rawwith array.include.js hosted with ❤ by GitHub

이렇게 조건들을 배열로 따로 작성하면 코드가 더 간결해집니다.

중첩은 최대한 적게, Return은 최대한 빨리.

1번의 예시를 조금 더 확장해보도록 하겠습니다.

  1. 과일값이 주어지지 않는다면 에러를 냅니다.
  2. 과일이 10개가 넘는다면 아주 많네요!를 출력합니다.
function test(fruit, quantity) {
const redFruits = ['사과', '딸기', '체리', '크랜베리'];
// 조건 1: 과일값이 주어져야 합니다.
if (fruit) {
//조건 2: 과일은 빨간색이 여야 합니다.
if (redFruits.includes(fruit)) {
console.log('빨갛습니다.');
// 조건 3: 과일이 10개 초과여야 합니다.
if (quantity > 10) {
console.log('아주 많네요!')
}
}
} else {
throw new Error('과일이 없어요!');
}
}
 
// 결과
test(null);
// 에러: '과일이 없어요!'
 
test('apple');
// 출력: '빨갛습니다.'
 
test('apple', 20);
// 출력: '빨갛습니다. 아주 많네요!'

view rawarray.include with color.js hosted with ❤ by GitHub

위의 코드에는

  1. 1개의 잘못된 파라미터를 골라내는 if~else 문
  2. 3단계의 중첩 if문(조건1, 조건2, 조건3) 이 있습니다.

제 코딩 스타일은 조건에 맞지 않으면 최대한 빨리 return 하는 겁니다.

// 잘못된 조건이 있으면 최대한 빨리 Return 합시다.
 
function test(fruit, quantity) {
const redFruits = ['사과', '딸기', '체리', '크랜베리'];
 
// 조건 1: 에러는 최대한 빨리
if (!fruit) throw new Error('과일이 없어요!');
 
// 조건 2: 과일은 빨간색이 여야 합니다.
if (redFruits.includes(fruit)) {
console.log('빨갛습니다.');
 
// 조건 3: 과일이 10개 초과여야 합니다.
if (quantity > 10) {
console.log('아주 많네요!');
}
}
}

view rawearly throw.js hosted with ❤ by GitHub

이렇게 하면 단 하나의 중첩 if 문만 남게 됩니다. 이런 코딩 스타일은 if 문으로 실행할 코드의 길이가 길 때 유용합니다. (긴 if 문 밑에 else를 찾아야 한다고 생각해보세요!)

조건을 부정하고 빨리 return 하는 것으로 중첩을 더 줄일 수 있습니다. 아래의 두 조건문을 보시죠!

function test(fruit, quantity) {
const redFruits = ['사과', '딸기', '체리', '크랜베리'];
 
if (!fruit) throw new Error('과일이 없어요!'); // 조건 1: 에러는 최대한 빨리
if (!redFruits.includes(fruit)) return; // 조건 2: 과일이 빨갛지 않으면 `return` 합니다.
 
console.log('빨갛습니다.');
if (quantity > 10) {
console.log('아주 많네요!');
}
}

view rawearly throw and return.js hosted with ❤ by GitHub

원래의 조건 2를 부정하는 것으로 우리의 코드는 if 문 중첩의 족쇄에서 벗어날 수 있었습니다! 이런 코딩 스타일은 우리가 검사해야 할 조건이 많을 때 가독성을 높이고 코드 길이를 줄이는 데 유용합니다. 이건 그렇게 어려운 규칙이 아닙니다. 자신에게 물어보세요. 이렇게 쓴 게 전에 쓴 것보다 훨씬 깔끔하고 가독성이 좋죠?

하지만 항상 적은 중첩과 빠른 리턴이 좋지는 않습니다. 여기에 관련된 기사와 Stackoverflow 글이 있습니다.

Avoid Else, Return Early: Tim Oxley

Should I return from a function early or use an if statement?

기본 파라미터와 비구조화를 사용하자.

이 코드가 당신에게는 익숙할 것 같네요. 우리는 null이나 undefined를 체크하거나 기본값을 설정하기 위해 아래와 같은 코드를 사용합니다.

function test(fruit, quantity) {
if (!fruit) return;
const q = quantity || 1; // quantity가 주어지지 않으면 기본값으로 1을 설정합니다.
 
console.log(`${q}개의 ${fruit}가 있어요!!`);
}
 
//test results
test('banana'); // '1개의 바나나가 있어요!!'
test('apple', 2); // '2개의 사과가 있어요!!'

view rawwithout default param.js hosted with ❤ by GitHub

기본 파라미터를 사용하면 변수 q를 사용하지 않아도 됩니다.

function test(fruit, quantity = 1) { // quantity가 주어지지 않으면 기본값으로 1을 설정합니다.
if (!fruit) return;
console.log(`${quantityq}개의 ${fruit}가 있어요!!`);
}
 
// 결과
test('banana'); // '1개의 바나나가 있어요!!'
test('apple', 2); // '2개의 사과가 있어요!!'

view rawwith default param.js hosted with ❤ by GitHub

훨씬 쉽고 직관적이지 않나요? 함수의 각 파라미터는 기본값을 가질 수 있습니다. 예를 들어, fruit에도 기본값을 주려면 function test(fruit = 'unknown', quantity = 1)와 같이 작성할 수 있습니다.

만약 fruit가 객체라면 어떨까요? 기본값을 어떻게 지정해야 할까요?

function test(fruit) {
// fruit.name이 주어진다면 출력됩니다.
if (fruit && fruit.name) {
console.log (fruit.name);
} else {
console.log('알 수 없어요');
}
}
 
// 결과
test(undefined); // '알 수 없어요'
test({ }); // 알 수 없어요
test({ name: '사과', color: '빨강' }); // 사과

view rawwithout destructing.js hosted with ❤ by GitHub

위의 예제를 보세요! 과일의 이름이 주어진다면 이름을, 아니면 '알 수 없어요'를 출력하고자 합니다. fruit && fruit.name를 사용하지 않고도 fruit.name에 기본값을 설정할 수 있습니다.

// 비구조화 - 프로퍼티의 이름으로 그 값만 가져옵니다.
// 기본값을 {}로 지정합니다.
function test({name} = {}) {
console.log (name || '알 수 없어요');
}
 
// 결과
test(undefined); // '알 수 없어요'
test({ }); // '알 수 없어요'
test({ name: '사과', color: '빨강' }); // '사과'

view rawwith destructing.js hosted with ❤ by GitHub

우리는 name 프로퍼티만 필요하므로 {name}을 사용하여 객체를 비구조화할 수 있습니다. 이제부터는 fruit.name 대신 name을 변수처럼 사용할 수 있습니다.

{}를 기본값으로 지정해주었습니다. 그렇게 하지 않으면 주어진 객체에 name프로퍼티가 없을 때 test(undefined) - Cannot destructure property name of 'undefined' or 'null'라는 에러를 보게될거에요!

라이브러리를 사용하는 걸 싫어하지 않으신다면, Null check 코드를 줄일 수 있는 몇 가지 방법이 있습니다.

  1. Lodash __.get()함수 사용하기
  2. Babel과 함께 Facebook의 IDX라이브러리 사용하기 예제코드 함수형프로그래밍을 한다면, Lodash 대신에 Lodash FP를 사용하는 것도 좋겠어요! Lodash FP는 Lodash가 함수형 프로그래밍에 맞게 개량된 버전입니다.

Switch 대신 Map과 오브젝트를 사용하자.

색에 따라 과일을 반환하는 아래의 함수를 살펴볼까요?

function test(color) {
// Switch ~ case 문을 사용하여 색을 구분합니다
switch (color) {
case '빨강':
return ['사과', '딸기'];
case '노랑':
return ['바나나', '파인애플'];
case '보라':
return ['포도', '자두'];
default:
return [];
}
}
// 결과
test(null); // []
test('노랑'); // ['바나나', '파인애플']

view rawwith swtich case.js hosted with ❤ by GitHub

별로 이상해 보이지 않는걸요. 하지만 더 간략하게 줄일 방법이 있습니다.

// 오브젝트를 이용해서 색에 따라 과일을 출력해봅시다
const fruitColor = {
빨강: ['사과', '딸기'],
노랑: ['바나나', '파인애플'],
보라: ['포도', '자두']
};
 
function test(color) {
return fruitColor[color] || [];
}

view rawwith object.js hosted with ❤ by GitHub

객체 대신에 Map을 사용할 수도 있습니다.

// Map을 이용해서 색에 따라 과일을 출력해봅시다
const fruitColor = new Map()
.set('빨강', ['사과', '딸기'])
.set('노랑', ['바나나', '파인애플'])
.set('보라', ['포도', '자두']);
 
function test(color) {
return fruitColor.get(color) || [];
}

view rawwith Map.js hosted with ❤ by GitHub

Map은 ES6에 추가된 키/밸류를 가지는 객체 타입입니다. 자세한 내용은 링크를 참조하세요. 과연 Case의 사용을 자제해야만 할까요? 그것은 자기 생각에 따라 달라질 겁니다.(개인적으로, 저는 가능한 Case 대신 객체를 사용하려고 합니다) 하지만 너무 하나만 사용하지 마세요. 때에 맞게 사용한다면 더욱 좋은 코드를 만들 수 있을 겁니다. Todd Motto는 Switch와 오브젝트에 대한 깊이 있는 글을 썼습니다.

코드를 재작성해봅시다

예를 들어, 우리는 아래의 코드와 같이 코드를 다시 써도 같은 결과를 얻을 수 있습니다.

const fruits = [
{ name: '사과', color: '빨강' },
{ name: '딸기', color: '빨강' },
{ name: '바나나', color: '노랑' },
{ name: '파인애플', color: '노랑' },
{ name: '포도', color: '보라' },
{ name: '자두', color: '보라' }
];
 
function test(color) {
// Array.filter를 사용해서 색이 맞는 과일을 찾습니다!
return fruits.filter(f => f.color == color);
}

view rawrefactoring.js hosted with ❤ by GitHub

같은 결과의 코드를 4개나 보여드렸어요! 항상 문제를 푸는 방법은 1가지가 아닙니다. 그래서 코딩이 재밌는 거에요!

배열의 전체 조건에는 Array.every, 부분 조건에는 Array.some을 사용하자.

마지막 팁은 새롭지만 그다지 새롭지 않은 자바스크립트 배열의 함수를 활용하는 것입니다. 다음은 모든 과일이 빨간색인지 확인하는 함수입니다.

const fruits = [
{ name: '사과', color: '빨강' },
{ name: '바나나', color: '노랑' },
{ name: '파인애플', color: '노랑' }
];
 
function test() {
let isAllRed = true;
 
// 조건: 모든 과일이 빨간색이 여야 합니다.
for (let f of fruits) {
if (!isAllRed) break;
isAllRed = (f.color == '빨강');
}
 
console.log(isAllRed); // false
}

view rawwithout array.some or array.every.js hosted with ❤ by GitHub

이 코드는 너무 길어요! 몇 줄의 코드를 Array.every로 다시 쓴다면 어떨까요?

const fruits = [
{ name: '사과', color: '빨강' },
{ name: '바나나', color: '노랑' },
{ name: '파인애플', color: '노랑' }
];
 
function test() {
// 조건: 모든 과일이 빨간색이 여야 합니다.
const isAllRed = fruits.every(f => f.color == '빨강');
 
console.log(isAllRed); // false
}

view rawwith array.every.js hosted with ❤ by GitHub

더 깔끔해졌습니다! 비슷한 예로, 과일 중 하나라도 빨간색인가를 판단하려면 Array.some을 사용할 수 있습니다.

const fruits = [
{ name: '사과', color: '빨강' },
{ name: '바나나', color: '노랑' },
{ name: '포도', color: '보라' }
];
 
function test() {
// 조건: 과일중 하나라도 빨간색이여야 합니다.
const isAnyRed = fruits.some(f => f.color == '빨강');
 
console.log(isAnyRed); // true
}

view rawwith array.some.js hosted with ❤ by GitHub

정리

더 가독성 좋은 코드를 만들려고 노력을 해봅시다. 당신이 이 글을 읽고 뭔가를 배웠다면 좋을 것 같아요!

이게 답니다! 즐코(즐거운 코딩)하세요!



출처: https://code-200.tistory.com/14

 

[번역글] JS에서 조건문을 더 낫게 쓰기 위한 5가지 팁

이 글은 Jecelyn Yeen의 5 Tips to Write Better Conditionals in JavaScript라는 글을 한국어로 번역한 글입니다. 원문 링크 JavaScript를 쓸 때는 많은 조건문을 사용합니다. 조건문을 더 보기 좋고, 깔끔하게..

code-200.tistory.com