Study/React

React.js - JSX

김만두_ 2023. 6. 21. 20:22

JSX 란

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

위에 희한한 태그 문법은 문자열도, HTML도 아니다.

JSX라 하며 JavaScript를 확장한 문법이다. JSX라고 하면 템플릿 언어가 떠오를 수도 있지만, JavaScript의 모든 기능이 포함되어 있다.

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

 

JSX 변환 과정

  • JSX는 브라우저에서 실행되기 전에 코드가 번들링되는 과정에서 바벨을 사용하여 자바스크립트 형태로 변환된다.
    // JSX
    const element = (
      <h1 className="greeting">
        Hello, world!
      </h1>
    );
    
    // JS로 변환된 JSX
    const element = React.createElement(
      'h1',
      {className: 'greeting'},
      'Hello, world!'
    );
  • JSX를 사용하지 않고 React.createElement() 함수를 사용하면 컴포넌트를 렌더링 할 수 있다. 하지만 이 방식은 JSX를 사용하는 방식보다 불편하다. JSX를 사용하면 쉽고 편하게 UI를 렌더링 할 수 있다.

JSX 장점

  1. 보기 쉽고 익숙하다.
    • JSX는 HTML 코드와 비슷하기 때문에 일반 자바스크립만 사용한 코드보다 더 익숙하며 가독성이 좋다.
  2. 높은 활용도
    • JSX에는 div, span 같은 HTML 태그를 사용할 수 있으며, 개발자가 만든 컴포넌트도 JSX 안에서 작성할 수 있다.

JSX 문법

반드시 태그는 닫혀야 한다.

  • <div>, <p>, <span>, <a> 같이 짝이 있는 태그의 경우 반드시 닫는 태그가 존재해야 한다. 그렇지 않을 경우 에러가 발생한다.
  • <img/>, <input/>, <br/> 같은 단독 태그(self-closing tag)의 경우에는 반드시 태그를 닫아줘야 한다. 그렇지 않을 경우 에러가 발생한다.

렌더링 될 루트 엘리먼트는 하나만 존재 해야 한다.

ReactDOM.render(
	<div>
		Hello
	</div>
	<div>
		Bye
	</div>,
	document.getElementById('root')
);
  • 위 예제처럼 렌더링 될 리액트 엘리먼트에서 루트 엘리먼트가 두 개 이상일 경우 에러가 발생한다. 때문에 두 개 이상의 루트 엘리먼트가 존재할 경우 아래와 같이 반드시 하나의 엘리먼트로 감싸져야 한다. (JSX는 자식 엘리먼트를 가질 수 있다.)
  • Virtual DOM에서 컴포넌트 변화를 감지해 낼 때 효율적으로 비교할 수 있도록 컴포넌트 내부는 하나의 DOM 트리 구조로 이루어져야 한다는 규칙 때문에, 리액트에서는 반드시 컴포넌트에 여러 요소가 있다면 반드시 부모 요소로 감싸야 한다.
ReactDOM.render(
	<div>
		<div>
			Hello
		</div>
		<div>
			Bye
		</div>
	</div>,
	document.getElementById('root')
);
  • 이처럼 하나의 엘리먼트로 감싸져야 하는 경우 무분멸한 div 태그를 사용하게 되고, 스타일 관련 코드가 꼬일 수도 있다. 이를 위해 리액트에서는 복수의 엘리먼트를 리턴하는 방법을 제공한다.

JS 표현식 사용하기

  • JSX에서는 중괄호 { } 를 사용하여 JS 표현식을 쓸 수 있다.
    const name = 'Josh Perez';
    const element = <h1>Hello, {name}</h1>; // Hello, Josh Perez
    
    ReactDOM.render(
      element,
      document.getElementById('root')
    );
    
    //---------------------------------------------------------------------------
    
    ReactDOM.render(
      <p>Random number : {Math.random() * 100}</p>,
      document.getElementById('root')
    );
    // 중괄호는 표현식 먼저 평가 돼 그 결과를 리턴하게 만든다.
    
    //---------------------------------------------------------------------------
    
    // 주석 사용하기
    // JSX에서 주석은 {}로 감싸준다. 여러 줄 주석만 사용 가능하다.
    ReactDOM.render(
        <div>
            <p>Hello</p>
            {/* 주석은 이렇게 작성합니다. */}
            <p
    		className='react' // 시작 태그를 여러줄로 작성 시 주석 작성 가능
            >World</p>
        	// 하지만 이런 주석이나
        	/* 이런 주석은 페이지에 그대로 노출됩니다.*/
        </div>,
        document.getElementById('app')
    );

조건부 렌더링

삼항 연산자(조건부 연산자)를 사용한 조건부 렌더링

  • JSX 내부의 JS 표현식에서는 if문을 사용할 수 없다. 때문에 조건에 따라 다른 내용을 렌더링 하고자 할 경우 JSX 밖에서 if 문을 사용하거나, 중괄호 안에서 삼항 연산자를 사용하면 된다.
    class App extends Component {
        render() {
            let name = 'React';
            return (
                <div>
                    {
                        name === 'React' ? (
                            <h1>This is React.</h1>
                        ) : (
                            <h1>This is not React.</h1>
                        )
                    }
                </div>
            );
        } 
    }

AND 연산자(&&)를 사용한 조건부 렌더링

  • 특정 조건을 만족할 때만 내용을 보여주고 싶을 때 사용
    class App extends Component {
        render() {
            let name = 'React';
            return (
                <div>
                    {
                        name === 'React' && <h1>This is React.</h1>
                    }
                </div>
            );
        } 
    }

OR 연산자(||)를 사용한 조건부 렌더링

  • 리액트 컴포넌트에서는 함수에서 undefined나 null을 반환하면 렌더링을 하려하면 오류가 발생한다. 반면 JSX 내부에서 undefined나 null을 렌더링하는 것은 괜찮다.
  • JSX 내부에서 undefined나 null을 렌더링하면 아무것도 보여주지 않는다.
  • OR 연산자는 AND 연산자와 다르게 특정 값이 undefined나 null일 경우 보여주고 싶은 문구가 있을 때 주로 사용한다.
    class App extends Component {
        render() {
            let name = undefined;
            return (
                <div>
                    {
                        name === 'React' || <h1>This is React.</h1>
                    }
                </div>
            );
        } 
    }

JSX에서 속성 정의하기

  • HTML 엘리먼트에 속성을 정의할 수 있는 것처럼, JSX에서도 엘리먼트에 속성을 정의 할 수 있다. 하지만 JSX는 HTML보다는 JavaScript에 가깝기 때문에 JSX에서는 HTML 엘리먼트에 속성을 정의하는 방식과는 약간 다른 방식으로 속성을 정의 한다.
  • JXS가 반환하는 React DOM 엘리먼트에서는 기존 HTML 속성이 아닌 camelCase로 작성된 속성명을 사용해야 한다. 예를 들어, JSX에서 class는 className이 되고 tabindex는 tabIndex가 된다.
    • className이 아닌 class 값을 설정 시 스타일이 설정은 되지만 경고 문구가 뜬다. 하지만 v16 이상 부터는 class를 className으로 변환시켜주고 경고를 띄워준다.
  • JSX에서 속성값을 정의 할 때 중괄호 { }를 사용하여 JS 표현식을 사용할 수 있는데 이 경우 따옴표를 사용하지 않는다.
    // JSX에서 프로퍼티 명은 카멜케이스로 작성되어야 한다.
    const element = <div tabIndex="0"></div>; 
    
    // 속성값 정의 시 JS 표현식을 사용고자자 할 경우 따옴표를 사용하지 않는다.
    const element = <img src={user.avatarUrl}/>;
  • 속성값 정의 방법
    1. 따옴표를 사용하여 문자열 입력 → 정적
    2. 중괄호를 사용하여 JS표현식 사용. 이 경우에는 따옴표를 사용하지 않음 → 동적
    • 속성값을 정의 할 경우 위 두 가지 방법 중 하나만 사용해야 하며, 동일한 속성에 위 두가지 방법을 사용할 수 없다. 한 속성에 하나의 방식만 사용해야 한다.

인라인 스타일링

  • JSX에서는 style 속성 안에 직접 CSS를 포함할 수 없으며, 스타일 정보를 담은 객체를 참조해야한다. 스타일 객체의 경우 기존 CSS 속성 명을 camelCase로 작성한다.
    var element = <div style="color:red;">Hello World</div>; // 인라인 CSS 사용불가
    
    //--------------------------------------------------------------------
    
    // JXS에서 CSS를 적용하기 위해서는 스타일 객체를 만들어야 한다.
    var styleObject = {
    	color: "#333",
    	padding: 0,
    	backgroundColor: "black", // 스타일 각체의 속성은 모두 camelCase로 작성한다.
    	fontSize: "32" 
    }
    
    var element = <div style={styleObject}>Hello World</div>;
    
    //--------------------------------------------------------------------
    
    // 스타일 객체를 style 속성 내부에 선언 가능
    var element = 
    <div style={ { color: "#333", fontSize: "32" } }>
    	Hello World
    </div>;

JSX 특징

대소문자를 구별한다.

  • JSX에서 HTML엘리먼트를 작성할 때는 반드시 소문자를 사용해야 하지만, 컴포넌트를 작성할 때는 컴포넌트 클래스 이름과 동일하게 PascalCase로 작성되어야 한다.
    ReactDOM.render(
    	<div>
    		<MyCustomComponent/>
    	</div>,
    	document.getElementById('root')
    );

주입공격을 방지한다.

  • 기본적으로 React DOM은 JSX에 삽입된 모든 값을 렌더링하기 전에 이스케이프 처리하므로, 애플리케이션에서 명시적으로 작성되지 않은 내용은 주입되지 않는다. 모든 항목은 렌더링 되기 전에 문자열로 변환된다. 이런 특성으로 인해 XSS (cross-site-scripting) 공격을 방지할 수 있다.

객체를 표현한다.

  • Babel은 JSX를 React.createElement() 호출로 트랜스파일한다. 때문에 첫 번째 코드는 두 번째 코드로 트랜스 파일 된다.
  • // 개발자가 작성한 코드 const element = ( <h1 className="greeting"> Hello, world! </h1> ); // babel로트랜스 파일된 코드 const element = React.createElement( 'h1', {className: 'greeting'}, 'Hello, world!' );
  • React.createElement()는 버그가 없는 코드를 작성하는 데 도움이 되도록 몇 가지 검사를 수행 후, 기본적으로 다음과 같은 객체를 생성한다.
  • const element = { type: 'h1', props: { className: 'greeting', children: 'Hello, world!' } }
  • 이렇게 생성된 객체를 “React 엘리먼트”라고 하며, 이는 화면에 표시하려는 항목에 대한 설명이라고 할 수 있다. React는 이러한 객체를 읽고 DOM을 구성하고 최신으로 유지하는 데 이러한 객체를 사용한다.