리액트에서는 기본적으로 페이지의 특정 요소에 변화가 일어날 때 해당 부분만 렌더링이 되어 사용자에게 보여지는데요.
이때 특정 상태나 상황에 따라 렌더링을 시켜 사용자에게 보여지게 하거나, 그 반대로 보여지지 않게 할 수 있습니다. 이는 조건문과 같은 조건부 연산자
를 활용하는 방법인데요. 아래에서 한번 그 방법들을 소개해 드리겠습니다.
가장 기본적인 방법으로, 조건에 따라 다른 컴포넌트를 렌더링하는 방법인데요. 코드의 길이가 길어지기 때문에 잘 사용되지 않는 방법이기도 합니다.
function App() { const [count, setCount] = useState(0); const [input, setInput] = useState(""); const onClickButton = (value) => { setCount(count + value); }; // useState의 count 변수를 이용해 조건문을 이용해 반환할 DOM 요소를 결정 짓는 함수를 정의 const renderMessage = () => { if (count > 0) { return <h2>카운터가 증가했습니다! 현재 값: {count}</h2>; } else if (count < 0) { return <h2>카운터가 감소했습니다! 현재 값: {count}</h2>; } else { return <h2>카운터가 0입니다.</h2>; } }; return ( <div className="App"> <h1>Simple Counter</h1> <section> <input value={input} onChange={(e) => setInput(e.target.value)} /> </section> <section> <Controller onClickButton={onClickButton} /> </section> {/* 페이지가 렌더링 될 때마다 count 변수의 변화에 따라 보여지는 DOM 요소도 함수 안에 정의된 각각의 DOM요소들과 같아질것임. */} {renderMessage()} </div> ); } export default App;
자바스크립트의 논리 연산자를 활용하여 조건에 따라 컴포넌트를 렌더링할 수 있습니다. 예를 들어, && 연산자의 단락 평가의 특성을 사용하면 조건이 true일 때만 컴포넌트를 렌더링할 수 있는데요.
주로 특정 상태를 조건의 전면 코드로, 해당 특정 상태가 변화될 때마다 렌더링 될 DOM의 요소를 후면 코드로 배치하여 사용되며, 주로 모달창, 온오프와 같은 기능에 많이 활용됩니다. (리액트에서 가장 많이 활용되는 방법이기도 합니다.)
function App() { const [count, setCount] = useState(0); const onClickButton = (value) => { setCount(count + value); }; return ( <div className="App"> <h1>Simple Counter</h1> <section> <Controller onClickButton={onClickButton} /> </section> {/* count가 0보다 클 때만 메시지를 표시 */} {count > 0 && <h2>카운터가 증가했습니다! 현재 값: {count}</h2>} </div> ); }
삼항 연산자를 이용하여 특정 상태가 참의 결과, 또는 거짓의 결과를 보일 때 그에 준하는 DOM 요소를 반환하도록 하는 방법으로, 렌더링 될 요소가 두 개 일 때 주로 활용됩니다.
그래서 논리 연산자가 주로 온오프를 담당했다면, 삼항 연산자의 경우 두 개 중 하나의 컴포넌트를 상황에 따라 렌더링 할 때 주로 사용합니다.
function App() { const [count, setCount] = useState(0); const onClickButton = (value) => { setCount(count + value); }; return ( <div className="App"> <h1>Simple Counter</h1> <section> <Controller onClickButton={onClickButton} /> </section> {/* 삼항 연산자를 사용하여 카운터 값에 따른 메시지 표시 */} <h2>{count > 0 ? `카운터가 증가했습니다! 현재 값: ${count}` : `카운터가 ${count}입니다.`}</h2> </div> ); } export default App;
2개 이상의 여러 조건이 필요한 경우 switch 문을 사용할 수 있습니다. if-else문과 사용법은 비슷하며, 리액트에서는 그리 많이 사용되지 않는 방법입니다.
function App() { const [count, setCount] = useState(0); const onClickButton = (value) => { setCount(count + value); }; const renderMessage = () => { switch (true) { case count > 0: return <h2>카운터가 증가했습니다! 현재 값: {count}</h2>; case count < 0: return <h2>카운터가 감소했습니다! 현재 값: {count}</h2>; default: return <h2>카운터가 0입니다.</h2>; } }; return ( <div className="App"> <h1>Simple Counter</h1> <section> <Controller onClickButton={onClickButton} /> </section> {/* switch 문을 사용하여 메시지 렌더링 */} {renderMessage()} </div> ); }
또한 리액트에서는 배열과 같은 반복적인 요소, 그러니까 반복자(iterator) 의 특성을 가진 요소를 활용해 요소가 가진 아이템들을 반복적으로 렌더링 하는 방법을 제공하고 있는데요.
이는 주로 서버에서 데이터를 제공 받아 화면에 뿌려줄 때 개발자가 일일이 컴포넌트를 짜야만 하는 불편함을 많이 덜어줄 수 있는, 리액트의 핵심 스킬이라고도 할 수 있습니다.
이러한 방법을 사용하기 위해서는 대상이 되는 앞서 말씀드린대로 iteraotr 특성을 가져야 하기에, 주로 배열 안에 하나 이상의 객체 정보를 뿌려줄 때 사용됩니다.
리액트에서 요소의 반복 렌더링에 가장 많이 쓰이는 함수입니다. 특히 map은 매 사이클마다 반환값이 있다는 점에서 반환값이 없는 foreach보다 더 많이 사용되기에, return 키워드를 이용한 리액트 렌더링 방식 특성상 map이 리액트에서 가장 많이 활용되는 목록 렌더링 함수라고 할 수 있습니다.
물론 for문을 돌릴수도 있겠으나, 코드의 간결함과 가독성을 고려했을 때 for문은 거의 쓰이지 않는 방식이기도 하죠.
주의할 점은 map을 이용할때 생성되는, 최종적으로 정보가 표시(렌더링)되는 DOM 요소에는 key 속성을 필수로 지정해 주어야 하며, 이때의 key 속성에는 각 아이템에 대한 고유한 id 값과 같은 변수들이 지정됩니다.
function UserList() { // 하드코딩된 사용자 데이터 const users = [ { id: 1, name: 'Alice', email: 'alice@example.com' }, { id: 2, name: 'Bob', email: 'bob@example.com' }, { id: 3, name: 'Charlie', email: 'charlie@example.com' }, { id: 4, name: 'David', email: 'david@example.com' }, { id: 5, name: 'Eve', email: 'eve@example.com' }, ]; return ( <div> <h1>User List</h1> <ul> {/* users 배열을 반복하여 렌더링 */} {users.map((user) => ( <li key={user.id}> {user.name} - {user.email} </li> ))} </ul> {/* map을 사용하지 않을 경우 <h1>User List</h1> <ul> <li>{users[0].name} - {users[0].email}</li> {/* Alice */} <li>{users[1].name} - {users[1].email}</li> {/* Bob */} <li>{users[2].name} - {users[2].email}</li> {/* Charlie */} <li>{users[3].name} - {users[3].email}</li> {/* David */} <li>{users[4].name} - {users[4].email}</li> {/* Eve */} </ul> */} </div> ); }
주로 주어진 조건에 따라 배열의 요소를 필터링하여 새로운 배열을 생성하는 데 사용됩니다. 리액트에서 filter
를 사용하여 특정 조건에 맞는 요소만 렌더링할 수 있습니다.
특히 filter
는 검색 정보와 같이 특정 키워드로 검색했을때 해당 키워드에 맞는 컴포넌트를 DOM 요소로 렌더링 하여 사용자에게 보여주거나, 삭제할 특정 컴포넌트 정보를 제외한 나머지 컴포넌트의 정보를 저장하는 방식으로 특정 컴포넌트를 삭제하는 효과를 주고자 할 때 사용됩니다.
// 1. 검색 기능을 구현할 때 사용되는 filter 함수 예시 import React, { useState } from 'react'; const users = [ { id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }, { id: 3, name: 'Charlie' }, { id: 4, name: 'David' }, { id: 5, name: 'Eve' }, ]; const UserList = () => { const [searchTerm, setSearchTerm] = useState(''); // 사용자가 입력한 키워드로 필터링을 한 뒤 그 결과를 filteredUseres에 저장 const filteredUsers = users.filter(user => user.name.toLowerCase().includes(searchTerm.toLowerCase()) ); return ( <div> <h1>User List</h1> <input type="text" placeholder="Search by name" value={searchTerm} {/* 사용자가 input에 매번 타이핑을 칠때마다 setSearchTerm 메서드가 실행되어 상태를 searchTerm에 세팅 */} onChange={(e) => setSearchTerm(e.target.value)} /> <ul> {/* 렌더링 될 때마다 searchTerm의 상태(사용자가 입력한 값)에 맞는 값들이 filteredUsers에 저장된 후 map 함수를 이용해 사용자에게 표시 */} {filteredUsers.map(user => ( <li key={user.id}>{user.name}</li> ))} </ul> </div> ); };
// 2. 특정 컴포넌트를 삭제하는 예시 const usersData = [ { id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }, { id: 3, name: 'Charlie' }, ]; const UserList = () => { const [users, setUsers] = useState(usersData); const deleteUser = (id) => { // 클릭한 사용자 id를 제외한 나머지 사용자만 필터링하여 상태 업데이트 const updatedUsers = users.filter(user => user.id !== id); setUsers(updatedUsers); }; return ( <div> <h1>User List</h1> <ul> {/* 요소를 클릭하면 해당 요소에 저장되어 있는 함수가 발동 그 해당 요소의 id값을 제외한 나머지 값만 updatedUsers에 저장된 후 setUsers 함수를 이용해 users에 다시 저장한 뒤 users.map을 이용해 제 렌더링 */} {users.map(user => ( <li key={user.id}> {user.name} <button onClick={() => deleteUser(user.id)}>Delete</button> </li> ))} </ul> </div> ); };
Fragment는 조건부 렌더링 중 map을 이용한 함수 사용에서 반환하고자 하는 DOM의 요소가 두 개 이상일 때 해당 요소들을 그룹화 하는 용도로 사용됩니다.
물론 Fragment를 사용하지 않고 <div>
태그로도 묶어서 렌더링 할수도 있지만, <div>
태그가 여러 개일 때에는 코드의 가독성이 떨어질 수 있기에, 앞서 말씀드린 경우에는 Fragment를 사용합니다. (경우에 따라 축약형인 <></>
태그도 사용할 수 있습니다.)
import React, { Fragment, useState } from 'react'; const users = [ { id: 1, name: 'Alice', email: 'alice@example.com' }, { id: 2, name: 'Bob', email: 'bob@example.com' }, { id: 3, name: 'Charlie', email: 'charlie@example.com' }, { id: 4, name: 'David', email: 'david@example.com' }, { id: 5, name: 'Eve', email: 'eve@example.com' }, ]; const UserList = () => { const [searchTerm, setSearchTerm] = useState(''); // 사용자가 입력한 키워드로 필터링 const filteredUsers = users.filter(user => user.name.toLowerCase().includes(searchTerm.toLowerCase()) ); return ( <> <h1>User List</h1> <input type="text" placeholder="Search by name" value={searchTerm} onChange={(e) => setSearchTerm(e.target.value)} /> <ul> {filteredUsers.length > 0 ? ( // 필터링된 결과가 있을 때 filteredUsers.map(user => ( {/* 추가 정보를 표시하기 위해 Fragment를 사용 */} <Fragment key={user.id}> <li>{user.name}</li> {/* user.id가 1이 아닌 경우만 정보가 표시되는데, 그 중에서도 user.email이 있는 경우는 그 경우를, 없는 경우 'No email available'을 렌더링 하게 표시 */} {user.id !== 1 && <p>Email: {user.email ? user.email : 'No email available'}</p>} </Fragment> )) ) : ( // 필터링된 결과가 없을 때 <li>No users found</li> )} </ul> </> ); };