[번역] JSX에서 && 대신 삼항 연산자를 사용하세요

I
Inkyu Oh

Front-End2026.01.08

Kent C. Dodds - 2021년 7월 1일


이 코드의 문제점은 무엇일까요?
function ContactList({ contacts }) {
return (
<div>
<ul>
{contacts.length &&
contacts.map((contact) => (
<li key={contact.id}>
{contact.firstName} {contact.lastName}
</li>
))}
</ul>
</div>
)
}
잘 모르시겠나요? 다른 질문을 드려보겠습니다. 만약 contacts[]라면 위 코드는 어떻게 될까요? 맞습니다! 0이 렌더링될 것입니다!
저는 예전에 PayPal에서 근무할 때 이 실수를 프로덕션에 배포한 적이 있습니다. 농담이 아닙니다. 바로 이 페이지에서였습니다.
연락처가 있는 PayPal 페이지

연락처가 없는 사용자가 접속했을 때, 그들은 다음과 같은 화면을 보게 되었습니다.
연락처 대신 0이 표시된 PayPal 페이지

네, 전혀 즐거운 일이 아니었죠... 왜 이런 일이 발생했을까요? JavaScript가 0 && anything을 평가할 때, 0은 거짓(falsy)이므로 결과는 항상 0이 되며, &&의 오른쪽은 평가조차 하지 않기 때문입니다.
해결책은 무엇일까요? 삼항 연산자(Ternary)를 사용하여 거짓인 경우에 무엇을 렌더링할지 명시적으로 작성하는 것입니다. 우리의 경우 아무것도 렌더링하지 않는 것이었으므로 null을 사용하는 것이 완벽합니다.
function ContactList({ contacts }) {
return (
<div>
<ul>
{contacts.length
? contacts.map((contact) => (
<li key={contact.id}>
{contact.firstName} {contact.lastName}
</li>
))
: null}
</ul>
</div>
)
}
그럼 이 코드는 어떨까요? 여기서는 무엇이 잘못되었을까요?
function Error({ error }) {
return error && <div className="fancy-error">{error.message}</div>
}
잘 모르시겠나요? 만약 errorundefined라면 어떻게 될까요? 그런 경우 다음과 같은 에러가 발생할 것입니다.
Uncaught Error: Error(...): Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null.
또는 프로덕션 환경에서는 다음과 같은 메시지를 볼 수도 있습니다.
react-dom.production.min.js:13 Uncaught Invariant Violation: Minified React error #152; visit https://reactjs.org/docs/error-decoder.html?invariant=152&args[]=f for the full message or use the non-minified dev environment for full errors and additional helpful warnings.
여기서의 문제는 undefined && anything은 항상 undefined로 평가된다는 점입니다.

그래서 무엇이 문제인가요?

사실 이 두 경우 모두의 근본적인 문제는 조건부 렌더링(Conditional rendering)을 하기 위해 &&를 사용하고 있다는 점입니다. 다르게 말하면, 조건부로 인자를 전달하기 위해 &&를 사용하고 있는 것입니다. 기억하세요, JSX는 React.createElement 호출을 위한 단순한 구문적 추상화(Syntactic abstraction)일 뿐입니다. 따라서 이는 다음과 같이 코드를 작성하려는 것과 같습니다.
function throwTheCandy(candyNames) {
for (const candyName of candyNames) {
throwCandy(candyName)
}
}

throwTheCandy(candies.length && candies.map((c) => c.name))
이것은 말이 되지 않죠? 타입이 완전히 꼬여버렸습니다. 그럼에도 React가 이를 허용하는 이유는 0을 렌더링하는 것이 유효하기 때문입니다.
다행히 TypeScript는 실수로 undefined를 반환하는 두 번째 예시와 같은 상황을 피하도록 도와줄 수 있습니다. 하지만 그러한 보호 계층을 넘어, 우리의 의도를 좀 더 명확하게 표현해 보는 것은 어떨까요? 무언가를 조건부로 수행하고 싶다면, 논리 AND 연산자(&&)를 남용하지 마세요.
우리는 흔히 if 조건문에서 &&를 사용하여 "이 두 값이 모두 참(truthy)이면 이 일을 수행하고, 그렇지 않으면 하지 마라"는 의미로 사용하기 때문에, 이것이 직관에 어긋나는 것처럼 보일 수 있습니다. 불행히도 우리의 사용 사례에서 && 연산자는 두 값이 모두 참이 아닐 경우 거짓인 쪽의 값을 반환하는 "특징"을 가지고 있습니다.
0 && true // 0
true && 0 // 0
false && true // false
true && '' // ''

실제 분기 구문을 사용하세요

따라서 저는 JSX에서 &&(또는 ||)를 남용하기보다는 삼항 연산자(condition ? trueConsequent : falseConsequent)나 심지어 if 문(그리고 미래에는 아마도 do 표현식(do expressions))과 같은 실제 분기 구문 기능을 사용할 것을 강력히 권장합니다.
저는 개인적으로 삼항 연산자를 선호하지만, if 문을 더 선호하신다면 그것도 좋습니다. if 문을 사용하여 연락처 목록 예시를 작성하면 다음과 같습니다.
function ContactList({ contacts }) {
let contactsElements = null
if (contacts.length) {
contactsElements = contacts.map((contact) => (
<li key={contact.id}>
{contact.firstName} {contact.lastName}
</li>
))
}

return (
<div>
<ul>{contactsElements}</ul>
</div>
)
}
개인적으로는 삼항 연산자가 더 읽기 쉽다고 생각합니다(동의하지 않으신다면, "가독성"은 주관적이며 어떤 것의 가독성은 다른 무엇보다 익숙함과 훨씬 더 관련이 깊다는 점을 기억하세요). 무엇을 선택하든 여러분의 자유지만, 버그는 만들지 마세요.
실제 분기 구문을 사용하는 또 다른 장점은 테스트 커버리지 도구가 테스트에서 특정 분기를 누락했을 때 이를 감지하고 표시할 수 있다는 점입니다.

결론

!!contacts.length && ...contacts.length > 0 && ..., 또는 Boolean(contacts.length) && ...를 사용하여 연락처 문제를 해결할 수 있다는 것을 잘 알고 있습니다. 하지만 저는 여전히 렌더링을 위해 논리 AND 연산자를 남용하지 않는 것을 선호합니다. 저는 삼항 연산자를 사용하여 명시적으로 표현하는 것을 선호합니다.
마지막으로, 제가 오늘 이 두 컴포넌트를 작성해야 한다면 다음과 같이 작성할 것입니다.
function ContactList({ contacts }) {
return (
<div>
<ul>
{contacts.length
? contacts.map((contact) => (
<li key={contact.id}>
{contact.firstName} {contact.lastName}
</li>
))
: null}
</ul>
</div>
)
}
function Error({ error }) {
return error ? <div className="fancy-error">{error.message}</div> : null
}
행운을 빕니다!
0
9

댓글

?

아직 댓글이 없습니다.

첫 번째 댓글을 작성해보세요!

Inkyu Oh님의 다른 글

더보기

유사한 내용의 글