https://nomadcoders.co/react-native-for-beginners
프로젝트를 하면서 앱을 개발해야 하는 일이 생겨서
니꼬 선생님의 강의를 듣고 React Native를 사용해
To Do 앱을 만들었습니다.
Expo를 사용하면 환경 설정이 간단하고
제공해 주는 API, 컴포넌트들이 많아서
쉽고 빠르게 개발이 가능합니다.
📝 To Do 앱
⦁ 🧾 기능
1. To Do List / 여행 리스트 추가 삭제
2. 체크박스 선택 시 텍스트 줄긋기 및 색 변경 ( 완료 체크 )
3. AsyncStorage 사용 ( Local Storage 비슷한 기능 )
📝 To Do 추가 / 삭제
⦁ To Do 추가
const travle = () => setWorking(false);
const work = () => setWorking(true);
const [checked, setChecked] = React.useState(false); //UI 라이브러리 사용
//Submit
const addToDo = async () => {
if (text === "") {
return
}
//todo 저장 Object.assign 오브젝트 합치기
const newToDos = { ...toDos, [Date.now()]: { text, working, checked}}
setToDos(newToDos)
//스토리지에 저장
saveToDos(newToDos)
//텍스트 초기화
setText("")
}
✔ Object 타입으로 데이터를 저장합니다.
State를 직접 사용하면 안되기 때문에 수정하기 위해서는 원본을 copy하고 set 메서드를 사용해야 합니다.
따라서 newToDos를 선언했습니다.
✔ES6 문법
const newToDos = {...toDos, [Date.now()] : {text, work: working}}
...toDos는 ES6 문법인데 Object.assign과 동일하게 기존 오브젝트에 이어붙일 수 있습니다.
⦁ To Do 삭제
To Do 삭제
const deleteToDo = (key) => {
Alert.alert("Delete To Do", "Are you sure?", [
{ text: "Cancel" },
{
text: "I'm Sure", onPress: async () => {
const newToDos = { ...toDos }
delete newToDos[key]
setToDos(newToDos)
await saveToDos(newToDos)
}
}
]);
}
✔ 네이티브팀의 Alert.alert을 사용해서 사용자에게 확인 메세지를 보냅니다.
Alert.alert("제목", "확인 메시지", [ 버튼1, 버튼2 ] )
⦁ To Do 객체 Map으로 표시하기
<ScrollView>
{
Object.keys(toDos).map((key) => (
<View style={styles.toDo} key={key}>
<Text style={styles.toDoText} >{toDos[key].text}</Text>
</View>
))}
</ScrollView>
✔ keys() 메서드를 사용해서 오브젝트의 key값만 추출하고,
map을 사용해서 반복합니다. (for문과 비슷합니다.)
✔ https://reactnative.dev/docs/0.60/scrollview
네이티브 팀에서 제공하는 스크롤 뷰를 사용하면
화면을 위 아래로 스크롤 할 수 있습니다.
📝 체크박스 선택 시 완료 기능
https://callstack.github.io/react-native-paper/checkbox.html
✔ 체크박스는 리액트 네이티브 UI 라이브러리인 react-native-paper 를 사용했습니다.
사용하기 편리하고 디자인이 예뻐요 !
⦁ 소스코드
//Todo 체크 메서드
const checkToDo = async (key) => {
const newToDos = { ...toDos }
const temp = newToDos[key].checked
newToDos[key].checked = !temp
setToDos(newToDos)
await saveToDos(newToDos)
}
✔ 체크 선택시 오브젝트의 checked 값을 반대로 변경해줍니다.
// style안에도 삼항연산자 가능
<Text style={{...styles.toDoText,
textDecorationLine: toDos[key].checked? 'line-through' : null,
color : toDos[key].checked ? 'green': 'white'}} >
{toDos[key].text}
</Text>
✔ style 안에도 위처럼 삼항연산자가 가능합니다.
체크가 되어있으면 글자 색을 변경해주고 line-through CSS를 적용해 선을 긋습니다.
📝 AsyncStorage
https://react-native-async-storage.github.io/async-storage/docs/usage/
✔ 리액트 네이티브 팀이 더 이상 지원하지 않기 때문에 외부 라이브러리를 사용해야합니다.
npm install @react-native-async-storage/async-storage
import AsyncStorage from '@react-native-async-storage/async-storage';
⦁ 사용하는 이유
데이터를 사용자의 디바이스에 저장하기 위해 사용합니다.
생성한 To Do 리스트가 앱을 재부팅하거나 새로고침해도 존재하게 됩니다.
⦁ 사용 방법
리액트의 Local Storage와 굉장히 비슷합니다.
하지만 String만 저장할 수 있기 때문에 적절한 형 변환이 필요합니다.
//async스토리지
const saveToDos = async (toSave) => {
await AsyncStorage.setItem(STORAGE_KEY, JSON.stringify(toSave)) //Object to String
}
const loadToDos = async () => {
try {
const s = await AsyncStorage.getItem(STORAGE_KEY)
//String to Object
setToDos(JSON.parse(s))
} catch (e) {
console.log(e)
}
}
🧾변환하는 메서드
JSON.stringify() : 오브젝트 to String
JSON.parse() : String to 오브젝트
⦁ 📝 전체 소스코드 보기
import { StatusBar } from 'expo-status-bar';
import { useEffect, useState } from 'react';
import { StyleSheet, Text, View, TouchableOpacity, TextInput, ScrollView, Alert, Platform } from 'react-native';
import { theme } from './color';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { Fontisto } from '@expo/vector-icons';
import { Checkbox } from 'react-native-paper';
import * as React from 'react';
const STORAGE_KEY = "@toDos";
export default function App() {
const [working, setWorking] = useState(true);
const travle = () => setWorking(false);
const work = () => setWorking(true);
const [checked, setChecked] = React.useState(false);
//todolist obj
const [toDos, setToDos] = useState({});
//텍스트인풋
const [text, setText] = useState("");
const onChangeText = (payload) => setText(payload);
//async스토리지
const saveToDos = async (toSave) => {
await AsyncStorage.setItem(STORAGE_KEY, JSON.stringify(toSave)) //Object to String
}
const loadToDos = async () => {
try {
const s = await AsyncStorage.getItem(STORAGE_KEY)
//String to Object
setToDos(JSON.parse(s))
} catch (e) {
console.log(e)
}
}
useEffect(() => {
loadToDos();
}, [])
//Submit
const addToDo = async () => {
if (text === "") {
return
}
//todo 저장 Object.assign 오브젝트 합치기
const newToDos = { ...toDos, [Date.now()]: { text, working, checked} }
setToDos(newToDos)
//스토리지에 저장
saveToDos(newToDos)
//텍스트 초기화
setText("")
}
//Todo 삭제
//삭제하기 위해 원래 오브젝트 복제하고 삭제하고 set
const deleteToDo = (key) => {
if(Platform.OS === "web"){
const ok = confirm("delete To do?");
if(ok){
const newToDos = { ...toDos }
delete newToDos[key]
setToDos(newToDos)
saveToDos(newToDos)
}
}
Alert.alert("Delete To Do", "Are you sure?", [
{ text: "Cancel" },
{
text: "I'm Sure", onPress: async () => {
const newToDos = { ...toDos }
delete newToDos[key]
setToDos(newToDos)
await saveToDos(newToDos)
}
}
]);
}
//Todo 체크
const checkToDo = async (key) => {
const newToDos = { ...toDos }
const temp = newToDos[key].checked
newToDos[key].checked = !temp
setToDos(newToDos)
await saveToDos(newToDos)
}
console.log()
return (
<View style={styles.container}>
<StatusBar style="auto" />
<View style={styles.header}>
<TouchableOpacity onPress={work}>
<Text style={{ ...styles.btnText, color: working ? "white" : theme.grey }}>Work</Text>
</TouchableOpacity>
<TouchableOpacity onPress={travle}>
<Text style={{ ...styles.btnText, color: working ? theme.grey : "white" }}>Travel</Text>
</TouchableOpacity>
</View>
<TextInput
returnKeyType='done'
onSubmitEditing={addToDo}
onChangeText={onChangeText}
placeholder={working ? "Add a To Do" : "어디로 여행갈까?"}
style={styles.input}>
</TextInput>
<ScrollView>
{
Object.keys(toDos).map((key) => (
toDos[key].working === working ?
<View style={styles.toDo} key={key}>
<Text style={{...styles.toDoText,
textDecorationLine: toDos[key].checked? 'line-through' : null,
color : toDos[key].checked ? 'green': 'white'}} >
{toDos[key].text}
</Text>
<View style={styles.btnView}>
<Checkbox key = {key}
status={toDos[key].checked ? 'checked' : 'unchecked'}
onPress={() => {
checkToDo(key);
}}
color="green"
/>
<TouchableOpacity onPress={() => deleteToDo(key)}>
<Fontisto name="trash" size={18} color="white" />
</TouchableOpacity>
</View>
</View>
: null
))}
</ScrollView>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: theme.bg,
paddingHorizontal: 20,
},
header: {
justifyContent: "space-between",
flexDirection: "row",
marginTop: 100
},
btnText: {
fontSize: 44,
fontWeight: "600",
},
input: {
backgroundColor: "white",
paddingVertical: 15,
paddingHorizontal: 20,
borderRadius: 30,
marginTop: 20,
fontSize: 18,
marginBottom: 15,
},
toDo: {
flex:1,
backgroundColor: theme.grey,
marginBottom: 10,
paddingVertical: 20,
paddingHorizontal: 30,
borderRadius: 15,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
},
toDoText: {
flex:0.7,
fontSize: 16,
fontWeight: "500",
},
btnView:{
flex:0.3,
flexDirection:"row",
alignItems:'center',
justifyContent:'space-around',
},
});
📝 color.js (theme)
export const theme = {
bg:"black",
grey:"#3A3D40",
toDoBg:"#5C5C60",
};
리액트 네이티브를 이용하면
안드로이드, IOS 심지어
웹과 Mac, Window까지 개발이 가능하다고 합니다.
아직까지 성능 면에서 완전히 네이티브를
이길 수는 없다고 하지만 러닝 커브도 적고
여러 플랫폼마다 개발해야 하는 수고가 없어지기 때문에
공부할 가치가 충분한 것 같습니다!
무엇보다 재미있습니다. 😆
참고 문헌 :
'Front-End > React Native' 카테고리의 다른 글
[React Native] 리액트 네이티브 링크 Linking 사용방법 (0) | 2022.11.17 |
---|---|
[React Native] 리액트 네이티브 라인 차트 만들기 (react-native-chart-kit) (0) | 2022.10.31 |
[React Native] 리액트 네이티브 코드 적용 안되는 이슈 해결 (캐시 초기화) (2) | 2022.10.26 |
[React Native] 리액트 네이티브 Navigation 사용하기 (라우팅) (0) | 2022.10.21 |
[React Native] 리액트 네이티브 안드로이드 실행 방법 (Open Android) (0) | 2022.10.21 |