티스토리 뷰

WEB/JavaScript

[클린코드 For JS] 5. 분기

Harimad 2022. 6. 28. 18:39

🔖TAG 💡DefaultCase, 💡earlyReturn, 💡else if/else 피하기, 💡null병합연산자, 💡Truthy&Falsy, 💡값식문, 💡단축평가(short-circuit evaluation), 💡드모르간의법칙, 💡부정조건문지양, 💡삼항연산자📕

출처😌

클린코드 자바스크립트 | Udemy

자료🙄

Git: pocojang/clean-code-js (github.com)


목차

1. 과정 소개 
2. 변수 다루기 
3. 타입 다루기
4. 경계 다루기
5. 분기 다루기 🚩

    5-1. 값식문

    5-2. 삼항 연산자 다루기

    5-3. Truthy & Falsy

    5-4. 단축평가 (short-circuit evaluation)

    5-5. else if 피하기

    5-6. else 피하기

    5-7. Early Return

    5-8. 부정 조건문 지양하기

    5-9. Default Case 고려하기

    5-10. 명시적인 연산자 사용 지향하기

    5-11. Nullish coalescing operator

    5-12. 드모르간의 법칙
6. 배열 다루기
7. 객체 다루기
8. 함수 다루기


5. 분기 다루기

 

5-1. 값식문

( ) 는 함수와 관련되어 있습니다.

아래는 리액트의 JSX가 Babel을 만나서 트랜스파일링 되는 과정입니다.

div 태그가 ' '에 감싸지고, html의 id가 객체로 바뀌고, 문자 부분이 ' ' 안에 감싸졌습니다.

// This JSX:
ReactDOM.render(
  <div id="msg">Hello World!</div>,		// →→→→→→→→→→→ ↓
  mountNode
);
// Babel을 만나면
// Is transformed to this JS:
ReactDOM.render(
  React.createElement('div', { id: 'msg' }, 'Hello World!'), //←←←
  mountNode
);

 

다음 케이스를 보겠습니다.

JSX에서 id 속성에 if 문을 넣었을 때 Babel에 의해 아래와 같이 변경 될까요?

아마 실행하면 문법 에러가 날겁니다.

if문을 객체안에 값으로 넣을 수 없습니다.

if문은 말그대로 문 입니다. for문도 마찬가지 입니다.

📌하지만 조건문을 다루는 삼항연산자는 객체의 으로 넣을 수 있습니다! (삼항연산자는 값으로 평가되기 때문입니다.)

// This JSX:
<div id={if (condition) { 'msg' }}>Hello World!</div> // id프로퍼티의 값을 if문으로 넣을 수 있을까?

// Is transformed to this JS:
React.createElement("div", {id: if (condition) { 'msg' }}, "Hello World!"); // JSX가 변환되어 객체의 값으로 if문이 동작할까? 그렇지않다.

ReactDOM.render(<div id={condition ? 'msg' : null}>Hello World!</div>, mountNode); // 이건 동작!

switch case 같은 경우도 조건문을 다루는데, 값으로 넣을 수 있습니다.

아래는 즉시실행함수(IIFE)입니다.

중괄호{ } 내부에는 값과 식만 넣어야 합니다.

{ } 안의 즉시실행함수는 값을 return 하기 때문에 switch case문을 사용할 수 있는 겁니다.

 

또 p 태그안에 if문을 사용하지 않고 논리연산자( '||' )했습니다.

좌측의 값이 false이면 오른쪽 값을 쓰겠다는 뜻이고

좌측의 값이 true이면 왼쪽 값을 쓰겠다는 의미입니다.

여기서도 if문을 사용하면 어떨까 생각하시는분이 계실 수 있습니다.

이렇게 값과 식 만으로도 분기문없이 조건문을 이용할 수 있습니다.

function ReactComponent() {
	return (
		<section>
			<h1>Color</h1>
			<h3>Name</h3>
			<p>{this.state.color || 'white'}</p>
			<h3>Hex</h3>
			<p>
				{(() => {
					switch (this.state.color) {
						case 'red':
							return '#FF0000';
						case 'green':
							return '#00FF00';
						case 'blue':
							return '#0000FF';
						default:
							return '#FFFFFF';
					}
				})()}
			</p>
		</section>
	);
}

 

아래는 즉시 실행함수로 감싸져 있습니다.

ObjectRow 컴포넌트를 여러개 그리기 위해

for문을 돌리면서 임시변수(rows)에 데이터를 푸쉬하고 있습니다.

그걸 나중에 반환하는 함수입니다.

아래의 함수를 어떻게 고치는게 더 보기 좋을까요?

function ReactComponent() {
	return (
		<tbody>
			{(() => {
				const rows = [];

				for (let i = 0; i < objectRows.length; i++) {
					rows.push(<ObjectRow key={i} data={objectRows[i]} />);
				}
				return rows;
			})()}
		</tbody>
	);
}

바로 고차함수를 사용하는 것입니다.

다시 한번 강조하면, 중괄호 { } 안에는 '값', '식'만 들어가야 합니다.

아래와 같이 간단하게 바꿀 수 있습니다.

map이라는 배열 메서드가 반복해서 return값(ObjectRow)을 배열에 담아내고 완성된 ObjectRow들이 JSX 내부에서 렌더링 됩니다.

function ReactComponent() {
	return (
		<tbody>
			{(objectRows.map((obj, i) => {
				<ObjectRow key={i} data={obj} />
			 })}
		</tbody>
	);
}

 

다음 케이스를 보겠습니다.

아래도 즉시 실행함수를 실행하는 코드입니다.

참고로 함수는 인자로 '값' 혹은 '식'을 받아야 하기 때문에 if문이나 for문은 들어갈 수 없습니다.

function ReactComponent() {
	return (
		<div>
			{(() => {
				if (conditionOne) return <span>One</span>;
				if (conditionTwo) return <span>Two</span>;
				else conditionOne;
				return <span>Three</span>;
			})()}
		</div>
	);
}

위의 코드를 리팩토링하면 다음과 같습니다.

function ReactComponent() {
	return (
		<div>
			{ conditionOne && <span>One</span> }
			{ conditionTwo && <span>Two</span> }
			{ !conditionTwo && <span>Three</span> }
		</div>
	);
}

 

요약

객체의 value 값으로 if/for/switch문이 들어갈 수 없습니다.

그럼, 함수의 인자로 if문 | for문 | switch문이 과연 들어갈 수 있을까?를 고민해보면 좋겠습니다.

들어갈 수 없습니다.

대신 함수의 인자로 함수는 넣을 수 있습니다. 이를 고차함수라고 합니다.

그래서 왜 고차함수를 활용해야하는지 까지 이해하셨으면 좋겠습니다.

 

이해

함수의 인자로 if/for/switch문이 들어 갈 수 없는 대신

값으로 평가 될 수 있는 함수를 넣으면 되겠고

그걸 활용한 것이 고차함수니까

고차함수에 대한 개념을 익히면 좋겠구나🤸‍♂️🎉

 

5-2. 삼항 연산자 다루기

삼항연산자로 과도하게 숏코딩을 하시는 분이 있습니다.

이것이 나쁜건 아니지만, 일관성있는 코드를 쓰는 것이 중요합니다.

삼항연산자는 3개의 피연산자를 취합니다.

문법은 아래와 같습니다.

조건 ? 참 : 거짓

중요한건 참, 거짓에는 '식' 이 들어가야 합니다.

그럼 당연히 '문'은 들어가지 못합니다.

 

아래는 대표적인 어지러운 삼항연산자 사용 예시입니다.

그 아래는 삼항연산자와 똑같은 조건이 나열된 조건식들입니다.

어떤 코드가 보기좋고 사용하고싶습니까?

저는 차라리 switch case문을 쓰고 싶습니다.

function example() {
	return condition1 ? value1 : condition2 ? value2 : condition3 ? value3 : value4;
}

function example() {
	if (condition1) {
		return value1;
	} else if (condition2) {
		return value2;
	} else if (condition3) {
		return value3;
	} else {
		return value4;
	}
}

아래처럼 switch case문으로 작성하면, switch 분기문을 따라 조건을 판별하고 default에서 리턴해주면 깔끔한 코드를 쓸 수 있습니다.

function example() {
	let temp = condition1 = condition2 = condition3;

	switch(key) {
		case value:
			break;
			//...
		default:
			break;
	}
}

 

여기서는 큰 문제가 존재할 까요? 큰 문제는 없어 보입니다.

하지만 if문이 중첩되어있어서 사람의 눈으로 보기에 한번에 이해되지 않을 수 있습니다.

괄호 ( )를 씌워준다면 훨씬 더 이해하기 좋은 코드가 됩니다.

const example = condition1 ? (a === 0 ? 'zero' : 'positive') : 'negative';

 

여기서 삼항연산자의 진가를 볼 수 있습니다.

isLogin이 있으면 getName함수를 name변수에 담고

없으면 '이름없음을 name 변수에 담습니다.

여기서 인자값으로 들어온 isLogin이 false일 수 있습니다.

이 때를 대응하기위해 삼항연산자를 쓸 수 있습니다.

당연히 if문으로도 똑같은 기능을 만들 수 있습니다. 하지만 삼항연산자가 훨씬 짧고 가독성이 좋습니다.

const welcomeMessage = (isLogin) => {
	const name = isLogin ? getName() : '이름없음';

	return `안녕하세요 ${name}`;
};

 

const isAdult = age > 19 ? 'yes' : 'no';

 

이 케이스는 BAD 케이스 입니다.

alert라는 window API를 가져오고 있습니다.

삼항연산자는 '참', '거짓' 자리에 '값' 혹은 '식'이 오면 좋은데

여기는 alert 함수를 실행하고 있습니다.

alert이 값을 리턴할 수 있지 않냐고 생각하실 수 있는데요, alert는 void를 리턴합니다. 반환이 없다는 뜻입니다.

그럼 alert가 참, 거짓 자리에 들어가서 undefined를 리턴하는 것이 됩니다.

그렇기 때문에 삼항연산자에서 참,거짓자리에 alert를 쓰는게 의미있는 코드인지 의문이 듭니다.

숏코딩을 위해 억지로 쓴게 아닌가 하는 생각이 듭니다.

 

더 나은 코드를 위해

if (isAdult) alert('입장이 가능합니다.') else alert('입장이 불가능합니다.')

를 쓰는게 더 나아 보입니다.

여러분은 어떤 선택을 원하시는 가요? 정답은 없습니다.

function alertMessage(isAdult) {
	isAdult ? alert('입장이 가능합니다.') : alert('입장이 불가능합니다.');
}

 

삼항연산자를 어떨때 쓰는게 좋을까요?

① 무언가의 값을 만들어 내고 그걸 변수에 담아낼 때 (위의 예시들 참고)

 

② 함수가 간단하게 값을 반환할 때

function alertMessage(isAdult) {
	return isAdult ? '입장이 가능합니다.' : '입장이 불가능합니다.';
}

③ 삼항연산자로 값을 만들어 내고, 그 값을 이용해서 수 많은 분기문을 만들어 낼 때

function alertMessage(isAdult) {
	const isAdult ? '입장이 가능합니다.' : '입장이 불가능합니다.';
    
    if (isAdult) {
    	// some logic
    }
    
    if (isAdult && gender === 'MAN') {
    
    }
    if (isAdult && gender === 'MAN') {
    
    }
    // ...
}

 

④ 삼항연산자를 3개의 피연산자를 필요로 한다했지만, 2개의 피연산자만 필요로 할 때

  예) 조건 ? : 거짓 /  조건 ? 참 : 거짓  ←인 경우

이 부분은 아래 챕터에서 보겠습니다.

 

5-3. Truthy & Falsy

JS는 동적인 언어라 의도치 않은 형변환이 일어날 수 있습니다.

아래 주석처럼 간단하게 조건식을 넣어도 동작합니다.

'참(truthy)'이기 때문 입니다.

 

참고: 참 같은 값 - 용어 사전 | MDN (mozilla.org)

참고: 거짓같은 값 - 용어 사전 | MDN (mozilla.org)

if ('string'.length > 0) {	// if('string'.length) {}
}

if (!isNaN(10)) {		// if(10) {}
}

if (boolean === true) {		// if(boolean) {}
}

아래는 인자값들은 전부 truthy로 평가됩니다.

if (true){ console.log(true) }
if ({}){ console.log(true) }
if ([]){ console.log(true) }
if (42){ console.log(true) }
if ("0"){ console.log(true) }
if ("false"){ console.log(true) }
if (new Date()){ console.log(true) }
if (-42){ console.log(true) }
if (12n){ console.log(true) }
if (3.14){ console.log(true) }
if (-3.14){ console.log(true) }
if (Infinity){ console.log(true) }
if (-Infinity){ console.log(true) }

아래의 인자값들은 전부 falsy로 평가됩니다. 즉, if문이 실행되지 않습니다.

if (false){ console.log(2)}
if (null){ console.log(2)}
if (undefined){ console.log(2)}
if (0){ console.log(2)}
if (-0){ console.log(2)}
if (0n){ console.log(2)}
if (NaN){ console.log(2)}
if (""){ console.log(2)}

 

물론 조건문을 많이 넣어서 값을 세세하게 걸러낼 수 있지만

아래는 마지막 함수 처럼 간단하게 truthy/falsy 개념을 이용하는 것이 좋습니다!

function printName(name) {
	if (name === undefined || name === null) {
		return '사람이 없네요';
	}

	return '안녕하세요 ' + name + '님';
}

printName('철수') // '안녕하세요 철수님'
printName() // '사람이 없네요'

//--------------------------------------------------

function printName2(name) {
	if (name === null) {
		return '사람이 없네요';
	}

	return '안녕하세요 ' + name + '님';
}

printName2('철수') // '안녕하세요 철수님'
printName2() // '안녕하세요 undefined님'

//--------------------------------------------------

function printName3(name) {
	if (name === undefined || name === null) {
		return '사람이 없네요';
	}

	return '안녕하세요 ' + name + '님';
}

var student = '철수';
var student = null;

printName3(student) // '사람이 없네요'

//----BEST----------------------------------------------

function printName4(name) {
	if (!name) {
		return '사람이 없네요';
	}

	return '안녕하세요 ' + name + '님';
}
printName4() // '사람이 없네요'
printName4('BOB') // '안녕하세요 BOB님'

 

/**
 * Truthy (참 같은 값)
 */
function SomeComponent({ isShowHeader }) {
	return (
		<div>
			{isShowHeader ? <Header /> : null}
			<Body />
		</div>
	);
}

function SomeComponent({ content }) {
	return <div>{content.length > 0 ? <MessageList messages={props.messages} /> : null}</div>;
}

 

5-4. 단축평가 (short-circuit evaluation)

참고: 03. 단축 평가 논리 계산법 · GitBook (vlpt.us)

 

true && true && '도달 O'

true && false && '도달 X'

false || false || '도달 O'

true || true || '도달 X'

 

아래의 if 분기를 간단하게 할 수 있습니다.

function fetchData() {
	if (state.data) {
		return state.data;
	} else {
		return 'Fetching...';
	}
}
// 단축분기로 결과를 빠르게 도출
function fetchData() {
	return state.data || 'Fetching...';
}

 

function favoriteDog(someDog) {
	let favoriteDog;

	if (someDog) {
		favoriteDog = someDog;
	} else {
		favoriteDog = '냐옹';
	}

	return favoriteDog + '입니다';
}

// 단축 분기 사용
function favoriteDog(someDog) {
	return (someDog || '냐옹') + '입니다';
}

favoriteDog() // 냐옹 입니다
favoriteDog('포메') // 포메 입니다

 

// 조건 길게 나열
function getActiveUserName(user, isLogin) {
    if (isLogin) {
        if (user) {
            if (user.name) {
                return user.name;
            } else {
                return '이름없음';
            }
        }
    }
}
// 단축 평가로 코드 줄이기
function getActiveUserName(user, isLogin) {
	if (isLogin && user) {
		if (user.name) {
			return user.name;
		} else {
			return '이름없음';
		}
	}
}

// 추가 단축 평가로 더 코드 줄이기
function getActiveUserName(user, isLogin) {
	if (isLogin && user) {
		return user.name || '이름없음';
	}
}

 

결론

의식적으로 논리연산자를 잘 이용해서 단축 평가를 자주 이용하도록 노력해야 합니다.

 

5-5. else if 피하기

아래의 결과는 어떻게 나올까요?

첫 번째 if 문에 걸립니다.

if와 else를 먼저 처리하고 else if의 if 문이 한번더 실행됩니다.

이럴 땐 else if 를 사용하지 않도록 훈련하는게 중요합니다.

else if 를 쓴다는게 애초에 조건에 대해서 명확하게 생각하고 있지않다는 상황일 수 있습니다.

그래서 else if 를 아예 안쓰거나 else if 가 늘어질 경우 switch문으로 쓰는게 더 낫습니다.

else if 문을 계쏙 쓰는 행위를 계속 한다면, 결국 switch문과 다를게 없습니다.

그럼 어떻게 할까요?

const x = 1;

if (x >= 0) {
	console.log('x는 0과 같거나 크다');
} else if (x > 0) {
	console.log('x는 0보다 크다 ');
} else {
	console.log('Else');
}

// 위의 코드는 아래와 논리적으로 일치합니다.

const x = 1;

if (x >= 0) {
	console.log('x는 0과 같거나 크다');
} else {
	if (x > 0) {
		console.log('x는 0보다 크다 ');
	}
}

아예 if문만 사용하는 겁니다.

그럼 명확하게 조건을 분리해 낼 수 있습니다.

const x = 1;

if (x >= 0) {
	console.log('x는 0과 같거나 크다');
}
if (x > 0) {
	console.log('x는 0보다 크다 ');
}

 

요약

if / else if 를 쓸 때 분기문이 파이프 라인으로 흐른다고 생각하면 절대 안됩니다.

if 와 else문을 먼저 처리하고 그다음 else if 의 if 문이 동작 한다는 것을 잊으면 안됩니다.

if / else if 문이 많이 늘어질 경우에는 차라리 switch case 문을 고려하는걸 추천합니다.

아니면 명확한 조건을 분리해낼 목적으로 if문만 쓰는 것도 좋습니다.

 

5-6. else 피하기

이전 챕터에서 if / else if 문을 맹목적으로 쓰지 말자고 했습니다.

이번 else문도 마찬가지 입니다.

if / else 문을 단축평가로 정말 간단하게 표현할 수 있습니다.

아니면 애초에 else문이 필요하지 않을 수 도있습니다.

function getUserName(user) {
	if (user.name) {
		return user.name;
	} else {
		return '이름없음';
	}
}

// 단축 평가로 짧게
function getUserName(user) {
	return user.name || '이름없음';
}

// else문 생략
function getUserName(user) {
	if (user.name) {
		return user.name;
	}
    return '이름없음';
}

if / else 문을 썼을 때 문제가 발생할 수 있습니다.

함수가 2가지 기능을 할 때가 예입니다.

아래는 무슨 문제가 있을 까요?

아래 함수의 이름만 보면 고객에게 인사를 하는 함수 입니다.

성인이 아닌 사람에게도 '안녕하세요'를 반환하도록 해야하는데 

맹목적으로 if / else문을 써버려서 else문이 동작 안하는 경우가 생깁니다.

함수가 if 문에 걸리면 report함수를 실행하는 기능과, else문에 걸려서 '안녕하세요'를 리턴하는 기능 2가지를 가지고 있습니다.

/**
 * age가 20 미만시 특수 함수 실행
 */
function getHelloCustomer(user) {
	if (user.age < 20) {
		report(user);
	} else {
		return '안녕하세요';
	}
}

 

애초에 else문을 안썼으면 문제가 해결됩니다.

그러면 함수를 분리 시키지 않고도 요구사항을 충족하게됩니다.

function getHelloCustomer(user) {
	if (user.age < 20) {
		report(user);
	}
    return '안녕하세요';
}

 

5-7. Early Return

아래의 코드 로직은 다음과 같습니다.

1. 로그인 여부

2. 토큰 존재 여부

3. 기 가입유저 확인 여부

  - 가입

  - 리프레시 후 로그인 성공처리

function loginService(isLogin, user) {
	if (!isLogin) {			// 로그인이 안되어있다면
		if (checkToken()) {	// 브라우저에서 토큰이 있다면
			if (!user.nickName) {	//유저의 nickName이 존재하지 않으면
				return registerUser(user);	// 회원가입에 유저를 보냄
			} else {		//유저의 nickName이 존재하면
				refreshToken();		// 토큰을 새로고침

				return '로그인 성공';	//리턴
			}
		} else {		// 브라우저에 토큰이 없으면
			throw new Error('No Token');	// 에러 던짐
		}
	}
}

위의 코드를 early return 을 적용해서 줄여보겠습니다.

아래의 코드는 의도가 명확하게 나열되어 있습니다.

로그인 되어있으면 리턴 -> 토큰 없으면 에러발생 -> 유저 닉네임 없으면 회원가입으로 보냄 ->  닉네임 있으면 새로고침

function loginService(isLogin, user) {
	// Early Return
	/**
	함수를 미리 종료
	사고하기 편하다.
	*/
	if (isLogin) {
		return;
	}

	if(!checkToken()) {
		throw new Error('No Token');
	}

	if (!user.nickName) {
		return registerUser(user);
	}
	
	refreshToken();

	return '로그인 성공';
}

 

두 번째 케이스를 보겠습니다.

아래 코드도 줄여보겠습니다.

function 오늘하루(condition, weather, isJob) {
	if (condition === 'GOOD') {
		공부();
		게임();
		유튜브보기();

		if (weather === 'GOOD') {
			운동();
			빨래();
		}

		if (isJob === 'GOOD') {
			야간업무();
			조기취침();
		}
	}
}

/최상위에 거르는 로직을 만들면 헤깔리지 않게 됩니다.

function 오늘하루(condition, weather, isJob) {
	// EARLY RETURN으로 흐름 끊기
	if (condition !== 'GOOD') {
		return;
	}

	공부();
	게임();
	유튜브보기();

	if (weather !== 'GOOD') {
		return;
	}
	
	운동();
	빨래();

	if (isJob !== 'GOOD') {
		return;
	}
	
	야간업무();
	조기취침();
}

 

결론

하나의 의존성이 많은 로직 묶고 있을 땐 early return으로 코드를 분리하면 흐름이 간단해집니다.

early return을 의식적으로 사용해보세요.

 

5-8. 부정 조건문 지양하기

아래와 같이 부정 조건문을 쓰지 않기를 추천드립니다.

 

이유

1. 생각을 여러번 해야 할 수 있습니다.

2. 프로그래밍 언어 자체로 if문이 처음부터 오고 true부터 실행 시킵니다.

  if문을 통상적으로 먼저 쓸땐 조건식이 true가 먼저 들어갈거란 기대가 있습니다.

 

하나의 예시로 isNaN을 들겠습니다.

const isCondition = true;
const isNotCondition = false;

if (!isCondition) {
	console.log('거짓인 경우에만 실행')
}

if (isNotCondition) {
	console.log('거짓인 경우에만 실행')
}

isNaN은 it Not a Number로서 숫자가 아니다를 말합니다.

아래처럼 쓸 때 헤깔리게 됩니다.

if (isNaN(3)) {
	console.log('Not a Number')
} else {
	console.log('Number')
}

숫자일 때만 로직이 동작하게 만들려면

한번 더 뒤집어야 합니다.

if ( !isNaN(3) )

이럴 바엔 차라리 아래처럼 함수를 만들어서 기능을 위임하는게 덜 헤깔립니다.

function isNumber(num) {
	return !Number.isNaN(num) && typeof num === 'number'
}

if (isNumber(3)) {
	console.log('숫자입니다!')
}

 

Q. 그럼 부정 조건문은 언제 사용할까요? (부정 조건 예외)

1. EARLY RETURN을 할 때 

2. 유효성 검증할 때 (Form Validation)

3. 보안 혹은 검사하는 로직

 

이 외에는 부정 조건문을 사용하는 것을 지양하는게 좋습니다.

헤깔리는 코드보다 명시적은 코드를 쓰면 좋겠습니다.

 

5-9. Default Case 고려하기

JS는 사용자와 상호작용하는 언어라고 생각합니다.

아래의 함수에서 sum()만 호출해도 어떠한 기본값을 갖게 하고 싶을 때도 있습니다.

function sum(x, y) {
	return x + y;
}

sum(100, 200);

위의 코드를 단축평가로 보완 합니다. 이러면 코드를 더욱 안전하게 사용할 수 있습니다.

function sum(x, y) {
	x = x || 1
	y = y || 1;

	return x + y;
}

sum(); // 2

 

아래는 요소를 만들어 내는 함수입니다.

그러나 항상 인자값을 넣기 귀찮을 때가 있습니다.

그럴 때 기본 값을 넣어주면 좋습니다.

function createElement(type, height, width) {
	const element = document.createElement(type);

	element.style.height = height;
	element.style.width = width;

	return element;
}

createElement('div', 100, 200) // 아이 귀찮아
createElement('div') // 이것만 넣고싶어

// 단축 평가로 Defualt Case 넣기
function createElement(type, height, width) {
	const element = document.createElement(type || 'div');

	element.style.height = height || 100;
	element.style.width = width || 100;

	return element;
}
createElement() // === createElement('div', 100, 100)

 

세 번째 케이스를 보겠습니다.

switch case에서는 default case가 필요없을 거라 생각할 수 있습니다.

요일은 항상 월~일 중에 존재합니다.

그럼에도 불구하고 누군가 switch key값을 잘못 넣을 때가 있습니다.

그럼 큰일 나겠죠?

function registerDay(userInputDay) {
	switch (userInputDay) {
		case '월요일': // some code
		case '화요일': // some code
		case '수요일': // some code
		case '목요일': // some code
		case '금요일': // some code
		case '토요일': // some code
		case '일요일': // some code
	}
}

e.target.value = '월ㄹ요일'; // 잘못된 정보
registerDay(e.target.value);

그렇다면 아래 처럼 에러처리를 해줘야 합니다.

function registerDay(userInputDay) {
	switch (userInputDay) {
        case '월요일': // some code
        case '화요일': // some code
        case '수요일': // some code
        case '목요일': // some code
        case '금요일': // some code
        case '토요일': // some code
        case '일요일': // some code
        default:
        	throw Error('입력값이 유효하지 않습니다.');
	}
}

 

네 번째 케이스는 리액트에서 Switch라는 구문 입니다.

3 번째 Route에서 Edge Case를 고려해 default 값을 줘서  대비해줬습니다.

const Root = () => (
	<Router history={browserHistory}>
		<Switch>
			<Route exact path="/" component={App} />
			<Route path="/welcome" component={Welcome} />
			<Route component={NotFound} />
		</Switch>
	</Router>
);

 

마지막 케이스는 parseInt 메서드 구문입니다.

parsrInt 메서드의 2번째인자는 기본값이 없습니다. 꼭 인자를 명시적으로 넣어줘야합니다.

function safeParseInt(number, radix) {
	return parseInt(number, radix || 10);
}

safeParseInt(1000) // 1000

 

결론

사용자의 실수를 방지하기 위해서 Defualt Case를 넣는 의식적인 훈련을 해야합니다.

 

5-10. 명시적인 연산자 사용 지향하기

프로그래밍을 공부할 때 우선순위를 많이 외우고 사용해야 할까요?

아닙니다. 그것보다 연산자 우선순위를 안전하게 사용할 수 있는 방법을 추구하는게 좋습니다.

항상 괄호 ( )를 달아서 사용해 주면 도움이 됩니다.

그렇지 않으면 항상 연산 우선순위에 대해 생각하게 되어 비효율적인 코드가 됩니다.

ex) ( 몸무게 / (신장 * 신장) )

참조 : 연산자 우선순위 - JavaScript | MDN (mozilla.org)

아래의 코드처럼 우선순위를 명시적으로 정해주는게 좋습니다.

if ((isLogin && token) || user) { }

아래의 전위/후위 연산자도 헤깔리는 경우가 있습니다.

let number;

function decrement(number) {
	number--;
	--number;
}

function increment(number) {
	++number;
	number++;
}

비동기나 루프문 안에서 증감같은 연산자가 들어갈 때도 우선순위가 헤깔립니다.

코드가 보기 힘들어 집니다.

또 오류가 생겼을 때 예측하기가 힘들어서 디버깅이 어려워집니다.

 

setTimeout(() => {
	
}, timeout)
whilte(condition) {
	number++;
}

Redux가 추구하는 가치는 예측 가능한 상태 컨테이너입니다.

우리도 항상 예측하기 쉬운 코드를 작성해야합니다. 즉 시간여행을 하며 디버깅이 가능해야합니다.

그래서 위의 예제를 아래 처럼 바꾸는게 좋다는 것입니다.

 let number;

function decrement() {
	number = number - 1;
	
}

function increment() {
	number = number + 1;
	
}

 

요약

1. 예측가능하고 디버깅 하기 쉬운 코드를 작성해야합니다.

2. 연사자 우선 순위는 괄호를 이용해서 명시적으로 나타냅니다. (((x + y) * z) && w)

3. 증감연산자는 명시적으로 +1 / 1로 대체해서 사용합니다.

 

5-11. Nullish coalescing operator

nul 병합 연산자 라고도 합니다.

이 문법은 비교적 최근 문법이기 때문에

예전에 나온 legacy나 브라우저는 안돌아갈 수가 있습니다.

그럴경우엔 따로 pollyfill을 준비해 줘야합니다.

그게 아닌경우엔 어떻게 활용할 수 있는지 보겠습니다.

 

null병합연산자는 사용할 때 주의해야할 점이 많습니다.

아래는 인자의 좌항이 truthy / falsy인 경우에 따라 값이 정해집니다.

만약 height 와 width를 명시적으로 0값을 줄 때에도 원치않게 default 값이 들어가게 됩니다.

function createElement(type, height, width) {
	const element = document.createElement(type || 'div');

	element.style.height = String(height || 10) + 'px';
	element.style.width = String(width || 10) + 'px';

	return element;
}

const el = createElement('div', 0, 0); // <div style="height: 10px; width: 10px;"></div>

숫자 0은 falsy에 해당 되기 때문입니다. 0 || '10' => '10'

거짓같은 값 참조 : 거짓같은 값 - 용어 사전 | MDN (mozilla.org)

를 보면 null 과 undefined는 falsy라는게 납득이 되지만 나머지는 데이터로써 값으로 평가받고 싶을 때가 있습니다.

바로 이럴 때 사용할 수 있는게 null병합 연산자 입니다.

📌type ?? 'div' 에서  ??의 좌측이 null이거나 undefined일때만 작동합니다.

function createElement(type, height, width) {
	const element = document.createElement(type ?? 'div');

	element.style.height = String(height ?? 10) + 'px';
	element.style.width = String(width ?? 10) + 'px';

	return element;
}

const el = createElement('div', 0, 0); // <div style="height: 0px; width: 0px;"></div>

 

null병합 연산자를 남용하면 부작용이 발생합니다.

아래의 코드에 대해서 테스트 해보겠습니다.

function helloWorld(message) {
	if (!message) {
		return 'Hello! World';
	}

	return 'Hello! ' + (message || 'World');
}

function helloWorld(message) {
	return 'Hello! ' + (message || 'World');
}

console.log(helloWorld()) // Hello! World
console.log(helloWorld('BOB')) // Hello! BOB
console.log(helloWorld(undefined)) // Hello! World
console.log(helloWorld(0)) // Hello! World

0 이 들어올 때는 값으로 평가 하고 싶을땐?

null병합 연산자를 사용합니다.

function helloWorld2(message) {
	return 'Hello! ' + (message ?? 'World');
}

console.log(helloWorld2(0)) // Hello! 0

 

세 번째 케이스는 문서를 같이 보겠습니다.

Nullish coalescing operator (??) - JavaScript | MDN (mozilla.org)

아래는 왜 오류가 날까요?

논리 연산자와 null병합 연산자는 혼합해서 쓸 수 없습니다.

사람들이 실수를 많이 할 수 있다는 이유로 제약을 걸었습니다.

null || undefined ?? "foo"; // raises a SyntaxError
true || undefined ?? "foo"; // raises a SyntaxError

이런 경우엔 해결이 됩니다.

console.log((null || undefined) ?? "foo"); // foo

 

/**
 * @see - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator#no_chaining_with_and_or_or_operators
 */
function getUserName(isLogin, user) {
  return isLogin && user ?? user.name;
}

 

function onToggleModal(isShow) {
	return isShow ?? false;
}

 

결론

null병합 연산자는 사용하기 편리하지만 실수를 유도 할 수 있습니다.

 

 

5-12. 드모르간의 법칙

아래의 코드에서 로그인 성공을 출력하기위해 조건을 걸었습니다.

하지만 로그인 실패를 출력하기 위해서 조건을 수정해야하는 경우가 있습니다.

이때 참고 할 수 있는게 드모르간의 법칙 입니다.

/*  드모르간의 법칙 공식
** true is not true
** false is not false
*/
if (!(A && B)) {
}

if (!A || !B) {
}
/////////////////////////
if (!(A || B)) {
}

if (!A && !B) {
}

 

/**
 * 드모르간의 법칙
 */
const isValidUser = true;	//서버에서 받아옴 (재활용할 수 밖에 없음)
const isValidToken = true;	//서버에서 받아옴 (재활용할 수 밖에 없음)

if (isValidToken && isValidUser) {
	console.log('로그인 성공!');  // '로그인 성공'
}
if (!(isValidToken && isValidUser)) {
	console.log('로그인 실패!');  // '로그인 실패'
}

if (!isValidToken || !isValidUser) {
	console.log('로그인 실패!');  // '로그인 실패'
}

'WEB > JavaScript' 카테고리의 다른 글

[클린코드 For JS] 7. 객체 다루기  (0) 2022.06.30
[클린코드 For JS] 6. 배열  (0) 2022.06.29
[클린코드 For JS] 4. 경계  (0) 2022.06.27
[클린코드 For JS] 3. 타입  (0) 2022.06.27
[클린코드 For JS] 1. 소개 & 2. 변수  (0) 2022.06.27
댓글
다크모드
Document description javascript psychology
더보기 ,제목1 태그 호버