본문 바로가기
Library | Framework/React

[React] Ajax 찍먹하기 2, 디커플링, 프레젠테이셔널/컨테이너 컴포넌트

by 나는김혜린 2022. 1. 17.

서론

저번 글과 이어서 진행된다는 점 유의. 이번 글에선 각 리스트 항목을 클릭했을 때 그 항목의 정보를 <article> 부분에 출력하는 기능을 구현할 것이다.

 

저번 글 링크

https://k99hyerin.tistory.com/23

 

Ajax로 컴포넌트 상태 변경하기

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

class App extends Component {
  state = {
    article:{title:'Welcome', desc:'Hello, React & Ajax'}
  }
  render() {
    return (
      <div className='App'>
        <h1>WEB</h1>
        <Nav></Nav>
        <Article title={this.state.article.title} desc={this.state.article.desc}></Article>
      </div>
    );
  }
}

먼저, 저번 코드에서 Article 컴포넌트를 생성해준다.

App 컴포넌트(상위)에서 state를 만들어 title, desc값을 Article 컴포넌트(하위) props로 넘겨준다.

그리고 Article 컴포넌트에서 title, desc를 UI로 보여준다.

UI는 전과 똑같지만, 컴포넌트로 구현했다.

 

이제 Nav 컴포넌트의 항목을 클릭했을 때 <article>의 내용이 변경되게 구현할 것이다.

class Nav extends Component {
  생략
  render() {
    var listTag = [];
    for(var i = 0; i < this.state.list.length; i++) {
      var li = this.state.list[i];
      listTag.push(<li key={li.id}>
        <a href={li.id} data-id={li.id} onClick={function(e) {
          e.preventDefault();
          console.log('trigger');
          this.props.onClick(e.target.dataset.id);
        }.bind(this)}>{li.title}
        </a>
        </li>);
    }
    생략
  }
}

class App extends Component {
  state = {
    article:{title:'Welcome', desc:'Hello, React & Ajax'}
  }
  render() {
    return (
      <div className='App'>
        <h1>WEB</h1>
        <Nav onClick={function(id) {
          fetch(id+'.json')
          .then(function(result) {
            return result.json();
          })
          .then(function(json) {
            this.setState({
              article:{
                title:json.title,
                desc:json.desc
              }
            })
          }.bind(this))
        }.bind(this)}></Nav>
        <Article title={this.state.article.title} desc={this.state.article.desc}></Article>
      </div>
    );
  }
}
<Nav onClick={function(id) {

}.bind(this)}></Nav>

우선 Nav 컴포넌트에 onClick props를 추가하고 Nav 컴포넌트 내에서 id를 인자로 props로 전달받은 onClick 함수를 호출해야 한다.

<a href={li.id} data-id={li.id} onClick={function(e) {
  e.preventDefault();
  console.log('trigger');
  this.props.onClick(e.target.dataset.id);
}.bind(this)}>
  {li.title}
</a>

그리고 onClick 함수에 id를 인자로 전달하기 위해 <a> 컴포넌트의 data-id props에 id를 미리 저장해 둔다.

-> e.target.dataset.id로 접근

=> Nav 컴포넌트의 onClick 함수를 클릭하면 id 값이 인자로 전달된다. (this.props.onClick(인자) -> <Nav onClick=(function(인자)) ></Nav>)

<Nav onClick={function(id) {
  fetch(id+'.json')
  .then(function(result) {
    return result.json();
  })
  .then(function(json) {
    this.setState({
      article:{
        title:json.title,
        desc:json.desc
      }
    })
  }.bind(this))
}.bind(this)}></Nav>

 

Nav 컴포넌트 내에서 건네준 id값을 onClick props 함수의 인자로 받고, fetch API를 이용해 id.json 파일을 읽는다.

이 파일을 json() 메서드로 js 객체로 바꾸고, setState 함수로 이 파일의 title, desc값을 저장한다.

 

여기까지 잘 되었다면 밑 동영상과 같이 나올 것이당.

 

 

디커플링(decoupling)이란?

: 컴포넌트가 어떤 특정한 데이터에 종속되지 않게 뜯어내는 것

이제 이때까지 만든 Nav 컴포넌트를 디커플링할 것이다. 즉, 데이터와는 상관없고 데이터를 표현하기만 하는 컴포넌트로 만들 것이다. 

 

보통 이러한 컴포넌트를 프레젠테이셔널(presentational) 컴포넌트라고 부른다.

반대로 이 프레젠테이셔널 컴포넌트를 둘러싼 데이터를 처리하고 사용자의 상호작용 등을 처리하는,

애플리케이션에 완전히 종속된 컴포넌트를 컨테이너(container) 컴포넌트라고 부른다.

 

현재 Nav 컴포넌트는 프레젠테이셔널 컴포넌트와 컨테이너 컴포넌트의 기능을 모두 갖고 있다.

이를 디커플링하기 위해 Nav 컴포넌트는 프레젠테이션을 담당하는 기능만 남기고, 컨테이너 기능을 App 컴포넌트로 분산시킬 것이다.

 

디커플링을 해보자!

일단 전 코드를 이렇게 수정해보자.

class Nav extends Component {
  render() {
    var listTag = [];
    for(var i = 0; i < this.props.list.length; i++) {
      var li = this.props.list[i];
      listTag.push(<li key={li.id}>
        <a href={li.id} data-id={li.id} onClick={function(e) {
          생략
  }
}

class App extends Component {
  state = {
    article:{title:'Welcome', desc:'Hello, React & Ajax'},
    list:[]
  }

  componentDidMount() {
    fetch('list.json')
    .then(function(result) {
      return result.json();
    })
    .then(function(json) {
      this.setState({list:json});
    }.bind(this))
  }

  render() {
    return (
      <div className='App'>
        <h1>WEB</h1>
        <Nav list={this.state.list} onClick={function(id) {
          생략
    );
  }
}

원래 Nav 컴포넌트에 있던 componentDidMount 메서드를 App 컴포넌트로 이동했다.

이 메서드가 다루는 값이 state.list이기 때문에 App state로 list를 이동해야 한다.

따라서 state에 list를 추가하고 Nav 컴포넌트의 state는 삭제한다.

 

그 다음, Nav 컴포넌트의 list props에 state.list 값을 전달한다. Nav 컴포넌트 입장에선 props로 받는 것이기 때문에 state.list를 props.list로 출력하게끔 수정하면 된다.

 

이렇게 하면 Nav 컴포넌트는 프레젠테이션, App 컴포넌트는 컨테이너 기능을 담당하게 되었다!

 

담소

오늘은 뭔가 배운 것도 많고 이해도 잘 돼서 좋았다.

챕터의 마지막 부분인 줄 알았는데 아직 한 부분이 남아서 또 다음 글로 적어야겠다!

홧팅홧팅

댓글