[React] Component

노유성·2023년 5월 2일
0
post-thumbnail

🌞컴포넌트란?

컴포넌트(Component)는 React에서 UI를 구성하는 빌딩 블록으로, 화면에 표시할 수 있는 독립적인 모듈입니다. 각 컴포넌트는 특정 기능을 수행하거나 UI 요소를 렌더링하며, 이러한 컴포넌트들을 조합하여 원하는 UI를 구성할 수 있습니다.
React 컴포넌트는 다른 컴포넌트에서도 사용할 수 있는 재사용성(reusability)을 가집니다. 또한, 각 컴포넌트는 자체적으로 상태(state)를 관리하고, 이벤트 처리와 같은 동작을 수행할 수 있습니다.
React 컴포넌트는 클래스 컴포넌트(Class Component)와 함수형 컴포넌트(Functional Component)로 나뉩니다. 클래스 컴포넌트는 ES6 클래스를 사용하여 정의하며, render() 메소드에서 UI를 반환합니다. 함수형 컴포넌트는 함수를 사용하여 정의하며, UI를 반환하는 JSX 코드를 작성합니다.
React 컴포넌트는 프로젝트 규모가 크고 복잡해지는 상황에서도 유연하게 대응할 수 있는 구조를 제공합니다. 컴포넌트 기반의 개발 방식을 통해, 개발자들은 각 컴포넌트를 독립적으로 개발하고 테스트할 수 있으며, 이를 조합하여 전체 UI를 구성할 수 있습니다. 또한, 컴포넌트 단위로 재사용성을 높일 수 있어 코드의 재사용성과 유지보수성을 향상시키는 데에도 기여합니다. -chatGPT

쉽게 말해서 Component는 UI의 기본 구성 단위이다. 우리가 건물을 지을 때 건물을 한 번에 통째로 짓는 것도 아니고 반대로 모래알 하나하나를 다져가면서 짓는 것이 아니라 이미 구조화된 재료들을 조합해서 만들 듯이, 전체 UI도 Component라는 구조화된 재료를 이용해서 구성한다.

하나의 컴포넌트를 여러 컴포넌트로 분리하는데에는 완벽한 기준은 없지만 재사용이 가능한 부분을 컴포넌트화 하고, 기능이 다른 부분들은 분리하는 것이 일반적이다.

예를 들어, 기존의 소스 코드는

그림과 같이 하나의 컴포넌트로 구성되어 있다면 이를

여러 개의 컴포넌트로 분리하는 것이다. 해당 그림에서 리스트 컴포넌트도 좀더 잘개 쪼갤 수 있는데 바로 할 일 목록들을 각각 하나의 컴포넌트로 취급하는 것이다.

청소하기, 공부하기, 필요한 물건들 사기 들도 하나의 독립적인 컴포넌트로 만든다면 좀 더 다양한 기능을 구사할 수 있다.

⭐컴포넌트 분리하기

🪐기존 소스코드

 const [todoList, setTodoList] = useState([]);
  const [value, setValue] = useState("");


  const btnStyle = {
    color: "#fff",
    border: "none",
    padding: "5px 9px",
    borderRadius: "50%",
    cursor: "pointer",
    float: "right"
  }

  const getStyle = (completed) => {
    return {
      padding: "10px",
      borderBottom: "1px #ccc dotted",
      textDecoration: completed ? 'line-through' : 'none'
    }
  }
  
  const isNull = () => {
    return value === "";
  }

  const handleSubmit = (e) => {
    e.preventDefault();

    if (isNull())
      return;
    let newTodo = {
      id: Date.now(),
      title: value,
      completed: false
    };

    setTodoList([...todoList, newTodo]);
    setValue("");
  };

  const handleChange = (e) => {
    setValue(e.target.value);
  };
  
  const handleDelete = (id) => {
    let newTodoList = todoList.filter(item => {
      return item.id !== id;
    })
    setTodoList(newTodoList);
    // return newTodoList;
  }

  const handleComplete = (id) => {
    let newTodoList = todoList.filter(item => {
      if(item.id === id) {
        item.completed = !item.completed;
      }
      return item;
    })

    setTodoList(newTodoList);
  }
  
  const eventBubbing = () => {
    console.log('i\'m evnet bubbling+')
  }

    return (
      <div onSubmit={eventBubbing} className="container">
        <div className="todoBlock">
          <h1>할 일 목록</h1>
          {todoList.map((data) => (
            <div key={data.id} style={getStyle(data.completed)}>
              <input type="checkbox" defaultChecked={false} 
              onChange={() => handleComplete(data.id)}/>
              {data.title}
              <button style={btnStyle} onClick={() => handleDelete(data.id)}>X</button>
            </div>
          ))}

          <form onSubmit={(e) => handleSubmit(e)}>
            <input type="text" name="value" style={{ flex: '10', padding: '5px' }} value={value} onChange={handleChange} />
            <input type="submit" value="입력" className="btn" style={{ flex: '1' }} />
          </form>
        </div>
      </div>
    );
}

하나의 소스코드 즉, 하나의 컴포넌트에 모든 기능과 모든 UI가 들어가있는 소스코드이다. 여기서 특정 UI에게만 특정 기능을 수행하려고 하면 작업이 굉장히 난해해진다. 그래서 해당 컴포는트를 4개의 컴포넌트로 나누었다.

🪐App component

mport React, { useState } from "react";
import './App.css'
import Lists from "./components/Lists";
import Form from "./components/Form";


export default function App() {

  console.log("App component");

  const [todoList, setTodoList] = useState([]);

  const eventBubbing = () => {
    console.log('i\'m evnet bubbling+')
  }

  const handleRemoveClick = () => {
    setTodoList([]);
  }

  return (
    <div className="flex items-center justify-center w-screen h-screen bg-blue-300" onSubmit={eventBubbing}>
      <div className="w-full p-6 m-4 bg-white rounded shadow lg:w-3/4 lg:max-w-lg">
        <div className="flex justify-between mb-3">
          <h1>할 일 목록</h1>
          <button onClick={handleRemoveClick}>Delete all</button>
        </div>
        <Lists todoList={todoList} setTodoList={setTodoList} />
        <Form todoList={todoList} setTodoList={setTodoList} />

      </div>
    </div>
  );
}

전체 틀이다. 앞으로 만들 컴포넌트들을 담기에 컨테이너라고 부르기도 한다.

🪐Form component

export default function Form({todoList, setTodoList}) {
    const [value, setValue] = useState("");
    
    const isNull = () => {
        return value === "";
    }

    const handleChange = (e) => {
        setValue(e.target.value);
    };

    const handleSubmit = (e) => {
        e.preventDefault();

        if (isNull())
            return;
        let newTodo = {
            id: Date.now(), 
            title: value,
            completed: false
        };
        setTodoList([...todoList, newTodo]);
        setValue("");
    };

    return (
        <div>
            <form onSubmit={(e) => handleSubmit(e)}>
                <input 
                    type="text"
                    name="value" 
                    className='w-full px-3 py-2 mr-4 text-gray-500 border rounded shadow'
                    value={value} 
                    placeholder='해야할 일을 입력하세요.'
                    onChange={handleChange} />
                <input 
                    type="submit"
                    value="입력"
                    className='p-2 text-blue-400 border-2 border-blue-400 rounded hover:text-white hover:bg-blue-200' />
            </form>
        </div>
    )
}

입력과 제출을 하는 기능을 하는 컴포넌트이다.

🪐Lists component

import React from 'react'
import List from './List'
import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd';

const Lists = React.memo(({ todoList, setTodoList }) => {
    console.log("Lists component");

    // result는 결과가 담겨온다. console.log로 확인가능
    // 그 중 result의 destination property를 이용한다.
    const handleEnd = (result) => {
        console.log(result)

        // 변경된게 없으면 함수 종료
        if (!result.destination) return;

        // 불변성을 지켜주기 위해서 데이터를 카피해서 가져옴
        const newTodoList = todoList;

        // 재배열될 아이템을 잘라서 가져옴
        const [reorderdItem] = newTodoList.splice(result.source.index, 1);

        // 재배열된 아이템을 도착지 인덱스에 추가함 -> 위치 변경 완료
        newTodoList.splice(result.destination.index, 0, reorderdItem);
        setTodoList(newTodoList);

    }

    return (
        <div>
            <DragDropContext onDragEnd={handleEnd}>
                <Droppable droppableId='Unique-droppable-id' >
                    {(provided) => (
                        <div {...provided.droppableProps} ref={provided.innerRef}>
                            {todoList.map((data, index) => (
                                <Draggable
                                    key={data.id}
                                    draggableId={data.id.toString()}
                                    index={index}
                                > 
                                    {(provided, snapshot) => (
                                        <List
                                            key={data.id}
                                            id={data.id}
                                            title={data.title}
                                            completed={data.completed}
                                            todoList={todoList}
                                            setTodoList={setTodoList}
                                            provided={provided}
                                            snapshot={snapshot}
                                        />
                                    )}
                                </Draggable>
                            ))}
                            {provided.placeholder}
                        </div>
                    )}
                </Droppable>
            </DragDropContext>
        </div>
    )
}
)

export default Lists;

DragAndDrop 기능을 가지고 있는 컴포넌트임과 동시에 List 컴포넌트를 가지고 있는 컨테이너의 기능을 하는 컴포넌트이다.

이전에 공부했던 것을 회고해보면 List 컴포넌트를 호출할 때 여러 개의 props를 넘겨주고 있는 것을 확인할 수 있다.

🪐List component

import React from 'react'

const List = React.memo(({
    id,
    title,
    completed,
    todoList,
    setTodoList,
    provided,
    snapshot
}) => {

    const handleDelete = (id) => {
        let newTodoList = todoList.filter(item => {
            return item.id !== id;
        })
        setTodoList(newTodoList);
    }


    const handleComplete = (id) => {
        let newTodoList = todoList.filter(item => {
            if (item.id === id) {
                item.completed = !item.completed;
            }
            return item;
        })

        setTodoList(newTodoList);
    }
    return (
        <div key={id}
            {...provided.draggableProps}
            {...provided.dragHandleProps}
            ref={provided.innerRef}
            className={`${snapshot.isDragging ? "bg-gray-400" : "bg-gray-100"} 'flex item-center justify-between w-full px-4 py-1 my-2 text-gray-600 bg-gray-100 border rounded`}>

            <div className='item-center'>
                <input type="checkbox"
                    defaultChecked={false}
                    onChange={() => handleComplete(id)} />
                <span
                    className={completed ? "line-through" : undefined}>{title}</span>
                <button
                    className='px-4 py-2 float-right'
                    onClick={() => handleDelete(id)}>X</button>
            </div>
        </div>
    )
})

export default List

마지막으로 가장 아래에 자리 잡고 있는 컴포넌트인 List 컴포넌트이다.

각각의 모든 컴포넌트들은 자기 만의 고유한 기능이 있으며 자식 컴포넌트가 부모 컴포넌트의 state 변수나 setter 함수를 이용하고 싶다면 부모 컴포넌트로부터 props로 받아서 사용하고 있는 구조이기에 상위 컴포넌트에서 리렌더링이 발생하면 하위 컴포넌트들은 전부 리렌더링이 되고 있는 구조이다.

profile
풀스택개발자가되고싶습니다:)

0개의 댓글