[React] 기초 | 특정 조건에서의 컴포넌트 렌더링 방법

Re_Go·2024년 10월 4일
0

React

목록 보기
11/12
post-thumbnail

1. 조건부 렌더링

리액트에서는 기본적으로 페이지의 특정 요소에 변화가 일어날 때 해당 부분만 렌더링이 되어 사용자에게 보여지는데요.

이때 특정 상태나 상황에 따라 렌더링을 시켜 사용자에게 보여지게 하거나, 그 반대로 보여지지 않게 할 수 있습니다. 이는 조건문과 같은 조건부 연산자를 활용하는 방법인데요. 아래에서 한번 그 방법들을 소개해 드리겠습니다.

- if-else 문

가장 기본적인 방법으로, 조건에 따라 다른 컴포넌트를 렌더링하는 방법인데요. 코드의 길이가 길어지기 때문에 잘 사용되지 않는 방법이기도 합니다.


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;

- switch 문

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>
  );
}

2. 목록 렌더링

또한 리액트에서는 배열과 같은 반복적인 요소, 그러니까 반복자(iterator) 의 특성을 가진 요소를 활용해 요소가 가진 아이템들을 반복적으로 렌더링 하는 방법을 제공하고 있는데요.

이는 주로 서버에서 데이터를 제공 받아 화면에 뿌려줄 때 개발자가 일일이 컴포넌트를 짜야만 하는 불편함을 많이 덜어줄 수 있는, 리액트의 핵심 스킬이라고도 할 수 있습니다.

이러한 방법을 사용하기 위해서는 대상이 되는 앞서 말씀드린대로 iteraotr 특성을 가져야 하기에, 주로 배열 안에 하나 이상의 객체 정보를 뿌려줄 때 사용됩니다.

- map

리액트에서 요소의 반복 렌더링에 가장 많이 쓰이는 함수입니다. 특히 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를 사용하여 특정 조건에 맞는 요소만 렌더링할 수 있습니다.

특히 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

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>
    </>
  );
};
profile
인생은 본인의 삶을 곱씹어보는 R과 타인의 삶을 배워 나아가는 L의 연속이다.

0개의 댓글