*이때 오류나는 경우 터미널을 Powershell이 아닌 Command Prompt로 변경
개발하기 위해 폴더 이동
expo 시작
*Expo 클라이언트 앱으로 개발을 진행할 것(같은 와이파이 상에서 연결이 안되는 문제 : LAN → Tunnel로 변경 후 새 QR 코드 인식해서 해결)
→ 구글 플레이스토어에서 앱 다운 후 QR 인식 (동일한 WIFI 연결)
Expo 파일 간단 설명
assets 폴더 : 이미지 폴더
node_modules : 도구 폴더
App.js : 화면이 되는 파일
app.json : 앱 배포 시 설명서
React Native 태그, Styles, Flex
App.js
import { StatusBar } from 'expo-status-bar';
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
//메인화면
export default function App() {
//화면 반환
return (
<View style={styles.container}>
<Text>Open up App.js to start working on your app!</Text>
<StatusBar style="auto" />
</View>
);
}
//화면을 꾸밈
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
<View>, <Text> 등 꺽쇠를 이용한 부분 → JSX라고 함 (화면을 그리는 문법)
<View> </View> 처럼 태그로 화면의 한 영역을 구성할 때 → 엘리먼트라고 함
사용할 태그는 import를 이용해서 가져온다.
import { StyleSheet, Text, View } from 'react-native';
모든 엘리먼트는 감싸는 최상위 엘리먼트가 있어야 한다.
return을 이용해 렌더링할 때 항상 소괄호로 감싸져있어야 한다.
export default function App() {
return (
<View style={styles.container}>
<Text>Open up App.js to start working on your app!</Text>
<StatusBar style="auto" />
</View>
);
}
View 태그
<View> : 화면의 영역을 잡아주는 엘리먼트
Text 태그
<Text> : 앱에 글을 작성하기 위해 사용하는 엘리먼트
문자는 반드시 Text 태그 안에 써야함!
ScrollView 태그
import React from 'react';
import { StyleSheet, Text, View, ScrollView } from 'react-native';
export default function App() {
return (
<ScrollView style={styles.container}>
<View style={styles.textContainer}>
<Text style={styles.textStyle}>영역을 충분히 갖는 텍스트 입니다!</Text>
</View>
<View style={styles.textContainer}>
<Text style={styles.textStyle}>영역을 충분히 갖는 텍스트 입니다!</Text>
</View>
<View style={styles.textContainer}>
<Text style={styles.textStyle}>영역을 충분히 갖는 텍스트 입니다!</Text>
</View>
<View style={styles.textContainer}>
<Text style={styles.textStyle}>영역을 충분히 갖는 텍스트 입니다!</Text>
</View>
<View style={styles.textContainer}>
<Text style={styles.textStyle}>영역을 충분히 갖는 텍스트 입니다!</Text>
</View>
<View style={styles.textContainer}>
<Text style={styles.textStyle}>영역을 충분히 갖는 텍스트 입니다!</Text>
</View>
<View style={styles.textContainer}>
<Text style={styles.textStyle}>영역을 충분히 갖는 텍스트 입니다!</Text>
</View>
<View style={styles.textContainer}>
<Text style={styles.textStyle}>영역을 충분히 갖는 텍스트 입니다!</Text>
</View>
</ScrollView>
);
}
<TouchableOpacity> : 버튼과 비슷한 역할을 함, 대신 화면에 영향을 주지 않음
임의의 영역과 디자인에 버튼 기능을 추가할 때 주로 사용할 태그
Image 태그
import React from 'react';
import { StyleSheet, Text, View, Image } from 'react-native';
//이렇게 상단에 가져와 사용할 이미지를 불러옵니다
import favicon from "./assets/favicon.png"
export default function App() {
return (
<View style={styles.container}>
<Image
source={favicon}
// 사용설명서에 나와 있는 resizeMode 속성 값을 그대로 넣어 적용합니다
resizeMode={"repeat"}
style={styles.imageStyle}
/>
</View>
);
}
<Image> : 이미지를 넣는 태그
import를 통해 assets 폴더의 이미지 파일을 가져온 다음 사용
source : import한 이미지
resizeMode : 이미지를 보여주는 방식-repeat : 이미지가 반복-그 외 contain, stretch, center
-cover : 이미지가 화면 전체에 꽉 차게 보여줌
import React from 'react';
import { StyleSheet, Text, View, Image } from 'react-native';
//이렇게 상단에 가져와 사용할 이미지를 불러옵니다
import favicon from "./assets/favicon.png"
export default function App() {
return (
<View style={styles.container}>
{/*이미지 태그 soruce 부분에 가져온 미지 이름을 넣습니다 */}
<Image
source={{uri:'https://images.unsplash.com/photo-1424819827928-55f0c8497861?fit=crop&w=600&h=600%27'}}
// 사용설명서에 나와 있는 resizeMode 속성 값을 그대로 넣어 적용합니다
resizeMode={"cover"}
style={styles.imageStyle}
/>
</View>
);
}
Could not find setVariantDir after 4.3.6 · Issue #176 · google/play-services-plugins
Describe the bug A problem occurred configuring project ':mobile'. > Failed to notify project evaluation listener. > com.android.build.gradle.internal.crash.ExternalApiUsageException:...
github.com
4.3.6 버전에서 삭제된 메소드를 여전히 호출하는 메소드가 있었던 것..
해당 이슈가 발생한지 얼마 안된 시점이었는데
다행히도(?) 며칠 만에 버그 픽스가 된 4.3.8 버전이 나왔고 Firebase DB와 연동을 마쳤다.
5월 중순 : 개발 시작 (레이아웃 구성)
Figma 툴을 이용해서 작성한 앱 화면 프로토타입을 바탕으로
팀원들이 각자 맡은 역할대로 안드로이드 스튜디오 안에서 레이아웃을 구현했다.
현재는 디자인 고려 안하고 화면 해상도에 맞게 레이아웃, 버튼 배치만 한 상태이다. (위의 이미지는 메인 화면)
디자인보단 기능 구현이 우선이라고 생각해서 색 조합이나 이미지뷰는 나중에 고려할 듯 하다.
나는 앱의 주요 기능들 중 일기장 부분 레이아웃을 맡아 일기 내용/작성/수정 페이지 레이아웃을 구현했다.
5월 말 : 일기장 기능 구현 중
우리 팀이 구현할 기능들 중 가장 먼저 일기장 기능을 다뤄보기로 했다.
Firebase DB 사용법을 찾아보며 간단히 일기를 등록하고, 삭제하는 것까지 구현했다.
그런데 문제점이 있었으니..
1. MaterialCalendarView
커스텀 캘린더뷰를 구현하기 위해 MaterialCalendar을 활용하려고 하는데
생각보다 MaterialCalendarView와 관련된 자료들이 많이 없으며
특정 부분에서 NullPointerException 에러가 발생하는데 해당 오류의 원인을 찾지 못했다.
2. ViewModel
우리는 MVVM(Model-View-ViewModel) 패턴을 이용하여 개발을 진행하고자 한다.
그런데 안드로이드 스튜디오의 ViewModel과 관련된 자료들을 찾아보았을 때
Kotlin 언어를 이용한 자료들이 대부분.. Java를 사용하여 설명을 작성한 경우가 거의 없었다ㅠㅠ
크로스 플랫폼 앱 : JS 하나의 언어로 두 개의 플랫폼의 앱을 제작 (강의에서 다룰 것)
리액트 네이티브 (React Native)
크로스 플랫폼 앱 개발 언어 중 하나
페이스북에서 만들었고 지원함
리액트(React.js) 라이브럴/프레임워크를 기반으로 앱 제작하는 기술
자바스크립트(JS)로 개발 가능
Javascript 기초
js는 웹 개발에서만 사용되는 기술이 아닌가? → 아니다. (리액트 네이티브가 js 기반)
js를 학습하면 웹/앱 개발 가능
크롬 F12 콘솔창에서 실습 진행
console.log("hello world")
console.log → 화면에 값을 출력하는 함수
F12 콘솔창 점 세개 버튼 클릭 → Dock side에서 팝업으로 띄울 수 있음
상단의 clear console 버튼을 클릭해서 콘솔창 정리 가능 (변수는 그대로 유지)
Shift+Enter로 여러 줄 입력 가능
변수
변수 선언
let 이용
let num = 20
console.log(num) //20
숫자형 및 문자형 변수
let a = 1
let b = 2
let c = a + b
console.log(c) //3
let first = 'yewon'
let last = 'jeong' //큰따옴표 or 작은따옴표로 문자열
console.log(first+" "+last) //yewon jeong
변수 이름 규칙
let firstName //카멜 케이스 (camel case)
let first_name // 스네이크 케이스 (snake case)
const 변수
값을 재할당할 수 없는 변수
let num = 100
const num2 = 1000
num = 200
num2 = 2000
console.log(num)
console.log(num2)
→ 위의 코드 실행 시 오류 발생
const로 선언한 변수는 값을 바꿀 수 없기 때문
리스트
리스트란?
여러 데이터들을 담을 수 있는 자료형
인덱스는 0부터 시작
let a_list = [1,2,3,4,'yewon',6,'jeong']
//두 번째 값을 꺼낸다 (인덱스 1)
console.log(a_list[1]) //2
특정 문자로 문자열을 나누기 : split() (ex. split('.') , split('@') ...)
문자열 합치기 : join()
let myname = "jeongyewon"
console.log(myname.toUpperCase()) //JEONGYEWON
let email = "yewon@gmail.com"
console.log(email.split('@')) //["yewon", "gmail.com"] 리스트 형식
console.log(email.split('@')[1]) //gmail.com
console.log(email.split('@')[1].split('.')) //["gmail", "com"]
console.log(email.split('@')[1].split('.')[1]) //com
let txt = "서울시-마포구-망원동"
let names = txt.split("-")
console.log(names) //["서울시", "마포구", "망원동"]
console.log(names.join('>')) //서울시>마포구>망원동
함수 만들기
함수 이름 지정
파라미터 이용
return을 이용하여 리턴값 반환
function funcName(){
console.log("함수")
}
funcName() //함수
function sum(num1,num2){
console.log(num1+num2)
}
sum(10,20) //30
function minus(num1,num2){
return num1-num2
}
let result = minus(100,10) //변수에 리턴값 저장
console.log(result) //90
리터럴 함수 선언 방식 : 변수에 함수를 저장
let a = function(){
console.log("리터럴 함수 방식")
}
a()
function is_adult(age, sex){
if(age > 20 && sex == '여'){
alert('성인 여성')
} else if (age > 20 && sex == '남') {
alert('성인 남성')
} else {
alert('청소년이에요')
}
}
OR 연산자
하나라도 참이면 참
function is_adult(age, sex){
if (age > 65 || age < 10) {
alert('탑승하실 수 없습니다')
} else if(age > 20 && sex == '여'){
alert('성인 여성')
} else if (age > 20 && sex == '남') {
alert('성인 남성')
} else {
alert('청소년이에요')
}
}
is_adult(25,'남') //성인 남성
반복문
for문
시작 조건, 반복 조건, 더하기
for(let i=0;i<10;i++){
console.log(i)
}
// 0~9까지 출력
리스트와 같이 이용
let people=['철수','영희','민수','형준','기남','동희']
for(let i=0;i<people.length;i++){
console.log(people[i])
}
리스트, 딕셔너리 복합 구조
let scores = [
{'name':'철수', 'score':90},
{'name':'영희', 'score':85},
{'name':'민수', 'score':70},
{'name':'형준', 'score':50},
{'name':'기남', 'score':68},
{'name':'동희', 'score':30},
]
for (let i=0;i<scores.length;i++){
if(scores[i]['score']<70){
console.log(scores[i]); //70점 미만의 점수인 경우에만 출력
}
}
앱개발에 자주 쓰이는 Javascript
화살표 함수
함수를 짧게 선언할 수 있음
function 대신 () ⇒ 이용
let a = () => {
console.log("arrow function")
}
a();
비구조 할당 방식
key에 따옴표를 붙이지 않아도 됨
. 을 이용해서 딕셔너리 안의 값 접근 가능
딕셔너리 키와 값을 빠르게 꺼낼 수 있음
let blog = {
owner : "noah",
url : "noahlogs.tistory.com",
getPost() {
console.log("ES6 문법 정리");
}
};
//기존 할당 방식
let owner = blog['owner']
let owner = blog.owner //동일한 결과
let getPost = blog.getPost()
//비구조 할당 방식
let { owner, getPost } = blog;
console.log(owner)
//비구조 할당 방식
let blogFunction = ({owner,url,getPost}) => {
console.log(owner)
console.log(url)
console.log(getPost())
}
blogFunction(blog)
백틱 (`) 사용
줄바꿈을 자유롭게
const message = `줄바꿈
자유롭게 가능`
message //"줄바꿈\n자유롭게 가능"
문자열 안에 변수 넣기
let name="정예원"
let str=`내 이름은 ${name}`
console.log(str) //내 이름은 정예원
딕셔너리 구성을 간단하게
변수 이름과 key가 같은 경우
기존에는 딕셔너리 내에서 name:name, job:job으로 썼음
let name="정예원"
let job = "개발자"
let dic={
name,
job
}
console.log(dic) //{name: "정예원", job: "개발자"}
map
기존 코드
//기존 코드
let numbers = [1,2,3,4,5,6,7]
for(let i=0; i<numbers.length; i++){
console.log(numbers[i])
}
→ 리스트 길이 값을 알아야 for문 사용 가능
map 활용
let numbers = [1,2,3,4,5,6,7]
numbers.map((value,i) => {
console.log(value,i)
})
//위와 동일한 코드
numbers.map(function(value,i){
console.log(value,i)
}
JS 파일 모듈화
export : 자바스크립트 값, 함수, 딕셔너리, JS 파일 자체를 외부로 내보내기
import : JS 파일로 불러오기
//util.js 파일
export function times(x){
return x*x
}
export function plusTwo(number){
return number + 2
}
//index.js 파일
import {times, plusTwo} from './util.js'
console.log(times(2))
console.log(plusTwo(3))
export default
//util.js 파일
export default function times(x){
return x*x
}
//app.js 파일
import k from './util.js'
console.log(k(4)) //16