[React] 주요 개념

nerry·2022년 1월 9일
0

React Series

목록 보기
2/2

1. Hello World

2. JSX 소개

JSX?

  • JavaScript를 확장한 문법
  • React element를 생성한다.
  • React는 렌더링 로직은 UI 로직에 따름
  • UI 로직
  • 이벤트 처리 방식
  • 시간에 따라 state가 변하는 방식
  • 화면 표시 위한 데이터 준비 방식
  • React는 Component 라 부르는 느슨한 연결 유닛으로 seperate concerns
  • JSX로 JavaScript 코드 안에서 UI 관련 작업시 시각적으로 도움이 됨

How To?

표현식 포함하기

const name = 'Yeri Kim'
const element=<h1>Hello,{name}</h1>

중괄호 안에 유효한 모든 JavaScript 표현식(변수, 함수)을 넣을 수 있음

자체도 표현식이다!

JSX 표현식은 컴파일 후 JavaScript 함수 호출이 되고, 객체로 인식한다.
if, for, 변수, 함수 반환 가능

속성 정의

attribute에 따옴표로 문자열 리터럴 혹은 중괄호로 JavaScript 표현식 삽입 가능

const element = <img name='yeri' src={user.url}></img>

다만, 동일 attribute에 두 가지 동시 사용 불가
또, camelCase 명명 규칙 따름

자식 정의 : JSX 태그는 자식을 포함할 수 있음

주입 공격 방지

React DOM은 JSX에 삽입된 모든 값을 렌더링하기 전에 Escape하므로 명시적이지 않으면 주입되지 않음
모든 항목은 렌더링 전 문자열로 변환됨 → XSS(cross-site-scripting)공격 방지

객체 표현

Babel : JSX를 React.createElement() 로 컴파일 → 결과로 객체 생성함
이 객체는 React Element라 하고, 화면에서 보고 싶은 표현임
이 객체를 통해 DOM을 구성하고 최신으로 유지함

엘리멘트 렌더링

Element : 화면에 표시할 내용 기술, 일반 객체이고 쉽게 생성 가능
React Dom은 이와 일치하도록 DOM을 업데이트한다.

Dom에 element 렌더링하기

Root DOM 노드가 있음 : 이 안에 들어가는 모든 element를 관리함
→ ReactDOM.rener()로 element를 root DOM 노드에 렌더링하면 됨

ReactDOM.render(element,document.getElementById('root');

렌더링 된 element 업데이트

React element는 불변 객체, 특정 시점의 ui를 보여주는 것
→ ReactDOM.render()를 새로 호출해야 ui가 업데이트 됨 (유일)
실제로는 한번만 호출됨. state 관리

변경된 부분만 업데이트하기

ReactDOM은 이전의 element와 비교하고 수정된 부분 같이 필요한 경우에만 DOM 업데이트
→ 시간의 변화에 따라 변화하는 것을 고민하는 것보다 수많은 버그를 없앰

3. Components와 Props

Component

  • ui를 재사용 가능한 개별적 여러 조각으로 나눔
  • JavaScript 함수와 유사함 → props라 하는 임의의 입력으로 화면에 표시 기술하는 element 반환

함수 컴포넌트와 클래스 컴포넌트

function Welcome(props){
  return <h1>hello,{props.name}</h1>;

props : 속성을 나타내는 데이터
객체 인자를 받은 후 React element를 반환하므로 유효함

class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

React에겐 동일한 컴포넌트다.

컴포넌트 렌더링

사용자 정의 component로 element를 나타낼 수 있음
이 element를 발견하면 JSX attribute와 자식을 컴포넌트에 단일 객체로 전달. 이 객체가 props이다.

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

const element = <Welcome name="Sara" />;
ReactDOM.render(
  element,
  document.getElementById('root')
);

위 코드 실행시
1. element로 render() 호출
2. React는 {name:'Sara'}를 props로 Welcome component 호출
3. 이 component는 hello sara element를 반환
4. ReactDOM은 이 element와 일치하도록 DOM을 효율적으로 업데이트함
컴포넌트의 이름은 항상 대문자

컴포넌트 합성

출력에 다른 component를 참조 가능 → 세부 단계에서 동일 추상 컴포넌트 사용 가능
일반적으로 React 앱은 최상위에 단일 App 컴포넌트를 갖고 있음
그래서, 기존 → React : 작은 컴포넌트부터 상단 계층으로 올라가면서 작업 필요

컴포넌트 추출

컴포넌트를 여러 작은 것들로 나눠야 함. → 재사용하기 좋아짐

  • 더 큰 앱에서 작업시 효율이 좋아짐.
  • ui 일부가 여러 번 사용되거나 자체적으로 복잡한 경우 컴포넌트가 나음

props는 읽기 전용

props를 자체적으로 수정하면 안됨. → 입력값을 수정하지 않고 항상 동일한 입력값에 대한 동일한 결과를 반환하므로 순수함수 라고 함.

  • 규칙: 모든 React 컴포넌트는 자신의 props를 다룰시 순수함수처럼 동작해야함.

5. State and LifeCycle

함수에서 클래스로 변환하기

  1. render()라는 빈 메소드 추가
  2. 함수 내용을 안으로 옮기기
  3. props를 this.props로 변경
  • 이에 따라 local state와 lifecycle method를 사용할 수 있음

클래스에 로컬 state 추가

  1. this.props.date -> this.state.date
  2. class constructor에 this.state를 지정
this.state={date: new Date()};

→ 스스로 타이머 설정 후 매초 스스로 업데이트할 수 있어짐

생명주기 메서드를 클래스에 추가하기

컴포넌트 삭제시 사용 중이던 리소스를 확보하는 것이 중요

  • 마운팅 : DOM 렌더링 될 때 마다 타이머 설정
    componentDidMount(){}
  • 언마운팅 : DOM 삭제시 타이머 해제
    componentWillUnmount(){}
    → 생명주기 메서드
  • 로컬 state 업데이트 : this.setState()
    → React가 state 변경 시 인지하여 render()를 호출해 표시 내용 확인 후 변경 내역에 맞게 DOM 업데이트

state 올바르게 사용하기

  • 직접 수정하지 않기
    setState()를 사용하여 업데이트!
  • State 업데이트는 비동기적일 수 있음
    한꺼번에 수정하기 보다.
    this.setState((state,props)=>{}
    위와 같이 객체 보다는 함수를 인자로 사용하는 형태로 사용할 것.
  • State 업데이트는 병합
    현재 state로 병합하는데, 별도의 setState()로 독립적 업데이트가 가능함
  • 데이터는 아래로 흐른다
    로컬 or 캡슐화
    특정 컴포넌트가 유나 무상태인지 알 수 없고, 함수인지 클래스인지 관심가질 필요 없다.
    state가 소유, 설정한 컴포넌트 이외 다른 컴포넌트에서 접근 불가
    → 하양식, 단방향식 데이터 흐름임
    state로 부터 파생된 ui 또는 데이터는 트리구조에서 자신의 아래에 있는 컴포넌트에만 영향을 끼침
    → 독립적인 것.

6.이벤트 처리하기

  • camelCase 사용하기
  • JSX 사용하여 함수로 이벤트 핸들러 전달
<button onClick={activateLasers}>
  Activate Lasers
</button>
  • false를 반환해도 기본 동작 방지 불가
    반드시 preventDefault를 명시적으로 호출
  • addEventListener 호출 필요 없음
    엘리먼트 렌더링시 리스너 제공하면 됨
  • 클래스로 컴포넌트 정의시, 이벤트 핸들러를 메서드로 만들기
    onClick={this.handleClick}으로 할 경우, 메서드를 바인딩 해야 함. 반환!?
  • 이벤트 핸들러에 인자 전달하기 (e,id) 로 할 수 있음

7.조건부 렌더링

  • 현재 상태에 맞게 UI 렌더링 with 조건부 연산자
  • 엘리먼트 변수
    출력은 변하지 않되 컴포넌트의 일부를 조건부로 렌더링할 수 있음
  • 인라인으로 처리하기
  1. 논리 && 연산자로 if를 인라인으로 표현
    조건 && expression 형태로 조건이 true일때만 출력이 됨
    다만 falsy 표현식이 반환됨
    unreadMessages.length > 0 && <h2>You have {unreadMessages.length} unread messages.</h2>
  2. 조건부 연산자 if-else 인라인으로 표현하기
    condition? true:false
  3. 컴포넌트가 렌더링하는 것을 막기
    null 반환

8.리스트와 key

여러개의 컴포넌트 렌더링하기

map()을 이용하여 numbers 배열 반복 실행하기

기본 리스트 컴포넌트

순서 없는 엘리먼트 리스트를 출력하는 컴포넌트로 리팩토링할 수 있음
key를 각 항목에 넣어야 함
1. React가 어떤 항목을 crud할지 식별하는 것을 돕는다.
2. 고유성 부여 위해 지정해야 함
list의 index는 최후의 수단..! ➡️ 성능 저하, state 문제가 발생할 수 있다. 지정하지 않으면 default로 사용하긴 함

key로 컴포넌트 추출하기

주변 배열 context에서만 의미가 있다.
map() 내부에 있는 엘리먼트에 넣어줘야 한다!

key는 형제 사이에서만 고유한 값이어야 한다.

컴포넌트로 전달하진 않는다. 이용해야한다면 prop으로 명시적 전달이 필요

JSX에 map() 포함시키기

중괄호 안에 map() 함수의 결과를 인라인으로 처리하기

9.폼

제어 컴포넌트 방식 이용!

제어 컴포넌트

React state를 신뢰 가능한 단일 출처로 만들어 두 요소를 결합할 수 있다. ➡️ 이를 통해 폼에 발생하는 사용자 입력값을 제어
즉, 항상 React state에 의해 input 값이 결정됨

  • <textarea>
  • <select> with <option>
  • <file> : 비제어 컴포넌트
  • 다중 입력 제어하기 : name attribute로 제어
  • 제어되는 input nul : value prop은 의도하지 않는 한 사용자가 변경할 수 없음. 변경 가능하다면 undefined나 null일 것임.
  • 제어 컴포넌트의 대안 : 비제어 컴포넌트
  • Formik : 유효성 검사, 방문 필드 추적 및 폼 제출 처리 등이 필요하다면 선택

10. State 끌어올리기

  • State를 공유하는 일은 그 값이 필요한 컴포넌트 간의 가장 가까운 공통 조상으로 state를 끌어올려 이뤄내는 방법
  • 조상이 공유될 state를 소유하여 일관된 값을 유지하도록 함.
  • 진리의 원천은 하나만 두어야 한다.
class TemperatureInput extends React.Component {
  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
  }

  handleChange(e) {
    this.props.onTemperatureChange(e.target.value);
  }

  render() {
    const temperature = this.props.temperature;
    const scale = this.props.scale;
    return (
      <fieldset>
        <legend>Enter temperature in {scaleNames[scale]}:</legend>
        <input value={temperature}
               onChange={this.handleChange} />
      </fieldset>
    );
  }
}
class Calculator extends React.Component {
  constructor(props) {
    super(props);
    this.handleCelsiusChange = this.handleCelsiusChange.bind(this);
    this.handleFahrenheitChange = this.handleFahrenheitChange.bind(this);
    this.state = {temperature: '', scale: 'c'};
  }

  handleCelsiusChange(temperature) {
    this.setState({scale: 'c', temperature});
  }

  handleFahrenheitChange(temperature) {
    this.setState({scale: 'f', temperature});
  }

  render() {
    const scale = this.state.scale;
    const temperature = this.state.temperature;
    const celsius = scale === 'f' ? tryConvert(temperature, toCelsius) : temperature;
    const fahrenheit = scale === 'c' ? tryConvert(temperature, toFahrenheit) : temperature;

    return (
      <div>
        <TemperatureInput
          scale="c"
          temperature={celsius}
          onTemperatureChange={this.handleCelsiusChange} />
        <TemperatureInput
          scale="f"
          temperature={fahrenheit}
          onTemperatureChange={this.handleFahrenheitChange} />
        <BoilingVerdict
          celsius={parseFloat(celsius)} />
      </div>
    );
  }
}
  • Calculator의 scale, temperature이 공통 state
  • 하위 컴포넌트인 TeperatureInput을 위 공통 state로 관리
  • 관리는 Calculator에 정의된 handle~이 props로 넘어갔기 때문에 가능하다.
    하위 state를 상위로 끌어올려 동기화 시킨 것 ➡️ 하향식 데이터 흐름

문제가 있을 땐 React Developer Tools로 디버깅

합성 vs 상속

React는 강력한 합성 모델을 갖고 있고, 이를 사용해서 코드 재사용하는 것이 좋다.

컴포넌트에서 다른 컴포넌트를 담기

  • Sidebar 혹은 Dialog 같은 '박스' 역할을 하는 컴포넌트 : 어떤 자식 엘리먼트가 들어올지 예상 불가 ➡️ 특수한 children prop을 이용해 전달
function WelcomeDialog() {
  return (
    <FancyBorder color="blue">
      <h1 className="Dialog-title">
        Welcome
      </h1>
      <p className="Dialog-message">
        Thank you for visiting our spacecraft!
      </p>
    </FancyBorder>
  );
}

FancyBorder은 {props.children}을 <div> 안에 렌더링하므로 최종 출력됨.

특수화

  • 구체적인 컴포넌트가 일반적인 컴포넌트를 렌더링하고 props를 통해 구성 ➡️ 특수한 경우! Dialog를 props로 구체화하여 WelcomeDialog 컴포넌트에서 렌더링

상속?

  • 상속 계층 구조로 작성을 권장할만한 사례를 페이스북은 찾지 못함.
  • 명시적이고 안전하게 커스터마이징 할 수 있도록 모든 유연성을 제공함.
  • 여러 컴포넌트에서 재사용하기를 원한다면 모듈로 분리해라!

React로 사고하기

목업으로 시작하기

1. UI를 컴포넌트 계층 구조로 나누기

  • 모든 컴포넌트 주변에 박스를 그리고 이름 붙이기
    • 어떤 것이 컴포넌트인가?
      단일 책임 원칙 : 하나의 컴포넌트는 한 가지 일을 하는게 이상적이다. 컴포넌트가 너무 커지면 작은 하위 컴포넌트로 분리해라
  • 디자이너가 정해뒀을 수도 있으니 확인하기!
  • 주로 JSON 데이터를 유저에게 보여주니, 데이터 모델을 잘 만들면 컴포넌트 구조와 잘 연결될 것 ➡️ Information Architecturef를 가지는 경향이 있음
  • 계층구조로 나열하기

2. React로 정적인 버전 만들기

아무 동작하지 않는 버전을 만들기 ➡️ state 사용하지 않기 (데이터가 바뀌는 것에 사용)

  • 하향식이나 상향식으로 만들기
    계층을 따라 만들기. 프로젝트가 커질 수록 상향식이 개발하기 편리
  • one-way data flow는 모든 것을 모듈화하고 빠르게 만들어준다.
  1. UI state에 대한 최소한의 표현 찾아내기
    state는 상호작용을 위해 데이터 모델을 변경할 수 있는 방법을 제공함
  • 변경 가능한 state의 최소 집합 생각하기
    중복 배제 원칙. 필요로 하는 변경 가능한 state의 최소 집합을 생각해야 함. ➡️ 가장 최소로 생각하고 나머지는 필요한 때마다 계산되도록 만들기 (ex. todo 배열만, 길이는 todo.length로 가져올 수 잇음)
  • state인지 판단 질문
    1. 부모로부터 props를 통해 전달?
    2. 시간이 지나도 변하지 않나?
    3. 컴포넌트 안의 다른 state나 props를 가지고 계산 가능?
    위 세 가지 중 하나라도 맞다면 state가 아님

4. state가 어디에 있어야 할지 찾기

어떤 컴포넌트가 state를 소유할지 찾기
단방향 데이터 흐름임을 기억
1. state 기반 렌더링하는 컴포넌트 찾기
2. 공유 소유 컴포넌트 : 공유 state를 갖는 모든 컴포넌트들의 상위 한 컴포넌트 찾기
3. 항상 더 상위가 가져야 함
4. 찾지 못했다면 state를 소유하는 컴포넌트를 하나 만들어 상위 계층에 추가하기

5. 역방향 데이터 흐름 추가하기

  • 상위 컴포넌트에서 하위 컴포넌트로 추적하면서 확인하기
  • 양방향 데이터 바인딩과 비교하면 데이터 흐름을 명시적으로 보이게 해서 어떻게 동작하는지 파악할 수 있음
profile
터벅터벅 개발(은좋은)자 로그

0개의 댓글