[React] 리액트 기초

Im-possible·2025년 6월 2일

React

  • SPA를 개발하기 위해 리액트를 사용한다
  • React 는 컴포넌트 기반의 프론트엔드 라이브러리
  • React 컴포넌트 이름은 항상 대문자로 시작
  • export import를 통해 모듈로 사용
    모듈을 사용하려면 <script> 태그에 type="module" 속성 추가
  • 순수함수를 기반으로 만들어진다
    - 동일한 props를 전달하면 항상 결과가 동일해야 함
    • <StrictMode>로 순수함수인지 확인

가상 DOM

  • 상태 변경 시 뷰를 리렌더링할 때, 브라우저 DOM에 바로 적용하지 않고 가상 DOM을 먼저 수정
  • 수정 전후의 가상 DOM을 비고하여 바뀐 부분만 실제 브라우저 DOM에 반영 -> 성능 최적화

JSX

  • HTML과 유사한 마크업을 작성할 수 있게 해주는 자바스크립트 확장 구문
  • Balel, ESBuild 같은 변환 도구로 자바스크립트 코드로 변환됨
  • 단일 루트 요소를 반환해야한다 -> 두개 이상의 태그는 무조건 하나의 태그로 감싸져있어야 한다.
    - Fragment 사용 <Fragment>...</Fragment>
    • 약어 사용 <>...</>
<>
  <h1>제목</h1>
  <p>내용 입력</p>
</>
  • 모든 태그는 꼭 닫아야한다.
<>
  <input id="1" type="text" name="1"/>
  <br/>
</>
  • 요소의 속성명은 카멜 표기법을 준수
    - 예약어 충돌을 위한 속성명이 바뀐 경우
    - class -> className
    - for -> htmlFor
<>
  <button onClick={ handleUp } className="upBtn">+</button>
</>
  • 보간법 사용할 때 표현식 사용
    - {} 안에는 변수값, 메서드 리턴값 등 값만 사용 가능
    • if문, for문 등 사용 불가능
      -> if문 대신 삼항 연산자 사용, for문 대신 forEach(), map()등 사용
  • 보간법 안에 중괄호 { {} } -> 자바스크립트 객체(JSON 형태) 사용

props(속성)

  • 부모 컴포넌트에서 자식 컴포넌트로 데이터를 전달
  • props로 객체를 전달 받을 때는 주소가 전달되는데, 자식 컴포넌트가 그 값을 직접 변경하는 것은 권장 X
function App(){
  const list = [
    { _id: 1, title: 'React 공부', done: false},
    { _id: 2, title: 'Javascript 프로젝트', done: true},
    { _id: 3, title: 'Javascript 공부', done: true},
  ];

  return (
    <div id="app">
      <div>
        <Title />
        <Title titleName="타이틀" />
        <TodoList list={ list } />
      </div>
    </div>
  );
}

// App()에서 title이 없을 경우 기본값으로 'Todo List' 매개변수 사용 - 기본값 매개변수
function Title({ titleName = 'Todo List' }){
  return (
    <div>
      <h1>{ title }</h1>
      <hr />
    </div>
  );
}

// 함수에 데이터 전달
function TodoList({ list }){
  const itemList = list.map(item => {
    return (
      <li key={ item._id }>{ item.title } - { item.done ? '완료' : '진행중' }</li>
    );
  });

  return (
    <ul className="todolist">
      { itemList }
    </ul>
  )
}
  • 함수에 데이터를 전달할 때 인수를 사용하듯 컴포넌트에 데이터를 전달할 때 props 사용
<TodoList list={ list } />
  • 부모 컴포넌트가 전달한 여러 속성이 자식 컴포넌트에는 하나의 Props 객체로 전달되므로 주로 구조 분해 할당을 이용해서 필요한 속성을 바로 꺼내서 사용
function TodoList({ list }){
  ....
}
  • 자신이 전달받은 props 전체를 자식 컴포넌트에 전달하고 싶을 때는 전개 연산자 사용
function Profile(props) {
  return (
    <div>
      <Avatar { ...props } />
    </div>
  );
}
  • 기본값 매개변수를 사용하면 props가 전달되지 않거나 undefined가 명시적으로 전달될 때 적용
function Title({ titleName = 'Todo List' }){
  ....
}
  • 컴포넌트에 속성을 넣어서 전달
  • props의 타입은 interface로 지정
functuin App() {
  ....
  return (
    <div id="counter">
      <Button color="pink" onClick={handleDown}>-</Button>
      <Button type="button" color="lightgray" onClick={(event) => handleReset(event)}>0</Button>
      <Button type="button" color="skyblue" onClick={handleUp}>+</Button>
      <span>{ count }</span>
    </div>
  )
}

// props 타입 지정
interface ButtonProps {
  children: string;
  type?: 'button'| 'reset' | 'submit';
  color?: 'pink' | 'lightgray' | 'skyblue';
  onClick: (event: React.MouseEvent) => void;
}

// children으로 <Button ...> </Button> 사이의 내용 사용
function Button ({children, type="button", color, onClick: handleClick}: ButtonProps){
  return (
    <button 
      type={ type } 
      onClick={ handleClick } 
      style={{ backgroundColor: color }} 
      className="rounded-button"
    >{ children }</button>
  )
}

children

  • 부모 컴포넌트에서 호출하는 자식 컴포넌트 태그 사이에 전달할 데이터를 입력하는 방식
  • 컴포넌트의 여는 태그와 닫는 태그 사이의 내용 포함
function App() {
  return (
  	<Title>Hello</Title>
  )
}

function Title({ children }) {
  return (
    <p>{ children }</p>
  )
}

State

  • 시간이 지남에 따라 변하는 데이터
  • 상태가 변경되면 해당 컴포넌트와 자식 컴포넌트가 리렌더링 됨

useState()

  • 상태값(컴포넌트에서 관리하는 데이터)를 추가하기 위한 훅
  • 컴포넌트가 렌더링 되는 동안에만 사용할 수 있는 특별한 함수
  • 컴포넌트의 최상위 수준이나 커스텀 훅 내부에서만 사용 가능 (조건문, 반복문, 일반 함수 블럭 {} 내부에서 사용 불가능)
  • useState가 호출되는 순서대로 배열에 순서대로 저장되므로 리렌더링 될때에도 순서가 정확히 지켜져야 한다.
  • state로 만든 변수는 컴포넌트가 여러번 사용돼도 각각의 값을 따로 관리

매개변수

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

const handleChange = () => {
  setState(state + 1);
}

상태의 불변성

  • 한번 정의한 상태는 그 값이 바뀌지 않도록 한다
    -> 객체나 배열의 내부 속성을 직접 변경해도 참조 주소는 바뀌지 않기에 원본의 값이 바뀐다
    -> 새로운 상태로 바꿀 때 기존 상태값을 수정하지 않고 새로운 상태값으로 교체해야 리렌더링 발생
  • 배열의 불변성을 위한 메서드
    - 추가: concat(), [ ...arr ]
    • 삭제: filter(), slice()
    • 수정: map()
    • 정렬: 배열 복사 후 reverse(), sort()
immer 라이브러리
  • immer

  • 객체를 불변성으로 만들어주는 라이브러리

  • 설치

npm i immer
  • produce(원본 객체, 콜백 함수)
  • draft: 복사한 새로운 객체, draft의 값을 수정한다
const [ user, setUser ] = useState({
    .... // 중첩 객체
  });

function handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
  // user를 복사한 새로운 객체를 만들어서 콜백함수의 인자로 전달
  const newUser = produce(user, (draft)=>{
    const address = draft.extra.addressBook.find(address => address.id === Number(event.target.id))
    address!.value = event.target.value;
  });
  setUser(newUser);
}

0개의 댓글