본문 바로가기
이론/Frontend

JSX 알아보기

by 유세지 2022. 7. 24.

JSX는 리액트를 사용한다면 많은 분들이 익히 만나보셨을 문법입니다. 저도 리액트 프로젝트를 하며 당연하게 사용하고 있지만 안타깝게도 JSX가 무엇인지, 정확히 어떤 역할을 하고 있는지는 잘 모르고 있습니다. 오늘은 공식 문서와 몇 가지 예제를 통해 JSX에 대해 조금 더 깊게 알아보겠습니다.

 

JSX가 왜 필요할까요?

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

 

위 코드는 공식 홈페이지에 기재된 가장 간단한 JSX 예시입니다. 변수 element에 할당된 <h1> 태그로 감싸진 구문이 바로 우리가 알고있는 JSX입니다.

 

잠시 리액트가 아닌, 순수한 자바스크립트를 사용하던 시절로 돌아가보겠습니다.

우리의 프로젝트는 대략 이런 방식을 따라 흘러가게 됩니다.

 

 

JS가 정적인 HTML과 CSS를 조작합니다.

 

자바스크립트 파일이 정적인 HTML, CSS 문서를 조작하여 원하는 화면을 띄워주거나, 다양한 동작을 할 수 있게 만들어줍니다. 아래와 같은 코드를 사용하면 HTML 요소의 스타일도 마음대로 변경할 수 있습니다.

 

const root = document.querySelector('#root');
root.style.backgroundColor = '#CCCCCC';

 

시간이 흘러 브라우저의 성능이 개선되고, 사용자의 니즈가 다양해지며 자바스크립트는 점점 더 많은 것을 해야 할 필요와 그런 요구를 충족시킬 수 있는 능력을 함께 갖게 되었습니다. 이러한 시장의 흐름에 맞춰 우리가 지금 사용하고 있는 리액트와 같은 도구들도 등장하게 되었지요.

 

리액트는 기존의 정적인 화면을 건드리는 수준에서 벗어나 직접 화면을 구성하는 덩어리들을 만들고, 부수고, 재조립하며 HTML 문서와 CSS 문서로부터 점점 더 주도권을 가져오게 됩니다. 기존의 정적인 문서들이 하는 역할들을 자바스크립트(리액트)가 대신하게 된 것입니다. (리액트가 왜 등장하게 되었는지는 다음에 다시 다루도록 하겠습니다)

 

이제는 리액트가 그 역할을 대신하고 있습니다.

 

 

리액트의 기본 요소가 되는 단위인 컴포넌트는 이러한 구조를 따릅니다.

 

// 1. 클래스 컴포넌트라면
class Hello extends React.Component {
  render() {
    return React.createElement('div', null, `Hello ${this.props.toWhat}`);
  }
}

// 2. 함수 컴포넌트라면
function Hello(props) {
  return React.createElement('div', null, `Hello ${this.props.toWhat}`);
}

ReactDOM.render(
  React.createElement(Hello, {toWhat: 'World'}, null),
  document.getElementById('root')
);

 

컴포넌트의 render() 과정에서 React.createElement() 라는 메서드를 이용하여 화면에 표시될 내용을 정해주고, 이를 ReactDOM.render 에서 불러와 대상이 되는 HTML 요소에 붙여 넣어줍니다.

 

현재까지 여러분이 사용하시던 모든 리액트는 이런 방식으로 동작하였고, 지금 이 순간도 마찬가지입니다. 아마 평소에 보시던 모양과 조금 달라서 당황스러우신 분들이 계실지도 모르겠습니다. 맞습니다. 우리에겐 아래의 코드가 훨씬 익숙합니다.

 

// 1. 클래스 컴포넌트라면
class Hello extends React.Component {
  render() {
    return <div>Hello {this.props.toWhat}</div>;
  }
}

// 2. 함수 컴포넌트라면
function Hello(props) {
  return <div>Hello {this.props.toWhat}</div>;
}

ReactDOM.render(
  <Hello toWhat="World" />,
  document.getElementById('root')
);

 

지금 보신 것처럼, JSX는 복잡한 리액트 코드를 단순하게 표현하여 가독성과 유지보수성을 높이고 빠른 개발을 도와주는 자바스크립트를 확장한 문법입니다. 마치 HTML 태그처럼 꺽쇠(<, >) 를 사용하여 우리에게 친숙함을 주지만, 실은 변환과정을 거쳐 자바스크립트 코드가 되는 것이죠. 실제로 Babel을 이용해 실제 코드의 모양을 보면 못생긴(?) 메서드들을 잔뜩 사용하는 모습을 확인할 수 있습니다.

 

Babel에 의해 변환 된 오른쪽이 실제로 실행되는 코드입니다.

 

 

어떤가요? 바벨 덕분에 우리는 편리한 JSX를 사용할 수 있었던 것입니다.

 

 

 

JSX는 자바스크립트로 변환됩니다.

우리는 위의 과정을 통해 JSX가 편리하게 사용하기 위해 만들어진 문법이며, 결국 바벨에 의해 자바스크립트로 변환되어 실행된다는걸 알았습니다. 그런데, JSX를 일반적인 html 태그처럼 사용했을때 이상한 부분이 조금 있습니다. 함께 살펴봅시다.

 

JSX에서는 클래스 어트리뷰트를 붙일때 class 대신 className을 사용하도록 권장합니다. 이를 무시하고 class를 사용하면 콘솔창에 warning이 표시됩니다.

 

className을 사용하라고 은근히 압박합니다.

 

16 버젼의 아래의 리액트에서는 인식할 수 없는 프로퍼티인 class를 그대로 전달하면 무시되었습니다. 비단 class 뿐 아니라 다른 어떤 프로퍼티도 마찬가지로, 인식할 수 없는 한 그 속성이 html 파일까지 변환되지 않았습니다.

 

 

16버젼 이전의 리액트에서는 class가 지정되지 않습니다.

 

다만 16버젼부터 이러한 방식이 변경되어 리액트가 인식할 수 없는 프로퍼티를 입력하더라도, 실제 html에 그대로 전달됩니다. 따라서 현재는 그냥 class를 사용하더라도 작동에는 문제가 없습니다.

 

16버젼부터는 class를 입력해도 지정은 됩니다.

 

글을 작성하는 시점에서 최신 버젼인 18.2 버젼으로 실행해 본 결과, 정상적으로 class가 지정된 것을 확인할 수 있습니다.

 

input 요소에 연결될 label에 붙여야 하는 for 으로 확인해보아도 마찬가지입니다. className과 마찬가지로 그대로 사용해도 동작에는 문제가 없지만, 콘솔창에는 다른 이름을 사용할 것을 권유합니다.

 

저는 이러한 현상이 다소 이해가 되지 않았습니다. 리액트에서 별 문제없이 정상적으로 사용할 수 있는 이름들인데, 왜 콘솔창에서는 이러한 친숙한 이름을 두고 오류를 띄우고 은근히 다른 이름을 사용할 것을 권유하는걸까요?

 

스택오버플로우의 답변에서는 자바스크립트에서 class가 예약어이기 때문이라고 설명하는 글이 종종 보입니다. 클래스를 생성하는 예약어인 class를 자바스크립트의 확장형인 JSX에서 그대로 사용했다간 추후에 문제가 발생할 가능성이 있어 충돌할 염려가 없는 className을 사용할 것을 권장한다는 내용입니다.

 

많은 경우를 테스트 한 것은 아니지만, 우리가 당장 실험해 본 기능들은 분명 문제 없이 동작하는 것을 확인했습니다. 예약어와의 충돌 가능성과, 같은 이름을 사용하는 데에서 오는 혼란이 이유라면 납득이 가지 않는 것은 아니지만 이 설명으로는 뭔가 명쾌하게 해결된다는 느낌이 부족했습니다.

 

관련된 내용을 좀 더 찾아보겠습니다.

 

Why are attribute names "class" and "for" discouraged?

 

이슈를 뒤져보니, 과거에도 저와 비슷한 궁금증을 가진 분께서 올리신 이슈가 존재했습니다. JSX 컴파일러는 class와 for 등을 변환된 객체의 키로 사용할 수 있을 정도로 똑똑한데, 굳이 className, htmlFor 같은 이름을 사용할 필요가 있느냐는 질문입니다.

 

페이스북 팀에서 리액트를 개발하고 있는 Paul O’Shannessy(@zpao)는 이 문제에 대해 

 

우리의 DOM 컴포넌트들은 자바스크립트의 API를 사용하기 때문에, 자바스크립트를 기준으로 하는 프로퍼티 이름을 사용하기로 정했습니다. 그러니 당신의 컴포넌트에서 class와 for 등을 사용하셔도 좋습니다.

 

라고 답변하였습니다.

 

이후에 비슷한 질문의 다른 이슈에서도 당시 리액트를 개발하던 Brandon Dail(@aweary)은 

 

"class"와 "className"을 모두 지원하는 것이 합리적이지만 React 코드베이스를 일관성 있게 유지하기 위해 더 많은 오버헤드가 발생한다는 단점이 있습니다. 하지만 이는 린트 도구를 사용하여 완화할 수 있습니다. 이것이 지원되는 브라우저에 문제를 일으키지 않는 한(IE8은 props.class에 액세스할 때 throw되지만 더 이상 문제가 되지 않음) 저는 허용하는 데 찬성합니다.

 

와 같은 의견을 밝혔고, 곧 리액트 16이 발표되면서 class와 for도 드디어 사용할 수 있게 되었습니다. 그래서 경고가 발생하면서도 class와 for을 사용할 수 있었던 것이네요. 이제야 완벽히 납득할 수 있는 흐름을 찾은 것 같습니다.

 

"DOM을 렌더링하는건 좀 더 관대해져도 된다고 생각합니다."

 

 

 

마무리

오늘은 JSX가 왜 생겨나게 되었고, 리액트에서 어떻게 작동할 수 있는지 알아보았습니다. 그러면서 생겼던 의문점에 대한 해결도 할 수 있었네요.

 

결론적으로 JSX에서 class와 for은 쓰셔도 괜찮습니다. 다만 작동을 하는 코드라고 해서 많은 사람들이 사용하고, 린트가 바꾸기를 권유하는 className과 htmlFor을 굳이 class나 for로 고칠 필요는 없어보입니다. 따라서 코드를 작성하시는 분들에게는 되도록이면 className과 htmlFor을 사용하시는 것을 추천드립니다. 

 

알려진 속성에 대해서는 그것을 사용하는게 좋습니다.

 

참고 문서

JSX 소개 - React docs

JSX 이해하기 - React docs

JSX 없이 사용하는 React - React docs

Dom Attributes in React 16 - Dan Abramov

Why are attribute names "class" and "for" discouraged? - React issue

ClassName and Class inconsistency - React issue

React Fire: Modernizing React DOM - React issue

 

반응형

댓글