따라하며 배우는 리액트 A-Z
[프론트엔드, 웹 개발] 강의입니다.
이 강의를 통해 리액트 기초부터 중급까지 배우게 됩니다. 하나의 강의로 개념도 익히고 실습도 하며, 리액트를 위해 필요한 대부분의 지식을 한번에 습득할 수 있도록 만들었습니다.
✍️
이런 걸
배워요!
리액트
NextJS
타입스크립트
정적 사이트 자동 배포
도커
강의: https://www.inflearn.com/course/%EB%94%B0%EB%9D%BC%ED%95%98%EB%8A%94-%EB%A6%AC%EC%95%A1%ED%8A%B8#
강의 자료 : https://github.com/braverokmc79/DiagramPDF
소스: https://github.dev/braverokmc79/react-todo-app
[3].To-Do 앱 최적화 하기
14.React Hooks란 무엇인가?
15.To-Do 앱을 클래스 컴포넌트에서 함수형 컴포넌트로 바꾸기
강의:
App.js
import React, { useState } from "react"; import "./App.css"; export default function App() { const [todoData, setTodoData] = useState([]); const [value, setValue] = useState(""); const btnStyle = { color: "#fff", border: "none", padding: "5px 9px", borderRadius: "50%", cursor: "pointer", float: "right" } const getStyle = (completed) => { return { padding: "10px", borderBottom: "1px #ccc dotted", textDecoration: completed ? "line-through" : "none" } } const handleClick = (id) => { let newTodoData = todoData.filter(data => data.id !== id); setTodoData(newTodoData); } const handleChnage = (e) => { setValue(e.target.value); } const handleSubmit = (e) => { e.preventDefault(); let newTodoData = { id: Date.now(), title: value, completed: false } setTodoData(prev => [...prev, newTodoData]) setValue(""); } const handleCompleteChange = (id) => { let newTodoData = todoData.map(data => { if (data.id === id) { data.completed = !data.completed } return data; }); setTodoData(newTodoData); } return ( <div className="container" > <div className="todoBlock"> <div className="title"> <h1>할일 목록</h1> </div> <form style={{ display: 'flex' }} onSubmit={handleSubmit}> <input type="text" name="value" style={{ flex: "10", padding: "5px" }} placeholder="해야 할일을 입력 하세요." value={value} onChange={handleChnage} /> <input type="submit" name="입력" className="btn" style={{ flex: '1' }} /> </form> {todoData.map(data => { return ( <div style={getStyle(data.completed)} key={data.id}> <input type="checkbox" defaultChecked={data.completed} onChange={() => handleCompleteChange(data.id)} /> {data.title} <button style={btnStyle} onClick={() => handleClick(data.id)}>x</button> </div> ) })} </div> </div > ) }
16.state와 props
강의:
17.할 일 목록 부분을 위한 컴포넌트 생성하기(컴포넌트 분리하기)
강의:
1.App.js
import React, { useState } from "react"; import "./App.css"; import List from "./components/List"; export default function App() { const [todoData, setTodoData] = useState([]); const [value, setValue] = useState(""); const handleChnage = (e) => { setValue(e.target.value); } const handleSubmit = (e) => { e.preventDefault(); let newTodoData = { id: Date.now(), title: value, completed: false } setTodoData(prev => [...prev, newTodoData]) setValue(""); } return ( <div className="container" > <div className="todoBlock"> <div className="title"> <h1>할일 목록</h1> </div> <form style={{ display: 'flex' }} onSubmit={handleSubmit}> <input type="text" name="value" style={{ flex: "10", padding: "5px" }} placeholder="해야 할일을 입력 하세요." value={value} onChange={handleChnage} /> <input type="submit" name="입력" className="btn" style={{ flex: '1' }} /> </form> <List todoData={todoData} setTodoData={setTodoData} /> </div> </div > ) }
2.components/List.js
import React from 'react' export default function List({ todoData, setTodoData }) { const btnStyle = { color: "#fff", border: "none", padding: "5px 9px", borderRadius: "50%", cursor: "pointer", float: "right" } const getStyle = (completed) => { return { padding: "10px", borderBottom: "1px #ccc dotted", textDecoration: completed ? "line-through" : "none" } } const handleClick = (id) => { let newTodoData = todoData.filter(data => data.id !== id); setTodoData(newTodoData); } const handleCompleteChange = (id) => { let newTodoData = todoData.map(data => { if (data.id === id) { data.completed = !data.completed } return data; }); setTodoData(newTodoData); } return ( <> {todoData.map(data => { return ( <div style={getStyle(data.completed)} key={data.id}> <input type="checkbox" defaultChecked={data.completed} onChange={() => handleCompleteChange(data.id)} /> {data.title} <button style={btnStyle} onClick={() => handleClick(data.id)}>x</button> </div> ) })} </> ); }
18.구조 분해 할당(Destructuring)
강의:
https://www.inflearn.com/course/따라하는-리액트/unit/119857?tab=curriculum
19.Form 부분을 위한 컴포넌트 생성하기
강의:
App.js
import React, { useState } from "react"; import "./App.css"; import Form from "./components/Form"; import List from "./components/List"; export default function App() { const [todoData, setTodoData] = useState([]); return ( <div className="container" > <div className="todoBlock"> <div className="title"> <h1>할일 목록</h1> </div> <Form setTodoData={setTodoData} /> <List todoData={todoData} setTodoData={setTodoData} /> </div> </div > ) }
Form.js
import React, { useState } from 'react'; const Form = ({ setTodoData }) => { const [value, setValue] = useState(""); const handleChnage = (e) => { setValue(e.target.value); } const handleSubmit = (e) => { e.preventDefault(); let newTodoData = { id: Date.now(), title: value, completed: false } setTodoData(prev => [...prev, newTodoData]) setValue(""); } return ( <> <form style={{ display: 'flex' }} onSubmit={handleSubmit}> <input type="text" name="value" style={{ flex: "10", padding: "5px" }} placeholder="해야 할일을 입력 하세요." value={value} onChange={handleChnage} /> <input type="submit" name="입력" className="btn" style={{ flex: '1' }} /> </form> </> ); }; export default Form;
[참조] 리액트 모듈 설치 시 나는 종속성(dependency) 에러 해결 방법
리액트18 버전에서 라이브러리들을 설치할 때
종속성에 관한 에러가 날 때가 많이 있습니다.
npm ERR! code ERESOLVE
npm ERR! ERESOLVE unable to resolve dependency tree
npm ERR! Found: react@18.1.0
npm ERR! node_modules/react
npm ERR! react@"^18.1.0" from the root project
npm ERR! Could not resolve dependency:
npm ERR! peer react@"^17.0.1" from react-dnd...
[원인]
unable to resolve dependency tree
리액트 18 버전 라이브러리와 설치하려는 해당 라이브러리의 종속성이 안 맞기 때문입니다.
[해결 방법]
이럴 때는 여러 가지 해결방법이 있는데
첫 번째는 리액트 버전을 17로 낮추는 것인데 별로 좋은 방법은 아닙니다.
다른 방법은 npm 대신에 yarn을 이용해서 yarn install로 종속성을 설치해주는 방법입니다.
만약 yarn으로 설치해도 안된다면
npm의 강제 설치 옵션으로 설치해주시면 됩니다.
--legacy-peer-deps : 기존 버전 다 무시하고 일단 설치.
--force : package-lock.json에 몇가지의 다른 의존 버전들을 추가하면서 설치.
20.TailWindCss소개
강의:
https://tailwindcss.com/docs/installation
npm install -D tailwindcss npx tailwindcss init
tailwind.config.js
/** @type {import('tailwindcss').Config} */ module.exports = { content: ["./src/**/*.{html,js}"], theme: { extend: {}, }, plugins: [], }
src/input.css
@tailwind base; @tailwind components; @tailwind utilities;
src/index.html
<!doctype html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link href="/dist/output.css" rel="stylesheet"> </head> <body> <h1 class="text-3xl font-bold underline"> Hello world! </h1> </body> </html>
21.TailWindCss로 Todo 앱 스타일링 해주기
강의:
https://www.inflearn.com/course/따라하는-리액트/unit/119860?tab=curriculum
1.App.js
import React, { useState } from "react"; import "./App.css"; import Form from "./components/Form"; import List from "./components/List"; export default function App() { const [todoData, setTodoData] = useState([]); return ( <div className="flex items-center justify-center w-screen h-screen bg-blue-300"> <div className="w-full p-6 m-4 bg-white rounded shadow lg:w-3/4 lg:max-w-lg "> <div className="flex justify-between mb-3"> <h1 className="justify-center text-2xl font-bold text-center">할일 목록</h1> </div> <Form setTodoData={setTodoData} /> <List todoData={todoData} setTodoData={setTodoData} /> </div> </div > ) }
2. components/List.js
import React from 'react' export default function List({ todoData, setTodoData }) { const btnStyle = { color: "#fff", border: "none", padding: "5px 9px", borderRadius: "50%", cursor: "pointer", float: "right" } const getStyle = (completed) => { return { padding: "10px", borderBottom: "1px #ccc dotted", textDecoration: completed ? "line-through" : "none" } } const handleClick = (id) => { let newTodoData = todoData.filter(data => data.id !== id); setTodoData(newTodoData); } const handleCompleteChange = (id) => { let newTodoData = todoData.map(data => { if (data.id === id) { data.completed = !data.completed; } return data; }); setTodoData(newTodoData); } return ( <> {todoData.map(data => { return ( <div className='flex items-center justify-between w-full px-4 py-1 my-2 text-gray-600 bg-gray-100 border rounded' style={getStyle(data.completed)} key={data.id}> <div> <input type="checkbox" defaultChecked={data.completed} onChange={() => handleCompleteChange(data.id)} /> <span className={data.completed ? "line-through" : undefined}>{data.title}</span> </div> <div className='items-center'> <button className='px-4 py-2 float-right' onClick={() => handleClick(data.id)}>x</button> </div> </div> ) })} </> ); }
3.components/Form.js
import React, { useState } from 'react'; const Form = ({ setTodoData }) => { const [value, setValue] = useState(""); const handleChnage = (e) => { setValue(e.target.value); } const handleSubmit = (e) => { e.preventDefault(); let newTodoData = { id: Date.now(), title: value, completed: false } setTodoData(prev => [...prev, newTodoData]) setValue(""); } return ( <> <form onSubmit={handleSubmit} className="flex pt-2"> <input className="w-full px-3 py-2 text-gray-500 rounded shadow border-2 border-gray-400" type="text" name="value" placeholder="해야 할일을 입력 하세요." value={value} onChange={handleChnage} /> <input className='p-2 text-blue-400 border-2 border-blue-400 rounded hover:text-white hover:bg-blue-20' type="submit" value="입력" /> </form> </> ); }; export default Form;
[react-beautiful-dnd react 18 버전에서 나는 에러 해결 방법]
리액트 18버전을 사용할 때 드래그 앤 드랍 기능을 사용하면 이러한 에러가 나옵니다.
react-beautiful-dnd Unable to find draggable with id : 123124124
그럴 때는 리액트의 StricMode를 제거해주시면 됩니다.
root.render( <App/> );
감사합니다.
22.Drag and Drop 기능 추가하기
강의:
설치 :https://www.npmjs.com/package/react-beautiful-dnd
npm i react-beautiful-dnd
components/List.js
import React from 'react' import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd'; export default function List({ todoData, setTodoData }) { const handleClick = (id) => { let newTodoData = todoData.filter(data => data.id !== id); setTodoData(newTodoData); } const handleCompleteChange = (id) => { let newTodoData = todoData.map(data => { if (data.id === id) { data.completed = !data.completed; } return data; }); setTodoData(newTodoData); } const handleEnd = (result) => { //result 매개변수에는 source 항목 및 대상 위치와 같은 드래그 이벤트에 대한 정보가 포함됩니다. console.log("handleEnd : ", result); //목적지가 없으면(이벤트 취소) 이 함수를 종료합니다. if (!result.destination) return; //리액트 불변성을 지켜주기 위해 새로운 todoData 생성 const newTodoData = todoData; //1. 변경시키는 아이템을 배열에서 지워줍니다. //2. return 값으로 채워진 아이템을 집어줍니다. const [reorderItem] = newTodoData.splice(result.source.index, 1); //원하는 자리에 reorderItem 을 insert 해줍니다. newTodoData.splice(result.destination.index, 0, reorderItem); setTodoData(newTodoData); } return ( <div> <DragDropContext onDragEnd={handleEnd}> <Droppable droppableId='to-dos'> {(provided) => ( <div {...provided.droppableProps} ref={provided.innerRef}> {todoData.map((data, index) => ( <Draggable key={data.id} draggableId={data.id.toString()} index={index} > {(provided, snapshot) => ( <div key={data.id} {...provided.draggableProps} ref={provided.innerRef} {...provided.dragHandleProps} className={snapshot.isDragging ? "selected" : "not-selected"} > <div className={`${snapshot.isDragging ? "bg-gray-100" : "bg-gray-400"} flex items-center justify-between w-full px-4 py-1 my-2 text-gray-600 border rounded`} key={data.id}> <div> <input type="checkbox" defaultChecked={data.completed} onChange={() => handleCompleteChange(data.id)} /> <span className={`${data.completed ? "line-through" : undefined} px-2`}>{data.title}</span> </div> <div className='items-center'> <button className='px-4 py-2 float-right' onClick={() => handleClick(data.id)}>x</button> </div> </div> </div> )} </Draggable> ))} </div> )} </Droppable> </DragDropContext> </div > ); }
댓글 ( 4)
댓글 남기기