JSX란
JSX란, 함수 호출과 객체 생성을 위한 문법적 편의를 제공하는 자바스크립트의 확장이다. 그리고 React.createElement() 호출을 반복하는 불편을 해소시킨다. JSX가 템플릿 엔진이나 HTML처럼 보일 수도 있지만 그렇진 않다. JSX는 React 엘리먼트를 생성하면서도 자바스크립트의 모든 기능을 쓸 수 있게 도와준다.
JSX의 장점을 몇 가지 적어보면 이러하다.
물론, JSX가 React에 필수는 아니다. 하지만 많은 사람들이 추천을 하고, 공식 웹사이트에서도 JSX 사용을 권장한다고 나온다.
자바스크립트 Ex)
React.createElement(
"div",
null,
React.createElement(HelloWorld, null),
React.createElement("br",null),
React.createElement(
"a",
{href : "http://nate.com"},
"hwibin nate"
)
)
위와 같은 자바스크립트 코드를 JSX로 표현하면 아래와 같다.
<div>
<HelloWorld/>
<br/>
<a href : "http:nate,com">hwibin nate</a>
</div>
JSX를 자세히 보면 본질적으로 XML과 문법이 비슷한 언어라는 것을 알 수 있다.
React는 UI와 자바스크립트 로직에 대한 설명을 한 곳으로 모아, 기존의 어긋난 관심사 분리를 고쳐놓았다고 볼 수 있다. JSX를 사용하면 코드가 HTML처럼 보여 읽고 쓰기훨씬 편리하다.
JSX는 여러 가지 도구를 사용해서 표준 ECMAScript로 컴파일할 수 있다. 참고로 자바스크립트는 ECMAScript이다. 하지만 JSX는 ECMAScript 명세의 일부가 아니라 기존에 정의된 의미 체계는 없다.
따라서 JSX를 자바스크립트로 변환하여 사용한다. 과정은 이러하다.
JSX -> 트랜스파일러(트랜스컴파일러) -> 자바스크립트 -> 브라우저
사실 처음에는 JSX에 화살괄호도 있고해서 이상하게 보일 순 있다.
하지만 이는 JSX의 강점 중 하나이다. React.createElement(NAME,~~)로 함수 호출을 반복해서 작성하는 대신 < NAME/>으로 입력할 내용을 줄일 수 있다. 이 글의 첫 번재, 두 번째 코드를 보면 알 수 있을 것이다. 이렇듯 JSX는 사용자 경험만큼 개발자 경험을 중요시 여긴다.
※ 물론 JSX 외에도 코드를 줄일 수 있는 방법은 있다. 그 중 대표적인 것이 React.DOM.을 사용하는 것인데, 예를 들면 이러하다.
React.createElement('hwibin',null,'thanks')
이것을
React.DOM.hwibin(null,'thanks')
이렇게 바꿀 수 있다. 이외에도 짧은 변수를 사용하는 것 등이 있다. 그래도 많은 사람들과 React 팀은 JSX를 추천한다.
그리고 다시한번 기억해야 할 것은, JSX를 사용하려면 브라우저에서 실행하기 전에 컴파일 또는 트랜스파일 과정을 거쳐 일반적인 자바스크립트로 변환해야 한다.
JSX로 React 엘리먼트 생성하기
JSX로 React 엘리먼트 객체를 생성하는 것은 간단하다. 예를 들면 이러하다.
자바스크립트 Ex)
React.createElement(
name,
{key1:value2, key2:value2, ~~~},
data1, data2,data3~~~
)
이것을 JSX로 바꾸면 이러하다.
<name key1=value1, key2=value2~~~>
<data1/>
<data2/>
~~~
</name>
이렇듯 JSX 코드에서 key1=value1 과 같은 속성 값의 쌍은 createElement()에 전달하는 두 번재 인자와 동일하다. 일단은 속성이 없는 경우를 생각하기로!
자바스크립트 Ex)
ReactDOM.render(
React.createElement('data', null, "Hello World'),
document.getElementById('content')
)
위 코드를 JSX로 작성하면 훨씬 간단히 쓸 수 있다.
ReactDOM.render(
<h1>Hello World</h1>,
document.getElementById('content')
)
당연히 JSX 문법으로 작성한 객체도 변수에 저장할 수 있다. 이렇게 말이다.
let helloWolrdElement = <h1>Hello World</h1>
ReactDOM.render(
helloWorldElement,
document.getElementById('content')
)
React 컴포넌트에 JSX 사용하기
앞에 예시로 든 < h1> JSX태그는 표준 HTML 태그 이름이기도 하다. 컴포넌트를 다룰 때도 같은 문법을 사용한다. 다만 조금 다른 것은, 컴포넌트 클래스의 이름의 경우 '대문자'로 시작한다는 것이다.
이해하기 쉽게 예시 코드를 하나 드리겠다.
먼저 자바스크립트로 먼저 작성한 예이다.
class Hwinbin extends React.Component{
render(){
return React.createElement('div',
null,
React.createElement('h1',null,'1. thanks'),
React.createElement('h1', null, '2. thankss'))
}
}
ReactDOM.render()
React.create(Hwibin, null),
document.getElementById('content')
위 코드를 JSX로 표현하면 아래와 같다.
class Hwibin extends React.Component{
render(){
return(
<div>
<h1>1. thanks</h1>
<h1>2. thankss</h1>
</div>
)
}
}
ReactDOM.render(
<Hwibin/>,
document.getElementById('content')
)
훨씬 읽기도, 이해하기도 쉽지 않은가?
정답은 아니지만, JSX의 화살괄호는 대표적인 논쟁거리이고, 누군가는 거부하는 이유 중 하나이다. 하지만 익숙해지면 훨씬 편해질 것이다.
아래 JSX코드에서 return문을 보면 소괄호를 사용한 것을 볼 수 있다. return문의 같은 행에, 이후로 아무것도 적지 않는 경우에는 반드시 괄호를 넣어야 한다. 만약 괄호를 넣지 않으면 자바스크립트는 return을 마치고 아무 값도 반환하지 않는다.
render(){
return (
<div>
</div>
)
}
render(){
return <div>
</div>
}
아래처럼 ()를 생략한 순 있지만 놓치거나 실수할 확률은 높을 것이다.
JSX에서 변수 출력
컴포넌트를 만들 때, 자체적으로 뷰를 변경할 수 있는 똑똑한 컴포넌트를 만들고 싶을 수 있다. 예를 들면, 현재 날짜/시간 컴포넌트에 하드 코딩보다는 현재 날짜와 시간을 사용하면 매우 유용할 것이다.
만약 JSX없이 React를 사용한다면, +를 이용해서 연결하거나, ES6+/ES2-15+를 사용한다면 백틱(`)과 ${varName}으로 표시한 문자열 템플릿을 사용할 수 있을 것이다. 명세에서는 이 기능을 공식적으로 '템플릿 리터럴' 이라고 한다.
예를들면, 일반적인 자바스크립트만으로 작성한 DateTimeNow 컴포넌트에서 속성을 텍스트로 사용한다고 하면 이렇게 코드가 짜여진다.
class DateTimeNow extends React.Component{
render(){
let dateTimeNow = new Date().toLocaleString()
return React.createElement(
'span',
null,
`Current date and time is ${dateTimeNow}`
)
}
}
위 코드를 JSX로 표현하면, 중괄호({}) 표기법을 사용하여 변수를 동적으로 출력할 수 있어 코드가 늘어나는 걸 많이 줄일 수 있다.
아래처럼 말이다.
class DateTimeNow extends React.Component{
render(){
let dateTimeNow = new Date().toLocaleString()
return <span>Current date and time is {dateTimeNow}</span>
}
}
지역변수 뿐 아니라 속성도 출력할 수 있다.
이 뿐 아니라, 자바스크립트 표현식이나 그 외 어떤 자바스크립트 코드라도 중괄호 안에서 실행 시킬 수도 있다. 예를 들면, 아래 처럼 날짜 형식을 적용할 수도 있다.
<p>Current time in your locale is {new Date(Date.now()).toLocaleTimeString()}</p>
JSX에서 속성 사용하기
엘리먼트 속성을 정의할 때는 속성 문법을 사용한다. 이것은 HTML/XML의 속성 문법과 비슷하다.
즉, JSX에서 속성을 전달하는 방법은 일반 HTML을 작성하는 방법과 같다. 그리고, 엘리먼트 속성을 입력해서 표준 HTML속성을 렌더링한다.
예를들어 링크 컴포넌트가 있는데, 이를 재사용하려면 href가 매번 다른 주소를 반영하도록 변경해야 한다. 이것을 '동적으로 설정한 값' 이라고 한다.
속성에 동적으로 생성한 값을 사용할 수 있는 컴포넌트의 경우, 이 값을 컴포넌트 속성(this.props)에서 가져온다. 그리고는 화살괄호(<>) 안에 중괄호({})를 넣어 속성에 동적으로 생성한 값을 엘리먼트에 전달하면 된다.
예를들어 사용자 계정에 연결하는 컴포넌트를 만든다고 가정하면, href와 title은 사용자에 따라 달라진다. 즉, 동적이다. 이렇게 코딩할 수 있을 것이다.
class LinkCom extends React.Component{
render(){
return <a href={this.props.url}
title={this.props.label}
target="_blank">Profile
}
}
href와 title을 렌더링 하기 위해 각각 url, label을 속성으로 사용하고 있다.
이 속성 값은 어디서 전달한 것일까? 속성 값은 LinkCom 생성 시에 정의된다. 즉, LinkCom을 생성하는 부모 컴포넌트에서 이 값을 정의한다.
다른 글에서 한번 말했었는데, React는 표준 속성이 아닌 이외의 속성은 렌더링에서 제외한다고 했었다. 이것은 JSX의 특성이 아니라 React의 동작 방식이다.
하지만 상황에 따라서는 사용자 지정 데이터를 속성으로 추가해야 할 때도 있다. 예를 들어, 어떤 목록 데이터가 있다고 생각했을 때, 이 데이터 중에는 앱에는 필수적이지만 사용자에게는 필요하지 않은 것이 있을 수 있다. 이런 정보를 DOM 요소에 속성으로 넣는 것은 사실 흔한 방식이다.
<li hwibin-awesome="true" name="hwibin">thanks!</li>
위의 코드에서는 hwibin-awesome과 name 속성을 사용하고 있다.
DOM의 HTML 비표준 속성에 데이터를 저장하는 것은 일반적으로는 안티패턴이라고 여겨진다. DOM을 데이터베이스나 프론트엔드 데이터 저장소로 사용하는 것은 적절하지 않기 때문이다. DOM에서 데이터를 가져오는 것은 메모리 상의 가상 저장소에서 데이터를 가져오는 것보다 느리다.
JSX를 사용할 때 데이터를 반드시 HTML 요소의 속성으로 저장해야 하는 경우에는 data-* 속성을 사용한다. 예를들어 속성에서 < li> 요소에 this.hwibinAwesome 값을 렌더링한다고 하면 아래와 같이 작성할 수 있다.
<li data-hwibin-awsome={this.hwibinAwesome}>thanks!</li>
하지만 표준 HTML 요소에 비표준 HTML 속성을 전달하면 해당 HTML 속성은 렌더링되지 않는다.
<li hwibin-awesome={this.hwibinAwesome}>thanks</li>
<li hwibinAwesome={this.hwibinAwesome}>thanks</li>
위 두 코드의 결과는 thanks로 같다.
사용자 지정 컴포넌트 클래스에는 내장렌더러가 없고, 표준 HTML 요소나 다른 사용자 지정 엘리먼트에 의존하므로 데이터를 다루기 위해 data-* 속성을 사용할 필요는 없다는 것을 알 수 있다. this.props를 통해 입력한 모든 속성에 접근할 수 있기 때문이다.
아래와 코드는 추천하지 않는다.
class HelloWord extends React.Component{
render(){
return <h1 title={this.props.title} id={htis.props.id}>
Hello {htis.props.frameworkName} world
</h1>
}
}
이렇듯 각 속성을 개별적으로 전달하면 코드가 많아진다. 또한, 속성을 변경해야 하는 경우에도 개선해야 할 코드가 밀접하게 결합된다.
모든 속성을 전달해야 한다면, 개별 속성을 따로 전달하는 것이 아니라, JSX에서는 이에 대한 해결책으로 생략 부호(...) 처럼 생긴 '펼침 연산자'를 사용할 수 있다.
class HelloWorld extends React.Component{
render(){
return <h1 {...this.props}>Hello {this.props.frameworkName} world</h1>
}
}
{...this.props}를 이용하면 모든 속성을 자식 엘리먼트로 전달할 수 있다.
React 컴포넌트 메서드 생성
React 컴포넌트에 애플리케이션을 위한 메서드를 자유롭게 추가할 수 있다. 이유는 React 컴포넌트가 클래스이기 때문이다.
아래처럼 말이다.
class Velog extends React.Component{
getUrl(){
return 'http://velog.com/chlgnlqls3'
}
render(){
~~~
}
}
즉, render() 외에도 임의의 메서드를 직접 만들 수 있는 것이다. 당연히 getUrl() 메서드를 사용하여 API 서버에 대한 URL을 추상화할 수도 있을 것이다. 이를 헬퍼 메서드라고 하는데, 헬퍼 메서드는 재사용 가능한 논리가 있고, render()를 포함하여 컴포넌트 어디서나 호출할 수도 있다.
JSX에서 직접 작성한 메서드의 반환 값을 출력하려면 변수를 출력할 때와 마찬가지로 {}를 사용하면 된다.
아래처럼 말이다.
class Velog extends React.Component{
getUrl(){
return 'http://velog.com/chlgnlqls3'
}
render(){
return(
<div>
<p>My velog URL is : <a href={this.getUrl()}>{this.getUrl()}</a></p>
<div>
)
}
}
위와 같이 JSX의 {}에서 컴포넌트 메서드를 직접 호출할 수 있다.
컴포넌트 메서드는 React의 이벤트 핸들러를 이해하기 위한 기초로 매우 중요하다!
JSX의 if/else 처리
if/else 조건의 결과에 따라 컴포넌트가 뷰를 변경할 수 있게 작성하는 경우도 있을 것이다.
컴포넌트 클래스에 조건에 따라 다른 링크 엘리먼트를 렌더링한다고 생각해보면, 링크의 텍스트와 URL은 user.session 값에 따라 다르게 렌더링된다.
자바스크립트 Ex)
render(){
if (user.session)
return React.createElement('a',{href: '/logout'}, 'Logout')
else
return React.createElement('a', {href: '/login'}, 'Login')
}
JSX Ex)
render(){
if(this.props.user.session)
return <a href="/logout">Logout</a>
else
return <a href="/login">Login</a>
}
만약, 위와 같은 엘리먼트를 < div> 로 감싼다고 생각해보면, 일반적인 자바스크립트로 작성하려면 변수를 추가하는 방법, 함수 표현식을 사용하는 방법, 삼항 연산자를 사용하는 방법을 생각해볼 수 있다. < div>의 createElement() 내부에는 if 조건문을 사용할 수 없기 때문이다! 중요한 점은, 런타임에 반드시 값을 가져와야 한다는 것이다.
3가지 스타일(변수, 표현식, 삼항 연산자)을 한번 JSX를 사용하지 않고 자바스크립트로 작성해보면 이러하다.
render(){
let link
if(this.props.user.session)
link = React.createElement('a', {href: '/logout'}, 'Logout')
else
link = React.createElement('a', {href: '/login'}, 'Login')
return React.createElement('div', null, link) => 변수 link를 사용
}
render(){
let link = (sessionFlag) => { => 표현식 생성
if(sessionFlag)
return React.createElement('a', {href: '/logout'}, 'Logout')
else
return React.createElement('a', {href: '/login'}, 'Login')
}
return React.createElement('div', null, link(this.props.user.session))
}
render(){
return React.createElement('div', null,
(this.props.user.session)
? React.createElement('a', {href: '/logout'}, 'Logout')
: React.createElement('a', {href: '/login'}, 'Login')
)
}
위 3가지 방법을 JSX로 사용하면 {} 표기법으로 변수를 출력하고 자바스크립트 코드도 실행할 수 있다. 바꾸면 아래와 같다.
1. 변수
render(){
let link
if (this.props.user.session)
link = <a href='/logout'>Logout</a>
else
link = <a href='/login'>Login</a>
return <div>{link}</div>
}
2. 표현식
render(){
let link = (sessionFlag) => {
if(sessionFlag)
return <a href='/logout'>Logout</a>
else
return <a href='/login'>Login</a>
}
return <div>{link(this.props.user.session)}</div>
}
3. 삼항연산자
render(){
return <div>
{(this.props.user.session)
? <a href='/logout'>Logout</a>
: <a href='/login'>Login</a>}
<div>
}
JSX 밖에 return 문 앞에 함수를 작성하는 방법인 2번 방법을 다른 방법으로 쓸 수도 있다. 같은 함수를 JSX 내부에서 '즉시실행함수'로 선언할 수 있다. 이 방법은 link처럼 변수를 추가하지 않고도 if/else 문을 런타임에 실행할 수 있다.
아래와 같다.
render(){
return <div>{
(sessionFloag)=> { => 즉시실행함수 정의
if(sessionFlag)
return <a href='/logout'>Logout</a>
else
return <a href='/login'>Login</a>
} (this.props.user.session) => 매개변수와 함께 즉시실행함수 실행
}</div>
}
이와 같은 방식을 전체 엘리먼트 뿐 아니라, 텍스트나 속성 값을 렌더링할 때도 적용할 수 있다. 중괄호 내에서 앞에 제시한 세 가지 방법 중 한 가지를 사용하면 된다.
예를 들면 아래와 같다.
render(){
let sessionFlag = htis.props.user.session => session을 지역변수에 담는다.
return <div>
<a href={(sessionFlag)?'/logout':'/login'}>
{(sessionFlag)?'Logout':'Login'}
}
이처럼 템플릿 엔진과 달리 JSX는 특별한 문법이 없고, 그저 자바스크립트를 사용하면 된다.
조금 쉽게 요약해서 JSX에서 if/else 조건을 구현할 때 쓰는 방법은 아래와 같다.
return 문 이전에 JSX 외부에서 변수를 선언한 후 JSX 내부에서 {}를 사용하여 출력
return 문 이전에 JSX 외부에서 값을 반환하는 함수 표현식을 선언한 후 JSX 내부의 {}에서 실행
삼항 연산자를 사용
JSX 내부에서 즉시실행함수를 사용
위 방법은 React와 JSX를 이용해 상호작용하는 UI를 만드는 데 중요한 조건 처리 방법이다.
JSX의 주석 작성
JSX의 주석은 일반 자바스크립트의 주석과 비슷하다. 다만, JSX에 주석을 추가할 때는 표준 자바스크립트의 주석을 {}로 감싸서 작성한다.
어렵지 않으니 별다른 설명보다는 예시 코드로 주석 작성을 설명하겠다.
let hwibin = (
<div>
{/*주석주석*/}
</div>
)
let velog = (
<div>
<Post
/*여러
줄의
주석*/
name=hwibin // 주석주석
/>
</div>
)
마지막으로 JSX에 대해서 기억해야 할 점은!
JSX 프로젝트를 제대로 작동시키려면 JSX를 '컴파일'해야 한다는 것이다. 브라우저는 JSX를 실행할 수 없다. 브라우저가 실행할 수 있는 것은 자바스크립트 뿐이다. 따라서 JSX를 보통의 자바스크립트로 변환해야 한다.