React

 

따라하며 배우는 리액트 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란 무엇인가?

 

강의: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/unit/119853?tab=curriculum

 

 

 

 

 

 

 

 

 

 

15.To-Do 앱을 클래스 컴포넌트에서 함수형 컴포넌트로 바꾸기

 

강의:

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/unit/119854?tab=curriculum

 

 

 

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

 

강의:

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/unit/119855?tab=curriculum

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

17.할 일 목록 부분을 위한 컴포넌트 생성하기(컴포넌트 분리하기)

강의:

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/unit/119856?tab=curriculum

 

 

 

 

 

 

 

 

 

 

 

 

 

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 부분을 위한 컴포넌트 생성하기

강의:

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/unit/119858?tab=curriculum

 

 

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://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/unit/119859?tab=curriculum

 

 

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 버전에서 나는 에러 해결 방법]

 

 

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/unit/120666?tab=curriculum

 

리액트 18버전을 사용할 때 드래그 앤 드랍 기능을 사용하면 이러한 에러가 나옵니다.

react-beautiful-dnd

Unable to find draggable with id : 123124124

 

그럴 때는 리액트의 StricMode를 제거해주시면 됩니다.

 

root.render( <App/>


);

 

감사합니다.

 

 

 

 

 

 

 

 

 

 

22.Drag and Drop 기능 추가하기

강의:

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/unit/119863?tab=curriculum

 

설치 :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 >
    );


}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

about author

PHRASE

Level 60  라이트

쓸데없는 생각이 자꾸 떠오를 때는 책을 읽어라. 쓸데없는 생각은 비교적 한가한 사람들이 느끼는 것이지 분주한 사람이 느끼지 않는다. 우리는 한가한 시간이 생길 때마다 유익한 책을 읽어 마음의 양식을 쌓아 두어야 한다. -처칠

댓글 ( 4)

댓글 남기기

작성