[Pro react] 3장. DOM 추상화의 내부

리액트의 이벤트

리액트는 합성 이벤트 시스템을 구현해 리액트 애플리케이션과 인터페이스에 일관성과 고성능을 보장한다.

일관성을 위해 이 이벤트 시스템은 이벤트가 여러 다른 브라우저와 플랫폼에서 동일한 속성을 갖도록 이벤트를 정규화한다.

또한 고성능을 위해 자동으로 이벤트를 위임한다. 리액트가 이벤트 핸들러를 노드 자체에 연결하는 것은 아니다. 실제로는 단 하나의 이벤트 리스너가 문서 루트에 연결되며, 이벤트가 발생하면 리액트가 이를 적절한 컴포넌트 요소로 매핑한다. 또한 리액트는 이벤트 리스너가 언마운트될 때 자동으로 이를 제거한다.

DOM 이벤트 리스너

HTML은 태그 특성을 위한 간단하고 이해하기 쉬운 이벤트 처리 API를 제공한다. 이 API의 문제는 바람직하지 않은 부수효과가 많기 때문이다. 이 API는 전역 스코프를 오염시키며, 큰 HTML파일의 컨텍스트 안에서 추적하기 어렵고, 성능이 떨어지며, 메모리 누수의 원인이 될 수 있다.

JSX는 HTML의 이벤트 처리 API와 비슷하게 사용하기 쉽고 손쉽게 이해할 수 있는 API를 이용하지만 HTML이벤트 처리 API와 달리 바람직하지 않은 부수 효과는 제거했다. 콜백 함수는 컴포넌트 스코프이며 이벤트 위임과 자동 관리 언마운팅을 이용해 영리하게 작업을 처리한다. 그런데 원래 HTML 구현과는 몃 가지 사소한 차이점이 있다는 데 주의해야 한다. 리액트의 속성에는 카멜표기법이 적용된다. 또한 모든 브라우저와 장치에서 일관성을 유지하기 위해 여러 브라우저와 버전에 포함된 모든 변형의 하위 집합을 구현한다.

이전 칸반앱에서 에로우펀션을 활용해 onClick 이벤트 핸들러 안에 다음과 같은 인라인 함수를 추가.

onClick={            
  () => this.setState({showDetails: !this.state.showDetails})
                }
render(){
        let cardDetails;
        if(this.state.showDetails){
            cardDetails = (
                <div className="card__details">
                    {this.props.description}
                    <CheckList cardId={this.props.id}
                               tasks = {this.props.tasks}/>
                </div>
            );
        };
        return (
            <div className="card">
                <div className="card__title" onClick={
                    () => this.setState({showDetails: !this.state.showDetails})
                }
                >{this.props.title}</div>
                {cardDetails}
            </div>
        );
    }
}
이놈을 아래와 같이 바꿀수 있다.

toggleDetails(){
       this.setState({showDetails: !this.state.showDetails})
   }

   render(){
       let cardDetails;
       if(this.state.showDetails){
           cardDetails = (
               <div className="card__details">
                   {this.props.description}
                   <CheckList cardId={this.props.id}
                              tasks = {this.props.tasks}/>
               </div>
           );
       };
       return (
           <div className="card">
               <div className="card__title" onClick={
                   this.toggleDetails.bind(this)
               }>{this.props.title}</div>
               {cardDetails}
           </div>
       );
   }
}

JSX 자세히 살펴보기

JSX는 자바스크립트 코드 안에 선언적인 XML 스타일의 구문을 작성할 수 있게 해주는 리액트의 선택적 자바스크립트 구문 확장이다.

리액트의 JSX는 웹 프로젝트에 대해서는 HTML과 비슷한 XML 태그 집합을 제공하지만, 다른 XML태그 집합을 이용해 사용자 인터페이스를 작성하는 사용 사례(예:리액트와 SVG, 리액트 캔버스, 리액트 네이티브)

트랜스파일(브라우저나 서버가 코드를 해석할 수 있도록 일반 자바스크립트로 변환)을 거치면 XML은 리액트 라이브러리에 대한 함수 호출로 변환된다.

<h1>Hello World</h1> => React.createElement("h1",null,"Hello World")

JSX의 장점

JSX의 특징

=> React는

return 대문자 기입

- 모든 요소는 짝이 맞아야 한다.
```HTML

JSX의 특이점

JSX에는 다루기 까다로운 측면이 있다. 이번 절에서는 JSX로 컴포넌트를 작성할 때 경험할 수 있는 일반적인 문제에 대처하는 간단한 기법과 팁, 전략에 대해 알아보쟈

단일 루트 노드 리액트 컴포넌트는 단일 루트 노드만 렌더링 할 수 있다. 이러한 제한이 있는 이유를 알아보기 위해 다음 render 함수의 return문을 살펴보자.

  return(
    <h1>Hello World</h1>
  )

  // 이는 다음과 같이 변화된다.
  return React.createElement("h1", null, "Hello World");

  // 그러나 다음과 코드는 유효하지 않다.
  return(
    <h1>Hello World</h1>
    <h2>Hello World</h2>
  )

정확히 말하면 이것은 JSX의 제한이 아니라 자바스크립트의 특징이다. return문은 단일 값만 반환할 수 있지만, 이 코드는 두 개의 문을 반환하려고 한다. 해결책은 아주 간단한데, 일반 자바스크립트와 마찬가지로 모든 반환값을 루트 객체 하나에 래핑하면 된다. 예를 들어 다음과 같이 작성할 수 있다.

return(
  <div>
    <h1>Hello World</h1>
    <h2>Hello World</h2>
  </div>
)

// 위 코드는 다음과 같이 변환한다면,
return React.createElement("div", null,
React.createElement("h1", null, "Hello World"),
React.createElement("h2", null, "World"),
)

즉, 단일 값을 반환하는 유효한 자바스크립트 코드다.

조건 절

if문은 JSX와는 잘 어울리지 않지만 이는 JSX의 제한이 아니라 JSX가 사실은 일반 자바스크립트이기 때문이다. 이해하는 데 도움이 되도록 JSX가 일반 자바스크립트로 변환되는 방법을 다시 확인해보자.

다음과 같은 JSX가 있다고 가정해보자.

return(
  <div className="salutation">Hello JSX</div>
)

그런데 다음과 같이 JSX중간에 IF절을 넣었다고 가정해 보자.

 <div className={if(condition) {"salutation"}}>
 Hello JSX
 </div>

이렇게 되면 에러가 발생한다. 어떻게 해결하겠는가??

JSX안에 if문을 사용할 수 없지만 삼항식을 이용하고 조건에 따라 변수에 값을 할당하는 방법과 같은 해결책이 있다.

리액트는 null과 정의되지 않은 값을 인식하며 JSX에서 이스케이프 처리할 경우 아무것도 출력하지 않는다.

<해결 방안=""> 1. 삼항식 이용 ```javascript render(){ return( <div className={condition ? "salutation" : ""}> Hello JSX </div> ) } ``` 삼항식은 조건에 따라 전체 노드를 렌더링하는 경우에도 잘 동작한다. ```
{condition ? Hello JSX : null} Hello JSX
``` 2. 조건을 밖으로 이동 삼항식으로 문제를 해결할 수 없을 때는 조건절을 JSX안쪽에서 바깥쪽으로 옮기는 방법이 있다. ```javascript render(){ return( <div className={condition ? "salutation" : ""}> Hello JSX </div> ) } ``` 이 놈을. ```javascript let className; if(condition){ className="salutation"; } render(){ return( <div className={condition}> Hello JSX </div> ) } ``` 리액트는 정의되지 않은 값을 처리하는 방법을 이해하며, 조건이 false일 경우 div 태그 안에 클래스특성을 생성하지 않는다. #### 칸반 앱: 카드가 열려있는지 여부 확인 ```javascript return (
<div style={sideColor}/> <div className={ this.state.showDetails?"card__title card__title--is-open" : "card__title" } onClick={ this.toggleDetails.bind(this) }>{this.props.title}
{cardDetails} </div> ); // =>CSS .card__title:before { display: inline-block; width: 1em; content: '▸'; } .card__title--is-open:before { content: '▾'; } ``` #### 공백 HTML의 경우 브라우저는 일반적으로 여러 행의 요소 간에 공백을 출력한다. 반면 리액트의 JSX는 분명한 지시가 있을 때만 공백을 출력한다. 예를 들어, 다음 JSX는 행 사이에 출력하지 않는다. 명시적으로 공백을 삽입하려면 빈 문자열{""}을 포함하는 식을 이용한다. #### JSX의 주석 JSX는 HTML이 아니므로 HTML주석을 지원하지 않는다. 단, 이렇게 사용가능하다. ```javascript let content = ( ); ``` #### 동적 HTML 렌더링 리액트에는 XSS 공격방지 기능이 기본적으로 내장돼 있다. 즉,HTML태그를 동적으로 생성하고 JSX에 추가하는 작업을 기본적으로 금지하낟. 이 기본 설정은 보안을 위해서는 바람직하지만 HTML을 동적으로 생성해야 하는 경우도 있다. 데이터를 마크다운 포맷으로 인터페이스를 렌더링하는 경우를 예를 들어보자 일단 마크드(Marked)라는 것을 설치하고 마크다운을 HTML로 변환한다. ```javascript // CARD.js render() { let cardDetails; if (this.state.showDetails) { cardDetails = (
<span dangerouslySetInnerHTML= /> <CheckList cardId={this.props.id} tasks={this.props.tasks} />
); } ``` #### 인라인 스타일링 JSX로 리액트 컴포넌트를 작성하는 것은 같은 파일 안에 UI정의와 상호작용을 경합하는 것. 앞에서 설명한 것 처럼 관심사를 분리하려면 각 관심사에 대해 잘 캡슐화되고, 독립적이며, 재사용 가능한 컴포넌트를 이용해야 한다. 그런데 사용자 인터페이스의 경우에는 콘텐츠와 상호작용 외에도 스타일을 고려해야 한다. 리액트는 자바스크립트를 이용한 인라인 스타일일을 지원하는데, 장점으로 - 셀럭터 없이 스타일의 범위지정 기능 - 특정성충돌이 예방 - 소스 순서에 관계없음 #### 폼 처리 리액트에서는 컴포넌트의 상태가 변경될 때마다 컴포넌트를 다시 렌더링해야 하므로 컴포넌트의 내부 상태를 최소환으로 유지한다. 리액트가 컴포넌트를 다시 렌더링하는 이유는 자바스크립트 코드상의 컴포넌트 상태를 정확하게 나타내고 인터페이스의 동기화를 유지하기 위해서다. 따라서 사용자가 상호작용하면 상태가 변경되는