리액트 공식 문서 - 조건부 렌더링 파트를 읽다가 궁금한 점이 생겼다.
문서에서는 &&
로 조건부 렌더링을 할 때 왼쪽에 숫자를 넣지 말라고 한다.
의도하지 않은 결과가 나올 수 있다는 것이 그 이유인데, 숫자형인 messageCount를 이용해서 messageCount && <p>New messages</p>
라는 코드를 작성한다고 하자.
메시지를 받으면(messageCount가 1 이상이면) 화면에 'New messages'가 화면에 출력되고, 받은 메시지가 없으면(messageCount가 0이면) 아무것도 출력되지 않는 것을 기대하지만, 정작 화면에는 '0'이 출력된다.
전혀 의도하지 않은 결과가 나오는 것을 알 수 있다. 처음에 왜 이런 결과가 나오는지 이해를 못해서 찾아봤고, 자바스크립트 AND 연산자인 &&
에 대한 이해가 없어서 이해하지 못했다는 것을 알게 되었다.
그래서 이번 포스팅에서는 자바스크립트 AND 연산자와 &&를 활용한 조건부 렌더링에 대해 자세히 알아보았다.
자바스크립트 AND 연산자 &&
에 대해 이해하지 못한 상태에서 &&
를 사용한 리액트 조건부 렌더링을 썼었다.
조건 && DOM
을 단순히 조건이 참이면 DOM을 렌더링하고, 조건이 거짓이면 아무것도 렌더링하지 않는 것으로만 이해하고 있었기에 &&
의 왼쪽이 조건이 아니라 숫자형 0일 때 "0"만 달랑 나오는 것을 이해하지 못했다.(이때까지 저 조건이 무조건 true
혹은 false
였다는 것도 자각하지 못했다)
조건부 렌더링에서 쓰이는 &&
은 리액트 고유 문법이 아니라 자바스크립트 문법이다. 그래서 먼저 자바스크립트 AND 연산자 &&
를 이해하고, 그 후에 왜 &&
왼쪽에 숫자형을 넣으면 안되는를 알아보려고 한다.
&&
자바스크립트의 AND 연산자인 &&
를 알아보자.
자바스크립트의 논리 연산자인
&&
는 모던자바스크립트 튜토리얼에서 자세히 설명하고 있다.
&&
를 사용하는 방법은 아래와 같다. 나는 피연산자가 불리언일 때와 아닐 때, 그리고 피연산자가 2개일 때와 여러개일 때 많이 헷갈렸다. 그래서 여러가지 경우를 예로 들어보았다.
- 연산자: 연산에 사용되는 기호
- 피연산자: 연산의 대상이 되는 값
- (예시) 수식
width * height
에서 연산자는*
이고 피연산자는width
와height
이다.
result = boolean1 && boolean2; // 피연산자가 2개이고 불린형일 때
'그리고(and)'는 모두 참일 때만 참이고, '또는(or)'는 하나라도 참이면 참이다 라는 개념은 많은 사람들에게 익숙할 것이다. 그래서 연산자가 AND이고 피연산자가 2개이며 모두 불리언인 수식을 먼저 알아보려고 한다.
우리가 아는 개념처럼 AND(&&
)도 피연산자가 모두 참일 때만 참이고 하나라도 거짓이면 거짓이다. 그래서 아래를 보면 두 피연산자가 모두 true
일 때만 true
를 반환하고 그 외의 경우에는 false
를 반환한다.
const result1 = true && true; // true
const result2 = false && true; // false
const result3 = true && false; // false
const result4 = false && false; // false
여기까지는 배경 지식이 없어도 알 수 있는 것인데, 사실 &&
는 첫번째 falsy를 찾아 반환하고 만약 끝까지 falsy가 없으면 마지막 값을 반환한다.
- falsy: 불리언으로 평가할 때 false가 되는 값으로 false/0/-0/0n/""/null/undefined/NaN 이렇게 여덟 가지가 있다.
- truthy: 불리언으로 평가할 때 true가 되는 값으로, falsy한 값을 뺀 모든 값이 truthy하다.
즉, 위의 결과는 아래의 과정을 거친 것이다.
// true: 끝까지 falsy를 찾지 못해서 마지막 true를 반환한다
const result1 = true && true;
// false: 첫번째 피연산자가 false여서 이를 반환한다
const result2 = false && true;
// false: 마지막 피연산자가 false여서 이를 반환한다
const result3 = true && false;
// false: 첫번째 피연산자가 false여서 이를 반환한다
const result4 = false && false;
result = value1 && value2; // 피연산자가 2개이고 불린형이 아닐 때
AND 연산을 할 때, 피연산자의 타입에 제한이 없어 불린형 외의 타입을 써도 된다. 불린형 외의 다른 타입이어도 &&
는 첫번째 falsy를 찾아 반환하고 만약 끝까지 falsy가 없으면 마지막 값을 반환한다. 라는 공식은 똑같이 적용된다.
연산을 할 때 각 피연산자는 불린형으로 변환되어 평가된다. 예를 들어 만약 피연산자가 숫자형이면 불린형으로 바뀌어 true나 false가 된다. 즉 undefined && 1
은 false && true
로, 1 && 0
은 true && false
로, 2 && '0'
은 true && true
로 변환되어 평가되는 것이다.
여기서 추가적으로 알아야 할 것은, 어떤 불리언으로 변환되든 반환되는 값은 원본값이라는 것이다. 즉, 변환된 값인 true나 false가 아닌 원본값이 반환된다.
undefined && 1
👉 false && true
로 변환됨 👉 첫번째 falsy값을 찾았는데 그게 undefined임 👉 undefined 반환함
1 && 0
👉 true && false
로 변환됨 👉 첫번째 falsy 값을 찾았는데 그게 0임 👉 0을 반환함
2 && '0'
👉 true && true
로 변환됨 👉 falsy한 값을 찾으려고 했으나 없음 👉 마지막 값인 '0'을 반환함
위에서 피연산자가 2개이고 불린형일 때의 경우 true와 false가 반환되는데, 그 이유는 원본값 자체가 true나 false이기 때문이다.
const result1 = true && true; // true
const result2 = false && true; // false
const result3 = true && false; // false
const result4 = false && false; // false
false && true
👉 그 자체로 false && true
임 👉 첫번째 falsy값을 찾았는데 그게 false임 👉 false를 반환함
true && true
👉 그 자체로 true && true
임 👉 falsy한 값을 찾으려고 했으나 없음 👉 마지막 값인 true을 반환함
result = value1 && value2 && value3 && value4; // 피연산자가 3개 이상일 때
피연산자가 3개 이상일 때도 &&
는 첫번째 falsy를 찾아 반환하고 만약 끝까지 falsy가 없으면 마지막 값을 반환한다는 공식은 동일하게 적용된다.
다만 추가적으로 알아야 하는 것은 &&
가 첫번째 falsy를 찾으면 거기서 평가를 멈춘다는 것이다. 아래와 같은 표현식에서 &&
는 첫번째 falsy를 찾았으니 result는 falsy1이 될 것이다. 그러면 그 뒤에 있는 truthy3과 falsy2는 true인지 false인지 평가하지 않는다.
result = truthy1 && truthy2 && falsy1 && truthy3 && falsy2
그래서 위의 과정을 살펴보면 아래와 같다.
1. truthy1를 평가함 👉 true임
2. truthy2를 평가함 👉 true임
3. falsy1을 평가함 👉 false임 👉 falsy1을 반환함, 끝
이렇게 표현식을 평가하는 도중에 평가 결과가 확정된 경우 나머지 평가 과정을 생략하는 것을 단락 평가(단축 평가)라고 한다.
특정 값이 유효할때에만 어떤 값을 조회하는 작업을 해야 할 때 단락 평가를 위해 &&
를 사용하면 좋다.
아래의 예시를 살펴보자. hamster가 존재하지 않을 때 hamster에서 age라는 속성을 가져오면 에러가 뜬다(+ 객체가 존재하는데 age라는 속성이 없으면 age 속성을 가져왔을 때 에러가 뜨지 않고 undefined라고 뜸) 따라서 객체가 존재하면 그 객체의 속성을 가져오라는 뜻으로 &&
를 씀으로써 에러를 피할 수 있다.
const dog = {
name: '해피',
age: 13
};
const cat = {
name: '애옹'
}
const hamster = null
const getAge = (animal) => {
return animal.age
}
console.log(getAge(dog)); // 13
console.log(getAge(cat)); // undefined
console.log(getAge(hamster)); // "TypeError: Cannot read properties of null (reading 'age')
console.log(hamster && getAge(hamster)); // null
// 단락 평가와는 상관 없지만 추가적으로 알아보았다
console.log(getAge(turtle)); // "ReferenceError: turtle is not defined
console.log(turtle && getAge(turtle)); // "ReferenceError: turtle is not defined
&&
위에서 알아본 개념을 리액트의 조건부 렌더링에 적용해볼 수 있다.
조건부 렌더링이란, 조건에 따라 화면을 다르게 그리는 것이다.
리액트에서 조건부 렌더링을 할 때 &&
를 사용해서 조건부 렌더링을 하는 것을 흔하게 볼 수 있는데, 조건 && jsx코드
형식으로 사용한다.
예를 들면 isLoggedIn이라는 조건이 true이면 화면에 '로그인 되었습니다!'라는 단락을 그리고 false이면 아무것도 그리지 않는 상황에서 아래와 같이 사용할 수 있다.
isLoggedIn && <p>로그인 되었습니다!</p>
&&
를 사용해서 조건부 렌더링을 할 때, 알아야 할 점이 두가지 있다.
&& 오른쪽에 쓰이는 모든 jsx 코드는 true로 평가된다.
왜냐하면 falsy에 해당하는 false/0/-0/0n/""/null/undefined/NaN이 아니기 때문이다.
&& 왼쪽에 쓰이는 조건(true 혹은 false)은 렌더링했을 때 화면에 아무것도 보이지 않는다.
따라서 조건 && jsx코드
에서 조건이 false이면 false가 반환되는데, 반환되어도 화면에 아무것도 보이지 않는다.
isLoggedIn이 true와 false일 때, 이렇게 두가지의 경우로 나누어 연산 과정을 살펴보자.
isLoggedIn && <p>로그인 되었습니다!</p>
isLoggedIn이 true일 때
첫번째 falsy를 찾아 반환하자 👉 isLoggedIn
이 falsy인가? 👉 아님 👉 <p>로그인 되었습니다!</p>
이 falsy인가? 👉 아님 👉 falsy를 찾지 못했으니 마지막 피연산자를 반환하자 👉 <p>로그인 되었습니다!</p>
이 반환되고 화면에 '로그인 되었습니다!'단락이 그려짐
isLoggedIn이 false일 때
첫번째 falsy를 찾아 반환하자 👉 isLoggedIn
이 falsy인가? 👉 맞음 👉 이것을 반환하자 👉 isLoggedIn이 반환됨 👉 isLoggedIn은 false라서 화면에 그려지지 않음 👉 화면에 아무것도 안보임
따라서 어떤 조건이면 jsx를 그리고, 어떤 조건에 부합하지 않으면 jsx를 그리지 않는 조건부 렌더링을 할 때 &&
를 사용할 수 있다.
&&
왼쪽에 숫자를 넣으면 안된다드디어 궁금했던 부분을 설명할 수 있게 되었다.
리액트 공식 문서 - 조건부 렌더링 파트에 따르면 &&
왼쪽에 숫자를 넣지 말라고 한다.
예를 들면 아래와 같이 작성하면 메시지가 존재하면 '새 메시지가 왔어요!'를 화면에 그릴 수 있다고 생각할 수 있다.
messageCount && <p>새 메시지가 왔어요!</p>
만약 messageCount가 0이라면 화면에 '새 메시지가 왔어요!'가 그려지지 않아야 할 것이다. 하지만 의도와는 다른 결과가 나오는데, 메시지가 오지 않아도(0개일 때) 화면에 '0'이 나타난다.
아무것도 나타나지 않거나, '새 메시지가 왔어요!'만 화면에 나타나게 하겠다는 의도와 다른 결과가 나오는 것이다.
왜냐하면, messageCount가 숫자형이고 0일 때 messageCount는 truthy가 아니라 falsy로 판단되기 때문이다. 연산 과정은 아래와 같다.
첫번째 falsy를 찾아 반환하자 👉 messageCount
이 falsy인가? 👉 맞음 👉 이것을 반환하자(다음 피연산자인 <p>새 메시지가 왔어요!</p>
를 평가하기 전에 평가를 중단함) 👉 messageCount이 반환됨 👉 messageCount은 0임 👉 화면에 0만 그려짐
따라서 공식문서에 있던 && 왼쪽에 숫자를 넣지 말라
는 건 이러한 과정으로 의도되지 않은 결과가 나올 수 있으니 쓰지 말라는 뜻이었다.
예전에 자바스크립트 문법을 공부하며 논리 연산자 &&
와 ||
를 여러번 공부하고 정리했었는데, &&
를 쓸 때 첫번째 falsy한 값을 찾는다는 부분에서 항상 막혔고 결국 완전히 이해하지 못했던 기억이 난다. 아무리 봐도 이해가 안돼서 답답했는데, 이번에는 이해한 것 같아 뿌듯하다. 그리고 평소에 조건부 렌더링에 생각 없이 썼던 &&
의 특성을 이해하게 되어서 기쁘다.