[리액트] && 조건부 렌더링

비얌·2023년 4월 9일
6
post-thumbnail

🧹 개요

리액트 공식 문서 - 조건부 렌더링 파트를 읽다가 궁금한 점이 생겼다.

문서에서는 &&로 조건부 렌더링을 할 때 왼쪽에 숫자를 넣지 말라고 한다.

의도하지 않은 결과가 나올 수 있다는 것이 그 이유인데, 숫자형인 messageCount를 이용해서 messageCount && <p>New messages</p>라는 코드를 작성한다고 하자.

메시지를 받으면(messageCount가 1 이상이면) 화면에 'New messages'가 화면에 출력되고, 받은 메시지가 없으면(messageCount가 0이면) 아무것도 출력되지 않는 것을 기대하지만, 정작 화면에는 '0'이 출력된다.

전혀 의도하지 않은 결과가 나오는 것을 알 수 있다. 처음에 왜 이런 결과가 나오는지 이해를 못해서 찾아봤고, 자바스크립트 AND 연산자인 &&에 대한 이해가 없어서 이해하지 못했다는 것을 알게 되었다.

그래서 이번 포스팅에서는 자바스크립트 AND 연산자와 &&를 활용한 조건부 렌더링에 대해 자세히 알아보았다.



🔎 잘못 알고 있었던 것

자바스크립트 AND 연산자 &&에 대해 이해하지 못한 상태에서 &&를 사용한 리액트 조건부 렌더링을 썼었다.

조건 && DOM을 단순히 조건이 참이면 DOM을 렌더링하고, 조건이 거짓이면 아무것도 렌더링하지 않는 것으로만 이해하고 있었기에 &&의 왼쪽이 조건이 아니라 숫자형 0일 때 "0"만 달랑 나오는 것을 이해하지 못했다.(이때까지 저 조건이 무조건 true 혹은 false였다는 것도 자각하지 못했다)

조건부 렌더링에서 쓰이는 &&은 리액트 고유 문법이 아니라 자바스크립트 문법이다. 그래서 먼저 자바스크립트 AND 연산자 &&를 이해하고, 그 후에 왜 && 왼쪽에 숫자형을 넣으면 안되는를 알아보려고 한다.



✅ AND 연산자 &&

자바스크립트의 AND 연산자인 &&를 알아보자.

자바스크립트의 논리 연산자인 &&모던자바스크립트 튜토리얼에서 자세히 설명하고 있다.

&&를 사용하는 방법은 아래와 같다. 나는 피연산자가 불리언일 때와 아닐 때, 그리고 피연산자가 2개일 때와 여러개일 때 많이 헷갈렸다. 그래서 여러가지 경우를 예로 들어보았다.

  • 연산자: 연산에 사용되는 기호
  • 피연산자: 연산의 대상이 되는 값
  • (예시) 수식 width * height에서 연산자는 *이고 피연산자는 widthheight이다.

피연산자가 2개이고 불린형일 때

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; 

피연산자가 2개이고 불린형이 아닐 때

result = value1 && value2; // 피연산자가 2개이고 불린형이 아닐 때

AND 연산을 할 때, 피연산자의 타입에 제한이 없어 불린형 외의 타입을 써도 된다. 불린형 외의 다른 타입이어도 &&는 첫번째 falsy를 찾아 반환하고 만약 끝까지 falsy가 없으면 마지막 값을 반환한다. 라는 공식은 똑같이 적용된다.

연산을 할 때 각 피연산자는 불린형으로 변환되어 평가된다. 예를 들어 만약 피연산자가 숫자형이면 불린형으로 바뀌어 true나 false가 된다. 즉 undefined && 1false && true로, 1 && 0true && 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을 반환함


피연산자가 3개 이상일 때

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>

&&를 사용해서 조건부 렌더링을 할 때, 알아야 할 점이 두가지 있다.

  1. && 오른쪽에 쓰이는 모든 jsx 코드는 true로 평가된다.
    왜냐하면 falsy에 해당하는 false/0/-0/0n/""/null/undefined/NaN이 아니기 때문이다.

  2. && 왼쪽에 쓰이는 조건(true 혹은 false)은 렌더링했을 때 화면에 아무것도 보이지 않는다.
    따라서 조건 && jsx코드에서 조건이 false이면 false가 반환되는데, 반환되어도 화면에 아무것도 보이지 않는다.


isLoggedIn이 true와 false일 때, 이렇게 두가지의 경우로 나누어 연산 과정을 살펴보자.

isLoggedIn && <p>로그인 되었습니다!</p>
  1. isLoggedIn이 true일 때
    첫번째 falsy를 찾아 반환하자 👉 isLoggedIn이 falsy인가? 👉 아님 👉 <p>로그인 되었습니다!</p>이 falsy인가? 👉 아님 👉 falsy를 찾지 못했으니 마지막 피연산자를 반환하자 👉 <p>로그인 되었습니다!</p>이 반환되고 화면에 '로그인 되었습니다!'단락이 그려짐

  2. 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한 값을 찾는다는 부분에서 항상 막혔고 결국 완전히 이해하지 못했던 기억이 난다. 아무리 봐도 이해가 안돼서 답답했는데, 이번에는 이해한 것 같아 뿌듯하다. 그리고 평소에 조건부 렌더링에 생각 없이 썼던 &&의 특성을 이해하게 되어서 기쁘다.

profile
🐹강화하고 싶은 기억을 기록하고 공유하자🐹

0개의 댓글