리액트 기초 및 실습 컴포넌트

김지영·2022년 6월 21일
0

React-study

목록 보기
1/5

리액트는?

  • 사용자 인터페이스를 구축하는 자바스크립트 라이브러리

  • 좀더 간단히 사용자 인터페이스를 구축할 수 있다.

  • 컴포넌트다!

    • 모든 사용자 인터페이스들은 컴포넌트로 구성되어있다.
    • 컴포넌트는 HTML + CSS + 자바스크립트 로직의 결합
    • 재사용이 가능하다.
    • 코드를 여러 파일로 분리하여 유지보수에 유리하다.
    • 컴포넌트와 이 컴포넌트들을 잘 결합해서 구축하는 것이 중요
  • HTML과 자바스크립트 그리고 CSS로 구성된 재사용 가능하고 반응하는 컴포넌트를 만들 수 있게 해준다.

  • 선언적 접근 방식 사용

  • 실제 웹페이지에서 어떤 요소가 추가되거나, 삭제되고, 업데이트되어야 하는지 해결하는 것

    • 자바스크립트에서처럼 직접 구체적인 DOM을 업데이트하는 지침들을 작성할 필요가 없다.
    • 최종 상태와 어떤 상황에서 어떤 상태가 사용되어야 하는지 정의한다.

리액트 시작하기

공식 github: create-react-app

node.js 설치

  • 브라우저 밖에서 자바스크립트 코드가 실행될 수 있도록 하는 자바스크립트 런타임
npx create-react-app my-app
cd my-app
npm start

리액트 프로젝트 파일을 받았을 때 구동방법

npm install
npm start

index.js

import ReactDOM from 'react-dom';

import './index.css';
import App from './App';

ReactDOM.render(<App />, document.getElementById('root'));
  • 프로젝트가 실행되었을 때 가장 먼저 실행되는 파일

  • react-dom이라는 서드 파티 라이브러리에서 ReactDOM을 임포트해서 index.js 파일의 내부 변수로 만든다.

  • ReactDOM은 render라는 메소드를 호출한다.

    • render 메소드는 두 개의 인수를 가지는데, 그 중 두번째 매개변수는 기본 자바스크립트인 DOM API라고 불리는 것을 가진다. 이것은 전역 문서 객체로 브라우저 사이트에 있는 자바스크립트에 자바스크립트를 조합하는 방식이다.
    • index.html안에 있는 root라는 id를 가지는 <div><App/>으로 대체된다.
    • <App/>은 JSX구문

App.js

function App() {
  return (
    <div>
      <h2>Let's get started!</h2>
    </div>
  );
}

export default App;
  • App이라는 함수를 갖고 있고, 이 함수를 내보낸다.

    • 자바스크립트에서 하나의 파일에 정의되어 있는 함수, 클래스 등을 다른 파일에서 사용하고 싶다면, 그것들을 내보내고 임포트 해야한다.
    • 내보낸 App 함수를 index.js에서 임포트
  • App 함수는 자바스크립트 안에 있는 Html 코드를 반환한다. (JSX 구문)

JSX

  • JSX = 자바스크립트 XML
  • JSX 구문을 입력하면 리액트가 화면 뒷단에서 브라우저에서 작동하는 코드로 변환을 해준다.
    • 개발자도구 > 소스 > static/js에서 우리가 작성한 코드 뿐만 아니라 변환을 위한 전체 리액트 패키지 코드(리액트 라이브러리 소스, 리액트 돔 라이브러리 소스 등..)가 있음을 볼 수 있다.

리액트 작동방식

  • 기존의 자바스크립트는 단락을 추가하고 글을 쓰려면 요소를 지정해서 textcontent를 입력하고, 위치에 찾아 넣어야하는 명령형 접근 방식을 따르고 있다.
  • 리액트에서는 div, h2 태그와 단락이 있는 최종상태를 정의하기만 하면(선언형 접근 방식) 리액트는 이런 것들을 화면에 불러오기 위한 지시사항들을 뒷단에 생성

실습: 사용자 지정 컴포넌트 만들기

  • components라는 폴더를 만들어서 컴포넌트 생성

  • 파일 이름은 첫번째 알파벳 대문자, 두단어 이상이 합해졌다면 단어들의 첫번째 알파벳 대문자 예) ExpensiveItem.js

  • 리액트에 있는 컴포넌트는 단지 자바스크립트 함수

  • 만든 함수를 내보내는 코드를 작성

  • function ExpenseItem() {
      return <h2>Expense item!</h2>
    }
    
    export default ExpenseItem;
  • App.js 파일에서 컴포넌트 임포트

  • import ExpenseItem from "./components/ExpenseItem";
    
    function App() {
      return (
        <div>
          <h2>Let's get started!</h2>
          <ExpenseItem></ExpenseItem>
        </div>
      );
    }
    
    export default App;
  • 리액트 요소 이름 규칙

    • 소문자로 시작하는 요소는 내장된 html요소이고, 대문자로 시작하는 요소는 개발자들에 의해 정의된 요소이다.
  • JSX 규칙

    • 반환하는 문장마다 반드시 한 개의 루트 요소를 가져야 한다.
  • function ExpenseItem() {
      return (
        <div>
          <div>March 28th 2021</div>
          <div>
            <h2>Car Insurance</h2>
            <div>$294.67</div>
          </div>
        </div>
      );
    }
    
    export default ExpenseItem;

기본 css 스타일 추가

  • css파일 작성 후 임포트
  • 여기서 class를 줄 때 className="" 이러한 형태로 입력해야한다.
    • html처럼 보이지만 실제 html이 아닌 리액트에서 만든 JSX구문이기 때문!

JSX에서 동적 데이터 출력 및 표현식 작업하기

  • 내용, 가격을 데이터를 받아서 입력된 값으로 동적으로 출력하고 싶다.

    • 자바스크립트 코드를 추가

    • const expenseDate = new Date(2021, 2, 28)
      const expenseTitle = 'Car Insurance'
      const expenseAmount = 294.67
    • 리액트 JSX에서 중괄호 {} 안에 일반적인 자바스크립트 코드를 작성할 수 있다.

    • date 객체는 string으로 변환하는 toISOString() 메소드 사용

    • function ExpenseItem() {
        const expenseDate = new Date(2021, 2, 28)
        const expenseTitle = 'Car Insurance'
        const expenseAmount = 294.67
      
        return (
          <div className='expense-item'>
            <div>{expenseDate.toISOString()}</div>
            <div className="expense-item__description">
              <h2>{expenseTitle}</h2>
              <div className="expense-item__price">${expenseAmount}</div>
            </div>
          </div>
        );
      }

props를 통해 데이터 전달하기

  • 일반 자바스크립트에서 함수를 재사용할 때 매개변수를 받아 재사용 할 수 있다.

  • 리액트에서 또한 매개변수를 사용하거나 props를 통해 재사용이 가능하다.

    • App 컴포넌트에서 바로 내부 컴포넌트의 결과값에 직접 접근하여 데이터를 보낼 수는 없다.
    • props를 이용하여 속성을 추가해서 사용자 지정 컴포넌트에 데이터를 전달한다.
    • 리액트에서는 사용자 지정 html을 가질 수 있고, 그것은 html 요소처럼 속성을 가질 수 있다.
    • 여기서 속성을 props라고 한다.
  • 현재 컴포넌트에 저장되어있는 값들이 컴포넌트가 아니라 App.js에 저장되어야한다.

  • 비용을 저장할 배열 생성

  • const expenses = [
        {
          id: 'e1',
          title: 'Toilet Paper',
          amount: 94.12,
          date: new Date(2020, 7, 14),
        },
        { id: 'e2', title: 'New TV', amount: 799.49, date: new Date(2021, 2, 12) },
        {
          id: 'e3',
          title: 'Car Insurance',
          amount: 294.67,
          date: new Date(2021, 2, 28),
        },
        {
          id: 'e4',
          title: 'New Desk (Wooden)',
          amount: 450,
          date: new Date(2021, 5, 12),
        },
      ];
  • ExpenseItem.js의 객체들은 데이터를 밖(App.js)에서 받아와야 한다.

  • 컴포넌트에 속성을 추가 (중괄호 구문 사용)

  • <ExpenseItem title={expenses[0].title} amount={expenses[0].amount} date={expenses[0].date}></ExpenseItem>
    <ExpenseItem title={expenses[1].title} amount={expenses[1].amount} date={expenses[1].date}></ExpenseItem>
    <ExpenseItem title={expenses[2].title} amount={expenses[2].amount} date={expenses[2].date}></ExpenseItem>
    <ExpenseItem title={expenses[3].title} amount={expenses[3].amount} date={expenses[3].date}></ExpenseItem>
  • 리액트는 모든 컴포넌트에서 한 개의 매개변수만을 사용하고, 그 매개변수는 프로퍼티로서 모든 속성을 받는 객체가 된다.

    • props객체에서 키와 밸류로 이루어진 파일 포맷을 얻는데 그것이 리액트에 의해 자동으로 전달된다.
  • function ExpenseItem(props) {
      return (
        <div className='expense-item'>
          <div>{props.date.toISOString()}</div>
          <div className="expense-item__description">
            <h2>{props.title}</h2>
            <div className="expense-item__price">${props.amount}</div>
          </div>
        </div>
      );
    }

컴포넌트에 JavaScript 논리 추가

  • date를 보기좋게 변경하기

    • date.props로 부터 month, year, day를 추출

    • toLocaleString() 메소드 활용 참고

    • function ExpenseItem(props) {
        const month = props.date.toLocaleString('en-US', { month: 'long' })
        const day = props.date.toLocaleString('en-US', { day: '2-digit' })
        const year = props.date.getFullYear()
      
        return (
          <div className='expense-item'>
            <div>
              <div>{month}</div>
              <div>{year}</div>
              <div>{day}</div>
            </div>
            <div className="expense-item__description">
              <h2>{props.title}</h2>
              <div className="expense-item__price">${props.amount}</div>
            </div>
          </div>
        );
      }

컴포넌트 분리

  • 현재의 ExpenseItem을 두가지 컴포넌트로 분리할 수 있다.

    • 이미 갖고있는 ExpenseItem 컴포넌트

    • import ExpenseDate from './ExpenseDate';
      import './ExpenseItem.css'
      
      function ExpenseItem(props) {
        return (
          <div className='expense-item'>
            <ExpenseDate date={props.date} />
            <div className="expense-item__description">
              <h2>{props.title}</h2>
              <div className="expense-item__price">${props.amount}</div>
            </div>
          </div>
        );
      }
      
      export default ExpenseItem;
    • date를 달력 형태로 화면에 렌더링하는 컴포넌트

    • function ExpenseDate(props) {
        const month = props.date.toLocaleString('en-US', { month: 'long' })
        const day = props.date.toLocaleString('en-US', { day: '2-digit' })
        const year = props.date.getFullYear()
        return (
          <div>
            <div>{month}</div>
            <div>{year}</div>
            <div>{day}</div>
          </div>
        )
      }
      
      export default ExpenseDate
  • 컴포넌트를 분리하면

    • 코드가 짧아져서 유지보수와 관리가 좀더 수월하다.
    • 코드의 재사용성이 높아진다.
  • 정리

    • App 컴포넌트에서 ExpenseItem 컴포넌트를 사용하고 있고, ExpenseItem 컴포넌트 안에는 ExpenseDate 컴포넌트를 사용
    • props를 통해 여러 개의 컴포넌트를 통해 데이터를 보낸다.
      • App 컴포넌트에서 ExpenseItem으로 데이터를 전달
      • ExpenseItem에서 데이터 일부 출력
      • ExpenseItem에서 props로 ExpenseDate에 date 데이터 전달
    • 전달하는 컴포넌트는 생략할 수 없다.

컴포지션

  • 다른 컨텐츠 주변에서 셸 역할을 하는 컴포넌트를 생성하고 싶다면?

  • props를 통해 모든 것을 설정하는 컴포넌트가 아니라 컴포넌트의 열고 닫는 태그 사이에 있는 컨텐츠를 전달하고 싶을 수도 있다.

  • 현재 만든 페이지를 보면 ExpenseItem.js와 Expenses.js 파일의 <div>컨테이너가 공동으로 갖고 있는 스타일을 추출하여 별도의 컴포넌트를 만들 수 있다.

    • box-shadow, border-radius를 새로 만든 Card 컴포넌트로 옮긴다.

    • Card 컴포넌트가 ExpenseItem이나 Expenses 컨텐츠를 감싸는 셸같은 역할을 한다.

    • ExpenseItem의 <div>를 사용자 지정 Card 컴포넌트로 대체할 수 있다.

      • 그러나 사용자 지정 컴포넌트는 일종의 컨텐츠를 감싸는 래퍼로 사용할 수 없다.

      • 해결방법

        • Card에 props를 취하도록 하고, 모든 컴포넌트가 갖는 리액트에 내장된 특별한 props를 사용한다.

        • function Card(props) {
            return <div className="card">{props.childeren}</div>
          }
        • props.children은 예약어이고, props.children의 값은 사용자 지정 컴포넌트의 열고 닫는 태그 사이에 있는 컨텐츠를 가리킨다.

        • 이때 css가 깨지는 것을 알 수 있는데 Card가 사용자 지정 컴포넌트 이므로 className이라는 속성을 줄 수 없는 것이다.

        • 변수 Card의 클래스에 props.className을 추가

        • function Card(props) {
            const classes = 'card ' + props.className
            return <div className={classes}>{props.children}</div>
          }
        • Expenses에도 div대신 Card 컴포넌트 사용

      • 정리

        • css파일의 일부 중복되는 코드뿐만 아니라 Html, JSX 코드, div 태그를 별도의 래퍼 컴포넌트로 추출할 수 있다.
        • 더 복잡한 JSX 구조를 가진 모달창, 경고창 같은 코드를 작성할 때 이러한 방식을 사용하면 수많은 코드 중복을 피할 수 있게 해주고, 다른 컴포넌트를 깔끔하게 유지할 수 있다.
        • Card를 래퍼로 사용하고 일부 내장된 html 요소를 사용하며 ExpenseDate에 있는 모든 것들을 모아 ExpenseItem 컴포넌트를 합성하고, 이것을 다른 컴포넌트에서 사용할 수 있다.
        • props.children은 래퍼 컴포넌트를 생성하게 한다.

JSX

  • JSX 코드를 사용하지 않고 직접 react 메소드를 사용한다면 복잡한 코드를 작성해야 한다.

  • return React.createElement(
      'div',
      {},
      React.createElement('h2', {}, "Let's get started!"),
      React.createElement(Expenses, { item: expenses })
    )
  • 이전에는 import React from 'react'라는 구문을 꼭 넣어주어야 JSX구문을 html로 변환이 가능했지만, 최근에는 React를 임포트하지 않아도 내부에서 변환이 일어나기 때문에 임포트 하지 않아도 된다.

  • 하지만 JSX 코드를 사용할 때 리액트가 사실은 내부에서 사용되고 있다는 것을 강조하기 위해 넣어주도록 하자.

컴포넌트 파일 구성

  • components 폴더 안에 모든 컴포넌트 파일들을 넣는 것 보다 기능에 따라 분류

    • Expenses 폴더 안에 Expenses와 ExpenseData를 렌더링 하는 컴포넌트
    • UI 폴더 안에 Card 컴포넌트
  • 컴포넌트 폴더를 구분하면 파일을 잘 정리하고 컴포넌트를 체계적으로 유지할 수 있다.

대체 함수 문법(Syntax)

  • function 키워드를 이용하여 함수를 작성하는 대신 화살표 함수로 작성할 수 있다.

0개의 댓글