홍혜진 박선주 김태혁
- 공통 - 웹 페이지 화면 구성
- 홍혜진 - 체크리스트 컴포넌트 추가, 삭제 기능 + Tailwind
- 박선주 - 체크리스트 컴포넌트 출력, 수정 기능
- 김태혁 - 캘린더 라이브러리 기능
- 배경색, 하얀 상자와 같은 레이아웃 요소를 컴포넌트로 나누어 재사용 가능하도록 했습니다.
- 기능을 구분하여 컴포넌트로 구성해 수정을 용이하게 했으며, 반복되는 부분에 재사용하였습니다.
npm i react-calendar
https://www.npmjs.com/package/react-calendar
react-calendar 라이브러리를 사용해 구현했습니다.
StyleTodoCalendar.jsx
에는 캘린더에 적용할 스타일이 작성되어있습니다.
작성된 스타일과 라이브러리를 활용해 MyCalendar
컴포넌트를 작성했습니다.
const MyCalendar = ({onChange}) => {
const [nowDate, setNowDate] = useState(new Date());
const handleDateChange = (selectedDate) => {
const formattedDate = moment(selectedDate).format('ddd MMM DD YYYY');
onChange(formattedDate);
setNowDate(moment(selectedDate).format('YYYY년 MM월 DD일'));
};
App 컴포넌트의 getCalendarDate 함수를 Props로 전달해 캘린더에서 날짜를 클릭하면 App컴포넌트의 상태를 바꿀 수 있습니다.
const [date, setDate] = useState(new Date().toDateString());
const getCalendarDate = (updatedDate) => {
setDate(updatedDate);
};
{
id: 9,
date: 'Thu Dec 15 2023',
title: '스터디 모임',
summary: '스터디 관련 이야기 나누기. 필기구 지참',
checked: false, //체크 박스 :Workig, Done 상태
},
리스트 항목이 완료된 항목과 완료되지 않은 항목을 Working과 Done 카테고리로 나누어 보여주기로 했습니다. 우측에는 각 카테고리의 개수를 계산해 표시합니다.
let printList = ''; // 최종적으로 props 보낼 체크리스트 데이터들
let workingNum = '0'; // working 해야하는 리스트 개수
let doneNum = '0'; // done 된 리스트 개수
if (status == 'Working') {
printList = lists.filter((list) => list.checked == false && list.date == date);
const done = lists.filter((list) => list.checked == true && list.date == date);
workingNum = printList.length;
doneNum = done.length;
} else if (status == 'Done') {
printList = lists.filter((list) => list.checked == true && list.date == date);
const work = lists.filter((list) => list.checked == false && list.date == date);
doneNum = printList.length;
workingNum = work.length;
}
APP컴포넌트에서 전달된 데이터 배열을 CheckListContainer 컴포넌트에서 하나의 객체로 분리해 전달해 CheckListItem에서 보여줍니다.
const CheckListItem = ({item, onUpdate}) => {
const id = item.id;
const [title, setTitle] = useState(item.title);
const [summary, setSummary] = useState(item.summary);
const date = item.date;
const checked = item.checked;
return(
<CheckBox checked={checked} onCheck={onCheckHandler}></CheckBox>
<p>{title}</p>
<p>{summary}</p>
<p>{date}</p>
);
};
두 개의 인풋 태그로 체크리스트의 제목과 상세 내용을 작성해 추가 할 수 있습니다. 제목 하단에 표시되는 카테고리가 Working이면 체크가 안된 항목을, Done이 선택됐으면 체크된 항목으로 생성합니다.
const addTodoHandler = ({ title, summary }) => {
let checked = false
if (status == "Done") checked = true;
const newList = {
id: self.crypto.randomUUID(),
date: date,
title,
summary,
checked
};
const updatedLists = [...lists, newList];
setDList(updatedLists);
};
CheckListContainer를 통해 각 체크리스트 데이터 객체에 생성된 삭제 버튼을 누르게 되면 해당 데이터 객체의 id값을 App의 delTodoHandlar로 가져옵니다. 가져온 id와 일치하지 않는 id를 deleteTodos에 담아 새로운 deleteTodos를 생성한 후 리렌더링 합니다.
-------------CheckListContainer 컴포넌트 ------------------
<div className='text-end'>
<button onClick = {() => onDelete(item.id)}>삭제</button>
</div>
-------------App 컴포넌트 ------------------
const delTodoHandlar = (id) => {
const deleteTodos = lists.filter(list => list.id !== id);
setDList(deleteTodos)
}
체크리스트 데이터 객체의 checked를 수정해주면 APP.jsx에서 렌더링이 되면서 3.
에서 구현한 대로 필터링이 됩니다.
결과적으로 Working 카테고리와 Done 카테고리 사이에서 체크리스트 항목이 이동하는 것처럼 느껴지게 됩니다.
-------------CheckBox 컴포넌트 ------------------
const CheckBox = ({checked, onCheck}) => {
return (
<label>
<input
type="checkbox"
checked={checked}
onChange={(e)=>onCheck(e)}/>
</label>
)
}
---------------------CheckListItem 컴포넌트------------------------
const checked = item.checked;
<CheckBox checked={checked} onCheck={onCheckHandler}></CheckBox>
const onCheckHandler =(e) => {
const value = e.target.checked;
onUpdate({id, date, title, summary, checked: value})
};
처음 수정 기능을 구현 할 때, 두 가지 방법을 생각했습니다.
- 수정 버튼을 누르면 창을 추가로 띄워서 수정
- 이미 나타나있는 페이지에서 바로 수정
하루에도 수많은 체크리스트들이 있을텐데 수정하기 위해 체크리스트 마다 창을 열고 닫는 과정이 피로할 것 같아, 2)번 방법을 선택했습니다.
그러나 실수로 클릭해 수정할 수 있다는 점도 생각해
수정 버튼을 눌렀을 때만 입력 태그로 바뀌는 기능을 추가하기 쉽게
컴포넌트의 state 값에 따라서 p태그와 input 태그를 오갈 수 있는 상태로 코드를 완성했습니다.
<input type='text' value={title} onChange={(e)=>whenType(e, setTitle)}></input>
const isUpdate = true; //true: input 태그로 표시 false: p 태그로 표시
const whenType = (e, func)=>{
let value = e.target.value;
func(value);
onUpdate({id, date, title, summary, checked}
}
onCheckHandler, whenType 두 이벤트 핸들러에 사용된 onUpdate()함수는 주어진 인수의 내용으로 App.jsx내부 데이터 배열을 수정합니다.
const UpdateList = ({ id, date, title, summary, checked }) => {
const updatedItem = {
id: id,
date: date,
title: title,
summary: summary,
checked: checked,
}
const updatedList = lists.map(list => list.id === id ? updatedItem : list)
setDList(updatedList);
}
- useState를 이용하여 박스가 체크되었는지 아닌지의 value값을 check로 선언하여 전달하려고 했음 -> 이렇게 하면 setChecked가 함수가 종료 된 후에 값이 바뀜 -> 체크박스가 한박자 늦게 동작 => e.target.checked를 이용하여 바로 값을 넘겨주도록 수정
- 캘린더 ui가 라이브러리가 css를 수정하기 힘듦 -> style.component로 구현
- 주로
Use react-error-boundary to handle errors in React
라는 오류로 나타났는데 에러가 표시된 줄이 비어있고 해결을 못했었음 - Vite 종료 후 새로운 파일을 생성한 후 기존 내용을 붙여넣기 함
깃 레포 폴더 안에 프로젝트 폴더가 생성되어 git 명령어를 사용할때와 npm run dev
명령어를 사용할 때 경로를 계속 왔다갔다해야해서 불편했습니다.
기존 레포를 삭제 하고 프로젝트 생성을 먼저 한 후 새로운 깃 레포에 푸시하여 해결했습니다.
함수 | 설명 |
---|---|
getCalendarDate() | Calendar에서 선택한 날짜를 가져오는 함수 |
getStatusValue() | 사용자가 선택한 카테고리를 가져오는 함수 |
addTodoHandler() | 체크 리스트 추가 |
UpdateList() | 체크리스트 내용 수정 |
delTodoHandler() | 체크리스트 삭제 |
- 괄호를 사용하는 방법: "arrow-parens" 규칙을 사용하여 항상 괄호를 사용하도록 설정
- 띄어쓰기 : "indent" 규칙을 사용하여 들여쓰기를 탭으로 설정
- 콤마 위치 : "comma-style" 규칙을 사용하여 콤마를 줄 끝에 배치
- 세미콜론 사용 : "semi" 규칙을 사용하여 항상 세미콜론을 사용하도록 설정
- 줄 간격 : "no-multiple-empty-lines" 규칙을 사용하여 연속된 빈 줄을 허용하지 않음
- 주석 스타일 : "multiline-comment-style" 규칙을 사용하여 여러 줄 주석 스타일을 강제
- 따음표 : "quotes" 규칙을 사용하여 큰 따옴표 사용하도록 설정
리액트 수업 복습이 막막하게 느껴졌는데, 이번 프로젝트로 팀원분들과 이야기 나누고, 함께 오류를 해결해나가면서 수업 내용을 천천히 다시 복습해볼 수 있었습니다. 특히 props 내용을 깊게 복습해보고, 흐름을 따라가 볼 수 있어 좋았습니다. 또한 처음으로 테일윈드를 사용해보았는데 상속으로 인해 CSS와는 다른 어려움을 느꼈지만, 코드내 tailwind를 다양하게 변경해보며 스타일 상속 범위에 대해 다시 관찰해보고 배워볼 수 있었습니다.
처음엔 간단한 동작이라 생각했는데 프로젝트를 생성하고 데이터 흐름과 수정에서 생각해야 할 것이 많아 생각보다 시간이 많이 걸렸습니다. 코드를 작성하고 보니 State 변수를 불필요한 곳에 남발한 것 같았습니다. 변수를 선언할 때 State 변수와 일반 변수의 차이를 구분해서 사용해야겠습니다.
git과 eslint를 이전에 했던 프로젝트 경험을 통해서 어떻게 규칙을 설정하면 좋을지, 또 프로젝트를 기능 작성 전에 다같이 화면을 어떻게 구성할 지 먼저 얘기를 나누고 설정을 하고 진행하면서 이전보다 훨씬 수월하게 프로젝트를 수행하였습니다. 또한 반복되는 화면이나 기능을 component를 이용하도록 노력을 하며 작성을 하면서 처음에는 이 방법이 오히려 더 어렵고 귀찮다고 생각했지만 막상 후반부에 기능을 갑자기 추가하거나 수정하려고 할 때 왜 이렇게 사용해야 하는지 알 수 있었습니다.