티스토리 뷰

생활코딩(이고잉님)의 React 강의를 보고 배운 내용을 정리하는 게시물입니다.

www.youtube.com/watch?v=XMb0w3KMw00&list=PLuHgQVnccGMCRv6f8H9K5Xwsdyg4sFSdi


드디어 40강짜리 강의를 모두 다 들었습니다!

👏👏👏👏👏👏

이제는 React로 Github.io 페이지 구축을 연습해보고

웹페이지 외의 앱을 만들기 위해 Flutter를 개인적으로 공부할 예정입니다.

 

20. Update 구현

개인적으로 CRUD 중에 가장 어려웠던 것 같습니다.

 

Create는 만들고 Contents에 추가하는 것으로

Read는 state의 mode만 변환하는 것으로

 

진행했다면, Update는 Create된 것을 Read하여 바로 다시 수정하는 방식(Create와 비슷하게)을 사용해야 합니다.

컴포넌트별로 복잡했기 때문에 코드를 천천히 살펴보면서 이해했습니다. 저는 복습할 때 이 블로그에 올린 React 글을 다시 읽어볼 예정입니다.

 

 

먼저 Update 기능을 어떻게 구현할 것인지 생각해보겠습니다.

최근에 선택된 아이디는 App.js의 state 속 'selected_content_id'에 저장이 됩니다.

그러므로 저장된 id를 받아와서 그 id에 해당하는 Contents를 가져오고 그것이 수정되면 다시 재저장하여 Contents에 적용시켜주면 되는 것입니다.

 

말은 쉽지만 많은 컴포넌트들 간에 정보가 교류되는 것이라서 상당히 많은 오류를 발생합니다.

물론 이고잉님의 강의를 천천히 따라가면 오류는 없지만 아무래도 영상을 보면서 작성하다보니 혼자만의 실수로 오류가 발생하기도..

 

먼저 UpdateContent.js 파일을 만들어줍니다.

이 때, Update는 Create와 유사한 방식으로 만들어주면 되기 때문에 CreateContent.js파일을 복사해서 가져오는 것이 더 편합니다.

 

constructor(props) {
      super(props);
      this.state = {
        id: this.props.data.id,
        title: this.props.data.title,
        desc:this.props.data.desc
      }
      this.inputFormHandler = this.inputFormHandler.bind(this);
    }

먼저 UpdateContent.js에서 constructor를 선언해줍니다. 이때 state에는 id와 title, 그리고 desc 총 3개가 들어갑니다.

id : update될 때, 현재 id가 무엇인지를 구분해주는 것 (현재 선택된 것이 어떤 건가?)

- title, desc : 선택된 것의 title, desc

 

그리고 inputFormHandler함수는 update될 때, title과 desc를 한 꺼번에 다루기 위해 함수화한 것입니다. 원래는 title을 수정했을 때, desc를 수정했을 때로 나누어야하는데 그러면 코드가 비효율적이 되어버리니까요.

inputFormHandler : input과 textarea를 수정할 때 바로 작성될 수 있도록하는 함수 (원래는 클릭한 것이 그대로 가져와야하기 때문에 react에서는 클릭된 것을, read-only로 가져온 것을 수정할 수 없게 하기에 작성한 함수)

 

inputFormHandler(e) {
      this.setState({[e.target.name]:e.target.value});
    }

수정될 때마다 수정하는 창(e.target)에 value를 자동으로 넣어줍니다. 이로 인해 수정할 때마다 수정한 문자가 바로바로 react에 전달되어 뜨게 됩니다.

 

<input
 type="text"
 name="title"
 placeholder="title"
 value={this.state.title}
 onChange={this.inputFormHandler}
></input>

<textarea
 onChange={this.inputFormHandler}  
 name="desc"
 placeholder="description"
 value={this.state.desc}></textarea>

그에 따른 input은 이렇게 구성되어 있습니다. 현재 state의 title을 가져와 title에 작성하고 이와 동일하게 desc에도 desc를 작성합니다. 변할 때는 당연히 inputFormHandler라는 함수 하나로 작동하구요.

 

else if (this.state.mode === 'update') {
      var _content = this.getReadContent();
      _article = <UpdateContent data={_content} onSubmit={function(_id, _title, _desc){
        var _contents = Array.from(this.state.contents);
        var i = 0;
        while (i < _contents.length) {
          if (_contents[i].id === _id) {
            _contents[i] = {id: _id, title: _title, desc: _desc}
            break;
          }
          i++;
        }

        this.setState({
          contents:_contents,
          mode: 'read'
        });
      }.bind(this)}></UpdateContent>;
}

 

App.js에서 가져온 update입니다. (Update를 클릭할 때 App.js의 mode를 setState를 이용하여 update로 교체해주었을 때 실행되는 render 함수 호출입니다.)

먼저 현재 Contents에서 전달받은 id를 찾고 바로 수정합니다.

그러면 수정된 것을 다시 contents로 전달하고 mode는 read로 변경하여 바로 읽을 수 있게 합니다.

read로 변경함으로서 '수정'버튼을 누르면 바로 수정된 것을 확인할 수 있겠죠?

 

 

getReadContent() {
    var i = 0;
    while(i < this.state.contents.length) {
      var data = this.state.contents[i];

      if (data.id === this.state.selected_content_id) {
        return data;
      } 
      i = i + 1;
    }
  }

이 함수는 Content Component에 선택된 것을 전달할 때 사용하는 함수입니다.

자주 사용되는 것 같아 함수화하셨다고 합니다! (그리고 가독성도 높이기 위함)

 

그리고 이와 유사하게 getContent() 함수로 현재 state의 mode를 확인하여 알맞은 코드를 진행시킵니다.

 

 getContent() {
    console.log('App render');
    var _title, _desc, _article = null;

    if(this.state.mode === 'welcome') {
      _title = this.state.welcome.title;
      _desc = this.state.welcome.desc;
      _article = <ReadContent title={_title} desc={_desc}></ReadContent>;
    } 
    
    else if (this.state.mode === 'read') {
      var _content = this.getReadContent();
      _article = <ReadContent title={_content.title} desc={_content.desc}></ReadContent>;
    }

    else if (this.state.mode === 'create') {
      _article = <CreateContent onSubmit={function(_title, _desc){
        // add content to this.state.contents
        this.max_content_id++;

        var _contents = this.state.contents.concat(
          {id:this.max_content_id, title:_title, desc: _desc}
        );

        this.setState({
          contents:_contents,
          selected_content_id:this.max_content_id,
          mode: 'read'
        });
        
        console.log(_title, _desc);
      }.bind(this)}></CreateContent>;
    }

    else if (this.state.mode === 'update') {
      var _content = this.getReadContent();
      _article = <UpdateContent data={_content} onSubmit={function(_id, _title, _desc){
        var _contents = Array.from(this.state.contents);
        var i = 0;
        while (i < _contents.length) {
          if (_contents[i].id === _id) {
            _contents[i] = {id: _id, title: _title, desc: _desc}
            break;
          }
          i++;
        }

        this.setState({
          contents:_contents,
          mode: 'read'
        });
      }.bind(this)}></UpdateContent>;
    }

    return _article;
  }

이로서 원래는 {_article}로 받아왔던 것을 {this.getContent()}로 받아올 수 있게 되었습니다.

 

Update는 코드만 따로 떼어놓으면 복잡하진 않지만 (사실 React용 Javascript와 HTML이 합쳐져서 매우 복잡하고 불편합니다만..) 내용 흐름이 상당히 복잡합니다.

 

Update를 간단하게 정리해보겠습니다.

 

1. Update를 클릭 시, Control.js에서 mode를 'update'로 바꾸라고 합니다.

2. App.js에 전달되어 update로 mode를 바꿉니다. 

3. getContent 함수가 호출됩니다.

4. getContent 함수에서 this.state.mode === 'update'의 조건을 충족시킵니다.

5. 현재 선택된 id에 해당하는 content를 getReadContent 함수를 호출하여 받아옵니다.

6. 해당 content를 data로 UpdateContent.js에 전달합니다.

 

[ 사용자가 수정했다고 가정 ]

 

7. 수정한 내용을 onChange 속성으로 inputFormHandler 함수를 호출하고 전달합니다.

8. 제출 버튼을 클릭하면 전달된 내용이 onSubmit 함수에 인자로 들어갑니다.

9. id에 해당하는 content의 title과 desc를 인자로 받아온(수정된) title과 desc로 변경합니다.

10. setState를 통해 mode를 read로 바꾸고 기존 content를 새로 바뀐 content로 수정합니다.

 

이렇게 되는 것 같습니다. 저도 적어보면서 아 이렇게 되는 구나를 한 번 더 쉽게 정리한 것 같네요 ㅋㅋㅋㅋ

한 마디로 update를 클릭한 것은 원래 있던 Control에서 mode를 바꿔주고 그에 따라 update 화면을 띄우고 수정된 내용을 App.js로 전달하여 바꾼다... 라고 보면 되겠습니다.


21. Delete 구현

CRUD 중에 그나마 쉬운 것 같은 Delete 구현을 마지막으로 정리하고 강의 정리를 마무리하려고 합니다!

 

<Control onChangeMode={function(_mode){
          if(_mode === 'delete') {
            if (window.confirm('Really?')) {
              var i = 0;
              var _max_contents_id = this.state.max_content_id;
              var _contents = Array.from(this.state.contents)
              while (i < _contents.length) {
                if (_contents[i].id === this.state.selected_content_id) {
                  _contents.splice(i, 1);
                  _max_contents_id--;
                  break;
                }
                i++;
              }

              this.setState({
                contents: _contents,
                selected_content_id: 1,
                mode: 'welcome',
                max_content_id : _max_contents_id
              })
            }
          } else {
            this.setState({
              mode: _mode
            })
          }
          this.setState({
            mode: _mode
          })
        }.bind(this)}> </Control>

상당히 짧고 귀엽죠?

선택된 것을 찾고 그 부분을 contents 에서 찾아 삭제 후 다시 전달하면 됩니다.

그리고 content의 개수가 한 개 줄었으니 당연히 max_content_id도 하나 줄여야겠죠?

 

강의에서는 그렇게만 하고 끝내지만

사실상 현재 선택된 id가 삭제되면 그 id는 다른 것을 가리키게 될 수도 있습니다. 약간 C와 C++ 언어에서 NullPointer 같은 느낌이랄까요

 

그래서 저는 무조건 삭제하면 selected_content_id를 1로 바꾸어주었습니다.

물론 이렇게 되면 '모든 걸 삭제하면 어떡하냐?' 라고 할 수도 있습니다.

그래서 그냥 -1과 같이 id로 선택할 수 없는 값을 넣어주고 delete 버튼을 눌렀을 경우, id가 -1인지 확인하여 한 번 더 선택을 상기시켜주는 방법도 좋을 것 같습니다.

 


드 디 어 끝 !

개인적으로 영상 강의를 완강한 건 거의 처음이라고 봐야할 것 같아요.

이 글을 작성할 시기에 머신러닝 야학 2기도 진행했어서 참여했는데, Tensorflow를 거의 찍먹하듯이 조금 맛본 상태입니다.

 

이번 겨울 방학에 제 개인적인 목표는

 

React를 이용하여 개인 홈페이지(포트폴리오 정도) 만들기

Flutter로 앱 프로젝트(현재 공부 중)

Tensorflow 이용하여 프로젝트(아무 생각이 없음)

이 세 가지입니다.

 

일단 첫 번째는 강의는 모두 들었으니 이제 실전처럼 구축해보려고 합니다..!!

강의에서 만들어진 예제 페이지는 CSS라곤 어디도 찾아볼 수 없는 밍밍한 상태이기에 이걸 어떻게 제가 잘 끌어내서 만들어 봐야겠네요 ㅋㅋㅋㅋ!!


 

댓글
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
Total
Today
Yesterday
최근에 올라온 글