한입 크기로 잘라 먹는 리액트 Section 7

yiseonline·2023년 6월 3일
0

udemy study

목록 보기
7/7
post-thumbnail

7.0 프로젝트 완성 예시 데모 사이트


뭐임 어이없네


7.1 페이지 라우팅 0 - React SPA & CSR

Routing이란?

  • 어떤 네트워크 내에서 통신 데이터를 보낼 경로를 선택하는 일련의 과정

Router

  • 데이터의 경로를 실시간으로 지정해주는 무 언가

Route+ing

  • 경로를 정해주는 행위 자체와 그런 과정들을 다 포함하여 일컫는 말

Page Routing ?
ㄴ 단순히 요청에 따라서 어떤 페이지를 돌려줄지 요청하는 과정을 일컫는 말

이렇게 홈이라는 경로를 가지고 도착한 요청에는 해당 경로에 알맞는 html 파일을 보내주는 형식으로 웹서버가 동작하게 됨

ㄴ 이렇게 여러개의 페이지를 준비 해놨다가 요청이 들어오면 경로에 따라 적절한 페이지를 보내주는 것
= Multipage Application = MPA

하지만 !! 우리가 배우는 리액트는
MPA가 아니라 Single Page Application (SPA) 방식을 따른다
ㄴ 페이지가 한개 짜리인 간단한 web application이다

(만들어볼 감정일기장 데모사이트)

ㄴ 귀엽다 !!

유데미 사이트는 페이지를 이동할 때마다 탭의 아이콘이 깜빡거리고 실제로 페이지가 이동될 때 깜빡인 다음에 이동이 됨
vs
리액트 웹페이지는 페이지가 이동할 때 전혀 깜빡이지가 않는다 ! 페이지들의 이동이 아주 빠르고 쾌적하다

ㄴ 어떻게 가능한거냐??


페이지 이동하고 싶어서 버튼 클릭하면

리액트가 알아서 페이지 업데이트 시킴 (컴포넌트가 교체될 때 빠른 속도로페이지가 변경된다!) -> 페이지 전환이 빨라진다

이런 방식을 Client Side Rendering (CSR) 이라고 한다

=> 리액트는 SPA 방식을 따르면서 CSR로 페이지를 렌더링한다 !


7.2 페이지 라우팅 1 - React Router 기본


시작도 못하고 헤매는 중..
눈물날고 같다

왜 npm start가 안되는거지? 내가 볼 땐 일단 react router 까는거에서 뭔가 잘못된 것 같당.. 근데 난 복붙 밖에 안했는데 잘못될 수가 있나? 날 억까하는듯 ㅠ 따흐흑 일단 난 시간이 없으니 계속 간다.. ㅡㅡ;


pages 폴더 만들고 그 안에 diary, edit, home, new 파일 넣는다 그 후에

const Diary=()=>{
    return (
    <div>
        <h1>Diary</h1>
        <p>이 곳은 일기 상세 페이지 입니다.</p>
    </div>
    );
};

export default Diary;

ㄴ 이렇게 코드 쓰고 이걸 복붙해서 나머지 파일들에도 이름만 바꿔서 복붙 !!

import './App.css';
import {BrowserRouter, Routes} from 'react-router-dom';

import Home from './pages/Home';
import New from './pages/New';
import Edit from './pages/Edit';
import Diary from './pages/Diary';


function App() {
  return (
    <BrowserRouter>
      <div className="App">
        <h2>App.js</h2>
        <Routes>
          <Route path='/' element={<Home/>}/>
        </Routes>
      </div>
    </BrowserRouter>
  );
}

export default App;

아까 4개 파일들 import 하고 함수 값 더 넣어준다

-last code-
(App.js)

import './App.css';
import {BrowserRouter, Route, Routes} from 'react-router-dom';

import RouteTest from './components/RouteTest';

import Home from './pages/Home';
import New from './pages/New';
import Edit from './pages/Edit';
import Diary from './pages/Diary';


function App() {
  return (
    <BrowserRouter>
      <div className="App">
        <h2>App.js</h2>
        <Routes>
          <Route path="/" element={<Home/>}/>
          <Route path='/new' element={<New/>}/>
          <Route path='/edit' element={<Edit/>}/>
          <Route path='/diary' element={<Diary/>}/>
        </Routes>
        <RouteTest/>
      </div>
    </BrowserRouter>
  );
}

export default App;

리액트 앱이 제공하는 index.html은 하나지만 app component와 그 안에 router들을 통해서 마치 페이지가 이동된 것 처럼 만들어준다
=> 리액트는 이런 방식으로 페이지를 전환해줌 (쾌적 & 빠름) = CSR


7.3 페이지 라우팅 2 - React Router 응용


이 세가지 방법을 배워 보즈아

1) Path Variable

경로에 변수를 사용하는 방법 -> path variable (얘네는 useParams라는 토글을 사용해서 가져옴)

<Route path='/diary/:id' element={<Diary/>}/>
-> 뒤에 있는 id라는 이름으로 뒤에 있는 값을 전달하겠다 라는 선언

(Diary.js)

import {useParams} from 'react-router-dom';
const Diary=()=>{

    const {id}=useParams();
    
    return (
    <div>
        <h1>Diary</h1>
        <p>이 곳은 일기 상세 페이지 입니다.</p>
    </div>
    );
};

export default Diary;

2) Query

  • 웹 페이지에 데이터를 전달하는 가장 간단한 방법

/edit?id=10&mode=dark (이름과 값을 엮어서 데이터를 전송하는 기법)
=> Query String

query ex)

import {useSearchParams} from 'react-router-dom';

const Edit=()=>{
    const [searchParams, setSearchParams]=useSearchParams();

    const id = searchParams.get('id');
    const mode = searchParams.get('mode');

    return (
    <div>
        <h1>Edit</h1>
        <p>이 곳은 일기 수정 페이지 입니다.</p>
        <button onClick={()=>setSearchParams({who:'winterlood'})}>
          QS 바꾸기
        </button>
    </div>
    );
};

export default Edit;

3) Page Moving

navigate 함수를 써서 실행함

page moving ex)

import {useSearchParams} from 'react-router-dom';

const Edit=()=>{
    const navigate = useNavigate();
    const [searchParams, setSearchParams]=useSearchParams();

    const id = searchParams.get('id');
    const mode = searchParams.get('mode');

    return (
    <div>
        <h1>Edit</h1>
        <p>이 곳은 일기 수정 페이지 입니다.</p>
        <button onClick  	{()=>setSearchParams({who:'winterlood'})}>
          QS 바꾸기
        </button>

        <button onClick={()=>{
            navigate("/home");
        }}>HOME으로 가기</button>
        
        <button onClick={()=>{
            navigate(-1);
        }}>뒤로 가기</button>
    </div>
    );
};

export default Edit;

버튼 안에 onclick 속에 navigate로 실행
첫번째는 home으로 가야돼서 navigate("/home"); 이렇게 해놨고
두번째는 뒤로가기여서 navigate(-1); 로 함 -1로 뒤로 가기가 실행된다 !!


7.4 프로젝트 기초 공사 1

1) 폰트 설정

@import url('https://fonts.googleapis.com/css2?family=Nanum+Pen+Script&family=Yeon+Sung&display=swap');

.App{
  padding:20px;
  font-family: 'Yeon Sung', cursive;
  font-family: "Nanum Pen Script",cursive;
}

구글 웹 폰트에 들어가서 url을 다운받고
App.css에 복붙해준다

2) 레이아웃 세팅

  • 모든 페이지에 반영되는 레이아웃을 세팅하장
@media (min-width:650px){
  .App{
    width:640px;
  }
}

@media(max-width:650px){
  .App{
    width: 90vw;
  }
}
  1. 650px 이상일 때 .App은 넓이를 640px이 되게 하겠다

  2. 650px이하일 때 .App 은 90vw (지금 화면에서 90퍼센트를 차지하게 하겠다)

@import url('https://fonts.googleapis.com/css2?family=Nanum+Pen+Script&family=Yeon+Sung&display=swap');

body{
  background-color: #f6f6f6;
  display: flex;
  justify-content: center;
  align-items: center; 
  font-family: 'Nanum Pen Script';
  min-height: 100vh;
  margin:0px;
}

@media (min-width:650px){
  .App{
    width:640px;
  }
}

@media(max-width:650px){
  .App{
    width: 90vw;
  }
}

#root{
  background-color: white;
  box-shadow: rgba(100,100,111,0.2) 0px 7px 29px 0px;
}

.App{
  min-height: 100vh;
  padding-left: 20px;
  padding-right: 20px;
}

ㄴ 레이아웃 세팅 완료 !!

3) 이미지 에셋 세팅

  • 감정 이미지들을 프로젝트에서 불러와 사용할 수 있는 환경 세팅

emotion 파일들을 assets 폴더 생성 후 집어넣어준다 !
그 후에

<img src={process.env.PUBLIC_URL + `/assets/emotion1.png`}/>

App.js에다가 코드를 넣어준다
process.env.PUBLIC_URL -> public 디렉토리를 경로화 하는것

<img src={process.env.PUBLIC_URL + `/assets/emotion1.png`}/>
<img src={process.env.PUBLIC_URL + `/assets/emotion2.png`}/>
<img src={process.env.PUBLIC_URL + `/assets/emotion3.png`}/>
<img src={process.env.PUBLIC_URL + `/assets/emotion4.png`}/>
<img src={process.env.PUBLIC_URL + `/assets/emotion5.png`}/>

5개 다 깔아준다

  const env=process.env; 
  env.PUBLIC_URL=env.PUBLIC_URL || "";

ㄴenv.PUBLIC이 있다면 담고 아니면 말아라

-last code-

import './App.css';
import {BrowserRouter, Route, Routes} from 'react-router-dom';

import Home from './pages/Home';
import New from './pages/New';
import Edit from './pages/Edit';
import Diary from './pages/Diary';


function App() {

  const env=process.env; 
  env.PUBLIC_URL=env.PUBLIC_URL || "";

  return (
    <BrowserRouter>
      <div className="App">
        <h2>App.js</h2>

        <Routes>
          <Route path="/" element={<Home/>}/>
          <Route path='/new' element={<New/>}/>
          <Route path='/edit' element={<Edit/>}/>
          <Route path='/diary/:id' element={<Diary/>}/>
        </Routes>
      </div>
    </BrowserRouter>
  );
}

export default App;

4) 공통 컴포넌트 세팅

  • 모든 페이지에 공통으로 사용되는 버튼, 헤더 컴포넌트 세팅

(App.css)

@import url('https://fonts.googleapis.com/css2?family=Nanum+Pen+Script&family=Yeon+Sung&display=swap');

body{
  background-color: #f6f6f6;
  display: flex;
  justify-content: center;
  align-items: center; 
  font-family: 'Nanum Pen Script';
  min-height: 100vh;
  margin:0px;
}

@media (min-width:650px){
  .App{
    width:640px;
  }
}

@media(max-width:650px){
  .App{
    width: 90vw;
  }
}

#root{
  background-color: white;
  box-shadow: rgba(100,100,111,0.2) 0px 7px 29px 0px;
}

.App{
  min-height: 100vh;
  padding-left: 20px;
  padding-right: 20px;
}

/*MyButton*/

.MyButton{
  cursor: pointer;
  border: none;
  border-radius: 5px;

  padding-top: 10px;
  padding-bottom: 20px;
  padding-right: 20px;
  padding-left: 20px;

  font-size: 18px;
  white-space: nowrap;
  font-family: 'Nanum Pen Script';
}

.MyButton_default{
  background-color: #ececec;
  color: black;
}

.MyButton_positive{
  background-color: #64c964;
  color: white;
}

.MyButton_negative{
  background-color: #fd565f;
  color:white;
}

(App.js)

import './App.css';
import {BrowserRouter, Route, Routes} from 'react-router-dom';

import Home from './pages/Home';
import New from './pages/New';
import Edit from './pages/Edit';
import Diary from './pages/Diary';

// COMPONENTS
import MyButton from "./components/MyButton";

function App() {

  const env=process.env; 
  env.PUBLIC_URL=env.PUBLIC_URL || "";

  return (
    <BrowserRouter>
      <div className="App">
        <h2>App.js</h2>

        <MyButton 
          text={'버튼'}
          onClick={()=>alert("버튼 클릭")}
          type={"positive"}
        />

        <MyButton 
          text={'버튼'}
          onClick={()=>alert("버튼 클릭")}
          type={"negative"}
        />

        <MyButton 
          text={'버튼'}
          onClick={()=>alert("버튼 클릭")}
        />

        <Routes>
          <Route path="/" element={<Home/>}/>
          <Route path='/new' element={<New/>}/>
          <Route path='/edit' element={<Edit/>}/>
          <Route path='/diary/:id' element={<Diary/>}/>
        </Routes>
      </div>
    </BrowserRouter>
  );
}

export default App;

(MyButton.js)

const MyButton = (text,type,onClick)=>{

    //없는 글자가 나타나면 강제로 default로 바꿔버림
    const btnType=['positive','negative'].includes(type)? type:'default';

    return(
        <button className={["MyButton",`MyButton_${type}`].join(" ")} 
        onClick={onClick}
        >
          {text}
        </button>
    );
};

MyButton.defaultProps={
    type:"default",
};

export default MyButton;

헤더 컴포넌트 만들기 !

(App.js)

import './App.css';
import {BrowserRouter, Route, Routes} from 'react-router-dom';

import Home from './pages/Home';
import New from './pages/New';
import Edit from './pages/Edit';
import Diary from './pages/Diary';

// COMPONENTS
import MyButton from "./components/MyButton";

function App() {

  const env=process.env; 
  env.PUBLIC_URL=env.PUBLIC_URL || "";

  return (
    <BrowserRouter>
      <div className="App">
        <MyHeader 
          headText={"App"} 
          leftChild={
            <MyButton text={'왼쪽버튼'} onClick={()=> alert("왼쪽 클릭")}/>
          }
          rightChild={
            <MyButton 
              text={'오른쪽 버튼'}
              onClick={()=> alert("오른쪽 클릭")}
            />
          }
        />
        <h2>App.js</h2>

        <MyButton 
          text={'버튼'}
          onClick={()=>alert("버튼 클릭")}
          type={"positive"}
        />

        <MyButton 
          text={'버튼'}
          onClick={()=>alert("버튼 클릭")}
          type={"negative"}
        />

        <MyButton 
          text={'버튼'}
          onClick={()=>alert("버튼 클릭")}
        />

        <Routes>
          <Route path="/" element={<Home/>}/>
          <Route path='/new' element={<New/>}/>
          <Route path='/edit' element={<Edit/>}/>
          <Route path='/diary/:id' element={<Diary/>}/>
        </Routes>
      </div>
    </BrowserRouter>
  );
}

export default App;

(App.css)

@import url('https://fonts.googleapis.com/css2?family=Nanum+Pen+Script&family=Yeon+Sung&display=swap');

body{
  background-color: #f6f6f6;
  display: flex;
  justify-content: center;
  align-items: center; 
  font-family: 'Nanum Pen Script';
  min-height: 100vh;
  margin:0px;
}

@media (min-width:650px){
  .App{
    width:640px;
  }
}

@media(max-width:650px){
  .App{
    width: 90vw;
  }
}

#root{
  background-color: white;
  box-shadow: rgba(100,100,111,0.2) 0px 7px 29px 0px;
}

.App{
  min-height: 100vh;
  padding-left: 20px;
  padding-right: 20px;
}

/*MyButton*/

.MyButton{
  cursor: pointer;
  border: none;
  border-radius: 5px;

  padding-top: 10px;
  padding-bottom: 20px;
  padding-right: 20px;
  padding-left: 20px;

  font-size: 18px;
  white-space: nowrap;
  font-family: 'Nanum Pen Script';
}

.MyButton_default{
  background-color: #ececec;
  color: black;
}

.MyButton_positive{
  background-color: #64c964;
  color: white;
}

.MyButton_negative{
  background-color: #fd565f;
  color:white;
}

/*HEADER*/

header{
  padding-top: 20px;
  padding-bottom: 20px;

  display: flex;
  align-items: center;
  border-bottom: 1px solid #e2e2e2;
}

header > div{
  display: flex;
}

header.head_text{
  width: 50%;
  font-size: 25px;
  justify-content: center;
}

header.head_btn_left{
  width: 25%;
  justify-content: start;
}

header.head_btn_right{
  width: 25%;
  justify-content: end;
}

header button{
  font-family: "Nanum Pen Script";
}

(MyHeader.js)

const MyHeader =({headText,leftChild,rightChild})=>{
    return <header>
        <div className="head_btn_left">
            {leftChild}
        </div>
        <div className="head_text">
            {headText}
        </div>
        <div className="head_btn_right">
            {rightChild}
        </div>
    </header>
}

export default MyHeader;

7.5 프로젝트 기초 공사 2

1) 상태 관리

(App.js)

import React, { useReducer, useRef } from 'react';
import './App.css';
import {BrowserRouter, Route, Routes} from 'react-router-dom';

import Home from './pages/Home';
import New from './pages/New';
import Edit from './pages/Edit';
import Diary from './pages/Diary';
import { useReducer } from 'react';

const reducer=(state,action)=>{
  let newState=[];
  switch(action.type){
    case 'INIT':{
      return action.data;
    }
    case 'CREATE':{
      newState=[action.data,...state];
      break;
    }
    case 'REMOVE':{
      newState=state.filter((it)=>it.id!==action.targetId);
      break;
    }
    case 'EDIT':{
      newState=state.map((it)=>
        it.id===action.data.id?{...action.data}:it
        );
      break;
    }
    default:
      return state;
  }
  return newState;
};

export const DiaryStateContext=React.createContext();
export const DiaryDispatchContext = React.createContext();
function App() {
  const [data,dispatch]=useReducer(reducer,[]);

  const dataId=useRef(0);
  //CREATE
  const onCreate=(date,content,emotion)=>{
    dispatch({type:"CREATE",data:{
      id:dataId.current,
      date:new Date(date).getTime(),
      content,
      emotion,
    }});
    dataId.current+=1;
  };

  //REMOVE
  const onRemove =(targetId)=>{
    dispatch({type:"REMOVE",targetId});
  };

  //EDIT
  const onEdit=(targetId,date,content,emotion)=>{
    dispatch({
      type:"EDIT",
      data:{
        id:targetId,
        date:new Date(date).getTime(),
        content,
        emotion,
      },
    });
  };

  return (
    <DiaryStateContext.Provider value={data}>
      <DiaryDispatchContext.Provider 
        value={{
          onCreate,
          onEdit,
          onRemove,
        }}
      >
      <BrowserRouter>
        <div className="App">
          <Routes>
            <Route path="/" element={<Home/>}/>
            <Route path='/new' element={<New/>}/>
            <Route path='/edit' element={<Edit/>}/>
            <Route path='/diary/:id' element={<Diary/>}/>
          </Routes>
        </div>
      </BrowserRouter>
      </DiaryDispatchContext.Provider>
    </DiaryStateContext.Provider>
  );
}

export default App;

7.6 HOME 구현하기


오늘 만들 결과물 !!

(App.js)

import React, { useReducer, useRef } from 'react';
import './App.css';
import {BrowserRouter, Route, Routes} from 'react-router-dom';

import Home from './pages/Home';
import New from './pages/New';
import Edit from './pages/Edit';
import Diary from './pages/Diary';
import { useReducer } from 'react';

const reducer=(state,action)=>{
  let newState=[];
  switch(action.type){
    case 'INIT':{
      return action.data;
    }
    case 'CREATE':{
      newState=[action.data,...state];
      break;
    }
    case 'REMOVE':{
      newState=state.filter((it)=>it.id!==action.targetId);
      break;
    }
    case 'EDIT':{
      newState=state.map((it)=>
        it.id===action.data.id?{...action.data}:it
        );
      break;
    }
    default:
      return state;
  }
  return newState;
};

export const DiaryStateContext=React.createContext();
export const DiaryDispatchContext = React.createContext();

const dummyData=[
  {
    id:1,
    emotion:1,
    content:"오늘의 일기 1번",
    date:12321
  },
  {
    id:2,
    emotion:2,
    content:"오늘의 일기 2번",
    date:12322
  },
  {
    id:3,
    emotion:3,
    content:"오늘의 일기 3번",
    date:12323,
  },
  {
    id:4,
    emotion:4,
    content:"오늘의 일기 4번",
    date:12324,
  },
  {
    id:5,
    emotion:5,
    content:"오늘의 일기 5번",
    date:12325,
  },
];

function App() {
  const [data,dispatch]=useReducer(reducer,dummyData);

  const dataId=useRef(0);
  //CREATE
  const onCreate=(date,content,emotion)=>{
    dispatch({type:"CREATE",data:{
      id:dataId.current,
      date:new Date(date).getTime(),
      content,
      emotion,
    }});
    dataId.current+=1;
  };

  //REMOVE
  const onRemove =(targetId)=>{
    dispatch({type:"REMOVE",targetId});
  };

  //EDIT
  const onEdit=(targetId,date,content,emotion)=>{
    dispatch({
      type:"EDIT",
      data:{
        id:targetId,
        date:new Date(date).getTime(),
        content,
        emotion,
      },
    });
  };

  return (
    <DiaryStateContext.Provider value={data}>
      <DiaryDispatchContext.Provider 
        value={{
          onCreate,
          onEdit,
          onRemove,
        }}
      >
      <BrowserRouter>
        <div className="App">
          <Routes>
            <Route path="/" element={<Home/>}/>
            <Route path='/new' element={<New/>}/>
            <Route path='/edit' element={<Edit/>}/>
            <Route path='/diary/:id' element={<Diary/>}/>
          </Routes>
        </div>
      </BrowserRouter>
      </DiaryDispatchContext.Provider>
    </DiaryStateContext.Provider>
  );
}

export default App;

(App.css)

@import url('https://fonts.googleapis.com/css2?family=Nanum+Pen+Script&family=Yeon+Sung&display=swap');

body{
  background-color: #f6f6f6;
  display: flex;
  justify-content: center;
  align-items: center; 
  font-family: 'Nanum Pen Script';
  min-height: 100vh;
  margin:0px;
}

@media (min-width:650px){
  .App{
    width:640px;
  }
}

@media(max-width:650px){
  .App{
    width: 90vw;
  }
}

#root{
  background-color: white;
  box-shadow: rgba(100,100,111,0.2) 0px 7px 29px 0px;
}

.App{
  min-height: 100vh;
  padding-left: 20px;
  padding-right: 20px;
}

/*MyButton*/

.MyButton{
  cursor: pointer;
  border: none;
  border-radius: 5px;

  padding-top: 10px;
  padding-bottom: 20px;
  padding-right: 20px;
  padding-left: 20px;

  font-size: 18px;
  white-space: nowrap;
  font-family: 'Nanum Pen Script';
}

.MyButton_default{
  background-color: #ececec;
  color: black;
}

.MyButton_positive{
  background-color: #64c964;
  color: white;
}

.MyButton_negative{
  background-color: #fd565f;
  color:white;
}

/*HEADER*/

header{
  padding-top: 20px;
  padding-bottom: 20px;

  display: flex;
  align-items: center;
  border-bottom: 1px solid #e2e2e2;
}

header > div{
  display: flex;
}

header.head_text{
  width: 50%;
  font-size: 25px;
  justify-content: center;
}

header.head_btn_left{
  width: 25%;
  justify-content: start;
}

header.head_btn_right{
  width: 25%;
  justify-content: end;
}

header button{
  font-family: "Nanum Pen Script";
}

/*DiaryList*/

.DiaryList.menu_wrapper{
  margin-top: 20px;
  margin-bottom: 30px;

  display: flex;
  justify-content: space-between;
}

.DiaryList.menu_wrapper .right_col{
  flex-grow: 1;
}

.DiaryList.menu_wrapper .right_col button{
  width: 100%;
}

.DiaryList .ControlMenu{
  margin-right: 10px;
  border: none;
  border-radius: 5px;
  background-color: #ececec;

  padding-top: 10px;
  padding-bottom: 10px;
  padding-left: 20px;
  padding-right: 20px;

  cursor: pointer;
  font-family: 'Nanum Pen Script';
  font-size: 18px;
}

/*Diary Item*/

.DiaryItem{
  padding-top: 15px;
  padding-bottom: 15px;

  border-bottom: 1px solid #e2e2e2;

  display: flex;
  justify-content: space-between;
}

.DiaryItem .emotino_img_wrapper{
  cursor: pointer;
  min-width: 120px;
  height: 80px;
  border-radius: 5px;
  display: flex;
  justify-self: center;
}

.DiaryItem .emotino_img_wrapper_1{
  background-color: #64c964;
}
.DiaryItem .emotino_img_wrapper_2{
  background-color: #9dd772;
}
.DiaryItem .emotino_img_wrapper_3{
  background-color: #fdce17;
}
.DiaryItem .emotino_img_wrapper_4{
  background-color: #fd8446;
}
.DiaryItem .emotino_img_wrapper_5{
  background-color: #fd565f;
}

.DiaryItem .emotino_img_wrapper img{
  width: 50%;
}

.DiaryItem .info_wrapper{
  flex-grow: 1;
  margin-left: 20px;
  cursor: pointer;
}

.DiaryItem .diarydate{
  font-weight: bold;
  font-size: 25px;
  margin-bottom: 5px;
}

.DiaryItem .diary_content_preview{
  font-size: 18px;
}

.DiaryItem .btn_wrapper{
  min-width: 70px;
}

(Home.js)

import { useContext, useEffect, useState } from "react";
import { DiaryStateContext } from "../App";

import MyHeader from'./../components/MyHeader';
import MyButton from './../components/MyButton';
import DiaryList from "../components/DiaryList";

const Home=()=>{

    const diaryList=useContext(DiaryStateContext);

    const [data,setData]=useState([]);
    const [curDate,setCurDate]=useState(new Date());
    const headText =`${curDate.getFullYear()}${curDate.getMonth()+1 }`

    useEffect(()=>{

        if(diaryList.length>=1){
        const firstDay =new Date(
            curDate.getFullYear(),
            curDate.getMonth(),
            1
        ).getTime();

        const lastDay = new Date(
            curDate.getFullYear(),
            curDate.getMonth()+1,
            0
        ).getTime();

        setData(
            diaryList.filter((it)=>firstDay<=it.date && it.data<=lastDay)
            );
          }
        },[diaryList, curDate]);

    useEffect(()=>{
        console.log(data);
    },[data]);

    const increaseMonth=()=>{
        setCurDate(
            new Date(curDate.getFullYear(),curDate.getMonth()+1,curDate.getDate())
        );
    };
    const decreaseMonth=()=>{
        setCurDate(
            new Date(curDate.getFullYear(),curDate.getMonth()-1,curDate.getDate())
        );
    };

    return (
    <div>
        <MyHeader headText={headText}
        leftChild={<MyButton text={"<"} onClick={()=>{}} />}
        rightChild={<MyButton text={">"} onClick={increaseMonth}/>}
        />
        <DiaryList diaryList={data}/>
    </div>
    );
};

export default Home;

(DiaryList.js)

import { useState } from "react";
import { useNavigate } from "react-router-dom";
import MyButton from "./MyButton";
import DiaryItem from "./DiaryItem";

const sortOptionList=[
    {value:"latest",name:"최신순"},
    {value:"oldest",name:"오래된 순"},
];

const filterOptionList = [
    {value:"all",name:"전부 다"},
    {value:"good", name:"좋은 감정만",},
    {value:"bad", name:"안좋은 감정만",},    
];

const ControlMenu=({value,onChange,optionList})=>{
    return (
      <select 
        className="ControlMenu"
        value={value} 
        onChange={(e)=>onChange(e.target.value)}
      >
        {optionList.map((it,idx)=>(
        <option key={idx} value={it.value}>
            {it.name}
        </option>
        ))}
      </select>
    );
};

const DiaryList = ({diaryList})=>{
    const navigate = useNavigate();
    const [sortType,setSortType]=useState('latest');
    const [filter,setFilter]=useState("all");

    const getProcessedDiaryList=()=>{

        const filterCallBack=(item)=>{
            if(filter==='good'){
                return parseInt(item.emotion)<=3;
            }else{
                return parseInt(item.emotion)>3;
            }
        }

        const compare =(a,b)=>{
            if(sortType==='latest'){
                return parseInt(b.date)-parseInt(a.date);
            }
            else{
                return parseInt(a.date)-parseInt(b.date);
            }
        };

        const copyList = JSON.parse(JSON.stringify(diaryList)) //diaryList를 문자열로 바꿈
        
        const filteredList = 
            filter === 'all'? copyList:copyList.filter((it)=>filterCallBack(it));
                
        const sortedList = copyList.sort(compare);
        return sortedList;
    };
    
    return (
    <div className="DiaryList">

    <div className="menu_wrapper">
        <div className="left_col">
            <ControlMenu 
            value={sortType} 
            onChange={setSortType}
            optionList={sortOptionList}
            />
            <ControlMenu
            value={filter}
            onChange={setFilter}
            optionList={filterOptionList}
            />
        </div>
        <div className="right_col">
            <MyButton 
                type={'positive'} 
                text={'새 일기 쓰기'} 
                onClick={()=>navigate('/new')}
            />
        </div>
    </div>

    {getProcessedDiaryList.map((it)=>(
        <DiaryItem key={it.id} {...it}/>
    ))}
  </div>
    );
};

DiaryList.defaultProps={
    diaryList:[],
};

export default DiaryList;

(DiaryItem.js)

import MyButton from "./MyButton";
import {useNavigate} from "react-router-dom";

const DiaryItem = ({id,emotion,content,date})=>{

    const env=process.env;
    env.PUBLIC_URL=env.PUBLIC_URL||"";

    const strDate = new Date(parseInt(date)).toLocaleDateString();
    const goDetail = ()=>{
        navigate(`/diary/${id}`);
    };

    const goEdit=()=>{
        navigate(`/edit/${id}`);
    };

    return (
        <div className="DiaryItem">
        <div 
        className={["emotion_img_wrapper",
        `emotion_img_wrapper_${emotion}`,
        ].join(" ")}
        >
            <img src={process.env.PUBLIC_URL + `assets/emotion${emotion}.png`}/>
        </div>
        <div onClick={goDetail} className="info_wrapper">
            <div className="diary_date">{strDate}</div>
            <div className="diary_content_preview">{content.slice(0,25)}</div>
        </div>
        <div className="btn_wrapper">
            <MyButton onClick={goEdit} text={"수정하기"}/>
        </div>
    </div>
    );
};

export default DiaryItem;

끝 !!

하지만 아직도 npm start는 되지 않고 있다... 슾ㄹ프다 ㅜㅡㅜ
아무래도 파일을 삭제하고 다시 해야 할듯 ..


7.7 페이지 구현 - 일기쓰기 (/new)

저번에 안되던 npm start를 해결하기 위해 원래 썼던 emotionDiary를 지우고 emotionDairy2를 만들어서 에러를 다 고쳐보았당
근데 문제가 또 발생

아무것도 안 나옴.. 진짜 억까다 이건


미치겠음

New.js 생성

import DiaryEditor from "../components/DiaryEditor";

const New=()=>{
    return( 
    <div>
        <DiaryEditor/>
    </div>
    );
};

export default New;

DiaryEditor.js에다가

const emotionList = [
    {
        emotion_id:1,
        emotion_img : process.env.PUBLIC_URL + `/assets/emotion1.png`,
        emotion_descript : '완전 좋음'
    },
    {
        emotion_id:2,
        emotion_img : process.env.PUBLIC_URL + `/assets/emotion2.png`,
        emotion_descript : '좋음'
    },
    {
        emotion_id:3,
        emotion_img : process.env.PUBLIC_URL + `/assets/emotion3.png`,
        emotion_descript : '그럭저럭'
    },
    {
        emotion_id:4,
        emotion_img : process.env.PUBLIC_URL + `/assets/emotion4.png`,
        emotion_descript : '나쁨'
    },
    {
        emotion_id:5,
        emotion_img : process.env.PUBLIC_URL + `/assets/emotion5.png`,
        emotion_descript : '끔찍함'
    },
]

감정 객체 넣고

DiaryEditor.js full code

import { useNavigate } from "react-router-dom";
import { useContext, useRef, useState } from "react";
import MyHeader from "./MyHeader";
import MyButton from "./MyButton";
import EmotionItem from "./EmotionItem";
import { DiaryDispatchContext } from "../App";

const emotionList = [
    {
        emotion_id:1,
        emotion_img : process.env.PUBLIC_URL + `/assets/emotion1.png`,
        emotion_descript : '완전 좋음'
    },
    {
        emotion_id:2,
        emotion_img : process.env.PUBLIC_URL + `/assets/emotion2.png`,
        emotion_descript : '좋음'
    },
    {
        emotion_id:3,
        emotion_img : process.env.PUBLIC_URL + `/assets/emotion3.png`,
        emotion_descript : '그럭저럭'
    },
    {
        emotion_id:4,
        emotion_img : process.env.PUBLIC_URL + `/assets/emotion4.png`,
        emotion_descript : '나쁨'
    },
    {
        emotion_id:5,
        emotion_img : process.env.PUBLIC_URL + `/assets/emotion5.png`,
        emotion_descript : '끔찍함'
    },
]

const getStringDate = (date)=>{
    return date.toISOString().slice(0,10);
};

const DiaryEditor =()=>{
    const contentRef = useRef();
    const [content,setContent]=useState("");
    const [emotion,setEmotion]=useState(3);
    const [date,setDate] = useState(getStringDate(new Date()));

    const {onCreate} = useContext(DiaryDispatchContext);
    const handleClickEmote = (emotion)=>{
        setEmotion(emotion); 
    };
    const navigate = useNavigate();

    const handleSubmit=()=>{
        if(content.length<1){
            contentRef.current.focus();
            return;
        }
        onCreate(date,content,emotion);
        navigate('/',{replace:true});
    };

    return(
    <div className="DiaryEditor">
        <MyHeader 
        headText={"새 일기 쓰기"}
        leftChild={
          <MyButton text ={"< 뒤로가기"} onclick ={()=>navigate(-1)}/>
        }
    />
    <div>
        <section>
          <h4>오늘은 언제인가요?</h4>
          <div className="input_box">
            <input 
                className="input_date"
                value = {date} 
                onChange={(e)=>setDate(e.target.value)} 
                type="date" 
            />
          </div>
        </section>
        <section>
            <h4>오늘의 감정</h4>
            <div className="input_box emotion_list_wrapper">
                {emotionList.map((it)=>(
                  <EmotionItem 
                  key = {it.emotion_id} 
                  {...it}
                  onClick={handleClickEmote}
                  isSelected = {it.emotion_id===emotion}
                  />
                ))}
            </div>
        </section>
        <section>
            <h4>오늘의 일기</h4>
            <div className="input_box text_wrapper">
                <textarea 
                  placeholder="오늘은 어땠나요 ?"
                  ref = {contentRef} 
                  value = {content} 
                  onChange={(e)=>setContent(e.target.value)}
                />
            </div>
        </section>
        <section>
            <div className="control_box">
                <MyButton text = {'취소하기'} onClick={()=>navigate(-1)}/>
                <MyButton 
                    text = {"작성완료"} 
                    type={"positive"} 
                    onClick={handleSubmit} 
                    />
            </div>
        </section>
    </div>
    </div>
    );
};

export default DiaryEditor;

전체적으로 추가ㅏ해주었따

App.css에 DiaryItem이랑 EmotionItem 속성들 추가해줌

/*DiaryEditor*/

.DiaryEditor section{
  margin-bottom: 40px;
}

.DiaryEditor h4{
  font-size: 22px;
  font-weight: bold;
}

.DiaryEditor .input_date{
  border: none;
  border-radius: 5px;
  background-color: #ececec;

  padding-top: 10px;
  padding-bottom: 10px;
  padding-left: 20px;
  padding-right: 20px;

  cursor:pointer;
  font-family: "Nanum Pen Script";
  font-size: 20px;
}
.DiaryEditor .emotion_list_wrapper{
  display: grid;
  grid-template-columns: repeat(5,auto);
  gap:2%;
}

.DiaryEditor textarea{
  font-family: "Nanum Pen Script";
  font-size: 20px;

  box-sizing: border-box;
  width: 100%;
  min-height: 200px;
  resize: vertical;

  border: none;
  border-radius: 5px;
  background-color: #ececec;

  padding: 20px;
}
.DiaryEditor .control_box{
  display: flex;
  justify-content: space-between;
  align-items: center;
}

/*EMOTION ITEM*/

.EmotionItem{
  cursor: pointer;
  
  border-radius: 5px;
  padding-top: 20px;
  padding-bottom: 20px;

  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}

.EmotionItem img{
  width: 50%;
  margin-bottom: 10px;
}

.EmotionItem span{
  font-size: 18px;
}

.EmotionItem_off{
  background-color: #ececec;
}

.EmotionItem_on_1{
  background-color: #64c964;
  color: white;
}

.EmotionItem_on_2{
  background-color: #9dd772;
  color: white;
}

.EmotionItem_on_3{
  background-color: #fdce17;
  color: white;
}

.EmotionItem_on_4{
  background-color: #fd8446;
  color: white;
}

.EmotionItem_on_5{
  background-color: #fd565f;
  color: white;
}

7.8 페이지구현 - 일기수정 (/edit)

우선 Edit.js 에다가

import { useContext, useEffect, useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import { DiaryStateContext } from '../App';
import DiaryEditor from '../components/DiaryEditor';

const Edit=()=>{

    const [originData,setOriginData] = useState();
    const navigate = useNavigate();
    const {id}=useParams();
    const diaryList = useContext(DiaryStateContext);

useEffect(()=>{
    if(diaryList.length>=1){
       const targetDiary = diaryList.find(
        (it)=>parseInt(it.id)===parseInt(id)
        ); 

        if(targetDiary){
            setOriginData(targetDiary);
        } else{
            navigate('/',{replace:true});
        }
    }
},[id,diaryList]);

    return (
    <div>
        {originData&&<DiaryEditor isEdit={true} originData={originData}/>}
    </div>
    );
};

export default Edit;

이렇게 추가해주었구

DiaryEditor.js에도 추가해줌

 const handleSubmit=()=>{
        if(content.length<1){
            contentRef.current.focus();
            return;
        }
        if(window.confirm(isEdit?"일기를 수정하시겠습니까?":"새로운 일기를 작성하시겠습니까?")){
            if(!isEdit){
                onCreate(date,content,emotion)
            }else{
                OnEdit(originData.id,date,content,emotion);
            }
        }
        
        navigate('/',{replace:true});
    };

ㄴ 함수 추가하기

DiaryEditor.js full code

import { useNavigate } from "react-router-dom";
import { useContext, useEffect, useRef, useState } from "react";
import MyHeader from "./MyHeader";
import MyButton from "./MyButton";
import EmotionItem from "./EmotionItem";
import { DiaryDispatchContext } from "../App";

const emotionList = [
    {
        emotion_id:1,
        emotion_img : process.env.PUBLIC_URL + `/assets/emotion1.png`,
        emotion_descript : '완전 좋음'
    },
    {
        emotion_id:2,
        emotion_img : process.env.PUBLIC_URL + `/assets/emotion2.png`,
        emotion_descript : '좋음'
    },
    {
        emotion_id:3,
        emotion_img : process.env.PUBLIC_URL + `/assets/emotion3.png`,
        emotion_descript : '그럭저럭'
    },
    {
        emotion_id:4,
        emotion_img : process.env.PUBLIC_URL + `/assets/emotion4.png`,
        emotion_descript : '나쁨'
    },
    {
        emotion_id:5,
        emotion_img : process.env.PUBLIC_URL + `/assets/emotion5.png`,
        emotion_descript : '끔찍함'
    },
]

const getStringDate = (date)=>{
    return date.toISOString().slice(0,10);
};

const DiaryEditor =(isEdit,originData)=>{
    const contentRef = useRef();
    const [content,setContent]=useState("");
    const [emotion,setEmotion]=useState(3);
    const [date,setDate] = useState(getStringDate(new Date()));

    const {onCreate,OnEdit} = useContext(DiaryDispatchContext);
    const handleClickEmote = (emotion)=>{
        setEmotion(emotion); 
    };
    const navigate = useNavigate();

    const handleSubmit=()=>{
        if(content.length<1){
            contentRef.current.focus();
            return;
        }
        if(window.confirm(isEdit?"일기를 수정하시겠습니까?":"새로운 일기를 작성하시겠습니까?")){
            if(!isEdit){
                onCreate(date,content,emotion)
            }else{
                OnEdit(originData.id,date,content,emotion);
            }
        }
        
        navigate('/',{replace:true});
    };

    useEffect(()=>{
        if(isEdit){
            setDate(getStringDate(new Date(parseInt(originData.date))));
            setEmotion(originData.emotion);
            setContent(originData.content);
        }
    },[isEdit,originData])

    return(
    <div className="DiaryEditor">
        <MyHeader 
        headText={isEdit ? "일기 수정하기" : "새 일기 쓰기"}
        leftChild={
          <MyButton text ={"< 뒤로가기"} onclick ={()=>navigate(-1)}/>
        }
    />
    <div>
        <section>
          <h4>오늘은 언제인가요?</h4>
          <div className="input_box">
            <input 
                className="input_date"
                value = {date} 
                onChange={(e)=>setDate(e.target.value)} 
                type="date" 
            />
          </div>
        </section>
        <section>
            <h4>오늘의 감정</h4>
            <div className="input_box emotion_list_wrapper">
                {emotionList.map((it)=>(
                  <EmotionItem 
                  key = {it.emotion_id} 
                  {...it}
                  onClick={handleClickEmote}
                  isSelected = {it.emotion_id===emotion}
                  />
                ))}
            </div>
        </section>
        <section>
            <h4>오늘의 일기</h4>
            <div className="input_box text_wrapper">
                <textarea 
                  placeholder="오늘은 어땠나요 ?"
                  ref = {contentRef} 
                  value = {content} 
                  onChange={(e)=>setContent(e.target.value)}
                />
            </div>
        </section>
        <section>
            <div className="control_box">
                <MyButton text = {'취소하기'} onClick={()=>navigate(-1)}/>
                <MyButton 
                    text = {"작성완료"} 
                    type={"positive"} 
                    onClick={handleSubmit} 
                    />
            </div>
        </section>
    </div>
    </div>
    );
};

export default DiaryEditor;

7.9 페이지구현 - 일기상세 (/diary)

Diary.js를 꾸며보아따

import {useNavigate, useParams} from 'react-router-dom';
import { useContext, useEffect, useState } from 'react';
import { DiaryStateContext } from '../App';
import { getStringDate } from '../util/date';
import MyHeader from '../components/MyHeader';
import MyButton from '../components/MyButton';
import { emotionList } from '../util/emotion';

const Diary=()=>{
    const {id}=useParams();
    const diaryList = useContext(DiaryStateContext);
    const navigate = useNavigate();
    const [data,setData]=useState();

    useEffect(()=>{
        if(diaryList.length>=1){
          const targetDiary = diaryList.find(
            (it)=>parseInt(it.id)===parseInt(id)
            );
            
            if(targetDiary){
                //일기가 존재할 때
                setData(targetDiary);
            }else{
                //일기가 없을 때
                alert("없는 일기입니다.");
                navigate('/',{replace:true});
            }
        }
    },[id,diaryList]);

    if(!data){
        return <div className='DiaryPage'>로딩중입니다...</div>
    }else{

        const curEmotionData = emotionList.find(
            (it)=>parseInt(it.emotion_id)===parseInt(data.emotion)
            );

      return (
        <div className='DiaryPage'>
          <MyHeader headText={`${getStringDate(new Date(data.date))} 기록`}
            leftChild={
                <MyButton text={"< 뒤로가기"} onClick={()=> navigate(-1)}/>
            }
            rightChild={
                <MyButton text = {"수정하기"} 
                onClick={()=>navigate(`/edit/${data.id}`)}
            
            />
            }
            />
            <article>
                <section>
                    <h4>오늘의 감정</h4>
                    <div className={[
                        'diary_img_wrapper',`diary_img_wrapper_${data.emotion}`,
                        ].join(" ")}
                        >
                        <img src = {curEmotionData.emotion_img}/>
                        <div className='emotion_descript'>
                            {curEmotionData.emotion_descript}
                        </div>
                    </div>
                </section>
                <section>
                    <h4>오늘의 일기</h4>
                    <div className='diary_content_wrapper'>
                        <p>{data.content}</p>
                    </div>
                </section>
            </article>
        </div>
      );
    }
};

export default Diary;

그리고 코드가 안 길어지게 util 이라는 폴더를 만들어서 긴 코드를 넣고
import해오는 방법도 새롭게 배움 !

util/date.js

export const getStringDate = (date)=>{
    return date.toISOString().slice(0,10);
};

util/emotion.js

export const emotionList = [
    {
        emotion_id:1,
        emotion_img : process.env.PUBLIC_URL + `/assets/emotion1.png`,
        emotion_descript : '완전 좋음'
    },
    {
        emotion_id:2,
        emotion_img : process.env.PUBLIC_URL + `/assets/emotion2.png`,
        emotion_descript : '좋음'
    },
    {
        emotion_id:3,
        emotion_img : process.env.PUBLIC_URL + `/assets/emotion3.png`,
        emotion_descript : '그럭저럭'
    },
    {
        emotion_id:4,
        emotion_img : process.env.PUBLIC_URL + `/assets/emotion4.png`,
        emotion_descript : '나쁨'
    },
    {
        emotion_id:5,
        emotion_img : process.env.PUBLIC_URL + `/assets/emotion5.png`,
        emotion_descript : '끔찍함'
    },
]

Diary부분에 해당하는 app.css에 속성도 추가하였음

/*Diary*/

.DiaryPage section{
  width: 100%;
  margin-bottom: 100px;

  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}

.DiaryPage h4{
  font-size: 22px;
  font-weight: bold;
}

.DiaryPage .diary_img_wrapper{
  background-color: #ececec;
  width: 25px;
  height: 25px;
  border-radius: 5px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-around;
}

.DiaryPage .diary_img_wrapper_1{
  background-color: #64c964;
}

.DiaryPage .diary_img_wrapper_2{
  background-color: #9dd772;
}

.DiaryPage .diary_img_wrapper_3{
  background-color: #fdce17;
}

.DiaryPage .diary_img_wrapper_4{
  background-color: #fd8446;
}

.DiaryPage .diary_img_wrapper_5{
  background-color: #fd565f;
}

.DiaryPage .emotion_descript{
  font-size: 25px;
  color: white;
}

.DiaryPage .diary_content_wrapper{
  width: 100%;
  background-color: #ececec;
  border-radius: 5px;
  word-break: keep-all;
  overflow-wrap: break-word;
}

.DiaryPage .diary_content_wrapper p{
  padding: 20px;
  text-align: left;
  font-size: 20px;
  font-family: 'Yeon Sung';
  font-weight: 400;
  line-height: 2.5;
}

7.10 흔히 만날 수 있는 버그 수정하기

1) encountered two children 이라는 버그를 만나면 !

 const dataId=useRef(0);
 
 ->  const dataId=useRef(6);

겹치는 키가 발생을 했는지 확인하고 있다면 바꾸자

2) 오타 !

강의에선 const [sortType,setSortType]=useState('latest'); 이거를
const [sortType,setSortType]=useState('lasest'); 라고 해놔서 버그가 났당

3) 마지막 시분초 적기 !
원래는

const lastDay = new Date(
            curDate.getFullYear(),
            curDate.getMonth()+1,
            0,
        ).getTime();

0 뿐이었지만

const lastDay = new Date(
            curDate.getFullYear(),
            curDate.getMonth()+1,
            0,
            23,
            59,
            59
        ).getTime();

이렇게 적어줘야함


7.11 LocalStorage를 일기 데이터베이스로 사용하기

state가 갖는 값은 유지되기 힘들다 -> 데이터베이스로 값을 저장해서 새로고침했을 땐 데이터베이스에서 값을 불러오기

localstorage 쓰기

useEffect(()=>{
    const item1 = localStorage.getItem('item1');
    const item2 = localStorage.getItem('item2');
    const item3 = JSON.parse(localStorage.getItem('item3'));
    console.log({item1,item2,item3});
  },[]);

app.js에다가

useEffect(()=>{
    const localData = localStorage.getItem('diary');
    if(localData){
      const diaryList = JSON.parse(localData).sor;test((a,b)=>parseInt(b.id)-parseInt(a.id));
      dataId.current = parseInt(diaryList[0].id)+1

      dispatch({type:"INIT",data:diaryList});
    }
  },[]);

이렇게 localstorage 이용하는 함수 짜줌

DiaryEditor.js에다가 삭제하기 버튼도 만들어따

rightChild={
            isEdit&&(
                <MyButton 
                text={"삭제하기"} 
                type={"negative"}
                onClick ={handleRemove} 
                />
            )
        }

7.12 최적화

1) 코드를 하나하나 보면서 최적화 판단하기 (정적분석)
2) 프로젝트 켜서 도구의 힘을 받아서 찾아내는 방법 (동적분석)

const ControlMenu=React.memo(({value,onChange,optionList})=>{
    return (
      <select 
        className="ControlMenu"
        value={value} 
        onChange={(e)=>onChange(e.target.value)}
      >
        {optionList.map((it,idx)=>(
        <option key={idx} value={it.value}>
            {it.name}
        </option>
        ))}
      </select>
    );

React.memo로 감싸면 강화된 컴포넌트로 돌려주는 고차컴포넌트임

굳이 쓸모 없는 이런 함수들은 지워준다 !

useEffect(()=>{
        console.log("control menu");
    });

그리고

const [sortType, setSortType]=useState("latest")

를 쓰면
굳이 아래에다가

 const handleSetSortType = (sortType)=>{
 	setSortType(sortType);
 }

를 안써도 된다

이미지, 동영상 등을 렌더링하면 렉걸릴수도 있다 그래서 react.memo적용하기

그리고
DiaryEditor.js 에서

const handleClickEmote = useCallback((emotion)=>{
        setEmotion(emotion); 
    },[]);

수정을 해주었당


7.13 배포 준비 & 프로젝트 빌드하기

package.json에 가면
"build": "react-scripts build"
이렇게 빌드 스크립트가 있당

이제 빌드 하려고 명령어 치려고 했는데

이게 뭐세요...

얘가 나타나기 시작한건 정확히

useEffect(()=>{
        const titleElement = document.getElementsByTagName('title')[0];
        titleElement.innerHTML = `감정 일기장 - ${id}번 일기 수정`;
    },[]);

얘를 입력하기 시작한 후인데
아무리봐도 강의랑 나랑 코드가 똑!!!!같다..

캐열심히 들었는데 여기서 막혀서 빌드 못하면
너무 억울한디...

3개의 댓글

어이쿠... "npm start"를 못했으면 언제 어디서 오류가 났는지도 모르겠네여.. 다음주까지 새로 폴더 만들어서 할 수 있는 거죠..? 시작부터 안되는 거면 흠 "npx create-react-app emotionDiary"로 폴더를 맞게 만든 건지 확인해바여!

답글 달기
comment-user-thumbnail
2023년 6월 5일

글 잘 읽었습니다.
npm 문제는 의외로 개발환경 문제에서 발생하는 경우도 있어서 해결하기 까다로울것 같네요 ㅠㅠ
빠른 해결을 바라겠습니다.

답글 달기

npm start가 안되는데도 끝까지 잘 해내신 게 대단합니다!
수고 많으셨어요!

답글 달기