2차 공부/TIL

24.06.20 리액트로 TodoList 관리 페이지 만들기

공대탈출 2024. 6. 20. 17:53

이전에 만들었었던 todolist 관리 페이지를 만들고자 한다.

이전에 만들었던 페이지

 

재활치료하는 느낌으로 리액트 props를 내려주고 state를 바꾸고하는 과정들을 연습해보고자하는 의미이다.

CRA이후 추가한 라이브러리는 없으며, 기회가 된다면 TodoList를 저장할 firebase나 supabase를 사용해볼 생각이다.

 


현재까지 진행한 상황은 다음과 같다.

 

일단 페이지를 먼저 나누어 보았다.

제일 위에 있는 Header부분

Todo를 추가하기 위한 inputBox부분

todo의 상태에 따라 다르게 저장하는 todoList부분

 

코드 작성을 시작하기 전 inputBox의 input과 inputBox, todoList에 반복되어 사용되는 button, todoList에 반복사용되는 todo, title, todo를 모아두는 todos는

반복되어 사용되는 컴포넌트이므로 commons 폴더에 만들어두어 재사용할 생각이다.

 

가장 먼저 발생했던 문제는 컴포넌트를 불러오지 못하는 문제였다.

App.js에서 큰 컴포넌트들을 합칠 생각이었다.

import Header from './components/header.jsx';
import inputBox from './components/inputBox.jsx';

function App() {
    return (
        <div>
            <Header />
            <inputBox />
        </div>
    );
}

export default App;

 

리액트 컴포넌트는 대문자로 시작하여야 한다는 규칙이 문제였다.

리액트 공식문서의 주의점

 

따라서 import, export default, function선언부분에서 모두 대문자시작으로 바꾸어 문제를 해결했다.

그 후 Header의 스타일을 만들어서 적용시켜주고, inputBox부터 반복되어 사용될 CustomInput과 CustomButton 컴포넌트를 만들어주었다.


import React from 'react';

const CustomInput = ({ customStyle }) => {
    return <input style={customStyle} />;
};

export default CustomInput;
import React from 'react';

const CustomButton = ({ children, customStyle }) => {
    return (
        <button style={customStyle}>
            {children}
        </button>
    );
};

export default CustomButton;

 

각 버튼의 기능에 따라 적절한 UI를 만들어야 하므로 customStyle이라는 props를 내려주어 적용시켰고, 각 버튼이 어떤 역할인지 직관적으로 사용자에게 보여주어야 하므로 children을 내려받아 버튼에 출력되도록 하였다.

나는 버튼에 마우스를 올렸을 때 작동하는 버튼인지 사용자에게 알려주는 것이 중요하다고 생각한다.

따라서 cursor:pointer이라는 스타일을 주는것도 당연하지만, onMouseOver와 onMouseOut을 설정하여 style이 바뀌도록 하였다.

이전에는 styled-component 라이브러리를 사용하여 hover시에 어떤 style을 보일것인지 설정하면 되었지만, 라이브러리 없는 리액트에선 어떻게 hover인지 판단할 state값이 필요했다.

 

function InputBox() {
    const inputBoxWrap = {
        width: '100%',
        backgroundColor: '#f1f1f1',
        display: 'flex',
        flexDirection: 'row',
        justifyContent: 'space-between',
        alignItems: 'center',
        padding: '20px',
        borderRadius: '20px',
    };
    const inputTitleWrap = {
        display: 'flex',
        flexDirection: 'row',
        alignItems: 'center',
        gap: '15px',
    };
    const customInputStyle = {
        border: 'none',
        borderRadius: '20px',
        width: '200px',
        height: '40px',
    };

    const [hover, setHover] = useState(false);
    const handleMouseOver = () => {
        setHover(true);
    };
    const handleMouseOut = () => {
        setHover(false);
    };
    const customButtonStyleOut = {
        width: '130px',
        height: '40px',
        borderRadius: '10px',
        border: 'none',
        backgroundColor: 'teal',
        color: 'white',
        fontWeight: 'bold',
        cursor: 'pointer',
        fontSize: '15px',
    };
    const customButtonStyleOver = {
        width: '130px',
        height: '40px',
        borderRadius: '10px',
        border: 'none',
        backgroundColor: 'rgb(2, 94, 94)',
        color: 'white',
        fontWeight: 'bold',
        cursor: 'pointer',
        fontSize: '15px',
    };

    return (
        <div style={inputBoxWrap}>
            <div style={inputTitleWrap}>
                <p>제목</p>
                <CustomInput customStyle={customInputStyle} />
                <p>내용</p>
                <CustomInput customStyle={customInputStyle} />
            </div>
            <CustomButton
                customStyle={hover ? customButtonStyleOver : customButtonStyleOut}
                mouseOver={handleMouseOver}
                mouseOut={handleMouseOut}
            >
                추가하기
            </CustomButton>
        </div>
    );
}

따라서 hover이라는 bool값을 가지는 state를 만들어주고, MouseOver됐을 때 true, MouseOut됐을 때 false가 되는 함수를 만들어 props로 내려주었다.

또 props로 내려주는 customStyle이 hover의 상태에 따라 달라지도록 삼항 연산자를 활용하여 내려주었다.

import React from 'react';

const CustomButton = ({ children, customStyle, mouseOver, mouseOut }) => {
    return (
        <button style={customStyle} onMouseOver={mouseOver} onMouseOut={mouseOut}>
            {children}
        </button>
    );
};

export default CustomButton;

다음으로 투두리스트의 상태에따라 분리하여 각각의 todo들을 보여주는 공간이다.

각 todo의 상태에 따라 Working(진행중) / Done(완료) 파트로 나눠져 보이게 된다.

Working의 Todo에는 삭제하기와 완료 버튼이 있고, Done의 Todo에는 삭제하기와 취소 버튼이 있다.

두 파트의 삭제하기 버튼은 todo를 완전히 지우는 것이고, 완료는 Working에서 Done으로, 취소는 Done에서 Working으로 이동하는 것이다.

 


생각을 조금 해보았다.

TodoList가 InputBox와 분리되어있는 컴포넌트이므로, 중앙 상태저장소가 없으면 상태값의 이동을 관리하기 어렵다고 생각하였다.

따라서 TodoList 전체를 InputBox 안에 넣어주어 props로 todo들을 넘겨주고, 해당 todo의 값을 변경하고, 삭제하는 등의 작업을 가능하도록 코드 구성을 바꿨다.

//InputBox의 return부분
<div>
    <div style={inputBoxWrap}>
        <div style={inputTitleWrap}>
            <p>제목</p>
            <CustomInput customStyle={customInputStyle} />
            <p>내용</p>
            <CustomInput customStyle={customInputStyle} />
        </div>
        <CustomButton
            customStyle={hover ? customButtonStyleOver : customButtonStyleOut}
            mouseOver={handleMouseOver}
            mouseOut={handleMouseOut}
        >
            추가하기
        </CustomButton>
    </div>
    <TodoList />
</div>

 


내일은 Todo를 담는 state와 해당 state의 변경을 만들어 보고 작동시키도록 할 것이다.