티스토리 뷰

리액트 기초 내용을 적용해보며 더욱 익숙해지고자 기본 중의 기본 To do app 만들기를 구현해봤습니다.

 

먼저 컴포넌트 구조는 아래와 같습니다.

 

 


 

 

[ App.js ]

메인 컴포넌트인 Todo.js 를 import 해줍니다.

import './App.css';
import Todo from './components/Todo';    // 메인 컴포넌트인 Todo.js

function App() {
  return (
    <Todo />
  );
}

export default App;

 

 


 

 

[ Todo.js ] 

 

import 부분 입니다.

컴포넌트 구조를 보면 Todo.js 안에 TodoForm.js와 TodoList.js가 있어야하므로 import 해줍니다.

css도 import 해줍니다.

그리고 고유 id값을 부여하기 위해 useRef를 import해주고, Todo 목록을 업데이트 해주기 위해 useState를 사용하기 때문에 역시 import 해줍니다.

 

 

삭제 기능을 위한 고유 id 부여를 해주기 위해 useRef를 사용해줬습니다.

Todo 목록을 업데이트 해주기 위해 useState를 사용해줬습니다.

 

 

고유 id값을 받아 filter() 메소드를 사용해 해당 item과 일치하지 않는 todo 아이템만 포함된 새로운 배열을 생성해줍니다.

이 때, setTodo 함수를 사용해여 todo 목록의 상태를 업데이트 해줍니다.

이렇게 하면 해당 item을 제외한 값만 필터링 되어 업데이트 되므로 삭제 기능이 되게 됩니다.

 

 

  1. todo.map(...) : map 함수를 사용하여 todo 배열을 순회합니다.
  2. item => : 각각의 todo 아이템에 접근하기 위한 매개변수 item을 정의합니다.
  3. item.id === id : 현재 순회 중인 todo 아이템의 id 속성이 주어진 id와 일치하는지 확인합니다.
  4. ? {...item, check : !item.check} : item : 조건 연산자 (? :)를 사용하여 일치하면 해당 아이템의 check 속성을 토글한 객체를 반환하고, 일치하지 않으면 그대로 해당 아이템을 반환합니다.
    • {...item, check : !item.check} : Spread 문법({...item})을 사용하여 현재 아이템의 모든 속성을 복사한 뒤, check 속성만 토글하여 새로운 객체를 생성합니다.
    • !item.check는 현재 아이템의 check 속성을 반전시킵니다.
  5. 전체 표현식은 map 함수를 통해 새로운 배열이 반환됩니다.

 

✔ !item.check 으로 반전시키는 이유?

check 속성을 토글하려는 것입니다. 즉, 현재의 상태가 true이면 false로, false이면 true로 바꾸고자 하는 것입니다.

일반적으로 토글(toggle)은 현재 상태의 반대로 전환하는 것을 의미합니다. 이것은 주로 사용자가 체크박스를 클릭할 때 해당 상태를 변경할 때나 어떤 항목의 활성화/비활성화 상태를 전환할 때 유용합니다.

따라서 !item.check는 현재 아이템의 check 속성을 반전시켜서 이전의 상태와 반대로 만들어주는 역할을 합니다.

 

  1. ...todo : Spread 문법을 사용하여 이전의 todo 목록을 복사합니다. 이전 목록을 변경하지 않고 새로운 목록을 생성하기 위한 것입니다.
  2. { id: no.current++, text, check: false } : 새로운 todo 아이템을 생성합니다.
    • id: no.current를 사용하여 현재 값에 1을 더한 값을 할당합니다. 이로써 각각의 todo 아이템은 고유한 id를 갖게 됩니다.
    • text : 함수의 매개변수로 전달받은 text를 그대로 사용합니다.
    • check : 초기 상태로 false를 설정합니다.
  3. 전체 표현식은 setTodo([...todo, {...}])을 통해 이전의 목록에 새로운 todo 아이템을 추가한 새로운 배열을 생성하고, 이를 setTodo 함수를 통해 상태로 설정합니다.

  1. <TodoForm onAdd={onAdd}/> : TodoForm 컴포넌트를 렌더링합니다. onAdd 라는 프로퍼티를 전달하여 부모 컴포넌트에서 정의한 onAdd 함수를 하위 컴포넌트로 전달합니다.
  2. <TodoList todo={todo} onToggle={onToggle} onDel={onDel}/> : TodoList 컴포넌트를 렌더링합니다. todo라는 프로퍼티로 현재의 할 일 목록을 전달하고, onToggle과 onDel 함수를 전달하여 할 일 항목의 체크 여부를 토글하거나 항목을 삭제하는 데 사용됩니다.

 

import React from "react";
import { useRef, useState } from 'react';
import '../css/Todos.css';
import TodoForm from './TodoForm';
import TodoList from './TodoList';

const Todo = () => {
    const no = useRef(0);   // 할 일(item)의 고유 id
    const [todo, setTodo] = useState([]);

    // < 삭제 >
    // 고유 id값을 받아 해당 item 삭제
    // setTodo 함수를 사용하여 todo 목록의 상태를 업데이트할건데
    // filter()를 사용하여 해당 item과 일치하지 않는 
    // todo 아이템만 포함된 새로운 배열을 생성
    const onDel = (id) => {
        setTodo(todo.filter(todo => todo.id !== id))
    }

    // < toggle >
    // 고유 id값을 받아 해당 item의 체크 상태를 설정
    // setTodo 함수를 사용하여 todo 목록의 상태를 업데이트할건데
    // map()을 사용하여 각각의 todo 아이템에 접근하고
    // 주어진 id에 해당하는 아이템을 찾아 check 속성을 토글
    // 나머지 아이템은 그대로 반환
    const onToggle = (id) => {
        setTodo(todo.map(item => item.id === id ? {...item, check : !item.check} : item))
    }

    // < 추가 >
    // setTodo 함수를 사용하여 todo 목록의 상태를 업데이트할건데
    // 이전 todo 목록을 복사하고
    // form에 입력받은 값을 새로운 todo 아이템으로 목록에 추가
    const onAdd = (text) => {
        setTodo([
            ...todo,   // 이전 todo 목록을 복사
            {
                id : no.current++,   // 새로운 id 부여
                text,   // 전달받은 text
                check : false   // 초기 check 속성을 false로 설정
            }
        ])
    }


    // TodoForm에는 onAdd함수만 넘겨주고
    // TodoList에는 todo객체와 토글, 삭제 함수를 넘겨준다.
    return (
        <div className="Todos">
            <h1>To Do List</h1>
            <TodoForm onAdd={onAdd}/>
            <TodoList todo={todo} onToggle={onToggle} onDel={onDel}/>
        </div>
    );

};

export default Todo;

 

 


 

 

[ TodoForm.js ] 

 

 

useRef와 입력 필드의 값을 state로 관리

  1. const textRef = useRef('') : useRef를 사용하여 입력 필드의 참조를 생성합니다. 이 참조는 입력 후에 입력 필드에 focus()를 호출하기 위해 사용됩니다.
  2. const [text, setText] = useState('') : 입력 필드의 값을 상태로 관리하기 위해 text 상태와 setText 함수를 생성합니다.

 

 

입력 필드 변화 함수 chageInput

 

changeInput 함수는 입력 필드에 변화가 있을 때 호출되는 함수로, 입력된 텍스트 값을 상태에 업데이트합니다.

 

이벤트 객체 e에서 target을 통해 이벤트가 발생한 HTML 엘리먼트(여기서는 입력 필드)에 접근하고, 해당 엘리먼트의 value 속성을 추출합니다. 추출한 value는 입력 필드에 사용자가 입력한 텍스트를 나타냅니다.

 

e.target은 이벤트가 발생한 HTML 엘리먼트를 가리킵니다. 따라서 e.target.value는 해당 엘리먼트(여기서는 입력 필드)의 현재 값, 즉 사용자가 입력한 텍스트를 나타냅니다. 이 값을 setText 함수를 사용하여 React 상태로 업데이트합니다. setText(value)는 입력 필드의 값이 변할 때마다 text 상태를 업데이트하게 됩니다.

 

 

입력값 폼 제출하는 함수 onSubmit

 

  1. const onSubmit = (e) => { ... }: 폼이 제출될 때 호출되는 함수로, e.preventDefault()를 사용하여 새로고침을 방지합니다. 그리고 입력값이 없을 경우 함수를 빠져나가고, 입력값이 있는 경우 onAdd 함수를 호출하여 새로운 할 일을 추가하고, 입력 필드를 초기화하고 다시 포커스를 입력 필드로 이동시킵니다.

즉, 주로 useRef를 사용하여 입력 후에 입력 필드에 자동으로 포커스를 설정하고, onAdd 함수를 호출하여 상위 컴포넌트로 새로운 할 일을 전달하는 기능을 수행합니다.

 

  1. <form className="TodoForm" onSubmit={onSubmit}> : 폼 엘리먼트를 생성하고, 클래스명이 "TodoForm"인 스타일을 적용하기 위해 className 속성을 사용합니다. onSubmit 함수는 폼이 제출될 때 호출됩니다.
  2. <input type="text" value={text} onChange={changeInput} ref={textRef}/> : 텍스트 입력 필드를 생성합니다. value 속성은 상태 text를 반영하여 입력 필드의 현재 값으로 설정되어 있습니다. onChange 이벤트는 입력 필드의 값이 변경될 때 호출되는 함수인 changeInput 함수를 설정합니다. ref={textRef}는 useRef를 사용하여 생성한 참조를 입력 필드에 할당합니다.
  3. <button type="submit">추가</button> : 제출 버튼을 생성하고, 폼이 제출될 때 onSubmit 함수가 호출되도록 설정합니다.

이렇게 구성된 TodoForm 컴포넌트는 사용자에게 할 일을 입력할 수 있는 폼을 제공하고, 이 폼이 제출될 때 onSubmit 함수를 호출하여 새로운 할 일을 추가합니다.

 

import React from "react";
import { useRef, useState } from 'react';
import '../css/TodoForm.css';

const TodoForm = ({onAdd}) => {
    const textRef = useRef('')   // 입력 후, 입력 필드에 focus()를 호출하기 위함
    const [text, setText] = useState('')   // 입력 필드의 값을 state(상태)로 관리

    const changeInput = (e) => {   // 입력된 텍스트 값을 state에 업데이트
        const {value} = e.target
        setText(value)
    }

    const onSubmit = (e) => {
        e.preventDefault()   // submit 후 새로고침 방지

        if(!text) return   // 입력값이 없으면 실행되지 않음

        // 입력값이 있는 경우
        onAdd(text)   // onAdd 함수를 호출하여 상위 컴포넌트로 새로운 할 일을 추가
        setText('')   // input란을 다시 공란으로 -> 입력 필드 초기화
        textRef.current.focus()   // useRef를 사용하여 input란에 자동 포커스 설정
    }

    return (
        <form className="TodoForm" onSubmit={onSubmit}>
            <input type="text" value={text} onChange={changeInput} ref={textRef} placeholder="할 일을 입력해주세요"/>
            <button type="submit">추가</button>
        </form>
    );
};

export default TodoForm;

 

 


 

 

[ TodoForm.js ] 

 

todo, onToggle, onDel 프로퍼티들을 받아와 렌더링 합니다.

 

 

  1. <ul className='TodoList'> : 할 일 목록을 나타내기 위한 <ul> 엘리먼트를 생성하고, 클래스명이 'TodoList'인 스타일을 적용하기 위해 className 속성을 사용합니다.
  2. { todo.map(item => <TodoItem key={item.id} item={item} onToggle={onToggle} onDel={onDel} />) } : todo 배열을 매핑하여 각각의 할 일 항목을 TodoItem 컴포넌트로 렌더링합니다.
  3. key 속성은 React에서 각 항목을 구별하기 위한 고유한 식별자로 사용되며, 여기서는 item.id를 사용합니다.
  4. onToggleonDel 콜백 함수는 각 항목에 전달되어 해당 항목의 완료 여부를 토글하거나 항목을 삭제하는 데 사용됩니다.

이러한 구조를 통해 TodoList 컴포넌트는 상위 컴포넌트로부터 전달받은 todo 배열을 기반으로 각각의 할 일 항목을 렌더링합니다.

 

import TodoItem from './TodoItem';
import '../css/TodoList.css'

// 메인 컴포넌트에서 받아온 todo데이터와 ontoggle, onDel 함수를 map으로 TodoItem.js에 넘겨준다.
const TodoList = ({todo, onToggle, onDel}) => {

    return (
        // 할 일 목록을 나타냄
        <ul className='TodoList'>   

            {   // todo 배열을 매핑하여 각각의 할 일 항목을 TodoItem으로 렌더링
                // key : 각 항목을 구별하기 위한 고유한 식별자 -> item.id 사용
                todo.map(item => <TodoItem key={item.id} item={item} onToggle={onToggle} onDel={onDel}/>)
            }
        </ul>
    );
}

export default TodoList;

 

 


 

 

[ TodoItem .js ] 

 

{ id, text, check } = item; : item 객체에서 id, text, check 프로퍼티를 추출합니다. 

 

  1. <li className={check ? 'on' : ''}> : check 값에 따라 클래스명을 동적으로 설정합니다. 만약 check가 true이면 'on' 클래스를 추가하고, 그렇지 않으면 빈 문자열을 할당합니다.
  2. <span onClick={() => onToggle(id)}> &#10003; </span> : 할 일 항목 왼쪽에 있는 체크 표시(✓)를 클릭하면 onToggle 함수를 호출하여 해당 항목의 완료 여부를 토글합니다.
    <input type="checkbox" checked={check} onChange={() => onToggle(id)} />
    으로 변경했습니다. 체크 박스를 클릭하면 같은 기능을 하게 됩니다!
  3. <em>{text}</em> : 할 일 텍스트를 강조 표시
  4. <button onClick={() => onDel(id)}> 삭제 </button> : 삭제 버튼을 클릭하면 onDel 함수를 호출하여 해당 항목을 삭제합니다.

 

import React from 'react';

const TodoItem = ({item, onToggle, onDel}) => {
    const {id, text, check} = item   // item 객체에서 id, text, check 추출
    
    return (
        // check 값에 따라 클래스명을 동적으로 설정
        // check가 true이면 'on' 클래스를 추가하고, 그렇지 않으면 빈 문자열을 할당
        // 체크 박스를 클릭하면 onToggle 함수를 호출하여 해당 항목의 완료 여부를 토글
        // 할 일 텍스트를 강조 표시
        // 삭제 버튼을 클릭하면 onDel 함수를 호출하여 해당 항목을 삭제
        <li className={check ? `on` : ""}>  
            {/*<span onClick={() => onToggle(id)}> &#10003; </span> */}
            <input type="checkbox" checked={check} onChange={() => onToggle(id)} />
            <em>{text}</em>
            <button onClick={() => onDel(id)}> 삭제 </button>
        </li>
    );
};

export default TodoItem;

 

 


 

 

 

컴포넌트를 적절히 분리하는 것이 정말 중요하다는 것을 느꼈습니다. 마치 프로젝트 초반 DB 설계를 할 때와 비슷한 마음가짐이랄까요..? 처음에 잘~ 해놓으면 프로젝트를 편하게 할 수 있구나. 이래서 중요하구나. 하는 생각이 듭니다.

그리고 여러가지 다양한 HOOK이 재밌고 유용하게 느껴집니다!

 

역시 뭐든지 직접 써먹고 정리를 해봐야 기억에 남는 것 같습니다.

아주 간단한 작업임에도 명확한 결과물을 보니 뿌듯하네요!

css까지 포함된 전체 코드는 깃허브에서 확인하실 수 있습니다. 😉

'Project 댕린이집' 카테고리의 다른 글

[Spring Security] 기본 개념  (0) 2024.01.25
쿠키 vs 세션 vs 토큰 vs JWT  (0) 2024.01.23
[React] React의 동적 라우팅, useParams  (0) 2024.01.18
[React] React-Router-Dom  (0) 2024.01.17
CORS 트러블슈팅  (0) 2024.01.12
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함