<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>27px.log</title>
        <link>https://velog.io/</link>
        <description>안녕하세요?</description>
        <lastBuildDate>Wed, 19 Oct 2022 14:57:48 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>27px.log</title>
            <url>https://images.velog.io/images/blueprint-12/profile/f6a3256a-90ee-4c76-8fb9-fc04decadf0b/social.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. 27px.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/blueprint-12" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[React] Context API 사용하기]]></title>
            <link>https://velog.io/@blueprint-12/React-Context-API-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@blueprint-12/React-Context-API-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</guid>
            <pubDate>Wed, 19 Oct 2022 14:57:48 GMT</pubDate>
            <description><![CDATA[<p>App컴포넌트에서 모든 데이터를 Provider 컴포넌트에 내려주면 
Provider 컴포넌트는 그 데이터를 자식 컴포넌트에게 다이렉트로 전달해줄 수 있습니다.
Provider컴포넌트의 자식 컴포넌트들은 Context 하에 존재합니다. 
(Redux의 스토어 Provider 같은 느낌)</p>
<ul>
<li>Provider 컴포넌트 문맥 안에 존재하지 않는 컴포넌트는 당연히 해당 데이터와 문맥에 접근할 수 없습니다. </li>
</ul>
<p>Context API를 사용하면 불필요한 Props drilling을 피할 수 있습니다. </p>
<p><strong>Context 생성</strong></p>
<pre><code class="language-javascript">const Mycontext = React.createContext(defaultValue);</code></pre>
<p><strong>Context Provider를 통한 데이터 공급</strong></p>
<pre><code class="language-react">&lt;MyContext.Provider value={전역으로 전달하고자 하는 값}&gt;
{//이 context 안에 위치할 자식 컴포넌트들}
&lt;/MyContext.Provider&gt;</code></pre>
<p><i>App.js</i></p>
<ul>
<li>context 컴포넌트를 React.createContext()를 통해 만들어준다. <blockquote>
<p>createContext를 통해 만든 컨텍스트에 접근하여 데이터를 꺼내와야 하기 때문에 export로 내보내줍시다. </p>
</blockquote>
</li>
</ul>
<pre><code class="language-react">//해당 context도 es모듈 시스템으로 내보내줘야 다른 곳에서 사용할 수 있기 때문에 export 해준다.

export const DiaryStateContext = React.createContext();


//중략, App 컴포넌트의 return 부에 만들어둔 context를 제공한다. 

return (
    &lt;DiaryStateContext.Provider value={data}&gt;
      &lt;div className=&quot;App&quot;&gt;
        &lt;DiaryEditor onCreate={onCreate} /&gt;
        &lt;div&gt;전체 일기: {data.length}&lt;/div&gt;
        &lt;div&gt;기분 좋은 일기 개수 : {goodCount}&lt;/div&gt;
        &lt;div&gt;기분 안좋은 일기 개수: {badCount}&lt;/div&gt;
        &lt;div&gt;기분 좋은 일기 퍼센트: {goodRatio}%&lt;/div&gt;
        &lt;DiaryList diaryList={data} onRemove={onRemove} onEdit={onEdit} /&gt;
      &lt;/div&gt;
    &lt;/DiaryStateContext.Provider&gt;
  );</code></pre>
<p>DiaryList 에서 불러와서 사용한다면, </p>
<ul>
<li>useContext 훅에 만들어둔 context컴포넌트를 인자로 전달해줍니다. App.js에 만들어둔 context 컴포넌트는 당연히 export 된 상태여야 하고 사용할 곳으로 import 해오는 것을 명심하세용</li>
</ul>
<p><i>src/components/DiaryList.js</i></p>
<pre><code class="language-javascript">import React, { useContext } from &quot;react&quot;;
import DiaryItem from &quot;./DiaryItem&quot;;
import { DiaryStateContext } from &quot;../App&quot;;

function DiaryList({ onRemove, onEdit }) {
  // useContext의 인자로 만들어둔 컨텍스트 컴포넌트를 넣어줘야 한다.
  // export해놓은 context 컴포넌트를 임포트해오고 사용
  const diaryList = useContext(DiaryStateContext);

  return (
    &lt;div className=&quot;DiaryList&quot;&gt;
      &lt;h2&gt;일기 리스트&lt;/h2&gt;
      &lt;b&gt;총 {diaryList.length}개의 일기가 있습니다.&lt;/b&gt;
      &lt;div&gt;
        &lt;ul&gt;
          {diaryList.map((item) =&gt; {
            return (
              &lt;DiaryItem
                key={item.id}
                {...item}
                onRemove={onRemove}
                onEdit={onEdit}
              /&gt;
            );
          })}
        &lt;/ul&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
}</code></pre>
<p>React Development tool로 확인해보면 아래와 같은 결과를 확인할 수 있습니다.</p>
<p> <img src="https://velog.velcdn.com/images/blueprint-12/post/2c6eb895-fa32-4374-8dd6-849deaeb63e2/image.JPG" alt=""></p>
<h4 id="export-default-span-stylecolor--redvsspan-export-">export default <span style="color : red">vs</span> export ?</h4>
<ul>
<li>export default 와 export의 차이점은 export default는 해당 파일에서 하나만 지정이 가능하며 별칭을 통해 다른 곳에서도 별칭을 지정하여 사용이 가능합니다. export로 내보내질 경우 {내보내는 애} 와 같은 형식으로 비구조화 할당으로 뽑아와 사용하며 별칭을 지정하여 사용할 수 없습니다. 대신 export로 내보내는 것에 개수 제한이 없습니다. </li>
</ul>
<p>우리가 props로 넘겨준 것들 중 이제 값이 아닌 함수들이 남았는데요. 얘네는 또 다른 context를 만들어줘야 합니다.
이유는 DiaryStateContext.Provider도 컴포넌트이기 때문에 value로 넘겨주는 값을 여러 개 넣어주게 되면 이 value값이 변할때마다 해당 Provider도 렌더링이 일어나고 하위 컴포넌트들도 리렌더링이 일어나게 되기 때문입니다.</p>
<p><i>App.js</i></p>
<pre><code class="language-javascript">export const DiaryDispatchContext = React.createContext();
</code></pre>
<p>useMemo를 사용하여 렌더링이 일어나지 않도록 만들어둔 함수명들을 하나로 묶어 줍니다. 새로운 context에 value값으로 넘겨줘야 하기 때문이죵(비구조화 할당을 통해 뽑아쓸거니까 {함수명, 함수명, 함수명}) useMemo를 사용하지 않으면 애써 각각의 함수에 useCallback으로 해놨던 최적화가 의미없게 됩니다.</p>
<p><i>App.js</i></p>
<pre><code class="language-javascript">//useMemo를 사용하지않고 그냥 객체로 묶어주게되면 컴포넌트가 재생성될 때 같이 다시 만들어지게 된다.
  //그렇기 때문에 useMemo를 통해 묶어줘야 한다.
  const memoizedDispatches = useMemo(() =&gt; {
    return { onCreate, onRemove, onEdit };
  }, []);</code></pre>
<p>onCreate, onEdit, onRemove 함수를 하나로 묶어서 그 데이터를 value값으로 넘겨줍니다.
결론적으로 Provider가 중첩된 모습으로 아래와 같이 나타나게 됩니다. </p>
<pre><code class="language-javascript">return (
    &lt;DiaryStateContext.Provider value={data}&gt;
      &lt;DiaryDispatchContext.Provider value={memoizedDispatches}&gt;
        &lt;div className=&quot;App&quot;&gt;
          &lt;DiaryEditor /&gt;
          &lt;div&gt;전체 일기: {data.length}&lt;/div&gt;
          &lt;div&gt;기분 좋은 일기 개수 : {goodCount}&lt;/div&gt;
          &lt;div&gt;기분 안좋은 일기 개수: {badCount}&lt;/div&gt;
          &lt;div&gt;기분 좋은 일기 퍼센트: {goodRatio}%&lt;/div&gt;
          &lt;DiaryList /&gt;
        &lt;/div&gt;
      &lt;/DiaryDispatchContext.Provider&gt;
    &lt;/DiaryStateContext.Provider&gt;
  );
</code></pre>
<p>제대로 작동하는 지 확인하려면 리액트 디벨롭먼트 툴을 사용하면 됩니다. 
<img src="https://velog.velcdn.com/images/blueprint-12/post/d9b23c35-2e29-46f8-be57-0afc6f6d1c3d/image.JPG" alt=""></p>
<ul>
<li>해당 Provider에서 3개의 함수 onCreate, onEdit, onRemove가 value로 잘 내려가고 있네요.🤸‍♀️</li>
</ul>
<p>해당 값을 받아와 쓸 곳에서 Provider컴포가 제공하는 value를 가져와 봅시다. </p>
<p><i>src/components/DiaryEditor.js</i></p>
<pre><code class="language-javascript">import React, { useState, useRef, useEffect, useContext } from &quot;react&quot;;
import { DiaryDispatchContext } from &quot;./App&quot;;

const { onCreate } = useContext(DiaryDispatchContext);
</code></pre>
<p>onRemove와 onEdit을 prop으로 받던 DiaryItem에서도 context를 통해 value를 가져와봅시다.</p>
<p><i>src/components/DiaryItem.js</i></p>
<pre><code class="language-javascript">import React, { useState, useRef, useEffect, useContext } from &quot;react&quot;;
import { DiaryDispatchContext } from &quot;../App&quot;;

function DiaryItem({ id, author, contents, emotion, created_date }) {
  const { onRemove, onEdit } = useContext(DiaryDispatchContext);

  //코드 중략 
</code></pre>
<p>함수를 전달해주는 중간다리 역할을 하던 컴포넌트에서 prop으로 넘겨주는 부분을 지워주면 
프롭 드릴링없이 context를 통해 다이렉트로 state를 value로 받아 사용할 수 있었습니다!</p>
<blockquote>
<p>😎 context를 한 파일에서 생성할 때 개수 제한은 없지만 컴포넌트 간 공유해야하는 데이터가 많을 경우는 redux와 같은 상태관리 라이브러리를 사용하는 것이 더 나을 거 같습니다. </p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] 상태관리 로직 분리하기 with useReducer()]]></title>
            <link>https://velog.io/@blueprint-12/React-%EC%83%81%ED%83%9C%EA%B4%80%EB%A6%AC-%EB%A1%9C%EC%A7%81-%EB%B6%84%EB%A6%AC%ED%95%98%EA%B8%B0-with-useReducer</link>
            <guid>https://velog.io/@blueprint-12/React-%EC%83%81%ED%83%9C%EA%B4%80%EB%A6%AC-%EB%A1%9C%EC%A7%81-%EB%B6%84%EB%A6%AC%ED%95%98%EA%B8%B0-with-useReducer</guid>
            <pubDate>Wed, 19 Oct 2022 11:06:20 GMT</pubDate>
            <description><![CDATA[<h2 id="usereducer">useReducer()</h2>
<p>:useReducer를 사용하면, 컴포넌트에서** 상태변화 로직을 분리** 시킬 수 있다!
useState처럼 컴포넌트의 상태를 관리해주는 React hooks
(useState를 대체할 수 있는 훌륭한 hook입니다.)
useState와 같이 비구조화 할당을 통해 사용합니다. </p>
<h3 id="문법">문법</h3>
<pre><code class="language-javascript">import React, {useReducer} from &quot;react&quot;;

// 중략
const [count, dispatch] = useReducer(reducer, 1);</code></pre>
<p>count는 state입니다. dispatch는 상태를 변화시키는 액션을 발생시키는 함수입니다.
우항에 useReducer를 호출할 때에는 꼭 인자로 reducer라는 함수를 전달해야 합니다.
dispatch는 상태변화를 일으키는 역할을 하는데 이 일어난 상태변화를 처리해주는 것이 reducer함수입니다. 2번째로 전달하는 인자는 count state의 초기값입니다. </p>
<p><i>ReducerEx.js</i></p>
<pre><code class="language-javascript">import React, { useReducer } from &quot;react&quot;;

const reducer = (state, action) =&gt; {
  switch (action.type) {
    case 1:
      return state + 1;
    case 10:
      return state + 10;
    case 100:
      return state + 100;
    case 1000:
      return state + 1000;
    default:
      //기존값 리턴
      return state;
  }
};

function ReducerEx() {
  const [count, dispatch] = useReducer(reducer, 1);

  return (
    &lt;div&gt;
      {count}
      &lt;button onClick={() =&gt; dispatch({ type: 1 })}&gt;add 1&lt;/button&gt;
      &lt;button onClick={() =&gt; dispatch({ type: 10 })}&gt;add 10&lt;/button&gt;
      &lt;button onClick={() =&gt; dispatch({ type: 100 })}&gt;add 100&lt;/button&gt;
      &lt;button onClick={() =&gt; dispatch({ type: 1000 })}&gt;add 1000&lt;/button&gt;
    &lt;/div&gt;
  );
}

export default DispatchEx;
</code></pre>
<p>예시 코드를 보면, dispatch에게 객체를 전달하고 있는데 이 객체를 <strong>action 객체</strong>라고 부릅니다. action이 곧 상태변화! 라고 생각하면 됩니다. </p>
<blockquote>
<p>action = 상태변화를 설명할 객체 //&quot;reducer야 너가 처리해야할 상태변화 이런 내용임 내가 들고옴~&quot;</p>
</blockquote>
<p>dispatch를 호출하면 상태변화가 일어나야 하고 그 상태변화에 대한 처리는 reducer에서 수행합니다. </p>
<p>reduce의 인자로는 state와 action이 있는 걸 확인했습니다. </p>
<ul>
<li>여기서 state란 <strong>현재 state</strong>를 가리키며 action은 <strong>dispatch를 통해 전달해준 액션 객체</strong>를 말합니다. </li>
</ul>
<p>reducer함수는 switch case문을 통해서 액션 타입에 따른 다른 state값을 return해주고 있습니다. return으로 반환하는 것은 새로운 state입니다. </p>
<blockquote>
<p><span style="color: blue"><strong>key point 👾</strong></span> dispatch 함수를 useCallback 내부에서 사용할 때, useCallback의 deps값을 신경쓰지 않아도 된다. reducer가 알아서 현재의 state값을 기억하고 참조하기 때문임</p>
</blockquote>
<p><i>App.js컴포넌트에서 분리된 상태로직 결과물</i></p>
<ul>
<li>useState() -&gt; useReducer() </li>
<li>setData() -&gt; dispatch()</li>
<li>reducer 함수는 따로 만들어 줘야 합니다. 보면 App컴포넌트의 내부가 아니라 외부에 작성한 이유이기도 합니다.(원래 useReducer 사용한 이유가 컴포넌트와 상태관리 로직을 분리하는 거였으니까^ㅅ^*)</li>
</ul>
<pre><code class="language-javascript">import React, {
  useRef,
  useEffect,
  useMemo,
  useCallback,
  useReducer,
} from &quot;react&quot;;
import &quot;./App.css&quot;;
import DiaryList from &quot;./components/DiaryList&quot;;
import DiaryEditor from &quot;./DiaryEditor&quot;;

const reducer = (state, action) =&gt; {
  switch (action.type) {
    case &quot;INIT&quot;: {
      return action.data;
    }
    case &quot;CREATE&quot;: {
      const created_date = new Date().getTime();
      const newItem = {
        ...action.data,
        created_date,
      };
      return [newItem, ...state];
    }
    case &quot;REMOVE&quot;: {
      return state.filter((item) =&gt; item.id !== action.targetId);
    }
    case &quot;EDIT&quot;: {
      return state.map((item) =&gt;
        //기존 state의 item의 아이디가 수정하려는 item의 아이디(targetId) 와 같으면
        //해당 객체의 기존 프로퍼티는 그대로 두고, 콘텐츠의 내용만 새로운 아이로 업데이트
        //주의점: contents가 뒤로 가게 전개 연산자와 같이 사용
        //수정하려는 item이 아니라면 원래 item 그대로 return
        item.id === action.targetId
          ? { ...item, contents: action.newContents }
          : item
      );
    }
    default:
      return state;
  }
};

function App() {
  //useReducer를 통해 상태관리를 할 것이라서 useState는 주석처리
  // const [data, setData] = useState([]);

  //useReducer의 2번째 인자는 항상 dispatch로
  const [data, dispatch] = useReducer(reducer, []);

  const getData = async () =&gt; {
    const res = await fetch(
      &quot;https://jsonplaceholder.typicode.com/comments&quot;
    ).then((res) =&gt; res.json());
    console.log(res);

    const initData = res.slice(0, 20).map((item) =&gt; {
      return {
        author: item.email,
        contents: item.body,
        emotion: Math.floor(Math.random() * 5) + 1,
        created_date: new Date().getTime(),
        id: dataId.current++,
      };
    });
    //action 객체에 타입을 적어주고 어떤 데이터로 state를 변경할지도 넘겨줘야 하기 때문에
    //setData로 전달해준 새로운 state를 data라는 이름으로 액션객체에 같이 실어줍니다.
    dispatch({ type: &quot;INIT&quot;, data: initData });
    // setData(initData);
  };
  useEffect(() =&gt; {
    getData();
  }, []);

  //useRef를 사용하여 고유 아이디를 만들 수 있다.
  const dataId = useRef(0);

  //리스트 아이템 추가 기능
  const onCreate = useCallback((author, contents, emotion) =&gt; {
    dispatch({
      type: &quot;CREATE&quot;,
      data: { author, contents, emotion, id: dataId.current },
    });

    dataId.current += 1;
  }, []);

  //리스트 아이템 삭제 기능
  const onRemove = useCallback((targetId) =&gt; {
    dispatch({ type: &quot;REMOVE&quot;, targetId });
  }, []);

  //리스트 아이템 수정 기능
  const onEdit = useCallback((targetId, newContents) =&gt; {
    dispatch({ type: &quot;EDIT&quot;, targetId, newContents });
  }, []);

  //리액트에서는 return을 가지고 있는 함수를 메모이제이션할 수 있다.
  //메모이제이션하고 싶은 함수를 useMemo를 통해 감싸주면 된다.
  //useMemo()의 첫번째 인자로 콜백함수를 받고 이 콜백함수가 리턴하는 값(연산)을 최적화할 수 있도록 도와준다.
  //useMemo(콜백함수, [deps])-&gt; 콜백함수를 유즈메모로 감싸주면 리턴해주는 아이는 더 이상 함수가 아니다.
  //그렇기 때문에 호출할 때에 ()는 없애줘야 한다. deps는 useEffect의 의존성 배열과 같은 역할을 한다.
  //해당 값에 어떤 값이 변했을 때에만 콜백 연산을 다시 수행할 지 알려준다.

  const getDiaryAnalysis = useMemo(() =&gt; {
    const goodCount = data.filter((item) =&gt; item.emotion &gt;= 3).length;
    const badCount = data.length - goodCount;
    const goodRatio = Math.floor((goodCount / data.length) * 100);
    return { goodCount, badCount, goodRatio };
  }, [data.length]);

  //useMemo를 함수에 씌워주었으니
  // const { goodCount, badCount, goodRatio } = getDiaryAnalysis();
  const { goodCount, badCount, goodRatio } = getDiaryAnalysis;

  return (
    &lt;div className=&quot;App&quot;&gt;
      &lt;DiaryEditor onCreate={onCreate} /&gt;
      &lt;div&gt;전체 일기: {data.length}&lt;/div&gt;
      &lt;div&gt;기분 좋은 일기 개수 : {goodCount}&lt;/div&gt;
      &lt;div&gt;기분 안좋은 일기 개수: {badCount}&lt;/div&gt;
      &lt;div&gt;기분 좋은 일기 퍼센트: {goodRatio}%&lt;/div&gt;
      &lt;DiaryList diaryList={data} onRemove={onRemove} onEdit={onEdit} /&gt;
    &lt;/div&gt;
  );
}

</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] Memoization ]]></title>
            <link>https://velog.io/@blueprint-12/React-Memoization</link>
            <guid>https://velog.io/@blueprint-12/React-Memoization</guid>
            <pubDate>Wed, 19 Oct 2022 08:57:32 GMT</pubDate>
            <description><![CDATA[<h1 id="성능-최적화">성능 최적화</h1>
<p>내부적으로 React는 UI를 최신화하기 위해 비용이 많이 드는 DOM작업의 수를 최소화하기 위해 몇 가지 방법을 활용한다. 많은 애플리케이션에서 React를 사용하면 성능을 최적화하기 위해 많은 작업을 수행하지 않고도 빠른 사용자 인터페이스를 제공할 수 있다.</p>
<ol>
<li><p>프로덕트 빌드를 사용해라 (React Developer Tools for Chrome)</p>
<ul>
<li>개발 모드의 React 기반 사이트에 접속하면 아이콘의 배경이 빨간색으로 표시되는데 앱을 개발할 때에는 개발 모드, 사용자에게 배포할 때에는 프로덕션 모드를 사용해야 한다. </li>
<li>.production.min.js로 끝나는 React 파일만이 프로덕션 환경에 적합하다.</li>
</ul>
</li>
<li><p>재조정(Reconciliation)을 피해라
컴포넌트의 props이나 state가 변경되면 React는 새로 반환된 엘리먼트를 이전에 렌더링된 엘리먼트와 비교하여 실제 DOM 업데이트가 필요한 지 여부를 결정한다. 같지 않을 경우 React는 DOM을 업데이트합니다. </p>
</li>
</ol>
<h2 id="memoization">Memoization?</h2>
<ul>
<li>결과를 캐싱하고, 다음 작업에서 캐싱한 것을 재사용하는 비싼 작업의 속도를 높이는 JS기술</li>
<li>이전 값을 메모리에 저장해 동일한 계산의 반복을 제거해 빠른 처리를 가능하게 하는 기술</li>
<li>캐시에 초기 작업 결과를 저장하여 사용함으로써 최적화할 수 있다. </li>
</ul>
<p>React에서는 useMemo, useCallback, React.memo가 memoization을 기반으로 작동한다. 
우선 React.memo는 HOC(고차 컴포넌트) 이고, useCallback과 useMemo는 Hooks이다. </p>
<blockquote>
<p>HOC(High Order Componenet)? 고차 컴포넌트는 컴포넌트를 가져와 새 컴포넌트를 반환하는 함수이다. </p>
</blockquote>
<pre><code class="language-javascript">const 강력한컴포넌트 = HighOrderComponenet(컴포넌트); </code></pre>
<h2 id="reactmemo컴포넌트">React.memo(컴포넌트)</h2>
<p>: 컴포넌트를 메모이제이션하는 것 </p>
<ul>
<li>직접 컴포넌트를 감싸서 사용한다. 이전 값을 메모리에 저장해 동일한 계산의 반복을 제거한다. props가 변경될 때까지 현재 memoized된 값을 그대로 사용하여 리렌더링을 막는다. 
물론, 자기 자신의 state가 변하면 리렌더링이 일어난다. <pre><code class="language-javascript">const MyComponent = React.memo(function MyComponent(props) {
/* props를 사용하여 렌더링 */
});</code></pre>
Reeact.memo는 props를 비교할 때 얕은 비교를 진행하는데, 원시 값의 경우는 같은 값을 갖는지 확인하고 참조값은 같은 주소 값을 가지고 있는 지 확인한다. 결론적으로 memo는 props의 값이 원시값일 때만 유효하고 참조값일 때는 유효하지 않는다. 
리액트에서 객체를 비교할 때는 &quot;얕은 비교&quot;를하기 때문이다. <blockquote>
<p>얕은 비교? 객체 주소에 의한 비교, 객체의 내용물은 비교X (설령 값이 같을 지라도 주소가 다르면 다르다고 판단한다.)</p>
</blockquote>
</li>
</ul>
<p>props가 갖는 복잡한 객체에 대하여 얕은 비교만 수행하는 것이 기본동작이며 다른 비교 동작을 원한다면, 두 번째 인자로 별도의 비교함수를 제공해야 한다. </p>
<p>즉, 2번째 비교 함수를 직접만들어서 그 내부에 깊은 비교를 할 수 있도록 로직을 구현하면 렌더링 방지를 할 수 있다. </p>
<pre><code class="language-javascript">const CounterA = React.memo(({ count }) =&gt; {
  useEffect(() =&gt; {
    console.log(`counter A :: count ${count}`);
  });
  return &lt;div&gt;{count}&lt;/div&gt;;
});

const CounterB = ({ obj }) =&gt; {
  useEffect(() =&gt; {
    console.log(`counter B :: obj ${obj.count}`);
  });
  return &lt;div&gt;{obj.count}&lt;/div&gt;;
};

const areEqual = (prevProp, nextProp) =&gt; {
  //이전 prop 값과 지금 prop값을 비교해서 같다면 true를 반환-&gt; 렌더링X
  // 다르다면 false 반환 -&gt; 렌더링 O
  //   if (prevProp.obj.count === nextProp.obj.count) {
  //     return true;
  //   } else {
  //     return false;
  //   }
  //위의 코드는 어차피 boolean 값을 반환하면되니까 아래와 같이 조건식 자체의 결과를 리턴해주면 된다.
  return prevProp.obj.count === nextProp.obj.count;
};

const MemoizedCounterB = React.memo(CounterB, areEqual);

function OptimizeTest() {
  const [count, setCount] = useState(1);
  const [obj, setObj] = useState({ count: 1 });

  return (
    &lt;div style={{ padding: 50 }}&gt;
      &lt;div&gt;
        Counter A
        &lt;button
          onClick={() =&gt; {
            setCount(count);
          }}
        &gt;
          버튼A
        &lt;/button&gt;
        &lt;CounterA count={count} /&gt;
      &lt;/div&gt;
      &lt;div&gt;
        counter B
        &lt;button
          onClick={() =&gt; {
            setObj({ count: obj.count });
          }}
        &gt;
          버튼B
        &lt;/button&gt;
        &lt;MemoizedCounterB obj={obj} /&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
}

export default OptimizeTest;

</code></pre>
<p>memo는 오직 성능 최적화를 위해서만 사용된다. 렌더링을 &#39;방지&#39;하기 위해서는 사용하면 안된다.</p>
<h2 id="usememo콜백함수-deps">useMemo(콜백함수, [deps])</h2>
<p>: 연산 결과를 메모이제이션하는 것</p>
<pre><code class="language-javascript">const memoizedValue = useMemo(() =&gt; computeExpensiveValue(a, b), [a, b]);</code></pre>
<p>useMemo()는 메모이제이션된 <strong>값</strong>을 반환한다. 
인자로는 2개를 받는다. 생성 함수와 그것의 의존성 값의 배열을 전달해야 한다. 
그렇게되면 useMemo는 의존성이 변경되었을 때만 메모이제이션된 값만 다시 계산한다. 
(배열이 없는 경우 매 렌더링 때마다 새 값을 계산하게 된다. deps에 값을 넣으셈)
-&gt; 이 최적화는 모든 렌더링 시의 고비용 계산을 방지해준다. </p>
<blockquote>
<p>👾 useMemo로 전달된 함수는 렌더링 중에 실행된다. 통상적으로 렌더링 중에는 하지 않는 것을 이 함수 내에서 하면 안된다. 
👾 의존성 값의 배열은 함수에 인자로 전달되진 않는다. 하지만, 함수 안에서 참조되는 모든 값은 의존성 값의 배열에 나타나야 한다. </p>
</blockquote>
<p>e.g.) side effects는 useEffect에서하는 일이지 useMemo에서 하는 일이 아니다. </p>
<pre><code class="language-javascript">
  //리액트에서는 return을 가지고 있는 함수를 메모이제이션할 수 있다.
  //메모이제이션하고 싶은 함수를 useMemo를 통해 감싸주면 된다.
  //useMemo()의 첫번째 인자로 콜백함수를 받고 이 콜백함수가 리턴하는 값(연산)을 최적화할 수 있도록 도와준다.
  //useMemo(콜백함수, [deps])-&gt; 콜백함수를 유즈메모로 감싸주면 리턴해주는 아이는 더 이상 함수가 아니다.
  //그렇기 때문에 호출할 때에 ()는 없애줘야 한다. deps는 useEffect의 의존성 배열과 같은 역할을 한다.
  //해당 값에 어떤 값이 변했을 때에만 콜백 연산을 다시 수행할 지 알려준다.
  const getDiaryAnalysis = useMemo(() =&gt; {
    console.log(&quot;일기 분석 시작&quot;);
    const goodCount = data.filter((item) =&gt; item.emotion &gt;= 3).length;
    const badCount = data.length - goodCount;
    const goodRatio = (goodCount / data.length) * 100;
    return { goodCount, badCount, goodRatio };
  }, [data.length]);

  //useMemo를 함수에 씌워주었으니 
  // const { goodCount, badCount, goodRatio } = getDiaryAnalysis();
  const { goodCount, badCount, goodRatio } = getDiaryAnalysis;</code></pre>
<p>위의 예시 코드에서 주의점은 getDiaryAnalysis는 함수였지만 useMemo로 콜백함수를 감싸준 뒤부터는 리턴 값이 함수가 아니라 값이기 때문에 ()로 호출하는 것이 아니라 그냥 변수로 대입해줘야 한다. 코드에서는 구조분해할당을 통해 useMemo의 결과값을 각각의 변수에 할당해주었다. </p>
<h2 id="usecallback">useCallback</h2>
<pre><code class="language-javascript">const memoizedCallback = useCallback(()=&gt;{
    dosomething(a,b);
}, [a,b])
</code></pre>
<p>메모이제이션된 콜백을 반환합니다. useMemo() Hook의 경우는 메모이제이션된 &quot;값&quot;을 반환했지만 useCallback() Hook은 메모이제이션된 &quot;콜백 함수&quot;를 반환합니다. </p>
<p>어떤 컴포넌트에 함수를 prop으로 내려주게 됐을 때 useCallback을 사용하면 불필요한 리렌더링을 방지할 수 있다. </p>
<p><i>App.js</i></p>
<pre><code class="language-javascript">//코드 중략....
 const onCreate = useCallback((author, contents, emotion) =&gt; {
    //현재 시간은 지역상수로 그냥 여기서 만들어줘도 됨
    const created_date = new Date().getTime();

    const newItem = {
      author,
      contents,
      emotion,
      created_date,
      id: dataId.current,
    };
    //다음 일기 아이디는 다른 아이디를 가져야 하니까 1추가해주는 로직
    dataId.current += 1;
    //최신 아이템이 위로 오게 만들어줘야 하기때문에 원래 데이터를 뒤에 전개연산자로 펼쳐주기
    setData([newItem, ...data]);
  }, []);

</code></pre>
<p>예를 들어, DiaryEditor 컴포넌트가 props로 onCreate 함수를 내려받는다고 했을 때 작성된 일기 리스트가 삭제됐을 때, 불필요하게 렌더링이 일어나고 있다. 이 때, onCreate함수가 있는 App.js에서 해당 함수에 useCallback을 적용하고 빈배열을 deps로 주게되면 처음 마운트 됐을 때만 한번 함수가 실행되고 그뒤로는 렌더링을 방지할 수 있다.</p>
<p>하지만, 이렇게되면 일기를 하나 새로 추가했을 때, 이전에 jsonplaceholder로 받아온 기본 20개의 리스트가 사라지고 새로 추가된 일기만 남게된다. 
이유: deps의 값이 빈배열이기 때문이다. 그렇다고 deps에 data를 넣게 되면 다시 일기를 수정하거나 삭제했을때도 diaryEditor 컴포넌트가 리렌더링되는 딜레마에 빠지게 된다.</p>
<p>이럴 때는 setData()의 인자로 새로운 값이 아닌 함수 자체를 넘기는 방식으로 업데이트하면 해결된다. </p>
<pre><code class="language-javascript"> const onCreate = useCallback((author, contents, emotion) =&gt; {
    //현재 시간은 지역상수로 그냥 여기서 만들어줘도 됨
    const created_date = new Date().getTime();

    const newItem = {
      author,
      contents,
      emotion,
      created_date,
      id: dataId.current,
    };
    //다음 일기 아이디는 다른 아이디를 가져야 하니까 1추가해주는 로직
    dataId.current += 1;
    //최신 아이템이 위로 오게 만들어줘야 하기때문에 원래 데이터를 뒤에 전개연산자로 펼쳐주기
    setData((data)=&gt;[newItem, ...data]);
  }, []);

</code></pre>
<p>위와 같이 기존 data를 인자로 받아서 새로운 객체를 리턴하는 콜백함수 자체를 setData의 인자로 넘겨주었더니 해결되었다. 사실상 이 방법은 setData(data+1); 를 setData((prev) =&gt; prev + 1)로 바꾸는 것과 동일해보인다. 후자의 방법이 이전 state값에 의존하고 있어 더 정확하다고 알고 있다. 이 방법은 함수형 업데이트라고 부른다. 
data 라는 콜백함수의 인자는 이전 state값의 스냅샷이기 때문에 deps가 빈배열이어도 이전 값을 알고 있어 문제를 방지해준다고 한다. </p>
<p>usecallBack의 가장 중요한 부분은 바로 setData()의 업데이트를 함수형 업데이트 방식으로 처리하는 것이다. 이전의 함수들도 아래와 같이 최적화할 수 있다. </p>
<pre><code class="language-javascript"> //리스트 아이템 삭제 기능
  const onRemove = useCallback((targetId) =&gt; {
    setData((data) =&gt; data.filter((item) =&gt; item.id !== targetId));
  }, []);

  //리스트 아이템 수정 기능
  const onEdit = useCallback((targetId, newContents) =&gt; {
    setData(
      //원래 데이터 배열의 item(일기) 객체의 아이디가
      //수정하려는 id와 동일하다면 그 객체의 일기요소를 전개 연산자로 풀어주고 contents의 값을 새로운 내용으로 덮어준다. 아니라면 원래 값유지
      (data) =&gt;
        data.map((item) =&gt;
          item.id === targetId ? { ...item, contents: newContents } : item
        )
    );
  }, []);
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] React에서 API 호출하기]]></title>
            <link>https://velog.io/@blueprint-12/React-React%EC%97%90%EC%84%9C-API-%ED%98%B8%EC%B6%9C%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@blueprint-12/React-React%EC%97%90%EC%84%9C-API-%ED%98%B8%EC%B6%9C%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sun, 16 Oct 2022 12:45:02 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>목표: useEffect를 이용하여 컴포넌트 Mount 시점에 API를 호출하고 해당 API의 결과값을 일기 데이터의 초기값으로 사용</p>
</blockquote>
<p>JSONPlaceholder를 사용할 것(데이터를 가져올 서버가 없기 때문에 open API를 활용할 거여용)</p>
<p>최상단 컴포넌트에서 데이터를 내려주고 있기 때문에 App.js에서 API를 통해 데이터를 세팅해주겠습니다. </p>
<p><i>src/App.js</i></p>
<pre><code class="language-javascript">import React, { useState, useRef, useEffect } from &quot;react&quot;;
import &quot;./App.css&quot;;
import DiaryList from &quot;./components/DiaryList&quot;;
import DiaryEditor from &quot;./DiaryEditor&quot;;

// https://jsonplaceholder.typicode.com/comments

function App() {
  //fetch를 통해 오픈API의 데이터를 가져와봅시다.
  const getData = async () =&gt; {
    const res = await fetch(
      &quot;https://jsonplaceholder.typicode.com/comments&quot;
    ).then((res) =&gt; res.json());
    console.log(res);

    //초기값으로 500개 중에 0부터 20개까지만 잘라내도록 하겠음
    const initData = res.slice(0, 20).map((item) =&gt; {
      return {
        author: item.email,
        contents: item.body,
        emotion: Math.floor(Math.random() * 5) + 1,
        created_date: new Date().getTime(),
        id: dataId.current++,
      };
    });
    //가공한 데이터를 가지고 일기 데이터의 초기값을 설정
    setData(initData);
  };
  useEffect(() =&gt; {
    getData();
  }, []);

    const [data, setData] = useState([]);

  //useRef를 사용하여 고유 아이디를 만들 수 있다.
  const dataId = useRef(0);


  //아래 코드는 중략 ...</code></pre>
<p><strong>getData 함수(비동기 처리 함수) 코드 설명</strong></p>
<ul>
<li>fetch메소드와 async await(ES8)을 통해 비동기 처리(API값을 가져오는 것) 그 이후의 작업은 받아온 데이터를 splice메소드를 통해 20개까지만 잘라내고 잘라낸 각각의 객체를 map함수를 통해 우리가 원하는 형태로 가공하여 return 합니다. <blockquote>
<p>fetch를 통해 가져온 데이터는 .json() 메소드를 통해 JSON형식으로 변환시켜줘야 합니다. </p>
</blockquote>
</li>
<li>emotion 값은 1 ~ 5 까지의 난수를 Math객체를 통해 구현가능합니다. </li>
<li>id로 들어가는 고유값은 이전에 useRef를 통해 만들어놓은 dataId 라는 변수의 current값을 1씩 증가시켜주는 방법으로 만들어줍니다. </li>
<li>완성된 객체가 담긴 initData를 setData함수의 인자로 넘겨주어 초기값을 세팅해줍니다. (useEffect의 콜백함수에서 getData()를 호출해주고 deps값을 아무것도 주지 않으면 컴포넌트가 최초 렌더링됐을 때만 실행됩니다.)</li>
</ul>
<p><strong>출처</strong>: 한입 크기로 잘라 먹는 리액트 (Udemy)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] React Lifecycle 제어하기]]></title>
            <link>https://velog.io/@blueprint-12/React-React-Lifecycle-%EC%A0%9C%EC%96%B4%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@blueprint-12/React-React-Lifecycle-%EC%A0%9C%EC%96%B4%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sun, 16 Oct 2022 11:29:22 GMT</pubDate>
            <description><![CDATA[<h2 id="lifecycle">Lifecycle?</h2>
<p>: 생명주기(생애주기)</p>
<p>간단히 하면 탄생/ 변화 / 죽음</p>
<ul>
<li>Mount(탄생) - 화면에 나타나는 것</li>
<li>Update(변화) - 업데이트, 리렌더</li>
<li>UnMount(죽음) - 화면에서 사라짐</li>
</ul>
<p>컴포넌트의 생명주기 동안에 우리가 각각 어떤 작업을 처리할 수 있는 것을 라이프사이클을 제어(이용)한다고 합니다. 
예를 들어, Mount시점에 초기화 작업을 할 수 있고 Update시점에 예외 처리 작업을, UnMount시점에 메모리 정리 작업을 하는 것을 말합니다. </p>
<h4 id="react-component-lifecycle-methods">React Component Lifecycle Methods</h4>
<blockquote>
<p>클래스형 컴포넌트에서만 제공합니다. state도 사실은 클래스형에서만 사용이 가능했으나 함수형 컴포넌트에서도 React 16.8 버전부터 Hooks를 통해 함수처럼 불러와서 사용할 수 있습니다. </p>
</blockquote>
<ul>
<li>ComponentDidMount</li>
<li>ComponentDidUpdate</li>
<li>ComponentWillUnmount</li>
</ul>
<p>Class형 컴포넌트의 길어지는 코드 길이 문제, 중복 코드 가독성 문제 등등을 해결하기 위해 등장한 것이 바로 <strong>리액트 Hooks</strong> 이다.</p>
<ul>
<li>useEffect</li>
<li>useState</li>
<li>useRef </li>
</ul>
<h3 id="useeffect를-통해-마운트-시점-업데이트-시점에-컴포넌트-제어하기">useEffect를 통해 마운트 시점, 업데이트 시점에 컴포넌트 제어하기</h3>
<p><i>src/Lifecycle.js</i></p>
<pre><code class="language-javascript">import React, { useState, useEffect } from &quot;react&quot;;

// 1.마운트 시점에 라이프사이클 제어하기
// 2. 업데이트 시점
// 3. 언마운트 시점

function Lifecycle() {
  const [count, setCount] = useState(0);
  const [text, setText] = useState(&quot;&quot;);

  //useEffect(콜백함수, [deps])
  //1.컴포넌트가 마운트됐을 때만 무언가를 처리하고 싶다면
  //의존성 배열을 []빈배열로 주고 내부에 사이드 이펙트 작업을 해주면 된다.

  useEffect(() =&gt; {
    console.log(&quot;마운트했당!&quot;);
  }, []);

  //2. 업데이트 시 무언가를 처리하고싶다면, 의존성 배열을
  //작성하지 않으면 된다.
  // 즉, useEffect의 dependency Array만 잘 활용하면
  // 우리가 감지하고 싶은 것만 컨트롤하여 쓸 수 있다.
  useEffect(() =&gt; {
    console.log(&quot;업데이트&quot;);
  });

  useEffect(() =&gt; {
    if (count &gt; 5) {
      alert(&quot;count가 5를 넘어갔습니다! 주의!!! count를 1로 초기화합니다.&quot;);
      setCount(1);
    }
  }, [count]);

  useEffect(() =&gt; {
    if (text.length === 0) {
      return;
    }
    console.log(`업데이트된 text state의 값은 ${text} 입니다.`);
  }, [text]);

  return (
    &lt;div style={{ padding: 20 }}&gt;
      &lt;div&gt;{count}&lt;/div&gt;
      &lt;button
        onClick={() =&gt; {
          setCount((prev) =&gt; prev + 1);
        }}
      &gt;
        +
      &lt;/button&gt;
      &lt;div&gt;
        &lt;input
          value={text}
          onChange={(e) =&gt; {
            setText(e.target.value);
          }}
        /&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
}

export default Lifecycle;
</code></pre>
<h3 id="unmount시-컴포넌트-제어하기">Unmount시 컴포넌트 제어하기</h3>
<p><i>src/LifecycleTwo.js</i></p>
<pre><code class="language-javascript">import React, { useEffect, useState } from &quot;react&quot;;

// 😎한 js파일에서 여러 컴포넌트를 만들어도 된다.
// 테스트용도이기 때문에 가독성은 고려하지 않고 UnmountTest컴포넌트를 만들어보겠습니다.

const UnmountTest = () =&gt; {
  return &lt;div&gt;unmount Testing Compo&lt;/div&gt;;
};

function LifecycleTwo() {
  const [isVisible, setIsVisible] = useState(false);
  const toggleVisible = setIsVisible(!isVisible);

  //단락회로 평가를 통해서 isVisible이 true 이면 뒤의 컴포넌트를 렌더링하고,
  //아니라면 렌더링하지 않는 것으로
  return (
    &lt;div&gt;
      &lt;button onClick={toggleVisible}&gt;ON/OFF&lt;/button&gt;
      {isVisible &amp;&amp; &lt;UnmountTest /&gt;}
    &lt;/div&gt;
  );
}

export default LifecycleTwo;</code></pre>
<p>위의 예시 코드에서 에러가 났는데, 에러 메세지는 아래와 같다.</p>
<blockquote>
<p>🔥 Too many re-renders. React limits the number of renders to prevent an infinite loop. 
해석: 리렌더링이 너무 많이 발생해용.. 제가 컨트롤하겠슴다. 
-리액트- </p>
</blockquote>
<pre><code class="language-javascript"> const toggleVisible = setIsVisible(!isVisible);</code></pre>
<p> <strong>원인</strong>: 해당 부분에서 함수 자체를 넘겨준 것이 아니라 할당값을 넘겨줬기 때문에 무한렌더링이 발생했다. </p>
<pre><code class="language-javascript"> const toggleVisible = () =&gt; setIsVisible(!isVisible);</code></pre>
<p><strong>에러해결</strong>: 화살표 함수로 변경하니 에러가 사라졌다. 실수하지 말기..!</p>
<p><i>다시 본론으로 돌아와 useEffect로 언마운트 시점에 컴포넌트를 제어해봅시다.</i></p>
<pre><code class="language-javascript">const UnmountTest = () =&gt; {
  useEffect(() =&gt; {
    console.log(&quot;마운트&quot;);
    return () =&gt; {
      //언마운트되는 시점에 발생하는 콜백함수를 리턴해주면 된다.
      console.log(&quot;언마운트&quot;);
    };
  }, []);

  return &lt;div&gt;unmount Testing Compo&lt;/div&gt;;
};</code></pre>
<p>위와 같이 UnmountTest 컴포넌트가 언마운트됐을 때 useEffect의 콜백함수에 return값으로 콜백함수를 주면 언마운트 시에 해당 함수를 실행시킬 수 있다.(unmount시에 라이프사이클을 통해 컴포넌트를 제어한 것이죠.)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[속보] 카카오 서버 터져서 티스토리 못씀 충격]]></title>
            <link>https://velog.io/@blueprint-12/%EC%86%8D%EB%B3%B4-%EC%B9%B4%EC%B9%B4%EC%98%A4-%EC%84%9C%EB%B2%84-%ED%84%B0%EC%A0%B8%EC%84%9C-%ED%8B%B0%EC%8A%A4%ED%86%A0%EB%A6%AC-%EB%AA%BB%EC%94%80-%EC%B6%A9%EA%B2%A9</link>
            <guid>https://velog.io/@blueprint-12/%EC%86%8D%EB%B3%B4-%EC%B9%B4%EC%B9%B4%EC%98%A4-%EC%84%9C%EB%B2%84-%ED%84%B0%EC%A0%B8%EC%84%9C-%ED%8B%B0%EC%8A%A4%ED%86%A0%EB%A6%AC-%EB%AA%BB%EC%94%80-%EC%B6%A9%EA%B2%A9</guid>
            <pubDate>Sat, 15 Oct 2022 12:12:11 GMT</pubDate>
            <description><![CDATA[<h3 id="하이-벨로그😎">하이 벨로그😎</h3>
<p>뭔가 .md같아서 신기하다. </p>
<p>코드는 어떻게 붙나 확인해봐야지</p>
<pre><code class="language-css">.DiaryEditor &gt; * {
  border-radius: 5px;
  border: #feca57;
}</code></pre>
<p>오우ㅋㅋ마크다운이랑 똑같은 문법이네 신기하다.
내 레포에 wiki에 작성하려다가 궁금해서 velog로 써보는건데 생각보다 매력적인 거 같다. 다른 글을 보니 타이틀마다 링크가 걸리던데 그건 또 어떻게 하는거지... notion이랑 velog 둘 다 매력적인데 notion은 아직 좀 어렵게 느껴진다. 자주 사용하다보면 늘겠지?</p>
<p>와 대박이네 벨로그 ..넘 예뿌당</p>
<pre><code class="language-javascript">function DiaryItem({ author, contents, emotion, created_date }) {
  //new Date()에 우리가 만들어놓은 밀리세컨드를 인자로 넘겨주면
  // Date객체를 생성해준다. 이 객체에 toLocaleString() 메소드를 쓰면 알아볼 수 있는 날짜로 변환시켜준다.

  return (
    &lt;div className=&quot;DiaryItem&quot;&gt;
      &lt;span&gt;
        작성자: {author} | 감정 점수:{emotion}
      &lt;/span&gt;
      &lt;br /&gt;
      &lt;span className=&quot;date&quot;&gt;
        작성 일자:{new Date(created_date).toLocaleString()}
      &lt;/span&gt;
      &lt;div&gt;내용 : {contents}&lt;/div&gt;
    &lt;/div&gt;
  );
}</code></pre>
]]></description>
        </item>
    </channel>
</rss>