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


이제 To Do App의 메인으로, 해야 할 일 부품을 만들어 주겠습니다.


ToDo 만들기

Todo.js를 생성하고 아래처럼 작성합니다.

// app/components/Todo.js

import React from "react"
import {View,Text,StyleSheet} from "react-native"

const TodoItem= () => (
    <View style={styles.todoContainer}>
        <View>
            <Text style={styles.todos}>할일 1</Text>
        </View>
    </View>
)

const styles = StyleSheet.create({
    todoContainer: {
        padding: 5,
        marginTop: 20,
        borderBottomWidth:1
    },
    todos: {
        fontSize: 20
    },
})

export default TodoItem

View에 todoContainer라는 이름으로 style을 줍니다.
style 설정이 끝나면 App.js에 TodoItem을 import 한 뒤, 부품을 두 개 조립해봅니다.

//App.js

<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/>
    <TodoItem/>
  </View>
</View>

실행 결과는 다음과 같습니다.

TodoItem의 width가 화면을 넘어가고 있으니 style을 조정해보겠습니다.

//Todo.js

import React from "react"
import {View,Text,StyleSheet,Dimensions} from "react-native"

const {width,height} = Dimensions.get('window')

const TodoItem= () => (
    <View style={styles.todoContainer}>
        <View>
            <Text style={styles.todos}>할일 1</Text>
        </View>
    </View>
)

const styles = StyleSheet.create({
    todoContainer: {
        padding: 5,
        marginTop: 20,
        borderBottomWidth:1,
        width: width-40
    },
    todos: {
        fontSize: 20
    },
})

export default TodoItem

Dimension을 이용하면 화면치수를 활용할 수 있습니다.
Dimensions.get('window')는 화면의 width와 height를 객체 형태로 받아올 수 있는 코드입니다. 위처럼 값을 받아온뒤, 현재 화면 -40으로 todoContainer의 width를 설정해 줍니다.


Icon 추가하기

To-Do List에 한 일을 체크할 수 있는 버튼이 필요합니다.
먼저 버튼으로 사용할 아이콘을 추가해봅니다.

expo에서 기본으로 제공해주는 ‘FontAwesome’의 ‘check’ 아이콘을 사용하겠습니다.

//Todo.js

import React from "react"
import {View,Text,StyleSheet,Dimensions} from "react-native"
import {FontAwesome} from "@expo/vector-icons"

const {width,height} = Dimensions.get('window')

const TodoItem= () => (
    <View style={styles.todoContainer}>
        <View>
            <FontAwesome name="check-circle" size={20}/>
            <Text style={styles.todos}>할일 1</Text>
        </View>
    </View>
)

const styles = StyleSheet.create({
    todoContainer: {
        padding: 5,
        marginTop: 20,
        borderBottomWidth:1,
        width: width-40
    },
    todos: {
        fontSize: 20
    },
})

export default TodoItem

FontAwesome의 check-circle을 가져와서 사용해 보겠습니다.

체크 ‘아이콘’이 생겨났습니다. 이제 이것을 ‘버튼’으로 만들어 보겠습니다.


TouchableOpacity

TouchableOpacity는 사용자가 터치했을때 반짝이는 컴포넌트입니다.
TouchableOpacity를 import 하고 사용하겠습니다.

//Todo.js

import {View,Text,StyleSheet,Dimensions,TouchableOpacity} from "react-native"

const TodoItem= () => (
    <View style={styles.todoContainer}>
        <View>
            <TouchableOpacity>  
                <FontAwesome name="check-circle" size={20}/>
            </TouchableOpacity>
            <Text style={styles.todos}>할일 1</Text>
        </View>
    </View>
)

그리고 실행해보면, 클릭하거나 터치했을 때 반짝거리는 것을 볼 수 있습니다.


레이아웃 조정 및 삭제 버튼

레이아웃을 조정해 보고 삭제 버튼도 만들어 보겠습니다.

//Todo.js

const TodoItem= () => (
    <View style={styles.todoContainer}>
        <View>
            <View style={styles.objContainer}>
                <TouchableOpacity>  
                    <FontAwesome name="check-circle" size={20}/>
                </TouchableOpacity>
                <Text style={styles.todos}>할일 1</Text>
                <TouchableOpacity>  
                    <FontAwesome name="close" size={20}/>
                </TouchableOpacity>
            </View>
        </View>
    </View>
)

const styles = StyleSheet.create({
    todoContainer: {
        padding: 5,
        marginTop: 20,
        borderBottomWidth:1,
        width: width-40
    },
    todos: {
        fontSize: 20
    },
    objContainer: {
        flexDirection:'row'
    }
})

React-Native의 컴포넌트들은 기본적으로 flexDirectioncolumn으로 설정되어 있습니다. 그렇기 때문에 모든 부품들이 세로로 쌓여있었습니다.
이렇게 세로로 배치된 컴포넌트들을 가로로 이어주려면 flexDirection 속성을 row으로 변경해야합니다.
주의할 점은 flexDirection 속성이 변경되면, alignItems가 세로정렬을, justifyContent가 가로 정렬 방식을 조정하게 됩니다.

이제 레이아웃을 조정 했고 삭제 할 수 있는 버튼을 추가했습니다.

이제 레이아웃을 좀 더 조정해 보겠습니다.

//Todo.js

const TodoItem= () => (
    <View style={styles.todoContainer}>
        <View>
            <View style={styles.objContainer}>
                <View style={styles.textContainer}>
                    <TouchableOpacity>  
                        <FontAwesome name="check-circle" size={20} style={styles.check}/>
                    </TouchableOpacity>
                    <Text style={styles.todos}>할일 1</Text>
                </View>
                <TouchableOpacity>  
                    <FontAwesome name="close" size={20}/>
                </TouchableOpacity>
            </View>
        </View>
    </View>
)

const styles = StyleSheet.create({
    todoContainer: {
        padding: 5,
        marginTop: 20,
        borderBottomWidth:1,
        width: width-40
    },
    todos: {
        fontSize: 20
    },
    objContainer: {
        flexDirection:'row',
        justifyContent:'space-between'
    },
    textContainer: {
        flexDirection:'row',
        alignItems:'center'
    },
    check: {
        marginRight: 10,
    }
})

flexDirectionrow로 설정되어 있을 때, justifyContent는 가로정렬 방식을 의미합니다. ``space-between`은 공백을 사이에 두고 정렬한다는 의미입니다.
현재 여기에선 objContainer안에 있는 View(textContainer)와 삭제 버튼을 정렬하되, 사이에 공백을 두고 정렬한다는 뜻입니다. 결과적으로 View는 왼편에 위치하게 되고, 삭제버튼은 오른편에 위치하게 됩니다.

그리고 textContainer는 icon과 text를 세로로 중앙정렬 하기 위해 flexDirectionrow로 바꾸고 alignItems를 이용하여 세로 정렬을 해주었습니다.

완료~!!


TodoItem 재사용하기 (Props)

할 일을 추가할 때마다 Todo.js를 새로 생성하는 것은 너무 비효율적입니다. 앞서 사용했던 Props를 이용해서 TodoItem을 재사용해보겠습니다.

//App.js

export default class App extends React.Component {
  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="알바 가기" />
          <TodoItem text="전화 하기"/>
        </View>
      </View>
                
    )
  }
}
//Todo.js

const TodoItem= ({text}) => (
    <View style={styles.todoContainer}>
        <View>
            <View style={styles.objContainer}>
                <View style={styles.textContainer}>
                    <TouchableOpacity>  
                        <FontAwesome name="check-circle" size={20} style={styles.check}/>
                    </TouchableOpacity>
                    <Text style={styles.todos}>{text}</Text>
                </View>
                <TouchableOpacity>  
                    <FontAwesome name="close" size={20}/>
                </TouchableOpacity>
            </View>
        </View>
    </View>
)

App.js 에서 TodoItem 부품을 조립하고, text속성을 임의로 줍니다.
그리고 Todo.js에서 text를 받아와 출력해줍니다.