To Do List

Kng_db·2022년 12월 12일
0

개인프로젝트

목록 보기
1/3

컴포넌트, props, state

세가지 기능을 활용해서 To Do list 만들기!
함수형 프로그래밍! (클래스형은 따로 있다.)
리액트는 스테이트와 프롭스가 변동되면 랜더링한다.
(이번 프로젝트는 기능구현이 우선이라 CSS(꾸미기)는 하지 않았다.)

와이어프레임

와이어프레임대로 위에서부터 차례대로 만들면 된다.
각 컴포넌트 폴더를 만들어주고 각 css파일까지 함께 넣어주면 깔끔하게 정리가능하다.
export default function 컴포넌트이름으로 밖으로 빼주고
import{컴포넌트이름} from ./경로로 불러와주면 된다.

그러면 각 컴포넌트 별로 살펴보자!
( 컴포넌트는 첫 글자를 대문자로 표현하고, 파일명도 대문자로 표현해준다.
그리고 .js와 .jsx는 기능상 차이는 없으나 컴포넌트가 들어간다면 .jsx로 표시해주자. )

App

가장 상위가 되는 컴포넌트로 사실상 body라고 보면된다! 실제로는 index.html에 body가 있고 컴포넌트들은 root에 표시되는 div이다 (js의 SPA 형태와 같아보인다)
먼저 App컴포넌트를 만들어준다

(아래사진 App.jsx 파일에서 오류코드가 있는데 글 마지막에 해결한 방법을 설명함)

useState를 사용해야하니 불러와주고 uuid 라이브러리는 유니크 아이디를 생성하기 위함이다.
uuid를 사용하려면 기본설치가 먼저 필요하니 옆의 링크를 먼저 확인하자 (react셋팅)

7번줄은 cosnt [state, setState] useState(""); 를 사용한 것인데
state의 객체를
setState로 편집하고 그걸 사용할 수 있게
useState를 입력.
("")는 초기값이니 필요하다면 입력하면 된다. // (오류코드 마지막에 해결방법제시)

나는 todos라는 객체에 들어갈 속성이 필요하니 state지정하고 시작!

Header(Title)

폴더 생성

폴더는 위와같이 만들면 깔끔하다. 이후 컴포넌트가 늘어날 때 마다 같은 방식으로 만들면 된다.
(이후 폴더 만드는 컷은 생략)

코드 확인

위와같이 import를 설정하면 되고 하위 컴포넌트들이 생성되면 추가 입력해주면 된다.
지금은 App컴퍼넌트 하위로 새로운 컴퍼넌트를 만들었으니 App.jsx에 Headerimport 해준다.

4번줄의 children는 상위 컴포넌트(App)에서 props를 받아온 것(상위 컴포넌트의 데이터를 받아옴)인데 더이상 하위 컴퍼넌트가 없다면 {children}으로 props 받아올 수 있다.(children도 사실 구조분해할당 같은 형식이지 않을까)

구조분해할당
구조분해할당은 props - props.이름의 구조를 분해해서 할당하는 것이다.
props를 끌어오는 곳(컴포넌트 매개변수('여기'))에 { .이름 에 해당하는 이름}을 넣으면 된다. 분해해서 끌어온 뒤 원하는 곳에 이름만 써서 할당하는 것이다.
(진행하다보면 사용할 부분이 있다. 그 때 자세히 확인하길 바람)

AddForm

Title(Header)부분을 다 만들었으니 Todo를 입력,등록할 수 있는 form부분을 만든다.

App.jsx 코드

컴포넌트 태그에 출력할 내용이 없다면 <AddForm setTodos={setTodos}/>이런식으로 만들어도 된다. 그러면 AddForm 컴포넌트를 살펴보자
코드 확인

여기서 짚고 넘어가야할 점은 prevetDefault는 단순히 새로고침을 막는 기능이 아니라, 기본적으로 가지고 있는 설정을 없애주는 역할을 하는 것이다!! 여기서는 onSubmit은 실행되면 새로고침 되는 특성을 가지고 있기 때문에 prevetDefault를 사용하면 새로고침을 막아 주는것!!

다시 말하지만 return전에 js작업을 한다. 코드의 설명은 주석에 달아놨으니 확인하면 된다. 이해가 안가는 부분은 html부분을 함께 보면서 하니 이해가 잘 됐다. 그러니 바로 return도 함께 확인해보자.

자, js작업한 부분은 주석을 잘 달아놨으니 어떤 기능인지는 안다.
그러니 어떻게 실행되는지 한번 알아보자.
그전에 form태그의 특징 먼저 살펴보면,

<form>
폼태그는, form 태그안에 button(컴퍼넌트화 되어 있을 수 있음)이 클릭이 되면 onSubmit(제출)이 실행 된다. 위 코드에서는 addTodo 이벤트가 실행된다.
그리고 추가로 <label><input>은 1+1이라고 생각하면 편하다. 라벨안의 내용을 클릭했을때 htmlFor와 같은 id값을 가진 <input>이 선택이 된다.

먼저, form태그에 Button을 통해 무언가 onSubmit(제출)되면 addTodo이벤트가 실행되게 코딩을 했고,
그 제출할 입력값을 input을 통해 넣는데 label은 위에서 설명했듯 사용자 편의를 위해 input과 1+1이라고 생각하면 된다.

inputtypetext타입으로 받으며 무언가 onChange(변경)되면 handleChange가 실행이 된다. autoFocus는 새로고침 했을 때 input이 선택되게 해준다.

value는 최종적으로 addTodo이벤트에 들어갈 todoValue가 들어가게 되고 이 값이 setTodos를 통해 todos에 입력된다.

여기서 setTodos를 보면 todos에 무엇이 들어갈지 정해준다. 확인해보면,

setTodos((prev)=>[...prev,{todo: todo, isDone: false, id: uuid()}]);
  // todo: todo와 같이 키 값과 value가 같으면 아래코드같이 todo 만 적어주고 넘어가도 된다.
  // setTodos((prev)=>[...prev,{todo, isDone: false, id: uuid()}]);
  // setTodos를 통해서 ...prev는 만든 todos를 다 불러오는 것이고 거기에
  // {}내용을 추가하는 것이다. todos에 들어갈 요소를 보면,
  // todo(내용), inDone(완료여부), id(유니크ID-각자의 주소 느낌) 이렇게 세가지 값을 필요로 한다.

그리고 마지막으로 setTodoValue("")를 초기화(addTodo이벤트 후 input창을 비워주는 것) 하면서 마무리 해준다.

코드를 보면 버튼을 컴포넌트로 만들어 놨는데 다른 곳에도 사용할 예정이기 때문에 컴포넌트로 만들었으며, 그렇기 떄문에 AddForm.jsx에서 Button을 import해서 사용해야 한다.
( <Button value="등록" /> 의 value는 그냥 value라는 이름으로 Button 컴포넌트에서 props 해온 것)

Button

코드 확인

Button은 여러 곳에서 사용하게 되니 컴포넌트로 만들어주고 각 버튼에서 사용할 기능을 구조분해할당을 통해 props 받아왔고,
Button을 사용할 컴퍼넌트에서 필요한 기능을 만들어 사용하게끔 했다.

여기서 한번 더 짚고 넘어가야 할 점은 AddFrom,jsx에서 form태그 안의 Button에는 onClick 이벤트를 넣지 않았음에도 onSubmit이 됐는데, 이건 외우고 넘어가자 !!

form 태그안에 button이 클릭되면 onSubmit이 실행된다.

버튼까지 만들었으니 이제 만들어진 todos를 출력해줄 곳을 만들면 된다.

TodoList

만들어진 todos들이 나열되는데 와이어프레임에서 보듯, working(진행중)과 Done(완료)를 나눠야하기 때문에 두 곳에 만들어 준다.

App.jsx 코드

여기서 name은 내가 넣어줄 todos의 list 키값(?) 으로 보는게 맞는 것 같다.
그리고 제출된 todos를 컨트롤 해야하니 setTodos도 props 한다.

코드 확인

구조분해할당으로 name,todos,setTodos를 가져온다.
리스트를 두 개로 나눠야 하니 삼항연산자를 사용해서isWorkingListnameworking인 리스트는 true, 그렇지 않은 친구들은 자동으로 false처리하며 namedone이다.
(세 가지 이상이면 if/if else/ else를 사용하면 괜찮을 것 같다.)

return에서 isWorkingListtrue리스트는 Working!!이라는 텍스트를 넣어주고
false리스트는 Done :)이라는 텍스트를 넣어준다.

그러면 각 리스트의 제목까지 달아줬으니 들어갈 리스트의 내용을 보자.
먼저 filter를 통해 todos의 요소중 isDonefalse인 항목이 isWorkingListtrue에 해당하는 working에 들어가야하니 앞에 !를 붙여준다.

filter를 통해 isDone을 확인해서 working에 나타낼지 done에 나타낼지 정했으니 filter로 들어갈 todos의 요소를 map메서드를 사용해서 입력한다. 그래서 필요한 값들인 todo(내용),isDone(list정함),setTodos(todos편집),key,id(각 todos창의 고유 키값)을 Todo 컴포넌트로 프롭스 해준다.

Todo

Todo 컴포넌트에서는 각 Todo의 id값을 이용해서 isDonetruefalse를 토글해주거나 그 id값을 통해 무엇을 출력해서 보낼지 정해주는 역할을 할 것이다.

코드 확인

주석처리가 되있어서 각 함수가 하는 역할은 확인하면 된다. 여기서 헷갈렷던 점은 t.idid가 있는데 t.id는 todos에 들어있는 모든 객체의 id를 말하고 비교대상 id는 내가 선택한 객체의 id이다.

그래서 삭제 함수를 말로 해석하자면 todos가 가지고있는 객체 중, 내가 선택한 특정 id와 같지않은 (한마디로 선택한 id말고 나머지 전부라고 할 수 있음) 객체들을 setTodos를 통해 todos로 보내어 호출하면 마치 삭제된 것처럼 선택한 것 제외한 나머지만 호출된다.

이 이벤트를 Button의 onClick을 통해서 실행하고 있다.


오류

위와같이 코딩하게되면 오류가 나게된다. (이거 찾느라 2시간썼다....)
그 이유를 알기위해 아래사진을 먼저 확인하자

위 코드의 최종 App.jsx 모습

여기서 오류가 난 부분은 10번줄이다.
const [todos, setTodos] = useState("");
먼저 내가 저렇게 만든 이유는 초기값을 안주고 시작하려고 만든 코드이다.
다시말해 어떤todos도 없는 상태에서 내가 입력한 todos만 추가하게 하려고 한 것이다.
하지만 여기서 문제가 뭐냐면 todos는 객체(배열)이다. 그런데 들어갈 useState("")에 빈 text를 넣은 것이다.

해결방법

useState("")를 -> usdState([]);로 만들어주는 방법이 있고,
같은 방법이지만 다른 형식으로 아래 코드와 같이 만들어 줄 수 있다.

빈 배열(A)을 만들어 주고 그 값을 useState(A) 이렇게 넣어주면 해결된다.
여기서 A배열안(27번줄)에 AddForm에서 선언한 todos의 형식

{
    todo: todo,
    isDone: false,
    id: uuid()
}

을 지켜서 몇 개 만들어주면 초기값으로 사용된다.


최종 실행모습

css를 사용안해서 보기는 안좋지만 기능은 완벽하게 구현했다.

profile
코딩 즐기는 사람

0개의 댓글