리액트 2주차

primav·2024년 11월 12일

멋사

목록 보기
24/29
post-thumbnail

2-1 리액트란?

React의 특징

  • SPA의 단점을 보완하는 몇가지 기술을 도입
  • SPA의 단점
    • 모든 기능을 한 페이지에서 다 구현하다 보니 상태(데이터) 관리가 어려움
    • 자바스크립트에서 HTML 코드를 생성해야 하므로 개발 생산성 저하
      브라우저의 DOM을 자주 갱신하다보면 성능 저하 발생
  • SPA의 단점을 보완하는 React의 특징
    • 컴포넌트 별로 상태 관리가 가능하고 전역 수준의 상태 관리를 지원하는 서드파티 라이브러리가 많음
    • JSX를 이용해서 HTML 생산성이 높음
    • 가상 DOM을 이용해서 성능 저하 최소

컴포넌트 기반 개발

  • 화면의 일부 UI를 만드는 컴포넌트 단위로 개발
  • 컴포넌트는 자바스크립트로 개발하며 재사용성이 뛰어남

상태 관리와 단방향 데이터 바인딩

  • 각각의 컴포넌트 내부에서 상태 관리 기능 제공
  • 전역 수준의 상태 관리를 위한 라이브러리(Context API, Redux, MobX, Recoil, Zustand 등)를 사용할 수 있음
  • 상태가 변경되면 뷰를 즉시 렌더링
  • 단방향 데이터 바인딩: state ➡️ view,
    view ➡️ Event Handler ➡️ setState() ➡️ state
    • View의 변경이 직접 State를 변경시키지 않고 Event Handler를 통해서만 변경 가능하게 구현해야 하므로, 상태가 변경되는 과정에 대한 예측과 추적이 용이하다.

가상 DOM (Virtual DOM)

  • 상태가 변경되어서 뷰를 렌더링할 때 브라우저 DOM에 바로 적용하지 않고 브라우저 DOM과 유사한 트리 구조의 가상 DOM을 수정한 후 수정 전의 가상 DOM수정 후의 가상 DOM을 비교해서 바뀐 부분만 브라우저 DOM에 실제 반영
  • DOM API를 이용한 화면 갱신 방법
    • 수정된 부분만 찾아서 갱신
      • 장점 - 화면 렌더링을 최소화해서 성능 굿
      • 단점 - 기존과 새로운 데이터를 비교해서 DOM에서 찾아 갱신해야하므로 코드가 복잡해진다.
    • 관련 영역 전체를 갱신
      • 장점 - 아예 덮어쓰는 것으로 코드가 비교할 필요 없어 간결해진다.
      • 단점 - 하나만 수정해서 갱신하면 되는데 전체를 다시 렌더링 하므로 성능 이슈 발생한다.
    • ⭐️ 가상 DOM 이용 ⭐️
      • 새 데이터로 만든 가상 DOM과 기존 DOM 비교해서 바뀐 부분만 찾아서 브라우저 DOM 갱신하므로 성능에 좋음
      • 바뀐 부분만 찾아서 리렌더링 하는 작업은 리액트가 담당하므로 코드가 간결해진다!

2-2 JSX

JSX란?

  • JSX(JavaScript XML)는 자바스크립트 파일 내에 HTML과 유사한 마크업을 작성할 수 있게 해주는 자바스크립트 확장 구문
  • 리액트와 JSX는 별개이기 때문에 리액트에서 JSX 사용이 필수는 아님
    (React.createElement() 직접 사용)

JSX 규칙

1. 단일 루트 요소를 반환해야 한다.

❌ 여러 객체 반환할 수 없음

return (
  <h1>Todo List</h1>
  <div>...<div>
);

⭕️ 루트 요소를 추가하여 단일 객체 반환

return (
  <div>
  	  <h1>Todo List</h1>
  	  <div>...<div>
  </div>
);

⭕️ Fragment 사용 - 렌더링에 영향 미치지 않음 <> </>로 약어 가능

return (
  <Fragment>
  	  <h1>Todo List</h1>
  	  <div>...<div>
  </Fragment>
);

2. 모든 태그는 닫는다.

3. 요소의 속성명은 카멜 표기법을 준수해야 한다.

ex) onclick -> onClick onkeyup -> onKeyUp

  • HTML에서 class 속성 추가
	<div id="todolist" class="todo"></div>
  • 자바스크립트에서 class 속성 추가
	document.querySelector('#todolist').className = 'todo'
  • JSX에서 class 속성 추가
	<div id="todolist" className="todo"></div>
  • JSX에서 class 속성을 동적으로 추가
	const todoClass = 'todo'
	<div id="todolist" className={ todoClass }></div>

4. 보간법 {} 사용할 때 표현식 사용해야 한다.

  • {} 안에는 변수 값, 메서드 리턴 값 등 만 사용 가능
  • if for 등은 사용할 수 없다.
  • if ➡️ 삼항 연산자
    (item.done ? <s>두부</s> : '두부')
  • for ➡️ forEach() , map()
{ // ❌ for문
  for(let i=0; i<itemList.length; i++){
    return item.title;
  }
}
 // ⭕️ map
{ itemList.map(item => item.title) }

5. 보간된 HTML 문자열은 인코딩 된다.

  • { } 내부의 값이 HTML 코드가 포함된 문자열인 경우 HTML 태그를 인코딩해서 처리하므로 브라우저에는 태그가 그대로 보여짐
  • XSS (Cross Site Scripting) 같은 공격에 대비하기 위한 규칙
const App(){
  const msg = '<i>World</i>';
  return <span>Hello { msg }</span>
}
만들어지는 문자열: <span>Hello &lt;i&gt;World&lt;/i&gt;</span>
예시 결과: Hello <i>World</i>

✔️ 해결 방법 (dangerouslySetInnerHTML) - HTML 태그 인코딩 X

const App(){
    // { msg }를 <span dangerouslySetInnerHTML=></span>로 변경
    const msg = '<i>World</i>';
    return <span>Hello <span dangerouslySetInnerHTML=></span></span>
  }

✔️ JSX는 XSS 공격에 안전하므로 JSX 사용

const App(){
    // const msg = '<i>World</i>';
    const msg = <i>World</i>;
    return <span>Hello { msg }</span>
  }

2-3 속성 (Props)

  • 상위 컴포넌트에서 하위 컴포넌트로 데이터를 전달할 때 사용

function App(){
  const title = 'React Props';
  let list = [
    { _id: 1, title: '리그오브 레전드', done: false},
    { _id: 2, title: '영화 보기(집에서)', done: false},
    { _id: 3, title: '던파', done: false},
  ];

  return (
    <div id="app">
      <div>
        <Title title={ title } />
        <TodoList list={ list } />
      </div>
    </div>
  );
}

function Title({ title='Default Title' }){
  return (
    <div>
      <h1>Simple Todo List - { title } :()</h1>
      <hr />
    </div>
  );
}

function TodoList({ list }){ // 구조 분해 할당
  const itemList = list.map(item => {
    return (
      <li key={ item._id }>{ item.title }</li>
    );
  });

  return (
    <ul className="todolist">
      { itemList }
    </ul>
  );
}    
  • 함수에 데이터를 전달할 때, 인수를 사용하듯이 컴포넌트에 데이터를 전달할 때 사용

  • 하위 컴포넌트에는 상위 컴포넌트가 전달한 여러 속성이 하나의 Props 객체로 전달되므로, 구조 분해 할당을 이용해서 필요한 속성 사용

  • 기본값 매개변수를 사용하면 Props가 전달되지 않거나 undefined가 명시적으로 전달될 때 적용

  • Props로 객체를 전달 받을 때 자식 컴포넌트가 그 값을 직접 변경하는 것은 지양

  • 자신이 전달받은 Props 전체를 하위 컴포넌트에 전달하고 싶을 때는 전개 연산자 사용

	function Profile(props) {
  		return (
    		<div>
     		 <Avatar { ...props } />
   		 </div>
 		 );
	 }

2-4 상태 (State)

  • 리액트에서는 시간이 지남에 따라 변하는 데이터를 상태라고 한다.
  • 상태가 변경되면 해당 컴포넌트와 하위 컴포넌트가 리렌더링 된다.

React.useState()

const [state, setState] = useState(initialState);
  • initialState - 상태의 초기값 (초기 렌더링 후 무시)
  • state - 저장된 상태값
  • setState - 상태값을 변경하는 setter 함수
    setter를 통해 상태가 변경되면 해당 컴포넌트는 다시 렌더링 됨

useState 특징

  • 컴포넌트가 렌더링 되는 동안에만 사용할 수 있는 특별한 함수 (훅)
  • 컴포넌트의 최상위 수준이나 커스텀 훅 내부에서만 사용 가능 (조건문, 반복문, 일반 함수 같은 {} 내부에서 사용불가)

상태 사용시 유의사항

상태의 불변성

  • 한번 정의한 상태는 그 값이 바뀌지 않도록 한다!
    • 새로운 상태로 바꿀 때 기존 값 수정x 새로운 상태값으로 교체'
    • 기본 데이터 타입 - 불변성 가짐
    • 참조형 데이터 타입 - 불변성 가지도록 복사해서 구현 (객체/배열)

배열의 불변성

➡️ 원본이 바뀌지 않고 새로운 배열이 나오는 메서드를 사용해야 한다.

피해야 할 메서드추천하는 메서드
추가push(), unshift()concat(), [...arr]
삭제pop(), shift()filter(), slice()
수정splice(), arr[i]map()
정렬reverse(), sort() 바로 사용x배열 복사 후 사용

중첩 객체 (복합 객체)

중첩 객체일 경우에는 불변성을 위해 수정될 속성을 포함한 객체와 그 객체를 포함하는 객체를 루트 객체까지 거슬러 올라가면서 전부 교체해야할 수 있다.

맨 끝단의 수정되는 객체가 있으면 윗단의, 윗단의, 윗단으로 거슬러 올라가 모든 부모를 교체해주어야 한다.

객체를 통째로 복제하면 시간과 비용이 너무 많이 들기 때문에 참조형인 것이다. (주소만 복사)
따라서 복합 객체를 수정하려면 맨 위의 부모만 바꾸는 것이 아니라 끝 단까지 가기 위한 부모를 모두 복제해야 한다.

✔️ 상태의 불변성 유지

const newAddressBook = user.extra.addressBook.map(address => {
  if(address.id === Number(e.target.name)){
    return { ...address, value: e.target.value };
  }else{
    return address;
  }
});

const newState = {
  ...user,
  extra: {
    ...user.extra,
    addressBook: newAddressBook
  }
};

setUser(newState);

이 때 각각의 부모 노드들을 전부 복제하는 것은 복잡한 작업이기 때문에 객체를 불변성으로 만들어주는 라이브러리가 있다.

npm install inner

✔️ immer 사용한 상태의 불변성 유지

const newState = produce(user, draft => { // user를 복사한 draft
  const address = draft.extra.addressBook.find(address => address.id === Number(e.target.name));
  address.value = e.target.value;
});

setUser(newState);

부모를 쫓아다니면서 복제한 객체들을 produce로 반환해준다.
교체가 필요한 부분만 복제를 해서 사용하고, 아닌 부분은 그대로 예전의 주소값을 참조한다.

0개의 댓글