React

4강 — 커진 앱 정리하기

슬라이드 보는 법

→ / Space다음으로
이전으로
↓ / ↑같은 주제 안에서 단계 이동
O전체 슬라이드 한눈에 보기
F전체화면

아래쪽 화살표가 보이면 그 주제는 단계별로 이어집니다

3강 복습

useState바뀌면 화면도 바뀌는 값
useEffect렌더링이 끝난 뒤 딴 일
끌어올리기공통 부모로 state를 올린다

오늘은 지금까지 만든 코드를 정리합니다

오늘 끝나면 할 수 있는 것

  • props가 길게 이어지는 문제를 푼다
  • DOM에 직접 접근한다
  • 불필요한 계산을 줄인다
  • 반복되는 로직을 훅으로 뽑는다
  • 폴더를 정리한다

오늘 배울 것

  • 18. props drilling
  • 19. Context
  • 20. useRef
  • 21. useMemo와 useCallback
  • 22. 커스텀 훅
  • 23. 폴더 구조

18. props drilling

props가 너무 멀리 간다

18. props drilling 문제

<App> user를 가지고 있다 └─ <Layout> user를 그냥 전달만 └─ <Header> user를 그냥 전달만 └─ <Avatar> 여기서 쓴다

가운데 컴포넌트들은 쓰지도 않으면서 받아 넘기기만 한다

18. props drilling 문제

          
            <Layout user={user}>
              <Header user={user}>
                <Avatar user={user} />
              </Header>
            </Layout>
          
        

19. Context

멀리 있는 컴포넌트에 값을 전달하려면

19. Context 1. 만들기

          src/UserContext.js
          
            import { createContext } from 'react';

            export const UserContext = createContext(null);
          
        

19. Context 2. 감싸기

          
            <UserContext.Provider value={user}>
              <Layout />
            </UserContext.Provider>
          
        

19. Context 3. 꺼내기

            
              import { useContext } from 'react';

              function Avatar() {
                const user = useContext(UserContext);
                return <img src={user.avatar} />;
              }
            
          

중간 컴포넌트는 아무것도 안 넘긴다

19. Context 주의

  • 전역 상태 도구가 아니다
  • 값이 바뀌면 쓰는 컴포넌트가 전부 다시 그려진다
  • 자주 바뀌지 않는 값에 쓴다 — 로그인 정보, 테마, 언어

20. useRef

DOM에 직접 닿거나 값을 기억하려면

20. useRef DOM 접근

            
              const inputRef = useRef(null);

              useEffect(() => {
                inputRef.current.focus();
              }, []);

              <input ref={inputRef} />
            
          

화면에 뜨자마자 커서가 들어간다

20. useRef 값 보관

            
              const timerRef = useRef(null);

              function start() {
                timerRef.current = setInterval(tick, 1000);
              }
              function stop() {
                clearInterval(timerRef.current);
              }
            
          

20. 정리

useState

바뀌면 다시 그린다

화면에 보이는 값
useRef

바뀌어도 안 그린다

화면과 무관한 값

21. useMemo와 useCallback

같은 계산을 다시 하지 않으려면

21. useMemo 문제

            
              function App({ items, keyword }) {
                // 렌더링될 때마다 전부 다시 거른다
                const result = items.filter(
                  (i) => i.name.includes(keyword)
                );
              }
            
          
items가 10만 개라면
매번 10만 번 검사한다

렌더링될 때마다 10만 번씩 걸러도 될까요?

21. useMemo 계산 결과

            
              const result = useMemo(
                () => items.filter(
                  (i) => i.name.includes(keyword)
                ),
                [items, keyword]
              );
            
          
items나 keyword가
바뀔 때만 다시 계산한다

21. useCallback 함수

          
            const handleClick = useCallback(() => {
              setCount((c) => c + 1);
            }, []);
          
        

21. 정리 남용 금지

  • 먼저 만들고, 느릴 때 넣는다
  • 미리 붙이면 코드만 복잡해지고 빨라지지 않는다
  • 느린지는 React DevTools Profiler로 확인한다

22. 커스텀 훅

반복되는 로직을 뽑아내려면

22. 커스텀 훅 문제

            
              function UserPage() {
                const [data, setData] = useState(null);
                const [loading, setLoading] = useState(true);
                useEffect(() => { /* fetch */ }, []);
              }

              function PostPage() {
                const [data, setData] = useState(null);   // 똑같다
                const [loading, setLoading] = useState(true);
                useEffect(() => { /* fetch */ }, []);
              }
            
          
같은 코드가 화면마다 반복된다

같은 코드가 화면마다 반복되면 어떻게 할까요?

22. 커스텀 훅 뽑아내기

          src/hooks/useFetch.js
          
            function useFetch(url) {
              const [data, setData] = useState(null);
              const [loading, setLoading] = useState(true);

              useEffect(() => {
                fetch(url)
                  .then((r) => r.json())
                  .then((d) => { setData(d); setLoading(false); });
              }, [url]);

              return { data, loading };
            }
          
        

22. 커스텀 훅 쓰기

            
              function UserPage() {
                const { data, loading } = useFetch("/api/users");

                if (loading) return <p>불러오는 중…</p>;
                return <UserList users={data} />;
              }
            
          
  • 홍길동
  • 윤경

22. 정리 훅 규칙

  • 이름은 반드시 use 로 시작
  • 훅은 컴포넌트나 훅의 맨 위에서만 부른다
  • 조건문 · 반복문 안에서 부르지 않는다

23. 폴더 구조

파일이 늘어나면

23. 폴더 구조

src/ ├─ components/ 재사용하는 조각 ├─ pages/ 화면 단위 ├─ hooks/ 커스텀 훅 ├─ contexts/ Context └─ utils/ 순수 함수

23. 정리

  • 처음부터 나누지 않는다. 불편해지면 나눈다
  • 한 파일에 컴포넌트 하나
  • 파일 이름은 컴포넌트 이름과 같게

정리

오늘 배운 것

한눈에

Context깊은 곳까지 값 전달. 자주 안 바뀌는 값에
useRefDOM 접근, 화면과 무관한 값 보관
useMemo계산 결과 기억
useCallback함수 기억
커스텀 훅반복 로직 추출. use로 시작
훅 규칙맨 위에서만. 조건문 안에서 금지

실습

할 일 목록 리팩터링

실습 과제

  1. 3강의 localStorage 로직을 커스텀 훅으로
  2. 테마(밝게/어둡게)를 Context로 만들기
  3. 입력창에 자동으로 커서 넣기 (useRef)

새로 만들지 말고 있는 코드를 옮깁니다

더 볼 것

5강에서는 실제로 내보냅니다