본문 바로가기
  • GDG on campus Ewha Tech Blog
3-1기 스터디/웹개발 기초

[웹개발 기초 스터디] React(1): Part2

by 한비 2021. 12. 24.
 

React

 

www.youtube.com


컴포넌트 만들기 (이어서)

 

Props

  • 태그의 이름과 속성을 통해 재사용성이 높은 문법이 만들어짐 → 컴포넌트 태그를 여러 번 사용할 때 각각 다른 결과를 출력할 수 있도록 하고싶음
  • 리액트 공식 문서의 Components and Props 항목 참고하기 - 아래는 props 권장 사용법
class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}
  • props를 이용해 App.js 의 Subject, Content 컴포넌트 수정하기 
class Subject extends Component {
  render(){
    return(
      <header>
        <h1>{this.props.title}</h1>
        {this.props.sub}
      </header>
    );
  }
}

class Content extends Component {
  render() {
    return(
      <article>
        <h2>{this.props.title}</h2>
        {this.props.desc}
      </article>
    );
  }
}

class App extends Component {
  render(){
    return (
      <div className="App">
        <Subject title="WEB" sub="world wide web!"></Subject>
        <Subject title="React" sub="For UI"></Subject>
        <TOC></TOC>
        <Content title="HTML" desc="HTML is HyperText Markup Language."></Content>
      </div>
    );
  }
}

 

React Developer Tools

  • 사용설명서는 너무 장황하거나 빈약함 → 원하는 것을 스스로 알아내는 능력이 필요함
  • 독립하는 데 필요한 것 1. 사용설명서를 볼 줄 아는 것 2. 현재 상태를 측정하고 분석할 줄 아는 것 + 질문과 검색을 통해 이루어내기
  • 현재의 상태를 알아내는 도구: React Developer Tools - 사용하는 브라우저의 확장판을 다운받기

크롬 확장판

  • 웹 브라우저에서 우클릭 - 개발자 도구 - element 탭은 웹 브라우저가 이해한 코드고 실제 작성한 코드는 다름 → react 탭을 누르면 우리가 실제로 작성한 component들을 보여줌/Props도 보여주고 실시간으로 값을 수정할 수 있음

 

Component 파일로 분리하기

  • src 디렉토리 안에 components 디렉토리를 만들고 각각의 컴포넌트를 파일로 정리하기
    1. components 디렉토리 안에 컴포넌트명.js 파일 생성
    2. 생성한 파일에 해당하는 컴포넌트의 코드 복사 붙여넣기
    3. 코드 상단에 import React, {Component} from 'react'; 추가하기
    4. 코드 하단에 export default 컴포넌트명; 추가하기
    5. App.js에 import 컴포넌트명 from "./components/컴포넌트명"; 추가
  • Content 컴포넌트의 파일 분리 예시 - Content.js
import React, {Component} from 'react';

class Content extends Component {
  render() {
    return(
      <article>
        <h2>{this.props.title}</h2>
        {this.props.desc}
      </article>
    );
  }
}

export default Content;

 


State와 key

State 소개

컴포넌트의 구조

  • Props: 사용자가 제품을 조작하는 장치 - 사용자가 컴포넌트를 사용하는 입장
  • State: 제품의 내부적 구현을 위한 메커니즘 - props의 값에 따라 내부 구현에 필요한 값
  • 컴포넌트가 좋은 부품이 되기 위해서는 외부의 props와 내부에 실제로 구현한 state가 철저히 구분되어야 함

 

State 사용

  • 외부에서 알 필요 없는 정보를 은닉하기 위해 사용 - app이 내부적으로 사용할 상태는 state로 사용
  • 컴포넌트의 초기화 - render()보다 먼저 실행되면서 그 컴포넌트를 초기화시켜주고 싶은 코드를 constructor() 안에 작성
  • 문자열이 아닌 코드로 실행하고 싶은 경우 중괄호 {}로 묶어서 작성
  • 상위 컴포넌트인 App의 상태를 하위 컴포넌트로 전달하고 싶을 때는 상위 컴포넌트의 state 값을 하위 컴포넌트의 props 값으로 전달하면 됨
class App extends Component {
  constructor(props){
    super(props);
    this.state = {
      subject:{title:'WEB', sub:'World Wide Web!'}
    }
  }
  render(){
    return (
      <div className="App">
        <Subject 
        title={this.state.subject.title} 
        sub={this.state.subject.sub}>
        </Subject>
        <TOC></TOC>
        <Content title="HTML" desc="HTML is HyperText Markup Language."></Content>
      </div>
    );
  }
}

 

key

  • 부모인 App의 입장에서는 state라는 내부 정보를 사용했고 자식한테 그것을 전달할 때는 props를 통해 전달함
  • APP의 입장에서는 TOC이 내부적으로 어떻게 돌아가는지 알 필요가 없음 - data라는 props로는 어떤 형태의 정보를 전달하면 되는가라는, 사용자 입장에서 알아야 하는 것만 알면 됨
//App.js
class App extends Component {
  constructor(props){
    super(props);
    this.state = {
      subject:{title:'WEB', sub:'World Wide Web!'}
      contents:[
        {id:1, title:'HTML', desc:'HTML is for information'},
        {id:2, title:'CSS', desc:'CSS is for design'},
        {id:3, title:'JavaScript', desc:'JavaScript is for interaction'}
      ]
    }
  }
  render(){
    return (
      <div className="App">
        <Subject
        title={this.state.subject.title} 
        sub={this.state.subject.sub}>
        </Subject>
        <TOC data={this.state.contents}></TOC> //자식에게는 props로 넘겨줌
        <Content title="HTML" desc="HTML is HyperText Markup Language."></Content>
      </div>
    );
  }
}

//TOC.js
class TOC extends Component {
  var lists [];
  var data = this.props.data;
  var i=0;
  while(i<data.length){
    lists.push(<li key={data[i].id}><a href={"/content/"+data[i].id}>{data[i].title}</a></li>);
    i = i + 1;
  }
  render() {
    return(
      <nav>
        <ul>
            {lists}
        </ul>
      </nav>
    );
  }
}

 


이벤트

  • 목표: WEB 글자에 링크를 걸어 링크를 누르면 본문에 welcome이 보이게 하기 / 다른 링크를 누르면 각각에 해당하는 본문이 보이도록 하기 → App 컴포넌트의 state를 바꿔 그 state가 Content 컴포넌트의 props에 전달되어 동적으로 변경되도록 함 - 리로드 없이 변경 가능

 

이벤트 state props 그리고 render 함수

  • 리액트에서 props나 state 값이 바뀔 경우 그 state를 갖고 있는 컴포넌트의 render 함수가 다시 호출됨 - 그 하위의 컴포넌트들의 render 함수도 다시 호출됨 → 값이 바뀌면 화면이 전부 다시 그려짐
  • render 함수는 어떤 html을 그릴 것인가를 결정함
//App.js
class App extends Component {
  constructor(props){
    super(props);
    this.state = {
      mode:'welcome',
      subject:{title:'WEB', sub:'World Wide Web!'},
      welcome:{title:'Welcome', desc:'Hello, React!!'},
      ...
    }
  }
  render(){
    var _title, _desc = null;
    if(this.state.mode === 'welcome'){
      _title = this.state.welcome.title;
      _desc = this.state.welcome.desc;
    } else if(this.state.mode === 'read'){
      _title = this.state.contents[0].title;
      _desc = this.state.contents[0].desc;
    }
		...
}

 

이벤트 설치

  • 자바스크립트는 onclick()을 사용하고 큰따옴표“”로 묶지만 리액트는 onClick()을 사용하고 중괄호{}로 묶음
  • onClick으로 이벤트를 설치해둔 경우 이벤트가 실행될 때(함수가 호출될 때) 리액트는 함수의 첫번째 파라미터의 값으로 이벤트라는 객체를 주입해줌
  • onClick은 기본적으로 클릭 이벤트가 발생하면 링크를 리로드하도록 동작함
  • debugger;: 크롬 개발자 도구를 켜둔 경우 이 코드를 만나면 실행을 멈추고 sources라는 페이지로 이동해 여러가지 정보를 보기 쉽게 함
  • e.preventDefault(): 이벤트가 발생한 태그의 기본적인 동작방법을 못하게 막음 → 이를 이용해 클릭해도 페이지가 전환되지 않도록 할 수 있음
return (
      <div className="App">
        <header>
          <h1><a href="/" onClick={function(e){
            console.log(e);
            e.preventDefault();
          }}>{this.state.subject.title}</a></h1>
          {this.state.subject.sub}
        </header>
        <TOC data={this.state.contents}></TOC>
        <Content title={_title} desc={_desc}></Content>
      </div>
    );

 

이벤트에서 state 변경하기

  • 이벤트 함수 안에 바로 this.state.mode = 'welcome';를 넣으면 두 가지 문제가 발생함
    1. 이벤트가 발생했을 때 실행되는 함수 안에서는 this에 아무 값도 세팅되어 있지 않음(컴포넌트 자기 자신을 가리키지 않음) - undefined state이므로 읽을 수 없음 → 함수가 끝난 직후에 .bind(this)를 추가해주면 됨
    2. state가 바뀌었다는 것을 리액트가 알아차리지 못함 → this.setState() 함수를 이용해야 함
<header>
	<h1><a href="/" onClick={function(e){
	  console.log(e);
    e.preventDefault();
    this.state.mode = 'welcome';
	  this.setState({
			mode:'welcome'
	  });
  }.bind(this)}>{this.state.subject.title}</a></h1> //App이라는 컴포넌트를 가리키는 객체를 주입
  {this.state.subject.sub}
</header>

 

이벤트 bind 함수 이해하기

  • 기본적으로 render 함수 안에서 this는 render 함수가 속한 컴포넌트 자체를 가리킴. 하지만 이벤트 함수는 this가 아무 값도 없음 - 강제로 this 주입해주는 bind 함수
  • 함수.bind(객체); → 함수 블록 안에서 this는 객체가 됨 (그렇게 동작하는 새로운 함수가 복제되어서 만들어짐);

 

이벤트 setState 함수 이해하기

  • 생성자(constructor)함수에서는 this.state.mode = 'welcome'; 와 같이 바로 state 값을 바꿀 수 있지만 그 외의 함수에서는 그렇게 하면 안됨 → this.setState() 함수에 변경하고 싶은 값을 객체 형태로 주어야 함
  • this.state.mode = 'welcome';와 같이 값을 바꾸는 것은 리액트 모르게 값을 바꾼 셈이 됨 - 렌더링 불가능
  • state.mode 값이 바뀌었다는 것을 리액트가 알았다면 render 함수들이 호출될 것임 (자식 컴포넌트들도 마찬가지) 이에 따라 화면의 내용이 변할 것임

댓글