210324_TIL : React 문답

seungyeon·2021년 3월 24일
5

TIL

목록 보기
41/64

IM: DAY 32

드디어 프론트엔드 개발의 꽃이라는 React를 배운 첫 날.
처음 배우는 개념치고는 아직까진 재미있다. 리액트는 앞으로도 계속, 자주 사용하게 될 기능인 만큼 제대로 이해하는 것을 목표로 삼았다. 오늘 알아봤던 state 개념과 내일 학습할 LifeCycle 등 리액트의 전반적인 작동원리를 머릿속에 체계적으로 정리할 수 있게 하는 것이 이번 주말까지의 목표!😎

오늘 한 일

  • React 학습
  • Twittler React (class component 방식)
  • Twittler React (functional component 방식)

🤔 묻고 답하기

React Basic

JSX 문법을 도입하게 된 이유가 무엇일까요? 어떤 장점이 있나요?

  • JSX 문법의 가장 큰 장점은 코드의 직관성이다. HTML과 JavaScript를 분리하여 개발하는 것이 기존의 개발 방법이었다. 그래서 한쪽의 코드만 봤을 때 이 코드가 정확히 어떤 동작을 하는 것인지 쉽게 알 수 없었다. 이에 유저에게 보여주고 싶은 view에 개발자가 직관적으로 도달할 수 있도록, 마치 HTML과 JavaScript가 합쳐진 것처럼 보이는 JSX가 도입되었다.

JSX를 사용하지 않고 React를 사용할 수도 있나요? (advanced)

  • JSX 문법을 사용하지 않고도 React 개발이 가능하다. React.createElement 를 이용해서 개발하면 된다.

JSX에 JavaScript 표현식을 쓰려면 어떤 방법으로 써야 하나요?

  • JSX에 JavaScript 표현식을 쓰려면 중괄호({})로 감싸주어야 한다.
function App() {
  const username = 'ㅇㅅㅇ';
  return (
    <div>{username}</div>
  )
}

import / export 구문은 어떤 식으로 사용할 수 있나요?

  • 작성한 컴포넌트는 import / export 구문을 이용해 재사용이 가능하다.
    파일 상단에서 import를 이용해 컴포넌트를 불러오고, 파일의 하단에서 export defalult 구문을 사용해 컴포넌트를 내보내는 방식으로 사용한다.
// TweetList.js
import React from 'react';

function TweetList() {
  return (
    {/*내용 생략*/}
  )
}

export default TweetList; // TweetList 컴포넌트를 내보낸다.

// App.js
import TweetList from './components/TweetList'; // TweetList 컴포넌트를 불러온다

function App() {
  return (
    <TweetList />
  )
}

export default App; // App 컴포넌트를 내보낸다.

Component와 Props

DOM을 처음 배울 때 페이지 단위로 작업하는 방법과 비교해서, 컴포넌트 단위로 개발할 때의 장점은 무엇인가요?

  • 컴포넌트 단위의 개발은 프론트엔드 개발의 모듈화를 가능하게 해준다. 컴포넌트란 UI를 구성하는 개별적인 view 단위로, 전체의 앱은 각 컴포넌트를 조립해서 만들어진다. 각 컴포넌트가 독립된 파일로 존재하기 때문에 코드의 재사용성이 높아지고 유지, 보수가 용이하다는 점이 있다.

하나의 컴포넌트에서 여러 개의 엘리먼트를 리턴할 수도 있나요? (참고: Fragments)

  • "두개 이상의 태그는 꼭 하나의 태그로 감싸주어야 한다"는 JSX 문법의 규칙 때문에 하나의 컴포넌트가 여러 개의 엘리먼트를 리턴하는 것이 불가능해보이지만, 이는 React.Fragment는 DOM에 직접적인 태그가 추가되지 않는다는 특징을 이용하면 간단하게 해결할 수 있다. (React.Fragment는 DOM에 엘리먼트를 추가하지 않고 여러 개의 엘리먼트를 묶어주는 역할을 한다.) <React.Fragment>...</React.Fragment> 또는 <>...</> 와 같이 표기할 수 있다. (후자의 경우 아직까지 지원하는 브라우저가 제한되어 있으므로 범용적인 사용을 위해서는 첫번째 방식으로 작성하는 것을 권장한다.)

<Tweet>나의 새 트윗</Tweet>이라는 컴포넌트 사용 방법이 있다고 가정할 때, 컴포넌트 내에서 나의 새 트윗이라는 문자열은 어떻게 접근할 수 있나요?

  • props.children을 이용하면 컴포넌트 태그 사이에 넣은 값을 조회할 수 있다.
    사용방법은 아래와 같다.
// App.js
return (
  <Tweet>나의 새 트윗</Tweet>
);

// Tweet.js
function Tweet({ children }) {
  return (
    <div>{children}</div>
  );
}

props를 다룰 때에 지켜야 하는 엄격한 규칙이 하나 있습니다. 무엇인가요?

  • 모든 React 컴포넌트는 자신의 props를 다룰 때 반드시 순수 함수처럼 동작해야 한다. (순수 함수란 입력값을 바꾸려 하지 않고 항상 동일한 입력값에 대해 동일한 결과를 반환하는 함수를 의미한다.)
    즉, 함수 컴포넌트나 클래스 컴포넌트 모두 컴포넌트의 자체 props를 수정해서는 안된다.

조건부 렌더링

다음 코드는 왜 잘못되었을까요? 어떻게 바르게 고칠 수 있을까요?

const tweet = <Tweet writer="김코딩">
  {
    if (nowLearning) {
      return '리액트'
    } else {
      return '배틀그라운드'
    }
  }는 늘 짜릿하네요
</Tweet>
  • jsx 문법에서는 if문이나 if else문을 사용할 수 없다. 그래서 일반적으로 if문은 && 연산자로, if else문은 삼항연산자를 대신 사용한다.
    위 코드는 다음과 같이 수정할 수 있다.
const tweet = <Tweet writer="김코딩">
  { nowLearning ? '리액트' : '배틀그라운드' }는 늘 짜릿하네요
</Tweet>
  • return문 밖에서는 JSX가 아니므로 자바스크립트 문법을 사용할 수 있다. 조건이 여러개라면 return문 밖에서 if else 문을 사용해서 걸러준 값을 변수에 할당한 뒤 return문에 변수를 이용해 넣어주는 방식을 사용할 수도 있다.

리스트와 Key

컴포넌트에서 배열의 갯수만큼 엘리먼트를 렌더링하고자 할 때, 어떤 방법으로 렌더링할 수 있나요?

  • 자바스크립트 배열의 내장함수 map() 을 사용한다. map()은 배열안에 있는 각 원소를 변환하여 새로운 배열을 만들어주는 함수이다. 우리는 이 함수를 이용해 데이터 배열을 리액트 엘리먼트로 이루어진 배열로 변환해주는 방식으로 배열을 렌더링한다.
// TweetList.js
/* 배열의 원소 한개에 해당하는 컴포넌트 */
function SingleTweet({ data }) {
  const { username, message } = data;
  return (
    <div>
      <span>{username}</span>
      <p>{message}</p>
    </div>
  );
}

/* 배열 전체를 렌더링해주는 컴포넌트 */
function TweetList( { datas } ) {
  return (
    <div>
      {
        datas.map(
          data => (<SingleTweet data={data} key={data.id} />)
        )
      }
    </div>
  )
}

컴포넌트에서 배열의 갯수만큼 엘리먼트를 렌더링할 때에 발생하는 경고의 의미는 무엇이며, 어떻게 해결할 수 있나요?


왜 이런 경고가 발생하는 걸까요?

  • 위 경고는 key 라는 props를 설정해주지 않았기 때문에 발생하는 것이다.
    리액트에서 배열을 렌더링 할 때에는 key 라는 props 를 설정해야한다. key 값은 각 원소들마다 가지고 있는 고유값으로 설정한다. (보통 배열 안의 각 원소의 고유 id값이 key 값이 된다.) 리액트에서 key 라는 props를 설정해주라고 경고 메시지까지 띄워가며 강제하는 이유는 각 고유 원소에 key 가 있어야만 배열이 업데이트 될 때 효율적으로 렌더링 될 수 있기 때문이다. 고유한 key를 가질 경우, 수정되지 않는 기존의 값은 그대로 두고 원하는 곳에 내용을 삽입하거나 삭제하는 방식으로 업데이트가 이루어진다. (만약에 배열안에 중복되는 key 가 있을 때에는 렌더링시에 오류메시지가 콘솔에 나타나게 되며, 업데이트가 제대로 이루어지지 않는다.)

State

props와 state의 차이점은 무엇인가요?

  • props는 읽기 전용, state는 읽기와 쓰기 모두 가능하다.
  • props는 부모 컴포넌트가 자식 컴포넌트에게 주는 값이다. 자식 컴포넌트에서는 부모 컴포넌트로부터 props를 받아올 뿐, 받아온 props를 직접 수정할 수 는 없다. props는 immutable하다.
  • state 는 컴포넌트 내부에서 선언하며 내부에서 값을 변경할 수 있다. 때문에 동적인 데이터를 다룰 때 state를 사용한다.

상태를 변경할 때 사용되는 this.setState는 어떻게 사용하나요?

  • 상태를 업데이트 할 때 클래스의 메서드 내부에서 this.setState 라는 함수를 사용한다.
state = {
  id: 0,
  name: 'Heeseung',
  age: 20,
}
  • setState 는, 객체로 전달되는 값만 업데이트 해준다.
    위와 같은 state 값이 있을 때 this.setState({ age: 21 }); 을 하게 된다면, id와 name은 그대로 남고, age 값만 업데이트 된다.
state = {
  id: 0,
  name: 'Heeseung',
  age: {
    koreanAge: 21,
    commonAge: 19,
  },
}
  • setState는 객체의 깊이가 깊어지면 확인하지 못한다. 때문에 깊이가 한 단계 이상일 경우 Spread syntax를 사용해주어야 한다.
    위와 같은 코드에서 state.age.commonAge만 20으로 바꾸고 싶다면 다음과 같이 작성해주어야 한다.
this.setState({
  id: 0,
  name: 'Heeseung',
  age: {
    ...this.state.age,
    commonAge: 19,
  },
});

+) 210325 추가

  • this.setState의 전달 인자로는 객체나 함수 둘 다 사용할 수 있다.
  • this.setState를 사용하는 함수 내에서 상태는 즉시 갱신되지 않는다.
  • render method 내에서 setState method를 실행시킨다면 Stack overflow 에러가 발생한다.

직접 this.state를 할당하거나, this.state의 키값을 수정하면 안되는 이유는 무엇인가요? 왜 꼭 this.setState를 사용해야만 할까요?

  • this.setState를 사용하지 않으면 컴포넌트는 새로운 값을 렌더링하지 않는다.
    state 에 있는 값을 바꾸기 위해서는, this.setState 를 무조건 거쳐야 한다. 직접 this.state를 할당하거나, this.state의 키값을 수정할 경우 리액트는 컴포넌트를 업데이트 시키지 않는다. 때문에 컴포넌트를 업데이트 시키려면 this.setState를 사용해야 한다.

React에서 이벤트를 처리할 때에 HTML에서와 다른 특징은 무엇인가요?

  • React의 이벤트 문법은 HTML과 몇 가지 차이점이 존재한다.
<button onClick={onClick}>클릭</button>
  • 이벤트 이름을 설정할 때 camelCase로 설정해주어야 한다.
    (onclick ❌ ➡ onClick ⭕, onchange ❌ ➡ onChange ⭕ 등)

  • 이벤트에 전달해주는 값은 함수이어야 한다.
    (만약 onClick={this.handleIncrease()} 와 같이 함수 실행을 전달해 줄 경우, 렌더링을 할 때마다 해당 함수가 호출되는 불상사가 벌어진다.)

    • 이벤트 핸들러 함수에 인자를 전달해야 하는 경우, 화살표 함수를 사용하거나 bind() 함수를 사용해야 한다.
const onRemove = (id) => {
  setDatas(datas.filter(data => data.id !== id));
};

// 화살표 함수 사용 방법
<button onClick={ () => onRemove(id) }>삭제</button>

// bind() 사용 방법
<button onClick={ onRemove.bind(this, id) }>삭제</button>

이벤트 처리시, 이벤트 핸들러를 다음과 같이 this 바인딩을 해줘야 하는 이유가 무엇인가요?

this.handleClick = this.handleClick.bind(this);

// 또는
<button onClick={this.handleClick.bind(this)}>
  • 메서드를 이벤트 핸들러로 등록하는 과정에서 각 메서드와 컴포넌트 인스턴스의 관계가 끊겨버리기 때문에 this.setStatethis는 더 이상 컴포넌트 인스턴스를 가르키지 않는다.* 때문에 this 가 컴포넌트 인스턴스를 가리키도록 이벤트 핸들러 함수에 bind 를 사용해서 해당 함수에서 가르킬 this 를 설정해주는 것이다.

    * 이벤트 핸들러를 this binding 없이 호출할 때에, 에러가 나는 이유

    : this는 undefined 혹은, window 객체이기 때문에 setState 함수가 존재하지 않는다.

더 공부할 것

  • styled-component

내일 할 일

  • Toy Problem
  • Twittler React (스타일 적용 수정하기)
    • 스타일 적용 수정하기
    • class component 방식 setState 부분 다시 살펴보기
    • 내가 작성한 코드의 효율성에 대해 고민해보기
  • Lifecycle 학습
  • Recast.ly 스프린트 시작 (페어 프로그래밍)

0개의 댓글