프로그래머스 클라우딩 어플리케이션 엔지니어링 - TIL 27~30일차 React-간단한 일기장 프로젝트

석진·2024년 2월 8일
0
post-thumbnail

📌React에서 dom 조작하기

📌세부목표

  • 일기 저장 버튼을 클릭했을 때 작성자와 일기가 정상적으로 입력되었는지 확인하고 아니라면 focus 하기
const authorInput = useRef();
 // useRef 함수를 호출해서 반환값을 상수에 담아준다.
          <input
            ref={authorInput} // authorInput이라는 레퍼런스 객체를 통해서 input 태그에 접근 가능해진다.
            name="author"
            value={state.author}
            onChange={handleChangeState}
          />
          
   const handleSubmit = () => {
    if (state.author.length < 1) {
      authorInput.current.focus();

      return;
    }
          
          
  • 상수에는 React.MutableRefObject 라는게 저장이된다.

📌React에서 리스트 렌더링 하기

📌세부목표

배열을 이용하여 React 에서 List 렌더링 해보고 개별적인 컴포넌트 만들어보기

 <DiaryList diaryList={dummyList} />
 const DiaryList = ({ diaryList }) => {
  return (
    <div className="DiaryList">
      <h2>일기 리스트</h2>
      <h4>{diaryList.length} 개의 일기가 있습니다.</h4>
      <div>
        {diaryList.map((it) => (
          <DiaryItem key={it.id} {...it} />
        ))}
      </div>
    </div>
  );
};

📌리스트 데이터 추가하기

React는 단방향으로만 데이터가 흐른다

  • App component 최상단에서 state를 관리 각각의 컴포넌트에 props 으로 넘겨준다.

📌리스트 데이터 삭제하기

  // App.js component
  
 const onDelete = (targetId) => {
    console.log(`${targetId}가 삭제되었습니다`);
    const newDiaryList = data.filter((it) => it.id !== targetId);
    setData(newDiaryList);
  };
  
  
  
  // DiaryItem.js component
      <button
        onClick={() => {
          if (window.confirm(`${id} 번째 일기를 정말 삭제하시겠습니까?`)) {
            onDelete(id);
          }
        }}
      >
        삭제하기
      </button>
  

📌데이터 수정하기

배열을 이용한 React List에 아이템을 동적으로 수정 해보기 With 조건부 렌더링

//App.js
  const onEdit = (targetId, newContent) => {
    setData(
      data.map((it) => it.id === targetId ? {...it, content: newContent} : it) 
    )
  }
  
  
  //Diary Item 
   const handleEdit = () => {
    
    if(localContent.length < 5) {
      localContentInput.current.focus();
      return;
    }

    if(window.confirm(`${id}번 째 일기를 수정하시겠습니까?`)) {
      onEdit(id,localContent);
      toggleIsEdit();
    }
   

  };
     <button onClick={handleEdit}>수정 완료</button>
  

📌React Lifecycle 제어하기 - useEffect

React 컴포넌트의 생에 주기(생명 주기)
탄생 -> 변화 -> 죽음

  • 탄생 : 화면에 나타나는 것 Mount ex) 초기화 작업
  • 변화 : 업데이트(리렌더) Update ex) 예외 처리 작업
  • 죽음 : 화면에서 사라짐 UnMount ex) 메모리 정리 작업
useEffect(() => {
  // todo ... (callback) 함수
  
},[]); <- Dependency Array (의존성 배열) 이 배열 내에 들어 있는 값이 변화하면 콜백 함수가 수행된다.
 useEffect(() => {
   console.log("Mount!");

   return () => {
      // Unmount 시점에 실행되게 됨
    console.log('Unmount!');
   };
   

 }, []);

📌React API 호출하기

세부 목표

  • useEffect 이용하여 컴포넌트 Mount 시점에 API를 호출하고 해당 API의 결과값을 일기 데이터의 초기값으로 이용하기
const getData = async() => {
   const res = await fetch('https://jsonplaceholder.typicode.com/comments').then((res) => res.json());

   const initData = res.slice(0, 20).map((it) => {
     return {
       author: it.email,
       content: it.body,
       emotion: Math.floor(Math.random() * 5) + 1,
       created_date: new Date().getTime(),
       id: dataId.current++,
     };
   });
   
   setData(initData);
 };

 useEffect(() => {
   getData();    
 },[]) 

📌최적화

최적화 1 - 연산 결과 재사용

📌연산 결과값을 재사용 하는 방법

현재 일기 데이터를 분석하는 함수를 제작하고 해당 함수가 일기 데이터의 길이가 변화하지 않을 때 값을 다시 계산하지 않도록 하기

  • Memoization 이해하기

📌Memoization

  • 이미 계산 해 본 연산 결과를 기억 해 두었다가 동일한 계산을 시키면, 다시 연산하지 않고 기억 해 두었던 데이터를 반환 시키게 하는 방법

마치 시험을 볼 때, 이미 풀어본 문제를 다시 풀어보지 않아도 답을 알고 있는 것 과 유사

  • 답을 기억해 둔다 -> 기억해 두었던 답을 다시 적는다(Memoization 을 이용한 연산 과정 최적화)

📌사용법

 const getDiaryAnalysis = useMemo(() => {
    console.log("일기 분석 시작");

    const goodCount = data.filter((it) => it.emotion >= 3).length;
    const badCount = data.length - goodCount;
    const goodRatio = (goodCount / data.length) * 100;
    return { goodCount, badCount, goodRatio };
  }, [data.length]);

  const { goodCount, badCount, goodRatio } = getDiaryAnalysis;
  • useMemo로 어떤 함수를 감싸고 2번째로 디펜더시 array를 전달해서 함수를 최적화 하면 더이상 함수가 아니다.
  • useMemo 라는 기능은 어떤 함수를 전달을 받아서 콜백함수가 return 하는 값을 그냥 return 한다.
  • 그렇기 때문에 값으로 사용하여야 한다.

    정리하자면 어떤 함수가 있고 그 함수가 어떤 값을 return 하고 있는데 그 return까지의 연산을 최적화하고 싶다면 useMemo를 사용해서 디펜더시 array에 어떤 값이 변화할 때만 다시 수행할 것인지 명시해 주게 되면 함수를 값처럼 사용해서 연산 최적화를 할 수 있다.

📌React.memo

  • 사용법 1
const TextView = React.Memo(({ text }) => {
  useEffect(() => {
        
  })

  return <div>{text}</div>;
});

const CountView = React.memo(({ count }) => {
    useEffect(() => {
       
    })

  return <div>{count}</div>;
});

const OptimizeTest = () => {
  const [count, setCount] = useState(1);
  const [text, setText] = useState("");

  return (
    <div style={{ padding: 50 }}>
      <div>
        <h2>count</h2>
        <CountView count={count} />
        <button onClick={() => setCount(count + 1)}></button>
      </div>
      <div>
        <h2>text</h2>
        <TextView text={text} />
        <input value={text} onChange={(e) => setText(e.target.value)} />
      </div>
    </div>
  );
};
  • 사용법 2
const CounterA = React.memo(({ count }) => {
  return <div>{count}</div>;
});

const CounterB = React.memo(({ obj }) => {
  return <div>{obj.count}</div>;
});

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

CounterB는 업데이트가 되었다고 출력이 된다.
렌더링이 일어난 이유는, Props인 obj 가 객체이기 때문이다.

  • 자바스크립트에서는 기본적으로 얕은 비교를 하게 되어서 문제가 발생하는 것
  • 객체의 주소의 의한 비교(얕은 비교)
  • 얕은 비교: 객체의 값을 비교하는 게 아니라 두개의 객체가 같은 주소에 있느냐를 비교한다.
const areEqual = (prevProps, nextProps) => {
if(prevProps.obj.count === nextProps.obj.count) {
  return true;
}
 return false;
}

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

<MemoziedCounterB obj={obj} />

이렇게 해주면 CounterB 버튼을 눌러도 MemoziedCounterB component 가 다시 렌더링이 일어나지 않는다.

profile
내 서비스 만들기

0개의 댓글