본문 바로가기

2024-05-31 리액트에서의 기본 문법 사용법

codeConnection 2024. 5. 31.

JSX

프로젝트 셋업에서 가장 먼저 눈에 띄는 것은 App.jsx와 같은 파일들이다.

이것은 jsx라는 문법을 사용하고, 콩글리쉬로는 '작스'라고 부른다.

안의 내용을 보면 자바스크립트와 HTML이 혼합되어 있는 형태처럼 보인다. 그런데 실제로 HTML은 아니고, HTML과 거의 흡사하게 생긴 JSX라는 코드다.

function App() {
    return (
        <div>
            Hello <b>World!</b>
        </div>
    );
}

위 코드를 보면 자바스크립트 함수 안에 return 키워드 내부에 HTML을 사용 중인 것처럼 보인다. 위 코드는 사실 리액트에서는 아래와 같이 해석한다.

function App() {
    return React.createElement("div", null, "Hello", React.createElement("b", null, "world));
}

별 것도 아닌 코드를 저렇게 어렵게 쓸 필요가 없게 해주는 것이 JSX 문법이다. JSX 문법에서는 자바스크립트와 HTML을 동시에 사용할 수 있도록 도와준다. 물론 엄연히 진짜 자바스크립트나 HTML과는 약간의 차이는 있다.

jsx 문법에서는 함수형 컴포넌트 내에서 자바스크립트 함수를 작성하고, return문 내부에서 화면을 렌더링 할 마크업을 한다.

JSX가 렌더링 되는 원리 (index.jsx)

// index.jsx

ReactDOM.render(
    <React.StrictMode>
        <App/>
    </React.StrictMode>,
    document.getElementById('root')
);

index.jsx 파일을 보면 ReactDOM.render라는 함수가 보이는데, 첫번째 매개 변수로는 페이지에 렌더링 할 내용을 JSX 형태로 작성하는 것이고, 두번째 매개 변수로는 해당 JSX를 렌더링할 document의 DOM 요소를 설정하는 것이다.

root라는 id가 있는 곳에 App 컴포넌트를 렌더링 해준다는 의미이다.

React.StrictMode는 리액트의 레거시 기능들을 사용하지 못하도록 엄격 모드를 설정해주는 것. 새로 배우는 초보자 입장에서는 레거시 기능을 사용할 일이 없지만, 또 그렇기 때문에 이것이 의도치 않은 에러를 발생시키면 어디서 문제가 발생한지 찾기 어려울 수 있으니 이 부분은 지워도 좋다.

return문은 하나의 요소로 무조건 감싸주어야 함.

// 에러나는 코드
function App() {
    return(
        <h1>Hello, World!</h1>
    );
}

// 에러 안 나는 코드
function App() {
    return(
        <>
            <h1>Hello, World!</h1>
        </>
    );
}

<><div> 대신 만든 건데, div로 CSS를 만들 거면 div로 감싸줘도 되지만 그럴 필요가 없는 경우에는 의미없는 div를 만든 것이기에 fragment라는 빈 태그로 감싸주면 됨.

div든 프래그먼트 등 하나로 감싸주어야 하는 이유는 virtual DOM에서 컴포넌트의 변화를 감지해낼 때 그것이 효율적이기 때문에 규칙으로 만들어두었기 때문임.

자바스크립트 표현식 사용하기

{} 중괄호로 감싸 자바스크립트 표현식을 사용할 수 있음.

function App() {

    const name = '장원영';

    return(
        <>
            <h1>안녕하세요? {name}입니다.</h1>
        </>
    );
}

export default App;

var, let, const

var는 레거시 문법으로, 자바스크립트 ES6에서 도입된 const 사용이 권장된다.

var는 스코프가 함수 단위이고, let과 const는 블록 단위이다. var는 스코프가 함수 단위인데다가 변수의 재선언도 가능하여 의도치 않은 에러를 발생시킬 수 있다.

// var의 이상한 점
function App() {

    var name = '장원영';

    if(true) {
        var name = '안유진';
        console.log(name); // '안유진'
    }

    console.log(name); // '안유진'

    return(
        <>
        </>
    );
}

// let과 const
function App() {

    let name = '장원영';

    if(true) {
        let name = '안유진';
        console.log(name); // '안유진'
    }

    console.log(name); // '장원영'

    return(
        <>
        </>
    );
}

var는 function App 함수 전체가 스코프라 전체가 통하기 때문에 if문 내부에서도 외부와 통한다.

그러나 letconst는 if문 외부와 내부는 통하지 않는다.

그리고 const는 값의 재할당이 불가능하기 때문에 의도치 않은 재할당으로 에러가 나는 것을 막을 수 있으므로, 잘 모르겠다면 일단 const로 상수를 선언하고, 값의 동적 할당이 필요하다고 판단되면 let으로 바꾸는 경험을 계속 해보는 것이 좋다.

JSX 내부(return문)에서는 자바스크립트 표현식만 사용 가능

자바스크립트 표현식(expression)과 문(statement)을 명확히 구분하여야 한다.

표현식이란, '값'으로 나타낼 수 있는 것들을 의미함. 즉 어떠한 '값'을 반환(return)하는 것들을 말한다.

그리고 문(statement)는 값으로 평가되지는 않고 어떠한 동작을 수행하는 것들을 말한다.

let x = 5;
// x가 표현식. 5라는 값으료 표현됨.
// let은 문. 변수를 선언하는 동작을 수행함.
// 이는 jsx에서 아래와 같이 사용 가능
<span> 나이 : {x}살 </span>

// for 반복문도 동작을 수행하는 것이지 값을 return하는 표현식이 아니라
// jsx에서 사용 불가능함. while 반복문도 마찬가지
for (let i = 0; i < 10; i++) {
  // 반복할 동작
}

// 단 map과 filter는 새로운 배열을 반환하기 때문에 표현식임.
const arr = [1, 2, 3, 4];
const doubled = arr.map(x => x * 2); // [2, 4, 6, 8]

const arr = [1, 2, 3, 4];
const doubled = arr.filter(x => x % 2 === 0); // [2, 4]

// if문이나 if...else 문은 return하는 값이 있는 게 아니라
// 참일 때 ~~ 동작 수행, 거짓일 때 ~~ 동작 수행이라
// 문(statement)임.
// 대신 값을 return하는 삼항 연산자는 표현식이기에 사용 가능함.
{age >= 18 ? '성인', '미성년자'}

논리곱연산자(&&)를 통한 조건부 렌더링

특정 조건일 때 내용을 보여주고(렌더링을 하고), 만족하지 않을 때는 렌더링하지 않게 하고 싶을 때가 있다. 이럴 때 조건부 연산자를 사용하면 된다.

논리합(||), 논리곱(&&)이라는 조건부 연산자를 사용하는 방법을 자바스크립트에서는 단축 평가(Short-Circuit Evaluation)라고 하는데, 리액트에서도 표현식에서 유용하게 사용하니 개념을 확실히 이해하는 것이 좋다.

// 삼항 연산자를 통한 조건부 렌더링
function App() {
    const name = '장원영';
    return
        <div>
            {name === '장원영' ? <span>{name}입니다.<span> : null};
        </div>

그리고 조건에 맞지 않아 아예 안 보여주고 싶으면 null을 렌더링 하면 됨. 클릭했을 때 토글하는 모달창 같은 것에도 삼항 연산자로 조건부 렌더링을 구현할 수 있음.

그러나 논리곱 연산자를 활용하면 더 짧은 코드로 구현 가능.

// 논리곱 연산자(&&)를 통한 조건부 렌더링
function App() {
    const name = '장원영';
    return
        <div>
            { name === '장원영' && <span>{name}입니다.</span> }
        </div>

이게 가능한 이유는 논리곱 연산자는 a && b 일 때

  1. 좌항(name === '장원영')이 true면 우항을 반환함.
  2. 좌항이 false면 좌항인 false를 반환함.

그런데 리액트는 false는 null 처럼 렌더링 하지 않으므로 화면에는 아무것도 렌더링 되지 않는다.

단축평가 원리 이해

  • 논리곱 연산자(&&) : 거짓이 우선.

    • 거짓을 만나면 멈춘다.
    • 첫번째 피연산자가 falsy한 값이면 다음 피연산자를 무시하고 falsy한 첫번째 피연산자를 반환.
  • 논리합 연산자(||) : 참이 우선.

    • 참을 만나면 멈춘다.
    • 첫번째 피연산자가 truthy한 값이면 다음 피연산자를 무시하고 truthy한 첫번째 피연산자를 반환.

두 조건부 연산자 모두 피연산자들에서 좇는 값이 없을 경우 마지막 피연산자를 반환함.

JSX를 괄호로 감싸는 경우와 아닌 경우

  • 한 줄로 표현가능 할 때 : 감싸지 않음.
  • 여러 라인으로 표현해야 할 때 : 감쌈.

그러나 이것은 컨벤션이고 의무가 아님.

undefined 렌더링 금지

리액트에서는 함수 컴포넌트에서 undefined가 렌더링 되면 에러가 발생함. JSX에서 표현식으로 호출하는 경우에는 상관없음.

function App() {

    let name;

    return name;
} // 에러

function App() {

    let name;

    return {name};
} // 아무것도 출력 안 됨.

아래와 같이 개선할 수 있다.

function App() {

    let name;

    return <div>name || '값이 undefined 입니다.';</div>
}
// 논리합 연산자는 truthy한 값을 반환함. name은 undefined로 falsy 한 값이니
// 우항인 string이 truthy한 값이라 좌항이 undefined면
// 출력할 메시지를 논리합 연산자로 설정해줄 수 있음.

인라인 스타일링

리액트에서 DOM 요소에 CSS를 인라인으로 적용시킬 때는 string 타입이 아니라 객체 타입으로 적용시켜줘야 함.

또한 - dash가 들어간 스타일 속성들은 자바스크립트 명명 규칙처럼 camelCase로 작성해야 함.

// 스타일을 미리 선언
function App() {

    const name = '장원영';

    const style= {
        backgroundColor: 'red',
        fontSize: '48px'
        padding: 16
    }

    return (
        <>
            <div style={style}>{name}</div>
        </>
    );
}

// 인라인 스타일링
// 스타일을 미리 선언
function App() {

    const name = '장원영';

    const style= 

    return (
        <>
            <div style={
                {
                   backgroundColor: 'red',
                    fontSize: '48px'
                    padding: 16
                }}> {name}</div>
        </>
    );
}

class(X), className(O)

리액트 v16 이상부터는 DOM 요소에 class를 주면 에러가 난다. 자바스크립트 class 키워드와 중복되기 때문인데 className으로 클래스를 명명해야 한다.

닫는 태그는 무조건 있어야 함

HTML에서는 닫는 태그를 작성하지 않아도 렌더링이 잘 되나, 리액트에서는 닫는 태그가 있는 태그들은 무조건 닫아줘야 한다.

// HTML
// 에러 안 남
<input>
<button>작성하기</button>

// JSX
// 에러 안 나게 하려면
<input></input>
// 또는 위처럼 열고 닫는 태그 사이에 내용물이 없는 경우에는
// 아래처럼 self-closing 태그 작성 가능.
<input/>

주석 달기

function App() {
    // 여기서는 이렇게 주석 닮

    return (
        <>
            {/* 여기서는 이렇게 주석 닮 */}
        </>
    );
}

ESLint로 문법 오류 예방하기

ESLint extension을 설치해서 주석 에러 같은 것을 예방할 수 있다.

초록색 줄은 경고이니 무시해도 되는데 확인은 해보면 좋고,

빨간색 줄은 에러이니 고쳐야만 한다.

prettier로 컨벤션 지키기

문이 끝나는 곳에서 세미콜론을 넣는다든지, 들여쓰기를 아름답게 한다든지, 작은 따옴표가 아닌 큰 따옴표로 string을 표현한다든지

이런 에러가 아닌 컨벤션을 파일 저장 시 자동으로 맞추어 준다.

그리고 팀의 컨벤션에 따라 .prettierrc 파일에서 이를 커스터마이징 해줄 수 있다.

// .prettierrc
{
    "singleQuote": true, // 큰 따옴표가 아닌 작은 따옴표로 통일
    "semi": true, // 세미콜론 언제나 붙이도록 설정
    "useTabs": false, // 들여쓰기 할 때 tab을 안 쓰고
    "tabWidth": 2 // 들여쓰기에는 공백 두 칸 적용
}

shift + command + p로 설정을 열고 format on save를 검색하여 체크해주면 파일 저장 시 자동으로 코드 라인을 맞춰 줌.

의도를 갖고 코드 라인을 수동으로 정리 상황이 오면 prettier는 성가실 수 있음. 코드 정리만 본인이 잘 해내면 설치 안 해도 됨.

댓글