본문 바로가기

Information Technology

[React Native] Weather APP

Expo -> Native 파일을 숨겨주고 휴대폰에서 테스트 가능. but Native 파일을 크게 관리하기 어려움

 

로그인 후 휴대폰과 기기 연결

 

* App 개발 3-ways

1. Swift, C#(iOS) / JAVA, Kotlin(Android) 기반

2. 반응형 웹 페이지를 그대로 앱 안에

3. iOS, Android 모두 JavsScript 엔진을 가지고 있음. 이걸 기반. 브릿지를 이용해서 JS코드를 리액트 네이티브에게 보냄. 항상 브릿지가 있어야 해서 속도가 느려질 수 있음. 게임을 만들 땐 그리 좋지 않음;


여기 보이는 Text, View, StyleSheet이 브릿지

React-Native에서 모든 실행 코드는 <View></View> 태그 안에서 동작함. 

텍스트 입력은 <Span></Span> 대신 <Text></Text> 코드 사용

why? 브릿지 때문에


container 안에서만 다른 stylesheet가 동작함

flex: 1 -> 한 칸을 전체 다 차지하려고 함


 

- Location

expo의 Location API

필요 없는 기능까지 가지고 있는 API를 모두 다운로드하지 않고, 필요한 기능만 다운해서 사용

import React from 'react';
import Loading from "./Loading"
import * as Location from "expo-location";

export default class extends React.Component {
getLoaction = async() => {	// 비동기처리
  const location = await Location.getCurrentPositionAsync(Accuracy.Highest) // getCurrent~가 끝나야 location 생성
  console.log(location);
}

  componentDidMount(){
    this.getLoaction();
  }

위치 서비스 요청

사용자가 permission하면 promise를 return. 이를 통해 asnyc(비동기) 처리 가능


export default class extends React.Component {
  state= {	// isLoading을 true로 우선 설정
    isLoading: true
  }
getLoaction = async() => {
  try{
    await Location.requestPermissionsAsync(); // 사용자가 허용하지 않으면 에러 발생->catch로 넘어감
    const {coords: {latitude, longitude}} = await Location.getCurrentPositionAsync()
    // getCurrent~ 에서 가져온 latitude와 longtitude를 coords의 객체로 저장
    this.setState({isLoading:false})
  }
  catch(error)
  {
      Alert.alert("Can't find you", "saaaaad")
  }
}

  componentDidMount(){
    this.getLoaction();
  }

  render()
  {
    const { isLoading } = this.state;
    return isLoading? <Loading />: null;
  }
};

React의 문법과 객체는 항상 { } 괄호 안에서 진행되고,

 

날씨 API 이용
좌표를 통해 위치 정보 이용

import axios from "axios"

const API_KEY = "key 입력";

export default class extends React.Component {
  state= {
    isLoading: true
  }

getWeather = async(latitude, longitude) => {
  const {data} = await axios.get(`api.openweathermap.org/data/2.5/weather?lat=${latitude}&lon=${longitude}&APPID=${API_KEY}`)
  // 변수를 그냥 사용하기 위해 백틱(`)사용 $( )
}

axios를 통해 URL에 저장된 데이터를 가져옴. 

ip-api를 통해 PC가 있는 위치 정보를 파악 가능.

axios가 위와 같은 데이터를 가져오면, 그 속의 객체들로부터 정보를 받아서 저장하는 작업


getWeather = async(latitude, longitude) => {
  const {data} = await axios.get(`http://api.openweathermap.org/data/2.5/weather?lat=${latitude}&lon=${longitude}&APPID=${API_KEY}&units=metric`) 
  // 변수를 그냥 사용하기 위해 백틱(`)사용 $( )
  this.setState({isLoading:false, temp: data.main.temp})
  // axios를 통해 가져온 data의 main 영역의 temp를 temp 변수에 저장
}

API 키 뒤의 &units=metric을 통해 섭씨온도로 변경 가능.

  render()
  {
    const { isLoading, temp } = this.state;
    return isLoading? <Loading />: <Weather temp={Math.round(temp)} />;
  }

삼단 조건문을 통해 Weather Component의 temp를 출력.


날씨를 나타낼 아이콘

getWeather = async(latitude, longitude) => {
  const {data: {
    main: {temp}, weather
  }} = await axios.get(`http://api.openweathermap.org/data/2.5/weather?lat=${latitude}&lon=${longitude}&APPID=${API_KEY}&units=metric`) // 변수를 그냥 사용하기 위해 백틱(`)사용 $( )
  this.setState({
    isLoading: false, 
    condition: weather[0].main,
    temp
  })
};

 

import React from "react";
import {View, Text, StyleSheet } from "react-native";
import PropTypes from "prop-types";
import { MaterialCommunityIcons } from "@expo/vector-icons";

export default function Weather({temp}){    // dumb component
    return <View style={styles.container}>
        <MaterialCommunityIcons size={86} name= "weather-sunny"></MaterialCommunityIcons>
        <Text>{temp}</Text>
        {/* <Text>{condition}</Text> */}
        </View>
}

필요한 디렉토리의 family name을 찾아서 대입하면 됨. 

이미지와 함께 온도 표시

Linear gradient

import { LinearGradient } from 'expo-linear-gradient';

export default function Weather({temp, condition}){    // dumb component
    return (
         <LinearGradient	
          colors={['#4c669f', '#3b5998', '#192f6a']}
          style={styles.container}>
        <View style={styles.halfcontainer}>
            <MaterialCommunityIcons size={90} color="white" name= "cloud"></MaterialCommunityIcons>
            <Text style={styles.temp}>{temp}o</Text>
        </View>
        <View style = {styles.halfcontainer}>
        </View>
        </LinearGradient>
    )}

<LinearGradient></LinearGradient>는 마치 gradient와 같이 동작

          import {View, Text, StyleSheet, StatusBar } from "react-native";
          <StatusBar barStyle="white-content"/> 

어플 위의 시간, 배터리 등등에 대한 설정

white-content -> 흰 글씨

dark-content -> 검정 글씨

 

const weatherOptions = {
    Clouds: {
        iconName: "weather-hail",
        gradient: ["#1d4350", "#a43931"]
    }
}

weatherOptions 객체 생성 후

<MaterialCommunityIcons size={90} color="white" name= {weatherOptions[condition].iconName}></MaterialCommunityIcons>

이미지를 불러올 때 weatherOptions의 멤버를 사용

 

    title: {
        fontSize: 45,
        color: "white",
        fontWeight: "300",
        marginBottom: 5
    },
    subtitle: {
        fontSize: 35,
        color: "white",
        fontWeight: "200"

    },
    textContainer: {
        paddingHorizontal: 5,	// 좌우 패딩
        alignItems: "flex-start"
    }
        <View style = {{...styles.halfcontainer, ...styles.textContainer}}> 
        // halfcontainer 속성과 textContainer속성을 전부 사용함
            <Text style={styles.title}>{weatherOptions[condition].title}</Text>
            <Text style={styles.subtitle}>{weatherOptions[condition].subtitle}</Text>
        </View>

* expo 재설치에 실패했을 때

npm 혹은 node를 재설치 하는 방법도 괜찮지만,

node-module 파일에 있는 expo 모듈을 직접 지우고 yarn 혹은 npm을 통해 expo를 최신 버전으로 설치하는 것도 하나의 방법이 될 수 있습니다.