JSX 사용규칙

date
Apr 3, 2023
slug
jsx-rules
author
status
Public
tags
React
summary
type
Post
thumbnail
category
updatedAt
Apr 7, 2024 02:52 PM

React를 사용하기 전에 알고있어야 할 Babel과 JSX

Babel

자바스크립트 트랜스파일러(transpiler).
트랜스파일(transpile)은 다른 실행 환경에서도 돌아갈 수 있도록 같은 언어를 유지한체 소스 코드의 형태만 바꾸는 과정을 의미합니다.
React는 JSX라는 특수한 문법을 사용해 코드를 작성하기 때문에 개발자가 작성한 원본 코드는 브라우저에서 제대로 실행이 되지 않습니다. 따라서 Webpack bundler와 Babel로더를 통해 React프로젝트를 빌드합니다.

JSX

JSX(JavaScript XML)는 Javascript에 XML을 추가한 확장한 문법입니다. 
브라우저에서 실행하기 전에 바벨을 사용하여 일반 자바스크립트 형태의 코드로 변환됩니다.
// 실제 작성할 JSX 예시 functionApp() { return ( <h1>Hello, GodDaeHee!</h1> ); } // 위와 같이 작성하면, 바벨이 다음과 같이 자바스크립트로 해석하여 준다. functionApp() { return React.createElement("h1",null, "Hello, GodDaeHee!"); }
- JSX는 하나의 파일에 자바스크립트와 HTML을 동시에 작성하여 편리합니다.
- 자바스크립트에서 HTML을 작성하듯이 하기 때문에 가독성이 높고 작성하기 쉽습니다.
Babel이 JSX를 JS로 변환한 결과물을 살펴보자 → 🔎Link

component는 대문자로 시작해야 한다.

React 컴포넌트 이름은 항상 대문자로 시작해야 하고 HTML 태그는 소문자로 시작해야 합니다.
트랜스파일링 된 결과물이 다른 것을 살펴볼 수 있습니다.
컴포넌트 이름 첫 글자를 대문자로 썼을 때
export default function MyApp() { return ( <div> <Test /> </div> ); }
import { jsx as _jsx } from "react/jsx-runtime"; export default function MyApp() { return /*#__PURE__*/_jsx("div", { children: /*#__PURE__*/_jsx(Test, {}) }); }
컴포넌트 이름 첫 글자를 소문자로 썼을 때
export default function MyApp() { return ( <div> <test /> </div> ); }
import { jsx as _jsx } from "react/jsx-runtime"; export default function MyApp() { return /*#__PURE__*/_jsx("div", { children: /*#__PURE__*/_jsx("test", {}) }); }
ide에서 뜨는 경고 메세지
notion image

tip - 함수명이 대문자일 필요는 없음

React 컴포넌트는 항상 대문자로 시작해야 하지만, 함수명이 대문자일 필요는 없습니다. 그러나 JSX 안에서 컴포넌트가 사용될 때에는 반드시 대문자로 시작해야 한다는 것에 유의하세요.
  • default export의 경우:
    • import시에 대문자로 시작하는 새로운 이름을 부여 (예: DefaultProfile)
  • named export의 경우:
    • import시에 as로 대문자로 시작하는 새로운 이름을 부여 (예: NamedTwo)
    • 컴포넌트 외부에서 대문자로 시작하는 새로운 변수에 할당 (예: NamedThree)
    • 컴포넌트 내부에서 대문자로 시작하는 새로운 변수에 할당 (예: NamedFour)
 

JSX 사용 규칙

- 하나의 JSX태그 반환하기

여러 개의 JSX 태그를 반환할 수 없습니다. <div>...</div> 또는 빈 <>...</> 래퍼와 같이 하나의 공유 부모로 감싸야 합니다. 왜 여러 JSX tag를 하나로 감싸줘야하나? JSX는 HTML처럼 보이지만 내부적으로는 JavaScript 객체로 변환됩니다. 하나의 배열로 감싸지 않은 하나의 함수에서는 두 개의 객체를 반환할 수 없습니다. 따라서 또 다른 태그나 Fragment로 감싸지 않으면 두 개의 JSX태그를 반환할 수 없습니다. 또한, Virtual DOM에서 컴포넌트 변화를 감지할 때 효율적으로 비교할 수 있도록 컴포넌트 내부는 하나의 DOM 트리 구조로 이루어져야 한다는 규칙이 있기 때문입니다.

tip - 마크업에 div를 추가하고 싶지 않을 때 - Fragment

마크업에 <div>를 추가하고 싶지 않다면 <></>를 사용하면 됩니다:
<> <h1>Hedy Lamarr's Todos</h1> <img src="https://i.imgur.com/yXOvdOSs.jpg" alt="Hedy Lamarr" class="photo" > <ul> ... </ul> </>
이런 빈 태그를 Fragment라고 합니다. Fragment는 브라우저상의 HTML 트리 구조에서 흔적을 남기지 않고 그룹화해줍니다.

- br과 같은 태그 닫기

- JSX는 HTML보다 더 엄격합니다. <br /> 과 같은 태그를 닫아야 합니다. img 태그처럼 자체적으로 닫는 태그도 반드시 <img />로 작성해야하며, <li> 같은 래핑 태그 역시<li>oranges</li> 형태로 작성해야 합니다.

- camelCase를 사용하기 & JSX styling

JSX는 JavaScript로 바뀌고 JSX로 작성된 속성은 JavaScript 객체의 키가 됩니다. 종종 컴포넌트 안에서 속성을 변수로 읽고 싶은 경우가 있을 것입니다. 예시) JSX에서는 stroke-width대신 strokeWidth을 사용합니다. class는 예약어이므로, React에서는 대신 해당 DOM 속성의 이름을 따서 className을 씁니다. 이런 모든 어트리뷰트는 React DOM엘리먼트에서 찾을 수 있습니다. 틀려도 걱정하지 마세요. React는 브라우저 콘솔 에서 수정 가능한 부분을 메세지로 알려줍니다. 기존 마크업에서 모든 어트리뷰트를 변환하는 것은 지루할 수 있습니다. 변환기 를 사용하여 기존 HTML과 SVG를 JSX로 변환하는 것을 추천합니다. 변환기는 매우 유용하지만 그래도 JSX를 편안하게 작성할 수 있도록 속성을 어떻게 쓰는지 이해하는 것도 중요합니다. style={{}}은 특별한 구문이 아니라 style={ }JSX 중괄호 안에 있는 일반 {}객체입니다. 스타일이 JavaScript 변수에 의존할 때 style속성을 사용할 수 있습니다. JSX에서 자바스크립트 문법을 쓰려면 {}를 써야 하기 때문에, 스타일을 적용할 때에도 객체 형태로 넣어 주어야 합니다. 이때 카멜 표기법으로 작성해야 합니다. (font-size => fontSize)
functionApp() { const style = { backgroundColor: 'green', fontSize: '12px' } return ( <div style={style}>Hello, GodDaeHee!</div> ); }
*JSX에서는 class가 아닌 className 을 사용.
 

- if문(for문) 대신 삼항 연산자(조건부 연산자) 사용하기

- if 구문과 for 루프는 JavaScript 표현식이 아니기 때문에 해당하기때문에, JSX 내부 자바스크립트 표현식에서는 사용할 수 없습니다. 그렇기 때문에 조건부에 따라 다른 렌더링 시 JSX 주변 코드에서 if문을 사용하거나,  {}안에서 삼항 연산자(조건부 연산자)를 사용 합니다.
JSX안에서 조건부 렌더링 하기
ex) 방법1 외부에서 사용
functionApp() { let desc = ''; const loginYn = 'Y'; if(loginYn === 'Y') { desc = <div>GodDaeHee 입니다.</div>; }else { desc = <div>비회원 입니다.</div>; } return ( <> {desc} </> ); }
ex) 방법2 내부에서 사용
functionApp() { const loginYn = 'Y'; return ( <> <div> {loginYn === 'Y' ? ( <div>GodDaeHee 입니다.</div> ) : ( <div>비회원 입니다.</div> )} </div> </> ); }
ex) 방법3 AND연산자(&&) 사용
// 조건이 만족하지 않을 경우 아무것도 노출되지 않는다. functionApp() { const loginYn = 'Y'; return ( <> <div> {loginYn === 'Y' && <div>GodDaeHee 입니다.</div>} </div> </> ); }
ex) 방법4 즉시실행함수 사용
functionApp() { const loginYn = 'Y'; return ( <> { (() => { if(loginYn === "Y"){ return (<div>GodDaeHee 입니다.</div>); }else{ return (<div>비회원 입니다.</div>); } })() } </> );
 

조건부 렌더링

React에서는 조건을 작성하기 위한 특별한 문법이 없습니다. (vue에서는 v-if/v-else/v-if-elsef 디렉티브를 사용) 대신 일반 JavaScript 코드를 작성할 때 사용하는 것과 동일한 기법을 사용하면 됩니다. 예를 들어, if문을 사용하여 조건부로 JSX를 포함할 수 있습니다.
let content; if (isLoggedIn) { content = <AdminPanel />; } else { content = <LoginForm />; } return ( <div> {content} </div> );
보다 간결한 코드를 원한다면 조건부 ? 연산자를 사용할 수 있습니다. if와 달리 JSX 내부에서 작동합니다: <div> {isLoggedIn ? ( <AdminPanel /> ) : ( <LoginForm /> )} </div>
else 분기가 필요하지 않은 경우 더 짧은 논리 && 구문을 사용할 수도 있습니다: <div> {isLoggedIn && <AdminPanel />} </div>

tip - &&연산자를 사용해 조건부 렌더링시 주의할 점

import Profile from './Profile.js'; const user = [ { id: 0, name: "Hedy Lamarr", imageUrl: "https://i.imgur.com/yXOvdOSs.jpg", imageSize: 90 }, { id: "Hedy Lamarr1", name: "Hedy Lamarr", imageUrl: "https://i.imgur.com/yXOvdOSs.jpg", imageSize: 90 } ]; export default function App() { return ( <> {user.map( (userInfo) => userInfo.id && <Profile user={userInfo} key={userInfo.id} /> )} </> ); }
위의 예제는 user의 id의 존재 여부에 따라 논리 연산자 &&를 사용해 조건부 렌더링을 구현한 예제입니다. JavaScript에서 0은 falsy값이므로 아무것도 렌더링이 되지 않아야 합니다. 하지만 위의 예제에서는 0이 렌더링 되어 보여집니다. 왜 그럴까요? JavaScript에서 && 연산자는 앞의 조건이 falsy한 값이라면, 해당 객체를 반환하기 때문에 위의 예제에서는 0이 반환 되어 렌더링 되는 것입니다. MDN - falsy를 참고하세요.
!!messageCount && <p>New messages</p>도 됩니다.
어떤 변수 앞의 NOT 논리 연산자 !는 바로 뒤따르는 변수가 true로 변환할 수 있는 값인 경우(truthy)에는 false를, false로 변환할 수 있는 값인 경우(falsy)에는 true를 반환합니다.
NOT 논리 연산자 !를 두 번 연속으로 작성하면(Double NOT !!) 이중부정이 되어, truthy 값은 true를, falsy 값은 false를 반환합니다.
val
판정
!val
!!val
val
판정
!val
!!val
0
falsy
true
false
0 이외의 모든 숫자
truthy
false
true
빈 문자열""
falsy
true
false
비어있지 않은 문자열
truthy
false
true
null
falsy
true
false
Symbol()
truthy
false
true
undefined
falsy
true
false
모든 참조형
truthy
false
true
따라서 숫자형인 messageCount0이 아닌 경우 !!messageCounttrue가 되어 뒤의 <p>New messages</p>를 렌더링하고, 반대로 0인 경우에는 !!messageCountfalse가 되므로 아무것도 렌더링하지 않습니다.

Javascript로 escape하기

XSS(cross-site-scripting) 공격 방지

React DOM은 JSX에 삽입된 값을 렌더링하기 전에 이스케이프합니다.
  • 이스케이프(escape)?특정 문자를 원래의 기능에서 벗어나게 변환하는 행위. 의도대로 구문 분석을 하도록 이스케이프 합니다.
  • XSS?이용자들이 보는 글에 스크립트를 주입하여 사용자의 정보(쿠키, 세션 등)를 가져가거나 비정상적인 기능을 수행하게 함. 리액트 돔은 모든 값을 이스케이프하기 때문에 HTML 본연의 태그나 스크립트 기능이 제거되어 XSS 공격을 방지합니다.
&은 &amp; <은 &lt; >은 &gt; "은 &quot; '은 &#39 띄어쓰기는 &nbsp;
중괄호를 사용하면 코드에서 일부 변수를 삽입하여 사용자에게 표시할 수 있도록 JavaScript로 “이스케이프”할 수 있습니다. 예를 들어 아래와 같이 작성시 user.name이 표시됩니다.
return ( <h1> {user.name} </h1> );
JSX 속성에서 “JavaScript로 이스케이프”할 수도 있지만 따옴표 대신 중괄호를 사용해야 합니다. 예를 들어 className="avatar""avatar" 문자열을 CSS 클래스로 전달하지만 src = {user.imageUrl} 는 JavaScript user.imageUrl 변수 값을 읽은 다음 해당 값을 src 속성으로 전달합니다.
 

화살표 함수 사용시 주의사항