├── 03. chapter3
├── .gitignore
├── sample.js
├── index.js
├── circle.js
├── package.json
└── package-lock.json
├── 08. project2
├── src
│ ├── index.css
│ ├── component
│ │ ├── Header.css
│ │ ├── Header.js
│ │ ├── TodoList.css
│ │ ├── TodoEditor.css
│ │ ├── TodoItem.css
│ │ ├── TodoItem.js
│ │ ├── TodoEditor.js
│ │ └── TodoList.js
│ ├── index.js
│ ├── App.css
│ └── App.js
├── public
│ ├── robots.txt
│ ├── favicon.ico
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── index.html
├── .gitignore
└── package.json
├── 09. chapter7
├── src
│ ├── index.css
│ ├── component
│ │ ├── Header.css
│ │ ├── Header.js
│ │ ├── TodoList.css
│ │ ├── TodoEditor.css
│ │ ├── TodoItem.css
│ │ ├── TodoItem.js
│ │ ├── TestComp.js
│ │ ├── TodoEditor.js
│ │ └── TodoList.js
│ ├── index.js
│ ├── App.css
│ └── App.js
├── public
│ ├── robots.txt
│ ├── favicon.ico
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── index.html
├── .gitignore
└── package.json
├── 10. chapter8
├── src
│ ├── index.css
│ ├── component
│ │ ├── Header.css
│ │ ├── Header.js
│ │ ├── TodoList.css
│ │ ├── TodoEditor.css
│ │ ├── TodoItem.css
│ │ ├── TodoItem.js
│ │ ├── TestComp.js
│ │ ├── TodoEditor.js
│ │ └── TodoList.js
│ ├── index.js
│ ├── App.css
│ └── App.js
├── public
│ ├── robots.txt
│ ├── favicon.ico
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── index.html
├── .gitignore
└── package.json
├── 11. chapter9
├── src
│ ├── index.css
│ ├── component
│ │ ├── Header.css
│ │ ├── Header.js
│ │ ├── TodoList.css
│ │ ├── TodoEditor.css
│ │ ├── TodoItem.css
│ │ ├── TestComp.js
│ │ ├── TodoItem.js
│ │ ├── TodoEditor.js
│ │ └── TodoList.js
│ ├── index.js
│ ├── App.css
│ └── App.js
├── public
│ ├── robots.txt
│ ├── favicon.ico
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── index.html
├── .gitignore
└── package.json
├── 12. project3
├── src
│ ├── App.css
│ ├── img
│ │ ├── emotion1.png
│ │ ├── emotion2.png
│ │ ├── emotion3.png
│ │ ├── emotion4.png
│ │ └── emotion5.png
│ ├── index.js
│ ├── component
│ │ ├── Header.js
│ │ ├── Button.js
│ │ ├── Button.css
│ │ ├── Header.css
│ │ ├── EmotionItem.js
│ │ ├── DiaryList.css
│ │ ├── EmotionItem.css
│ │ ├── Editor.css
│ │ ├── Viewer.js
│ │ ├── DiaryItem.js
│ │ ├── DiaryItem.css
│ │ ├── Viewer.css
│ │ ├── DiaryList.js
│ │ └── Editor.js
│ ├── index.css
│ ├── hooks
│ │ └── useDiary.js
│ ├── pages
│ │ ├── New.js
│ │ ├── Diary.js
│ │ ├── Home.js
│ │ └── Edit.js
│ ├── util.js
│ └── App.js
├── public
│ ├── robots.txt
│ ├── favicon.ico
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── index.html
├── .gitignore
└── package.json
├── 13. chapter10
├── src
│ ├── App.css
│ ├── img
│ │ ├── emotion1.png
│ │ ├── emotion2.png
│ │ ├── emotion3.png
│ │ ├── emotion4.png
│ │ └── emotion5.png
│ ├── index.js
│ ├── component
│ │ ├── Header.js
│ │ ├── Button.js
│ │ ├── Button.css
│ │ ├── Header.css
│ │ ├── EmotionItem.js
│ │ ├── DiaryList.css
│ │ ├── Editor.css
│ │ ├── EmotionItem.css
│ │ ├── Viewer.js
│ │ ├── DiaryItem.js
│ │ ├── DiaryItem.css
│ │ ├── Viewer.css
│ │ ├── DiaryList.js
│ │ └── Editor.js
│ ├── index.css
│ ├── hooks
│ │ └── useDiary.js
│ ├── pages
│ │ ├── New.js
│ │ ├── Diary.js
│ │ ├── Home.js
│ │ └── Edit.js
│ └── util.js
├── public
│ ├── robots.txt
│ ├── favicon.ico
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── index.html
├── .gitignore
└── package.json
├── 14. chapter11
├── src
│ ├── App.css
│ ├── img
│ │ ├── emotion1.png
│ │ ├── emotion2.png
│ │ ├── emotion3.png
│ │ ├── emotion4.png
│ │ └── emotion5.png
│ ├── index.js
│ ├── component
│ │ ├── Header.js
│ │ ├── Button.js
│ │ ├── Button.css
│ │ ├── Header.css
│ │ ├── EmotionItem.js
│ │ ├── DiaryList.css
│ │ ├── Editor.css
│ │ ├── EmotionItem.css
│ │ ├── Viewer.js
│ │ ├── DiaryItem.js
│ │ ├── DiaryItem.css
│ │ ├── Viewer.css
│ │ ├── DiaryList.js
│ │ └── Editor.js
│ ├── index.css
│ ├── hooks
│ │ └── useDiary.js
│ ├── pages
│ │ ├── New.js
│ │ ├── Diary.js
│ │ ├── Home.js
│ │ └── Edit.js
│ └── util.js
├── public
│ ├── robots.txt
│ ├── favicon.ico
│ ├── logo192.png
│ ├── logo512.png
│ ├── thumbnail.png
│ ├── manifest.json
│ └── index.html
├── .gitignore
└── package.json
├── 05. chapter5
├── src
│ ├── component
│ │ ├── Body.css
│ │ ├── Footer.js
│ │ ├── Header.js
│ │ └── Body.js
│ ├── index.js
│ ├── index.css
│ ├── App.js
│ └── App.css
├── public
│ ├── robots.txt
│ ├── favicon.ico
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── index.html
├── .gitignore
├── package.json
└── README.md
├── 04. chapter4
├── public
│ ├── robots.txt
│ ├── favicon.ico
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── index.html
├── src
│ ├── App.js
│ ├── setupTests.js
│ ├── App.test.js
│ ├── index.css
│ ├── reportWebVitals.js
│ ├── index.js
│ ├── App.css
│ └── logo.svg
├── .gitignore
├── package.json
└── README.md
├── 06. project1
├── public
│ ├── robots.txt
│ ├── favicon.ico
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── index.html
├── src
│ ├── component
│ │ ├── Viewer.js
│ │ └── Controller.js
│ ├── index.js
│ ├── App.css
│ ├── index.css
│ └── App.js
├── .gitignore
├── package.json
└── README.md
├── 07. chapter6
├── public
│ ├── robots.txt
│ ├── favicon.ico
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── index.html
├── src
│ ├── component
│ │ ├── Viewer.js
│ │ ├── Even.js
│ │ └── Controller.js
│ ├── index.js
│ ├── App.css
│ ├── index.css
│ └── App.js
├── .gitignore
└── package.json
├── README.md
└── CodeSandbox.md
/03. chapter3/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
--------------------------------------------------------------------------------
/03. chapter3/sample.js:
--------------------------------------------------------------------------------
1 | console.log("hello");
2 |
--------------------------------------------------------------------------------
/08. project2/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0px;
3 | }
4 |
--------------------------------------------------------------------------------
/09. chapter7/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0px;
3 | }
4 |
--------------------------------------------------------------------------------
/10. chapter8/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0px;
3 | }
4 |
--------------------------------------------------------------------------------
/11. chapter9/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0px;
3 | }
4 |
--------------------------------------------------------------------------------
/12. project3/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | padding-left: 20px;
3 | padding-right: 20px;
4 | }
5 |
--------------------------------------------------------------------------------
/13. chapter10/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | padding-left: 20px;
3 | padding-right: 20px;
4 | }
5 |
--------------------------------------------------------------------------------
/14. chapter11/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | padding-left: 20px;
3 | padding-right: 20px;
4 | }
5 |
--------------------------------------------------------------------------------
/05. chapter5/src/component/Body.css:
--------------------------------------------------------------------------------
1 | .body {
2 | background-color: green;
3 | color: blue;
4 | }
5 |
--------------------------------------------------------------------------------
/08. project2/src/component/Header.css:
--------------------------------------------------------------------------------
1 | .Header h1 {
2 | margin-bottom: 0px;
3 | color: #1f93ff;
4 | }
5 |
--------------------------------------------------------------------------------
/09. chapter7/src/component/Header.css:
--------------------------------------------------------------------------------
1 | .Header h1 {
2 | margin-bottom: 0px;
3 | color: #1f93ff;
4 | }
5 |
--------------------------------------------------------------------------------
/10. chapter8/src/component/Header.css:
--------------------------------------------------------------------------------
1 | .Header h1 {
2 | margin-bottom: 0px;
3 | color: #1f93ff;
4 | }
5 |
--------------------------------------------------------------------------------
/11. chapter9/src/component/Header.css:
--------------------------------------------------------------------------------
1 | .Header h1 {
2 | margin-bottom: 0px;
3 | color: #1f93ff;
4 | }
5 |
--------------------------------------------------------------------------------
/04. chapter4/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/05. chapter5/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/06. project1/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/07. chapter6/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/08. project2/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/09. chapter7/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/10. chapter8/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/11. chapter9/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/12. project3/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/13. chapter10/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/14. chapter11/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/04. chapter4/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/winterlood/one-bite-react/HEAD/04. chapter4/public/favicon.ico
--------------------------------------------------------------------------------
/04. chapter4/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/winterlood/one-bite-react/HEAD/04. chapter4/public/logo192.png
--------------------------------------------------------------------------------
/04. chapter4/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/winterlood/one-bite-react/HEAD/04. chapter4/public/logo512.png
--------------------------------------------------------------------------------
/05. chapter5/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/winterlood/one-bite-react/HEAD/05. chapter5/public/favicon.ico
--------------------------------------------------------------------------------
/05. chapter5/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/winterlood/one-bite-react/HEAD/05. chapter5/public/logo192.png
--------------------------------------------------------------------------------
/05. chapter5/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/winterlood/one-bite-react/HEAD/05. chapter5/public/logo512.png
--------------------------------------------------------------------------------
/06. project1/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/winterlood/one-bite-react/HEAD/06. project1/public/favicon.ico
--------------------------------------------------------------------------------
/06. project1/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/winterlood/one-bite-react/HEAD/06. project1/public/logo192.png
--------------------------------------------------------------------------------
/06. project1/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/winterlood/one-bite-react/HEAD/06. project1/public/logo512.png
--------------------------------------------------------------------------------
/07. chapter6/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/winterlood/one-bite-react/HEAD/07. chapter6/public/favicon.ico
--------------------------------------------------------------------------------
/07. chapter6/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/winterlood/one-bite-react/HEAD/07. chapter6/public/logo192.png
--------------------------------------------------------------------------------
/07. chapter6/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/winterlood/one-bite-react/HEAD/07. chapter6/public/logo512.png
--------------------------------------------------------------------------------
/08. project2/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/winterlood/one-bite-react/HEAD/08. project2/public/favicon.ico
--------------------------------------------------------------------------------
/08. project2/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/winterlood/one-bite-react/HEAD/08. project2/public/logo192.png
--------------------------------------------------------------------------------
/08. project2/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/winterlood/one-bite-react/HEAD/08. project2/public/logo512.png
--------------------------------------------------------------------------------
/09. chapter7/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/winterlood/one-bite-react/HEAD/09. chapter7/public/favicon.ico
--------------------------------------------------------------------------------
/09. chapter7/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/winterlood/one-bite-react/HEAD/09. chapter7/public/logo192.png
--------------------------------------------------------------------------------
/09. chapter7/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/winterlood/one-bite-react/HEAD/09. chapter7/public/logo512.png
--------------------------------------------------------------------------------
/10. chapter8/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/winterlood/one-bite-react/HEAD/10. chapter8/public/favicon.ico
--------------------------------------------------------------------------------
/10. chapter8/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/winterlood/one-bite-react/HEAD/10. chapter8/public/logo192.png
--------------------------------------------------------------------------------
/10. chapter8/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/winterlood/one-bite-react/HEAD/10. chapter8/public/logo512.png
--------------------------------------------------------------------------------
/11. chapter9/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/winterlood/one-bite-react/HEAD/11. chapter9/public/favicon.ico
--------------------------------------------------------------------------------
/11. chapter9/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/winterlood/one-bite-react/HEAD/11. chapter9/public/logo192.png
--------------------------------------------------------------------------------
/11. chapter9/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/winterlood/one-bite-react/HEAD/11. chapter9/public/logo512.png
--------------------------------------------------------------------------------
/12. project3/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/winterlood/one-bite-react/HEAD/12. project3/public/favicon.ico
--------------------------------------------------------------------------------
/12. project3/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/winterlood/one-bite-react/HEAD/12. project3/public/logo192.png
--------------------------------------------------------------------------------
/12. project3/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/winterlood/one-bite-react/HEAD/12. project3/public/logo512.png
--------------------------------------------------------------------------------
/12. project3/src/img/emotion1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/winterlood/one-bite-react/HEAD/12. project3/src/img/emotion1.png
--------------------------------------------------------------------------------
/12. project3/src/img/emotion2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/winterlood/one-bite-react/HEAD/12. project3/src/img/emotion2.png
--------------------------------------------------------------------------------
/12. project3/src/img/emotion3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/winterlood/one-bite-react/HEAD/12. project3/src/img/emotion3.png
--------------------------------------------------------------------------------
/12. project3/src/img/emotion4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/winterlood/one-bite-react/HEAD/12. project3/src/img/emotion4.png
--------------------------------------------------------------------------------
/12. project3/src/img/emotion5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/winterlood/one-bite-react/HEAD/12. project3/src/img/emotion5.png
--------------------------------------------------------------------------------
/13. chapter10/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/winterlood/one-bite-react/HEAD/13. chapter10/public/favicon.ico
--------------------------------------------------------------------------------
/13. chapter10/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/winterlood/one-bite-react/HEAD/13. chapter10/public/logo192.png
--------------------------------------------------------------------------------
/13. chapter10/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/winterlood/one-bite-react/HEAD/13. chapter10/public/logo512.png
--------------------------------------------------------------------------------
/14. chapter11/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/winterlood/one-bite-react/HEAD/14. chapter11/public/favicon.ico
--------------------------------------------------------------------------------
/14. chapter11/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/winterlood/one-bite-react/HEAD/14. chapter11/public/logo192.png
--------------------------------------------------------------------------------
/14. chapter11/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/winterlood/one-bite-react/HEAD/14. chapter11/public/logo512.png
--------------------------------------------------------------------------------
/13. chapter10/src/img/emotion1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/winterlood/one-bite-react/HEAD/13. chapter10/src/img/emotion1.png
--------------------------------------------------------------------------------
/13. chapter10/src/img/emotion2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/winterlood/one-bite-react/HEAD/13. chapter10/src/img/emotion2.png
--------------------------------------------------------------------------------
/13. chapter10/src/img/emotion3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/winterlood/one-bite-react/HEAD/13. chapter10/src/img/emotion3.png
--------------------------------------------------------------------------------
/13. chapter10/src/img/emotion4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/winterlood/one-bite-react/HEAD/13. chapter10/src/img/emotion4.png
--------------------------------------------------------------------------------
/13. chapter10/src/img/emotion5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/winterlood/one-bite-react/HEAD/13. chapter10/src/img/emotion5.png
--------------------------------------------------------------------------------
/14. chapter11/public/thumbnail.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/winterlood/one-bite-react/HEAD/14. chapter11/public/thumbnail.png
--------------------------------------------------------------------------------
/14. chapter11/src/img/emotion1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/winterlood/one-bite-react/HEAD/14. chapter11/src/img/emotion1.png
--------------------------------------------------------------------------------
/14. chapter11/src/img/emotion2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/winterlood/one-bite-react/HEAD/14. chapter11/src/img/emotion2.png
--------------------------------------------------------------------------------
/14. chapter11/src/img/emotion3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/winterlood/one-bite-react/HEAD/14. chapter11/src/img/emotion3.png
--------------------------------------------------------------------------------
/14. chapter11/src/img/emotion4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/winterlood/one-bite-react/HEAD/14. chapter11/src/img/emotion4.png
--------------------------------------------------------------------------------
/14. chapter11/src/img/emotion5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/winterlood/one-bite-react/HEAD/14. chapter11/src/img/emotion5.png
--------------------------------------------------------------------------------
/05. chapter5/src/component/Footer.js:
--------------------------------------------------------------------------------
1 | function Footer() {
2 | return (
3 |
6 | );
7 | }
8 | export default Footer;
9 |
--------------------------------------------------------------------------------
/03. chapter3/index.js:
--------------------------------------------------------------------------------
1 | import lodash from "lodash";
2 |
3 | const arr = [1, 1, 1, 2, 2, 1, 1, 4, 4, 3, 2];
4 | const uniqueArr = lodash.uniqBy(arr);
5 |
6 | console.log(uniqueArr);
7 |
--------------------------------------------------------------------------------
/05. chapter5/src/component/Header.js:
--------------------------------------------------------------------------------
1 | function Header() {
2 | return (
3 |
6 | );
7 | }
8 |
9 | export default Header;
10 |
--------------------------------------------------------------------------------
/06. project1/src/component/Viewer.js:
--------------------------------------------------------------------------------
1 | const Viewer = ({ count }) => {
2 | return (
3 |
4 |
현재 카운트 :
5 |
{count}
6 |
7 | );
8 | };
9 | export default Viewer;
10 |
--------------------------------------------------------------------------------
/07. chapter6/src/component/Viewer.js:
--------------------------------------------------------------------------------
1 | const Viewer = ({ count }) => {
2 | return (
3 |
4 |
현재 카운트 :
5 |
{count}
6 |
7 | );
8 | };
9 | export default Viewer;
10 |
--------------------------------------------------------------------------------
/04. chapter4/src/App.js:
--------------------------------------------------------------------------------
1 | import logo from "./logo.svg";
2 | import "./App.css";
3 | function App() {
4 | return (
5 |
6 |
안녕하세요
7 |
8 | );
9 | }
10 | export default App;
11 |
--------------------------------------------------------------------------------
/05. chapter5/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import "./index.css";
4 | import App from "./App";
5 |
6 | const root = ReactDOM.createRoot(document.getElementById("root"));
7 | root.render( );
8 |
--------------------------------------------------------------------------------
/06. project1/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import "./index.css";
4 | import App from "./App";
5 |
6 | const root = ReactDOM.createRoot(document.getElementById("root"));
7 | root.render( );
8 |
--------------------------------------------------------------------------------
/07. chapter6/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import "./index.css";
4 | import App from "./App";
5 |
6 | const root = ReactDOM.createRoot(document.getElementById("root"));
7 | root.render( );
8 |
--------------------------------------------------------------------------------
/08. project2/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import "./index.css";
4 | import App from "./App";
5 |
6 | const root = ReactDOM.createRoot(document.getElementById("root"));
7 | root.render( );
8 |
--------------------------------------------------------------------------------
/09. chapter7/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import "./index.css";
4 | import App from "./App";
5 |
6 | const root = ReactDOM.createRoot(document.getElementById("root"));
7 | root.render( );
8 |
--------------------------------------------------------------------------------
/10. chapter8/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import "./index.css";
4 | import App from "./App";
5 |
6 | const root = ReactDOM.createRoot(document.getElementById("root"));
7 | root.render( );
8 |
--------------------------------------------------------------------------------
/11. chapter9/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import "./index.css";
4 | import App from "./App";
5 |
6 | const root = ReactDOM.createRoot(document.getElementById("root"));
7 | root.render( );
8 |
--------------------------------------------------------------------------------
/03. chapter3/circle.js:
--------------------------------------------------------------------------------
1 | const PI = 3.141592;
2 |
3 | function getArea(radius) {
4 | return PI * radius * radius;
5 | }
6 |
7 | function getCircumference(radius) {
8 | return 2 * PI * radius;
9 | }
10 |
11 | export { PI, getArea, getCircumference };
12 |
--------------------------------------------------------------------------------
/08. project2/src/component/Header.js:
--------------------------------------------------------------------------------
1 | import "./Header.css";
2 |
3 | const Header = () => {
4 | return (
5 |
6 |
오늘은 📅
7 | {new Date().toDateString()}
8 |
9 | );
10 | };
11 | export default Header;
12 |
--------------------------------------------------------------------------------
/09. chapter7/src/component/Header.js:
--------------------------------------------------------------------------------
1 | import "./Header.css";
2 |
3 | const Header = () => {
4 | return (
5 |
6 |
오늘은 📅
7 | {new Date().toDateString()}
8 |
9 | );
10 | };
11 | export default Header;
12 |
--------------------------------------------------------------------------------
/04. chapter4/src/setupTests.js:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom';
6 |
--------------------------------------------------------------------------------
/07. chapter6/src/component/Even.js:
--------------------------------------------------------------------------------
1 | import { useEffect } from "react";
2 | function Even() {
3 | useEffect(() => {
4 | return () => {
5 | console.log("Even 컴포넌트 언마운트");
6 | };
7 | }, []);
8 | return 현재 카운트는 짝수입니다
;
9 | }
10 | export default Even;
11 |
--------------------------------------------------------------------------------
/08. project2/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | max-width: 500px;
3 | width: 100%;
4 | margin: 0 auto;
5 | box-sizing: border-box;
6 | padding: 20px;
7 | /* border: 1px solid gray; <- 삭제하거나 주석 처리 하세요 */
8 | display: flex;
9 | flex-direction: column;
10 | gap: 30px;
11 | }
12 |
--------------------------------------------------------------------------------
/09. chapter7/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | max-width: 500px;
3 | width: 100%;
4 | margin: 0 auto;
5 | box-sizing: border-box;
6 | padding: 20px;
7 | /* border: 1px solid gray; <- 삭제하거나 주석 처리 하세요 */
8 | display: flex;
9 | flex-direction: column;
10 | gap: 30px;
11 | }
12 |
--------------------------------------------------------------------------------
/10. chapter8/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | max-width: 500px;
3 | width: 100%;
4 | margin: 0 auto;
5 | box-sizing: border-box;
6 | padding: 20px;
7 | /* border: 1px solid gray; <- 삭제하거나 주석 처리 하세요 */
8 | display: flex;
9 | flex-direction: column;
10 | gap: 30px;
11 | }
12 |
--------------------------------------------------------------------------------
/11. chapter9/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | max-width: 500px;
3 | width: 100%;
4 | margin: 0 auto;
5 | box-sizing: border-box;
6 | padding: 20px;
7 | /* border: 1px solid gray; <- 삭제하거나 주석 처리 하세요 */
8 | display: flex;
9 | flex-direction: column;
10 | gap: 30px;
11 | }
12 |
--------------------------------------------------------------------------------
/04. chapter4/src/App.test.js:
--------------------------------------------------------------------------------
1 | import { render, screen } from '@testing-library/react';
2 | import App from './App';
3 |
4 | test('renders learn react link', () => {
5 | render( );
6 | const linkElement = screen.getByText(/learn react/i);
7 | expect(linkElement).toBeInTheDocument();
8 | });
9 |
--------------------------------------------------------------------------------
/10. chapter8/src/component/Header.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import "./Header.css";
3 |
4 | const Header = () => {
5 | return (
6 |
7 |
오늘은 📅
8 | {new Date().toDateString()}
9 |
10 | );
11 | };
12 | export default React.memo(Header);
13 |
--------------------------------------------------------------------------------
/11. chapter9/src/component/Header.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import "./Header.css";
3 |
4 | const Header = () => {
5 | return (
6 |
7 |
오늘은 📅
8 | {new Date().toDateString()}
9 |
10 | );
11 | };
12 | export default React.memo(Header);
13 |
--------------------------------------------------------------------------------
/06. project1/src/App.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding: 20px;
3 | }
4 |
5 | .App {
6 | margin: 0 auto;
7 | width: 500px;
8 | }
9 |
10 | .App > section {
11 | padding: 20px;
12 | background-color: rgb(245, 245, 245);
13 | border: 1px solid rgb(240, 240, 240);
14 | border-radius: 5px;
15 | margin-bottom: 10px;
16 | }
17 |
--------------------------------------------------------------------------------
/07. chapter6/src/App.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding: 20px;
3 | }
4 |
5 | .App {
6 | margin: 0 auto;
7 | width: 500px;
8 | }
9 |
10 | .App > section {
11 | padding: 20px;
12 | background-color: rgb(245, 245, 245);
13 | border: 1px solid rgb(240, 240, 240);
14 | border-radius: 5px;
15 | margin-bottom: 10px;
16 | }
17 |
--------------------------------------------------------------------------------
/12. project3/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import "./index.css";
4 | import App from "./App";
5 | import { BrowserRouter } from "react-router-dom";
6 |
7 | const root = ReactDOM.createRoot(document.getElementById("root"));
8 | root.render(
9 |
10 |
11 |
12 | );
13 |
--------------------------------------------------------------------------------
/13. chapter10/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import "./index.css";
4 | import App from "./App";
5 | import { BrowserRouter } from "react-router-dom";
6 |
7 | const root = ReactDOM.createRoot(document.getElementById("root"));
8 | root.render(
9 |
10 |
11 |
12 | );
13 |
--------------------------------------------------------------------------------
/14. chapter11/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import "./index.css";
4 | import App from "./App";
5 | import { BrowserRouter } from "react-router-dom";
6 |
7 | const root = ReactDOM.createRoot(document.getElementById("root"));
8 | root.render(
9 |
10 |
11 |
12 | );
13 |
--------------------------------------------------------------------------------
/03. chapter3/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "chapter3",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "node index.js",
8 | "test": "echo \"Error: no test specified\" && exit 1"
9 | },
10 | "author": "",
11 | "license": "ISC",
12 | "type": "module",
13 | "dependencies": {
14 | "lodash": "^4.17.21"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/12. project3/src/component/Header.js:
--------------------------------------------------------------------------------
1 | import "./Header.css";
2 | const Header = ({ title, leftChild, rightChild }) => {
3 | return (
4 |
5 |
{leftChild}
6 |
{title}
7 |
{rightChild}
8 |
9 | );
10 | };
11 | export default Header;
12 |
--------------------------------------------------------------------------------
/13. chapter10/src/component/Header.js:
--------------------------------------------------------------------------------
1 | import "./Header.css";
2 | const Header = ({ title, leftChild, rightChild }) => {
3 | return (
4 |
5 |
{leftChild}
6 |
{title}
7 |
{rightChild}
8 |
9 | );
10 | };
11 | export default Header;
12 |
--------------------------------------------------------------------------------
/14. chapter11/src/component/Header.js:
--------------------------------------------------------------------------------
1 | import "./Header.css";
2 | const Header = ({ title, leftChild, rightChild }) => {
3 | return (
4 |
5 |
{leftChild}
6 |
{title}
7 |
{rightChild}
8 |
9 | );
10 | };
11 | export default Header;
12 |
--------------------------------------------------------------------------------
/04. chapter4/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/05. chapter5/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/06. project1/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/07. chapter6/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/08. project2/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/09. chapter7/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/10. chapter8/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/11. chapter9/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/12. project3/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/13. chapter10/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/14. chapter11/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/04. chapter4/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | code {
11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12 | monospace;
13 | }
14 |
--------------------------------------------------------------------------------
/05. chapter5/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | code {
11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12 | monospace;
13 | }
14 |
--------------------------------------------------------------------------------
/06. project1/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | code {
11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12 | monospace;
13 | }
14 |
--------------------------------------------------------------------------------
/07. chapter6/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | code {
11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12 | monospace;
13 | }
14 |
--------------------------------------------------------------------------------
/04. chapter4/src/reportWebVitals.js:
--------------------------------------------------------------------------------
1 | const reportWebVitals = onPerfEntry => {
2 | if (onPerfEntry && onPerfEntry instanceof Function) {
3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
4 | getCLS(onPerfEntry);
5 | getFID(onPerfEntry);
6 | getFCP(onPerfEntry);
7 | getLCP(onPerfEntry);
8 | getTTFB(onPerfEntry);
9 | });
10 | }
11 | };
12 |
13 | export default reportWebVitals;
14 |
--------------------------------------------------------------------------------
/12. project3/src/component/Button.js:
--------------------------------------------------------------------------------
1 | import "./Button.css";
2 |
3 | const Button = ({ text, type, onClick }) => {
4 | const btnType = ["positive", "negative"].includes(type) ? type : "default";
5 | return (
6 |
10 | {text}
11 |
12 | );
13 | };
14 | Button.defaultProps = {
15 | type: "default",
16 | };
17 | export default Button;
18 |
--------------------------------------------------------------------------------
/13. chapter10/src/component/Button.js:
--------------------------------------------------------------------------------
1 | import "./Button.css";
2 |
3 | const Button = ({ text, type, onClick }) => {
4 | const btnType = ["positive", "negative"].includes(type) ? type : "default";
5 | return (
6 |
10 | {text}
11 |
12 | );
13 | };
14 | Button.defaultProps = {
15 | type: "default",
16 | };
17 | export default Button;
18 |
--------------------------------------------------------------------------------
/14. chapter11/src/component/Button.js:
--------------------------------------------------------------------------------
1 | import "./Button.css";
2 |
3 | const Button = ({ text, type, onClick }) => {
4 | const btnType = ["positive", "negative"].includes(type) ? type : "default";
5 | return (
6 |
10 | {text}
11 |
12 | );
13 | };
14 | Button.defaultProps = {
15 | type: "default",
16 | };
17 | export default Button;
18 |
--------------------------------------------------------------------------------
/05. chapter5/src/App.js:
--------------------------------------------------------------------------------
1 | import "./App.css";
2 | import Header from "./component/Header";
3 | import Body from "./component/Body";
4 | import Footer from "./component/Footer";
5 |
6 | function ChildComp() {
7 | return child component
;
8 | }
9 |
10 | function App() {
11 | return (
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | );
20 | }
21 |
22 | export default App;
23 |
--------------------------------------------------------------------------------
/08. project2/src/component/TodoList.css:
--------------------------------------------------------------------------------
1 | /* 검색 폼에 스타일 적용 */
2 | .TodoList .searchbar {
3 | margin-bottom: 20px;
4 | width: 100%;
5 | border: none;
6 | border-bottom: 1px solid rgb(220, 220, 220);
7 | box-sizing: border-box;
8 | padding-top: 15px;
9 | padding-bottom: 15px;
10 | }
11 |
12 | /* 검색 폼을 클릭했을 때의 스타일 적용 */
13 | .TodoList .searchbar:focus {
14 | outline: none;
15 | border-bottom: 1px solid #1f93ff;
16 | }
17 |
18 | .TodoList .list_wrapper {
19 | display: flex;
20 | flex-direction: column;
21 | gap: 20px;
22 | }
23 |
--------------------------------------------------------------------------------
/09. chapter7/src/component/TodoList.css:
--------------------------------------------------------------------------------
1 | /* 검색 폼에 스타일 적용 */
2 | .TodoList .searchbar {
3 | margin-bottom: 20px;
4 | width: 100%;
5 | border: none;
6 | border-bottom: 1px solid rgb(220, 220, 220);
7 | box-sizing: border-box;
8 | padding-top: 15px;
9 | padding-bottom: 15px;
10 | }
11 |
12 | /* 검색 폼을 클릭했을 때의 스타일 적용 */
13 | .TodoList .searchbar:focus {
14 | outline: none;
15 | border-bottom: 1px solid #1f93ff;
16 | }
17 |
18 | .TodoList .list_wrapper {
19 | display: flex;
20 | flex-direction: column;
21 | gap: 20px;
22 | }
23 |
--------------------------------------------------------------------------------
/10. chapter8/src/component/TodoList.css:
--------------------------------------------------------------------------------
1 | /* 검색 폼에 스타일 적용 */
2 | .TodoList .searchbar {
3 | margin-bottom: 20px;
4 | width: 100%;
5 | border: none;
6 | border-bottom: 1px solid rgb(220, 220, 220);
7 | box-sizing: border-box;
8 | padding-top: 15px;
9 | padding-bottom: 15px;
10 | }
11 |
12 | /* 검색 폼을 클릭했을 때의 스타일 적용 */
13 | .TodoList .searchbar:focus {
14 | outline: none;
15 | border-bottom: 1px solid #1f93ff;
16 | }
17 |
18 | .TodoList .list_wrapper {
19 | display: flex;
20 | flex-direction: column;
21 | gap: 20px;
22 | }
23 |
--------------------------------------------------------------------------------
/11. chapter9/src/component/TodoList.css:
--------------------------------------------------------------------------------
1 | /* 검색 폼에 스타일 적용 */
2 | .TodoList .searchbar {
3 | margin-bottom: 20px;
4 | width: 100%;
5 | border: none;
6 | border-bottom: 1px solid rgb(220, 220, 220);
7 | box-sizing: border-box;
8 | padding-top: 15px;
9 | padding-bottom: 15px;
10 | }
11 |
12 | /* 검색 폼을 클릭했을 때의 스타일 적용 */
13 | .TodoList .searchbar:focus {
14 | outline: none;
15 | border-bottom: 1px solid #1f93ff;
16 | }
17 |
18 | .TodoList .list_wrapper {
19 | display: flex;
20 | flex-direction: column;
21 | gap: 20px;
22 | }
23 |
--------------------------------------------------------------------------------
/06. project1/src/component/Controller.js:
--------------------------------------------------------------------------------
1 | const Controller = ({ handleSetCount }) => {
2 | return (
3 |
4 | handleSetCount(-1)}>-1
5 | handleSetCount(-10)}>-10
6 | handleSetCount(-100)}>-100
7 | handleSetCount(100)}>+100
8 | handleSetCount(10)}>+10
9 | handleSetCount(1)}>+1
10 |
11 | );
12 | };
13 | export default Controller;
14 |
--------------------------------------------------------------------------------
/07. chapter6/src/component/Controller.js:
--------------------------------------------------------------------------------
1 | const Controller = ({ handleSetCount }) => {
2 | return (
3 |
4 | handleSetCount(-1)}>-1
5 | handleSetCount(-10)}>-10
6 | handleSetCount(-100)}>-100
7 | handleSetCount(100)}>+100
8 | handleSetCount(10)}>+10
9 | handleSetCount(1)}>+1
10 |
11 | );
12 | };
13 | export default Controller;
14 |
--------------------------------------------------------------------------------
/12. project3/src/component/Button.css:
--------------------------------------------------------------------------------
1 | .Button {
2 | cursor: pointer;
3 | border: none;
4 | border-radius: 5px;
5 | padding-top: 10px;
6 | padding-bottom: 10px;
7 | padding-left: 20px;
8 | padding-right: 20px;
9 | font-size: 18px;
10 | white-space: nowrap;
11 | font-family: "Nanum Pen Script";
12 | }
13 |
14 | .Button_default {
15 | background-color: #ececec;
16 | color: black;
17 | }
18 | .Button_positive {
19 | background-color: #64c964;
20 | color: white;
21 | }
22 | .Button_negative {
23 | background-color: #fd565f;
24 | color: white;
25 | }
26 |
--------------------------------------------------------------------------------
/13. chapter10/src/component/Button.css:
--------------------------------------------------------------------------------
1 | .Button {
2 | cursor: pointer;
3 | border: none;
4 | border-radius: 5px;
5 | padding-top: 10px;
6 | padding-bottom: 10px;
7 | padding-left: 20px;
8 | padding-right: 20px;
9 | font-size: 18px;
10 | white-space: nowrap;
11 | font-family: "Nanum Pen Script";
12 | }
13 |
14 | .Button_default {
15 | background-color: #ececec;
16 | color: black;
17 | }
18 | .Button_positive {
19 | background-color: #64c964;
20 | color: white;
21 | }
22 | .Button_negative {
23 | background-color: #fd565f;
24 | color: white;
25 | }
26 |
--------------------------------------------------------------------------------
/14. chapter11/src/component/Button.css:
--------------------------------------------------------------------------------
1 | .Button {
2 | cursor: pointer;
3 | border: none;
4 | border-radius: 5px;
5 | padding-top: 10px;
6 | padding-bottom: 10px;
7 | padding-left: 20px;
8 | padding-right: 20px;
9 | font-size: 18px;
10 | white-space: nowrap;
11 | font-family: "Nanum Pen Script";
12 | }
13 |
14 | .Button_default {
15 | background-color: #ececec;
16 | color: black;
17 | }
18 | .Button_positive {
19 | background-color: #64c964;
20 | color: white;
21 | }
22 | .Button_negative {
23 | background-color: #fd565f;
24 | color: white;
25 | }
26 |
--------------------------------------------------------------------------------
/08. project2/src/component/TodoEditor.css:
--------------------------------------------------------------------------------
1 | .TodoEditor .editor_wrapper {
2 | width: 100%;
3 | display: flex;
4 | gap: 10px;
5 | }
6 |
7 | .TodoEditor input {
8 | flex: 1;
9 | box-sizing: border-box;
10 | border: 1px solid rgb(220, 220, 220);
11 | border-radius: 5px;
12 | padding: 15px;
13 | }
14 |
15 | .TodoEditor input:focus {
16 | outline: none;
17 | border: 1px solid #1f93ff;
18 | }
19 |
20 | .TodoEditor button {
21 | cursor: pointer;
22 | width: 80px;
23 | border: none;
24 | background-color: #1f93ff;
25 | color: white;
26 | border-radius: 5px;
27 | }
28 |
--------------------------------------------------------------------------------
/09. chapter7/src/component/TodoEditor.css:
--------------------------------------------------------------------------------
1 | .TodoEditor .editor_wrapper {
2 | width: 100%;
3 | display: flex;
4 | gap: 10px;
5 | }
6 |
7 | .TodoEditor input {
8 | flex: 1;
9 | box-sizing: border-box;
10 | border: 1px solid rgb(220, 220, 220);
11 | border-radius: 5px;
12 | padding: 15px;
13 | }
14 |
15 | .TodoEditor input:focus {
16 | outline: none;
17 | border: 1px solid #1f93ff;
18 | }
19 |
20 | .TodoEditor button {
21 | cursor: pointer;
22 | width: 80px;
23 | border: none;
24 | background-color: #1f93ff;
25 | color: white;
26 | border-radius: 5px;
27 | }
28 |
--------------------------------------------------------------------------------
/10. chapter8/src/component/TodoEditor.css:
--------------------------------------------------------------------------------
1 | .TodoEditor .editor_wrapper {
2 | width: 100%;
3 | display: flex;
4 | gap: 10px;
5 | }
6 |
7 | .TodoEditor input {
8 | flex: 1;
9 | box-sizing: border-box;
10 | border: 1px solid rgb(220, 220, 220);
11 | border-radius: 5px;
12 | padding: 15px;
13 | }
14 |
15 | .TodoEditor input:focus {
16 | outline: none;
17 | border: 1px solid #1f93ff;
18 | }
19 |
20 | .TodoEditor button {
21 | cursor: pointer;
22 | width: 80px;
23 | border: none;
24 | background-color: #1f93ff;
25 | color: white;
26 | border-radius: 5px;
27 | }
28 |
--------------------------------------------------------------------------------
/11. chapter9/src/component/TodoEditor.css:
--------------------------------------------------------------------------------
1 | .TodoEditor .editor_wrapper {
2 | width: 100%;
3 | display: flex;
4 | gap: 10px;
5 | }
6 |
7 | .TodoEditor input {
8 | flex: 1;
9 | box-sizing: border-box;
10 | border: 1px solid rgb(220, 220, 220);
11 | border-radius: 5px;
12 | padding: 15px;
13 | }
14 |
15 | .TodoEditor input:focus {
16 | outline: none;
17 | border: 1px solid #1f93ff;
18 | }
19 |
20 | .TodoEditor button {
21 | cursor: pointer;
22 | width: 80px;
23 | border: none;
24 | background-color: #1f93ff;
25 | color: white;
26 | border-radius: 5px;
27 | }
28 |
--------------------------------------------------------------------------------
/12. project3/src/index.css:
--------------------------------------------------------------------------------
1 | @import url("https://fonts.googleapis.com/css2?family=Nanum+Pen+Script&family=Yeon+Sung&display=swap");
2 |
3 | html,
4 | body {
5 | margin: 0px;
6 | width: 100%;
7 | background-color: #f6f6f6;
8 | display: flex;
9 | justify-content: center;
10 | font-family: "Nanum Pen Script";
11 | }
12 |
13 | body {
14 | height: 100%;
15 | }
16 |
17 | #root {
18 | margin: 0 auto;
19 | max-width: 600px;
20 | width: 100%;
21 | min-height: 100vh;
22 | height: 100%;
23 | background-color: white;
24 | box-shadow: rgba(100, 100, 100, 0.2) 0px 7px 29px 0px;
25 | }
26 |
--------------------------------------------------------------------------------
/13. chapter10/src/index.css:
--------------------------------------------------------------------------------
1 | @import url("https://fonts.googleapis.com/css2?family=Nanum+Pen+Script&family=Yeon+Sung&display=swap");
2 |
3 | html,
4 | body {
5 | margin: 0px;
6 | width: 100%;
7 | background-color: #f6f6f6;
8 | display: flex;
9 | justify-content: center;
10 | font-family: "Nanum Pen Script";
11 | }
12 |
13 | body {
14 | height: 100%;
15 | }
16 |
17 | #root {
18 | margin: 0 auto;
19 | max-width: 600px;
20 | width: 100%;
21 | min-height: 100vh;
22 | height: 100%;
23 | background-color: white;
24 | box-shadow: rgba(100, 100, 100, 0.2) 0px 7px 29px 0px;
25 | }
26 |
--------------------------------------------------------------------------------
/14. chapter11/src/index.css:
--------------------------------------------------------------------------------
1 | @import url("https://fonts.googleapis.com/css2?family=Nanum+Pen+Script&family=Yeon+Sung&display=swap");
2 |
3 | html,
4 | body {
5 | margin: 0px;
6 | width: 100%;
7 | background-color: #f6f6f6;
8 | display: flex;
9 | justify-content: center;
10 | font-family: "Nanum Pen Script";
11 | }
12 |
13 | body {
14 | height: 100%;
15 | }
16 |
17 | #root {
18 | margin: 0 auto;
19 | max-width: 600px;
20 | width: 100%;
21 | min-height: 100vh;
22 | height: 100%;
23 | background-color: white;
24 | box-shadow: rgba(100, 100, 100, 0.2) 0px 7px 29px 0px;
25 | }
26 |
--------------------------------------------------------------------------------
/12. project3/src/component/Header.css:
--------------------------------------------------------------------------------
1 | .Header {
2 | padding-top: 20px;
3 | padding-bottom: 20px;
4 | display: flex;
5 | align-items: center;
6 | border-bottom: 1px solid #e2e2e2;
7 | }
8 |
9 | .Header > div {
10 | display: flex;
11 | }
12 |
13 | .Header button {
14 | font-family: "Nanum Pen Script";
15 | }
16 |
17 | .Header .header_title {
18 | width: 50%;
19 | font-size: 25px;
20 | justify-content: center;
21 | }
22 |
23 | .Header .header_left {
24 | width: 25%;
25 | justify-content: start;
26 | }
27 |
28 | .Header .header_right {
29 | width: 25%;
30 | justify-content: end;
31 | }
32 |
--------------------------------------------------------------------------------
/13. chapter10/src/component/Header.css:
--------------------------------------------------------------------------------
1 | .Header {
2 | padding-top: 20px;
3 | padding-bottom: 20px;
4 | display: flex;
5 | align-items: center;
6 | border-bottom: 1px solid #e2e2e2;
7 | }
8 |
9 | .Header > div {
10 | display: flex;
11 | }
12 |
13 | .Header button {
14 | font-family: "Nanum Pen Script";
15 | }
16 |
17 | .Header .header_title {
18 | width: 50%;
19 | font-size: 25px;
20 | justify-content: center;
21 | }
22 |
23 | .Header .header_left {
24 | width: 25%;
25 | justify-content: start;
26 | }
27 |
28 | .Header .header_right {
29 | width: 25%;
30 | justify-content: end;
31 | }
32 |
--------------------------------------------------------------------------------
/14. chapter11/src/component/Header.css:
--------------------------------------------------------------------------------
1 | .Header {
2 | padding-top: 20px;
3 | padding-bottom: 20px;
4 | display: flex;
5 | align-items: center;
6 | border-bottom: 1px solid #e2e2e2;
7 | }
8 |
9 | .Header > div {
10 | display: flex;
11 | }
12 |
13 | .Header button {
14 | font-family: "Nanum Pen Script";
15 | }
16 |
17 | .Header .header_title {
18 | width: 50%;
19 | font-size: 25px;
20 | justify-content: center;
21 | }
22 |
23 | .Header .header_left {
24 | width: 25%;
25 | justify-content: start;
26 | }
27 |
28 | .Header .header_right {
29 | width: 25%;
30 | justify-content: end;
31 | }
32 |
--------------------------------------------------------------------------------
/12. project3/src/component/EmotionItem.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import "./EmotionItem.css";
3 |
4 | const EmotionItem = ({ id, img, name, onClick, isSelected }) => {
5 | const handleOnClick = () => {
6 | onClick(id);
7 | };
8 |
9 | return (
10 |
17 |
18 |
{name}
19 |
20 | );
21 | };
22 | export default React.memo(EmotionItem);
23 |
--------------------------------------------------------------------------------
/13. chapter10/src/component/EmotionItem.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import "./EmotionItem.css";
3 |
4 | const EmotionItem = ({ id, img, name, onClick, isSelected }) => {
5 | const handleOnClick = () => {
6 | onClick(id);
7 | };
8 |
9 | return (
10 |
17 |
18 |
{name}
19 |
20 | );
21 | };
22 | export default React.memo(EmotionItem);
23 |
--------------------------------------------------------------------------------
/14. chapter11/src/component/EmotionItem.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import "./EmotionItem.css";
3 |
4 | const EmotionItem = ({ id, img, name, onClick, isSelected }) => {
5 | const handleOnClick = () => {
6 | onClick(id);
7 | };
8 |
9 | return (
10 |
17 |
18 |
{name}
19 |
20 | );
21 | };
22 | export default React.memo(EmotionItem);
23 |
--------------------------------------------------------------------------------
/04. chapter4/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import "./index.css";
4 | import App from "./App";
5 | import reportWebVitals from "./reportWebVitals";
6 |
7 | const root = ReactDOM.createRoot(document.getElementById("root"));
8 | root.render(
9 |
10 |
11 |
12 | );
13 |
14 | // If you want to start measuring performance in your app, pass a function
15 | // to log results (for example: reportWebVitals(console.log))
16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
17 | reportWebVitals();
18 |
--------------------------------------------------------------------------------
/04. chapter4/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/05. chapter5/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/06. project1/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/07. chapter6/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/08. project2/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/09. chapter7/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/10. chapter8/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/11. chapter9/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/12. project3/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/13. chapter10/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/14. chapter11/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/03. chapter3/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "chapter3",
3 | "version": "1.0.0",
4 | "lockfileVersion": 3,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "chapter3",
9 | "version": "1.0.0",
10 | "license": "ISC",
11 | "dependencies": {
12 | "lodash": "^4.17.21"
13 | }
14 | },
15 | "node_modules/lodash": {
16 | "version": "4.17.21",
17 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
18 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/06. project1/src/App.js:
--------------------------------------------------------------------------------
1 | import "./App.css";
2 | import { useState } from "react";
3 | import Controller from "./component/Controller";
4 | import Viewer from "./component/Viewer";
5 |
6 | function App() {
7 | const [count, setCount] = useState(0);
8 | const handleSetCount = (value) => {
9 | setCount(count + value);
10 | };
11 |
12 | return (
13 |
14 |
Simple Counter
15 |
18 |
21 |
22 | );
23 | }
24 | export default App;
25 |
--------------------------------------------------------------------------------
/05. chapter5/src/component/Body.js:
--------------------------------------------------------------------------------
1 | import { useRef, useState } from "react";
2 |
3 | function Body() {
4 | const [text, setText] = useState("");
5 | const textRef = useRef();
6 |
7 | const handleOnChange = (e) => {
8 | setText(e.target.value);
9 | };
10 |
11 | const handleOnClick = () => {
12 | if (text.length < 5) {
13 | textRef.current.focus();
14 | } else {
15 | alert(text);
16 | setText("");
17 | }
18 | };
19 |
20 | return (
21 |
22 |
23 | 작성 완료
24 |
25 | );
26 | }
27 | export default Body;
28 |
--------------------------------------------------------------------------------
/12. project3/src/hooks/useDiary.js:
--------------------------------------------------------------------------------
1 | import { useContext, useState, useEffect } from "react";
2 | import { useNavigate } from "react-router-dom";
3 | import { DiaryStateContext } from "../App";
4 | const useDiary = (id) => {
5 | const data = useContext(DiaryStateContext);
6 | const [diary, setDiary] = useState();
7 | const navigate = useNavigate();
8 |
9 | useEffect(() => {
10 | const matchDiary = data.find((it) => String(it.id) === String(id));
11 | if (matchDiary) {
12 | setDiary(matchDiary);
13 | } else {
14 | alert("일기가 존재하지 않습니다");
15 | navigate("/", { replace: true });
16 | }
17 | }, [id, data]);
18 |
19 | return diary;
20 | };
21 | export default useDiary;
22 |
--------------------------------------------------------------------------------
/13. chapter10/src/hooks/useDiary.js:
--------------------------------------------------------------------------------
1 | import { useContext, useState, useEffect } from "react";
2 | import { useNavigate } from "react-router-dom";
3 | import { DiaryStateContext } from "../App";
4 | const useDiary = (id) => {
5 | const data = useContext(DiaryStateContext);
6 | const [diary, setDiary] = useState();
7 | const navigate = useNavigate();
8 |
9 | useEffect(() => {
10 | const matchDiary = data.find((it) => String(it.id) === String(id));
11 | if (matchDiary) {
12 | setDiary(matchDiary);
13 | } else {
14 | alert("일기가 존재하지 않습니다");
15 | navigate("/", { replace: true });
16 | }
17 | }, [id, data]);
18 |
19 | return diary;
20 | };
21 | export default useDiary;
22 |
--------------------------------------------------------------------------------
/14. chapter11/src/hooks/useDiary.js:
--------------------------------------------------------------------------------
1 | import { useContext, useState, useEffect } from "react";
2 | import { useNavigate } from "react-router-dom";
3 | import { DiaryStateContext } from "../App";
4 | const useDiary = (id) => {
5 | const data = useContext(DiaryStateContext);
6 | const [diary, setDiary] = useState();
7 | const navigate = useNavigate();
8 |
9 | useEffect(() => {
10 | const matchDiary = data.find((it) => String(it.id) === String(id));
11 | if (matchDiary) {
12 | setDiary(matchDiary);
13 | } else {
14 | alert("일기가 존재하지 않습니다");
15 | navigate("/", { replace: true });
16 | }
17 | }, [id, data]);
18 |
19 | return diary;
20 | };
21 | export default useDiary;
22 |
--------------------------------------------------------------------------------
/12. project3/src/component/DiaryList.css:
--------------------------------------------------------------------------------
1 | .DiaryList {
2 | }
3 |
4 | .DiaryList .menu_wrapper {
5 | margin-top: 20px;
6 | margin-bottom: 30px;
7 | display: flex;
8 | justify-content: space-between;
9 | }
10 |
11 | .DiaryList .left_col select {
12 | margin-right: 10px;
13 | border: none;
14 | border-radius: 5px;
15 | background-color: #ececec;
16 | padding-top: 10px;
17 | padding-bottom: 10px;
18 | padding-left: 20px;
19 | padding-right: 20px;
20 | cursor: pointer;
21 | font-family: "Nanum Pen Script";
22 | font-size: 18px;
23 | }
24 |
25 | .DiaryList .menu_wrapper .right_col {
26 | flex-grow: 1;
27 | }
28 |
29 | .DiaryList .menu_wrapper .right_col button {
30 | width: 100%;
31 | }
32 |
--------------------------------------------------------------------------------
/13. chapter10/src/component/DiaryList.css:
--------------------------------------------------------------------------------
1 | .DiaryList {
2 | }
3 |
4 | .DiaryList .menu_wrapper {
5 | margin-top: 20px;
6 | margin-bottom: 30px;
7 | display: flex;
8 | justify-content: space-between;
9 | }
10 |
11 | .DiaryList .left_col select {
12 | margin-right: 10px;
13 | border: none;
14 | border-radius: 5px;
15 | background-color: #ececec;
16 | padding-top: 10px;
17 | padding-bottom: 10px;
18 | padding-left: 20px;
19 | padding-right: 20px;
20 | cursor: pointer;
21 | font-family: "Nanum Pen Script";
22 | font-size: 18px;
23 | }
24 |
25 | .DiaryList .menu_wrapper .right_col {
26 | flex-grow: 1;
27 | }
28 |
29 | .DiaryList .menu_wrapper .right_col button {
30 | width: 100%;
31 | }
32 |
--------------------------------------------------------------------------------
/14. chapter11/src/component/DiaryList.css:
--------------------------------------------------------------------------------
1 | .DiaryList {
2 | }
3 |
4 | .DiaryList .menu_wrapper {
5 | margin-top: 20px;
6 | margin-bottom: 30px;
7 | display: flex;
8 | justify-content: space-between;
9 | }
10 |
11 | .DiaryList .left_col select {
12 | margin-right: 10px;
13 | border: none;
14 | border-radius: 5px;
15 | background-color: #ececec;
16 | padding-top: 10px;
17 | padding-bottom: 10px;
18 | padding-left: 20px;
19 | padding-right: 20px;
20 | cursor: pointer;
21 | font-family: "Nanum Pen Script";
22 | font-size: 18px;
23 | }
24 |
25 | .DiaryList .menu_wrapper .right_col {
26 | flex-grow: 1;
27 | }
28 |
29 | .DiaryList .menu_wrapper .right_col button {
30 | width: 100%;
31 | }
32 |
--------------------------------------------------------------------------------
/08. project2/src/component/TodoItem.css:
--------------------------------------------------------------------------------
1 | /* 할 일 아이템 박스 스타일 적용 */
2 | .TodoItem {
3 | display: flex;
4 | align-items: center;
5 | gap: 20px;
6 | padding-bottom: 20px;
7 | border-bottom: 1px solid rgb(240, 240, 240);
8 | }
9 |
10 | /* 체크박스를 감싼 박스에 스타일 적용 */
11 | .TodoItem .checkbox_col {
12 | width: 20px;
13 | }
14 |
15 | /* 할 일 텍스트를 감싼 박스에 스타일 적용 */
16 | .TodoItem .title_col {
17 | flex: 1;
18 | }
19 |
20 | /* 할 일 아이템 등록 시간을 감싼 박스에 스타일 적용 */
21 | .TodoItem .date_col {
22 | font-size: 14px;
23 | color: gray;
24 | }
25 |
26 | /* 삭제 버튼에 스타일 적용 */
27 | .TodoItem .btn_col button {
28 | cursor: pointer;
29 | color: gray;
30 | font-size: 14px;
31 | border: none;
32 | border-radius: 5px;
33 | padding: 5px;
34 | }
35 |
--------------------------------------------------------------------------------
/09. chapter7/src/component/TodoItem.css:
--------------------------------------------------------------------------------
1 | /* 할 일 아이템 박스 스타일 적용 */
2 | .TodoItem {
3 | display: flex;
4 | align-items: center;
5 | gap: 20px;
6 | padding-bottom: 20px;
7 | border-bottom: 1px solid rgb(240, 240, 240);
8 | }
9 |
10 | /* 체크박스를 감싼 박스에 스타일 적용 */
11 | .TodoItem .checkbox_col {
12 | width: 20px;
13 | }
14 |
15 | /* 할 일 텍스트를 감싼 박스에 스타일 적용 */
16 | .TodoItem .title_col {
17 | flex: 1;
18 | }
19 |
20 | /* 할 일 아이템 등록 시간을 감싼 박스에 스타일 적용 */
21 | .TodoItem .date_col {
22 | font-size: 14px;
23 | color: gray;
24 | }
25 |
26 | /* 삭제 버튼에 스타일 적용 */
27 | .TodoItem .btn_col button {
28 | cursor: pointer;
29 | color: gray;
30 | font-size: 14px;
31 | border: none;
32 | border-radius: 5px;
33 | padding: 5px;
34 | }
35 |
--------------------------------------------------------------------------------
/10. chapter8/src/component/TodoItem.css:
--------------------------------------------------------------------------------
1 | /* 할 일 아이템 박스 스타일 적용 */
2 | .TodoItem {
3 | display: flex;
4 | align-items: center;
5 | gap: 20px;
6 | padding-bottom: 20px;
7 | border-bottom: 1px solid rgb(240, 240, 240);
8 | }
9 |
10 | /* 체크박스를 감싼 박스에 스타일 적용 */
11 | .TodoItem .checkbox_col {
12 | width: 20px;
13 | }
14 |
15 | /* 할 일 텍스트를 감싼 박스에 스타일 적용 */
16 | .TodoItem .title_col {
17 | flex: 1;
18 | }
19 |
20 | /* 할 일 아이템 등록 시간을 감싼 박스에 스타일 적용 */
21 | .TodoItem .date_col {
22 | font-size: 14px;
23 | color: gray;
24 | }
25 |
26 | /* 삭제 버튼에 스타일 적용 */
27 | .TodoItem .btn_col button {
28 | cursor: pointer;
29 | color: gray;
30 | font-size: 14px;
31 | border: none;
32 | border-radius: 5px;
33 | padding: 5px;
34 | }
35 |
--------------------------------------------------------------------------------
/11. chapter9/src/component/TodoItem.css:
--------------------------------------------------------------------------------
1 | /* 할 일 아이템 박스 스타일 적용 */
2 | .TodoItem {
3 | display: flex;
4 | align-items: center;
5 | gap: 20px;
6 | padding-bottom: 20px;
7 | border-bottom: 1px solid rgb(240, 240, 240);
8 | }
9 |
10 | /* 체크박스를 감싼 박스에 스타일 적용 */
11 | .TodoItem .checkbox_col {
12 | width: 20px;
13 | }
14 |
15 | /* 할 일 텍스트를 감싼 박스에 스타일 적용 */
16 | .TodoItem .title_col {
17 | flex: 1;
18 | }
19 |
20 | /* 할 일 아이템 등록 시간을 감싼 박스에 스타일 적용 */
21 | .TodoItem .date_col {
22 | font-size: 14px;
23 | color: gray;
24 | }
25 |
26 | /* 삭제 버튼에 스타일 적용 */
27 | .TodoItem .btn_col button {
28 | cursor: pointer;
29 | color: gray;
30 | font-size: 14px;
31 | border: none;
32 | border-radius: 5px;
33 | padding: 5px;
34 | }
35 |
--------------------------------------------------------------------------------
/04. chapter4/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
4 |
5 | .App-logo {
6 | height: 40vmin;
7 | pointer-events: none;
8 | }
9 |
10 | @media (prefers-reduced-motion: no-preference) {
11 | .App-logo {
12 | animation: App-logo-spin infinite 20s linear;
13 | }
14 | }
15 |
16 | .App-header {
17 | background-color: #282c34;
18 | min-height: 100vh;
19 | display: flex;
20 | flex-direction: column;
21 | align-items: center;
22 | justify-content: center;
23 | font-size: calc(10px + 2vmin);
24 | color: white;
25 | }
26 |
27 | .App-link {
28 | color: #61dafb;
29 | }
30 |
31 | @keyframes App-logo-spin {
32 | from {
33 | transform: rotate(0deg);
34 | }
35 | to {
36 | transform: rotate(360deg);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/05. chapter5/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
4 |
5 | .App-logo {
6 | height: 40vmin;
7 | pointer-events: none;
8 | }
9 |
10 | @media (prefers-reduced-motion: no-preference) {
11 | .App-logo {
12 | animation: App-logo-spin infinite 20s linear;
13 | }
14 | }
15 |
16 | .App-header {
17 | background-color: #282c34;
18 | min-height: 100vh;
19 | display: flex;
20 | flex-direction: column;
21 | align-items: center;
22 | justify-content: center;
23 | font-size: calc(10px + 2vmin);
24 | color: white;
25 | }
26 |
27 | .App-link {
28 | color: #61dafb;
29 | }
30 |
31 | @keyframes App-logo-spin {
32 | from {
33 | transform: rotate(0deg);
34 | }
35 | to {
36 | transform: rotate(360deg);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/08. project2/src/component/TodoItem.js:
--------------------------------------------------------------------------------
1 | import "./TodoItem.css";
2 |
3 | const TodoItem = ({ id, content, isDone, createdDate, onUpdate, onDelete }) => {
4 | const onChangeCheckbox = () => {
5 | onUpdate(id);
6 | };
7 | const onClickDelete = () => {
8 | onDelete(id);
9 | };
10 |
11 | return (
12 |
13 |
14 |
15 |
16 |
{content}
17 |
18 | {new Date(createdDate).toLocaleDateString()}
19 |
20 |
21 | 삭제
22 |
23 |
24 | );
25 | };
26 | export default TodoItem;
27 |
--------------------------------------------------------------------------------
/09. chapter7/src/component/TodoItem.js:
--------------------------------------------------------------------------------
1 | import "./TodoItem.css";
2 |
3 | const TodoItem = ({ id, content, isDone, createdDate, onUpdate, onDelete }) => {
4 | const onChangeCheckbox = () => {
5 | onUpdate(id);
6 | };
7 | const onClickDelete = () => {
8 | onDelete(id);
9 | };
10 |
11 | return (
12 |
13 |
14 |
15 |
16 |
{content}
17 |
18 | {new Date(createdDate).toLocaleDateString()}
19 |
20 |
21 | 삭제
22 |
23 |
24 | );
25 | };
26 | export default TodoItem;
27 |
--------------------------------------------------------------------------------
/12. project3/src/component/EmotionItem.css:
--------------------------------------------------------------------------------
1 | .EmotionItem {
2 | cursor: pointer;
3 | border-radius: 5px;
4 | padding: 20px;
5 | display: flex;
6 | flex-direction: column;
7 | justify-content: center;
8 | align-items: center;
9 | }
10 |
11 | .EmotionItem img {
12 | width: 50%;
13 | margin-bottom: 10px;
14 | }
15 |
16 | .EmotionItem span {
17 | font-size: 18px;
18 | }
19 |
20 | .EmotionItem_off {
21 | background-color: #ececec;
22 | }
23 |
24 | .EmotionItem_on_1 {
25 | background-color: #64c964;
26 | color: white;
27 | }
28 |
29 | .EmotionItem_on_2 {
30 | background-color: #9dd772;
31 | color: white;
32 | }
33 |
34 | .EmotionItem_on_3 {
35 | background-color: #fdce17;
36 | color: white;
37 | }
38 |
39 | .EmotionItem_on_4 {
40 | background-color: #fd8446;
41 | color: white;
42 | }
43 |
44 | .EmotionItem_on_5 {
45 | background-color: #fd565f;
46 | color: white;
47 | }
48 |
--------------------------------------------------------------------------------
/10. chapter8/src/component/TodoItem.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import "./TodoItem.css";
3 |
4 | const TodoItem = ({ id, content, isDone, createdDate, onUpdate, onDelete }) => {
5 | console.log(`${id} TodoItem 업데이트`);
6 |
7 | const onChangeCheckbox = () => {
8 | onUpdate(id);
9 | };
10 | const onClickDelete = () => {
11 | onDelete(id);
12 | };
13 |
14 | return (
15 |
16 |
17 |
18 |
19 |
{content}
20 |
21 | {new Date(createdDate).toLocaleDateString()}
22 |
23 |
24 | 삭제
25 |
26 |
27 | );
28 | };
29 | export default React.memo(TodoItem);
30 |
--------------------------------------------------------------------------------
/12. project3/src/component/Editor.css:
--------------------------------------------------------------------------------
1 | .Editor {
2 | }
3 |
4 | .Editor .editor_section {
5 | margin-bottom: 40px;
6 | }
7 |
8 | .Editor h4 {
9 | font-size: 22px;
10 | font-weight: bold;
11 | }
12 |
13 | .Editor input,
14 | textarea {
15 | border: none;
16 | border-radius: 5px;
17 | background-color: #ececec;
18 | padding: 20px;
19 | font-size: 20px;
20 | font-family: "Nanum Pen Script";
21 | }
22 |
23 | .Editor input {
24 | padding-top: 10px;
25 | padding-bottom: 10px;
26 | cursor: pointer;
27 | }
28 |
29 | .Editor textarea {
30 | width: 100%;
31 | min-height: 200px;
32 | box-sizing: border-box;
33 | resize: vertical;
34 | }
35 |
36 | .Editor .bottom_section {
37 | display: flex;
38 | justify-content: space-between;
39 | align-items: center;
40 | }
41 |
42 | .Editor .emotion_list_wrapper {
43 | display: flex;
44 | justify-content: space-around;
45 | gap: 2%;
46 | }
47 |
--------------------------------------------------------------------------------
/13. chapter10/src/component/Editor.css:
--------------------------------------------------------------------------------
1 | .Editor {
2 | }
3 |
4 | .Editor .editor_section {
5 | margin-bottom: 40px;
6 | }
7 |
8 | .Editor h4 {
9 | font-size: 22px;
10 | font-weight: bold;
11 | }
12 |
13 | .Editor input,
14 | textarea {
15 | border: none;
16 | border-radius: 5px;
17 | background-color: #ececec;
18 | padding: 20px;
19 | font-size: 20px;
20 | font-family: "Nanum Pen Script";
21 | }
22 |
23 | .Editor input {
24 | padding-top: 10px;
25 | padding-bottom: 10px;
26 | cursor: pointer;
27 | }
28 |
29 | .Editor textarea {
30 | width: 100%;
31 | min-height: 200px;
32 | box-sizing: border-box;
33 | resize: vertical;
34 | }
35 |
36 | .Editor .bottom_section {
37 | display: flex;
38 | justify-content: space-between;
39 | align-items: center;
40 | }
41 |
42 | .Editor .emotion_list_wrapper {
43 | display: flex;
44 | justify-content: space-around;
45 | gap: 2%;
46 | }
47 |
--------------------------------------------------------------------------------
/13. chapter10/src/component/EmotionItem.css:
--------------------------------------------------------------------------------
1 | .EmotionItem {
2 | cursor: pointer;
3 | border-radius: 5px;
4 | padding: 20px;
5 | display: flex;
6 | flex-direction: column;
7 | justify-content: center;
8 | align-items: center;
9 | }
10 |
11 | .EmotionItem img {
12 | width: 50%;
13 | margin-bottom: 10px;
14 | }
15 |
16 | .EmotionItem span {
17 | font-size: 18px;
18 | }
19 |
20 | .EmotionItem_off {
21 | background-color: #ececec;
22 | }
23 |
24 | .EmotionItem_on_1 {
25 | background-color: #64c964;
26 | color: white;
27 | }
28 |
29 | .EmotionItem_on_2 {
30 | background-color: #9dd772;
31 | color: white;
32 | }
33 |
34 | .EmotionItem_on_3 {
35 | background-color: #fdce17;
36 | color: white;
37 | }
38 |
39 | .EmotionItem_on_4 {
40 | background-color: #fd8446;
41 | color: white;
42 | }
43 |
44 | .EmotionItem_on_5 {
45 | background-color: #fd565f;
46 | color: white;
47 | }
48 |
--------------------------------------------------------------------------------
/14. chapter11/src/component/Editor.css:
--------------------------------------------------------------------------------
1 | .Editor {
2 | }
3 |
4 | .Editor .editor_section {
5 | margin-bottom: 40px;
6 | }
7 |
8 | .Editor h4 {
9 | font-size: 22px;
10 | font-weight: bold;
11 | }
12 |
13 | .Editor input,
14 | textarea {
15 | border: none;
16 | border-radius: 5px;
17 | background-color: #ececec;
18 | padding: 20px;
19 | font-size: 20px;
20 | font-family: "Nanum Pen Script";
21 | }
22 |
23 | .Editor input {
24 | padding-top: 10px;
25 | padding-bottom: 10px;
26 | cursor: pointer;
27 | }
28 |
29 | .Editor textarea {
30 | width: 100%;
31 | min-height: 200px;
32 | box-sizing: border-box;
33 | resize: vertical;
34 | }
35 |
36 | .Editor .bottom_section {
37 | display: flex;
38 | justify-content: space-between;
39 | align-items: center;
40 | }
41 |
42 | .Editor .emotion_list_wrapper {
43 | display: flex;
44 | justify-content: space-around;
45 | gap: 2%;
46 | }
47 |
--------------------------------------------------------------------------------
/14. chapter11/src/component/EmotionItem.css:
--------------------------------------------------------------------------------
1 | .EmotionItem {
2 | cursor: pointer;
3 | border-radius: 5px;
4 | padding: 20px;
5 | display: flex;
6 | flex-direction: column;
7 | justify-content: center;
8 | align-items: center;
9 | }
10 |
11 | .EmotionItem img {
12 | width: 50%;
13 | margin-bottom: 10px;
14 | }
15 |
16 | .EmotionItem span {
17 | font-size: 18px;
18 | }
19 |
20 | .EmotionItem_off {
21 | background-color: #ececec;
22 | }
23 |
24 | .EmotionItem_on_1 {
25 | background-color: #64c964;
26 | color: white;
27 | }
28 |
29 | .EmotionItem_on_2 {
30 | background-color: #9dd772;
31 | color: white;
32 | }
33 |
34 | .EmotionItem_on_3 {
35 | background-color: #fdce17;
36 | color: white;
37 | }
38 |
39 | .EmotionItem_on_4 {
40 | background-color: #fd8446;
41 | color: white;
42 | }
43 |
44 | .EmotionItem_on_5 {
45 | background-color: #fd565f;
46 | color: white;
47 | }
48 |
--------------------------------------------------------------------------------
/12. project3/src/component/Viewer.js:
--------------------------------------------------------------------------------
1 | import "./Viewer.css";
2 | import { emotionList } from "../util";
3 |
4 | const Viewer = ({ content, emotionId }) => {
5 | const emotionItem = emotionList.find((it) => it.id === emotionId);
6 | console.log(emotionItem);
7 |
8 | return (
9 |
10 |
11 | 오늘의 감정
12 |
18 |
19 |
{emotionItem.name}
20 |
21 |
22 |
28 |
29 | );
30 | };
31 | export default Viewer;
32 |
--------------------------------------------------------------------------------
/13. chapter10/src/component/Viewer.js:
--------------------------------------------------------------------------------
1 | import "./Viewer.css";
2 | import { emotionList } from "../util";
3 |
4 | const Viewer = ({ content, emotionId }) => {
5 | const emotionItem = emotionList.find((it) => it.id === emotionId);
6 | console.log(emotionItem);
7 |
8 | return (
9 |
10 |
11 | 오늘의 감정
12 |
18 |
19 |
{emotionItem.name}
20 |
21 |
22 |
28 |
29 | );
30 | };
31 | export default Viewer;
32 |
--------------------------------------------------------------------------------
/14. chapter11/src/component/Viewer.js:
--------------------------------------------------------------------------------
1 | import "./Viewer.css";
2 | import { emotionList } from "../util";
3 |
4 | const Viewer = ({ content, emotionId }) => {
5 | const emotionItem = emotionList.find((it) => it.id === emotionId);
6 | console.log(emotionItem);
7 |
8 | return (
9 |
10 |
11 | 오늘의 감정
12 |
18 |
19 |
{emotionItem.name}
20 |
21 |
22 |
28 |
29 | );
30 | };
31 | export default Viewer;
32 |
--------------------------------------------------------------------------------
/12. project3/src/pages/New.js:
--------------------------------------------------------------------------------
1 | import { useNavigate } from "react-router-dom";
2 | import Button from "../component/Button";
3 | import Header from "../component/Header";
4 | import Editor from "../component/Editor";
5 | import { useContext } from "react";
6 | import { DiaryDispatchContext } from "../App";
7 |
8 | const New = () => {
9 | const { onCreate } = useContext(DiaryDispatchContext);
10 | const navigate = useNavigate();
11 |
12 | const goBack = () => {
13 | navigate(-1);
14 | };
15 |
16 | const onSubmit = (data) => {
17 | const { date, content, emotionId } = data;
18 | onCreate(date, content, emotionId);
19 | navigate("/", { replace: true });
20 | };
21 |
22 | return (
23 |
24 | }
27 | />
28 |
29 |
30 | );
31 | };
32 | export default New;
33 |
--------------------------------------------------------------------------------
/13. chapter10/src/pages/New.js:
--------------------------------------------------------------------------------
1 | import { useNavigate } from "react-router-dom";
2 | import Button from "../component/Button";
3 | import Header from "../component/Header";
4 | import Editor from "../component/Editor";
5 | import { useContext } from "react";
6 | import { DiaryDispatchContext } from "../App";
7 |
8 | const New = () => {
9 | const { onCreate } = useContext(DiaryDispatchContext);
10 | const navigate = useNavigate();
11 |
12 | const goBack = () => {
13 | navigate(-1);
14 | };
15 |
16 | const onSubmit = (data) => {
17 | const { date, content, emotionId } = data;
18 | onCreate(date, content, emotionId);
19 | navigate("/", { replace: true });
20 | };
21 |
22 | return (
23 |
24 | }
27 | />
28 |
29 |
30 | );
31 | };
32 | export default New;
33 |
--------------------------------------------------------------------------------
/04. chapter4/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "chapter4",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.16.5",
7 | "@testing-library/react": "^13.4.0",
8 | "@testing-library/user-event": "^13.5.0",
9 | "react": "^18.2.0",
10 | "react-dom": "^18.2.0",
11 | "react-scripts": "5.0.1",
12 | "web-vitals": "^2.1.4"
13 | },
14 | "scripts": {
15 | "start": "react-scripts start",
16 | "build": "react-scripts build",
17 | "test": "react-scripts test",
18 | "eject": "react-scripts eject"
19 | },
20 | "eslintConfig": {
21 | "extends": [
22 | "react-app",
23 | "react-app/jest"
24 | ]
25 | },
26 | "browserslist": {
27 | "production": [
28 | ">0.2%",
29 | "not dead",
30 | "not op_mini all"
31 | ],
32 | "development": [
33 | "last 1 chrome version",
34 | "last 1 firefox version",
35 | "last 1 safari version"
36 | ]
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/05. chapter5/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "chapter5",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.16.5",
7 | "@testing-library/react": "^13.4.0",
8 | "@testing-library/user-event": "^13.5.0",
9 | "react": "^18.2.0",
10 | "react-dom": "^18.2.0",
11 | "react-scripts": "5.0.1",
12 | "web-vitals": "^2.1.4"
13 | },
14 | "scripts": {
15 | "start": "react-scripts start",
16 | "build": "react-scripts build",
17 | "test": "react-scripts test",
18 | "eject": "react-scripts eject"
19 | },
20 | "eslintConfig": {
21 | "extends": [
22 | "react-app",
23 | "react-app/jest"
24 | ]
25 | },
26 | "browserslist": {
27 | "production": [
28 | ">0.2%",
29 | "not dead",
30 | "not op_mini all"
31 | ],
32 | "development": [
33 | "last 1 chrome version",
34 | "last 1 firefox version",
35 | "last 1 safari version"
36 | ]
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/06. project1/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "project1",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.16.5",
7 | "@testing-library/react": "^13.4.0",
8 | "@testing-library/user-event": "^13.5.0",
9 | "react": "^18.2.0",
10 | "react-dom": "^18.2.0",
11 | "react-scripts": "5.0.1",
12 | "web-vitals": "^2.1.4"
13 | },
14 | "scripts": {
15 | "start": "react-scripts start",
16 | "build": "react-scripts build",
17 | "test": "react-scripts test",
18 | "eject": "react-scripts eject"
19 | },
20 | "eslintConfig": {
21 | "extends": [
22 | "react-app",
23 | "react-app/jest"
24 | ]
25 | },
26 | "browserslist": {
27 | "production": [
28 | ">0.2%",
29 | "not dead",
30 | "not op_mini all"
31 | ],
32 | "development": [
33 | "last 1 chrome version",
34 | "last 1 firefox version",
35 | "last 1 safari version"
36 | ]
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/07. chapter6/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "project1",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.16.5",
7 | "@testing-library/react": "^13.4.0",
8 | "@testing-library/user-event": "^13.5.0",
9 | "react": "^18.2.0",
10 | "react-dom": "^18.2.0",
11 | "react-scripts": "5.0.1",
12 | "web-vitals": "^2.1.4"
13 | },
14 | "scripts": {
15 | "start": "react-scripts start",
16 | "build": "react-scripts build",
17 | "test": "react-scripts test",
18 | "eject": "react-scripts eject"
19 | },
20 | "eslintConfig": {
21 | "extends": [
22 | "react-app",
23 | "react-app/jest"
24 | ]
25 | },
26 | "browserslist": {
27 | "production": [
28 | ">0.2%",
29 | "not dead",
30 | "not op_mini all"
31 | ],
32 | "development": [
33 | "last 1 chrome version",
34 | "last 1 firefox version",
35 | "last 1 safari version"
36 | ]
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/08. project2/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "project2",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.16.5",
7 | "@testing-library/react": "^13.4.0",
8 | "@testing-library/user-event": "^13.5.0",
9 | "react": "^18.2.0",
10 | "react-dom": "^18.2.0",
11 | "react-scripts": "5.0.1",
12 | "web-vitals": "^2.1.4"
13 | },
14 | "scripts": {
15 | "start": "react-scripts start",
16 | "build": "react-scripts build",
17 | "test": "react-scripts test",
18 | "eject": "react-scripts eject"
19 | },
20 | "eslintConfig": {
21 | "extends": [
22 | "react-app",
23 | "react-app/jest"
24 | ]
25 | },
26 | "browserslist": {
27 | "production": [
28 | ">0.2%",
29 | "not dead",
30 | "not op_mini all"
31 | ],
32 | "development": [
33 | "last 1 chrome version",
34 | "last 1 firefox version",
35 | "last 1 safari version"
36 | ]
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/09. chapter7/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "project2",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.16.5",
7 | "@testing-library/react": "^13.4.0",
8 | "@testing-library/user-event": "^13.5.0",
9 | "react": "^18.2.0",
10 | "react-dom": "^18.2.0",
11 | "react-scripts": "5.0.1",
12 | "web-vitals": "^2.1.4"
13 | },
14 | "scripts": {
15 | "start": "react-scripts start",
16 | "build": "react-scripts build",
17 | "test": "react-scripts test",
18 | "eject": "react-scripts eject"
19 | },
20 | "eslintConfig": {
21 | "extends": [
22 | "react-app",
23 | "react-app/jest"
24 | ]
25 | },
26 | "browserslist": {
27 | "production": [
28 | ">0.2%",
29 | "not dead",
30 | "not op_mini all"
31 | ],
32 | "development": [
33 | "last 1 chrome version",
34 | "last 1 firefox version",
35 | "last 1 safari version"
36 | ]
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/09. chapter7/src/component/TestComp.js:
--------------------------------------------------------------------------------
1 | import { useReducer } from "react";
2 |
3 | function reducer(state, action) {
4 | switch (action.type) {
5 | case "INCREASE":
6 | return state + action.data;
7 | case "DECREASE":
8 | return state - action.data;
9 | case "INIT":
10 | return 0;
11 | default:
12 | return state;
13 | }
14 | }
15 |
16 | function TestComp() {
17 | const [count, dispatch] = useReducer(reducer, 0);
18 |
19 | return (
20 |
21 |
테스트 컴포넌트
22 |
23 | {count}
24 |
25 |
26 | dispatch({ type: "INCREASE", data: 1 })}>
27 | +
28 |
29 | dispatch({ type: "DECREASE", data: 1 })}>
30 | -
31 |
32 | dispatch({ type: "INIT" })}>0으로 초기화
33 |
34 |
35 | );
36 | }
37 | export default TestComp;
38 |
--------------------------------------------------------------------------------
/10. chapter8/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "project2",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.16.5",
7 | "@testing-library/react": "^13.4.0",
8 | "@testing-library/user-event": "^13.5.0",
9 | "react": "^18.2.0",
10 | "react-dom": "^18.2.0",
11 | "react-scripts": "5.0.1",
12 | "web-vitals": "^2.1.4"
13 | },
14 | "scripts": {
15 | "start": "react-scripts start",
16 | "build": "react-scripts build",
17 | "test": "react-scripts test",
18 | "eject": "react-scripts eject"
19 | },
20 | "eslintConfig": {
21 | "extends": [
22 | "react-app",
23 | "react-app/jest"
24 | ]
25 | },
26 | "browserslist": {
27 | "production": [
28 | ">0.2%",
29 | "not dead",
30 | "not op_mini all"
31 | ],
32 | "development": [
33 | "last 1 chrome version",
34 | "last 1 firefox version",
35 | "last 1 safari version"
36 | ]
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/10. chapter8/src/component/TestComp.js:
--------------------------------------------------------------------------------
1 | import { useReducer } from "react";
2 |
3 | function reducer(state, action) {
4 | switch (action.type) {
5 | case "INCREASE":
6 | return state + action.data;
7 | case "DECREASE":
8 | return state - action.data;
9 | case "INIT":
10 | return 0;
11 | default:
12 | return state;
13 | }
14 | }
15 |
16 | function TestComp() {
17 | const [count, dispatch] = useReducer(reducer, 0);
18 |
19 | return (
20 |
21 |
테스트 컴포넌트
22 |
23 | {count}
24 |
25 |
26 | dispatch({ type: "INCREASE", data: 1 })}>
27 | +
28 |
29 | dispatch({ type: "DECREASE", data: 1 })}>
30 | -
31 |
32 | dispatch({ type: "INIT" })}>0으로 초기화
33 |
34 |
35 | );
36 | }
37 | export default TestComp;
38 |
--------------------------------------------------------------------------------
/11. chapter9/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "project2",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.16.5",
7 | "@testing-library/react": "^13.4.0",
8 | "@testing-library/user-event": "^13.5.0",
9 | "react": "^18.2.0",
10 | "react-dom": "^18.2.0",
11 | "react-scripts": "5.0.1",
12 | "web-vitals": "^2.1.4"
13 | },
14 | "scripts": {
15 | "start": "react-scripts start",
16 | "build": "react-scripts build",
17 | "test": "react-scripts test",
18 | "eject": "react-scripts eject"
19 | },
20 | "eslintConfig": {
21 | "extends": [
22 | "react-app",
23 | "react-app/jest"
24 | ]
25 | },
26 | "browserslist": {
27 | "production": [
28 | ">0.2%",
29 | "not dead",
30 | "not op_mini all"
31 | ],
32 | "development": [
33 | "last 1 chrome version",
34 | "last 1 firefox version",
35 | "last 1 safari version"
36 | ]
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/11. chapter9/src/component/TestComp.js:
--------------------------------------------------------------------------------
1 | import { useReducer } from "react";
2 |
3 | function reducer(state, action) {
4 | switch (action.type) {
5 | case "INCREASE":
6 | return state + action.data;
7 | case "DECREASE":
8 | return state - action.data;
9 | case "INIT":
10 | return 0;
11 | default:
12 | return state;
13 | }
14 | }
15 |
16 | function TestComp() {
17 | const [count, dispatch] = useReducer(reducer, 0);
18 |
19 | return (
20 |
21 |
테스트 컴포넌트
22 |
23 | {count}
24 |
25 |
26 | dispatch({ type: "INCREASE", data: 1 })}>
27 | +
28 |
29 | dispatch({ type: "DECREASE", data: 1 })}>
30 | -
31 |
32 | dispatch({ type: "INIT" })}>0으로 초기화
33 |
34 |
35 | );
36 | }
37 | export default TestComp;
38 |
--------------------------------------------------------------------------------
/07. chapter6/src/App.js:
--------------------------------------------------------------------------------
1 | import "./App.css";
2 | import { useRef, useState } from "react";
3 | import Controller from "./component/Controller";
4 | import Viewer from "./component/Viewer";
5 | import Even from "./component/Even";
6 |
7 | function App() {
8 | const [count, setCount] = useState(0);
9 | const [text, setText] = useState("");
10 |
11 | const handleSetCount = (value) => {
12 | setCount(count + value);
13 | };
14 | const handleChangeText = (e) => {
15 | setText(e.target.value);
16 | };
17 |
18 | const didMountRef = useRef(false);
19 |
20 | return (
21 |
22 |
Simple Counter
23 |
26 |
27 |
28 | {count % 2 === 0 && }
29 |
30 |
33 |
34 | );
35 | }
36 | export default App;
37 |
--------------------------------------------------------------------------------
/11. chapter9/src/component/TodoItem.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from "react";
2 | import { TodoDispatchContext } from "../App";
3 | import "./TodoItem.css";
4 |
5 | const TodoItem = ({ id, content, isDone, createdDate }) => {
6 | console.log(`${id} TodoItem 업데이트`);
7 | const { onUpdate, onDelete } = useContext(TodoDispatchContext);
8 |
9 | const onChangeCheckbox = () => {
10 | onUpdate(id);
11 | };
12 | const onClickDelete = () => {
13 | onDelete(id);
14 | };
15 |
16 | return (
17 |
18 |
19 |
20 |
21 |
{content}
22 |
23 | {new Date(createdDate).toLocaleDateString()}
24 |
25 |
26 | 삭제
27 |
28 |
29 | );
30 | };
31 | export default React.memo(TodoItem);
32 |
--------------------------------------------------------------------------------
/12. project3/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "project3",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.16.5",
7 | "@testing-library/react": "^13.4.0",
8 | "@testing-library/user-event": "^13.5.0",
9 | "react": "^18.2.0",
10 | "react-dom": "^18.2.0",
11 | "react-router-dom": "^6.8.1",
12 | "react-scripts": "5.0.1",
13 | "web-vitals": "^2.1.4"
14 | },
15 | "scripts": {
16 | "start": "react-scripts start",
17 | "build": "react-scripts build",
18 | "test": "react-scripts test",
19 | "eject": "react-scripts eject"
20 | },
21 | "eslintConfig": {
22 | "extends": [
23 | "react-app",
24 | "react-app/jest"
25 | ]
26 | },
27 | "browserslist": {
28 | "production": [
29 | ">0.2%",
30 | "not dead",
31 | "not op_mini all"
32 | ],
33 | "development": [
34 | "last 1 chrome version",
35 | "last 1 firefox version",
36 | "last 1 safari version"
37 | ]
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/13. chapter10/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "project3",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.16.5",
7 | "@testing-library/react": "^13.4.0",
8 | "@testing-library/user-event": "^13.5.0",
9 | "react": "^18.2.0",
10 | "react-dom": "^18.2.0",
11 | "react-router-dom": "^6.8.1",
12 | "react-scripts": "5.0.1",
13 | "web-vitals": "^2.1.4"
14 | },
15 | "scripts": {
16 | "start": "react-scripts start",
17 | "build": "react-scripts build",
18 | "test": "react-scripts test",
19 | "eject": "react-scripts eject"
20 | },
21 | "eslintConfig": {
22 | "extends": [
23 | "react-app",
24 | "react-app/jest"
25 | ]
26 | },
27 | "browserslist": {
28 | "production": [
29 | ">0.2%",
30 | "not dead",
31 | "not op_mini all"
32 | ],
33 | "development": [
34 | "last 1 chrome version",
35 | "last 1 firefox version",
36 | "last 1 safari version"
37 | ]
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/14. chapter11/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "project3",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.16.5",
7 | "@testing-library/react": "^13.4.0",
8 | "@testing-library/user-event": "^13.5.0",
9 | "react": "^18.2.0",
10 | "react-dom": "^18.2.0",
11 | "react-router-dom": "^6.8.1",
12 | "react-scripts": "5.0.1",
13 | "web-vitals": "^2.1.4"
14 | },
15 | "scripts": {
16 | "start": "react-scripts start",
17 | "build": "react-scripts build",
18 | "test": "react-scripts test",
19 | "eject": "react-scripts eject"
20 | },
21 | "eslintConfig": {
22 | "extends": [
23 | "react-app",
24 | "react-app/jest"
25 | ]
26 | },
27 | "browserslist": {
28 | "production": [
29 | ">0.2%",
30 | "not dead",
31 | "not op_mini all"
32 | ],
33 | "development": [
34 | "last 1 chrome version",
35 | "last 1 firefox version",
36 | "last 1 safari version"
37 | ]
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/14. chapter11/src/pages/New.js:
--------------------------------------------------------------------------------
1 | import { useNavigate } from "react-router-dom";
2 | import Button from "../component/Button";
3 | import Header from "../component/Header";
4 | import Editor from "../component/Editor";
5 | import { DiaryDispatchContext } from "../App";
6 | import { useContext, useEffect } from "react";
7 | import { setPageTitle } from "../util";
8 |
9 | const New = () => {
10 | const { onCreate } = useContext(DiaryDispatchContext);
11 | const navigate = useNavigate();
12 |
13 | useEffect(() => {
14 | setPageTitle("새 일기 쓰기");
15 | }, []);
16 |
17 | const goBack = () => {
18 | navigate(-1);
19 | };
20 |
21 | const onSubmit = (data) => {
22 | const { date, content, emotionId } = data;
23 | onCreate(date, content, emotionId);
24 | navigate("/", { replace: true });
25 | };
26 |
27 | return (
28 |
29 | }
32 | />
33 |
34 |
35 | );
36 | };
37 | export default New;
38 |
--------------------------------------------------------------------------------
/08. project2/src/component/TodoEditor.js:
--------------------------------------------------------------------------------
1 | import { useState, useRef } from "react";
2 | import "./TodoEditor.css";
3 |
4 | const TodoEditor = ({ onCreate }) => {
5 | const [content, setContent] = useState("");
6 | const inputRef = useRef();
7 |
8 | const onChangeContent = (e) => {
9 | setContent(e.target.value);
10 | };
11 | const onSubmit = () => {
12 | if (!content) {
13 | inputRef.current.focus();
14 | return;
15 | }
16 | onCreate(content);
17 | setContent("");
18 | };
19 | const onKeyDown = (e) => {
20 | if (e.keyCode === 13) {
21 | onSubmit();
22 | }
23 | };
24 |
25 | return (
26 |
27 |
새로운 Todo 작성하기 ✏
28 |
29 |
36 | 추가
37 |
38 |
39 | );
40 | };
41 | export default TodoEditor;
42 |
--------------------------------------------------------------------------------
/09. chapter7/src/component/TodoEditor.js:
--------------------------------------------------------------------------------
1 | import { useState, useRef } from "react";
2 | import "./TodoEditor.css";
3 |
4 | const TodoEditor = ({ onCreate }) => {
5 | const [content, setContent] = useState("");
6 | const inputRef = useRef();
7 |
8 | const onChangeContent = (e) => {
9 | setContent(e.target.value);
10 | };
11 | const onSubmit = () => {
12 | if (!content) {
13 | inputRef.current.focus();
14 | return;
15 | }
16 | onCreate(content);
17 | setContent("");
18 | };
19 | const onKeyDown = (e) => {
20 | if (e.keyCode === 13) {
21 | onSubmit();
22 | }
23 | };
24 |
25 | return (
26 |
27 |
새로운 Todo 작성하기 ✏
28 |
29 |
36 | 추가
37 |
38 |
39 | );
40 | };
41 | export default TodoEditor;
42 |
--------------------------------------------------------------------------------
/10. chapter8/src/component/TodoEditor.js:
--------------------------------------------------------------------------------
1 | import { useState, useRef } from "react";
2 | import "./TodoEditor.css";
3 |
4 | const TodoEditor = ({ onCreate }) => {
5 | const [content, setContent] = useState("");
6 | const inputRef = useRef();
7 |
8 | const onChangeContent = (e) => {
9 | setContent(e.target.value);
10 | };
11 | const onSubmit = () => {
12 | if (!content) {
13 | inputRef.current.focus();
14 | return;
15 | }
16 | onCreate(content);
17 | setContent("");
18 | };
19 | const onKeyDown = (e) => {
20 | if (e.keyCode === 13) {
21 | onSubmit();
22 | }
23 | };
24 |
25 | return (
26 |
27 |
새로운 Todo 작성하기 ✏
28 |
29 |
36 | 추가
37 |
38 |
39 | );
40 | };
41 | export default TodoEditor;
42 |
--------------------------------------------------------------------------------
/08. project2/src/component/TodoList.js:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 | import TodoItem from "./TodoItem";
3 | import "./TodoList.css";
4 |
5 | const TodoList = ({ todo, onUpdate, onDelete }) => {
6 | const [search, setSearch] = useState("");
7 | const onChangeSearch = (e) => {
8 | setSearch(e.target.value);
9 | };
10 |
11 | const getSearchResult = () => {
12 | return search === ""
13 | ? todo
14 | : todo.filter((it) =>
15 | it.content.toLowerCase().includes(search.toLowerCase())
16 | );
17 | };
18 |
19 | return (
20 |
21 |
Todo List 🌱
22 |
28 |
29 | {getSearchResult().map((it) => (
30 |
36 | ))}
37 |
38 |
39 | );
40 | };
41 | export default TodoList;
42 |
--------------------------------------------------------------------------------
/09. chapter7/src/component/TodoList.js:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 | import TodoItem from "./TodoItem";
3 | import "./TodoList.css";
4 |
5 | const TodoList = ({ todo, onUpdate, onDelete }) => {
6 | const [search, setSearch] = useState("");
7 | const onChangeSearch = (e) => {
8 | setSearch(e.target.value);
9 | };
10 |
11 | const getSearchResult = () => {
12 | return search === ""
13 | ? todo
14 | : todo.filter((it) =>
15 | it.content.toLowerCase().includes(search.toLowerCase())
16 | );
17 | };
18 |
19 | return (
20 |
21 |
Todo List 🌱
22 |
28 |
29 | {getSearchResult().map((it) => (
30 |
36 | ))}
37 |
38 |
39 | );
40 | };
41 | export default TodoList;
42 |
--------------------------------------------------------------------------------
/12. project3/src/pages/Diary.js:
--------------------------------------------------------------------------------
1 | import { useNavigate, useParams } from "react-router-dom";
2 | import Button from "../component/Button";
3 | import Header from "../component/Header";
4 | import useDiary from "../hooks/useDiary";
5 | import { getFormattedDate } from "../util";
6 | import Viewer from "../component/Viewer";
7 |
8 | const Diary = () => {
9 | const { id } = useParams();
10 | const data = useDiary(id);
11 |
12 | const navigate = useNavigate();
13 | const goBack = () => {
14 | navigate(-1);
15 | };
16 |
17 | const goEdit = () => {
18 | navigate(`/edit/${id}`);
19 | };
20 |
21 | if (!data) {
22 | return 일기를 불러오고 있습니다...
;
23 | } else {
24 | const { date, emotionId, content } = data;
25 | const title = `${getFormattedDate(new Date(Number(date)))} 기록`;
26 | return (
27 |
28 | }
31 | rightChild={ }
32 | />
33 |
34 |
35 | );
36 | }
37 | };
38 | export default Diary;
39 |
--------------------------------------------------------------------------------
/13. chapter10/src/pages/Diary.js:
--------------------------------------------------------------------------------
1 | import { useNavigate, useParams } from "react-router-dom";
2 | import Button from "../component/Button";
3 | import Header from "../component/Header";
4 | import useDiary from "../hooks/useDiary";
5 | import { getFormattedDate } from "../util";
6 | import Viewer from "../component/Viewer";
7 |
8 | const Diary = () => {
9 | const { id } = useParams();
10 | const data = useDiary(id);
11 |
12 | const navigate = useNavigate();
13 | const goBack = () => {
14 | navigate(-1);
15 | };
16 |
17 | const goEdit = () => {
18 | navigate(`/edit/${id}`);
19 | };
20 |
21 | if (!data) {
22 | return 일기를 불러오고 있습니다...
;
23 | } else {
24 | const { date, emotionId, content } = data;
25 | const title = `${getFormattedDate(new Date(Number(date)))} 기록`;
26 | return (
27 |
28 | }
31 | rightChild={ }
32 | />
33 |
34 |
35 | );
36 | }
37 | };
38 | export default Diary;
39 |
--------------------------------------------------------------------------------
/11. chapter9/src/component/TodoEditor.js:
--------------------------------------------------------------------------------
1 | import { useContext, useState, useRef } from "react";
2 | import { TodoDispatchContext } from "../App";
3 | import "./TodoEditor.css";
4 |
5 | const TodoEditor = () => {
6 | const { onCreate } = useContext(TodoDispatchContext);
7 |
8 | const [content, setContent] = useState("");
9 | const inputRef = useRef();
10 |
11 | const onChangeContent = (e) => {
12 | setContent(e.target.value);
13 | };
14 | const onSubmit = () => {
15 | if (!content) {
16 | inputRef.current.focus();
17 | return;
18 | }
19 | onCreate(content);
20 | setContent("");
21 | };
22 | const onKeyDown = (e) => {
23 | if (e.keyCode === 13) {
24 | onSubmit();
25 | }
26 | };
27 |
28 | return (
29 |
30 |
새로운 Todo 작성하기 ✏
31 |
32 |
39 | 추가
40 |
41 |
42 | );
43 | };
44 | export default TodoEditor;
45 |
--------------------------------------------------------------------------------
/12. project3/src/component/DiaryItem.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { useNavigate } from "react-router-dom";
3 | import "./DiaryItem.css";
4 | import { getEmotionImgById } from "../util";
5 | import Button from "./Button";
6 |
7 | const DiaryItem = ({ id, emotionId, content, date }) => {
8 | const navigate = useNavigate();
9 | const goDetail = () => {
10 | navigate(`/diary/${id}`);
11 | };
12 | const goEdit = () => {
13 | navigate(`/edit/${id}`);
14 | };
15 | return (
16 |
17 |
21 |
22 |
23 |
24 |
25 | {new Date(parseInt(date)).toLocaleDateString()}
26 |
27 |
{content.slice(0, 25)}
28 |
29 |
30 |
31 |
32 |
33 | );
34 | };
35 |
36 | export default React.memo(DiaryItem);
37 |
--------------------------------------------------------------------------------
/13. chapter10/src/component/DiaryItem.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { useNavigate } from "react-router-dom";
3 | import "./DiaryItem.css";
4 | import { getEmotionImgById } from "../util";
5 | import Button from "./Button";
6 |
7 | const DiaryItem = ({ id, emotionId, content, date }) => {
8 | const navigate = useNavigate();
9 | const goDetail = () => {
10 | navigate(`/diary/${id}`);
11 | };
12 | const goEdit = () => {
13 | navigate(`/edit/${id}`);
14 | };
15 | return (
16 |
17 |
21 |
22 |
23 |
24 |
25 | {new Date(parseInt(date)).toLocaleDateString()}
26 |
27 |
{content.slice(0, 25)}
28 |
29 |
30 |
31 |
32 |
33 | );
34 | };
35 |
36 | export default React.memo(DiaryItem);
37 |
--------------------------------------------------------------------------------
/14. chapter11/src/component/DiaryItem.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { useNavigate } from "react-router-dom";
3 | import "./DiaryItem.css";
4 | import { getEmotionImgById } from "../util";
5 | import Button from "./Button";
6 |
7 | const DiaryItem = ({ id, emotionId, content, date }) => {
8 | const navigate = useNavigate();
9 | const goDetail = () => {
10 | navigate(`/diary/${id}`);
11 | };
12 | const goEdit = () => {
13 | navigate(`/edit/${id}`);
14 | };
15 | return (
16 |
17 |
21 |
22 |
23 |
24 |
25 | {new Date(parseInt(date)).toLocaleDateString()}
26 |
27 |
{content.slice(0, 25)}
28 |
29 |
30 |
31 |
32 |
33 | );
34 | };
35 |
36 | export default React.memo(DiaryItem);
37 |
--------------------------------------------------------------------------------
/12. project3/src/component/DiaryItem.css:
--------------------------------------------------------------------------------
1 | .DiaryItem {
2 | padding-top: 15px;
3 | padding-bottom: 15px;
4 | border-bottom: 1px solid #e2e2e2;
5 | display: flex;
6 | justify-content: space-between;
7 | }
8 |
9 | .DiaryItem .img_section {
10 | cursor: pointer;
11 | min-width: 120px;
12 | height: 80px;
13 | border-radius: 5px;
14 | display: flex;
15 | justify-content: center;
16 | }
17 |
18 | .DiaryItem .img_section_1 {
19 | background-color: #64c964;
20 | }
21 |
22 | .DiaryItem .img_section_2 {
23 | background-color: #9dd772;
24 | }
25 |
26 | .DiaryItem .img_section_3 {
27 | background-color: #fdce17;
28 | }
29 |
30 | .DiaryItem .img_section_4 {
31 | background-color: #fd8446;
32 | }
33 |
34 | .DiaryItem .img_section_5 {
35 | background-color: #fd565f;
36 | }
37 |
38 | .DiaryItem .img_section img {
39 | width: 50%;
40 | }
41 |
42 | .DiaryItem .info_section {
43 | flex-grow: 1;
44 | margin-left: 20px;
45 | cursor: pointer;
46 | }
47 |
48 | .DiaryItem .info_section .date_wrapper {
49 | font-weight: bold;
50 | font-size: 25px;
51 | margin-bottom: 5px;
52 | }
53 |
54 | .DiaryItem .info_section .content_wrapper {
55 | font-size: 18px;
56 | }
57 |
58 | .DiaryItem .button_section {
59 | min-width: 70px;
60 | }
61 |
--------------------------------------------------------------------------------
/13. chapter10/src/component/DiaryItem.css:
--------------------------------------------------------------------------------
1 | .DiaryItem {
2 | padding-top: 15px;
3 | padding-bottom: 15px;
4 | border-bottom: 1px solid #e2e2e2;
5 | display: flex;
6 | justify-content: space-between;
7 | }
8 |
9 | .DiaryItem .img_section {
10 | cursor: pointer;
11 | min-width: 120px;
12 | height: 80px;
13 | border-radius: 5px;
14 | display: flex;
15 | justify-content: center;
16 | }
17 |
18 | .DiaryItem .img_section_1 {
19 | background-color: #64c964;
20 | }
21 |
22 | .DiaryItem .img_section_2 {
23 | background-color: #9dd772;
24 | }
25 |
26 | .DiaryItem .img_section_3 {
27 | background-color: #fdce17;
28 | }
29 |
30 | .DiaryItem .img_section_4 {
31 | background-color: #fd8446;
32 | }
33 |
34 | .DiaryItem .img_section_5 {
35 | background-color: #fd565f;
36 | }
37 |
38 | .DiaryItem .img_section img {
39 | width: 50%;
40 | }
41 |
42 | .DiaryItem .info_section {
43 | flex-grow: 1;
44 | margin-left: 20px;
45 | cursor: pointer;
46 | }
47 |
48 | .DiaryItem .info_section .date_wrapper {
49 | font-weight: bold;
50 | font-size: 25px;
51 | margin-bottom: 5px;
52 | }
53 |
54 | .DiaryItem .info_section .content_wrapper {
55 | font-size: 18px;
56 | }
57 |
58 | .DiaryItem .button_section {
59 | min-width: 70px;
60 | }
61 |
--------------------------------------------------------------------------------
/14. chapter11/src/component/DiaryItem.css:
--------------------------------------------------------------------------------
1 | .DiaryItem {
2 | padding-top: 15px;
3 | padding-bottom: 15px;
4 | border-bottom: 1px solid #e2e2e2;
5 | display: flex;
6 | justify-content: space-between;
7 | }
8 |
9 | .DiaryItem .img_section {
10 | cursor: pointer;
11 | min-width: 120px;
12 | height: 80px;
13 | border-radius: 5px;
14 | display: flex;
15 | justify-content: center;
16 | }
17 |
18 | .DiaryItem .img_section_1 {
19 | background-color: #64c964;
20 | }
21 |
22 | .DiaryItem .img_section_2 {
23 | background-color: #9dd772;
24 | }
25 |
26 | .DiaryItem .img_section_3 {
27 | background-color: #fdce17;
28 | }
29 |
30 | .DiaryItem .img_section_4 {
31 | background-color: #fd8446;
32 | }
33 |
34 | .DiaryItem .img_section_5 {
35 | background-color: #fd565f;
36 | }
37 |
38 | .DiaryItem .img_section img {
39 | width: 50%;
40 | }
41 |
42 | .DiaryItem .info_section {
43 | flex-grow: 1;
44 | margin-left: 20px;
45 | cursor: pointer;
46 | }
47 |
48 | .DiaryItem .info_section .date_wrapper {
49 | font-weight: bold;
50 | font-size: 25px;
51 | margin-bottom: 5px;
52 | }
53 |
54 | .DiaryItem .info_section .content_wrapper {
55 | font-size: 18px;
56 | }
57 |
58 | .DiaryItem .button_section {
59 | min-width: 70px;
60 | }
61 |
--------------------------------------------------------------------------------
/14. chapter11/src/pages/Diary.js:
--------------------------------------------------------------------------------
1 | import { useNavigate, useParams } from "react-router-dom";
2 | import Button from "../component/Button";
3 | import Header from "../component/Header";
4 | import useDiary from "../hooks/useDiary";
5 | import { getFormattedDate } from "../util";
6 | import Viewer from "../component/Viewer";
7 | import { useEffect } from "react";
8 | import { setPageTitle } from "../util";
9 |
10 | const Diary = () => {
11 | const { id } = useParams();
12 | const data = useDiary(id);
13 |
14 | useEffect(() => {
15 | setPageTitle(`${id}번 일기`);
16 | }, []);
17 |
18 | const navigate = useNavigate();
19 | const goBack = () => {
20 | navigate(-1);
21 | };
22 |
23 | const goEdit = () => {
24 | navigate(`/edit/${id}`);
25 | };
26 |
27 | if (!data) {
28 | return 일기를 불러오고 있습니다...
;
29 | } else {
30 | const { date, emotionId, content } = data;
31 | const title = `${getFormattedDate(new Date(Number(date)))} 기록`;
32 | return (
33 |
34 | }
37 | rightChild={ }
38 | />
39 |
40 |
41 | );
42 | }
43 | };
44 | export default Diary;
45 |
--------------------------------------------------------------------------------
/12. project3/src/component/Viewer.css:
--------------------------------------------------------------------------------
1 | .Viewer {
2 | }
3 |
4 | .Viewer section {
5 | width: 100%;
6 | margin-bottom: 100px;
7 | display: flex;
8 | flex-direction: column;
9 | align-items: center;
10 | text-align: center;
11 | }
12 |
13 | .Viewer h4 {
14 | font-size: 22px;
15 | font-weight: bold;
16 | }
17 |
18 | .Viewer .emotion_img_wrapper {
19 | background-color: #ececec;
20 | width: 250px;
21 | height: 250px;
22 | border-radius: 5px;
23 | display: flex;
24 | flex-direction: column;
25 | align-items: center;
26 | justify-content: space-around;
27 | }
28 |
29 | .Viewer .emotion_img_wrapper_1 {
30 | background-color: #64c964;
31 | }
32 |
33 | .Viewer .emotion_img_wrapper_2 {
34 | background-color: #9dd772;
35 | }
36 |
37 | .Viewer .emotion_img_wrapper_3 {
38 | background-color: #fdce17;
39 | }
40 |
41 | .Viewer .emotion_img_wrapper_4 {
42 | background-color: #fd8446;
43 | }
44 |
45 | .Viewer .emotion_img_wrapper_5 {
46 | background-color: #fd565f;
47 | }
48 |
49 | .Viewer .emotion_descript {
50 | font-size: 25px;
51 | color: white;
52 | }
53 |
54 | .Viewer .content_wrapper {
55 | width: 100%;
56 | background-color: #ececec;
57 | border-radius: 5px;
58 | word-break: keep-all;
59 | overflow-wrap: break-word;
60 | }
61 |
62 | .Viewer .content_wrapper p {
63 | padding: 20px;
64 | text-align: left;
65 | font-size: 20px;
66 | font-family: "Yeon Sung";
67 | font-weight: 400;
68 | line-height: 2.5;
69 | }
70 |
--------------------------------------------------------------------------------
/13. chapter10/src/component/Viewer.css:
--------------------------------------------------------------------------------
1 | .Viewer {
2 | }
3 |
4 | .Viewer section {
5 | width: 100%;
6 | margin-bottom: 100px;
7 | display: flex;
8 | flex-direction: column;
9 | align-items: center;
10 | text-align: center;
11 | }
12 |
13 | .Viewer h4 {
14 | font-size: 22px;
15 | font-weight: bold;
16 | }
17 |
18 | .Viewer .emotion_img_wrapper {
19 | background-color: #ececec;
20 | width: 250px;
21 | height: 250px;
22 | border-radius: 5px;
23 | display: flex;
24 | flex-direction: column;
25 | align-items: center;
26 | justify-content: space-around;
27 | }
28 |
29 | .Viewer .emotion_img_wrapper_1 {
30 | background-color: #64c964;
31 | }
32 |
33 | .Viewer .emotion_img_wrapper_2 {
34 | background-color: #9dd772;
35 | }
36 |
37 | .Viewer .emotion_img_wrapper_3 {
38 | background-color: #fdce17;
39 | }
40 |
41 | .Viewer .emotion_img_wrapper_4 {
42 | background-color: #fd8446;
43 | }
44 |
45 | .Viewer .emotion_img_wrapper_5 {
46 | background-color: #fd565f;
47 | }
48 |
49 | .Viewer .emotion_descript {
50 | font-size: 25px;
51 | color: white;
52 | }
53 |
54 | .Viewer .content_wrapper {
55 | width: 100%;
56 | background-color: #ececec;
57 | border-radius: 5px;
58 | word-break: keep-all;
59 | overflow-wrap: break-word;
60 | }
61 |
62 | .Viewer .content_wrapper p {
63 | padding: 20px;
64 | text-align: left;
65 | font-size: 20px;
66 | font-family: "Yeon Sung";
67 | font-weight: 400;
68 | line-height: 2.5;
69 | }
70 |
--------------------------------------------------------------------------------
/14. chapter11/src/component/Viewer.css:
--------------------------------------------------------------------------------
1 | .Viewer {
2 | }
3 |
4 | .Viewer section {
5 | width: 100%;
6 | margin-bottom: 100px;
7 | display: flex;
8 | flex-direction: column;
9 | align-items: center;
10 | text-align: center;
11 | }
12 |
13 | .Viewer h4 {
14 | font-size: 22px;
15 | font-weight: bold;
16 | }
17 |
18 | .Viewer .emotion_img_wrapper {
19 | background-color: #ececec;
20 | width: 250px;
21 | height: 250px;
22 | border-radius: 5px;
23 | display: flex;
24 | flex-direction: column;
25 | align-items: center;
26 | justify-content: space-around;
27 | }
28 |
29 | .Viewer .emotion_img_wrapper_1 {
30 | background-color: #64c964;
31 | }
32 |
33 | .Viewer .emotion_img_wrapper_2 {
34 | background-color: #9dd772;
35 | }
36 |
37 | .Viewer .emotion_img_wrapper_3 {
38 | background-color: #fdce17;
39 | }
40 |
41 | .Viewer .emotion_img_wrapper_4 {
42 | background-color: #fd8446;
43 | }
44 |
45 | .Viewer .emotion_img_wrapper_5 {
46 | background-color: #fd565f;
47 | }
48 |
49 | .Viewer .emotion_descript {
50 | font-size: 25px;
51 | color: white;
52 | }
53 |
54 | .Viewer .content_wrapper {
55 | width: 100%;
56 | background-color: #ececec;
57 | border-radius: 5px;
58 | word-break: keep-all;
59 | overflow-wrap: break-word;
60 | }
61 |
62 | .Viewer .content_wrapper p {
63 | padding: 20px;
64 | text-align: left;
65 | font-size: 20px;
66 | font-family: "Yeon Sung";
67 | font-weight: 400;
68 | line-height: 2.5;
69 | }
70 |
--------------------------------------------------------------------------------
/08. project2/src/App.js:
--------------------------------------------------------------------------------
1 | import "./App.css";
2 | import { useState, useRef } from "react";
3 | import Header from "./component/Header";
4 | import TodoEditor from "./component/TodoEditor";
5 | import TodoList from "./component/TodoList";
6 |
7 | const mockTodo = [
8 | {
9 | id: 0,
10 | isDone: false,
11 | content: "React 공부하기",
12 | createdDate: new Date().getTime(),
13 | },
14 | {
15 | id: 1,
16 | isDone: false,
17 | content: "빨래 널기",
18 | createdDate: new Date().getTime(),
19 | },
20 | {
21 | id: 2,
22 | isDone: false,
23 | content: "노래 연습하기",
24 | createdDate: new Date().getTime(),
25 | },
26 | ];
27 |
28 | function App() {
29 | const [todo, setTodo] = useState(mockTodo);
30 | const idRef = useRef(3);
31 |
32 | const onCreate = (content) => {
33 | const newItem = {
34 | id: idRef.current,
35 | content,
36 | isDone: false,
37 | createdDate: new Date().getTime(),
38 | };
39 | setTodo([newItem, ...todo]);
40 | idRef.current += 1;
41 | };
42 |
43 | const onUpdate = (targetId) => {
44 | setTodo(
45 | todo.map((it) =>
46 | it.id === targetId ? { ...it, isDone: !it.isDone } : it
47 | )
48 | );
49 | };
50 |
51 | const onDelete = (targetId) => {
52 | setTodo(todo.filter((it) => it.id !== targetId));
53 | };
54 |
55 | return (
56 |
57 |
58 |
59 |
60 |
61 | );
62 | }
63 | export default App;
64 |
--------------------------------------------------------------------------------
/12. project3/src/pages/Home.js:
--------------------------------------------------------------------------------
1 | import { useState, useContext, useEffect } from "react";
2 | import { DiaryStateContext } from "../App";
3 | import Button from "../component/Button";
4 | import Header from "../component/Header";
5 | import { getMonthRangeByDate } from "../util";
6 | import DiaryList from "../component/DiaryList";
7 |
8 | const Home = () => {
9 | const data = useContext(DiaryStateContext);
10 | const [pivotDate, setPivotDate] = useState(new Date());
11 | const headerTitle = `${pivotDate.getFullYear()}년 ${
12 | pivotDate.getMonth() + 1
13 | }월`;
14 | const [filteredData, setFilteredData] = useState([]);
15 |
16 | useEffect(() => {
17 | if (data.length >= 1) {
18 | const { beginTimeStamp, endTimeStamp } = getMonthRangeByDate(pivotDate);
19 | setFilteredData(
20 | data.filter(
21 | (it) => beginTimeStamp <= it.date && it.date <= endTimeStamp
22 | )
23 | );
24 | } else {
25 | setFilteredData([]);
26 | }
27 | }, [data, pivotDate]);
28 |
29 | const onIncreaseMonth = () => {
30 | setPivotDate(new Date(pivotDate.getFullYear(), pivotDate.getMonth() + 1));
31 | };
32 | const onDecreaseMonth = () => {
33 | setPivotDate(new Date(pivotDate.getFullYear(), pivotDate.getMonth() - 1));
34 | };
35 | return (
36 |
37 | }
40 | rightChild={"} onClick={onIncreaseMonth} />}
41 | />
42 |
43 |
44 | );
45 | };
46 | export default Home;
47 |
--------------------------------------------------------------------------------
/13. chapter10/src/pages/Home.js:
--------------------------------------------------------------------------------
1 | import { useState, useContext, useEffect } from "react";
2 | import { DiaryStateContext } from "../App";
3 | import Button from "../component/Button";
4 | import Header from "../component/Header";
5 | import { getMonthRangeByDate } from "../util";
6 | import DiaryList from "../component/DiaryList";
7 |
8 | const Home = () => {
9 | const data = useContext(DiaryStateContext);
10 | const [pivotDate, setPivotDate] = useState(new Date());
11 | const headerTitle = `${pivotDate.getFullYear()}년 ${
12 | pivotDate.getMonth() + 1
13 | }월`;
14 | const [filteredData, setFilteredData] = useState([]);
15 |
16 | useEffect(() => {
17 | if (data.length >= 1) {
18 | const { beginTimeStamp, endTimeStamp } = getMonthRangeByDate(pivotDate);
19 | setFilteredData(
20 | data.filter(
21 | (it) => beginTimeStamp <= it.date && it.date <= endTimeStamp
22 | )
23 | );
24 | } else {
25 | setFilteredData([]);
26 | }
27 | }, [data, pivotDate]);
28 |
29 | const onIncreaseMonth = () => {
30 | setPivotDate(new Date(pivotDate.getFullYear(), pivotDate.getMonth() + 1));
31 | };
32 | const onDecreaseMonth = () => {
33 | setPivotDate(new Date(pivotDate.getFullYear(), pivotDate.getMonth() - 1));
34 | };
35 | return (
36 |
37 | }
40 | rightChild={"} onClick={onIncreaseMonth} />}
41 | />
42 |
43 |
44 | );
45 | };
46 | export default Home;
47 |
--------------------------------------------------------------------------------
/12. project3/src/pages/Edit.js:
--------------------------------------------------------------------------------
1 | import { useContext } from "react";
2 | import { DiaryDispatchContext } from "../App";
3 | import { useNavigate, useParams } from "react-router-dom";
4 | import useDiary from "../hooks/useDiary";
5 | import Button from "../component/Button";
6 | import Header from "../component/Header";
7 | import Editor from "../component/Editor";
8 |
9 | const Edit = () => {
10 | const { onDelete, onUpdate } = useContext(DiaryDispatchContext);
11 | const { id } = useParams();
12 | const data = useDiary(id);
13 | const navigate = useNavigate();
14 |
15 | const goBack = () => {
16 | navigate(-1);
17 | };
18 |
19 | const onClickDelete = () => {
20 | if (window.confirm("일기를 정말 삭제할까요? 다시 복구되지 않아요!")) {
21 | onDelete(id);
22 | navigate("/", { replace: true });
23 | }
24 | };
25 |
26 | const onSubmit = (data) => {
27 | if (window.confirm("일기를 정말 수정할까요?")) {
28 | const { date, content, emotionId } = data;
29 | onUpdate(id, date, content, emotionId);
30 | navigate("/", { replace: true });
31 | }
32 | };
33 |
34 | if (!data) {
35 | return 일기를 불러오고 있습니다...
;
36 | } else {
37 | return (
38 |
39 | }
42 | rightChild={
43 |
48 | }
49 | />
50 |
51 |
52 | );
53 | }
54 | };
55 | export default Edit;
56 |
--------------------------------------------------------------------------------
/13. chapter10/src/pages/Edit.js:
--------------------------------------------------------------------------------
1 | import { useContext } from "react";
2 | import { DiaryDispatchContext } from "../App";
3 | import { useNavigate, useParams } from "react-router-dom";
4 | import useDiary from "../hooks/useDiary";
5 | import Button from "../component/Button";
6 | import Header from "../component/Header";
7 | import Editor from "../component/Editor";
8 |
9 | const Edit = () => {
10 | const { onDelete, onUpdate } = useContext(DiaryDispatchContext);
11 | const { id } = useParams();
12 | const data = useDiary(id);
13 | const navigate = useNavigate();
14 |
15 | const goBack = () => {
16 | navigate(-1);
17 | };
18 |
19 | const onClickDelete = () => {
20 | if (window.confirm("일기를 정말 삭제할까요? 다시 복구되지 않아요!")) {
21 | onDelete(id);
22 | navigate("/", { replace: true });
23 | }
24 | };
25 |
26 | const onSubmit = (data) => {
27 | if (window.confirm("일기를 정말 수정할까요?")) {
28 | const { date, content, emotionId } = data;
29 | onUpdate(id, date, content, emotionId);
30 | navigate("/", { replace: true });
31 | }
32 | };
33 |
34 | if (!data) {
35 | return 일기를 불러오고 있습니다...
;
36 | } else {
37 | return (
38 |
39 | }
42 | rightChild={
43 |
48 | }
49 | />
50 |
51 |
52 | );
53 | }
54 | };
55 | export default Edit;
56 |
--------------------------------------------------------------------------------
/14. chapter11/src/pages/Home.js:
--------------------------------------------------------------------------------
1 | import { useState, useContext, useEffect } from "react";
2 | import { DiaryStateContext } from "../App";
3 | import Button from "../component/Button";
4 | import Header from "../component/Header";
5 | import DiaryList from "../component/DiaryList";
6 | import { getMonthRangeByDate, setPageTitle } from "../util";
7 |
8 | const Home = () => {
9 | const data = useContext(DiaryStateContext);
10 | const [pivotDate, setPivotDate] = useState(new Date());
11 | const headerTitle = `${pivotDate.getFullYear()}년 ${
12 | pivotDate.getMonth() + 1
13 | }월`;
14 | const [filteredData, setFilteredData] = useState([]);
15 |
16 | useEffect(() => {
17 | setPageTitle("Winterlood의 감정 일기장");
18 | }, []);
19 |
20 | useEffect(() => {
21 | if (data.length >= 1) {
22 | const { beginTimeStamp, endTimeStamp } = getMonthRangeByDate(pivotDate);
23 | setFilteredData(
24 | data.filter(
25 | (it) => beginTimeStamp <= it.date && it.date <= endTimeStamp
26 | )
27 | );
28 | } else {
29 | setFilteredData([]);
30 | }
31 | }, [data, pivotDate]);
32 |
33 | const onIncreaseMonth = () => {
34 | setPivotDate(new Date(pivotDate.getFullYear(), pivotDate.getMonth() + 1));
35 | };
36 | const onDecreaseMonth = () => {
37 | setPivotDate(new Date(pivotDate.getFullYear(), pivotDate.getMonth() - 1));
38 | };
39 | return (
40 |
41 | }
44 | rightChild={"} onClick={onIncreaseMonth} />}
45 | />
46 |
47 |
48 | );
49 | };
50 | export default Home;
51 |
--------------------------------------------------------------------------------
/10. chapter8/src/component/TodoList.js:
--------------------------------------------------------------------------------
1 | import { useMemo, useState } from "react";
2 | import TodoItem from "./TodoItem";
3 | import "./TodoList.css";
4 |
5 | const TodoList = ({ todo, onUpdate, onDelete }) => {
6 | const [search, setSearch] = useState("");
7 | const onChangeSearch = (e) => {
8 | setSearch(e.target.value);
9 | };
10 |
11 | const getSearchResult = () => {
12 | return search === ""
13 | ? todo
14 | : todo.filter((it) =>
15 | it.content.toLowerCase().includes(search.toLowerCase())
16 | );
17 | };
18 |
19 | const analyzeTodo = useMemo(() => {
20 | const totalCount = todo.length;
21 | const doneCount = todo.filter((it) => it.isDone).length;
22 | const notDoneCount = totalCount - doneCount;
23 | return {
24 | totalCount,
25 | doneCount,
26 | notDoneCount,
27 | };
28 | }, [todo]);
29 |
30 | const { totalCount, doneCount, notDoneCount } = analyzeTodo;
31 |
32 | return (
33 |
34 |
Todo List 🌱
35 |
36 |
총개수: {totalCount}
37 |
완료된 할 일: {doneCount}
38 |
아직 완료하지 못한 할 일: {notDoneCount}
39 |
40 |
46 |
47 | {getSearchResult().map((it) => (
48 |
54 | ))}
55 |
56 |
57 | );
58 | };
59 | export default TodoList;
60 |
--------------------------------------------------------------------------------
/11. chapter9/src/component/TodoList.js:
--------------------------------------------------------------------------------
1 | import { useContext, useMemo, useState } from "react";
2 | import { TodoStateContext } from "../App";
3 |
4 | import TodoItem from "./TodoItem";
5 | import "./TodoList.css";
6 |
7 | const TodoList = () => {
8 | const todo = useContext(TodoStateContext);
9 |
10 | const [search, setSearch] = useState("");
11 | const onChangeSearch = (e) => {
12 | setSearch(e.target.value);
13 | };
14 |
15 | const getSearchResult = () => {
16 | return search === ""
17 | ? todo
18 | : todo.filter((it) =>
19 | it.content.toLowerCase().includes(search.toLowerCase())
20 | );
21 | };
22 |
23 | const analyzeTodo = useMemo(() => {
24 | const totalCount = todo.length;
25 | const doneCount = todo.filter((it) => it.isDone).length;
26 | const notDoneCount = totalCount - doneCount;
27 | return {
28 | totalCount,
29 | doneCount,
30 | notDoneCount,
31 | };
32 | }, [todo]);
33 |
34 | const { totalCount, doneCount, notDoneCount } = analyzeTodo;
35 |
36 | return (
37 |
38 |
Todo List 🌱
39 |
40 |
총개수: {totalCount}
41 |
완료된 할 일: {doneCount}
42 |
아직 완료하지 못한 할 일: {notDoneCount}
43 |
44 |
50 |
51 | {getSearchResult().map((it) => (
52 |
53 | ))}
54 |
55 |
56 | );
57 | };
58 |
59 | TodoList.defaultProps = {
60 | todo: [],
61 | };
62 |
63 | export default TodoList;
64 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 인사이트 서적 "한 입 크기로 잘라먹는 리액트"
2 | 에셋 & 코드 저장소 입니다.
3 |
4 | ## CodeSandBox에 문제가 있으신가요?
5 | 23년 11월 21일 부로 **CodeSandBox**의 버전이 업데이트되면서
6 |
7 | 기존 버전과 달라지는 부분이 있어 많은 분들께서 어려움을 겪으시는 것 같습니다.
8 |
9 | 이에 아래 겪으실 수 있는 문제의 유형별로 대처법을 남겨두었으니 참고해주시면 감사하겠습니다.
10 |
11 | [CodeSandBox가 도서와 다를 때](https://winterlood.notion.site/CodeSandbox-f2f61517db25482aac2a9f31919f19fc?pvs=4)
12 |
13 | ## 커뮤니티
14 | - 네이버 카페 : https://cafe.winterlood.com
15 | - 카카오 오픈채팅방 1 : https://open.kakao.com/o/gOWIoeKd
16 | - 카카오 오픈채팅방 2 : https://open.kakao.com/o/gIBtjXZg
17 |
18 | (오픈채팅방 입장 비밀 번호는 도서 또는 강의를 구입하면 확인 가능합니다)
19 |
20 | ## 프로젝트 에셋
21 |
22 | ### 폰트
23 |
24 | [다운로드](https://drive.google.com/file/d/1EHW8FN7sVAo2eV9ZH9Te46wRa3Rjmy_b/view?usp=sharing)
25 |
26 | ### 이미지 파일
27 |
28 | | 장 | 파일 이름 | 파일 미리보기 | 다운로드 링크 |
29 | |---|---|---|---|
30 | | 프로젝트 3 | **emotion.zip** 감정 이미지 | | [다운로드](https://drive.google.com/file/d/18yHh0G1OYHhwO8dsapcSHnOg3T1oWiWg/view?usp=sharing) |
31 | | 11장 | **favicon.ico** 감정 일기장 아이콘 | | [다운로드](https://drive.google.com/file/d/1bj9pO6_1n56mg5yaQW0dK06k1X1d95-q/view?usp=sharing) |
32 | | 11장 | **thumbnail.png** 감정 일기장 썸네일 | | [다운로드](https://drive.google.com/file/d/13fhUVpbbgUfSEnZjmIYoTnHp8XPGnbrn/view?usp=sharing) |
33 |
34 |
35 | 
36 |
--------------------------------------------------------------------------------
/14. chapter11/src/pages/Edit.js:
--------------------------------------------------------------------------------
1 | import { DiaryDispatchContext } from "../App";
2 | import { useNavigate, useParams } from "react-router-dom";
3 | import useDiary from "../hooks/useDiary";
4 | import Button from "../component/Button";
5 | import Header from "../component/Header";
6 | import Editor from "../component/Editor";
7 | import { useContext, useEffect } from "react";
8 | import { setPageTitle } from "../util";
9 |
10 | const Edit = () => {
11 | const { onDelete, onUpdate } = useContext(DiaryDispatchContext);
12 | const { id } = useParams();
13 | const data = useDiary(id);
14 | const navigate = useNavigate();
15 |
16 | useEffect(() => {
17 | setPageTitle(`${id}번 일기 수정하기`);
18 | }, []);
19 |
20 | const goBack = () => {
21 | navigate(-1);
22 | };
23 |
24 | const onClickDelete = () => {
25 | if (window.confirm("일기를 정말 삭제할까요? 다시 복구되지 않아요!")) {
26 | onDelete(id);
27 | navigate("/", { replace: true });
28 | }
29 | };
30 |
31 | const onSubmit = (data) => {
32 | if (window.confirm("일기를 정말 수정할까요?")) {
33 | const { date, content, emotionId } = data;
34 | onUpdate(id, date, content, emotionId);
35 | navigate("/", { replace: true });
36 | }
37 | };
38 |
39 | if (!data) {
40 | return 일기를 불러오고 있습니다...
;
41 | } else {
42 | return (
43 |
44 | }
47 | rightChild={
48 |
53 | }
54 | />
55 |
56 |
57 | );
58 | }
59 | };
60 | export default Edit;
61 |
--------------------------------------------------------------------------------
/04. chapter4/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 | You need to enable JavaScript to run this app.
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/05. chapter5/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 | You need to enable JavaScript to run this app.
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/06. project1/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 | You need to enable JavaScript to run this app.
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/07. chapter6/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 | You need to enable JavaScript to run this app.
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/08. project2/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 | You need to enable JavaScript to run this app.
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/09. chapter7/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 | You need to enable JavaScript to run this app.
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/10. chapter8/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 | You need to enable JavaScript to run this app.
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/11. chapter9/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 | You need to enable JavaScript to run this app.
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/12. project3/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 | You need to enable JavaScript to run this app.
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/13. chapter10/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 | You need to enable JavaScript to run this app.
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/12. project3/src/util.js:
--------------------------------------------------------------------------------
1 | import emotion1 from "./img/emotion1.png";
2 | import emotion2 from "./img/emotion2.png";
3 | import emotion3 from "./img/emotion3.png";
4 | import emotion4 from "./img/emotion4.png";
5 | import emotion5 from "./img/emotion5.png";
6 |
7 | export const getEmotionImgById = (emotionId) => {
8 | const targetEmotionId = String(emotionId);
9 | switch (targetEmotionId) {
10 | case "1":
11 | return emotion1;
12 | case "2":
13 | return emotion2;
14 | case "3":
15 | return emotion3;
16 | case "4":
17 | return emotion4;
18 | case "5":
19 | return emotion5;
20 | default:
21 | return null;
22 | }
23 | };
24 |
25 | export const getFormattedDate = (targetDate) => {
26 | let year = targetDate.getFullYear();
27 | let month = targetDate.getMonth() + 1;
28 | let date = targetDate.getDate();
29 | if (month < 10) {
30 | month = `0${month}`;
31 | }
32 | if (date < 10) {
33 | date = `0${date}`;
34 | }
35 | return `${year}-${month}-${date}`;
36 | };
37 |
38 | export const emotionList = [
39 | {
40 | id: 1,
41 | name: "완전 좋음",
42 | img: getEmotionImgById(1),
43 | },
44 | {
45 | id: 2,
46 | name: "좋음",
47 | img: getEmotionImgById(2),
48 | },
49 | {
50 | id: 3,
51 | name: "그럭저럭",
52 | img: getEmotionImgById(3),
53 | },
54 | {
55 | id: 4,
56 | name: "나쁨",
57 | img: getEmotionImgById(4),
58 | },
59 | {
60 | id: 5,
61 | name: "끔찍함",
62 | img: getEmotionImgById(5),
63 | },
64 | ];
65 |
66 | export const getMonthRangeByDate = (date) => {
67 | const beginTimeStamp = new Date(
68 | date.getFullYear(),
69 | date.getMonth(),
70 | 1
71 | ).getTime();
72 | const endTimeStamp = new Date(
73 | date.getFullYear(),
74 | date.getMonth() + 1,
75 | 0,
76 | 23,
77 | 59,
78 | 59
79 | ).getTime();
80 | return { beginTimeStamp, endTimeStamp };
81 | };
82 |
--------------------------------------------------------------------------------
/13. chapter10/src/util.js:
--------------------------------------------------------------------------------
1 | import emotion1 from "./img/emotion1.png";
2 | import emotion2 from "./img/emotion2.png";
3 | import emotion3 from "./img/emotion3.png";
4 | import emotion4 from "./img/emotion4.png";
5 | import emotion5 from "./img/emotion5.png";
6 |
7 | export const getEmotionImgById = (emotionId) => {
8 | const targetEmotionId = String(emotionId);
9 | switch (targetEmotionId) {
10 | case "1":
11 | return emotion1;
12 | case "2":
13 | return emotion2;
14 | case "3":
15 | return emotion3;
16 | case "4":
17 | return emotion4;
18 | case "5":
19 | return emotion5;
20 | default:
21 | return null;
22 | }
23 | };
24 |
25 | export const getFormattedDate = (targetDate) => {
26 | let year = targetDate.getFullYear();
27 | let month = targetDate.getMonth() + 1;
28 | let date = targetDate.getDate();
29 | if (month < 10) {
30 | month = `0${month}`;
31 | }
32 | if (date < 10) {
33 | date = `0${date}`;
34 | }
35 | return `${year}-${month}-${date}`;
36 | };
37 |
38 | export const emotionList = [
39 | {
40 | id: 1,
41 | name: "완전 좋음",
42 | img: getEmotionImgById(1),
43 | },
44 | {
45 | id: 2,
46 | name: "좋음",
47 | img: getEmotionImgById(2),
48 | },
49 | {
50 | id: 3,
51 | name: "그럭저럭",
52 | img: getEmotionImgById(3),
53 | },
54 | {
55 | id: 4,
56 | name: "나쁨",
57 | img: getEmotionImgById(4),
58 | },
59 | {
60 | id: 5,
61 | name: "끔찍함",
62 | img: getEmotionImgById(5),
63 | },
64 | ];
65 |
66 | export const getMonthRangeByDate = (date) => {
67 | const beginTimeStamp = new Date(
68 | date.getFullYear(),
69 | date.getMonth(),
70 | 1
71 | ).getTime();
72 | const endTimeStamp = new Date(
73 | date.getFullYear(),
74 | date.getMonth() + 1,
75 | 0,
76 | 23,
77 | 59,
78 | 59
79 | ).getTime();
80 | return { beginTimeStamp, endTimeStamp };
81 | };
82 |
--------------------------------------------------------------------------------
/12. project3/src/component/DiaryList.js:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from "react";
2 | import Button from "./Button";
3 | import "./DiaryList.css";
4 | import { useNavigate } from "react-router-dom";
5 | import DiaryItem from "./DiaryItem";
6 |
7 | const sortOptionList = [
8 | { value: "latest", name: "최신순" },
9 | { value: "oldest", name: "오래된 순" },
10 | ];
11 |
12 | const DiaryList = ({ data }) => {
13 | const navigate = useNavigate();
14 | const [sortType, setSortType] = useState("latest");
15 | const [sortedData, setSortedData] = useState([]);
16 |
17 | useEffect(() => {
18 | const compare = (a, b) => {
19 | if (sortType === "latest") {
20 | return Number(b.date) - Number(a.date);
21 | } else {
22 | return Number(a.date) - Number(b.date);
23 | }
24 | };
25 | const copyList = JSON.parse(JSON.stringify(data));
26 | copyList.sort(compare);
27 | setSortedData(copyList);
28 | }, [data, sortType]);
29 |
30 | const onChangeSortType = (e) => {
31 | setSortType(e.target.value);
32 | };
33 | const onClickNew = () => {
34 | navigate("/new");
35 | };
36 |
37 | return (
38 |
39 |
40 |
41 |
42 | {sortOptionList.map((it, idx) => (
43 |
44 | {it.name}
45 |
46 | ))}
47 |
48 |
49 |
50 |
55 |
56 |
57 |
58 | {sortedData.map((it) => (
59 |
60 | ))}
61 |
62 |
63 | );
64 | };
65 | export default DiaryList;
66 |
--------------------------------------------------------------------------------
/13. chapter10/src/component/DiaryList.js:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from "react";
2 | import Button from "./Button";
3 | import "./DiaryList.css";
4 | import { useNavigate } from "react-router-dom";
5 | import DiaryItem from "./DiaryItem";
6 |
7 | const sortOptionList = [
8 | { value: "latest", name: "최신순" },
9 | { value: "oldest", name: "오래된 순" },
10 | ];
11 |
12 | const DiaryList = ({ data }) => {
13 | const navigate = useNavigate();
14 | const [sortType, setSortType] = useState("latest");
15 | const [sortedData, setSortedData] = useState([]);
16 |
17 | useEffect(() => {
18 | const compare = (a, b) => {
19 | if (sortType === "latest") {
20 | return Number(b.date) - Number(a.date);
21 | } else {
22 | return Number(a.date) - Number(b.date);
23 | }
24 | };
25 | const copyList = JSON.parse(JSON.stringify(data));
26 | copyList.sort(compare);
27 | setSortedData(copyList);
28 | }, [data, sortType]);
29 |
30 | const onChangeSortType = (e) => {
31 | setSortType(e.target.value);
32 | };
33 | const onClickNew = () => {
34 | navigate("/new");
35 | };
36 |
37 | return (
38 |
39 |
40 |
41 |
42 | {sortOptionList.map((it, idx) => (
43 |
44 | {it.name}
45 |
46 | ))}
47 |
48 |
49 |
50 |
55 |
56 |
57 |
58 | {sortedData.map((it) => (
59 |
60 | ))}
61 |
62 |
63 | );
64 | };
65 | export default DiaryList;
66 |
--------------------------------------------------------------------------------
/14. chapter11/src/component/DiaryList.js:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from "react";
2 | import Button from "./Button";
3 | import "./DiaryList.css";
4 | import { useNavigate } from "react-router-dom";
5 | import DiaryItem from "./DiaryItem";
6 |
7 | const sortOptionList = [
8 | { value: "latest", name: "최신순" },
9 | { value: "oldest", name: "오래된 순" },
10 | ];
11 |
12 | const DiaryList = ({ data }) => {
13 | const navigate = useNavigate();
14 | const [sortType, setSortType] = useState("latest");
15 | const [sortedData, setSortedData] = useState([]);
16 |
17 | useEffect(() => {
18 | const compare = (a, b) => {
19 | if (sortType === "latest") {
20 | return Number(b.date) - Number(a.date);
21 | } else {
22 | return Number(a.date) - Number(b.date);
23 | }
24 | };
25 | const copyList = JSON.parse(JSON.stringify(data));
26 | copyList.sort(compare);
27 | setSortedData(copyList);
28 | }, [data, sortType]);
29 |
30 | const onChangeSortType = (e) => {
31 | setSortType(e.target.value);
32 | };
33 | const onClickNew = () => {
34 | navigate("/new");
35 | };
36 |
37 | return (
38 |
39 |
40 |
41 |
42 | {sortOptionList.map((it, idx) => (
43 |
44 | {it.name}
45 |
46 | ))}
47 |
48 |
49 |
50 |
55 |
56 |
57 |
58 | {sortedData.map((it) => (
59 |
60 | ))}
61 |
62 |
63 | );
64 | };
65 | export default DiaryList;
66 |
--------------------------------------------------------------------------------
/14. chapter11/src/util.js:
--------------------------------------------------------------------------------
1 | import emotion1 from "./img/emotion1.png";
2 | import emotion2 from "./img/emotion2.png";
3 | import emotion3 from "./img/emotion3.png";
4 | import emotion4 from "./img/emotion4.png";
5 | import emotion5 from "./img/emotion5.png";
6 |
7 | export const getEmotionImgById = (emotionId) => {
8 | const targetEmotionId = String(emotionId);
9 | switch (targetEmotionId) {
10 | case "1":
11 | return emotion1;
12 | case "2":
13 | return emotion2;
14 | case "3":
15 | return emotion3;
16 | case "4":
17 | return emotion4;
18 | case "5":
19 | return emotion5;
20 | default:
21 | return null;
22 | }
23 | };
24 |
25 | export const getFormattedDate = (targetDate) => {
26 | let year = targetDate.getFullYear();
27 | let month = targetDate.getMonth() + 1;
28 | let date = targetDate.getDate();
29 | if (month < 10) {
30 | month = `0${month}`;
31 | }
32 | if (date < 10) {
33 | date = `0${date}`;
34 | }
35 | return `${year}-${month}-${date}`;
36 | };
37 |
38 | export const emotionList = [
39 | {
40 | id: 1,
41 | name: "완전 좋음",
42 | img: getEmotionImgById(1),
43 | },
44 | {
45 | id: 2,
46 | name: "좋음",
47 | img: getEmotionImgById(2),
48 | },
49 | {
50 | id: 3,
51 | name: "그럭저럭",
52 | img: getEmotionImgById(3),
53 | },
54 | {
55 | id: 4,
56 | name: "나쁨",
57 | img: getEmotionImgById(4),
58 | },
59 | {
60 | id: 5,
61 | name: "끔찍함",
62 | img: getEmotionImgById(5),
63 | },
64 | ];
65 |
66 | export const getMonthRangeByDate = (date) => {
67 | const beginTimeStamp = new Date(
68 | date.getFullYear(),
69 | date.getMonth(),
70 | 1
71 | ).getTime();
72 | const endTimeStamp = new Date(
73 | date.getFullYear(),
74 | date.getMonth() + 1,
75 | 0,
76 | 23,
77 | 59,
78 | 59
79 | ).getTime();
80 | return { beginTimeStamp, endTimeStamp };
81 | };
82 |
83 | export const setPageTitle = (title) => {
84 | const titleElement = document.getElementsByTagName("title")[0];
85 | titleElement.innerText = title;
86 | };
87 |
--------------------------------------------------------------------------------
/14. chapter11/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | Winterlood의 감정 일기장
28 |
29 |
30 |
31 |
32 |
33 | You need to enable JavaScript to run this app.
34 |
35 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/10. chapter8/src/App.js:
--------------------------------------------------------------------------------
1 | import "./App.css";
2 | import { useCallback, useReducer, useRef } from "react";
3 | import Header from "./component/Header";
4 | import TodoEditor from "./component/TodoEditor";
5 | import TodoList from "./component/TodoList";
6 |
7 | const mockTodo = [
8 | {
9 | id: 0,
10 | isDone: false,
11 | content: "React 공부하기",
12 | createdDate: new Date().getTime(),
13 | },
14 | {
15 | id: 1,
16 | isDone: false,
17 | content: "빨래 널기",
18 | createdDate: new Date().getTime(),
19 | },
20 | {
21 | id: 2,
22 | isDone: false,
23 | content: "노래 연습하기",
24 | createdDate: new Date().getTime(),
25 | },
26 | ];
27 |
28 | function reducer(state, action) {
29 | switch (action.type) {
30 | case "CREATE": {
31 | return [action.newItem, ...state];
32 | }
33 | case "UPDATE": {
34 | return state.map((it) =>
35 | it.id === action.targetId
36 | ? {
37 | ...it,
38 | isDone: !it.isDone,
39 | }
40 | : it
41 | );
42 | }
43 | case "DELETE": {
44 | return state.filter((it) => it.id !== action.targetId);
45 | }
46 | default:
47 | return state;
48 | }
49 | }
50 |
51 | function App() {
52 | const [todo, dispatch] = useReducer(reducer, mockTodo);
53 | const idRef = useRef(3);
54 |
55 | const onCreate = (content) => {
56 | dispatch({
57 | type: "CREATE",
58 | newItem: {
59 | id: idRef.current,
60 | content,
61 | isDone: false,
62 | createdDate: new Date().getTime(),
63 | },
64 | });
65 | idRef.current += 1;
66 | };
67 |
68 | const onUpdate = useCallback((targetId) => {
69 | dispatch({
70 | type: "UPDATE",
71 | targetId,
72 | });
73 | }, []);
74 |
75 | const onDelete = useCallback((targetId) => {
76 | dispatch({
77 | type: "DELETE",
78 | targetId,
79 | });
80 | }, []);
81 |
82 | return (
83 |
84 |
85 |
86 |
87 |
88 | );
89 | }
90 | export default App;
91 |
--------------------------------------------------------------------------------
/09. chapter7/src/App.js:
--------------------------------------------------------------------------------
1 | import "./App.css";
2 | import { useReducer, useRef } from "react";
3 | import Header from "./component/Header";
4 | import TodoEditor from "./component/TodoEditor";
5 | import TodoList from "./component/TodoList";
6 | import TestComp from "./component/TestComp";
7 |
8 | const mockTodo = [
9 | {
10 | id: 0,
11 | isDone: false,
12 | content: "React 공부하기",
13 | createdDate: new Date().getTime(),
14 | },
15 | {
16 | id: 1,
17 | isDone: false,
18 | content: "빨래 널기",
19 | createdDate: new Date().getTime(),
20 | },
21 | {
22 | id: 2,
23 | isDone: false,
24 | content: "노래 연습하기",
25 | createdDate: new Date().getTime(),
26 | },
27 | ];
28 |
29 | function reducer(state, action) {
30 | switch (action.type) {
31 | case "CREATE": {
32 | return [action.newItem, ...state];
33 | }
34 | case "UPDATE": {
35 | return state.map((it) =>
36 | it.id === action.targetId
37 | ? {
38 | ...it,
39 | isDone: !it.isDone,
40 | }
41 | : it
42 | );
43 | }
44 | case "DELETE": {
45 | return state.filter((it) => it.id !== action.targetId);
46 | }
47 | default:
48 | return state;
49 | }
50 | }
51 |
52 | function App() {
53 | const [todo, dispatch] = useReducer(reducer, mockTodo);
54 | const idRef = useRef(3);
55 |
56 | const onCreate = (content) => {
57 | dispatch({
58 | type: "CREATE",
59 | newItem: {
60 | id: idRef.current,
61 | content,
62 | isDone: false,
63 | createdDate: new Date().getTime(),
64 | },
65 | });
66 | idRef.current += 1;
67 | };
68 |
69 | const onUpdate = (targetId) => {
70 | dispatch({
71 | type: "UPDATE",
72 | targetId,
73 | });
74 | };
75 |
76 | const onDelete = (targetId) => {
77 | dispatch({
78 | type: "DELETE",
79 | targetId,
80 | });
81 | };
82 |
83 | return (
84 |
85 | {/* */}
86 |
87 |
88 |
89 |
90 | );
91 | }
92 | export default App;
93 |
--------------------------------------------------------------------------------
/04. chapter4/src/logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/12. project3/src/component/Editor.js:
--------------------------------------------------------------------------------
1 | import "./Editor.css";
2 | import { useState, useEffect, useCallback } from "react";
3 | import { emotionList, getFormattedDate } from "../util";
4 | import Button from "./Button";
5 | import { useNavigate } from "react-router-dom";
6 | import EmotionItem from "./EmotionItem";
7 |
8 | const Editor = ({ initData, onSubmit }) => {
9 | const navigate = useNavigate();
10 | const [state, setState] = useState({
11 | date: getFormattedDate(new Date()),
12 | emotionId: 3,
13 | content: "",
14 | });
15 |
16 | useEffect(() => {
17 | if (initData) {
18 | setState({
19 | ...initData,
20 | date: getFormattedDate(new Date(parseInt(initData.date))),
21 | });
22 | }
23 | }, [initData]);
24 |
25 | const handleChangeDate = (e) => {
26 | setState({
27 | ...state,
28 | date: e.target.value,
29 | });
30 | };
31 |
32 | const handleChangeContent = (e) => {
33 | setState({
34 | ...state,
35 | content: e.target.value,
36 | });
37 | };
38 |
39 | const handleChangeEmotion = useCallback((emotionId) => {
40 | setState((state) => ({
41 | ...state,
42 | emotionId,
43 | }));
44 | }, []);
45 |
46 | const handleSubmit = () => {
47 | onSubmit(state);
48 | };
49 |
50 | const handleOnGoBack = () => {
51 | navigate(-1);
52 | };
53 |
54 | return (
55 |
56 |
오늘의 날짜
57 |
58 |
59 |
60 |
61 |
오늘의 감정
62 |
63 | {emotionList.map((it) => (
64 |
70 | ))}
71 |
72 |
73 |
74 |
오늘의 일기
75 |
76 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 | );
90 | };
91 | export default Editor;
92 |
--------------------------------------------------------------------------------
/13. chapter10/src/component/Editor.js:
--------------------------------------------------------------------------------
1 | import "./Editor.css";
2 | import { useState, useEffect, useCallback } from "react";
3 | import { emotionList, getFormattedDate } from "../util";
4 | import Button from "./Button";
5 | import { useNavigate } from "react-router-dom";
6 | import EmotionItem from "./EmotionItem";
7 |
8 | const Editor = ({ initData, onSubmit }) => {
9 | const navigate = useNavigate();
10 | const [state, setState] = useState({
11 | date: getFormattedDate(new Date()),
12 | emotionId: 3,
13 | content: "",
14 | });
15 |
16 | useEffect(() => {
17 | if (initData) {
18 | setState({
19 | ...initData,
20 | date: getFormattedDate(new Date(parseInt(initData.date))),
21 | });
22 | }
23 | }, [initData]);
24 |
25 | const handleChangeDate = (e) => {
26 | setState({
27 | ...state,
28 | date: e.target.value,
29 | });
30 | };
31 |
32 | const handleChangeContent = (e) => {
33 | setState({
34 | ...state,
35 | content: e.target.value,
36 | });
37 | };
38 |
39 | const handleChangeEmotion = useCallback((emotionId) => {
40 | setState((state) => ({
41 | ...state,
42 | emotionId,
43 | }));
44 | }, []);
45 |
46 | const handleSubmit = () => {
47 | onSubmit(state);
48 | };
49 |
50 | const handleOnGoBack = () => {
51 | navigate(-1);
52 | };
53 |
54 | return (
55 |
56 |
오늘의 날짜
57 |
58 |
59 |
60 |
61 |
오늘의 감정
62 |
63 | {emotionList.map((it) => (
64 |
70 | ))}
71 |
72 |
73 |
74 |
오늘의 일기
75 |
76 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 | );
90 | };
91 | export default Editor;
92 |
--------------------------------------------------------------------------------
/14. chapter11/src/component/Editor.js:
--------------------------------------------------------------------------------
1 | import "./Editor.css";
2 | import { useState, useEffect, useCallback } from "react";
3 | import { emotionList, getFormattedDate } from "../util";
4 | import Button from "./Button";
5 | import { useNavigate } from "react-router-dom";
6 | import EmotionItem from "./EmotionItem";
7 |
8 | const Editor = ({ initData, onSubmit }) => {
9 | const navigate = useNavigate();
10 | const [state, setState] = useState({
11 | date: getFormattedDate(new Date()),
12 | emotionId: 3,
13 | content: "",
14 | });
15 |
16 | useEffect(() => {
17 | if (initData) {
18 | setState({
19 | ...initData,
20 | date: getFormattedDate(new Date(parseInt(initData.date))),
21 | });
22 | }
23 | }, [initData]);
24 |
25 | const handleChangeDate = (e) => {
26 | setState({
27 | ...state,
28 | date: e.target.value,
29 | });
30 | };
31 |
32 | const handleChangeContent = (e) => {
33 | setState({
34 | ...state,
35 | content: e.target.value,
36 | });
37 | };
38 |
39 | const handleChangeEmotion = useCallback((emotionId) => {
40 | setState((state) => ({
41 | ...state,
42 | emotionId,
43 | }));
44 | }, []);
45 |
46 | const handleSubmit = () => {
47 | onSubmit(state);
48 | };
49 |
50 | const handleOnGoBack = () => {
51 | navigate(-1);
52 | };
53 |
54 | return (
55 |
56 |
오늘의 날짜
57 |
58 |
59 |
60 |
61 |
오늘의 감정
62 |
63 | {emotionList.map((it) => (
64 |
70 | ))}
71 |
72 |
73 |
74 |
오늘의 일기
75 |
76 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 | );
90 | };
91 | export default Editor;
92 |
--------------------------------------------------------------------------------
/11. chapter9/src/App.js:
--------------------------------------------------------------------------------
1 | import "./App.css";
2 | import React, { useMemo, useCallback, useReducer, useRef } from "react";
3 | import Header from "./component/Header";
4 | import TodoEditor from "./component/TodoEditor";
5 | import TodoList from "./component/TodoList";
6 |
7 | const mockTodo = [
8 | {
9 | id: 0,
10 | isDone: false,
11 | content: "React 공부하기",
12 | createdDate: new Date().getTime(),
13 | },
14 | {
15 | id: 1,
16 | isDone: false,
17 | content: "빨래 널기",
18 | createdDate: new Date().getTime(),
19 | },
20 | {
21 | id: 2,
22 | isDone: false,
23 | content: "노래 연습하기",
24 | createdDate: new Date().getTime(),
25 | },
26 | ];
27 |
28 | function reducer(state, action) {
29 | switch (action.type) {
30 | case "CREATE": {
31 | return [action.newItem, ...state];
32 | }
33 | case "UPDATE": {
34 | return state.map((it) =>
35 | it.id === action.targetId
36 | ? {
37 | ...it,
38 | isDone: !it.isDone,
39 | }
40 | : it
41 | );
42 | }
43 | case "DELETE": {
44 | return state.filter((it) => it.id !== action.targetId);
45 | }
46 | default:
47 | return state;
48 | }
49 | }
50 |
51 | export const TodoStateContext = React.createContext();
52 | export const TodoDispatchContext = React.createContext();
53 |
54 | function App() {
55 | const [todo, dispatch] = useReducer(reducer, mockTodo);
56 | const idRef = useRef(3);
57 |
58 | const onCreate = (content) => {
59 | dispatch({
60 | type: "CREATE",
61 | newItem: {
62 | id: idRef.current,
63 | content,
64 | isDone: false,
65 | createdDate: new Date().getTime(),
66 | },
67 | });
68 | idRef.current += 1;
69 | };
70 |
71 | const onUpdate = useCallback((targetId) => {
72 | dispatch({
73 | type: "UPDATE",
74 | targetId,
75 | });
76 | }, []);
77 |
78 | const onDelete = useCallback((targetId) => {
79 | dispatch({
80 | type: "DELETE",
81 | targetId,
82 | });
83 | }, []);
84 |
85 | const memoizedDispatches = useMemo(() => {
86 | return { onCreate, onUpdate, onDelete };
87 | }, []);
88 |
89 | return (
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 | );
99 | }
100 | export default App;
101 |
--------------------------------------------------------------------------------
/CodeSandbox.md:
--------------------------------------------------------------------------------
1 | “한 입 크기로 잘라먹는 리액트” 도서 및 강의에서는
2 | 간단한 자바스크립트 코드를 실습하기 위해 **CodeSandBox**라는 온라인 에디터를 이용합니다.
3 | 도서 및 강의에서도 CodeSandBox의 사용법을 자세히 다루고 있습니다.
4 |
5 | 그러나 23년 11월 부터 **CodeSandBox**의 UI가 지속적으로 변경되면서
6 | 독자 및 수강생 여러분들이 많은 불편함을 겪으실 것으로 예상됩니다.
7 |
8 | 따라서 이러한 불편 사항들을 해소하기 위해 본 문서를 남겨두었으니
9 | 문서를 참고하시어 CodeSandBox를 통해 실습을 잘 진행하셨으면 좋겠습니다.
10 |
11 | ## 01. CodeSandbox 계정 생성하기
12 |
13 | 23년 11월 부로 게스트(계정을 생성하지 않은 유저)의 권한이 대폭 축소되었습니다.
14 | 따라서 원활한 실습을 위해 계정 생성이 필요합니다.
15 |
16 | 계정 생성을 위해 codesandbox.io로 접속하신 다음
17 | 아래 그림에 빨간색 박스로 표시된 "Sign in" 버튼을 클릭합니다.
18 |
19 | 
20 |
21 | 그럼 회원가입 및 로그인 화면이 나타납니다.
22 | 여기서는 아무 방법이나 선택하셔도 상관없습니다.
23 |
24 |
25 | 계정을 생성하고 로그인 하시면 아래와 같은 화면이 나타납니다.
26 |
27 |
28 | ## 02. CodeSandbox 설정 변경하기
29 |
30 | 이제 도서나 강의와 같인 환경에서 실습하기 위한 몇가지 환경 설정이 필요합니다.
31 | 로그인 이후 나타나는 화면에서 왼쪽 위의 네모 박스 모양의 로고를 클릭해 메뉴 창을 연 다음
32 | Settings를 클릭합니다.
33 |
34 | 
35 |
36 |
37 | 그럼 다음 그림과 같은 Settings 창이 나타납니다.
38 | 여기서 좌측의 Experiments 탭을 클릭합니다.
39 |
40 | 
41 |
42 | 여기서 Sandbox beta editor 옵션의 스위치를 아예 꺼 주시면 됩니다.
43 | 그럼 다음과 같이 왜 beta editor를 사용하지 않을 것인지 이유를 묻습니다.
44 | 원하시는 답을 선택하신 다음 Disable 버튼을 클릭하시면 됩니다.
45 |
46 | 
47 |
48 | Disable 버튼을 클릭하시면 beta editor가 비활성화 됩니다.
49 | 참고로 Disable 버튼을 클릭했음에도 UI상으로는 스위치가 안 꺼질 수 있습니다.
50 | 그럴 때에는 클릭하신 다음 Settings 패널을 종료하셨다가 다시 열어보시면 꺼져있을 수 있습니다.
51 |
52 | 
53 |
54 | 여기까지 설정해 주셨다면 모두 완료되었습니다.
55 | 이제 설정한 옵션이 잘 적용되도록 하기 위해
56 | 새로고침을 2-3회 정도 진행하시거나 브라우저를 아예 껐다가 다시 켜시기 바랍니다.
57 |
58 | ## 03. 새로운 SandBox 생성하기
59 |
60 | 이제 새로운 Sandbox를 생성하여 실습을 진행할 수 있습니다.
61 | 여러분의 원활한 실습 환경을 위해 템플릿을 만들어 두었는데요
62 | 아래 링크로 접속하실 수 있습니다.
63 | [한입 크기로 잘라먹는 리액트 Sandbox 템플릿](https://codesandbox.io/s/han-ib-keugiro-jalrameogneun-riaegteu-vanila-js-template-p8kdf0)
64 |
65 | 위 링크로 접속하시면 페이지 우측 상단에 **Fork** 라는 파란색 버튼이 나타납니다.
66 | 여기서 Fork란 쉽게 말해 "이 템플릿을 사용해 실습 환경을 구축하겠다" 라고 이해하시면 됩니다.
67 | 이 Fork 버튼을 클릭한 다음 자신의 계정을 선택합니다.
68 |
69 |
70 | 그럼 다음과 같이 페이지 우측 하단에 "Forked Sandbox"라는 메세지가 나타납니다.
71 |
72 |
73 | 이때 브라우저를 한번 **새로고침** 해 주시면 템플릿을 이용한 실습 환경 구축이 완료됩니다.
74 | 자바스크립트 코드를 작성한 다음 console 탭을 열어서 확인해보세요
75 |
76 |
77 |
78 | 이제 강의나 도서의 설명에 따라 실습을 진행하시면 됩니다.
79 | 행복한 프로그래밍이 되시길 바랍니다.
80 |
81 |
82 |
83 |
--------------------------------------------------------------------------------
/12. project3/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { useReducer, useRef, useEffect, useState } from "react";
2 | import { Routes, Route, Link } from "react-router-dom";
3 | import "./App.css";
4 | import Home from "./pages/Home";
5 | import New from "./pages/New";
6 | import Diary from "./pages/Diary";
7 | import Edit from "./pages/Edit";
8 |
9 | const mockData = [
10 | {
11 | id: "mock1",
12 | date: new Date().getTime() - 1,
13 | content: "mock1",
14 | emotionId: 1,
15 | },
16 | {
17 | id: "mock2",
18 | date: new Date().getTime() - 2,
19 | content: "mock2",
20 | emotionId: 2,
21 | },
22 | {
23 | id: "mock3",
24 | date: new Date().getTime() - 3,
25 | content: "mock3",
26 | emotionId: 3,
27 | },
28 | ];
29 |
30 | function reducer(state, action) {
31 | switch (action.type) {
32 | case "INIT": {
33 | return action.data;
34 | }
35 | case "CREATE": {
36 | return [action.data, ...state];
37 | }
38 | case "UPDATE": {
39 | return state.map((it) =>
40 | String(it.id) === String(action.data.id) ? { ...action.data } : it
41 | );
42 | }
43 | case "DELETE": {
44 | return state.filter((it) => String(it.id) !== String(action.targetId));
45 | }
46 | default: {
47 | return state;
48 | }
49 | }
50 | }
51 |
52 | export const DiaryStateContext = React.createContext();
53 | export const DiaryDispatchContext = React.createContext();
54 |
55 | function App() {
56 | const [isDataLoaded, setIsDataLoaded] = useState(false);
57 | const [data, dispatch] = useReducer(reducer, []);
58 | const idRef = useRef(0);
59 |
60 | useEffect(() => {
61 | dispatch({
62 | type: "INIT",
63 | data: mockData,
64 | });
65 | setIsDataLoaded(true);
66 | }, []);
67 |
68 | const onCreate = (date, content, emotionId) => {
69 | dispatch({
70 | type: "CREATE",
71 | data: {
72 | id: idRef.current,
73 | date: new Date(date).getTime(),
74 | content,
75 | emotionId,
76 | },
77 | });
78 | idRef.current += 1;
79 | };
80 |
81 | const onUpdate = (targetId, date, content, emotionId) => {
82 | dispatch({
83 | type: "UPDATE",
84 | data: {
85 | id: targetId,
86 | date: new Date(date).getTime(),
87 | content,
88 | emotionId,
89 | },
90 | });
91 | };
92 |
93 | const onDelete = (targetId) => {
94 | dispatch({
95 | type: "DELETE",
96 | targetId,
97 | });
98 | };
99 |
100 | if (!isDataLoaded) {
101 | return 데이터를 불러오는 중입니다
;
102 | } else {
103 | return (
104 |
105 |
112 |
113 |
114 | } />
115 | } />
116 | } />
117 | } />
118 |
119 |
120 |
121 |
122 | );
123 | }
124 | }
125 | export default App;
126 |
--------------------------------------------------------------------------------
/04. chapter4/README.md:
--------------------------------------------------------------------------------
1 | # Getting Started with Create React App
2 |
3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
4 |
5 | ## Available Scripts
6 |
7 | In the project directory, you can run:
8 |
9 | ### `npm start`
10 |
11 | Runs the app in the development mode.\
12 | Open [http://localhost:3000](http://localhost:3000) to view it in your browser.
13 |
14 | The page will reload when you make changes.\
15 | You may also see any lint errors in the console.
16 |
17 | ### `npm test`
18 |
19 | Launches the test runner in the interactive watch mode.\
20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
21 |
22 | ### `npm run build`
23 |
24 | Builds the app for production to the `build` folder.\
25 | It correctly bundles React in production mode and optimizes the build for the best performance.
26 |
27 | The build is minified and the filenames include the hashes.\
28 | Your app is ready to be deployed!
29 |
30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
31 |
32 | ### `npm run eject`
33 |
34 | **Note: this is a one-way operation. Once you `eject`, you can't go back!**
35 |
36 | If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
37 |
38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own.
39 |
40 | You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it.
41 |
42 | ## Learn More
43 |
44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
45 |
46 | To learn React, check out the [React documentation](https://reactjs.org/).
47 |
48 | ### Code Splitting
49 |
50 | This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)
51 |
52 | ### Analyzing the Bundle Size
53 |
54 | This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)
55 |
56 | ### Making a Progressive Web App
57 |
58 | This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)
59 |
60 | ### Advanced Configuration
61 |
62 | This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)
63 |
64 | ### Deployment
65 |
66 | This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)
67 |
68 | ### `npm run build` fails to minify
69 |
70 | This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)
71 |
--------------------------------------------------------------------------------
/05. chapter5/README.md:
--------------------------------------------------------------------------------
1 | # Getting Started with Create React App
2 |
3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
4 |
5 | ## Available Scripts
6 |
7 | In the project directory, you can run:
8 |
9 | ### `npm start`
10 |
11 | Runs the app in the development mode.\
12 | Open [http://localhost:3000](http://localhost:3000) to view it in your browser.
13 |
14 | The page will reload when you make changes.\
15 | You may also see any lint errors in the console.
16 |
17 | ### `npm test`
18 |
19 | Launches the test runner in the interactive watch mode.\
20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
21 |
22 | ### `npm run build`
23 |
24 | Builds the app for production to the `build` folder.\
25 | It correctly bundles React in production mode and optimizes the build for the best performance.
26 |
27 | The build is minified and the filenames include the hashes.\
28 | Your app is ready to be deployed!
29 |
30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
31 |
32 | ### `npm run eject`
33 |
34 | **Note: this is a one-way operation. Once you `eject`, you can't go back!**
35 |
36 | If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
37 |
38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own.
39 |
40 | You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it.
41 |
42 | ## Learn More
43 |
44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
45 |
46 | To learn React, check out the [React documentation](https://reactjs.org/).
47 |
48 | ### Code Splitting
49 |
50 | This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)
51 |
52 | ### Analyzing the Bundle Size
53 |
54 | This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)
55 |
56 | ### Making a Progressive Web App
57 |
58 | This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)
59 |
60 | ### Advanced Configuration
61 |
62 | This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)
63 |
64 | ### Deployment
65 |
66 | This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)
67 |
68 | ### `npm run build` fails to minify
69 |
70 | This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)
71 |
--------------------------------------------------------------------------------
/06. project1/README.md:
--------------------------------------------------------------------------------
1 | # Getting Started with Create React App
2 |
3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
4 |
5 | ## Available Scripts
6 |
7 | In the project directory, you can run:
8 |
9 | ### `npm start`
10 |
11 | Runs the app in the development mode.\
12 | Open [http://localhost:3000](http://localhost:3000) to view it in your browser.
13 |
14 | The page will reload when you make changes.\
15 | You may also see any lint errors in the console.
16 |
17 | ### `npm test`
18 |
19 | Launches the test runner in the interactive watch mode.\
20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
21 |
22 | ### `npm run build`
23 |
24 | Builds the app for production to the `build` folder.\
25 | It correctly bundles React in production mode and optimizes the build for the best performance.
26 |
27 | The build is minified and the filenames include the hashes.\
28 | Your app is ready to be deployed!
29 |
30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
31 |
32 | ### `npm run eject`
33 |
34 | **Note: this is a one-way operation. Once you `eject`, you can't go back!**
35 |
36 | If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
37 |
38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own.
39 |
40 | You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it.
41 |
42 | ## Learn More
43 |
44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
45 |
46 | To learn React, check out the [React documentation](https://reactjs.org/).
47 |
48 | ### Code Splitting
49 |
50 | This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)
51 |
52 | ### Analyzing the Bundle Size
53 |
54 | This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)
55 |
56 | ### Making a Progressive Web App
57 |
58 | This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)
59 |
60 | ### Advanced Configuration
61 |
62 | This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)
63 |
64 | ### Deployment
65 |
66 | This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)
67 |
68 | ### `npm run build` fails to minify
69 |
70 | This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)
71 |
--------------------------------------------------------------------------------