├── 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 |
4 |

header

5 |
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 | 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 | 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 | 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 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | ); 12 | }; 13 | export default Controller; 14 | -------------------------------------------------------------------------------- /07. chapter6/src/component/Controller.js: -------------------------------------------------------------------------------- 1 | const Controller = ({ handleSetCount }) => { 2 | return ( 3 |
4 | 5 | 6 | 7 | 8 | 9 | 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 | {`emotion${id}`} 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 | {`emotion${id}`} 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 | {`emotion${id}`} 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 |
16 | 17 |
18 |
19 | 20 |
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 | {emotionItem.name} 19 |
{emotionItem.name}
20 |
21 |
22 |
23 |

오늘의 일기

24 |
25 |

{content}

26 |
27 |
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 | {emotionItem.name} 19 |
{emotionItem.name}
20 |
21 |
22 |
23 |

오늘의 일기

24 |
25 |

{content}

26 |
27 |
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 | {emotionItem.name} 19 |
{emotionItem.name}
20 |
21 |
22 |
23 |

오늘의 일기

24 |
25 |

{content}

26 |
27 |
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 | 29 | 32 | 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 | 29 | 32 | 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 | 29 | 32 | 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 |
24 | 25 |
26 |
27 | 28 | {count % 2 === 0 && } 29 |
30 |
31 | 32 |
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={
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={
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 | {`emotion${emotionId}`} 22 |
23 |
24 |
25 | {new Date(parseInt(date)).toLocaleDateString()} 26 |
27 |
{content.slice(0, 25)}
28 |
29 |
30 |
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 | {`emotion${emotionId}`} 22 |
23 |
24 |
25 | {new Date(parseInt(date)).toLocaleDateString()} 26 |
27 |
{content.slice(0, 25)}
28 |
29 |
30 |
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 | {`emotion${emotionId}`} 22 |
23 |
24 |
25 | {new Date(parseInt(date)).toLocaleDateString()} 26 |
27 |
{content.slice(0, 25)}
28 |
29 |
30 |
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={
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={
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={
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 |
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 |
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={
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**
감정 이미지 | image | [다운로드](https://drive.google.com/file/d/18yHh0G1OYHhwO8dsapcSHnOg3T1oWiWg/view?usp=sharing) | 31 | | 11장 | **favicon.ico**
감정 일기장 아이콘 | image | [다운로드](https://drive.google.com/file/d/1bj9pO6_1n56mg5yaQW0dK06k1X1d95-q/view?usp=sharing) | 32 | | 11장 | **thumbnail.png**
감정 일기장 썸네일 | image | [다운로드](https://drive.google.com/file/d/13fhUVpbbgUfSEnZjmIYoTnHp8XPGnbrn/view?usp=sharing) | 33 | 34 | 35 | ![](https://cdn.inflearn.com/public/files/courses/328340/b1a281cf-51a0-4d70-a5c3-17241ef45358/328340-eng.png) 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 |
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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 48 |
49 |
50 |
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 | 48 |
49 |
50 |
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 | 48 |
49 |
50 |
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 | 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 |