앞에서 컴포넌트가 돌려주던 그 태그처럼 생긴 문법, 그게 JSX(자바스크립트 확장 문법)다. 리액트 코드를 처음 보면 자바스크립트 한복판에 HTML이 박혀 있어서 이게 문법적으로 되는 건가 싶다. 결론부터 말하면 JSX는 HTML이 아니라 자바스크립트다. 다만 사람 눈에 익은 태그 모양으로 쓰게 해줄 뿐이다. 이 정체를 알고 나면 헷갈리던 규칙들이 한 줄로 정리된다.
JSX는 HTML이 아니라 자바스크립트다. 빌드 단계에서 바벨(Babel)이라는 도구가 JSX를 평범한 함수 호출로 바꾼다. <h1>안녕</h1>은 대략 React.createElement("h1", null, "안녕")로 변환된다. 우리가 태그를 쓰는 건 이 함수 호출을 손으로 적기 귀찮으니 보기 좋은 껍데기를 씌운 것뿐이다. 그래서 JSX 자리엔 결국 자바스크립트 값이 들어가고, 변수에 담거나 함수에서 돌려줄 수도 있다.
const greeting = <h1>안녕하세요</h1>;
이 한 줄이 아무 문제 없이 돌아간다. greeting에는 화면에 그릴 요소가 담기고, 이걸 컴포넌트에서 돌려주면 '안녕하세요'가 제목으로 나타난다. 태그가 문자열이 아니라 값이라는 감을 잡는 게 첫걸음이다.
중괄호로 자바스크립트 값을 꽂는다. JSX 안에서 자바스크립트 값을 쓰고 싶으면 중괄호로 감싼다. 변수든, 계산식이든, 함수 호출이든 중괄호 안에 넣으면 그 결과가 그 자리에 박힌다.
function Welcome() {
const name = "김코딩";
return <h1>{name}님 환영합니다</h1>;
}
화면에는 '김코딩님 환영합니다'가 나타난다. 중괄호 안의 name이 그 값으로 치환된 것이다. 계산도 된다. <p>{1 + 2}개</p>라고 쓰면 화면엔 '3개'가 찍힌다. 다만 중괄호 안에는 값을 만들어내는 표현식만 들어갈 수 있다. if 문이나 for 문 같은 문장은 못 넣는다. 이 제약이 처음엔 답답한데, 조금 뒤에 우회로가 나온다.
속성 이름이 HTML과 조금 다르다. JSX는 결국 자바스크립트라, HTML에서 쓰던 속성 이름 몇 개가 자바스크립트와 겹치지 않게 바뀐다. 제일 자주 걸리는 게 class다. class는 자바스크립트 예약어라 JSX에선 className으로 쓴다.
<div className="card">내용</div>
이렇게 써야 화면의 div에 card 클래스가 붙는다. class라고 쓰면 경고가 뜨고 스타일이 안 먹는다. 처음엔 이게 왜 안 되나 싶지만, JSX가 결국 자바스크립트라 class라는 예약어를 속성 이름으로 그대로 쓸 수 없다는 걸 떠올리면 납득이 간다. 라벨의 for도 반복문 키워드와 겹쳐서 마찬가지로 htmlFor가 된다. 그 밖에 onclick은 onClick, tabindex는 tabIndex처럼 여러 단어로 된 속성은 낙타 표기법(camelCase, 중간 단어 첫 글자를 대문자로)으로 적는다. 나는 리액트를 막 시작했을 때 class로 도배해 놓고 스타일이 왜 안 먹나 한참 헤맸다. 콘솔 경고를 진작 봤으면 될 일이었다.
태그는 반드시 닫고, 부모는 하나여야 한다. HTML에선 대충 넘어가던 것도 JSX에선 엄격하다. img나 br처럼 내용이 없는 태그도 <img />, <br />처럼 슬래시로 닫아야 한다. 그리고 컴포넌트가 돌려주는 태그는 반드시 하나의 부모로 감싸야 한다. 나란히 두 개를 그냥 돌려주면 에러가 난다.
function Info() {
return (
<>
<h1>제목</h1>
<p>내용</p>
</>
);
}
제목과 내용을 나란히 돌려주고 싶은데 굳이 div로 감싸긴 싫을 때, 빈 태그 <>와 </>로 묶는다. 이걸 프래그먼트(Fragment, 조각 묶음)라 부른다. 화면에는 제목과 내용만 나타나고, 감싸는 div는 실제 DOM에 생기지 않는다. 의미 없는 껍데기 태그로 구조가 지저분해지는 걸 막아준다.
스타일은 문자열이 아니라 객체로 준다. 인라인 스타일을 줄 때도 HTML과 다르다. 문자열이 아니라 자바스크립트 객체를 넘긴다.
<p style={{ color: "red", fontSize: "20px" }}>빨간 글씨</p>
중괄호가 두 겹인 게 눈에 걸릴 텐데, 이유가 있다. 바깥 중괄호는 'JSX 안에서 자바스크립트를 쓰겠다'는 표시고, 안쪽 중괄호는 스타일 값을 담은 객체 그 자체다. 속성 이름도 font-size가 아니라 fontSize처럼 낙타 표기법으로 쓴다. 화면에는 20픽셀 크기의 빨간 글씨로 '빨간 글씨'가 나타난다. 이 두 겹 중괄호를 한 겹으로 잘못 쓰면 에러가 나니, 스타일은 객체, 그래서 중괄호 두 개라고 외워두면 편하다.
조건은 삼항 연산자나 앤드로 처리한다. 중괄호 안에 if를 못 넣으니, 조건에 따라 다르게 그릴 땐 표현식으로 바꿔 쓴다. 둘 중 하나를 고를 땐 삼항 연산자, 조건이 참일 때만 보여줄 땐 &&를 쓴다.
function Cart({ count }) {
return (
<div>
{count > 0 && <p>{count}개 담김</p>}
</div>
);
}
count가 0보다 크면 화면에 '3개 담김' 같은 문구가 나타나고, 0이면 아무것도 안 나타난다. &&는 왼쪽이 참일 때만 오른쪽을 내놓는 자바스크립트 성질을 그대로 활용한 것이다. 문장 대신 표현식으로 화면을 그린다는 감각이, JSX가 자바스크립트라는 사실과 정확히 맞물린다.
여러 줄로 쓸 땐 소괄호로 감싼다. JSX가 한 줄이면 그냥 return 뒤에 적으면 되지만, 태그가 여러 줄로 길어지면 소괄호로 감싸는 게 안전하다. 이유가 좀 얄궂다. 자바스크립트는 return 뒤에서 줄을 바꾸면 거기서 문장이 끝난 걸로 오해하고 몰래 세미콜론을 붙여버리는 버릇이 있다.
function Profile() {
return (
<div>
<h1>김코딩</h1>
<p>프론트 개발자</p>
</div>
);
}
return과 같은 줄에 소괄호를 열어두면 자바스크립트가 문장이 아직 안 끝났다고 알아채서, 여러 줄에 걸친 태그를 통째로 하나의 값으로 돌려준다. 괄호 없이 return 다음 줄에서 태그를 시작하면 화면에 아무것도 안 나오는데, 원인을 모르면 한참 헤맨다. 나도 이걸로 저녁 시간을 통째로 날린 적이 있다. 여러 줄 JSX는 소괄호로 감싼다, 이건 습관으로 굳혀두는 게 낫다.
결국 JSX는 보기 좋게 포장한 자바스크립트다. JSX의 규칙들은 따로 외울 잡다한 목록이 아니라, 이게 자바스크립트라는 한 가지 사실에서 전부 흘러나온다. 값을 꽂으려면 중괄호를 쓰고, 예약어와 겹치는 속성은 이름이 바뀌고, 태그는 값이라 하나로 묶어 돌려줘야 한다. 태그 모양에 속아 HTML을 쓰고 있다고 착각하지만 않으면, JSX는 금방 손에 익는 편한 도구가 된다.