앱개발 종합반 수강을 마치며...

앱개발 종합반을 수강하며 제작한 나만의 꿀팁 앱

수강 계기 및 목적

나는 현재 컴공 4학년에 재학중이고, 앱 또는 웹 개발 프론트엔드 개발자가 되어야겠다고 진로 방향을 생각하고 있었다.

마침 1학기 종강도 했겠다, 방학 때 새롭게 공부를 시작해볼까 했던 터라 "앱개발 종합반"을 신청했다.

 

현재 한이음 프로젝트는 안드로이드 스튜디오를 이용해서 개발을 진행하고 있는데

앱개발 종합반의 강의 계획표를 보니 리액트 네이티브를 다룬다고 나와있었다.

 

리액트 네이티브에 대해 찾아보니 안드로이드/iOS를 모두 지원하는 크로스 플랫폼 앱을 제작할 수 있는 프레임워크라고 한다.

만약 나중에 앱 개발자가 된다면 두 개의 플랫폼을 모두 지원하는

크로스 플랫폼 앱 개발을 하는 것이 유리하고 생산적일거라는 생각이 들었다.

 

또한, 프론트엔드 개발자가 되기 위해서는 자바스크립트에 익숙해지고 잘 다룰 줄 알아야한다고 들었다.

앱개발 종합반 1주차 강의에서 자바스크립트의 기초를 잡고

2주차부터 리액트 네이티브 수업이 진행된다고 하기에 (리액트 네이티브가 자바스크립트를 기반으로 하기 때문)

자바스크립트 찐찐 기초밖에 모르는 상태에서 해당 강의를 수강함으로써

자바스크립트와 친숙해지고 그것을 기반으로 하는 프레임워크를 배우고자 하였다.

 

 

배운 점

올해 캡스톤디자인 프로젝트를 하면서 자바스크립트를 조금 다루긴 했으나,

잘 모르는 상태에서 이곳저곳에서 코드를 긁어오니 너무 어렵게만 느껴졌고

자바스크립트에 대한 약간의 거부감(?)이 드는 상태였다.

 

앞서 말한 것처럼 앱개발 종합반 1주차에는 자바스크립트의 기초를 배웠다.

리스트와 딕셔너리 자료구조를 배우고, 이것의 복합 구조가 JSON 데이터의 구조라는 것을 알게 되었다.

JSON이 무엇인지 모르는 상태에서 얼핏 봤을 땐 구조가 복잡해보여서 거부감이 들었는데

강의에서 쉽게 접근할 수 있도록 설명을 해주셔서 그런지 "별거 아니네!!" 라며 자신감을 갖게 되었다ㅋㅋㅋ

그리고 강의명이 앱개발 종합반인만큼, 앱개발 시 자주 사용되는 자바스크립트 문법을 따로 알려주셔서 좋았다.

 

2주차에는 본격적으로 리액트 네이티브 프로젝트를 생성하고 Expo를 사용해보았다.

안드로이드 스튜디오에서 개발을 할 때는 수정사항이 있을 때마다

매번 새롭게 빌드하고 실행해야하기 때문에 꽤나 번거로운 작업이 필요한데

Expo 클라이언트 앱을 이용해서 수정사항이 즉시 화면에 반영되는걸 보고 굉장히 편리하다는 생각이 들었다.

그리고 Expo 자체에서 제공해주는 기능들이 많아서 유용하게 활용할 수 있을 것 같았다.

리액트의 태그는 안드로이드 스튜디오의 레이아웃 xml이, 스타일은 CSS가 생각이 나서 어렵지 않게 받아들일 수 있었다.

 

3주차가 앱개발 5주간의 수업 중 가장 어렵게 느껴졌던 강의이다.

리액트의 기본 개념인 컴포넌트, 속성, 상태 중 상태!!가 처음에는 되게 추상적으로 느껴져서 어려웠는데

앱개발 종합반 수업에서 계속 다루는 나만의 꿀팁 앱 예제를 통해 다행히 이해할 수 있었다.

개인적으로 스파르타코딩클럽 강의에서 제공하는 코드스니펫 기능! 너무 좋은거 같다.

코드스니펫이 없었다면 강의에서 다룬 코드를 일일이 타자치고 있었을텐데

전공 강의를 4년간 들으면서 느낀건데, 무념무상으로 이게 뭔지도 모른채 하염없이 타자치고 있는 것보단

차라리 복붙을 해놓고 해당 코드에 대한 이해를 하는 것이 더 낫기 때문!

 

4주차에는 날씨 서버 외부 API를 불러와서 적용하는 방법을 배웠다.

내가 구현하고자 하는 기능을 구글링해보면 해당 기능 관련 API를 쉽게 찾을 수 있으므로

앞으로 개발할 때 다양한 API와 라이브러리를 활용하면 좋겠다는 생각이 들었다.

그리고 Google Firebase를 앱에 연결하고 스토리지, 데이터베이스를 이용해서 서버리스 아키텍처를 구현해보았다.

 

대망의 5주차! 지금까지 배운 내용을 바탕으로 앱을 개발하고나서

최종적으로 배포하기 위해 구글 광고를 앱 화면에 추가하고 구글 플레이스토어에서 배포하는 방법을 배웠다.

관련 자료를 하나하나 찾아가면서 구현하기에는 꽤나 번거로웠을텐데, 강의를 통해 처음부터 끝까지 차근차근 방법을 알 수 있어서 좋았다.

리액트 네이티브를 더 깊이 학습해서 이번에 배운 내용을 바탕으로 추후에 꼭 앱을 출시해보고 싶다~ 

 

향후 목표

수업 시간에 사용되었던 예제인 '나만의 꿀팁' 앱에 다양한 기능을 추가하여 완성도를 높일 것이고

이것과 별도로 앱개발 종합반에서 배운 내용들을 바탕으로 개인 프로젝트 '스케줄 앱'을 완성해볼 것이다!

 

소감

앱개발 종합반에서 자바스크립트와 친숙해지고 리액트를 기반으로 한 리액트 네이티브를 다뤄보았다.

깔끔한 강의 영상과 강의 자료(노션, pdf)를 보며 학습할 수 있어서 만족스러웠고

슬랙 채널 또는 주말에 있는 즉문즉답 시간을 활용하여 Q&A가 이루어지기 때문에

강의 내용 중 이해가 안되는 부분이나 그 외에 각자 공부하다가 막히는 부분을 물어볼 곳이 있다는 점이

스파르타코딩클럽 강의의 가장 큰 장점이지 않을까 싶다.

 

 

이상 앱개발 종합반 완주 후기 끝~

애드몹(AdMob) 설정

  • 앱 내에 구글 배너광고를 쉽게 붙일 수 있도록 도와줌
  • 수익 현황 알려줌
  • 일정 수익 이상이 되면 환전 가능

애드몹 광고 종류

  1. 배너 : 앱 레이아웃 일부를 차지하는 광고
  2. 전면 : 전체 페이지 광고 형식
  3. 리워드 : 광고를 본 사용자에게 리워드 제공
  4. 네이티브 고급 광고 : 맞춤설정 광고 (커스터마이징)

애드몹 설치하기

  • Expo에서 구글 애드몹을 지원해준다.
expo install expo-ads-admob
  • app.json 파일에 다음과 같이 추가한다.
"ios": {
      "supportsTablet": true,
      "buildNumber": "1.0.0",
      "bundleIdentifier": "com.myhoneytip.yewon",
      "config": {
        "googleMobileAdsAppId": ""
      }
  },
"android": {
      "package": "com.myhoneytip.yewon",
      "versionCode": 1,
      "config": {
        "googleMobileAdsAppId": ""
      }
  }
  • googleMobileAdsAppId에 대해서는 자신이 애드몹에 추가한 앱 ID를 입력한다.
  • 안드로이드, iOS 모두 앱을 추가해야함

가로 배너 생성

  • 광고 단위 만들기 - 배너 선택

  • 광고 단위 이름 입력

  • MainPage.js에 다음 코드를 추가한다.
import {Platform} from 'react-native';
import {
  setTestDeviceIDAsync,
  AdMobBanner,
  AdMobInterstitial,
  PublisherBanner,
  AdMobRewarded
} from 'expo-ads-admob';
<View>
{Platform.OS === 'ios' ? (
	<AdMobBanner
		bannerSize="fullBanner"
		servePersonalizedAds={true}
		adUnitID="iOS 광고 단위 아이디"
		style={styles.banner}
	/>
) : (
	<AdMobBanner
		bannerSize="fullBanner"
		servePersonalizedAds={true}
		adUnitID="Android 광고 단위 아이디"
		style={styles.banner}
	/>
)}
</View>
  • bannerSize : 배너 크기 조정
  • servePersonalizedAds : 사용자 맞춤 광고 제공 여부
  • adUnitID : 광고 단위 아이디
  • style을 이용하여 스타일 적용 가능

전면 배너 생성

  • Card.js
import {
  setTestDeviceIDAsync,
  AdMobBanner,
  AdMobInterstitial,
  PublisherBanner,
  AdMobRewarded
} from 'expo-ads-admob';
export default function Card({content,navigation}){

    useEffect(()=>{
        Platform.OS === 'ios' ? AdMobInterstitial.setAdUnitID("광고 단위 ID") : AdMobInterstitial.setAdUnitID("광고 단위 ID")

        AdMobInterstitial.addEventListener("interstitialDidLoad", () =>
            console.log("interstitialDidLoad")
        );
        AdMobInterstitial.addEventListener("interstitialDidFailToLoad", () =>
            console.log("interstitialDidFailToLoad")
        );
        AdMobInterstitial.addEventListener("interstitialDidOpen", () =>
            console.log("interstitialDidOpen")
        );
        AdMobInterstitial.addEventListener("interstitialDidClose", () => {
              //광고가 끝나면 다음 코드 줄이 실행!
            console.log("interstitialDidClose")
            navigation.navigate('DetailPage',{idx:content.idx})
        });
    },[])
    const goDetail = async () =>{

      await AdMobInterstitial.requestAdAsync({ servePersonalizedAds: true});
      await AdMobInterstitial.showAdAsync();
    }

    return(
        <TouchableOpacity style={styles.card} onPress={()=>{goDetail()}}>
            <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>
        </TouchableOpacity>
    )
}

앱을 배포하기 위해 필요한 것들

  1. 앱 로고
  2. 스플래시 스크린
  3. 앱 마켓에 올릴 설명 이미지
  4. 앱 버전 관리, 안드로이드, iOS 인증서 관리 등

스플래시 스크린

  • assets 폴더 안에 있는 splash.png 이미지 수정

로고 준비

  • assets 폴더 안에 있는 icon.png 이미지 수정

최종 앱 파일 생성

app.json

  • version / ios - buildNumber / android - versionCode → 매번 다르게
expo build:android

permission 코드

"permissions": ["ACCESS_FINE_LOCATION", "ACCESS_COARSE_LOCATION"]

 


내가 제작한 앱에 광고를 넣는 방법과 최종적으로 구글 플레이스토어에 배포하는 방법을 배웠다.

 

광고는 구글 애드몹(Admob) 서비스를 이용하여 앱 메인 화면 하단에 배너 광고를 넣고,

각 꿀팁을 클릭해서 상세 페이지로 넘어가기 전에 전면 광고가 나오도록 했다.

 

애드몹 신청 과정이 꽤나 길긴 했으나, 한번 승인받은 이후에

광고 단위를 새롭게 만드는 방법이나 그것을 앱에 적용하는 방법은 어렵지 않았다.

 

구글 플레이스토어에 배포하기 위해 apk 파일을 만들고 등록하는 과정도 직접 따라해봤기 때문에

추후에 나만의 앱을 제작한다면 이번 수업으로 배운 구글 광고 추가와 배포하는 방법을 적용하면 될 것 같다!

 

 

앱 안에서 나오는 배너 광고와 전면 광고의 모습이다

날씨 서버 외부 API 사용하기

  • API 유형
    1. 서버가 제공하는 도메인 사용
    1. 서버가 만들어놓은 함수 사용
  • 이번에 사용할 날씨 서버 API → 외부 API를 도메인 형태로 요청하는 방식
  • 다음 순서로 API를 통해 날씨를 가져올 것
    1. 현재 위치(좌표) 데이터를 가져오기
    1. 위치 데이터를 이용해서 현재 위치 날씨 데이터 가져오기

 

위치 데이터 가져오기

💡
expo-location 도구를 활용할 것!

expo-location 설치하기

expo install expo-location

 

코드 작성

import * as Location from "expo-location";
  • expo-location은 Location 이라는 이름으로 사용할 것이다.

 

useEffect(()=>{
    setTimeout(()=>{
				...
        getLocation()
    },1000)
  },[])
  • 앱이 실행됐을 때 getLocation() 함수를 이용해 현재 위치 데이터를 가져온다.

 

const getLocation = async () => {
    try {
      //자바스크립트 함수의 실행순서를 고정하기 위해 쓰는 async,await
      await Location.requestPermissionsAsync();
      const locationData= await Location.getCurrentPositionAsync();
      console.log(locationData)

    } catch (error) {
      //혹시나 위치를 못가져올 경우를 대비해서, 안내를 준비합니다
      Alert.alert("위치를 찾을 수가 없습니다.", "앱을 껏다 켜볼까요?");
    }
  }
  • 외부 API 요청은 try/catch 이용

 

  • requestPermissionAsync() : 권한을 물어보는 팝업
  • 허용 선택 → 아래 코드 진행
  • getCurrentPositionAsync() : 현재 위치 좌표를 가져오는 함수

 

💡
함수 실행 순서를 정해주는 async / await
  • 무거운 기능(네트워크, 파일 시스템 접근 등)을 다룰 때 코드를 작성한 순서대로 실행하지 않을 수 있다.
  • 자바스크립트는 비동기 특성을 가지고 있어서 먼저 끝나는 함수부터 결과값을 가져온다.
  • 순차적으로 함수를 실행시키기 위해 async, await을 사용한다.
  • 함수 앞에 async를 붙이고 그 내부에서 사용될 함수 앞에 await를 붙인다.

 

날씨 데이터 가져오기

💡
openweathermap api 를 사용할 것!

 

axios 도구 설치

  • 서버가 제공하는 도메인 형식의 API를 사용하려면 axios 도구가 필요하다.
yarn add axios

 

코드 작성

import axios from "axios"
  • 설치한 axios 도구 import

 

const getLocation = async () => {
    try {
      await Location.requestPermissionsAsync();
      const locationData= await Location.getCurrentPositionAsync();
      const latitude = locationData['coords']['latitude']
      const longitude = locationData['coords']['longitude']
      const API_KEY = "cfc258c75e1da2149c33daffd07a911d";
      const result = await axios.get(
        `http://api.openweathermap.org/data/2.5/weather?lat=${latitude}&lon=${longitude}&appid=${API_KEY}&units=metric`
      );

      const temp = result.data.main.temp; 
      const condition = result.data.weather[0].main

    } catch (error) {
      Alert.alert("위치를 찾을 수가 없습니다.", "앱을 껏다 켜볼까요?");
    }
  }
  • expo-location을 이용하여 경도, 위도 가져오기
  • axios.get(도메인 주소)을 이용해서 날씨 데이터를 가져온다.
  • 도메인 주소에는 경도, 위도, 발급받은 키가 들어간다.

 

const [weather, setWeather] = useState({
    temp : 0,
    condition : ''
})
  • 날씨 정보를 상태(state)로 관리

 

const getLocation = async () => {
	try{
		...
		setWeather({
			temp,condition
		 })
	}
	...
}
  • getLocation 함수 내에서 날씨 정보를 가져온 뒤 상태 반영

 

파이어베이스 (Firebase) 사용하기

서버리스 (serverless)

  • 직접 서버를 구현, 구성할 필요 없이 서버 기능을 제공하는 곳에서 서비스를 사용하면 된다.

 

파이어베이스 (Firebase) 란?

  • 구글에서 만든 서버리스 서비스
  • 실시간 데이터베이스, 인증, 스토리지, 호스팅 등 많은 기능 제공
  • 이용 절차
    1. 파이어베이스 가입하기
    1. 파이어베이스 프로젝트 생성
    1. 사용할 파이어베이스 서비스 활성화

 

파이어베이스를 앱에 연결하기

  • 자바스크립트(웹 개발 언어)로 리액트 네이티브 프로젝트 진행 중 → 웹 앱 선택
  • 스크립트를 복사하여 Firebase SDK 추가하기

 

expo 도구 설치

expo install firebase

 

firebaseConfig.js 생성

import firebase from 'firebase/app';

// 사용할 파이어베이스 서비스 주석을 해제해서 사용
//import "firebase/auth";
import "firebase/database";
//import "firebase/firestore";
//import "firebase/functions";
import "firebase/storage";

// Initialize Firebase
const firebaseConfig = {
  apiKey: "AIzaSyBMXK-Vpv5wW6TYEKxMdzLGsKn4UY-vNX0",
  authDomain: "sparta-myhoneytip-yewon.firebaseapp.com",
  projectId: "sparta-myhoneytip-yewon",
  storageBucket: "sparta-myhoneytip-yewon.appspot.com",
  messagingSenderId: "44731016366",
  appId: "1:44731016366:web:3e1b8f1f29b3440ddbb93f",
  measurementId: "G-E4WCLCL8P6"
};

if (!firebase.apps.length) {
    firebase.initializeApp(firebaseConfig);
}

export const firebase_db = firebase.database()

 

파일 스토리지 (storage)

  • 파이어베이스 사이트에서 Storage 생성
  • 폴더 생성, 파일 업로드 가능

 

리얼타임 데이터베이스

  • JSON 형태로 데이터를 저장/관리한다.
  • 리얼타임 데이터베이스 생성 후 data.json 파일 가져오기

 

전체 데이터 읽기

  • 리얼타임 데이터베이스 고유 주소를 이용해서 데이터를 가져온다.
  • 전체 데이터 읽는 코드 :
firebase_db.ref('/tip').once('value').then((snapshot) => {
   let tip = snapshot.val();
})
  • 실제 적용 :
import {firebase_db} from "../firebaseConfig"

useEffect(()=>{
    setTimeout(()=>{
				...
        firebase_db.ref('/tip').once('value').then((snapshot) => {
          console.log("파이어베이스에서 데이터 가져왔습니다!!")
          let tip = snapshot.val();
          setState(tip)
          setCateState(tip)
          getLocation()
          setReady(false)
    },1000)

    
  },[])
  • useEffect 함수 안에서 firebase 전체 데이터 읽어오기

 

특정 데이터 읽기

  • Card.js 수정
<TouchableOpacity style={styles.card} onPress={()=>{navigation.navigate('DetailPage',{idx:content.idx})}}>
  • 기존에는 navigate에 content를 넘겨주었음
  • content의 idx만 넘기도록 수정

 

  • DetailPage.js
import {firebase_db} from "../firebaseConfig"
...
useEffect(()=>{
		...
    const { idx } = route.params;
    firebase_db.ref('/tip/'+idx).once('value').then((snapshot) => {
        let tip = snapshot.val();
        setTip(tip)
    });
},[])
  • route.params를 이용해 idx 받아서 firebase 데이터베이스에서 읽어오기

 

expo-contants

  • 사용자마다 고유한 ID값을 생성해준다.
expo install expo-constants
import Constants from 'expo-constants';

console.log(Constants.installationId)

 

데이터 쓰기

import {firebase_db} from "../firebaseConfig"
import Constants from 'expo-constants';

const like = () => {
        const user_id = Constants.installationId;
        firebase_db.ref('/like/'+user_idx+'/'+ tip.idx).set(tip,function(error){
            console.log(error)
            Alert.alert("찜 완료!")
        });
    }
  • 팁 찜하기 버튼을 클릭하면 firebase 데이터베이스에 유저의 아이디와 팁 인덱스를 이용하여 데이터를 추가한다.

 


API를 이용해서 서버에서 날씨 정보 가져오기!

expo-location을 이용해서 현재 위치를 받아오고

openweathermap api를 이용해서 현재 내 위치의 날씨 정보를 가져온다.

두번째 사진 오른쪽 상단에 실제 날씨 정보가 출력된 것을 확인할 수 있었다.

 

리액트와 expo, 그외 기타 사이트에서 다양한 API를 제공해줘서 앱 개발하는데 있어서 매우 편리한 것 같다.

API를 활용하니 생각보다 내 위치 및 날씨 데이터를 가져오는 절차가 간단했다!

앞으로 앱을 개발할 때 날씨 정보 뿐만 아니라 다양한 API를 찾아보고 활용하도록 해야겠다.

 

구글 파이어베이스 DB를 이용하여 팁 목록과 나의 찜 목록을 다룰 수 있었다.

사진은 4주차 숙제로 구현한 팀 찜하기 기능과 찜 목록이다.

 

파이어베이스는 한이음 프로젝트 하면서 이미 접해본 적이 있기 때문에 낯설지 않았다.

안드로이드 스튜디오에서는 파이어베이스와 연동하기 위한 절차가 꽤 길었는데 (파이어베이스 sdk 파일 추가, gradle 파일에 dependecy 추가 등등..)

리액트 네이티브는 웹앱 (자바스크립트를 기반으로 함) 이라 그런지 간단히 스크립트만 복붙하여 연동을 마칠 수 있었다.

 

대신 DB에서 데이터를 삭제할 때 함수를 잘못 작성했다가 console.log가 무한 반복된다거나 하는 오류들을 겪어서

다시 그런 실수를 반복하지 않도록 삭제할 때 작성할 코드를 조심히 작성해야겠다는 생각이 들었다.

remove 함수를 이용해서 DB 데이터를 삭제하는 방법은 공식 문서를 참고하여 이해했다.

( https://firebase.google.com/docs/reference/js/firebase.database.Reference?authuser=2#remove )

 

이제 리액트 네이티브에서 파이어베이스를 다루는 방법도 알게 되었으니

내 개인 프로젝트로 만들고 있는 일정 관리 앱에도 유용하게 사용할 수 있을 것 같다!

리액트 컴포넌트, 속성, 상태

리액트 기초지식

  1. 컴포넌트(Component) : 정해진 요소를 사용하여 만든 화면의 일부분
  1. 상태(State) : 컴포넌트에서 데이터를 유지하고 관리하기 위한 방법 = 데이터
  1. 속성(Props) : 상위 컴포넌트에서 하위 컴포넌트로 데이터를 전달하는 방식 = 데이터 전달
  1. useEffect : 화면에 컴포넌트가 그려지면 처음 실행해야 하는 함수들을 모아두는 곳
  • Component에 있는 Props가 데이터를 전달 → Component 안의 State가 데이터를 관리

 

컴포넌트(Component)

  • UI 요소, 화면의 모든 부분

 

  • Card.js
//비구조 할당 방식으로 넘긴 속성 데이터를 꺼내 사용함
export default function Card({content}) {
    return (<View style={styles.card}>
        <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>)
}
  • 비구조 할당 방식 이용

 

  • MainPage.js
import Card from '../components/Card';

...

<View style={styles.cardContainer}>
	{/* 하나의 카드 영역을 나타내는 View */}
  {
		 tip.map((content,i)=>{
	     return (<Card content={content} key={i}/>)
     })
  }    
</View>
  • MainPage.js에서 Card.js로 content라는 데이터를 넘겼다.

 

속성(Props)

  • 컴포넌트에 데이터를 전달하는 것
  • key와 value의 형태 (ex. content={content})
  • 컴포넌트에 대해 map으로 반복문 → 반드시 인덱스(i)를 key={i}로 속성 전달

 

상태(useState)와 useEffect

  • 컴포넌트마다 보유, 관리하는 데이터를 상태라고 한다.
  • 리액트에서 상태(state) → useState로 생성, setState로 변경 가능

useState

  • 리액트에서 화면은 데이터에 따라 변경되고, 상태(state)로 관리되는 데이터가 변경되면 화면이 바뀐다.
  • UI = component(state)

useEffect

  • 화면이 로딩되고 가장 먼저 실행되는 함수
useEffect(()=>{
	화면이 그려지고 나서 가장 먼저 실행돼야 할 코드
},[])
  • 데이터를 준비(데이터를 받고 상태(state)에 반영)할 때 사용
  • 화면이 그려진다 → useEffect가 데이터를 준비 → 상태 데이터가 업데이트 되었으니 다시 화면이 그려진다

 

const [state, setState] = useState([])

useEffect(()=>{
	setState(data)
},[])
  • state : 컴포넌트에서 관리될 상태 데이터를 담고 있는 변수
  • setState : state를 변경시킬 때 사용해야하는 함수
  • useState() 안에 전달되는 것 → state 초기값
  • useEffect 함수로 data.json에서 가져온 데이터를 state에 담음

 

로딩 화면

  • useEffect는 화면이 그려지고 나서 실행되는데, 현재는 화면을 그릴 때 이용할 state에 값이 설정되어 있지 않은 상태임
  • 따라서 로딩 화면을 먼저 그린 후 → useEffect를 통해 state를 설정하고 나서 → 메인 화면을 그려야 오류가 나지 않음

 

  • Loading.js 파일 생성 후 MainPage.js를 다음과 같이 수정
import Loading from '../components/Loading';

const [state,setState] = useState([])
const [ready,setReady] = useState(true) //초기 ready : True

useEffect(()=>{
    setTimeout(()=>{
        setState(data)
        setReady(false)
    },1000) //1000 = 1초
 },[])
  • 상태는 여러개 만들어도 된다.
  • setTimeout으로 1초 뒤에 상태 관리에 들어가게 함

 

return ready ? <Loading/> :  (
    <ScrollView style={styles.container}>
      <Text style={styles.title}>나만의 꿀팁</Text>
			 <Text style={styles.weather}>오늘의 날씨: {todayWeather + '°C ' + todayCondition} </Text>
      <Image style={styles.mainImage} source={main}/>
      <ScrollView style={styles.middleContainer} horizontal indicatorStyle={"white"}>
        <TouchableOpacity style={styles.middleButton01}><Text style={styles.middleButtonText}>생활</Text></TouchableOpacity>
        <TouchableOpacity style={styles.middleButton02}><Text style={styles.middleButtonText}>재테크</Text></TouchableOpacity>
        <TouchableOpacity style={styles.middleButton03}><Text style={styles.middleButtonText}>반려견</Text></TouchableOpacity>
        <TouchableOpacity style={styles.middleButton04}><Text style={styles.middleButtonText}>꿀팁 찜</Text></TouchableOpacity>
      </ScrollView>
      <View style={styles.cardContainer}>
         {
          tip.map((content,i)=>{
            return (<Card content={content} key={i}/>)
          })
        }
      </View>
    </ScrollView>
  );
  • 삼항연산자를 이용해서 ready가 true이면 로딩 화면을, false이면 메인 화면을 그린다.
  • 실행 순서
    1. 초기 ready 상태는 true여서 로딩 화면을 먼저 그림
    1. useEffect가 실행돼서 1초 뒤에 state 값을 설정, ready를 false로 변경
    1. ready 상태가 변경되었으니 자동으로 retrun ready ? ... 가 실행됨
    1. false이므로 메인 화면이 그려짐

 

카테고리 기능

MainPage.js

const [state,setState] = useState([]) //전체 리스트
const [cateState,setCateState] = useState([]) //카테고리 선택에 따른 리스트
const [ready,setReady] = useState(true)

useEffect(()=>{
	setTimeout(()=>{
		let tip = data.tip;
		setState(tip)
		setCateState(tip) //처음 로딩 시 전체 리스트로 초기화
		setReady(false)
	},1000)
},[])
  • 선택한 카테고리에 따라 보여줄 리스트를 저장하는 cateState
  • useEffect에서 cateState를 tip (전체 데이터) 으로 초기화

 

<ScrollView style={styles.middleContainer} horizontal indicatorStyle={"white"}>
	<TouchableOpacity style={styles.middleButtonAll} onPress={()=>{category('전체보기')}}><Text style={styles.middleButtonTextAll}>전체보기</Text></TouchableOpacity>
	<TouchableOpacity style={styles.middleButton01} onPress={()=>{category('생활')}}><Text style={styles.middleButtonText}>생활</Text></TouchableOpacity>
	<TouchableOpacity style={styles.middleButton02} onPress={()=>{category('재테크')}}><Text style={styles.middleButtonText}>재테크</Text></TouchableOpacity>
	<TouchableOpacity style={styles.middleButton03} onPress={()=>{category('반려견')}}><Text style={styles.middleButtonText}>반려견</Text></TouchableOpacity>
	<TouchableOpacity style={styles.middleButton04} onPress={()=>{category('꿀팁 찜')}}><Text style={styles.middleButtonText}>꿀팁 찜</Text></TouchableOpacity>
</ScrollView>
  • 카테고리 버튼마다 onPress 함수를 설정함
  • category 함수에 카테고리 이름을 넘겨주도록 함

 

const category = (cate) => {
	if(cate == "전체보기"){
		//전체보기면 원래 꿀팁 데이터를 담고 있는 상태값으로 다시 초기화
		setCateState(state)
		}else{ //전체(state)에서 선택한 카테고리 데이터만 추출
			setCateState(state.filter((d)=>{
			return d.category == cate //true일때 cateSate에 넣는다
		}))
	}
}
  • category 함수에는 cate(카테고리 이름) 파라미터가 있음
  • 전체보기를 클릭했다면 cateState를 전체 데이터(state)로 변경
  • 그 외 카테고리 버튼을 클릭했다면 전체 데이터(state) 중 해당 카테고리에 속하는 데이터만 cateState에 저장 (filter 함수 이용)

 


 

스택네비게이터

네비게이션

  • 컴포넌트들을 페이지화 시켜서 해당 페이지끼리 이동을 가능하게 하는 라이브러리

 

도구 설치

yarn add @react-navigation/native
expo install react-native-gesture-handler react-native-reanimated react-native-screens react-native-safe-area-context @react-native-community/masked-view

 

스택네비게이션

  • 컴포넌트에 페이지 기능 부여, 컴포넌트에서 컴포넌트로 이동할 수 있도록 해준다.
  • Stack.Screen : 페이지
  • Stack.Navigator : 책갈피 (모든 페이지를 등록시킬 곳)

 

설치

yarn add @react-navigation/stack

 

적용하기

  • StackNavigator.js
import { createStackNavigator } from '@react-navigation/stack';
//페이지로 만들 컴포넌트 불러오기
import DetailPage from '../pages/DetailPage';
import MainPage from '../pages/MainPage';

const Stack = createStackNavigator();
const StackNavigator = () =>{
	return (
		<Stack.Navigator
			screenOptions={{
				headerStyle: {
				backgroundColor: "black",
				borderBottomColor: "black",
				shadowColor: "black",
				height:100
			},
			headerTintColor: "#FFFFFF",
			headerBackTitleVisible: false
		}}
	  >
			//페이지로 만들 컴포넌트
	    <Stack.Screen name="MainPage" component={MainPage}/>
	    <Stack.Screen name="DetailPage" component={DetailPage}/>
    </Stack.Navigator>
	)
}

export default StackNavigator;

 

  • App.js
import React from 'react';
import { StatusBar } from 'expo-status-bar';
import {NavigationContainer} from '@react-navigation/native';
import StackNavigator from './navigation/StackNavigator'

export default function App() {
	console.disableYellowBox = true;

  return ( 
  <NavigationContainer>
    <StatusBar style="black" />
    <StackNavigator/>
  </NavigationContainer>);
}
  • 앱의 가장 최상위 컴포넌트인 App.js에 네비게이션 기능을 달아준 것
  • NavigationContainer 태그로 StatusBar(상태바)과 StackNavigator(직접 만든 스택네비게이션)을 감싼다.

 

💡
Stack.screen에 등록된 모든 페이지 컴포넌트들은 navigationroute라는 딕셔너리(객체)를 속성으로 넘겨받아 사용할 수 있다!
navigation.setOptions({
   title:'나만의 꿀팁'
})
navigation.navigate("DetailPage")
navigation.navigate("DetailPage",{
  title: title
})

const { title} = route.params; //비구조 할당방식
  • navigation
    1. setOptions : 제목, 헤더 옵션 스타일 등 바꿀 수 있음
    1. navigate : 해당 페이지로 이동하는 함수 (StackNavigator.js의 Stack.screen name 속성), 두번째 매개변수로 딕셔너리 데이터 전달
  • route : navigate로 인해 전달받은 데이터를 받는 딕셔너리

 


 

공유하기 기능

  • React Native 자체에서 제공하는 기능이다.
import { Share } from "react-native";

export default function DetailPage({navigation,route}) {
		...
    const share = () => {
        Share.share({
            message:`${tip.title} \n\n ${tip.desc} \n\n ${tip.image}`,
        });
    }
    return (
        <ScrollView style={styles.container}>
            <Image style={styles.image} source={{uri:tip.image}}/>
            <View style={styles.textContainer}>
                <Text style={styles.title}>{tip.title}</Text>
                <Text style={styles.desc}>{tip.desc}</Text>
                <View style={styles.buttonGroup}>
                    <TouchableOpacity style={styles.button} onPress={()=>popup()}><Text style={styles.buttonText}>팁 찜하기</Text></TouchableOpacity>
                    <TouchableOpacity style={styles.button} onPress={()=>share()}><Text style={styles.buttonText}>팁 공유하기</Text></TouchableOpacity>
                </View>
            </View>
        </ScrollView>
    )
}
  • share 함수를 이용해 공유할 때 전달할 제목, 설명, 이미지 등을 설정할 수 있음

 


 

외부 링크 클릭 이벤트

도구 설치

expo install expo-linking

 

적용하기

import * as Linking from 'expo-linking';

const link = () => {
	Linking.openURL("https://spartacodingclub.kr")
}

<TouchableOpacity style={styles.button} onPress={()=>link()}><Text style={styles.buttonText}>외부 링크</Text></TouchableOpacity>

 


리액트를 기반으로 하는 리액트 네이티브를 공부하고 있기 때문에

리액트의 기초 지식인 컴포넌트/속성/상태에 대해 공부했다.

상태의 개념이 처음 접했을 때 헷갈렸는데, 상태의 변화가 일어날 때 화면을 새로 그린다는 것에 유의하자.

스택네비게이터를 이용해 여러 페이지간의 이동을 구현하는 방법을 알게 되어 좀더 앱다운 앱을 만들 수 있게 되었다.

그 외 공유하기 및 외부 링크 클릭 이벤트를 구현하는 방법을 익혀서 추후에 앱을 출시하게 된다면 유용하게 사용할 수 있을 것 같다!

스택네비게이션의 이용을 여러 번 복습해서 익숙해질때까지 공부해야겠다.

 

카드를 클릭하여 해당 내용의 상세 페이지로 이동한 모습

 

이번 주 숙제 : 어바웃 페이지에서 외부 링크 열기, 꿀팁찜 페이지 만들어서 네비게이터에 연결하기

리액트 네이티브 & Expo

리액트 네이티브

리액트(React) + 네이티브(Native)

리액트는 JS 기반인데 개발하다보면 카카오톡 로그인 등 안드로이드, iOS 각각의 코드를 알아야하는 상황이 생김 → Expo를 이용함

 

Expo

  • 리액트 네이티브 코어 앱 개발 코어 기술을 Expo로 감싸서 JS로만 개발할 수 있는 환경을 구축하도록 도와주는 역할
  • Expo 클라이언트 앱-Expo 서버를 킨다 → QR 코드를 찍어서 개발중인 앱을 확인한다.

개발 환경 준비

  1. Node & NPM-Node.js로 JS 개발 환경 구축-Node.js 설치 시 NPM 자동 설치
  2. -유용한 JS 도구를 가지고 올 때 NPM(Node Package Manager) 사용
  1. Yarn-NPM보다 더 효율적으로 JS 도구를 가져올 수 있는 도구
    💡
    npm install -g yarn * -g : 컴퓨터에 전역적으로 설치한다는 옵션
  2. -NPM을 이용해서 설치
  1. Expo 명령어 도구 설치-Expo 도구 : NPM을 이용해서 설치
  2. 💡
    npm install -g expo-cli
  1. Expo 가입 및 로컬에 Expo 계정 세팅
    • cmd창에서 Expo 서비스 로그인
    💡
    expo login —username 사용자이름
  1. Expo 실행하기
    • 바탕화면에 sparta study 생성
    • VS code에서 Open Folder - sparta study 폴더 선택
    • View - Terminal 클릭해서 터미널 창 열기
    • expo init으로 프로젝트 생성
    💡
    expo init sparta-honeytip-yewon
    *이때 오류나는 경우 터미널을 Powershell이 아닌 Command Prompt로 변경
    • 개발하기 위해 폴더 이동
    💡
    cd sparta-honeytip-yewon
    • expo 시작
    💡
    expo start
    *Expo 클라이언트 앱으로 개발을 진행할 것(같은 와이파이 상에서 연결이 안되는 문제 : LAN → Tunnel로 변경 후 새 QR 코드 인식해서 해결)
  2. → 구글 플레이스토어에서 앱 다운 후 QR 인식 (동일한 WIFI 연결)
  1. 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>
  );
}
  • <ScrollView> : 감싼 엘리먼트 스크롤이 가능해진다.

 

Button 태그

import React from 'react';
import { StyleSheet, Text, View, Button, Alert } from 'react-native';

export default function App() {
  return (
    <View style={styles.container}>
      <View style={styles.textContainer}>
        <Text style={styles.textStyle}>아래 버튼을 눌러주세요</Text>
        <Button 
          style={styles.buttonStyle} 
          title="버튼입니다 "
          color="#f194ff" 
          onPress={function(){
            Alert.alert('팝업 알람입니다!!')
          }}
        />
        <Button 
            style={styles.buttonStyle} 
            title="버튼입니다 "
            color="#FF0000" 
            onPress={()=>{
              Alert.alert('팝업 알람입니다!!')
            }}
          />
          </View>
    </View>
  );
}
  • <Button> : 버튼을 만들 수 있는 엘리먼트
  • title 속성 : 버튼 안에 넣을 문자
  • color 속성 : 버튼 색상
  • onPress 속성 : 버튼 클릭 시 실행할 함수 연결

 

TouchableOpacity

  • <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>
  );
}
  • 외부 이미지를 가져와서 사용하는 방법
  • source에 uri 지정

*코드가 너무 길면 alt+z를 눌러서 한 화면에 코드가 다 들어오도록 설정

 

Styles

import React from 'react';
import { StyleSheet, Text, View, Image } from 'react-native';

export default function App() {
  return (
    <View style={styles.container}>
      <View style={styles.textContainer}>
        <Text style={styles.textStyle}>스파르타 코딩클럽!!</Text>
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    justifyContent:"center",
    alignContent:"center"
  },
  textContainer: {
    margin:10,
    padding: 10,
    borderRadius:10,
    borderWidth:2,
    borderColor:"#000",
    borderStyle:"dotted",

  },
  textStyle: {
    color:"red",
    fontSize:20,
    fontWeight:"700",
    textAlign:"center"
  }
});
  • const style : 값의 재할당이 불가능한 style 변수 생성
  • StyleSheet에 딕셔너리를 넘겨줌
  • margin, padding, borderRadius, borderWidth, borderColor, borderStyle
  • color, fontSize, fontWeight, textAlign

 

Flex

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

export default function App() {
  return (
    <View style={styles.container}>
      <View style={styles.containerOne}>

      </View>
      <View style={styles.containerTwo}>
        <View style={styles.innerOne}>
         
        </View>
        <View style={styles.innerTwo}>
          <Text>!!컨텐츠!!</Text>
        </View>

      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex:1
  },
  containerOne: {
    flex:1,
    backgroundColor:"red"
  },
  containerTwo:{
    flex:2,
    flexDirection:"row",
    backgroundColor:"yellow"
  },
  innerOne: {
    flex:1,
    backgroundColor:"blue"
  },
  innerTwo: {
    flex:4,
    flexDirection:"row",
    justifyContent:"flex-end",
    backgroundColor:"orange"
  }
});
  • flex : 레이아웃 비율 (상대적)
  • flexDirection : flex 방향 지정
  • 상하 : column (기본) / 좌우 : row
  • justifyContent : flexDirection에 따라 정렬
  • flex-start, flex-end, center를 가장 자주 씀, 그 외 space-around, space-between, space-evenly
  • alignItems : flexDirection 방향과 반대로 정렬 (flex 영역 내에서 사용 가능)
  • alignSelf : flex 없어도 가능
  • numberOfLines={3} : 텍스트 잘리는 부분 ... 으로 대체

 

앱&자바스크립트 모듈, 반복문

  • App.js : 모듈 시스템에 의해서 내보내짐
  • 앞으로 여러 파일들을 만들 때 export default function을 이용해서 내보낼 것
export default function App() {...}

 

  • data.json 파일 : 딕셔너리 + 리스트 구조
  • App.js에 data.json 파일 import
import data from './data.json'

 

  • data.tip으로 json 파일의 tip 가져오기
let tip = data.tip;
let todayWeather = 10 + 17;
let todayCondition = "흐림"

 

  • App.js JSX 문법 안에서 map을 이용한 반복문
  • 반복문을 돌릴 때 가장 최상위 태그에는 key값이 주어져야 한다. (key는 유니크함)
<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>

 

  • JSX 안에서 중괄호 { }를 쓰면 그 안에서 자바스크립트 문법을 쓸 수 있다!
<Text style={styles.weather}>오늘의 날씨: {todayWeather + '°C ' + todayCondition} </Text>

 

삼항연산자

let result = 조건 ? 참일때 : 거짓일때
let result = 10>2 : '참' : '거짓'

tip.map((content,i)={
	return i%2==0 ? (<View>...</View>) : (<View>...</View>)
})

 

숙제

어바웃 페이지 만들기

Uploaded by Notion2Tistory v1.1.0


지난 주 1주차 수업에서는 자바스크립트의 기초를 배웠고

이번 주에는 본격적으로 리액트를 이용하여 앱을 만들어보는 시간을 가졌다.

Node.js, NPM, Expo 등 미리 세팅해야할 사항이 많아보여서 처음에는 약간 겁을 먹었으나

세팅을 무사히 마친 후 VS Code에서 JSX의 다양한 태그들을 활용하며 레이아웃을 구성하니 흥미로웠다.

 

태그를 사용하여 레이아웃을 구성하는 것은 안드로이드 스튜디오와 비슷해서 낯설지 않았고

이미 css를 공부했었기 때문에 StyleSheet를 활용하여 꾸미는 것도 익숙했다.

아직 flex 개념이 헷갈리는데 추가적으로 공부를 해서 익혀야겠다. (안드로이드 스튜디오 layout_gravity가 생각나는 속성이었다.)

 

Expo 클라이언트 앱을 이용하여 실시간으로 내가 수정한 코드가 반영된 모습을 볼 수 있는 점이 정말 편리했다.

안드로이드 스튜디오 같은 경우에는 미리보기 창으로 레이아웃을 얼추 볼 수 있긴 하나

제대로 실행시키려면 빌드를 해야하기 때문에 시간이 걸려서 은근 번거로운데

Expo는 ctrl+s 할 때마다 바로 핸드폰 화면에 수정 사항이 반영되기 때문에 앞으로 개발하면서 시간 단축이 많이 될 것 같다는 생각이 들었다.

또한 오류 메세지도 자세하게 나오는 편이라 어느 부분에서 오류가 났는지 확인하기 쉬웠다.

 

이번 주 강의를 열심히 수강한 결과, 마지막에 첨부한 이미지처럼 숙제로 어바웃 페이지를 구현할 수 있었다.

오늘 막 리액트에 입문한 것 치고 많은 것을 배우고 그것을 응용할 수 있는 능력을 가지게 된 것 같아 좋았다!

앱개발이란?

앱 서비스를 만드는 것 → 클라이언트, 서버를 모두 만들어야함

  • 클라이언트 : 사용자가 보는 화면
  • 서버 : 데이터가 있는 곳, 데이터 요청 시 응답해주는 곳

 

앱 개발 : 안드로이드, iOS(아이폰)

  • 안드로이드 → Java & Kotlin
  • iOS → Swift

운영체제가 다르기 때문에 개발 기술 언어가 다르며, 둘 다 러닝 커브 (허들) 존재

 

앱개발 종류

  1. 네이티브 앱 : 안드로이드, iOS를 각각 개발
  1. 하이브리드 앱 : 웹 사이트를 만들고 간단히 배포하는 앱
  1. 크로스 플랫폼 앱 : 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)

→ 위의 코드 실행 시 오류 발생

 

💡
Uncaught TypeError: Assignment to constant variable. at <anonymous>:4:6

const로 선언한 변수는 값을 바꿀 수 없기 때문

 

리스트

리스트란?

  • 여러 데이터들을 담을 수 있는 자료형
  • 인덱스는 0부터 시작
let a_list = [1,2,3,4,'yewon',6,'jeong']

//두 번째 값을 꺼낸다 (인덱스 1)
console.log(a_list[1]) //2

 

값 추가하기

  • push 함수 이용
a_list.push('sparta')
console.log(a_list) //[1, 2, 3, 4, "yewon", 6, "jeong", "sparta"]

 

길이

  • length
a_list.length //8

 

딕셔너리

딕셔너리란?

  • 사전 형식
  • key : value
let a_dict = {"name":"yewon", "age":23}
console.log(a_dict["name"])
a_dict["height"] = 200 //값 추가
console.log(a_dict) //{name: "yewon", age: 23, height: 200}

 

리스트, 딕셔너리 복합 구조

let names = [{"name":"yewon", "age":23}, {"name":"yewon2","age":23}]
console.log(names)

실행 결과

💡
(2) [{…}, {…}] 0: {name: "yewon", age: 23} 1: {name: "yewon2", age: 23} length: 2 __proto__: Array(0)

 

names.push({"name":"yewon3","age":23}) //3
console.log(names)
names[1]["age"] //23

 

JSON 데이터 구조

*크롬 확장 프로그램 JSON view 설치

 

함수

기본 제공 함수

  • 나머지 구하기 : %
  • 대문자로 바꾸기 : toUpperCase()
  • 특정 문자로 문자열을 나누기 : 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()

 

조건문

if - else if - else

function is_adult(age){
    if(age>20){
        console.log("성인")
    }else if(age>10){
        console.log("청소년")
    }else{
        console.log("어린이")
    }
}
is_adult(30) //성인

 

AND 연산자

  • 모든 조건이 참이여야 참
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)
}
💡
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
💡
export default는 하나의 파일 안에서 유일해야함
//util.js 파일
export default function times(x){
		return x*x
}
//app.js 파일
import k from './util.js'
console.log(k(4)) //16

 

Uploaded by Notion2Tistory v1.1.0


지금까지 "리액트"라는 것을 들어보긴 했으나 내가 직접 다루고 공부한 적은 없었다.

오늘부터 수강하게 된 앱개발 종합반에서는 리액트 네이티브 앱 개발을 배울 것이다.

 

리액트 네이티브란, 안드로이드와 iOS 두 개의 운영체제에서 모두 실행될 수 있는 크로스 플랫폼 앱 개발 언어 중 하나이며,
이번 주에는 리액트를 본격적으로 다루기 전에
리액트의 기반 언어인 자바스크립트의 기초를 배우는 시간을 가졌다.

자바스크립트의 리스트, 딕셔너리 구조와 이 둘의 복합 구조를 사용하는 방법을 알게 되었고
JSON 데이터 구조가 리스트와 딕셔너리의 복합 구조라는 사실을 알게 되어
JSON이라는 내가 알지 못하던 것에 대한 거부감(?)이 사라진 듯 하다.

튜터님이 차근차근 잘 알려주셔서 그런듯!👍

그리고 앱 개발 시 자주 쓰인다고 하는 화살표 함수, 비구조 할당 방식, map 등을 배워서 좋았다~

+ Recent posts