리액트 기본 문법(JSX)과 컴포넌트 소개

JSX 설명

JSX 문법 사용 O

JSX 문법 사용 O

JSX 문법 사용 X

JSX 문법 사용 X

두 코드를 비교했을 때, JSX 문법을 사용한 코드가 가독성이 훨씬 좋다.

 

JSX

공식 문서 링크 : https://ko.reactjs.org/docs/introducing-jsx.html 

 

JSX 소개 – React

A JavaScript library for building user interfaces

ko.reactjs.org

 

const element = <h1>Hello, world!</h1>;

위에 희한한 태그 문법은 문자열도, HTML도 아니다. JSX라 하며 JavaScript를 확장한 문법이다. 

JSX는 React “엘리먼트(element)” 를 생성한다.

 

JSX란?

더보기

React에서는 본질적으로 렌더링 로직이 UI 로직(이벤트가 처리되는 방식, 시간에 따라 state가 변하는 방식, 화면에 표시하기 위해 데이터가 준비되는 방식 등)과 연결된다는 사실을 받아들인다.

React는 별도의 파일에 마크업과 로직을 넣어 기술을 인위적으로 분리하는 대신, 둘 다 포함하는 “컴포넌트”라고 부르는 느슨하게 연결된 유닛으로 관심사를 분리한다. 이후 섹션에서 다시 컴포넌트로 돌아오겠지만, JS에 마크업을 넣는 게 익숙해지지 않는다면 이 이야기가 확신을 줄 것이다.

React는 JSX 사용이 필수가 아니지만, 대부분의 사람은 JavaScript 코드 안에서 UI 관련 작업을 할 때 시각적으로 더 도움이 된다고 생각한다. 또한 React가 더욱 도움이 되는 에러 및 경고 메시지를 표시할 수 있게 해준다.

 

JSX에 표현식 포함하기

아래 예시에서는 name이라는 변수를 선언한 후 중괄호로 감싸 JSX 안에 사용했다.

const name = 'Josh Perez';const element = <h1>Hello, {name}</h1>;
ReactDOM.render(
  element,
  document.getElementById('root')
);

JSX의 중괄호 안에는 유효한 모든 JavaScript 표현식을 넣을 수 있다.

ex) 2 + 2, user.firstName 또는 formatName(user) 등은 모두 유효한 JavaScript 표현식이다.

아래 예시에서는 JavaScript 함수 호출의 결과인 formatName(user)을 <h1> 엘리먼트에 포함했다.

function formatName(user) {
  return user.firstName + ' ' + user.lastName;
}

const user = {
  firstName: 'Harper',
  lastName: 'Perez'
};

const element = (
  <h1>
    Hello, {formatName(user)}!  </h1>
);

ReactDOM.render(
  element,
  document.getElementById('root')
);

 

JSX도 표현식이다

컴파일이 끝나면, JSX 표현식이 정규 JavaScript 함수 호출이 되고 JavaScript 객체로 인식된다.

즉, JSX를 if 구문 및 for loop 안에 사용하고, 변수에 할당하고, 인자로서 받아들이고, 함수로부터 반환할 수 있다.

function getGreeting(user) {
  if (user) {
    return <h1>Hello, {formatName(user)}!</h1>;  }
  return <h1>Hello, Stranger.</h1>;}

 

JSX 속성 정의

어트리뷰트에 따옴표를 이용해 문자열 리터럴을 정의할 수 있다.

const element = <a href="https://www.reactjs.org"> link </a>;

중괄호를 사용하여 어트리뷰트에 JavaScript 표현식을 삽입할 수도 있다.

const element = <img src={user.avatarUrl}></img>;
JSX는 HTML보다는 JavaScript에 가깝기 때문에, React DOM은 HTML 어트리뷰트 이름 대신 camelCase 프로퍼티 명명 규칙을 사용한다.
예를 들어, JSX에서 class는 className가 되고 tabindex는 tabIndex가 된다.

 

JSX로 자식 정의

태그가 비어있다면 XML처럼 /> 를 이용해 바로 닫아주어야 한다.

const element = <img src={user.avatarUrl} />;

JSX 태그는 자식을 포함할 수 있다.

const element = (
  <div>
    <h1>Hello!</h1>
    <h2>Good to see you here.</h2>
  </div>
);

 

조건부 렌더링

JSX 자체가 표현식이라고 앞서 설명을 했다.

const getGreetingMessage = (name) => {
    if (name === "plum") return `Hello, ${name}! Learn React`;
    else return `Welcome, ${name}! Learn React`;
  };
  
  return (
  	<a href="https://reactjs.org>{getGreetingMessage("plum")}</a>
  )

그렇다면 위의 코드를 아래 코드로 바꾸는 것은 가능할까?

  return (
  	<a href="https://reactjs.org>{
    	if (name === "plum") return `Hello, ${name}! Learn React`;
        else return `Welcome, ${name}! Learn React`;
        }
    </a>
  )

정답은 ❌이다.

왜냐하면 if문은 JavaScript를 사용하는 것 뿐이지 'JavaScript 표현식'은 아니기 때문이다.

for문도 마찬가지이다.

 

그렇다면 중괄호 안에서 조건문을 사용하고 싶다면 어떻게 해야 할까?

return() 문 내에서 if-else 구문을 삼항 연산자로 표현하면 된다!

  return (
  	<a href="https://reactjs.org>{
    	name === "plum"
        ?
          `Hello, ${name}! Learn React`
        :
          `Welcome, ${name}! Learn React`;
        }
    </a>
  )

삼항 연산자는 표현식이기 때문에 사용이 가능하다.

 

 

if문일 때만 무언가를 렌더링을 하고 싶을 때는 어떻게 해야 할까?

return() 문 내에서 if 구문을 논리 연산자로 표현하자!

return (
	<a href="https://reactjs.org">{
    	name === "plum"
        &&
        `Hello, ${name}! Learn React`
    }
    </a>
)

위의 코드는 name이 "plum"일 때, 문자열이 출력되는 코드이다.

만약 조건이 false라면 무시하고 건너 뛰게 된다.

 

falsy 표현식을 반환하면 && 뒤에 있는 표현식은 건너뛰지만 falsy 표현식이 반환된다는 것에 주의하자.

아래는 예시 코드이다

const count = 0;
<div>{count && <h1>Messages: {count}</h1>}</div>

count가 0으로 falsy이기 때문에 && 뒤에 있는 <h1> 은 렌더링이 되지 않는다.

하지만, falsy인 0이 반환되기 때문에 결과적으로는 0이 출력되는 것을 볼 수 있다.

이 때 falsy값을 반환받고 싶지 않다면 어떻게 해야할까?

falsy 앞에 !!을 붙여서 false로 만들게 되면 값을 반환받지 않는다.

 

falsy를 반환받았을 때, 값이 출력이 되지 않는 경우도 있다.

const memo = ""

{memo && `메모 : ${memo}`}

이 때 memo가 falsy이기 때문에 && 뒤는 무시가 되고 memo의 값이 반환된다.

이 때 memo는 빈 문자열이기 때문에 아무것도 출력되지 않는다.

만약 '메모 : ' 형식으로 출력을 하고 싶다면 어떻게 해야 할까?

const memo = ""

{memo !== null && `메모 : ${memo}`}

위의 코드처럼 조건을 바꾼다면 `메모 : ` 형식으로 출력할 수 있다.

 

 

조건부 렌더링 공식 문서 링크 : https://ko.reactjs.org/docs/conditional-rendering.html

 

조건부 렌더링 – React

A JavaScript library for building user interfaces

ko.reactjs.org

쭉 읽어 보는 것을 추천한다.

 

Component 기본 개념 및 실습

공식 문서 링크 : https://ko.reactjs.org/docs/components-and-props.html

 

Components와 Props

컴포넌트를 통해 UI를 재사용 가능한 개별적인 여러 조각으로 나누고, 각 조각을 개별적으로 살펴볼 수 있다.

 

만약 App 함수가 return하는 React element가 지금보다 많았다면?

function App() {
  return (
    <div className="App" tabIndex="0">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload. Yahoo !!!
        </p>
        <p className="aaa" />
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
	Learn React
        </a>
      </header>
    </div>
  );
}

가독성이 낮아지고 코드를 이해하는 데에 시간이 많이 소요될 것이다.

➡ <header> 태그만 Header라는 컴포넌트로 분리할 수 있다.  * 항상 대문자로 시작해야 함

 

Header.js

import React from "react";
import logo from "../logo.svg";

export default function Header() {
  return (
    <header className="App-header">
      <img src={logo} className="App-logo" alt="logo" />
      <p>
        Edit <code>src/App.js</code> and save to reload. Yahoo !!!
      </p>
      <p className="aaa" />
      <a
        className="App-link"
        href="https://reactjs.org"
        target="_blank"
        rel="noopener noreferrer"
      >
        Learn React
      </a>
    </header>
  );
}

App.js

import "./App.css";
import Header from "./components/Header";

function App() {
  return (
    <div className="App">
      <Header />
    </div>
  );
}

export default App;

이처럼 Header를 컴포넌트화 해서 App.js에 컴포넌트로 추가할 수 있다.

 

함수 컴포넌트와 클래스 컴포넌트

컴포넌트를 정의하는 가장 간단한 방법은 JavaScript 함수를 작성하는 것이다.

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

 

또한 ES6 class를 사용하여 컴포넌트를 정의할 수 있습니다.

class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

class를 이용하여 컴포넌트를 작성할 때는 render() 함수 안에서 return을 해야 한다.

 

둘 중에서 함수 컴포넌트를 더 많이 사용한다.

 

 

컴포넌트 렌더링

이전까지는 DOM 태그만을 이용해 React 엘리먼트를 나타냈다.

const element = <div />;

React 엘리먼트는 사용자 정의 컴포넌트로도 나타낼 수 있습니다.

const element = <Welcome name="Sara" />;

 

Props 기본 개념 및 실습

Header 컴포넌트를 여러 개의 페이지에서 보여주어야 하는 상황을 상상해보자.

Header 컴포넌트를 잘 만들어놓으면 여기저기에서 사용할 수 있다.

 

만약 각 페이지마다, Header 안에 들어있는 텍스트를 다르게 보여주고 싶다면 어떻게 해야 할까?

➡ 각 Header를 따로 export 하면 된다!

 

Header.js

import React from "react";
import logo from "../logo.svg";

export default function Header() {
  return (
    <header className="App-header">
      <img src={logo} className="App-logo" alt="logo" />
      <p>
        Edit <code>src/App.js</code> and save to reload. Yahoo !!!
      </p>
      <p className="aaa" />
      <a
        className="App-link"
        href="https://reactjs.org"
        target="_blank"
        rel="noopener noreferrer"
      >
        Learn React
      </a>
    </header>
  );
}

export const HeaderA = () => {
  return (
    <header className="App-header">
      <img src={logo} className="App-logo" alt="logo" />
      <p>
        Edit <code>src/App.js</code> and save to reload. Yahoo !!!
      </p>
      <p className="aaa" />
      <a
        className="App-link"
        href="https://reactjs.org"
        target="_blank"
        rel="noopener noreferrer"
      >
        Learn React A
      </a>
    </header>
  );
};

export const HeaderB = () => {
  return (
    <header className="App-header">
      <img src={logo} className="App-logo" alt="logo" />
      <p>
        Edit <code>src/App.js</code> and save to reload. Yahoo !!!
      </p>
      <p className="aaa" />
      <a
        className="App-link"
        href="https://reactjs.org"
        target="_blank"
        rel="noopener noreferrer"
      >
        Learn React B
      </a>
    </header>
  );
};

export const HeaderC = () => {
  return (
    <header className="App-header">
      <img src={logo} className="App-logo" alt="logo" />
      <p>
        Edit <code>src/App.js</code> and save to reload. Yahoo !!!
      </p>
      <p className="aaa" />
      <a
        className="App-link"
        href="https://reactjs.org"
        target="_blank"
        rel="noopener noreferrer"
      >
        Learn React c
      </a>
    </header>
  );
};

App.js

import "./App.css";
import { HeaderA, HeaderB, HeaderC } from "./components/Header";

function App() {
  return (
    <div className="App">
      <HeaderA />
      <HeaderB />
      <HeaderC />
    </div>
  );
}

export default App;

 

다른 방식으로 Header.js 를 구성할 수 있다. 

Header 컴포넌트를 객체로 만드는 방법 (점 표기법이라고 부른다. 참고용)

Header.js

import React from "react";
import logo from "../logo.svg";

const Header = {
  A: function A() {
    return (
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload. Yahoo !!!
        </p>
        <p className="aaa" />
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React A
        </a>
      </header>
    );
  },

  B: function B() {
    return (
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload. Yahoo !!!
        </p>
        <p className="aaa" />
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React B
        </a>
      </header>
    );
  },
  C: function C() {
    return (
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload. Yahoo !!!
        </p>
        <p className="aaa" />
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React c
        </a>
      </header>
    );
  },
};

export default Header;

App.js

import "./App.css";
import Header from "./components/Header";

function App() {
  return (
    <div className="App">
      <Header.A />
      <Header.B />
      <Header.C />
    </div>
  );
}

export default App;

 

위와 같이 Header 컴포넌트를 따로 export 하는 것은 비효율적이다. 

상당 부분 코드가 겹치고, 바뀌는 부분은 일부분이기 때문이다.

➡ props를 활용하면 사용자 정의 컴포넌트에 추가적인 값을 전달할 수 있다!

 

Header.js

import React from "react";
import logo from "../logo.svg";

const Header = (props) => {
  return (
    <header className="App-header">
      <img src={logo} className="App-logo" alt="logo" />
      <p>
        Edit <code>src/App.js</code> and save to reload. Yahoo !!!
      </p>
      <p className="aaa" />
      <a
        className="App-link"
        href="https://reactjs.org"
        target="_blank"
        rel="noopener noreferrer"
      >
        {props.title}
      </a>
    </header>
  );
};

export default Header;

App.js

import "./App.css";
import Header from "./components/Header";

function App() {
  return (
    <div className="App">
      <Header title={"Learn React A"} />
      <Header title={"Learn React B"} />
      <Header title={"Learn React C"} />
    </div>
  );
}

export default App;

App.js 의 자식 컴포넌트가 Header.js이다.

이렇게 부모 컴포넌트가 자식 컴포넌트한테 값을 넘겨주고 싶다면 props를 이용하면 좋다.

 

props는 리액트 컴포넌트라면 기본적으로 가지고 있는 객체이다.

그래서 넘겨주는 값에 대해서 props.~~ 로 접근할 수 있다.

 

props는 읽기 전용이다.

함수 컴포넌트나 클래스 컴포넌트 모두 컴포넌트의 자체 props를 수정해서는 안 된다.

모든 React 컴포넌트는 자신의 props를 다룰 때 반드시 순수 함수처럼 동작해야 한다.

 

props.children

공식 문서 링크 : https://ko.reactjs.org/docs/glossary.html#propschildren 

 

React 기술 용어 모음 – React

A JavaScript library for building user interfaces

ko.reactjs.org

props.children

모든 컴포넌트에서 props.children을 사용할 수 있다. props.children은 컴포넌트의 여는 태그와 닫는 태그 사이의 내용을 포함한다.

예를 들어,

<Welcome>Hello world!</Welcome>

Hello world! 문자열은 Welcome 컴포넌트의 props.children으로 사용할 수 있다.

<Welcome title={"Hello world!"} /> // props.title
<Welcome>Hello world!</Welcome> // props.children

props.title과 props.children 중 어떤 방식으로 값을 넘길지는 개인의 선택이다.

여는 태그와 닫는 태그가 한 줄로 떨어질 수 있다면 title을 사용하는 것이 더 깔끔한 방법이다.

 

그럼 언제 props.children을 활용할까?

1. 사용자 정의 컴포넌트가 일반적인 html 태그의 구조와 비슷할 때

<button onClick={() => alert("submit")}>제출하기</button>;
<button onClick={() => alert("delete")}>삭제하기</button>;
<button onClick={() => alert("add")}>추가하기</button>;

라는 버튼을 아래처럼 사용자 정의 컴포넌트로 만든다고 가정을 해보자.

export default function Button(props){
	// TODO: 아래의 button 태그에 스타일 코드 추가
    return <button onClick={props.handleClick}>{props.children}</button>
}
<Button onClick={() => alert("submit")}>제출하기</Button>;
<Button onClick={() => alert("delete")}>삭제하기</Button>;
<Button onClick={() => alert("add")}>추가하기</Button>;

사용자 컴포넌트를 만들었을 때의 구조와 그 전의 구조가 굉장히 동일하다는 것을 볼 수 있다.

 

즉, 사용자 정의 컴포넌트를 보고 그 안에 있는 렌더링 구조를, 컴포넌트를 사용하는 곳에서 예측할 수 있을 때 props.children을 사용하는 것을 지향한다.

 

아까 보았던 Welcome 컴포넌트는 'Welcome'이라는 이름만 보았을 때, 내부적인 구조를 예상할 수 없기 때문에 명시적으로 props.title로 넘기는 것이 더 직관적이다.

 

또 언제 props.children을 활용할까?

2. 컴포넌트에 다른 컴포넌트를 전달해야 할 때

<Welcome>
	<또다른 사용자 정의 컴포넌트 />
</Welcome>

이런 경우에 props.children을 사용하는 것이 좀 더 관용적이다. 

예를 들어,

<Layout>
	<Apage />
</Layout>

<Layout>
	<Bpage />
</Layout>

<Layout>
	<Cpage />
</Layout>

사용자 정의 컴포넌트인 Layout에 props.children으로 넘긴다.

그렇게 된다면 Layout 내부에는 props.children에 대한 공통적으로 적용할 style 코드가 들어있는 것이다.

➡ 컴포넌트에 다른 컴포넌트를 전달해야 할 때 props.children을 자주 사용한다.

 

props를 더 잘 다루기

어떤 경우에는 컴포넌트를 보여주고 싶고, 어떤 경우에는 컴포넌트를 보여주고 싶지 않을 때?

➡ 조건부 렌더링을 활용하자

 

조건부 렌더링을 할 때, props 값으로 아무것도 넘기지않으면 default 값은 true이다.

 

props로 보내는 값이 많아지면, props.~~ 이런 식으로 prefix를 적는 것이 힘들 때가 있다.

➡ destructuring을 하자!

어떻게?

props로 인자를 받는 공간에, ({ withImg, firstName, lastName }) 

키 값만으로도 접근이 가능하다!

 

props의 값을 넘기는 쪽에서 태그 안에 일일이 프로퍼티 값을 설정하는 방법 외에,

객체 자체를 넘기는 방법도 존재한다.

넘길 때에는 정보를 넘길 태그에 {...객체이름} 이라고 표시한다.

 

'React' 카테고리의 다른 글

[React] 리스트와 key / 폼과 이벤트 제어하기  (0) 2022.07.26
[React] Lifecycle와 useEffect()  (0) 2022.07.25
[React] State  (0) 2022.07.09
리액트 프로젝트의 개발 환경 설정  (1) 2022.06.30
React 이론  (0) 2022.06.30