Learn React 0장 / 빠른 시작

user profile img

신현호

React

React Docs

Post Thumbnail

목차

    0장 / 빠른 시작

    컴포넌트 생성 및 중첩하기

    React 앱은 컴포넌트로 만들어집니다. 컴포넌트는 고유한 로직과 모양을 가진 UI의 일부입니다.
    컴포넌트는 버튼만큼 작을 수도 있고 전체 페이지만큼 클 수도 있습니다.

    React 컴포넌트는 마크업을 반환하는 JS 함수입니다.

    jsx

    function MyButton() {
      return <button>I'm a button</button>
    }
    

    만들어진 컴포넌트는 다른 컴포넌트에 중첩하여 사용할 수도 있습니다.

    jsx

    export default function MyApp() {
      return (
        <div>
          <h1>Welcome to my app</h1>
          <MyButton />
        </div>
      )
    }
    

    <MyButtons /> 가 대문자로 시작하는 것에 주목하세요. 이게 React 컴포넌트라는 것을 알 수 있는 방법입니다.
    React 컴포넌트 이름은 항상 대문자로 시작해야하고 HTML 태그는 소문자로 시작해야 합니다.

    React 컴포넌트 네이밍 주의점

    React 컴포넌트는 항상 대문자로 시작해야하지만, 함수명 자체가 대문자일 필요는 없습니다.
    하지만, JSX 안에서 사용될 때는 반드시 대문자로 시작해야한다는 점을 유의하세요.

    JSX로 마크업 작성하기

    위에서 본 마크업 구문은 JSX라고 합니다. 선택 사항이지만 대부분 React 프로젝트는 편의성을 위해 JSX를 사용합니다.
    JSX는 HTML보다 더 엄격해서 항상 태그를 닫아야합니다. 또한 여러개의 JSX 태그를 반환할 수 없습니다.

    jsx

    function AboutPage() {
      return (
        <>
          <h1>About</h1>
          <p>
            Hello there.
            <br />
            How do you do?
          </p>
        </>
      )
    }
    

    스타일 추가하기

    React에서는 className으로 CSS 클래스를 지정합니다. HTML class 어트리뷰트와 같은 방식으로 작동합니다.

    jsx

    <img className="avatar" />
    

    그런 다음 별도의 CSS 파일에 해당 규칙을 작성합니다.

    css

    /* In your CSS */
    .avatar {
      border-radius: 50%;
    }
    

    React는 CSS 파일을 추가하는 방법을 규정하지 않습니다. 가장 간단한 경우 HTML에 <link> 태그를 추가하면 됩니다.

    React에서 CSS를 적용하는 다양한 방법

    현재 웹 애플리케이션에서는 스타일 구성 방식을 크게 CSS-in-JSCSS-in-CSS로 분류합니다.
    CSS-in-CSS는 CSS를 정의할 때 .css 혹은 .scss 등의 파일을 생성하여 이를 통해 CSS를 관리하는 것입니다.
    CSS-in-JS는 JS를 사용하여 CSS를 작성하고 관리하는 방법입니다.
    CSS-in-CSS의 경우는 대표적으로 SASS가, CSS-in-JS의 경우는 대표적으로 Styled-component가 있습니다.

    데이터 표시하기

    JSX를 사용하면 JS에 마크업을 넣을 수 있습니다. 중괄호를 사용하면 코드에서 일부 변수를 삽입하여 사용자에게 표시할 수 있도록 JS로 이스케이프할 수 있습니다.

    jsx

    return <h1>{user.name}</h1>
    

    JSX 속성에서 JS로 이스케이프 할 수도 있지만 이 경우에는 대신 중괄호를 사용해야 합니다.
    예를 들어, className="avatar""avatar" 을 CSS 클래스로 전달하지만 src={user.imageURL} 는 JS user.imageUrl 변수 값을 읽은 다음 해당 값을 전달합니다.

    jsx

    return <img className="avatar" src={user.imageUrl} />
    

    JSX 중괄호 안에 문자열 연결과 같이 더 복잡한 표현식을 넣을 수도 있습니다.

    위의 예에서 style={{}} 은 특별한 구문이 아니라 JSX 중괄호 안에 있는 일반 객체입니다.

    조건부 렌더링

    React에서는 조건을 작성하기 위한 특별한 문법이 없습니다. 대신 일반 JS 코드를 작성할 때 사용하는 것과 동일한 기법을 사용하면 됩니다.
    예를 들어, if문을 사용하여 조건부로 JSX를 포함할 수 있습니다.

    jsx

    let content
    if (isLoggedIn) {
      content = <AdminPanel />
    } else {
      content = <LoginForm />
    }
    return <div>{content}</div>
    

    보다 간결한 코드를 원한다면 조건부 ? 연산자를 사용할 수 있습니다. if와 달리 JSX 내부에서도 동작합니다.

    jsx

    <div>{isLoggedIn ? <AdminPanel /> : <LoginForm />}</div>
    

    else분기가 필요하지 않은 경우에는 더 짧은 논리 && 구문을 사용할 수도 있습니다.

    jsx

    <div>{isLoggedIn && <AdminPanel />}</div>
    

    목록 렌더링

    컴포넌트 목록을 렌더링하려면 for loop배열 map 함수와 같은 JS 기능을 사용해야합니다.

    예를 들어, 다음과 같은 상품 배열이 있다고 가정해 보겠습니다.

    js

    const products = [
      { title: 'Cabbage', id: 1 },
      { title: 'Garlic', id: 2 },
      { title: 'Apple', id: 3 },
    ]
    

    컴포넌트 내에서 map() 함수를 사용하여 상품 배열을 <li> 항목 배열로 변환합니다.

    jsx

    const listItems = products.map((product) => (
      <li key={product.id}>{product.title}</li>
    ))
    
    return <ul>{listItems}</ul>
    

    <li>key 속성이 있는 것에 주목하세요. 목록의 각 항목에 대해, 형제 항목 중에서 해당 항목을 고유하게 식별하는 문자열 또는 숫자를 전달해야 합니다.
    일반적으로 키는 데이터베이스 ID와 같은 데이터에서 가져와야 합니다. React는 나중에 항목을 삽입, 삭제 또는 재정렬할 때 어떤 일이 일어났는지 이해하기 위해 키를 사용합니다.

    이벤트에 응답하기

    컴포넌트 내부에 이벤트 핸들러 함수를 선언하여 이벤트에 응답할 수 있습니다.

    jsx

    function MyButton() {
      function handleClick() {
        alert('You clicked me!')
      }
    
      return <button onClick={handleClick}>Click me</button>
    }
    

    onClick={handleClick} 의 끝에 괄호가 없는 것을 주목하세요! 이벤트 핸들러 함수를 호출하지 마세요! 단지 전달만 하면 됩니다.
    React는 사용자가 버튼을 클릭할 때 이벤트 핸들러를 호출합니다.

    클릭 했을 때 실행이 두번 되는 현상

    index.js 혹은 App.js에서 <StrictMode> 가 선언되어있기 때문입니다.
    이는 production 환경에서는 두 번 실행되지 않으니 안심하세요.

    화면 업데이트하기

    컴포넌트가 특정 정보를 기억하여 표시하기를 원하는 경우가 종종 있습니다. 예를 들어 버튼이 클릭된 횟수를 카운트하고 싶을 때요.
    이렇게 하려면 컴포넌트에 state를 추가하면 됩니다.

    이를 사용하기 위해서는 먼저 React 에서 useState를 가져옵니다.

    jsx

    import { useState } from 'react'
    

    이제 컴포넌트 내부에 state 변수를 선언할 수 있습니다.

    jsx

    function MyButton() {
      const [count, setCount] = useState(0)
      // ...
    }
    

    useState를 통해서 우리는 두 가지를 얻을 수 있습니다. 바로 현재 state(count)이를 업데이트할 수 있는 함수(setCount) 입니다.
    어떤 이름을 붙여도 상관은 없지만 일반적으로는 [something, setSomething] 과 같이 호출하는게 일반적입니다.

    버튼이 처음 표시될 때는 useState() 에 0을 전달했기 때문에 count가 0이 됩니다. state를 변경하려면 setCount() 를 호출하고 새 값을 전달합니다.

    jsx

    function MyButton() {
      const [count, setCount] = useState(0)
    
      function handleClick() {
        setCount(count + 1)
      }
    
      return <button onClick={handleClick}>Clicked {count} times</button>
    }
    

    동일한 컴포넌트를 여러 번 렌더링하면 각각 고유한 state를 갖게 됩니다. 아래의 예제를 확인해보세요

    각 버튼이 고유한 state를 기억하고 다른 버튼에 영향을 주지 않는 방식에 주목하세요

    아래의 링크에서 setState의 작동 방식을 알아보는 것을 추천합니다.

    상태 업데이트를 잘 사용하는 방법

    useState의 호출로 받아오는 setSomething이라는 함수는 비동기적으로 작동합니다,
    그렇기때문에 단순 값을 인자로 넣어주기보단 callback을 활용하는 것이 더 우리의 의도대로 작동합니다.
    ex) setSomething((prev) => prev + 1)

    훅 사용하기

    React에서 use로 시작하는 함수를 훅(Hook) 이라고 합니다. useState는 React에서 제공하는 빌트인 훅입니다.
    다른 빌트인 훅들은 React API reference에서 찾을 수 있으며, 기존의 훅을 조합하여 자신만의 훅을 작성할 수도 있습니다.

    훅은 일반 함수보다 더 제한적입니다. 컴포넌트(또른 다른 훅)의 최상위 레벨에서만 훅을 호출할 수 있습니다.
    조건문이나 반복문에서 useState를 사용하고 싶다면, 대신 새로운 컴포넌트를 추출하고 그 컴포넌트에 작성하세요.

    컴포넌트 간 데이터 공유하기

    이전 예제에서는 각각의 MyButton에 독립적인 count가 있었고, 각 버튼을 클릭하면 클릭한 버튼의 count만 변경되었습니다.

    component data share 1

    하지만 데이터를 공유하고 항상 함께 업데이트하기 위한 컴포넌트가 필요한 경우가 많습니다.

    두 컴포넌트 모두 동일한 count를 표시하고 함께 업데이트하려면 개별 버튼에서 모든 버튼이 포함된 가장 가까운 컴포넌트로 state를 위쪽으로 이동해야 합니다.

    component data share 2

    이제 두 버튼 중 하나를 클릭하면 두 버튼 모두 변경됩니다. 이를 코드로 표현하는 방법은 다음과 같습니다.

    먼저, MyButton에서 MyApp으로 state를 위로 이동시킵니다.

    jsx

    export default function MyApp() {
      const [count, setCount] = useState(0)
    
      function handleClick() {
        setCount(count + 1)
      }
    
      return (
        <div>
          <h1>Counters that update separately</h1>
          <MyButton />
          <MyButton />
        </div>
      )
    }
    
    function MyButton() {
      // ... we're moving code from here ...
    }
    

    그런 다음 MyApp에서 MyButton으로 공유 클릭 핸들러와 함께 state를 전달합니다.

    jsx

    export default function MyApp() {
      const [count, setCount] = useState(0)
    
      function handleClick() {
        setCount(count + 1)
      }
    
      return (
        <div>
          <h1>Counters that update together</h1>
          <MyButton count={count} onClick={handleClick} />
          <MyButton count={count} onClick={handleClick} />
        </div>
      )
    }
    

    이렇게 전달한 정보를 props라고 합니다. 이제 MyApp 컴포넌트에는 count state와 handleClick 이벤트 핸들러가 포함되어 있으며, 이 두가지를 각 버튼에 props로 전달합니다.

    마지막으로, 부모 컴포넌트에서 전달한 props를 읽기 위해 MyButton을 변경합니다.

    jsx

    function MyButton({ count, onClick }) {
      return <button onClick={onClick}>Clicked {count} times</button>
    }
    

    버튼을 클릭하면 이벤트 핸들러가 실행됩니다. 각 버튼의 onClick prop은 MyApp 내부의 handleClick 함수로 설정되었으므로 그 안에 있는 코드가 실행됩니다.
    이 코드는 setCount(count + 1) 를 호출하여 count를 증가시킵니다. 새로운 count는 각 버튼에 props로 전달되므로 모든 버튼에 새로운 값이 표시됩니다.

    이를 state 끌어올리기라고 합니다. state를 끌어올리면 컴포넌트 간에 공유할 수 있습니다.

    Profile Image

    신현호

    Frontend Developer

    프론트엔드 개발자를 꿈꾸고 있는 대학생입니다. 끊임없이 배우고 성장하는 개발자가 되기 위해 노력하고 있습니다.

    React Docs

    총 5개의 포스트가 존재합니다.