1.1JSX
React Component들이 반환하는 JSX의 정의와 구성 요소 및 변환 원리
TL;DR
추억의 쪽지 시험
1. JSX의 정의
JSX(JavaScript XML)는 JavaScript 안에서 XML(Extensible Markup Language)을 사용할 수 있게 해주는 문법 확장(Syntax Extension)이다.
JSX는 React가 등장하면서 그 개발사인 Meta에서 소개한 새로운 구문이긴 하지만, React에 종속적인것은 아니다. 또, ECMAScript라고 불리는 자바스크립트의 표준의 일부도 아니다. 따라서 자바스크립트 엔진에서 독자적으로 JSX를 실행하면 에러가 발생한다. JSX는 트랜스파일러를 거쳐야만 비로소 자바스크립트 런타임이 이해할 수 있는 의미있는 자바스크립트 코드로 변환된다.
JSX의 설계 목적은 다양한 트랜스파일러에서 다양한 속성을 가진 트리 구조를 토큰화해 ECMAScript로 변환하는 것이다. 즉, 자바스크립트 내부에서 표현하기 어려웠던 XML 스타일의 트리 구문을 작성할 수 있도록 도움을 주는 새로운 문법이다.
JSX는 JSXElement, JSXAttributes, JSXChildren 그리고 JSXStrings라는 4가지 컴포넌트로 구성되어 있다.
1-1. JSXElement
HTML의 element와 비슷한 역할을 하는, JSX의 가장 기본 요소다. 다음과 같은 형태 중 하나다.
JSXOpeningElement:<JSXElement JSXAttributes(optional)>형식의 오프닝 태그다.JSXClosingElement:</JSXElement>식의, Opening Element의 종료를 알리는 태그.JSXSelfClosingElement:<JSXElement JSXAttributes />식으로 요소가 시작되고 스스로 종료되는 태그. 내부적으로 자식을 포함할 수 없는 형태다.JSXFragment:<>로 시작해</>로 끝나는, 아무런 요소가 없는 형태다. 다만</>식의 selfclosingelement 형태는 불가능하다.
React에서 JSXElement의 이름은 항상 대문자로 시작해야만 사용이 가능한데, 이는 Babel이 JSX를 변환할 때 소문자로 시작하는 요소는 HTML 태그명으로 처리하고 대문자로 시작하는 요소는 변수(사용자가 만든 컴포넌트)로 구분해 처리하기 때문이다.
이외에도 JSXElement의 요소 이름으로 쓸 수 있는 JSXElementName에는 여러 문법이 있는데, React에서는 잘 쓰지 않는다.(JSXNamespacedName, JSXMemeberExpression처럼 두 개의 JSXIdentifier를 :나 .로 잇는 문법이다.)
1-2. JSXAttributes
JSXElement에 부여할 수 있는 속성으로, 필수값은 아니다.
JSXSpreadAttributes: 자바스크립트의 Spread Operator와 동일한 역할을 한다.{...AssignmentExpression}: 자바스크립트의AssignmentExpression으로 취급되는 모든 표현식이 가능하다. 즉, 단순히 객체뿐 아니라 조건문 표현식, 화살표 함수, 변수 참조 등 다양한 것들이 들어갈 수 있다.
JSXAttribute: 속성을 나타내는 키와 값으로 표현된다. 키는JSXAttributeName, 값은JSXAttributeValue로 불린다.- 값에 해당하는 
JSXAttributeValue는 "큰 따옴표로 구성된 문자열", '작은 따옴표로 구성된 문자열',{AssignmentExpression}, 다른JSXElement그리고JSXFragment를 가질 수 있다. 
- 값에 해당하는 
 
1-3. JSXChildren
JSX는 속성을 가진 트리 구조를 작성할 수 있도록 도움을 주는 문법이기에, JSX로는 부모와 자식 관계를 나타낼 수 있다. JSXElement의 자식 값은 JSXChildren이다.
JSXChild:JSXChildren을 이루는 기본 단위로, 아래의 요소들이 올 수 있다.JSXTextJSXElementJSXFragment{ JSXChildExpression (optional) }자바스크립트의AssignmentExpression이 올 수 있다.
1-4. JSXStrings
JSX 문법을 구성하는 요소 중 문자열 리터럴이다.
jsxconst el = <div className="container">Hello</div>
이 경우 JSXAttributeValue안의 문자열 "container"와 JSXText인 "Hello"가 모두 JSXStrings다.
HTML에서 사용 가능한 문자열은 모두 JSXStrings에서도 사용 가능하다. 이는 개발자가 HTML의 내용을 JSX로 바로 가져올 수 있도록 의도적으로 설계된 부분으로, "큰 따옴표로 구성된 문자열", '작은 따옴표로 구성된 문자열' 그리고 JSXText가 올 수 있다.
2. JSX는 어떻게 자바스크립트에서 변환될까?
우선 JSX는 React에서 @babel/plugin-transform-react-jsx라는 플러그인을 통해 자바스크립트가 이해할 수 있는 형태로 변환된다.
jsxconst ComponentA = <A required={true}>Hello World</A> const ComponentB = <>Hello World</> const ComponentC = ( <div> <span>Hello World</span> </div> )
이런 JSX 코드는 @babel/plugin-transform-react-jsx를 통해 이렇게 변환되곤 했다:
javascript'use strict' var ComponentA = React.createElement( A, { required: true, }, 'Hello World', ) var ComponentB = React.createElement(React.Fragment, null, 'Hello World') var ComponentC = React.createElement( 'div', null, React.createElement('span', null, 'hello world') )
React 17, Babel 7.9.0 이후에는 JSX 변환이 React.createElement 대신 자동 런타임(automatic runtime)을 통해 수행되는데, 이 방식 덕분에 import React from 'react'를 안 쓰고도 JSX를 쓸 수 있게 되었고, Babel이 내부적으로 jsx(자식이 하나인 React Element 생성 시), jsxs(자식이 둘 이상인 React Element 생성 시 쓰는 함수), Fragment등을 자동 import해 번들 크기를 줄이고 JSX를 React 외의 라이브러리(Preact, emotion 등)에서도 재사용할 수 있게 되었다.
이 자동 런타임으로 트랜스파일한 결과는 아래와 같다:
javascript'use strict' var _jsxRuntime = require('custom-jsx-library/jsx-runtime') var ComponentA = (0, _jsxRuntime.jsx)(A, { required: true, children: 'Hello World', }) var ComponentB = (0, _jsxRuntime.jsx)(_jsxRuntime.Fragment, { children: 'Hello World', }) var ComponentC = (0, _jsxRuntime.jsx)('div', { children: (0, _jsxRuntime.jsx)('span', { children: 'Hello World', }), })
(0, _jsxRuntime.jsx)(A, {...})식의 문법에 익숙하지 않을 수 있는데, 이건 _jsxRuntime.jsx(A, {...})라는 함수 호출이다. (0, fn) 패턴은 Babel이 함수 호출 앞에 쓰는 흔한 패턴으로, 함수의 this 문맥이 꼬이지 않게 하기 위해 쓰인다. 위에서 보다시피
_jsxRuntime은 jsx-runtime이라는 객체를 import해서 쓰는 것인데, _jsxRuntime.jsx는 객체 _jsxRuntime의 jsx라는 메소드를 호출하는 것이다.그런데 이
_jsxRuntime이 어디서 왔는지는 상황마다 다르다. Babel이 생성한 코드는 다양한 환경에서 실행되어, 호출 대상이 객체냐 함수냐에 따라서 this가 잘못 binding 될 수 있다. 이에 Babel은 jsx 함수를 순수 함수가 될 수 있도록 this를 undefined가 되도록 강제하기 위해 (0, ...) 패턴을 사용한다. (0, ...)은 comma 연산자로, 0을 평가하고(아무 효과 없는 값이라 버려짐) 이후에 남은 _jsxRuntime.jsx라는 함수를 평가하는데 이로 인해 전체 표현식의 결과인 _jsxRuntime.jsx만 남아, 그냥 함수로 호출하라는 의미가 된다.이 두 결과물들은 약간 차이가 있지만, 근본적인 공통점들이 있다:
JSXElement를 첫 번째 인수로 선언해 요소를 정의- 옵셔널인 
JSXChildren,JSXAttributes,JSXStrings는 이후 인수로 넘겨 처리 
결국 JSX는 브라우저가 직접 실행할 수 없는 UI 선언 문법으로, Babel 트랜스파일 과정을 통해 트리 형태의 JavaScript 객체(React element)로 변환되어 React 렌더러는 이를 실제 DOM으로 그린다.