클린코드 자바스크립트(2) - 분기
1. 분기 다루기
1) 값식문
- 값식문이란, jsx에서 본 것과 같이 { }안에 식이 오직 값으로만 이루어진 문법을 말한다. 이 때, 값 이외의 다른 것(조건문, 수식 등)을 사용하면 에러가 발생한다.
// jsx 분기문을 작성할 때, 참고
<p>
{(()=> {
// jsx 분기문 다룰때 참고하자
switch (color) {
case 'red':
return '#FF0000';
case 'green':
return '#00FF00';
default:
return '#FFFFFF';
}
})
}
</p>
2) 삼항연산자
- 삼항연산자를 사용할 때는 덕지덕지 사용하기 보다는 ‘3가지 기준’을 갖고 작성하는게 좋다.
- 기준 1. 분기를 다루고 값을 바로 저장하는 경우
const welcomeMessage = (isLogin) => {
const name = isLogin ? name : "이름 없음"
return `어서오세요 ${name}님`
}
- 기준 2. 연속된 분기를 다룰 때는 삼항 연산자보다 switch문이 나을 수 있음
// bad case 1
// 가독성이 떨어짐
const example = () => {
return condition1 ? value1
: condition2 ? value2
: condition3 ? value3
: value4;
}
// bad case2
// else if, else의 사용은 낭비 면에서 피하는 것이 좋다
const example = () => {
if (condition1) { return value1; }
else if (condition2) { return value2; }
else if (condition3) { return value3; }
else { return value4; }
}
// good case - switch 문으로 교체
const example = () => {
switch (res) {
case value:
break;
default:
break;
}
}
- 기준 3. 삼항 연산자는 되도록 무언가의 값이 만들어지는 경우에 사용하는 것이 좋음
// bad case
// 실행에는 문제가 없지만, 삼항연산자에 실행문으로 함수를 사용하는 것은 삼항연산자의 의도와 맞지 않음
const alertMessage = (isAdult) => {
isAdult > 19
? window.alert("성인입니다.")
: window.alert("미성년입니다.")
}
// good case
// 실행문으로 함수를 사용하는 경우, if - else 가 나을 수 있음 (early return 은 뒤에서 다룸)
const alertMessage = (isAdult) => {
if(isAdult > 19) {
window.alert("성인입니다.");
} else {
window.alert("미성년입니다.");
}
}
3) Truthy & Falsy
- Truthy 모음 (원본 링크)
if (true)
if ({}) // 주의 1. 빈 배열을 Truthy 하게 봄
if ([]) // 주의 2. 빈 객체를 Truthy 하게 봄
if (42)
if ("0")
if ("false")
if (new Date())
if (-42)
if (12n)
if (3.14)
if (-3.14)
if (Infinity)
if (-Infinity)
- Falsy 모음 (원본 링크)
if (false)
if (null)
if (undefined)
if (0)
if (-0)
if (0n)
if (NaN)
if ("")
- 실무에서 적용해볼만 Validation 관련 코드 (단, 엄격한 검사는 아니니 주의하자)
const printName = (name) => {
// if (name === null && name === undefined)
if (!name) {
return '사람이 없네요.'
}
return '안녕하세요 ' + name + '님';
}
console.log(printName()); // 사람이 없네요
4) 단축평가
-
단축평가는 논리연산자(and, or)로 볼 수도 있지만, 값을 바로 할당할 때는 아래와 같은 개념으로 이해하는 게 좋다.
-
&&: false 를 만나면 평가를 중지한다.
true && true && '도달 O' // '도달 O'
true && false && '도달 X' // '도달 X'
- ll: true 를 만나면 평가를 중지한다. 예를 들어 A ll B 가 있다면, A가 false일 때 B를 저장한다.
false && false && '도달 O' // '도달 O'
true || true || '도달 X' // '도달 X'
- 예제 1
// 원본 코드
const fetchData = (data) => {
if (data) {
return data;
} else {
return 'Fetching...';
}
}
// 예제 1 - 삼항연산자 good case
const fetchData = (data) => {
return data ? data : 'Fetching...'
}
// 예제 2 - 단축평가 good case
const fetchData = (data) => {
return data || 'Fetching...'
}
- 예제 2
// 원본 코드
const favoriteDog = (dog) => {
let favoriteDog;
if (someDog) {
favoriteDog = dog;
} else {
favoriteDog = '냐옹';
}
return favoriteDog + '입니다'
}
// 예제 2 - 삼항연산자
const favoriteDog = (dog) => {
return someDog ? `${dog} 입니다` : `${냐옹} 입니다`;
}
// 예제 2 - 단축평가
const favoriteDog = (dog) => {
return (someDog || '냐옹') + '입니다';
}
- 예제 3
// 원본 코드
const getActiveUserName = (user, isLogin) => {
if (isLogin && user) {
return user.name
} else {
return '이름없음'
}
}
// 예제 3 - 단축평가
const getActiveUserName = (user, isLogin) => {
if (isLogin && user) {
return user.name || '이름없음'
}
}
5) else if 피하기
- if - else if - else 로 길어지는 경우 switch로 대체하거나, if로 코드를 명료하게 작성하는게 좋다. 왜냐면, else if 는 else 동작 후 if가 동작하는 구조이기에 조건만 확실하다면, else는 낭비이기 때문이다.
const x = 1;
if (x > 0) {
console.log('x는 0과 같거나 크다'); // x는 0과 같거나 크다
} else if (x > 0) {
console.log('x는 0보보다 크다');
}
// else if <- else + if 로 대체 가능하며, 두 방식 모두 낭비이다
const x = 1;
if (x > 0) {
console.log('x는 0과 같거나 크다'); // x는 0과 같거나 크다
}
else {
if (x > 0) {
console.log('x는 0보보다 크다');
}
}
6) if - else 를 습관처럼 사용하는 걸 조심하자.
- 논리적으로 명료해서 사용하는 경우가 많지만, 스타일적으로나 함수가 여러기능을 수행하게 될 때 예상치 못한 결과가 나올 수 있다.
// bad case
const getActiveUserNAme = (user) => {
if (user.name) {
return user.name;
} else {
return '이름없음';
}
}
// good case - early return 으로 스타일적으로 개선
const getActiveUserNAme = (user) => {
if (user.name) {
return user.name;
}
return '이름없음';
}
// good case - 단축평가(||)로 스타일적으로 개선
const getActiveUserNAme = (user) => {
return user.name || '이름없음';
}
7) early return 을 사용하자
-
early return을 사용하면, 논리는 바뀌지 않으면서 가독성을 높일 수 있다.
-
예제 1
// early return 적용 전
const loginService = (isLogin, user) => {
// 로그인 여부
if (!isLogin) {
// 토큰 체크
if (!checkToken()) {
// 기가입 재확인
if (!user.nickname) {
return registerUser(user);
} else {
refreshToken();
return '로그인 성공';
}
} else {
throw new Error('No Token')
}
}
}
// early return 적용 후 예시
// 조건문 간의 의존성을 줄이는 효과가 있음
const loginService = (isLogin, user) => {
// 로그인이 되어있으면, Early Return
if (isLogin) {
return
}
if (!checkToken()) {
throw new Error('No Token');
}
if (!user.nickname) {
return registerUser(user);
}
refreshToken();
return '로그인 성공';
}
- 예제 2
// early return 적용 전 예시 -2
const Todo = (condition, weather, isJob) => {
if (condition === "GOOD") {
공부();
게임();
유튜브보기();
if (weather === "GOOD") {
운동();
빨래();
}
if (isJob === "GOOD") {
야간업무();
조기취침();
}
}
}
// early return 적용 후 예시 -2
// 최상위에 거르는 로직을 넣으면, 조건문 간의 의존성을 줄일 수 있다.
const Todo = (condition, weather, isJob) => {
// early return으로 상위 로직으로부터 하위로직들의 의존성이 줄어들었음
if (condition !== "GOOD") {
return
}
공부();
게임();
유튜브보기();
if (weather === "GOOD") {
운동();
빨래();
}
if (isJob === "GOOD") {
야간업무();
조기취침();
}
}
8) 부정조건문 지양하기
-
부정조건문을 습관적으로 사용하는 경우가 많은데, 아래와 같은 이유로 지양하는 편이 좋다.
- 이유 1. 생각을 여러번 해야할 수 있다. (예를 들어, isNan과 부정조건문을 함께 사용하는 경우)
- 이유 2. 프로그래밍 언어 적으로 봤을 때, if문이 처음부터 오고 true부터 실행시키는 구조로 만들어졌다.
// 부정조건문을 사용한 Bad case if (!Number.isNaN(3)) { console.log('숫자입니다') } // 부정조건문을 사용한 Good case const isNumber = (num) => { return !Number.isNan(num) && typeof num === 'number' } if (isNumber(num)) { console.log('숫자입니다') }
-
반대로 부정조건문을 사용하는 경우도 있다.
- 경우 1. Early return
- 경우 2. Form Validation
- 경우 3. 보안 혹은 검사하는 로직
9) Default Case 고려하기
- 다수의 parameters를 전달하는 경우, 객체를 사용하게 될 때나 여러 경우에 Default Case를 작성해버릇 하면 아래와 같은 효과를 얻을 수 있다.
- 효과 1. 사용자의 편의를 위해, 예를 들어 어떠한 값도 넘겨받지 않았을 때 동작하게 한다던지 또는 담당자나 기획자와 소통할 때도 정책과 관련된 기본값을 상의하기에도 좋다.
- 효과 2. 팀에서 사용하는 코어 라이브러리 개발을 하는 경우에도 유용하다
// bad case - 1
const sum = (x, y) => {
return x + y
}
// good case - 1
// 사용자가 parameters를 전달하지 않고 호출하더라도 결과를 얻어낼 수 있음
const sum = (x, y) => {
x = x || 0;
y = y || 0;
return x + y
}
// bad case - 2
// 어떠한 html tag가 반함되게 하는 라이브러리 배포 상황
// 이러한 상황에서 사용자는 매번 type, height, width를 넣어줘야 해서 불편하다고 할 수 있음
const createElement = (type, height, width) => {
const element = document.createElement(type);
element.style.height = height;
element.style.width = width;
return element;
}
// good case - 2
// default 를 작성하여 편의성 개선
const createElement = (type, height, width) => {
// 개선 1
const element = document.createElement(type) || 'div';
// 개선 2
element.style.height = height || 100;
element.style.width = width || 100;
return element;
10) Nullish coalescing operator (Null 병합 연산자)
- Null 병합 연산자(??)는 Null과 undefined를 평가하는데에만 사용하는 신규 연산자이다.
- 단, Null 병합 연산자는 실수를 유발할 수 있기 때문에 주의하자.
- 이유 1. 별 생각없이 ll 를 패스하고 ?? 를 사용할 수 있음
- 이유 2. null ll undefined ?? false 와 같이 () 없이 병렬로 사용하는 경우, js에서 코더의 실수를 막기위해 처리를 막아두었음
- 단순예제
const justNull = null;
const justFalsy = 0;
console.log(justFalsy || "Falsy"); // Falsy
console.log(justFalsy ?? "Falsy"); // 0
console.log(justNull || "Falsy"); // Falsy
console.log(justNull ?? "Falsy"); // Falsy
- 실전 케이스
// Null, undefined 와 나머지 Falsy(0, -0 등)를 구분못하는 경우
const 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;
}
createElement("div>", Null, undefined) // 정상, height, width 크기가 디폴트인 10px 요소가 생성
createElement("<div>", 0, 0) // 에러, height, width 크기가 10px인 요소가 생성
/* ------------------------------------------------------------------------------------------ */
// Null 병합 연산자로 Null, undefined만 구분
const 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;
}
createElement("div>", Null, undefined) // 정상, height, width 크기가 10px인 요소 생성
createElement("<div>", 0, 0) // 정상, height, width 크기가 0px인 요소가 생성
11) 드모르간의 법칙
- 드모르간의 법칙을 사용하면, 조건문의 논리구성을 비교적 쉽게 읽히도록 사용할 수 있어 유지보수에 좋다. 그리고 드모르간의 법칙을 이용하면, !()형태로 껍데기를 벗겨 이해하기 쉬운 코드로 개선할 수 있다.
true is not true
false is not false
// 드모르간 법칙 적용 전,
const isValidUser1 = true;
const isValidToken1 = true;
if (isValidUser1 && isValidToken1) {
console.log('로그인 성공!'); // 실행
}
if (!(isValidUser1 && isValidToken1)) {
console.log('로그인 실패!');
}
const isValidUser2 = true;
const isValidToken2 = false;
if (isValidUser2 && isValidToken2) {
console.log('로그인 성공!');
}
if (!(isValidUser2 && isValidToken2)) {
console.log('로그인 실패!'); // 실행
}
const isValidUser3 = true;
const isValidToken3 = false;
if (isValidUser3 || isValidToken3) {
console.log('로그인 성공!'); // 실행
}
if (!(isValidUser3 || isValidToken3)) {
console.log('로그인 실패!');
}
const isValidUser4 = false;
const isValidToken4 = false;
if (isValidUser4 || isValidToken4) {
console.log('로그인 성공!');
}
if (!(isValidUser4 || isValidToken4)) {
console.log('로그인 실패!'); // 실행
}
- 드모르간을 사용하여, 아래와 같이 !()형태의 껍대기를 벗겨냄으로써 코드를 이해하고 유지보수하는데 긍정적 효과를 얻을 수 있음.
// 드모르간 적용 후
const isValidUser2 = false;
const isValidToken2 = false;
if (!(isValidUser2 && isValidToken2)) {
console.log('로그인 실패!'); // 실행
}
// 드모르간의 법칙으로 껍데기()를 벗겨냄
if (!isValidUser2 || !isValidToken2) {
console.log('로그인 실패!'); // 실행
}
const isValidUser3 = false;
const isValidToken3 = false;
if (!(isValidUser3 || isValidToken3)) {
console.log('로그인 실패!'); // 실행
}
// 드모르간의 법칙으로 껍데기()를 벗겨냄
if (!isValidUser3 && !isValidToken3) {
console.log('로그인 실패!'); // 실행
}
다음에 다루게 될 것
- 배열
댓글남기기