[React-Native]To-Do-App 만들기 (4)


지금까지는 고정된 값을 할당하고 이를 활용했습니다. 하지만 사용자가 직접 데이터를 입력하고 표시하기 위해선 유동적으로 데이터를 다룰 필요가 있는데, 이때 사용되는 것이 state, 상태 값입니다.


State

이전에 배운 Props는 값을 상위에서 하위로 전달할 수 있었습니다. 하지만 값을 변경할 수는 없었습니다.
이번에 배울 state는 값을 직접 수정하거나 추가할 수 있습니다. 해당 state값이 변경되면 그 값을 사용하고 있는 화면은 다시 그려집니다.

App.js에 state를 추가합니다.
state는 {key:value}형태로 작성합니다.

export default class App extends React.Component {

  state = {
    todos : [
      {
        title : "물 3잔 마시기",
        isComplete: false
      },
      {
        title: "30분 이상 걷기",
        isComplete: false
      }
    ]
  }

  render() {
    return (
      <View style={styles.container}>
        <View style={styles.centered}>
            <Header/>
        </View>
        <View style={styles.inputContainer}>
          <SubTitle title="To-Do 입력"/>
          <Input/>
        </View>
        <View style={styles.todoContainer}>
          <SubTitle title="To-Do List"/>
          <TodoItem text={this.state.todos[0].title} isComplete={this.state.todos[0].isComplete}/>
          <TodoItem text={this.state.todos[1].title} isComplete={this.state.todos[1].isComplete}/>
        </View>
      </View>
                
    )
  }
}

state에 설정한 값을 사용할 때는 this.state.값 형식으로 사용하면됩니다. 우리는 state안에 있는 todos 배열의 첫번째 순서있는 할일의 title을 가져와 사용할 예정이니 this.state.todos[0].title 라고 써줍니다. 똑같은 방법으로 isComplete도 state값을 가져와 props로 사용합니다.


FlatList

FlatList는 React-Native에서 제공되는 컴포넌트로, 리스트를 구현할 때 사용합니다. 지금 까지 만든 할 일 부품들을 하나의 리스트에 넣어줄 예정입니다.

// App.js

import {StyleSheet,Text,View,FlatList} from 'react-native'

<FlatList
    data={this.state.todos}
    renderItem={this._makeTodoItem}
    keyExtractor={(item,index) => {return `$(index)`}} />

FlatList를 import 해주고 코드를 작성합니다.
FlatList의 속성을 알아보겠습니다.

  1. data : FlatList에서 렌더링할 데이터를 의미합니다. 속성 값은 배열로 입력해야합니다.

  2. renderItem : data 배열에 속한 하나의 데이터, 즉 한 일 한개의 정보를 바탕으로 컴포넌트를 리턴합니다. 속성값은 함수로 입력해야 합니다.

  3. keyExtractor : 각 아이템의 key값을 생성해 지정해 줍니다.


FlatList의 renderItem

이제 데이터를 집어 넣으면 알아서 리스트에 부품이 등록되게 해보겠습니다.

renderItem을 사용할 예정인데 공식 문서를 보면, 사용할 수 있는 값이 몇가지 있습니다.
이 중 주목해야 할 부분은 렌더링할 data를 받아오는 item과 그 데이터가 몇 번째 데이터인지 확인 할 수 있는 index 입니다.
앞서 만들기로한 함수인 _makeTodoItem 을 만들어 보겠습니다. _makeTodoItem은 우리가 할 일의 컴포넌트를 각각의 정보를 이용해 만들어주는 역할 을 합니다. 만약 할 일 데이터가 4개 있다면 이 함수를 renderItem의 속성값으로 주고, 데이터를 이용해 컴포넌트 4개를 만들어 줍니다.

데이터의 갯수만큼 내부적으로 반복문을 돈다고 생각하면 쉽습니다.

_makeTodoItem = ({ item, index }) => {
    return (
      <TodoItem text={item.title} isComplete={item.isComplete} />
    )
}

함수나 변수 이름앞에 _를 붙이면 이는 클래스 안에서만 사용할 예정이라는 뜻을 내포하게 됩니다.

결과는 다음과 같습니다.


할일 추가하기

이제 사용자가 직접 할 일을 추가할 수 있게 만들도록 해보겠습니다.

우선 입력한 일을 저장하는 작업을 해보겠습니다.

입력한 값 저장하기

현재 할일 데이터는 state에 저장되어 있습니다. state는 변경되는 데이터를 담는 곳입니다. 이제 입력한 할 일도 state에 저장해야 합니다.

state = {
    inputValue: "",
    todos: [
      {
        title: "물 3잔 마시기",
        isComplete: false
      },
      {
        title: "30분 이상 걷기",
        isComplete: false
      }
    ]
  }

state에 inputValue 값을 추가해줍니다. 입력창에 저 inputValue가 보여지게할 예정입니다. 앱 시작시 보이는 입력창은 공백을 넣어주겠습니다.

할 일이 입력되면 그 입력 값을 받아와야 합니다. Input의 속성과 값을 변경해 보겠습니다.

//App.js
    
    <View style={styles.inputContainer}>
      <SubTitle title="To-Do 입력" />
      <Input
          value={this.state.inputValue}
          changeText={this._changeText}/>
    </View>

Input에 valuechangText라는 props를 위처럼 추가해주겠습니다. 그리고 속성값으로 각 state에 넣어줬던 inputValue값과 _changeText를 전할 하겠습니다.
_changeText는 입력창의 값이 변경될 때마다 실행시켜줄 함수 이름입니다.
_changeText함수를 추가해 보겠습니다.

//App.js

_changeText = (value) => {
    this.setState({inputValue: value});
  }

state값을 변경할 때는 this.setState({}) 형태를 사용합니다. 예를 들어 state안에 inputValue값을 변경하고 싶다면 this.setState({inputValue:'안녕'}) 이런 식으로 사용하면 됩니다.

이제 Input.js로 넘어가 상위컴포넌트에서 받은 속성을 활용해보겠습니다.

//Input.js

const Input = ({value,changeText}) => (
    <TextInput
        value={value}
        style={styles.input}
        placeholder={"오늘 어떤 일을 하실건가요?"}
        maxLength={30}
        onChangeText={changeText}
        returnKeyType="done"/>
)

value와 changeText를 받아와 값을 React-Native의 컴포넌트의 TextInput의 속성값으로 전달합니다.
onChangeText는 입력된 텍스트가 변경될 때 실행되는 이벤트라 생각하면 됩니다. 입력된 텍스트가 변경될 때 위에서 넘겨는 _chagnText함수가 실행됩니다.
value속성은 입력창에 입력된 값을 의미합니다. value속성 값으로 넘겨준 {this.state.inputValue}값을 넣어줍니다. 이 state.inputValue는 텍스트가 변경될 때마다 실행되는 함수를 걸어놨으니 텍스트가 변경될 때마다 state값도 바뀔 예정입니다.


저장한 일 추가하기

이제 할 일을 입력하고 완료 버튼을 누르면 리스트에 추가되게끔 할 예정입니다.
완료 버튼을 누르면 리스트에 추가되어야 하니, 버튼을 눌렀을 때 실행되는 코드가 필요합니다. App.js로 가서 Input 컴포넌트를 수정해봅니다.

//App.js

<View style={styles.inputContainer}>
	<SubTitle title="To-Do 입력"/>
	<Input
	    value={this.state.inputValue}
	    changeText={this._changeText}
	    addTodo={this._addTodoItem}/>
</View>

Input 컴포넌트에 addTodo라는 props를 추가하였습니다. 속성값으로는 _addTodoItem이라는 함수를 주고 있습니다. 바로 작성해 보겠습니다.

// App.js

_addTodoItem = () => {
	// prevTodo라는 변수에 지금 state에 있는 todos를 넣습니다.
  const prevTodo = this.state.todos
	// 입력한 할 일의 내용을 state.inputValue에서 받아와 title에 넣습니다.
	// 할 일을 입력할 시에는 보통 완료하지 않은 경우가 많으니 완료여부는 false를 기본값으로 설정합니다.
	// 이렇게 만든 할 일 덩어리를 newTodo라는 변수에 집어넣습니다.
  const newTodo = { title: this.state.inputValue, isComplete: false}

	//todos에 지금까지 저장된 할 일 덩어리들과(prevTodo)와 방금 추가한 할 일 덩어리(newTodo)를 이어서(concat) 저장합니다(setState). 추가가 완료된 이후에 입력창은 다시 공백으로 만듭니다.(inputValue: '')
  this.setState({
    inputValue: '',
    todos: prevTodo.concat(newTodo)
  })
}

작성된 props를 사용해야 하니 Input.js로 이동해 보겠습니다.

// Input.js

const Input = ({value,changeText,addTodo}) => (
    <TextInput
        value={value}
        onChangeText={changeText}
        onEndEditing={addTodo}
        style={styles.input}
        placeholder={"오늘 어떤 일을 하실건가요?"}
        maxLength={30}
        returnKeyType="done"/>
)

위에서 넘겨준 addTodo속성을 받아 TextInput 컴포넌트의 onEndEditing 속성에 넣어줍니다.
onEndEditing은 현재 입력창에 입력하는 작업이 끝났을 경우, 즉 완료 버튼을 눌렀을 때 실행하는 작업을 등록하는 속성입니다.

이제 실행하면 다음과 같이 작동됩니다.