종종 else if문이나 else문을 지양해야한다는 소리를 들어서 도대체 왜 그래야하는지 궁금해서 찾아보게되었다.
그런데 생각보다 글이 많지 않거나 그냥 지양해야한다 하고 끝인 글이 꽤 있어서 구글링하는데 조금 힘들었다.
else if문
else if문이 마치 파이프라인처럼 앞의 if문과 연결되어 차례대로 실행된다고 생각하면 절대 안된다.
else if 문은 else문 처리가 한 번 되고 if문이 실행되는 것과 같다.
cosnt number = 10
if ( number >= 1){
console.log('number은 1보다 크거나 같다.');
} else if (number > 1){
console.log('number은 1보다 크다.');
}
// 'number은 1보다 크거나 같다.'
cosnt number = 10
if ( number >= 1){
console.log('number은 1보다 크거나 같다.');
} else {
if (number > 1){
console.log('number은 1보다 크다.');
}
}
// 'number은 1보다 크거나 같다.'
이 둘의 코드는 논리적으로 같으며, 결과도 같다.
따라서 else if 보다는 아래 코드처럼 if문을 써서 조건을 분리하는 편이 명확하다.
cosnt number = 10
if ( number >= 1){
console.log('number은 1보다 크거나 같다.');
}
if (number > 1){
console.log('number은 1보다 크다.');
}
// 'number은 1보다 크거나 같다.'
조건이 많아서 if문이 많아진다면 switch문으로 대체하는 편이 낫다.
else if문을 지양하는 또 다른 이유는 바로 가독성 때문이다.
const color = "red";
if (color === "blue") {
console.log(1);
} else if (color === "black") {
console.log(2);
} else if (color === "red") {
console.log(3);
}
보다는
const map = new Map();
map.set("blue", 1);
map.set("black", 2);
map.set("red", 3);
console.log(map.get(color));
이렇게 하는 것이 더 가독성이 좋고 작동원리를 파악하기 쉽다.
실제로 효율은 상황에따라 다르지만 일반적으로 map이 효율성이 더 좋은 경우가 많다.
else 문
else를 쓰지 않아도 조건이 논리적으로 분기된다.
function getActiveUserName(user) {
if (user.name) {
return user.name;
} else {
return '이름없음';
}
}
위 코드는 아래처럼 리팩토링하면 훨씬 좋다.
function getActiveUserName(user) {
if (user.name) {
return user.name;
}
return '이름없음';
}
else를 쓰지 않아야 하는 이유는 스타일상의 문제뿐만 아니라, 반전된 로직을 작성하게 되는 논리적 위험이 있기 때문이다.
하나의 함수가 두가지 이상의 기능을 할 때 else를 무분별하게 사용하면, 이런 문제가 생길 수 있다.
// age가 20 미만시 report라는 특수 함수를 실행하며, 손님에게 인사를 하는 로직을 작성하려고 한다.
function getHelloCustomer() {
if (user.age < 20) {
report(user);
} else {
return '안녕하세요';
}
}
// 이 코드에서는 else 때문에, 20세 미만에게만 인사를 하지 않는 의도하지 않은 결과가 발생한다.
- 아래 코드처럼 else문을 없애면, 두 기능(미성년자 확인하여 신고, 손님에게 인사)이 분리되어 손님의 나이에 관계없이 인사하는 기능이 실행된다. 아래 코드처럼 리팩토링 하는 것이 적절하다.
function getHelloCustomer() {
if (user.age < 20) {
report(user);
}
return '안녕하세요';
}
추가적으로 해외 글 중에 이런 것이 있다.
5 Tips to Write Better Conditionals in JavaScript
아래 글부터는 '조건문을 다룰때 더 좋고 깔금한 조건문을 작성하기 위한 5가지 팁'에대한 정리글이다.
1. 조건문이 반복된다면 includes 메소드를 활용
function test(fruit) {
if (fruit == 'apple' || fruit == 'strawberry') {
console.log('red');
}
}
필자는 그냥 이렇게 썼던 것 같다. 프로그래머스 문제를 풀 때도 이런식으로 '||' 이것을 굉장히 많이 썼다.
그런데 여기서 더 과일이 늘어난다면 ? 추가될때마다 '||' 을 사용해준다면 난잡한 코드가 될 것이다.
function test(fruit) {
if (
fruit == "apple" ||
fruit == "strawberry" ||
fruit === "cherry" ||
fruit === "cranberries"
) {
console.log("red");
}
}
이거보다 더 fruit가 늘어난다면 ??? 생각만해도 끔찍하다.
그럴때 바로
Array.includes()를 사용하면 훨씬 가독성이 좋아진다.
function test(fruit) {
// extract conditions to array
const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];
if (redFruits.includes(fruit)) {
console.log('red');
}
}
이전보다 훨씬 코드가 깔금해 졌다.
2. 조건문의 중첩은 지양하고 리턴은 빠르게
두 가지 조건을 더 포함하여 이전 예제를 확장을 할 것이다.
- 과일이 제공되지 않으면 오류 발생하시오
- 10을 초과하는 경우 과일 수량을 수용하고 출력하시오
function test(fruit, quantity) {
const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];
// condition 1: fruit must has value
if (fruit) {
// condition 2: must be red
if (redFruits.includes(fruit)) {
console.log('red');
// condition 3: must be big quantity
if (quantity > 10) {
console.log('big quantity');
}
}
} else {
throw new Error('No fruit!');
}
}
// test results
test(null); // error: No fruits
test('apple'); // print: red
test('apple', 20); // print: red, big quantity
와 보기만해도 벌써 복잡해보여서 리팩토링하고싶어지는 코드이다.
- if/else문이 사용
- 3단계의 if문이 사용
여기서 가장 먼저 봐야할 것은 바로 오류발생이다. 과일이 없다면 그 즉시 바로 오류를 발생시키는 게 훨씬 좋다.
바로 early return을 하는 것이다.
function test(fruit, quantity) {
const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];
// condition 1: throw error early
if (!fruit) throw new Error('No fruit!');
// condition 2: must be red
if (redFruits.includes(fruit)) {
console.log('red');
// condition 3: must be big quantity
if (quantity > 10) {
console.log('big quantity');
}
}
}
이렇게 해서 중첩수준이 한 단계 낮아졌다.
이런 코딩스타일은 특히 긴 if문이 있는 경우에 좋다.
그런데 여기서 더 줄일 수 있다.
/_ return early when invalid conditions found _/
function test(fruit, quantity) {
const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];
if (!fruit) throw new Error('No fruit!'); // condition 1: throw error early
if (!redFruits.includes(fruit)) return; // condition 2: stop when fruit is not red
console.log('red');
// condition 3: must be big quantity
if (quantity > 10) {
console.log('big quantity');
}
}
이렇게 하면 이제 이 코드에 중첩문이 없다.
그렇다고 항상 적은 중첩 및 조기 반환을 목표로 하되 과도하게 사용하면 좋지 않다는 이야기도 하고있다.
- if/else 코딩 스타일에 대한 StackOverflow 토론
public void SomeFunction(bool someCondition)
{
if (someCondition)
{
// Do Something
}
}
public void SomeFunction(bool someCondition)
{
if (!someCondition)
return;
// Do Something
}
이렇게 두 가지 코딩 방식이 있다면 어떻게 하겠습니까?
저는 후자를 추천합니다.
코드가 복잡해 질 수록 후자가 빛을 바랍니다.
early return (x)
public int SomeFunction(bool cond1, string name, int value, AuthInfo perms)
{
int retval = SUCCESS;
if (someCondition)
{
if (name != null && name != "")
{
if (value != 0)
{
if (perms.allow(name)
{
// Do Something
}
else
{
reval = PERM_DENY;
}
}
else
{
retval = BAD_VALUE;
}
}
else
{
retval = BAD_NAME;
}
}
else
{
retval = BAD_COND;
}
return retval;
}
early return (o)
public int SomeFunction(bool cond1, string name, int value, AuthInfo perms)
{
if (!someCondition)
return BAD_COND;
if (name == null || name == "")
return BAD_NAME;
if (value == 0)
return BAD_VALUE;
if (!perms.allow(name))
return PERM_DENY;
// Do something
return SUCCESS;
}
3. 기본 함수 매개변수 사용 및 구조화
자바스크립트로 작업할 때 항상 null/undefined값을 확인하고 기본값을 할당 해야 합니다.
function test(fruit, quantity) {
if (!fruit) return;
const q = quantity || 1; // if quantity not provided, default to one
console.log(`We have ${q} ${fruit}!`);
}
//test results
test('banana'); // We have 1 banana!
test('apple', 2); // We have 2 apple!
사실, q 기본 함수 매개변수를 할당하여 변수를 제거할 수 있습니다.
function test(fruit, quantity = 1) { // if quantity not provided, default to one
if (!fruit) return;
console.log(`We have ${quantity} ${fruit}!`);
}
//test results
test('banana'); // We have 1 banana!
test('apple', 2); // We have 2 apple!
이렇게 하면 훨씬 직관적이지 않습니까 ?
만약 fruit가 객채라면 어떻게해야할까요
function test(fruit) {
// printing fruit name if value provided
if (fruit && fruit.name) {
console.log (fruit.name);
} else {
console.log('unknown');
}
}
//test results
test(undefined); // unknown
test({ }); // unknown
test({ name: 'apple', color: 'red' }); // apple
파라미터가 넘어왔는지 검사하고, 객체에 name이라는 속성이 존재하는지 확인하고 그제서야 객체의 속성을 출력하는 함수이다.
// destructing - get name property only
// assign default empty object {}
function test({name} = {}) {
console.log (name || 'unknown');
}
//test results
test(undefined); // unknown
test({ }); // unknown
test({ name: 'apple', color: 'red' }); // apple
과일에서 속성 name만 필요하므로 {name}을 사용하여 매개 변수를 재구성할 수 있으므로 fruit.name 대신 코드에서 변수를 사용하여 name을 사용할 수 있습니다.
4. switch 문보다 Map / Object Literal 사용하기
아래 예를 살펴보겠습니다. 색상을 기반으로 과일을 인쇄하려고 합니다.
function test(color) {
// use switch case to find fruits in color
switch (color) {
case 'red':
return ['apple', 'strawberry'];
case 'yellow':
return ['banana', 'pineapple'];
case 'purple':
return ['grape', 'plum'];
default:
return [];
}
}
//test results
test(null); // []
test('yellow'); // ['banana', 'pineapple']
위의 코드는 아무 문제가 없어 보이지만 꽤 장황합니다.
더 깨끗한 구문을 사용하여 객체 리터럴을 사용하여 동일한 결과를 얻을 수 있습니다.
// use object literal to find fruits in color
const fruitColor = {
red: ['apple', 'strawberry'],
yellow: ['banana', 'pineapple'],
purple: ['grape', 'plum']
};
function test(color) {
return fruitColor[color] || [];
}
또는 Map을 사용하여 동일한 결과를 얻을 수 있습니다.
// use Map to find fruits in color
const fruitColor = new Map()
.set('red', ['apple', 'strawberry'])
.set('yellow', ['banana', 'pineapple'])
.set('purple', ['grape', 'plum']);
function test(color) {
return fruitColor.get(color) || [];
}
Map은 ES2015(es6)부터 사용 가능한 객체 유형으로 키 값 쌍을 저장할 수 있습니다.
(map을 이렇게 밖으로 빼서 전역으로 해준 이유는 함수 안에 넣으면 함수가 실행될때마다 map이 생성되는 연산이 추가가 되어서 성능상 손해이다.)
5. 전체/부분 기준에 대해 Array.every 및 Array.some 사용
이 마지막 팁은 코드 줄을 줄이기 위해 새로운 Javascript 배열 기능을 활용하는 방법에 관한 것입니다.
아래 코드를 보면 모든 과일이 빨간색인지 확인하고 싶습니다.
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
}
이 코드를 Array.every을 사용하여 줄일 수 있습니다.
const fruits = [
{ name: 'apple', color: 'red' },
{ name: 'banana', color: 'yellow' },
{ name: 'grape', color: 'purple' }
];
function test() {
// condition: short way, all fruits must be red
const isAllRed = fruits.every(f => f.color == 'red');
console.log(isAllRed); // false
}
비슷한 방식으로 Araay.some을 이용하면 빨간 과일이 일부 존재하는지 확인할 수 있다.
const fruits = [
{ name: 'apple', color: 'red' },
{ name: 'banana', color: 'yellow' },
{ name: 'grape', color: 'purple' }
];
function test() {
// condition: if any fruit is red
const isAnyRed = fruits.some(f => f.color == 'red');
console.log(isAnyRed); // true
}
'정보 > The 공부' 카테고리의 다른 글
[고찰] 상처 받지 않으면서 피드백 주고 받기 (1) | 2022.11.04 |
---|---|
[클린코드] 어떻게 의미 있는 이름을 지을 수 있을까? (0) | 2022.10.31 |
[우테콘2022] 우리 팀의 코드리뷰 문화, 이렇게 조금씩 발전했어요 - (3) 코드리뷰 문화, 이렇게 개선했어요 (0) | 2022.10.21 |
[우테콘2022] 우리 팀의 코드리뷰 문화, 이렇게 조금씩 발전했어요 - (2) 코드리뷰, 우리팀은 이렇게 (0) | 2022.10.21 |
[우테콘2022] 우리 팀의 코드리뷰 문화, 이렇게 조금씩 발전했어요 - (1) 코드리뷰를 왜 해야할까요? (0) | 2022.10.21 |