리액트 기본 개념 정리 (2) – JSX / Props

JSX 정리 (HTML 차이점 정리)

처음에 리액트가 나왔을때는 위 이미지와 같이 사용하는 JSX가 없었다. 그래서 아래 이미지와 같이 사용했다.

이처럼 jsx 가 등장하고 나서, 코드가 훨씬 직관적이고 디자이너와의 협업이 쉬워졌다.

대신 html과는 조금 다른 점이 있다.

  1. class 선택자 부여시, class가 아닌 className=”” 을 써야함
  2. onclick 은 onClick=”” 으로 표기
  3. html은 마크업 언어, jsx는 엄밀히 말하면 js 코드(babel이 html로 변환함)
  4. jsx는 태그 내에서 {} 을 이용해서 함수 / 변수 / 비즈니스 로직을 삽입할 수 있다. (js 를 사용할 수 있다)
  5. return 으로 형제노드를 쓸 수 없다. 한가지 태그로 감싸줘야한다. (jsx fragment 혹은 <></>로 감싼다)

// app.jsx
import React from "react";
import "./app.css";

function App() {
  const name = "dylan";
  const list = ["🍏", "🍎", "🍐"];
  return (
    <React.Fragment>
      {/* 형제노드가 있을 경우, <></> 혹은 React.Fragment 사용 */}

      <h1 className="title" onClick="">
        Hello, World! {name}
      </h1>
      <h1>Dylan test!</h1>

      {/* name 이 있을경우, 출력 */}
      {name && <h1>if name is existed, exposed this {name}</h1>}

      {/* map 을 이용해서 array 아이템 출력*/}
      {list.map((item) => (
        <h1>{item}</h1>
      ))}
    </React.Fragment>
  );
}

export default App;

만약 jsx 코드가 헷갈린다면, 하기 사이트를 참고하자

https://transform.tools/html-to-jsx

Component 만들어보기

react 익스텐션을 설치했으면, /src/components/ 내에 test.jsx 를 만들고, rcc를 입력해보자. 자동으로 컴포넌트 class 를 상속받는 클래스가 생성된다. (클래스 이름은 대문자로 해야함)

그리고 app.jsx 로 가서, Test 클래스를 받아오자.

그냥 return Test 로 하는게 아니라 태그로 감싸야한다는 것 잊지 말자 🙂

import React, { Component } from "react";

class Test extends Component {
  state = {
    count: 0,
  };

  handleIncrement = () => {
    /**
     * state 오브젝트 안에 있는 count를 증가한 후 , state 업데이트
     * 리액트에 state가 업데이트 되었음을 알리기 위해 setState() 호출
     */

    this.setState({
      count: this.state.count + 1,
    });
  };

  handleDecrement = () => {
    const count = this.state.count - 1;
    this.setState({
      count: count < 0 ? 0 : count,
    });
  };
  render() {
    return (
      <li className="habit">
        <span className="habit-name">Reading</span>
        <span className="habit-count">{this.state.count}</span>
        <button
          className="habit-button habit-increase"
          onClick={this.handleIncrement}
        >
          <i className="fas fa-plus-square"></i>
        </button>
        <button
          className="habit-button habit-decrease"
          onClick={this.handleDecrement}
        >
          <i className="fas fa-minus-square"></i>
        </button>
        <button className="habit-button habit-delete">
          <i className="fas fa-trash"></i>
        </button>
      </li>
    );
  }
}

export default Test;
// app.jsx

import "./app.css";
import Test from "./components/test";

function App() {
  return <Test />;
}

export default App;

참고사항

일반적으로 jsx파일의 명명규칙은 따로 정의되어 있지 않으며, nextjs의 경우 app-jsx.js 와 같이 많이 사용하지만 react의 경우에는 파일 이름을 규정하지 않는다. (팀 혹은 회사에서 사용하는 컨벤션을 따르면 됨)

또 리액트 컴포넌트라면 .jsx 확장자를 사용하는게 추후 알아보기 쉽다. (ex. AppJSX.jsx)

또 일반적으로 컴포넌트의 경우 /src 아래에 components 라는 폴더내에 위치시키는게 좋다.


Props 란?

컴포넌트 밖에서 주어지는 데이터이다.

컴포넌트 안에서 자체적으로 데이터를 정의해서 사용하는 State와는 다르게, Props은 컴포넌트 외부에서 데이터를 제공받는다. 가장 근본적인 이유는 컴포넌트의 재사용을 높이기 위해서이다. 상황에 따라 주어진 데이터를 받아서 그 데이터에 맞게 UI를 보여주기 위해서 사용되어진다.

아래처럼 부모컴포넌트에서 이렇게 LikeButton 컴포넌트를 사용할때 title, onClick과 같은 아이들을 인자로 전달해 주면 이 아이들이 props 오브젝트로 묶여서 LikeButton 컴포넌트에 전달되어진다.

<LikeButton title={'Like'} onClick={this.handleClick} />

그래서 LikeButton안에서 this.props.titlethis.props.onClick으로 각각 전달된 ‘Like’ 와 ‘this.handleClick’ 함수에 접근 할 수가 있다.

class App extends Component {
    handleClick = (event) =>{
        console.log(event);
    };
    render() {
        return <LikeButton title="Like" onClick={this.handleClick}/>
    }
}

이렇게 App 부모 컴포넌트에서 LikeButton 컴포넌트에 title과 onClick을 인자로 전달해주면,

class LikeButton extends Component {
    state = {
        numberOfLikes: 0,
    };
    render() {
        console.log(this.props);
        console.log(this.state);
        return <button>{this.state.numberOfLikes}</button>
    }
}

전달된 인자들이 오브젝트로 묶어져서 LikeButton 컴포넌트 안에서 this.props로 할당되어진다.


props 예제

props는 컴포넌트로 전달해주는 하나의 인자로 보면 될듯하다.

// App.js (컴포넌트 호출 구간)
//import logo from "./logo.svg";
import "./App.css";
import * as React from "react";
import Profile from "./components/Profile";

function AppProfile() {
  return (
    <>
      <Avatar
        image="https://images.unsplash.com/photo-1527980965255-d3b416303d12?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1180&q=80"
        isNew={true}
      />
      <Profile
        image="https://images.unsplash.com/photo-1527980965255-d3b416303d12?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1180&q=80" 
        name="James Kim"
        title="frontend dev"
      /> // 인자 전달
      <Profile
        image="https://images.unsplash.com/photo-1580489944761-15a19d654956?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=922&q=80"
        name="Anna Young"
        title="frontend dev"
      />
      <Profile
        image="https://images.unsplash.com/photo-1633332755192-727a05c4013d?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1160&q=80"
        name="Bob Yu"
        title="backend dev"
      />
    </>
  );
}

export default AppProfile;
// Profile.jsx (컴포넌트)
import React from "react";

export default function Profile(props) {
  return (
    <div className="profile">
      <img className="photo" src={props.image} alt="avatar" />
      <h1>{props.name}</h1>
      <p>{props.title}</p>
    </div>
  );
}

재사용성이 좋아짐 🙂

자바스크립트 object deconstructing 을 이용해서 받아올 props 키를 명시해주면, 아래와 같이도 사용할 수 있다 (props 생략)

// Profile.jsx (컴포넌트)
import React from "react";

export default function Profile({image,name,title}) {
  return (
    <div className="profile">
      <img className="photo" src={image} alt="avatar" />
      <h1>{name}</h1>
      <p>{title}</p>
    </div>
  );
}