React 입문 주차 개인 과제
구현해야 할 기능
UI 구현
Todo 추가
Todo 삭제
Todo 완료 상태 변경하기 (완료 <-> 진행중)
과제 요구 사항
디자인은 취향대로, 화면 구성은 동일하게
1. 제목, 내용 입력후 [추가하기] 버튼 클릭시 working에 새로운 Todo가 추가되고 제목 input과 내용 input은 다시 빈 값으로 바뀌도록
2. Todo의 isDone 상태가 true이면 상태 버튼의 라벨을 취소, isDone이 false면 라벨을 완료로 조건부 렌더링.
3. Todo의 상태가 working이면 위쪽에 위치, done이면 아래쪽에 위치하게.
4. Layout의 최대 넓이는 1200px, 최소 넓이는 800px 으로 제한, 전체 화면의 가운데로 정렬.
5. 폴더 구조, 컴포넌트 구성도 나누어 구현하기.
Hint
1. 예제에 사용한 hook은 오직 useState
2. 기능 구현을 위해 생성한 함수는 2개 onChangeHandler, onSubmitHandler
3. 사용한 javascript 내장 메서드는 map 과 filter
4. todo의 initial state는 {id: 0, title:"", body:"", isDone: false}
Truble Shooting
1. App.js 파일에 코드 모두 작성, 컴포넌트로 나누기 힘듦
-> 아직 부모, 자식 컴포넌트에 대한 이해가 부족한 상태에서 만들어진 화면을 어디에 무엇을 담아야 할 지 감이 오지 않았고 생각 할 수록 머리가 복잡하여서 개요도를 그려봄.
대충 그리고나니 어디에 뭘 넣어야 할 지 감이 와서 새로 시작하는 기분으로 새 폴더를 만들어 각 컴포넌트를 서로 연결되도록 짜놓고, 개요도 대로 함수선언을 App.js 아래 TodoList.jsx에 모두 해주었는데, 어떤 것들은 글씨가 죽어있는 현상을 발견. props로 자식 컴포넌트들에 보내주어도 여전히 함수선언이 되질 않았다. 잠시 밥을 먹고 앞서 보았던 예제들을 보면 함수 선언이 항상 각 컴포넌트에 되어있었는데, 그렇게 해도 될 것 같았다. 다시 개요도를 작성해서 각 컴포넌트에 필요한 함수를 선언하고 필요한 것은 props로 부모->자식 컴포넌트로 전달하는 구성으로 바꾸었다.
2. input박스에 글씨 안써짐
input 박스 안에 onChangeHandler 부분을 onChange={onChangeHandler}라고 썼어야 했는데 onChangeHandler라고 써넣음.
3. Console창 에러 Warning: The tag <layout> is unrecognized in this browser. If you meant to render a React component, start its name with an uppercase letter.
<todo>에도 같은 문제가 발생해 commend+F로 <layout> 과 <todo>를 찾아보니
이렇게 씌여있기 때문. 지워주니 에러메세지는 사라졌다.
4. 화면 밖으로 카드 나감
이 Todo들을 묶어준 <div>의 이름이 list-wrapper였는데 이것의 css에서 flex-wrap: flex 라고 적혀있었음. wrap으로 바꿔주니 문제 해결되었다. css 공부가 중요하다는 것을 깨달은 순간.
CSS flex-wrap property는 flex-item 요소들이 강제로 한줄에 배치되게 할 것인지, 또는 가능한 영역 내에서 벗어나지 않고 여러행으로 나눠 표현 할 것인지 결정하는 속성. 영역 내에서 벗어나지 못하게 설정한다면 동시에 요소의 방향 축을 결정할 수 있다.
flex-wrap: nowrap; 강제로 한줄에 배치됨. 레이아웃 밖으로 튀어나가게됨
flex-wrap: wrap; 가능한 영역 내에서 벗어나지 않고 여러행으로 나누어짐
flex-wrap: wrap-reverse; 가능한 영역 내에서 카드 순서가 반대로 바뀜
https://developer.mozilla.org/ko/docs/Web/CSS/flex-wrap
flex-wrap - CSS: Cascading Style Sheets | MDN
CSS flex-wrap property는 flex-item 요소들이 강제로 한줄에 배치되게 할 것인지, 또는 가능한 영역 내에서 벗어나지 않고 여러행으로 나누어 표현 할 것인지 결정하는 속성입니다. 만약 영역 내에서 벗
developer.mozilla.org
다시 공부해 보기
state 란 컴포넌트 내부에서 바뀔 수 있는 값을 의미한다.
const [value, setValue] = useState("ㅇㅇㅇ") ex) const [todos, setTodos] = useState("초기값")
useState란 hook은 먼저 const로 선언을 하고, [] 빈 배열을 생성하고, 배열의 첫 번째 자리에는 이 state의 이름, 두번째 자리에는 set을 붙이고 state의 이름을 붙인다. 그리고 useState는 이 state의 원하는 처음값을 넣어준다. 처음 값은 initial state라고 하는데 언제든 변할 수 있는 값이기 때문에 처음 값 이라는 개념이 존재한다.
props는 부모 컴포넌트로부터 받아온 데이터이다. 이 프로젝트에서는 MytodoList.jsx에서 todos 와 setTodos를 props로 Layout에서 필요로하는 자식 컴포넌트들에게 보내주고 또 그 컴포넌트들이 생성한 state를 그의 자식 컴포넌트들에게 보내주고있다.
이벤트 핸들러
onChangeHandler
import React, { useState } from "react";
const App = () => {
const [value, setValue] = useState("");
const onChangeHandler = (event) => {
const inputValue = event.target.value;
setValue(inputValue);
};
console.log(value) // value가 어떻게 변하는지 한번 콘솔로 볼까요?
return (
<div>
<input type="text" onChange={onChangeHandler} value={value} />
</div>
);
};
export default App;
위 프로젝트에서 onChangeHandler는 input값에 적용되었다. input에서는 보통 사용자가 입력한 값을 state로 관리하는 패턴을 많이 사용한다. input, useState를 이용해 input값을 넣을 value라는 state를 생성했다.
이제 이벤트 핸들러를 state와 연결하려면 input에 onChange라는 이벤트를 넣어주고, 우리가 생성한 이벤트 핸들러 함수를 넣는다.
import React, { useState } from "react";
const App = () => {
const [value, setValue] = useState("");
const onChangeHandler = (event) => {
const inputValue = event.target.value;
setValue(inputValue);
};
console.log(value) // value가 어떻게 변하는지 한번 콘솔로 볼까요?
return (
<div>
<input type="text" onChange={onChangeHandler} value={value} />
</div>
);
};
export default App;
그리고 이제 이벤트 핸들러 안에서 자바스크립트의 event 객체를 꺼내 사용할 수 있다. 사용자가 입력한 input값은 event.target.value로 꺼내 쓸 수가 있다. 마지막으로 state인 value를 input의 attribute인 value에 넣어주면 input, state 연결은 끝!
그래서 value를 콘솔에 찍으면, 입력할 때마다 value가 바뀐다. 이것은 리액트가 아닌 HTML DOM event의 개념!
onSubmitHandler
버튼의 type이 submit이면 수신하는 이벤트를 생략한다. 이것이 submit 이벤트인데. <form onSubmit={submitHandler} 라고 form이 제출될때마다 실행하는 함수를 만들 수 있다. 아니면 onClick={onSubmitHandler}라고 버튼에 직접 달아주는 방식이 있다.
처음에 예시 웹페이지를 개발자도구로 뜯어보는데 왜 버튼에 온클릭 함수가 안들어가있는지 의아했는데 form 태그에 onSubmit 함수를 넣을 수 있다는 것을 알고 놀라웠다. 여기서 submit 후 서버에 resquest를 보내는 것은 기본으로 실행되는 동작으로 새로고침이 되면 모든 동작들이 리로드가 되기 때문에 이것을 막기위해 preventDefault를 실행해준다.
DOM이란 XML이나 HTML에 접근하기 위한 일종의 인터페이스이고 이 객체 모델은 문서 내의 모든 요소를 정의하고, 각각의 요소에 접근하는 방법을 제공한다.
이러한 DOM은 W3C 표준 객체 모델이며 다음과 같이 계층 구조로 표현된다.
DOM의 종류
W3C DOM 표준은 세 가지 모델로 구분됩니다.
1. Core DOM : 모든 문서 타입을 위한 DOM 모델
2. HTML DOM : HTML 문서를 위한 DOM 모델. HTML 문서를 조작하고 접근하는 표준화된 방법을 정의한다.
3. XML DOM : XML 문서를 위한 DOM 모델. XML요소에 접근 가능하게 한다.
사용된 함수
Map () 반복되는 컴포넌트를 렌더링하기 위하여 자바스크립트 배열의 내장 함수인 map()을 사용한다.
파라미터로 전달된 함수를 사용해 배열 내 각 요소를 원하는 규칙에 따라 변환 후 새로운 배열을 생성.
import React from 'react';
import Main from './Main';
const App = () => {
const names = ["갓대희", "김대희", "한대희"]
const nameList = names.map((name) => (<Main name={name}/>))
return (
<div>
{nameList}
</div>
);
}
export default App;
App.js에 names를 선언해주고 배열 names를 map을 통해 원하는 컴포넌트에 name={name}으로 전달한다.
import React from 'react';
const Main = (props) => {
return (
<div>
<h3>안녕하세요. {props.name} 입니다.</h3>
</div>
);
};
export default Main;
Main 컴포넌트에서 {props.name}으로 받았다.
import React from 'react';
const App = () => {
const names = [
{userName : "갓대희", age : 19},
{userName : "김대희", age : 29},
{userName : "한대희", age : 39}
]
const nameList = names.map((v) => (<Main name={v.userName} age={v.age}/>))
return (
<div>
{nameList}
</div>
);
}
2차원 배열을 객체로 표현할 수 있다. Main에는 다음과 같이 씌여야 한다.
import React from 'react';
const Main = (props) => {
return (
<div>
<h3>안녕하세요. {props.name}({props.age}) 입니다.</h3>
</div>
);
};
export default Main;
결과는 안녕하세요 갓대희(19) ... 한대희 (39).
filter함수를 이용해 30대 이하만 표현 할 수도 있다.
import React from 'react';
import Main from './Main';
const App = () => {
const names = [
{userName : "갓대희", age : 19},
{userName : "김대희", age : 29},
{userName : "한대희", age : 39}
]
let nameList = names.filter(v => v.age < 30);
nameList = nameList.map(v => (<Main name={v.userName} age={v.age}/>));
return (
<div>
{nameList}
</div>
);
}
위와 같이 할 경우 생기는 문제는 key값이 없다는 것이다. 이때 오류메세지를 보게 되는데 각 자식들에게 key를 부여해야 한다는 것이다.
import React from 'react';
import Main from './Main';
const App = () => {
const names = [
{userName : "갓대희", age : 19},
{userName : "김대희", age : 29},
{userName : "한대희", age : 39}
]
const nameList = names.map(v => (<Main key={v.userName} name={v.userName} age={v.age}/>));
return (
<div>
{nameList}
</div>
);
}
export default App;
위에서 key={v.userName}으로 유저네임으로 키를 부여 해 주었다.
출처: https://goddaehee.tistory.com/303 [갓대희의 작은공간:티스토리]
다음은 프로젝트에서 쓰인 긁어온 코드
function List({setTodos, todos}) { //todos와 setTodos를 부모컴퍼넌트에게서 받았음
//filter 메소드는 각요소마다 콜백함수를 실행해서 리턴값을 true인것만 걸러내는 함수
const onRemove = (selectedId) => { //함수 실행시 todos 배열에서 todo.id가 피라미터와(selectedId) 일치하지 않는 요소만 추출해서 새로운 배열을 만듦. (= todo.id가 selectedId인 것을 제거하고 나머지만 배열에 남는다.)
console.log(onRemove)
const tobedoneTodos = todos.filter((todo) => {
return todo.id !== selectedId
})
setTodos(tobedoneTodos) //결과적으로 그 배열은 완료해야 할 리스트 = tobedoneTodos가 된다.
}
const TobeDone = (selectedId) => {
console.log(TobeDone)
const newTodos = todos.map((todo)=> {
if (todo.id === selectedId) { //id 비교 => 넘겨 받은 id 와 기존 todo list 에 있는 id 와 비교. id 가 같다면 => { ...todo } 를 이용하여 기존 객체를 복사. 그리고 { ...todo, isDone: !todo.isDone } 을 이용하여 기존 객체의 isDone 을 한번 뒤집어 취소로 변경됨.
return {...todo, isDone: !todo.isDone} // id 가 다르다면 => 아무런 변화도 주지 않는다.
} else {
return {...todo} // 아무 변화 없음
}
})
setTodos(newTodos)
};
함수 의미가 잘 이해가 안되어서 주석을 달아놓았다. onRemove를 시행하면 selectedId와 기존 배열에있던 id를 비교해 같은 것을 찾아내고, 그것은 삭제시킴과 동시에 selectedId가 아닌 것들을 반환해서 표현한다.
TobeDone 먼저 기존 id 값들과 사용자가 선택한 selectedId를 비교해서 같다면, todo의 기본값 isDone = false (완료버튼) 을 반전시켜서 취소버튼 상태로 만든다.
이것을 이해하는게 상당히 복잡했다. 이 버튼이 완료였다가 취소로 바뀌는건지 취소였다가 완료로 바뀌는건지 굉장히 헷갈렸다. 그리고 todo 컴포넌트에서 취소와 완료 글씨가 바뀌어야하기 때문에 {todo.isDone ? "취 소" : "완 료"} 삼항연산자로 표현했다.
회고
일단 개념을 잡는 것 자체가 힘들었다. 리액트를 처음 사용 해 보는데 누군가의 말 처럼 갑자기 들판에 내던져진 느낌이었다. 어디서부터 무엇을 시작해야 할 지 막막했다. 팀원 분들 중에는 잘하는 분들도 계셔서 과제를 받자마자 그 날 기능구현이 끝난 분들도 계셨다.
처음에 그 분들께 질문하면서 도움을 받아보았으나 내 머릿속에 이해가 안된 상태로 이렇게 저렇게 해보세요 하는 디렉션을 듣는것이 가르쳐주시는 분에게도 실례였고 나에게도 도움이 되지 않을 것이라 생각했다. 일단 하나로 합쳐진 컴포넌트를 쪼개는 것이 어렵다는 것을 깨달았고, 너무 급하게 하지 말고 주어진 참고자료를 천천히 보시라는 팀원분의 말씀에 다시 자료를 천천히 읽으면서 이해하려고 해보고, 지급된 강의도 좀 들으면서 개념을 살짝 잡아갔다. 그리고 개요도를 그려 본 것이 큰 도움이 된 것 같다. 썼던 코드들은 거의 모두 긁어온 것이라서 내가 긁어오면서 솔직히 이래도 되는걸까 자괴감이 많이 들었는데, 다른 분들이 무에서 유를 만들어 내기 위해서는 처음에 그렇게 시작하는 수 밖에 없고, 가져다 쓴 코드를 완벽히 이해하고 다른 프로젝트를 수행 할 때 가져다 쓰고, 응용 할 수 있느냐가 중요하다고 말씀해주셔서
용기를 얻어 이 프로젝트를 무사히 끝낼 수 있었다.
무엇보다도 도움을 많이 주셨던 팀원 분들과 예전 팀원 분들이 함께 오류를 살펴보고 같이 해답을 찾아나가는 과정이 있었기에 완성 할 수 있었던 프로젝트였다. 기꺼이 나의 일 처럼 도움을 주셨던 분들에게 깊이 감사드린다!
목요일 제출인 과제이기 때문에 남은 시간 이 코드를 충분히 이해해보고, 이걸 활용해서 간단한 프로젝트를 하나 만들어 보고 싶다.
그리도 다음주차 프로젝트에 나오게 될 라우팅과 훅을 공부해야겠다.
프로젝트 제작시 참고하기 좋은 사이트
1. props 통해 컴포넌트에게 값 전달하기
https://react.vlpt.us/basic/05-props.html
5. props 를 통해 컴포넌트에게 값 전달하기 · GitBook
5. props 를 통해 컴포넌트에게 값 전달하기 이번에는 컴포넌트의 props 라는 개념에 대해서 알아보겠습니다. props 는 properties 의 줄임말입니다. 우리가 어떠한 값을 컴포넌트에게 전달해줘야 할 때,
react.vlpt.us
2. 리액트의 렌더링은 어떻게 일어나는가?
https://yceffort.kr/2022/04/deep-dive-in-react-rendering#key%EC%99%80-reconciliation
Home
yceffort
yceffort.kr
3. spread 와 rest 는 어떻게 쓰이는가?
https://learnjs.vlpt.us/useful/07-spread-and-rest.html
07. spread 와 rest 문법 · GitBook
07. spread 와 rest 이번에는 ES6 에서 도입된 spread 와 rest 문법에 대해서 알아보겠습니다. 서로 완전히 다른 문법인데요, 은근히 좀 비슷합니다. spread 일단 spread 문법부터 알아봅시다. spread 라는 단어
learnjs.vlpt.us
4. todoList 만들기
https://zinirun.github.io/2020/08/27/react-first-todolist/
React 첫번째 프로젝트 - TodoList | zinirun
개발 타임랩스 은근히 재밌다.. 리액트로 만든 첫번째 프로젝트, 투두리스트 항상 뭔가를 배우면 그걸로 프로젝트를 하나 만들어봐야(기왕이면 배포까지 해봐야) 직성이 풀리고 그 다음 주제로
zinirun.github.io
5. Todo App
https://simplereact.gitbook.io/simplereact/10.-todo-app-update-status
10. TODO APP (UPDATE - STATUS) - simpleReact
function Todo({ todo: { id, text, isDone }, onDelete, onUpdateStatus }) {
simplereact.gitbook.io
6. 반복되는 컴포넌트 효율적으로 보여주기 (map함수)
https://chanhuiseok.github.io/posts/react-8/
[React] 반복되는 컴포넌트를 효율적으로 보여주기 - map 함수
컴퓨터/IT/알고리즘 정리 블로그
chanhuiseok.github.io
7. filter 함수
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/filter\
Array.prototype.filter() - JavaScript | MDN
filter() 메서드는 주어진 함수의 테스트를 통과하는 모든 요소를 모아 새로운 배열로 반환합니다.
developer.mozilla.org
8. 함수 선언문, 표현식, 화살표 함수 비교
https://velog.io/@bigbrothershin/%EC%98%A4%EB%8A%98-%EA%B3%B5%EB%B6%80%ED%95%A0-%EA%B2%832020.1.30
JavaScript - 함수 선언문, 함수 표현식 그리고 화살표 함수 비교
JavaScript - 함수 선언문 방식과 함수 표현식 방식의 차이
velog.io
9. map 안에서 3항 연산자 사용하기
[react] map 안에서 3항 연산자 사용하기
프로젝트를 진행하며 가장 많이 고민하고 시도해봤는데 결국 기초오브기초가 없어서 고생했던 리뷰완료 처리에 대해 이야기해 보자. (찌그러진 아이콘은 무시해주세요..)resgame: 완료한 게임 ->
velog.io
10. 구조분해할당
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
구조 분해 할당 - JavaScript | MDN
구조 분해 할당 구문은 배열이나 객체의 속성을 해체하여 그 값을 개별 변수에 담을 수 있게 하는 JavaScript 표현식입니다.
developer.mozilla.org
'Sparta x 이노베이션 캠프 > 팀 프로젝트' 카테고리의 다른 글
6주차: React 미니프로젝트 "오늘도 무사히" (0) | 2022.09.12 |
---|---|
4주차 : React로 Redux를 활용한 My To Do List 만들기. map is not a function (1) | 2022.08.25 |
4주차 : React로 Redux를 이용한 Todolist 만들기 (0) | 2022.08.25 |
1주차 : [About me] 로그인&회원가입 페이지, 이미지 드래그&드롭 파일 업로드 구현(feat. Phython) (0) | 2022.08.03 |
1주차 : [About me] 미니프로젝트 포트폴리오 업로드 웹사이트 만들기 (0) | 2022.08.01 |