-
[앱개발 종합]리액트-네이티브 Part.1카테고리 없음 2021. 7. 6. 17:41
2주차. 리액트-네이티브로 앱개발하기
<리액트-네이티브란?>
리액트 네이티브는 우리가 배운 자바스크립트 언어 하나로 안드로이드 앱과 iOS앱 두 가지 모두 만들어주는 라이브러리(개발 도구) 입니다.
<Expo란?>
리액트 네이티브 앱 개발을 수월하게 도와주는 도구가 존재하는데, 이것이 바로 Expo입니다. 리액트 네이티브로 앱을 개발할 때, 안드로이드 & iOS 코드를 건드려야 하는 대부분의 상황들을 안 건드려도 되게끔 도와주는 툴입니다. 또한 앱 개발을 편리하게 해주는 도구들이 많이 존재합니다.
Expo를 이용한 앱 개발 순서
1. Expo명령어 설치하기
2. 로컬에 Expo계정 세팅하기
3. expo init 명령어로 기본앱 생성하기
: 터미널에서 expo init 명령어로 프로젝트를 생성하면 Expo로 부터 기본 골격이 짜여져 있는 코드가 생성되는데, 각 파일들을 간단히 알아보자면.. asset - 앱이 동작되는데 기본적으로 가지고 있는 이미지나 아이콘을 담는 폴더, nodes_modules - 앱 개발을 하면서 설치할 라이브러리들이 저장되는 장소, App.js - 앱의 메인 화면, app.json - 앱이 가지는 기본 정보들을 설정하는 파일
4. expo start로 Expo 앱 실행하기
: 터미널에서 expo start 명령어를 실행해주면 QR코드가 생성됩니다.
5. 휴대폰에 설치한 Expo 클라이언트 앱으로 Expo 앱 실행
휴대폰으로 QR코드를 인식시키면 바로 앱 화면이 생성되는 것을 확인할 수 있습니다.
추가) App.js로 개발하다보면 노란 경고창이 뜰 때가 있는데, 이 때 App.js에 다음과 같은 코드 한줄을 통해 안보이게 할 수 있습니다.
console.disableYellowBox = true;
<JSX 문법>
HTML태그와 비슷하게 생긴 JSX는 App.js의 화면을 구성하는데 필요한 태그 문법입니다. 리액트 네이티브에서 return은 작성한 JSX문법으로 구성된 화면을 앱에 보여주는 역할을 하는 겁니다. (렌더링)
1. <View></View>
: 화면의 영역(레이아웃)을 잡아주는 엘리먼트 입니다. App.js에서 View는 화면 전체 영역을 가지게 됩니다. View를 통 해 화면을 분할하기도 하는데 이때는 StyleSheet가 필요하답니다.
2. <Text></Text>
: 앱에 텍스트를 작성하려면 반드시 사용해야 하는 엘리먼트입니다.
3. <ScrollView</ScrollView>
: 앱은 보통 많은 양의 정보를 담고 있습니다. 그래서 앱 화면을 벗어나는 경우가 많은데 이때 ScrollView를 사용하면 스크롤이 가능해지면서 모든 컨텐츠를 볼 수 있습니다.
4. <Button/>
: 앱에서 버튼을 누르면 팝업이 뜨거나, 다른 페이지로 이동하는데 Button을 통해 쉽게 버튼을 만들 수 있습니다.
버튼이 눌리면 팝업이 뜨게 하기 위해서는 onPress속성을 통해 팝업이 뜨게 할 수 있습니다.
onPress={function(){ //onPress={()=>{ Alert.alert('팝업 알람입니다!!') }}
5. <TouchableOpacity/>
: 영역을 충분히 가지는 텍스트 카드 부분이라고 할 수 있습니다. 버튼의 역할을 하는 이 엘리먼트는 화면의 임의의 영
역과 디자인에 버튼 기능을 할 때 사용합니다.
6. <Image>
: 앱에 이미지를 삽입할 때 사용하는 태그로, 두가지 방식으로 이미지를 사용할 수 있습니다.
- assets폴더에 있는 이미지를 가져와서(import) 사용하는 방법 ex) import favicon from "./assets/favicon.png"
- 외부 이미지의 uri를 넣어서 사용하는 방법
<JSX문법 - StyleSheet>
구성한 앱화면을 꾸미기 위한 StyleSheet은 결국 객체(딕셔너리)를 하나 만드는데, 이쁜 옷들을 입혀주는 객체 입니다. 이 객체에 옷을 사용법대로 생성한 다음 잘 정리해 두고, JSX 엘리먼트에서 사용합니다. 각각의 태그별로 여러 Style을 가지고 있기 때문에 중요한 것 위주로 설명하고, 나머지는 직접 구성하면서 설명해보도록 하겠습니다.
1. margin과 padding
: margin과 padding은 다음 이미지들 처럼, 영역의 바깥과 안의 여백을 결정합니다. margin은 어느정도 여백을 두고 배치할 것인지를 의미하고, padding은 어느정도의 여백을 두어 내부의 요소를 위치시킬 것인지를 결정합니다.
2. 컨텐츠의 위치 : flex
: flex는 영역을 차지하는 속성인데, 레이아웃을 상대적으로 결정하게 됩니다. 만약 전체 화면이 1이고 A가 1, B가 2ㅇ 니 경우에 A는 전체화면의 1/3을 차지하고, B는 전체화면의 2/3를 차지하게 되는 것입니다. 즉, 같은 레벨의 엘리먼 트들의 flex 합을 각자의 flex 속성값 대로 가져갑니다.
3. flexDirection
: 자리잡은 영역의 방향을 의미합니다. flexDirection:"row"는 가로 방향으로 , "colum"은 세로 방향으로 영역을 배치합 니다. 기본값은 colum입니다.
4. justifyContent
: justifyContent는 flexDirection과 동일한 방향으로 정렬하는 속성입니다 flexDirection: 'column'에서 justifyContent는 상하 정렬, flexDirection: 'row'에서 justifyContent는 좌우 정렬을 뜻합니다. flex-start, center, flex-end, space- between, space-around 속성을 가집니다.
5. alignItems
: Align Items는 Flex Direction과 수직한 방향(반대 방향이라고 생각하면 편합니다)으로 정렬하는 속성입니다. flexDirection: 'column'에서 alignItems는 좌우 정렬, flexDirection: 'row'에서 alignItems는 상하 정렬을 뜻합니다 flex-start, center, flex-end, stretch 속성을 가집니다.
<메인 화면 꾸미기>
import React from 'react'; import { StyleSheet, Text, View, ScrollView, Image, TouchableOpacity } from 'react-native'; export default function App() { return ( <ScrollView style={styles.container}> <Text style={styles.textStyle}>나만의 꿀팁</Text> <Image source={{uri:'https://firebasestorage.googleapis.com/v0/b/sparta-image.appspot.com/o/lecture%2Fmain.png?alt=media&token=8e5eb78d-19ee-4359-9209-347d125b322c'}} // 사용설명서에 나와 있는 resizeMode 속성 값을 그대로 넣어 적용합니다 resizeMode={"cover"} style={styles.mainImage} /> <ScrollView style={styles.menucontainer} horizontal indicatorStyle={"white"} > //스크롤 뷰를 가로로 하기 위한 속성 <TouchableOpacity style={styles.textContainer1}> <Text style={styles.menu_textStyle}>생활</Text> </TouchableOpacity> <TouchableOpacity style={styles.textContainer2}> <Text style={styles.menu_textStyle}>재테크</Text> </TouchableOpacity> <TouchableOpacity style={styles.textContainer3}> <Text style={styles.menu_textStyle}>반려견</Text> </TouchableOpacity> <TouchableOpacity style={styles.textContainer4}> <Text style={styles.menu_textStyle}>꿀팁 찜</Text> </TouchableOpacity> </ScrollView> <View style={styles.feed}> <Image style={styles.feedImage} source={{uri:"https://firebasestorage.googleapis.com/v0/b/sparta-image.appspot.com/o/lecture%2Fpizza.png?alt=media&token=1a099927-d818-45d4-b48a-7906fd0d2ad3"}}/> <View style={styles.feedText}> <Text style={styles.feedTitle}>먹다 남은 피자를 촉촉하게!</Text> <Text style={styles.feedInfo} numberOfLines={3}>먹다 남은 피자는 수분이 날라가기 때문에 처음처럼 맛있게 먹을 수 없는데요. 이럴 경우 그릇에 물을 받아 전자레인지 안에서 1분 30초에서 2분 정도 함께 돌려주면 촉촉하게 먹을 수 있습니다. 물이 전자레인지 안에서 수증기를 일으키고, 피자에 촉촉함을 더해줍니다.</Text> <Text style={styles.feedDate}>2020.09.09</Text> </View> </View> </ScrollView> ); } const styles = StyleSheet.create({ container: { backgroundColor: '#fff', }, textStyle: { color:"black", fontSize:20, fontWeight:"700", marginLeft:20, marginTop:50 }, mainImage: { width:'90%', height:200, borderRadius:10, marginTop:20, alignSelf:"center" }, menucontainer: { marginTop:20 }, textContainer1: { width:100, height:60, backgroundColor:'#fdc453', borderRadius:15, margin:10, padding:20 }, textContainer2: { width:100, height:60, backgroundColor:'#fe8d6f', borderRadius:15, margin:10, padding:20 }, textContainer3: { width:100, height:60, backgroundColor:'#9adbc5', borderRadius:15, margin:10, padding:20 }, textContainer4: { width:100, height:60, backgroundColor:'#f886a8', borderRadius:15, margin:10, padding:20 }, menu_textStyle: { color:"white", //글자의 크기를 결정합니다 fontSize:15, fontWeight:"700", textAlign:'center' }, feed:{ marginTop:20, flex:1, flexDirection:"row", borderBottomWidth:1, borderBottomColor:"gray", paddingBottom:10 }, feedImage: { flex:1, width:100, height:100, borderRadius:10, }, feedText: { flex:2, flexDirection:"column", marginLeft:10, }, feedTitle: { fontSize:20, fontWeight:"700" }, feedInfo: { fontSize:15 }, feedDate: { fontSize:10, color:"gray", } });
<앱과 자바스크립트>
-모듈과 반복문 (data.json)
위에서는 피드(카드)에 해당하는 내용을 직접적으로 넣어주고 있는데 이 데이터가 많다면 어떻게 할까요? data.json에 준비된 데이터가 존재한다면 반복문을 통해 담겨져 있는 데이터를 사용할 수 있습니다.
//data.json { "tip":[ { "idx":0, "category":"생활", "title":"먹다 남은 피자를 촉촉하게!", "image":"https://firebasestorage.googleapis.com/v0/b/sparta-image.appspot.com/o/lecture%2Fpizza.png?alt=media&token=1a099927-d818-45d4-b48a-7906fd0d2ad3", "desc":"먹다 남은 피자는 수분이 날라가기 때문에 처음처럼 맛있게 먹을 수 없는데요. 이럴 경우 그릇에 물을 받아 전자레인지 안에서 1분 30초에서 2분 정도 함께 돌려주면 촉촉하게 먹을 수 있습니다. 물이 전자레인지 안에서 수증기를 일으키고, 피자에 촉촉함을 더해줍니다.", "date":"2020.09.09" }, .... }
준비된 데이터를 App.js에서 불러오기 위해서는 "import data from './data.json';"로 불러오게 됩니다. 이렇게 불러온 데이터를 내가 구성하고 싶은대로 구성하기 위해 반복문을 사용하는데 지난 시간에 학습했던 map을 이용하면 다음과 같이 코드를 구성할 수 있습니다.
//App.js <View style={styles.cardContainer}> {/* 하나의 카드 영역을 나타내는 View */} { tip.map((content,i)=>{ return (<View style={styles.card} key={i}> <Image style={styles.cardImage} source={{uri:content.image}}/> <View style={styles.cardText}> <Text style={styles.cardTitle} numberOfLines={1}>{content.title}</Text> <Text style={styles.cardDesc} numberOfLines={3}>{content.desc}</Text> <Text style={styles.cardDate}>{content.date}</Text> </View> </View>) }) } </View>
-{}표현식
export default function App() { console.disableYellowBox = true; //return 구문 밖에서는 슬래시 두개 방식으로 주석 let tip = data.tip; let todayWeather = 10 + 17; let todayCondition = "흐림" return ( <ScrollView style={styles.container}> <Text style={styles.title}>나만의 꿀팁</Text> <Text style={styles.weather}>오늘의 날씨: {todayWeather + '°C ' + todayCondition} </Text> //{}표현식 사용 <Image style={styles.mainImage} source={main}/>
-if문
if문, 조건문을 통해서 홀수의 데이터 혹은 짝수의 데이터만 다르게 보이게 할 수 있습니다. 위에서 작성하였던 코드에 삼항 연산자를 사용하여 홀수 데이터와 짝수 데이터를 구분하는 코드는 다음과 같습니다.
<View style={styles.cardContainer}> {/* 하나의 카드 영역을 나타내는 View */} { tip.map((content,i)=>{ return i % 2 == 0 ? (<View style={styles.cardEven} key={i}> //삼항연산자 사용 <Image style={styles.cardImage} source={{uri:content.image}}/> <View style={styles.cardText}> <Text style={styles.cardTitle} numberOfLines={1}>{content.title}</Text> <Text style={styles.cardDesc} numberOfLines={3}>{content.desc}</Text> <Text style={styles.cardDate}>{content.date}</Text> </View> </View>) : (<View style={styles.cardOdd} key={i}> <Image style={styles.cardImage} source={{uri:content.image}}/> <View style={styles.cardText}> <Text style={styles.cardTitle} numberOfLines={1}>{content.title}</Text> <Text style={styles.cardDesc} numberOfLines={3}>{content.desc}</Text> <Text style={styles.cardDate}>{content.date}</Text> </View> </View>) }) } </View>
<어바웃 화면 만들기>
import React from 'react'; import main from '../assets/main.png'; import { StyleSheet, Text, View, Image, TouchableOpacity, ScrollView, Alert, Linking} from 'react-native'; export default function AboutPage() { console.disableYellowBox = true; const customAlert = () => { //Alert.alert("OOO의 인스타계정으로 이동합니다.") Linking.openURL('https://www.instagram.com/instaId/') } return ( <View style={styles.container}> <Text style={styles.title}>HI! 스파르타코딩 앱개발반에 오신걸 환영합니다</Text> <View style={styles.textContainer}> <Image style={styles.Image} source={{uri:"https://firebasestorage.googleapis.com/v0/b/sparta-image.appspot.com/o/lecture%2FaboutImage.png?alt=media&token=13e1c4f6-b802-4975-9773-e305fc7475c4"}}/> <Text style={styles.context1}>많은 내용을 간결하게 담아내려 노력했습니다!</Text> <Text style={styles.context2}>꼭 완주하셔서 꼭 여러분 것으로 만들어가시길 바랍니다.</Text> <TouchableOpacity style={styles.button} onPress={customAlert}><Text style={styles.ButtonText}>OOO의 인스타 계정</Text></TouchableOpacity> </View> </View> ); } const styles = StyleSheet.create({ container: { height: 1000, backgroundColor: '#260074', }, title: { fontSize: 40, fontWeight: '700', textAlign:'center', color:'#fff', marginTop:100, marginLeft:15, marginRight:15 }, textContainer: { marginTop:50, marginLeft:30, marginRight:30, marginBottom:50, height:550, paddingTop: 50, paddingLeft: 25, paddingRight: 25, borderRadius:30, backgroundColor:"#fff", }, Image: { width:'50%', height:150, borderRadius:25, marginTop:20, alignSelf:"center" }, context1: { fontSize: 25, fontWeight: '700', textAlign:'center', color:'#000', marginTop:20, }, context2: { fontSize: 18, fontWeight: '700', textAlign:'center', color:'#000', marginTop:20, }, button: { width:200, height:70, padding:20, backgroundColor:"#fdc453", borderRadius:15, margin:30, alignSelf:"center" }, ButtonText: { color:"#fff", fontSize: 18, fontWeight:"700", textAlign:"center" }, });