2023.02.01 Confitional Rendering 조건부 렌더링
React에서는 원하는 동작을 캡슐화하는 컴포넌트를 만들 수 있습니다. 이렇게 하면 애플리케이션의 상태에 따라서 컴포넌트 중 몇 개만을 렌더링할 수 있습니다.
Condition은 조건,상태 라는 뜻을 갖고 있습니다.
컴퓨터 프로그래밍에서의 컨디션은 조건을 의미합니다.
따라서 공식 영문 용어인 Conditional Rendering을 조건에 따른 렌더링이라고 해석하고
일반적으로 조건부 렌더링이라고 줄여서 부릅니다.
React에서 조건부 렌더링은 JavaScript에서의 조건 처리와 같이 동작합니다.
if 나 조건부 연산자 와 같은 JavaScript 연산자를 현재 상태를 나타내는 엘리먼트를 만드는 데에 사용하세요.
그러면 React는 현재 상태에 맞게 UI를 업데이트할 것입니다.
결론적으로 조건부 렌더링은 어떠란 조건에 따라서 렌더링이 달라지는 것을 의미합니다.
조건문의 결과는 true아니면 false가 나오는데 이 결과에 따라서 렌더링을 다르게 하는 것을 조건부 렌더링이라고 정의하는 것입니다.
에를 들어 조건문의 값이 true이면 버튼을 보여주고
false이면 버튼을 가리는 것도 하나의 조건부 렌더링이라고 할 수 있습니다.
function UserGreeting(props) {
return <h1>Welcome back!</h1>;
}
function GuestGreeting(props) {
return <h1>Please sign up.</h1>;
}
UserGreeting 컴포넌트는 이미 회원인 사용자에게 보여줄 메시지를 출력하는 컴포넌트이고,
GuestGreeting 컴포넌트는 아직 가입하지 않은 게스트 사용자에게 보여줄 메시지를 출력하는 컴포넌트 입니다.
이제 사용자의 로그인 상태에 맞게 위 컴포넌트 중 하나를 보여주는 Greeting 컴포넌트를 만듭니다. 다음 코드에는 조건부 렌더링을 사용하여 이를 구현하고 있습니다.
function Greeting(props) {
const isLoggedIn = props.isLoggedIn;
if (isLoggedIn) {
return <UserGreeting />;
}
return <GuestGreeting />;
}
const root = ReactDOM.createRoot(document.getElementById('root'));
// Try changing to isLoggedIn={true}:
root.render(<Greeting isLoggedIn={false} />);
위에 나오는 Greeting 컴포넌트는 isLoggedIn이라는 변수의 값이 true에 해당되는 값이면
UserGreeting 컴포넌트를 리턴하고,
그렇지 않으면 GestGreeting 컴포넌트를 리턴하는 컴포넌트입니다.
props로 들어오는 isLoggedIn의 값에 따라서 화면에 출력되는 결과가 달라지게 됩니다.
이처럼 조건에 따라 렌더링의 결과가 달라지도록 하는 것을 조건부 렌더링이라고 부릅니다.
보통의 프로그래밍 언어에서는 참,거짓을 구분하기 위해서 Boolean 형태의 자료형이 존재하고,
그 값을 true와 false 둘 중에 하나가 됩니다.
그리고 boolean과 자료형이 다른 값을 비교하게 되면 오류가 발생하게 됩니다.
하지만 자바스크립트에는 true는 아니지만 true로 여겨지는 truthy,
false는 아니지만 false로 여겨지는 falsy가 존재합니다.
조건부 렌더링을 사용하다 보면 렌더링해야 될 컴포넌트를 변수처럼 다루고 싶을 때가 있습니다.
이때 사용할 수 있는 방법이 바로 엘리먼트 변수 입니다.
아래 예제를 살펴봅시다.
function LoginButton(props) {
return (
<button onClick={props.onClick}>
Login
</button>
);
}
function LogoutButton(props) {
return (
<button onClick={props.onClick}>
Logout
</button>
);
}
이 컴포넌트는 현재 상태에 맞게 이나 을 렌더링합니다. 또한 이전 예시에서의 도 함께 렌더링합니다.
class LoginControl extends React.Component {
constructor(props) {
super(props);
this.handleLoginClick = this.handleLoginClick.bind(this);
this.handleLogoutClick = this.handleLogoutClick.bind(this);
this.state = {isLoggedIn: false};
}
handleLoginClick() {
this.setState({isLoggedIn: true});
}
handleLogoutClick() {
this.setState({isLoggedIn: false});
}
render() {
const isLoggedIn = this.state.isLoggedIn;
let button;
if (isLoggedIn) {
button = <LogoutButton onClick={this.handleLogoutClick} />;
} else {
button = <LoginButton onClick={this.handleLoginClick} />;
}
return (
<div>
<Greeting isLoggedIn={isLoggedIn} />
{button}
</div>
);
}
}
위 코드를 보면 isLoggedIn의 값에 따라서 button이라는 변수에 컴포넌트를 대입하는 것을 볼 수 있습니다.
그리고 이렇게 컴포넌트가 대입된 변수를 return에 넣어 실제로 컴포넌트가 렌더링이 되도록 만들고 있습니다.
실제로는 컴프넌트로부터 생성된 리액트 엘리먼트를 넣는 것입니다.
이처럼 엘리먼트를 변수처럼 저장해서 사용하는 방법을 엘리먼트 변수라고 부릅니다.
변수를 선언하고 if를 사용해서 조건부로 렌더링 하는 것은 좋은 방법이지만 더 짧은 구문을 사용하고 싶을 때가 있을 수 있습니다.
여러 조건을 JSX 안에서 인라인(inline)으로 처리할 방법 몇 가지를 아래에서 소개하겠습니다.
인라인(Inline)이라는 단어는 라인(line)의 안(in)이라는 의미를 갖고 있습니다.
말 그대로 코드를 별도로 분리된 곳에 작성하지 않고 해당 코드가 필요한 곳 안에 직접 집어넣는다는 뜻입니다.
그래서 인라인 조건이라고 하면 조건문을 코드 안에 집어넣는 것이라는 뜻을 갖고 있습니다.
if문을 필요한 곳에 직접 집어 넣어 사용하는 방법
if문과 동일한 효과를 내기 위해 &&라는 논리 연산자를 사용
true && expression -> expression
false && expression -> false
JSX 안에는 중괄호를 이용해서 표현식을 포함 할 수 있습니다.
그 안에 JavaScript의 논리 연산자 &&를 사용하면 쉽게 엘리먼트를 조건부로 넣을 수 있습니다.
function Mailbox(props) {
const unreadMessages = props.unreadMessages;
return (
<div>
<h1>Hello!</h1>
{unreadMessages.length > 0 &&
<h2>
You have {unreadMessages.length} unread messages.
</h2>
}
</div>
);
}
const messages = ['React', 'Re: React', 'Re:Re: React'];
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Mailbox unreadMessages={messages} />);
JavaScript에서 true && expression은 항상 expression으로 평가되고
false && expression은 항상 false로 평가됩니다.
따라서 && 뒤의 엘리먼트는 조건이 true일때 출력이 됩니다.
조건이 false라면 React는 무시하고 건너뜁니다.
falsy 표현식을 반환하면 여전히 && 뒤에 있는 표현식은 건너뛰지만 falsy 표현식이 반환된다는 것에 주의해주세요.
아래 예시에서, 코드의 결과는 화면에 아무것도 아 나오는 것이 아니라 count의 값인 0이 들어가서
<div>0</div>이 render 메서드에서 반환됩니다.
render() {
const count = 0;
return (
<div>
{count && <h1>Messages: {count}</h1>}
</div>
);
}
인라인 if-else는 필요한 곳에 if-else문을 직접 넣어서 사용하는 방법
조건문의 값에 따라 다른 엘리먼트를 보여줄 때 사용한다.
이를 위해서 삼항 연산자를 사용한다.
조건문 ? 참일 경우 : 거짓일 경우
엘리먼트를 조건부로 렌더링하는 다른 방법은 조건부 연산자인 condition ? true: false를 사용하는 것입니다.
아래의 예시에서는 짧은 구문을 조건부로 렌더링합니다.
function UserStatus(props){
return (
<div>
The user is <b>{props.isLoggedIn ? 'currently' : 'not'}</b> logged in.
</div>
);
}
가독성은 좀 떨어지지만, 더 큰 표현식에도 이 구문을 사용할 수 있습니다.
(문자열이 아닌 엘리먼트를 넣어서 사용할 수도 있습니다)
import { useState } from "react";
function LoginControl(props) {
const [isLoggedIn, setIsLoggedIn] = useState(false);
const handleLoginClick = () => {
setIsLoggedIn(true);
}
return (
<div>
<Greeting isLoggedIn={isLoggedIn} />
{isLoggedIn
? <LogoutButton onClick={handleLogoutClick} />
: <LoginButton onClick={handleLoginClick} />
}
</div>
)
}
여기에서는 isLoggedIn의 값이 true인 경우에 LogoutButton를 출력하고
false인 경우 LoginButton을 출력하게 됩니다.
이처럼 인라인 If-Else는 조건에 따라 각기 다른 엘리먼트를 렌더링하고 싶을 때 사용합니다.
JavaScript와 마찬가지로, 가독성이 좋다고 생각하는 방식을 선택하면 됩니다.
또한 조건이 너무 복잡하다면 컴포넌트를 분리하기 좋을 때 일 수도 있다는 것을 기억하세요.
컴포넌트를 렌더링하고 싶지 않을 때에는 null을 리턴하면 된다.
리액트에서는 null을 리턴하면 렌더링되지 않기 때문이다.
가끔 다른 컴포넌트에 의해 렌더링될 때 컴포넌트 자체를 숨기고 싶을 때가 있을 수 있습니다.
이때는 렌더링 결과를 출력하는 대신 null을 반환하면 해결할 수 있습니다.
아래의 예시에서는 <WarningBanner />가 props.warning의 값에 의해서 렌더링됩니다.
prop이 false라면 null을 리턴하고 컴포넌트는 렌더링하지 않게 됩니다.
function WarningBanner(props) {
if (!props.warn) {
return null;
}
return (
<div className="warning">
Warning!
</div>
);
}
WarningBanner 컴포넌트를 실제로 사용하는 아래 코드를 보도록 합시다.
import { useState } from "react";
function MainPage(props) {
const [showWarning, setShowwarning] = useState(false);
const handleToggleClick = () => {
setShowwarning(prevShowWarnig => !prevShowWarnig);
}
return (
<div>
<WarningBanner warning={showWarning} />
<button onClick={handleToggleClick}>
{showWarning ? '감추기' : '보이기'}
</button>
</div>
)
}
위 코드에 나오는 Page 컴포넌트는 showWarning이라는 state의 값을 WarningBanner 컴포넌트의 props로 전달하여
showWarning의 값에 따라 경고문을 표시하거나 또는 표시하지 않게됩니다.
이처럼 리액트에서는 특정 컴포넌트를 렌더링하고 싶지 않을 경우 null을 리턴하면 된다는 것을 기억하세요.
참고로 컴포넌트의 render( ) 메서드로부터 null을 반환하는 것은 생명주기 메서드 호출에 영향을 주지 않습니다.
그 예로 componentDidUpdate( )는 계속해서 호출되게 됩니다.
사용자의 로그인 여부를 나타내는 툴바 컴포넌트를 직접 만들어 보겠습니다.
import React from "react";
const styles = {
wrapper: {
padding: 16,
display: "flex",
flexDirection: "row",
borderBottom: "1px solid grey",
},
greeting: {
marginRight: 8,
},
};
function Toolbar(props) {
const { isLoggedIn, onClickLogin, onClickLogout } = props;
return (
<div style={styles.wrapper}>
{isLoggedIn && <span style={styles.greeting}>환영합니다!</span>}
{isLoggedIn ? (
<button onClick={onClickLogout}>로그아웃</button>
) : (
<button onClick={onClickLogin}>로그인</button>
)}
</div>
);
}
export default Toolbar;
Toolbar 컴포넌트는 사용자의 로그인 여부를 나타내는 isLoggedIn이라는 값을 props로 받아서 조건부 렌더링을 사용하여 환영 메시지를 표시하거나 감추고,
로그인 로그아웃 버튼을 보여주는 역할을 합니다.
&& 연산자와 ?(삼항 연산자)연산자 등 두 가지 종류의 연산자를 사용하여 조건부 렌더링을 구현했습니다.
이렇게 만든 Toolbar 컴포넌트를 사용하는 컴포넌트를 만들어 보겠습니다.
import React, { useState } from "react";
import Toolbar from "./Toolbar";
function LandingPage(props) {
const [isLoggedIn, setIsLoggedIn] = useState(false);
const onClickLogin = () => {
setIsLoggedIn(true);
};
const onClickLogout = () => {
setIsLoggedIn(false);
};
return (
<div>
<Toolbar
isLoggedIn={isLoggedIn}
onClickLogin={onClickLogin}
onClickLogout={onClickLogout}
/>
<div style={{ padding: 16 }}>소플과 함께하는 리액트 공부!</div>
</div>
);
}
export default LandingPage;
LandingPage 컴포넌트는 useState( ) 훅을 사용해서 사용자의 로그인 여부를 자체적으로 관리합니다.
그리고 이 값을 Toolbar 컴포넌트에 전달하여 로그인 여부레 따라 툴바에 적절한 사용자 인터페이스가 표시되도록 합니다.
수정
import React, { Profiler } from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import LandingPage from './chapter_09/LandingPage';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<LandingPage />
</React.StrictMode>
);
reportWebVitals();
로그인 버튼 클릭 시 아래와 같이 바뀐다
조건문을 코드 안에 집어넣는 것
인라인 IF
인라인 IF-ELSE