함께 성장하는 프로독학러

5-1. 전화번호부 어플리케이션 만들기 - 구상, Contact 검색 기능 구현 본문

Programming/react.js

5-1. 전화번호부 어플리케이션 만들기 - 구상, Contact 검색 기능 구현

프로독학러 2018. 4. 19. 19:02

안녕하세요, 프로독학러 입니다.


이번 포스팅에서는 지난 포스팅 까지 배웠던 개념들을 가지고 간단한 웹 어플리케이션을 만들어 보도록 하겠습니다.

*velopert 님의 Youtube 강의를 정리한 내용이라고 보시면 될 것 같습니다. (4강)

<Contact application - velopert>


먼저 우리가 만들 웹 어플리케이션의 완성된 모습을 보고 어떻게 구현해야 할지 생각해 보도록 하겠습니다.



우리가 만들 Contact 어플리케이션은 전화번호부 어플리케이션입니다.

Contact 목록에서 사람을 클릭하면, Detail information 창에 정보가 표시되고, 

아래에 있는 Edit, Remove 버튼을 이용해 정보를 수정하거나 삭제 할 수 있습니다.

맨 아래의 Create new user 를 이용하면 목록에 새로운 전화번호 정보를 추가 할 수 있습니다.


위의 어플리케이션은 크게 세 가지의 컴포넌트로 나눠 볼 수 있습니다.

전화번호부 목록 (Contacts), 상세 뷰 (Detail information), 유저 생성 (Create user)

그리고 전화번호부 목록에서 각각의 항목들은 목록에 변화가 있을 때 마다 프로그래밍 적으로 업데이트 되야 하므로(목록에 추가하거나 목록에서 삭제하거나) 각각의 항목역시 컴포넌트로 볼 수 있습니다.



각각 컴포넌트의 이름은 위의 사진과 같이 정하도록 하겠습니다.


그럼 제일 먼저 Contact 컴포넌트 부터 만들어 보도록 하겠습니다.

./src/components 경로에 Contact.js 파일을 만듭니다.


(./src/components/Contact.js)

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
import React, { Component } from 'react';
 
import ContactInfo from './ContactInfo';
 
class Contact extends Component {
  state = {
    keyword : '',
    contactData : [{
      name : 'David',
      phone : '010-1234-5678'
    }, {
      name : 'Albert',
      phone : '010-1234-1234'
    }, {
      name : 'John',
      phone : '010-5678-5678'
    }, {
      name : 'Wade',
      phone : '010-4312-5678'
    },]
  }
  _searchContact = (e) => { // 2. input 태그의 값이 변경 될 때마다 this.state.keyword 값이 변경
    this.setState({
      keyword : e.target.value
    });
  }
  render(){
    const mapToComponents = (data) => { // 3. 매핑 (들어온 인자는 this.state.contactData)
      data.sort(); // 3-1. this.state.contactData 를 정렬 (유니코드 값을 기준으로)
      data = data.filter( // 3-2. 정렬된 데이터를 필터링
        (contact) => { // 콜백함수의 인자는 element[, index, array] 콜백함수의 리턴값은 리턴값을 만족하는 엘리먼트들의 새로운 배열
          return contact.name.toLowerCase() // 이름기준, 대소문자 구별 없이, 검색 (indexOf 메서드로)
          .indexOf(this.state.keyword.toLowerCase()) > -1// indexOf메서드의 인자는 검색할 내용 (string) 검색 결과가 없으면 리턴값은 -1
        }
      ); // 4. input 태그에 글자가 입력 될 때마다 리턴되는 배열이 달라짐. 필터링 된 배열을 data에 담고
      return data.map( // 5. 해당 data 배열을 매핑
        (contact, i) => { // map 메서드의 첫 번째 인자 - item, 두 번째 인자 - index
          return (<ContactInfo contact={contact} key={i} />); // map 메서드의 리턴값은 콜백함수의 리턴값을 각각의 원소로하는 기존배열과 같은 길이의 배열
        } // 6. ContactInfo 컴포넌트가 배열의 길이만큼 리턴
      );
    }
    return(
      <div>
        <h1>Contact</h1>
        <input
          name="keyword"
          placeholder="Search"
          value={this.state.keyword} 
          onChange={this._searchContact} // 1. input 창에 입력될 때 마다
        />
        <div>{mapToComponents(this.state.contactData)}</div> {/*7. 목록으로 표시*/}
      </div>
    )
  }
}
 
export default Contact;
 
cs


코드가 조금 복잡하지만, 차근차근 살펴보도록 하겠습니다.

코드의 6번째 줄에서 Contact 컴포넌트의 state 를 정의 했습니다.

state의 keyword 는 입력된 검색어를 의미하는 state 입니다. 일단은 빈 문자로 처리해 두었습니다.

state의 contactData 는 기본적으로 설정해 놓은 전화번호부 목록입니다. 배열안의 각각의 원소를 객체로 처리해 이름과 전화번호를 저장했습니다.

코드의 22번째 줄에는 _searchContact 함수가 정의 돼 있습니다. 이는 검색창에 키보드를 입력할 때 마다 입력된 값으로 state.keyword 를 변경시켜줄 함수입니다.

코드의 28번째 줄에서 검색된 정보들을 각각의 컴포넌트로 매핑하는 mapToComponents 를 정의 했습니다.

그리고 코드의 42번째에서 Contact 컴포넌트가 어떻게 동작할지 JSX 를 통해 정의 했습니다.


이제부터 주석으로 처리된 번호의 순서대로 Contact 컴포넌트가 작동하는 방식에 대해서 설명해 드리도록 하겠습니다.

다소 복잡할 수 도 있으나 천천히 따라오시면 생각보다 많이 어렵지도 않으니 잘 따라와 주시기 바랍니다.


1) 맨 처음 살펴본 부분은 코드의 45번째 줄에 위치한 input 태그입니다.

input 태그에서 onChange 이벤트를 통해 input 태그 안에 어떤 값이 입력 됐을 때 호출될 함수를 지정합니다. (49번째 줄)


2) 코드의 22번째 줄에 input 태그가 변경될 때 마다 호출되는 함수가 정의 되 있습니다. (함수명은 우리가 직접구분한 함수임을 구분하기 위해 맨 앞에 언더바를 추가했습니다.)

이 함수는 input 태그에 값이 입력될 때마다 입력된 값으로 state.keyword 를 변경하는 메서드 입니다.

(e는 이벤트 객체로, e.target.value 는 이벤트의 타겟, 즉 input 태그의 value 값을 의미합니다. - input 필드에 입력되어있는 값).


1~2) 은 input 태그에 값을 입력하면 그 값으로 state.keyword 를 변경하는 코드입니다.


3) mapToComponents 함수를 정의 했습니다. 이 함수는 코드의 51번째 줄에서 호출되는 함수로, contactInfo 컴포넌트들을 반환합니다.

(검색된 각각의 전화번호부 목록 컴포넌트)

*(자바스크립트의 map 메서드에 익숙하지 않으신 분들은 아래의 링크를 참조해 주세요)

<자바스크립트 - 배열의 map 메서드>


3-1) 인자로 들어온 데이터를 sort 메소드를 이용해 정렬합니다.

*(자바스크립트의 sort 메서드에 익숙하지 않으신 분들은 아래의 링크를 참조해 주세요)

<자바스크립트 - 배열의 sort 메서드>


3-2)  정렬된 데이터를 필터링 합니다.

배열의 필터메서드의 리턴값은 필터링된 엘리먼트들의 새로운 배열입니다.

indexOf 메서드를 통해 name 이 검출된 엘리먼트들만을 data 변수에 대체합니다.

*(자바스크립트의 indexOf 메서드에 익숙하지 않으신 분들은 아래의 링크를 참조해 주세요)

<자바스크립트 - 배열의 indexOf 메서드>


4) state.keyword(input 태그에 입력된 값) 를 기준으로 이름이 검색된 배열의 원소들의 집합이 리턴되므로 data 배열은 input 태그에 내용이 입력될 때마다 달라집니다.


5) data 를 매핑합니다.


6) data를 매핑한 결과는 ContactInfo 컴포넌트들의 배열입니다.

ContactInfo 컴포넌트로 프롭스를 전달합니다.(contact 로 각각의 객체, key 값으로 index 를 전달)

* 같은 컴포넌트가 여러개 렌더링 될 때는 각각의 컴포넌트에 key 값이 있어야 리액트가 헷갈리지 않고 동작합니다.


7) mapToComponents 메소드의 인자로 state.contactData 를 주었습니다.

state.contactData 를 정렬하고, 검색 결과에 따라 필터링하고, 매핑하여 결과를 ContactInfo 컴포넌트들로 렌더링 합니다.


그렇다면 렌더링 될 ContactInfo 컴포넌트를 만들어 봅시다.

src/components 폴더에 ContactInfo.js 파일을 생성합니다.


(./src/components/ContactInfo.js)

1
2
3
4
5
6
7
8
9
import React, { Component } from 'react';
 
export default class ContactInfo extends Component {
    render() {
        return (
            <div>{this.props.contact.name}</div>
        );
    }
}
cs


위의 코드에서는 class 를 정의하고 export 했던 방식과 다르게 바로 export 하는 값으로 class 정의 했습니다.

(작동되는 원리는 같습니다)

코드의 6번째 줄에서 Contact 컴포넌트로 부터 전달받은 프롭스의 name 속성을 컨테이너 엘리먼트 안에 담았습니다.

(Contact.js 에서 매핑된 리턴값에서 contact 프롭스로 필터링된 배열의 원소(전화번호부, 이름을 가진 객체)를 전달했습니다 - Contact.js 의 38번째 줄)


Contact.js 의 3 번째 줄에서 ContactInfo 컴포넌트를 호출했습니다.


Contact 컴포넌트와 그 안에서 렌더링 되는 ContactInfo 컴포넌트를 엔트리 파일인 index.js 파일에 임포트 시켜 브라우저에서 확인해 보겠습니다.


(./src/index.js)

1
2
3
4
5
6
7
8
9
10
11
12
13
import React from 'react';
import ReactDOM from 'react-dom';
 
import Contact from './components/Contact';
 
const title = 'Is it working? hot module replacement...';
 
ReactDOM.render(
  <Contact />,
  document.getElementById('root')
);
 
module.hot.accept();
cs


코드의 4번째 줄에서 Contact 컴포넌트를 호출했습니다. (ContactInfo 컴포넌트는 Contact 안에 포함되어 있으므로 따로 임포트 할 필요 없습니다.)

그리고 코드의 9번째 줄에서 Contact 컴포넌트를 렌더링 했습니다.



브라우저에 잘 구현 된 것을 알 수 있습니다.


Search 창에 d 를 입력하면 d 가 포함된 David 와 Wade 가 목록에 표시되는 것을 알 수 있습니다.


정리하자면, Contact 의 검색 기능은 input 태그에 값이 입력될 때 마다(onChange 이벤트) 입력된 값(이벤트 객체의 타겟의 value)으로 Contact 컴포넌트의 스테이트 값을 변경하고, 변경된 스테이트 값을 기준으로 state.contactData 배열을 필터링 및 매핑을 하고 리턴된 배열로 새로운 목록(ContactInfo 컴포넌트)을 만들어 표시하는 것 입니다.


여기까지 Contact 컴포넌트의 검색기능 구현에 대해서 알아보았습니다.

다음 포스팅에서는 ContactInfo 컴포넌트를 클릭했을 때 상세정보를 보여주는 기능을 추가해 보도록 하겠습니다. (ContactDetail 컴포넌트)


**참고 자료 (항상 감사드립니다)

https://velopert.com/reactjs-tutorials


*다녀가셨다는 표시는 공감으로 부탁드릴게요! (로그인 하지 않으셔도 공감은 가능합니다 ㅎㅎ)


Comments