이전부터 useCallback과 useMemo를 알고 있었지만 최근에 실제로 써보면서 다시 관련 내용을 찾아보는 나를 발견하고 블로그에 간단히 정리해보기로 했다.
참조 : https://medium.com/@jan.hesters/usecallback-vs-usememo-c23ad1dc60
===와 ==) const memoizedCallback = useCallback(
  () => {
    doSomething(a, b);
  },
  [a, b],
);
메모이제이션된 콜백을 반환한다. 메모이제이션된 콜백은 콜백의 의존성(dependency)이 변경되었을 때만 변경된다.
const memoizedValue = useMemo(() => computeExpensiveValue(a,b), [a,b]);
메모이제이션된 값을 반환한다. 의존성(dependency)가 변경되었을 때만 메모이제이션된 값을 다시 계산한다. 이를 통해 불필요한 연산을 피한다.
useMemo로 전달된 함수는 렌더링 중에 실행된다! side effect는useEffect에서 하는 일이고useMemo에서 하는 일이 아니다.
의존성 배열에 아무 것도 없으면 매 랜더링마다 새 값을 계산한다.
useCallback(fn, deps)은 useMemo(() => fn, deps)와 같다. 
함수를 다른 변수와 같이 다룰 수 있는 함수를 말한다.
const number = 1;
const greeting = 'hello';
function foo() {
  return 'bar';
}
// 변수에 할당한 함수
const otherFoo = function() {
  return 'bar';
}
// 변수에 할당한 함수(Arrow Function)
const anotherFoo = () => 'bar';
number;
greeting;
foo();
otherFoo();
anotherFoo();
함수를 인자로 전달 받거나 함수를 결과로 반환하는 함수.
다음의 실행 값이 무엇이 나올지 생각해보자
const identity = x => x;
identity(1);
identity(foo); 
identity(foo)();
결과
identity(1); // 1
identity(foo); // ƒ foo() {
  return 'bar';
  }
identity(foo)(); // 'bar'
identity(foo)가 함수를 return한 부분이 중요하다.
identity(foo)()는 return한 함수를 실행시킨 결과를 반환했다.
참조 평등은 두 객체에 대한 포인터가 동일하다는 것을 의미한다.
결과를 예상해보자
const anotherFoo = () => 'bar';
function sameFoo() {
  return 'bar';
}
const fooReference = foo;
'hello' === 'hello';
greeting === otherGreeting;
foo === foo;
foo === otherFoo;
foo === anotherFoo;
foo === sameFoo;
foo === fooReference;
결과
'hello' === 'hello'; // true
greeting === otherGreeting; //true
foo === foo; // true
foo === otherFoo; // false
foo === anotherFoo; // false
foo === sameFoo; // false
foo === fooReference; //true
여기서 눈 여겨봐야 하는 부분은 greeting === otherGreeting; //true와 foo === sameFoo; // false의 결과가 전혀 다른 것이다.
foo와 smaeFoo는 실제로 같지 않다. 즉, 포인터의 위치가 같지 않다. 
자, 위의 사전 지식을 열심히 습득했다면 이제 useCallback과 useMemo의 차이를 짐작할 수 있다!!!!
useCallback과 useMemo가 무엇을 반환하는지 생각해보자
useCallback(fn, deps)
useMemo(fn, deps)
결과
useCallback은 호출되지 않은 함수를 반환
useMemo호출한 함수의 결과를 반환
identity(foo); 와 identity(foo)();의 결과 차이를 다시 생각해보자!!
useCallback과 useMemo의 사용
function foo() {
  return 'bar';
}
CONST memoizedCallback = useCallback( foo, []);
CONST memoizedResult = useMemo( foo, []);
위를 바탕으로 다음의 실행 결과를 예상해보자
memoizedCallback;
memoizedResult;
memoizedCallback();
memoizedResult();
결과
memoizedCallback; 
// f foo() {
  return 'bar';
}
memoizedResult; // 'bar'
memoizedCallback(); // 'bar'
memoizedResult(); // TypeError
리액트에서 useCallback과 useMemo가 사용되는 모습
function MyComponent({foo, initial}) {
  const memoizedCallback = useCallback(() => {
    someFunc(foo, bar);
  }, [foo, bar]);
  
  const memoizedResult = useMemo(() => someOtherFunc(foo, bar), [foo, bar]);
  // 등등
}
useCallback은 deps를 사용하는 함수를 호출하는 inline callback을 사용한다. useMemo은 일부 함수를 호출하고 그 결과를 반환하는 "create function"을 사용한다. 왜 다음은 잘 못 되었을까?
function sum(a, b) {
  console.log('sum() ran');
  return a + b;
}
function App() {
  const [val1, setVal1] = useState(0);
  const [val2, setVal2] = useState(0);
  const [name, setName] = useState('Jim');
  const result = useCallback(sum(val1, val2), [val1, val2]);
  return (
    <div className="App">
      <input
        value={val1}
        onChange={({ target }) =>
          setVal1(parseInt(target.value || 0, 10))
        }
      />
      <input
        value={val2}
        onChange={({ target }) =>
          setVal2(parseInt(target.value || 0, 10))
        }
      />
      <input
        placeholder="Name"
        value={name}
        onChange={({ target }) => setName(target.value)}
      />
      <p>{result}</p>
    </div>
  );
}
힌트 : useCallback은 함수를 반환하지 값을 반환하지 않는다
답
: useCallback는 값의 메모이제이션에 사용할 수 없다. 즉. useCallback(fn(), [deps])로는 사용 불가
import React, { useEffect, useState } from 'react';
import ListItem from '@material-ui/core/ListItem';
import ListItemText from '@material-ui/core/ListItemText';
function User({ userId }) {
 const [user, setUser] = useState({ name: '', email: '' });
 const fetchUser = async () => {
   const res = await fetch(
     `https://jsonplaceholder.typicode.com/users/${userId}`
   );
   const newUser = await res.json();
   setUser(newUser);
 };
 useEffect(() => {
   fetchUser();
 }, []);
 return (
   <ListItem dense divider>
     <ListItemText primary={user.name} secondary={user.email} />
   </ListItem>
 );
}
export default User;
 해당 컴포넌트를 실행시킨다면 Infinity Loop를 확인 할 수 있다.
클로저 때문...!
const fetchUser = async () => {
 const res = await fetch(
   `https://jsonplaceholder.typicode.com/users/${userId}`
 );
 const newUser = await res.json();
 setUser(newUser); // 🔴 setState triggers re-render
};
useEffect(() => {
 fetchUser();
}, [fetchUser]); // fetchUser is a new function on every render
 setState가 계속 랜더링이 되도록 만든다.
그럼 해결책은 어떤 것이 있을까?
 : useEffect내로 fetchUser 함수를 이동시켜 클로저 문제를 해결
// 1. Way to solve the infinite loop
useEffect(() => {
 const fetchUser = async () => {
   const res = await fetch(
     `https://jsonplaceholder.typicode.com/users/${userId}`
   );
   const newUser = await res.json();
   setUser(newUser); // Triggers re-render, but ...
 };
 fetchUser();
}, [userId]); // ✅ ... userId stays the same.
 : userId가 바뀌면 fetchUser가 호출된다.  
// 2. Way to solve the infinite loop
const fetchUser = useCallback(async () => {
 const res = await fetch(
   `https://jsonplaceholder.typicode.com/users/${userId}`
 );
 const newUser = await res.json();
 setUser(newUser);
}, [userId]);
useEffect(() => {
 fetchUser();
}, [fetchUser]); // ✅ fetchUser stays the same between renders
기능에는 문제가 없지만 랜더링 될 때마다 실행되는 함수... 어떻게 할까?
// Some FP magic 🧙🏼♂️
const filter = (f, arr) => arr.filter(f);
const prop = key => obj => obj[key];
const getName = prop('name');
const strIncludes = query => str => str.includes(query);
const toLower = str => str.toLowerCase();
const pipe = (...fns) => x => fns.reduce((y, f) => f(y), x);
const nameIncludes = query =>
 pipe(
   getName,
   toLower,
   strIncludes(toLower(query))
 );
function UserList({ query, users }) {
 // 🔴 Recalculated on every render
 const filteredUsers = filter(nameIncludes(query), users);
 // ...
}
 : query와 users가 변경 되지 않으면 filteredUser 호출 없이 기존 값을 가져오도록!
function UserList({query, users}) {
  // ✅ Recalculated when query or users change
  const filteredUsesr = useMemo(
  () => filter(nameIncludes(query), users),
  [query, users]
  );
};