CodeLyst

[React] 인프런 강의 [따라하며 배우는 리액트] Section 3

#React@HalexH
thumb nail

[따라하며 배우는 리액트] Section 3

Section 3 : Todo앱 최적화

  • React Hooks

    • React Hooks는 class없이 state를 사용할 수 있는 기능 ⇒ functional component 사용
    • Hooks 사용 이유 : 코드가 간결해짐, 더 빠른 성능, 더 적은 기능제공으로 세부화
    • HOC(Higher Order Component : 컴포넌트를 인자로 받아서 새로운 리액트 컴포넌트를 리턴하는 함수)를 사용할 때 Wrapper가 너무 많아져서 가독성이 떨어지는 문제를 Hooks에서는 Custom Hooks를 통해 컴포넌트를 만들어서 처리
    • 리액트 생명주기를 위해 useEffect를 사용
    • state업데이트를 위해 useState를 사용
    // setName을 이용해서 state를 업데이트 가능 // 아래와 같은 방식으로 사용 const [name, setName] = useState("")
  • State VS Props

    • State와 Props의 공통점 : 데이터 전달
    • State : 해당 컴포넌트 내부에서 데이터를 전달
      • State는 변경 가능(mutable) ⇒ 변경 시에 re-rendering이 됨
    • Props : 상속하는 부모 컴포넌트로부터 자식 컴포넌트로 데이터를 전달
      • Props는 변경 불가능(immutable)

      • 데이터를 변하게 하려면 부모 컴포넌에서 state를 변경 ⇒ 자식 컴포넌트도 변경

        // Props의 구성요소로 todoData, setTodoData, ...를 부모 컴포넌트에서 가져옴 // 화살표함수의 형태로 선언 const Lists = React.memo(({ todoData, setTodoData, handleClick }) => { // ... } // export deafult Lists를 해서 Lists함수를 import로 부를 수 있게 설정 export default Lists // 혹은 한번에 선언, 기본함수의 형태로 선언 export default function Lists({ todoData, setTodoData, handleClick }) { // ... }
  • 컴포넌트화 (개인적인 의견)

    • 리액트의 코드는 최대한 짧고 간결하게 필요한 기능만을 담는 것이 중요
      • Dependency (Coupling) 낮추고 Inheritance (Cohesion) 올리기
    • 장점 : 가독성이 좋고, 유지보수가 쉬움
  • TailWindCSS

    • CSS 스타일링을 편하게 할 수 있는 CSS 프레임워크
    • 설치방법
    npm install -D tailwindcss postcss autoprefixer
    • 아래와 같이 tailwind.config.js 설정
    /** @type {import('tailwindcss').Config} */ module.exports = { content: [ "./src/**/*.{js,jsx,ts,tsx}" ], theme: { extend: {}, }, plugins: [], }
    • index.css 설정
    @tailwind base; @tailwind components; @tailwind utilities;
    • 장점

      • CSS 파일을 직접 다루지 않아도됨
      • 필요한 내용만을 담을 수 있음, 기존 CSS파일을 이용하면 불필요한 내용도 포함됨
      • 적응만 된다면 편함
    • 단점 (개인적)

      • 최적화를 위해선 결국 사용하면 안된다고 한다
    • 사용 방법

      <div className="flex items-center justify-center w-screen h-screen bg-blue-100"> <div className="w-full p-6 m-4 bg-white rounded shadow lg:w-3/4 lg:max-w-lg"> <div className="flex justify-between mb-3">
      • 공식문서에서 설치 방법 및 사용 방법 상세히 설명되어있음

      Installation - Tailwind CSS

  • Drag and Drop

    • 모듈 설치
    npm install react-beautiful-dnd --save
    • strictMode에서 지원안함
    • import 내용
    import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd' // DragDropContext : Drag and Drop을 활성화해야하는 부분을 감싸기 // Droppable : Drop 가능한 영역을 감싸기, <Draggable /> 포함 // Draggable : Drag 가능한 영역을 감싸기 // 아래와 같이 보통 DragDropContext 내부에 Droppable 내부에 Draggable 존재 <DragDropContext onDragEnd={handleEnd}> <Droppable droppableId="todo"> {(provided) => ( <div {...provided.droppableProps} ref={provided.innerRef}> {todoData.map((data, index) => ( <Draggable key={data.id} draggableId={data.id.toString()} index={index}> ....
    • provided object에는 스타일 지정 및 조회를 위한 속성이 포함되어 있음
    • 사용자가 요소를 드래그하는 경우 className 속성을 selected로 변경. 나중에 스타일을 적용하는데 사용 ⇒ snapshot.isDragging을 이용해서 스타일링 가능
    • 예시코드
    <Droppable droppableId="todo"> {(provided) => ( // provided가 전체를 감쌈 // droppable인 경우 div에 {...provided.droppableProps} ref={provided.innerRef}를 설정 <div {...provided.droppableProps} ref={provided.innerRef}> {todoData.map((data, index) => ( // map이기 때문에 key값 필요 <Draggable key={data.id} draggableId={data.id.toString()} index={index}> {(provided, snapshot) => ( // provided와 snapshot이 전체를 감쌈 // draggable인 경우 div에 key={id} {...provided.draggableProps} // ref={provided.innerRef} {...provided.dragHandleProps} 값이 필요 // Props로 List.js에 아래에 해당하는 값을 넘겨줌 <List handleClick={handleClick} key={data.id} id={data.id} title={data.title} completed={data.completed} todoData={todoData} setTodoData={setTodoData} provided={provided} snapshot={snapshot} /> )} </Draggable> ))} {provided.placeholder} </div> )} </Droppable> // provided.placeholder를 사용하면 드래그가 자연스러워짐
    • 드래그 앤 드랍한 순서 값을 저장하기
      • splice메소드 사용 : 배열의 기존 요소를 삭제 또는 교체하거나 새 요소를 추가하여 배열의 내용을 변경
    // <DragDropContext onDragEnd={handleEnd}> 에서 handleEnd를 사용 const handleEnd = (result) => { console.log(result) if (!result.destination) return; const newTodoData = [...todoData]; // 변경시키는 아이템을 배열에서 지워주기 // return 값으로 지워진 아이템을 잡아주기 const [reorderedItem] = newTodoData.splice(result.source.index, 1); // 원하는 자리에 reorderedItem을 insert 하기 newTodoData.splice(result.destination.index, 0, reorderedItem); setTodoData(newTodoData); localStorage.setItem('todoData', JSON.stringify(newTodoData)); }
  • 리액트 불변성 지키기

    • 원시타입 : Boolean, String, Number, null, undefined, Symbol ⇒ 불변성을 가지고 있음
      • 고정된 크기로 Call Stack메모리에 저장
      • 실제 데이터가 변수에 할당
    • 참조타입 : Object, Array ⇒ 불변성이 없음
      • 데이터 크기가 정해지지 않고 Call Stack메모리에 저장
      • 데이터의 값은 heap 메모리의 주소값을 할당
      • 프로그래밍 시 유의
      • 값을 바꿀 때 Call Stack의 주소값은 같은데 Heap 메모리 값이 바뀜 ⇒ 불변성 유지 X
    • 불변성을 지켜야 하는 이유
      • 참조 타입에서 객체나 배열의 값이 변할때 이전의 값을 참조하고 있는 다른 객체에서 오류 발생 가능
      • 리액트에서 화면을 업데이트할때 불변성을 지켜서 값을 이전값과 비교 후에 업데이트하기 때문
    • 불변성을 지키는 방법
      • 참조 타입에서 값을 바꿀 경우에 새로운 배열을 반환하는 메소드를 사용
      • spread operator([…array, 5]), map, filter, slice, reduce
      • 원본 데이터를 변경하는 메소드 : splice, push
  • React.memo

    • 컴포넌트 렌더링 최적화를 위해 사용하는 기능
    • 리액트의 가장 중요한 원리인 컴포넌트 별로 렌더링을 위해서 필요
    • 컴포넌트에 사용하게 되면 렌더링 시에 Props값들로 그 값이 자식 컴포넌트에 영향을 줄 수있는데 그 값이 기존 값과 같다면 (변경되지 않았다면) 렌더링하지 않고 변경시에만 자식 컴포넌트에 영향을 주게 하는 기능
    // 사용방법 : 원하는 컴포넌트를 React.memo로 감싸기 // 화살표함수에서 사용가능 const Lists = React.memo(({ todoData, setTodoData, handleClick }) => { // ... }
  • useCallback

    • 함수도 마찬가지로 자식 컴포넌트에 Props값들로 내렸을때 함수를 포함하는 컴포넌트가 렌더링될때마다 자식 컴포넌트도 함수가 새롭게 만들어지는 문제 발생
    • re-rendering ⇒ 함수 재생성 ⇒ 자식 컴포넌트 또한 re-rendering
    // 사용 방법 : useCallback 안에 콜백함수와 의존성 배열을 순서대로 넣기 // 함수 내에서 참조하는 state, props가 있다면 의존성 배열에 추가 ex) [todoData] const handleClick = useCallback((id) => { let newTodoData = todoData.filter(data => data.id !== id) console.log('newTodoData', newTodoData) setTodoData(newTodoData) localStorage.setItem('todoData', JSON.stringify(newTodoData)); }, [todoData] ); // useCallback으로 인해 todoData값이 변하지 않으면 함수가 재생성되지 않음
  • useMemo

    • 비용이 많이드는 함수 호출 결과값을 저장해서 다시 함수 호출시에 저장된 값을 반환
    // 사용방법 : useMemo로 감싸고 의존성 배열에 감싸진 함수에서 사용하는 값을 넣기 function Component({a, b}) { const result = useMemo(() => compute(a, b), [a, b]) return <div>{result}</div> }
  • React Developer Tools

    • 구글 확장프로그램
    • 화면에 이벤트를 띄우거나 작동을 할때마다 렌더링되는 컴포넌트를 확인 가능
    • 개발자 도구 → Components → 설정 → Highlight updates when components render에 체크
  • localStorage에 값 담기

    • 웹페이지 내에 값을 저장 가능
    • 새로고침해도 값이 유지
    • setItem으로 localStorage에 값을 저장
    const handleClick = useCallback((id) => { let newTodoData = todoData.filter(data => data.id !== id) console.log('newTodoData', newTodoData) setTodoData(newTodoData) // setTodoData로 newTodoData를 설정했을때 localStorage에도 저장 // 객체나 배열을 저장시에는 JSON.stringify를 이용해서 저장 localStorage.setItem('todoData', JSON.stringify(newTodoData)); }, [todoData]
    • getItem으로 localStorage에 저장된 값을 반환 후 사용
    // 값이 존재하면 값을 가져오고 값이 존재하지 않으면 값을 가져오지 않음 const initialTodoData = localStorage.getItem("todoData") ? JSON.parse(localStorage.getItem("todoData")) : []; // 반환된 값을 사용 const [todoData, setTodoData] = useState(initialTodoData)