React Native - 기초1

» FE

해당 내용은 노마드 코더의 React Native 강의를 보고 정리한 내용입니다. https://nomadcoders.co/react-native-for-beginners/lobby

환경설정

ios simulator로 확인할 예정
시작할 때는

cd 폴더 이름 #프로젝트 위치로 이동
code . #vscode로 이동
npm start #터미널에 입력 -> i 입력하면 ios simulator로 확인 가능

Layout

반응형으로 만드는 것이 좋음

import React from "react";
import { View } from "react-native";

export default function App(){
  return (
    <View style=>
      <View style=></View>
      <View style=></View>
      <View style=></View>
    </View>
  );
}
  • display:flex 필요 없음 ->
    • 기본적으로 모든 View가 flexContainer
  • 기본값 Column
    • <View style=> 해줘야 Row
  • 웹이 아니라서 요소들이 화면 바깥으로 나가도 스크롤 x

  • 부모에 flex:1을 추가하면 자식들이 가진 비율만큼 차지
    • tomato : teal : orange = 1 : 2 : 1

Style

요구사항:

  • 현재 내가 있는 도시를 보여줄 것
  • 그 위치의 16일간의 기상예보를 가져올 것

✅ https://reactnative.dev/docs/components-and-apis 참고

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

const{ width:SCREEN_WIDTH }=Dimensions.get("window");
//내 기기 화면 너비 가져오기

export default function App() {
  return (
    <View style={styles.container}>
      <View style={styles.city}>
        <Text style={styles.cityname}>서울</Text>
      </View>
      <ScrollView
        pagingEnabled //자유로운 스크롤 방지(한 페이지만 바로 보이게)
        horizontal //가로 스크롤
        indicatorStyle='gray' //스크롤 색상(ios만 가능)
        contentContainerStyle={styles.weather} //스크롤은 contentContainerStyle로 해야함
      >
        <View style={styles.day}>
          <Text style={styles.temp}>0</Text>
          <Text style={styles.desc}>Snowy</Text>
        </View>
        <View style={styles.day}>
          <Text style={styles.temp}>0</Text>
          <Text style={styles.desc}>Snowy</Text>
        </View>
        <View style={styles.day}>
          <Text style={styles.temp}>0</Text>
          <Text style={styles.desc}>Snowy</Text>
        </View>
        <View style={styles.day}>
          <Text style={styles.temp}>0</Text>
          <Text style={styles.desc}>Snowy</Text>
        </View>
      </ScrollView>
    </View>

  );
}

const styles = StyleSheet.create({
  container:{
    flex:1, 
    backgroundColor:"#fff",
  },
   city: {
    flex: 1,
    backgroundColor: "transparent",
    justifyContent: "center",
    alignItems:"center",

   },
   cityname: {
    fontSize: 38,
    fontWeight: "500",
   },
   weather: {
   },
   day: {
    width: SCREEN_WIDTH,
    backgroundColor: "transparent",
    alignItems: "center",
   },
   temp: {
    fontSize: 158,
    fontWeight: "600",
    marginTop: 50,
   },
   desc: {
    fontSize: 38,
    fontWeight: "300",
    marginTop: -30,
   }

})

Location

import { View, Text, Dimensions, ScrollView, StyleSheet } from 'react-native';
import * as Location from "expo-location";
import React, { useEffect, useState } from "react";

const{ width:SCREEN_WIDTH }=Dimensions.get("window");

export default function App() {
  const [city, setCity] = useState("Loading..."); //기본값 로딩
  const [location, setLocation] = useState();
  const [ok, setOk] = useState(true);
  const ask = async () => {
    const { granted } = await Location.requestForegroundPermissionsAsync(); //앱 사용 중에만 location을 받음

    /*const permission = await Location.requestForegroundPermissionsAsync();
    console.log(permission);

    (NOBRIDGE) LOG  {"canAskAgain": true, "expires": "never", "granted": true, "status": "granted"}
    */ //위치 요청하기 가능 상태


    if (!granted) {
      setOk(false); //유저가 권한 요청 거절할 경우
    }
    const {
      coords: { latitude, longitude },
    } = await Location.getCurrentPositionAsync({ accuracy: 5 }); //사용자 위도, 경도 받기(얼마나 정확하게 위치 받을지를 옵션으로)
    const location = await Location.reverseGeocodeAsync(
      { latitude, longitude },
      { useGoogleMaps: false }
    ); //reverseGeocode로 위도 경도 받아서 위치 출력


    //console.log(location);
    /* 
    {"city": "샌프란시스코", "country": "미 합중국", "district": "Union Square", "isoCountryCode": "US", "name": "Powell St", "postalCode": "94108", "region": "CA", "street": "Ellis St", "streetNumber": "2–16", "subregion": "샌프란시스코", "timezone": "America/Los_Angeles"}]
    */ //ios simulator를 돌리면 항상 미국으로 뜸


    setCity(location[0].city);
  };
  useEffect(() => {
    ask();
  }, []);
  return (
    <View style={styles.container}>
      <View style={styles.city}>
        <Text style={styles.cityname}>{city}</Text>
        // 생략..
      </View>
    </View>
  )
}

Weather

import { View, Text, Dimensions, ActivityIndicator, ScrollView, StyleSheet } from 'react-native';
import * as Location from "expo-location";
import React, { useEffect, useState } from "react";

const{ width:SCREEN_WIDTH }=Dimensions.get("window");

const API_KEY = ""; //자바스크립트 키 가져오기

export default function App() {
  const [city, setCity] = useState("Loading...");
  const [days, setDays] = useState([]);
  const [ok, setOk] = useState(true);
  const getWeather = async () => {
    const { granted } = await Location.requestForegroundPermissionsAsync();
    if (!granted) {
      setOk(false);
    }
    const {
      coords: { latitude, longitude },
    } = await Location.getCurrentPositionAsync({ accuracy: 5 });
    const location = await Location.reverseGeocodeAsync(
      { latitude, longitude },
      { useGoogleMaps: false }
    );
    setCity(location[0].city);
    const response = await fetch(`https://api.openweathermap.org/data/2.5/forecast?lat=${latitude}&lon=${longitude}&appid=${API_KEY}&units=metric`); //날씨 api로 가져오기
    //&units=metric : 섭씨로 바꾸기
    const json = await response.json();
    setDays(
        json.list.filter((weather) => {
          if (weather.dt_txt.includes("0:00:00")) {return weather;} //온도 가져오기
          })
        );
  };
  useEffect(() => {
    getWeather();
  }, []);
  return (
    <View style={styles.container}>
      <View style={styles.city}>
        <Text style={styles.cityname}>{city}</Text>
      </View>
      <ScrollView
        pagingEnabled
        horizontal
        indicatorStyle='gray'
        contentContainerStyle={styles.weather}
      >
        {days.length === 0 ? (
        <View style={styles.day}>
          <ActivityIndicator
              color="gray"
              style={{ marginTop: 10 }}
              size="large"
            /></View>
          ) : (
            days.map((day, index) => (
              <View key={index} style={styles.day}>
                <Text style={styles.tinyText}>{new Date(day.dt * 1000).toString().substring(0, 10)}</Text>
              <Text style={styles.temp}>
                {parseFloat(day.main.temp).toFixed(1)}
              </Text>
              <Text style={styles.desc}>{day.weather[0].main}</Text>
              <Text style={styles.tinyText}>{day.weather[0].description}</Text>
            </View>
          ))
        )}
      </ScrollView>
    </View>

  );
}

const styles = StyleSheet.create({
  //생략..
   tinyText: {
    fontSize: 20,
  },
})
  • 삼향 연산자
    • {조건 ? (true일 때) : (false일 때) }
    • 위에선 day 길이가 0일 경우 ActivityIndicator로 로딩 애니메이션이 뜨도록 함
  • {parseFloat(day.main.temp).toFixed(1)}
    • 소수점 아래 한 자리만 보이도록

Icons

import { Fontisto } from "@expo/vector-icons";

const icons = {
  Clouds: "cloudy",
  Clear: "day-sunny",
  Atmosphere: "cloudy-gusts",
  Snow: "snow",
  Rain: "rains",
  Drizzle: "rain",
  Thunderstorm: "lightning",
};
//생략..
return (
    //생략..
    {days.length === 0 ? (
         <View style={{ ...styles.day, alignItems: "center" }}> //스타일 2개를 적용시키는 방법 {} 2개 열고 스타일1, 스타일2
          <ActivityIndicator
              color="gray"
              style={{ marginTop: 10 }}
              size="large"
            /></View>
          ) : (
            days.map((day, index) => (
              <View key={index} style={styles.day}>
                <Text style={styles.tinyText}>{new Date(day.dt * 1000).toString().substring(0, 10)}</Text>
                <View
                style={{
                  flexDirection: "row",
                  alignItems: "center",
                  width: "100%",
                  justifyContent: "space-between",
                }}
              >
                <Text style={styles.temp}>
                  {parseFloat(day.main.temp).toFixed(1)}
                </Text>
                <Fontisto
                  name={icons[day.weather[0].main]} //api로 가져온 날씨 이름 넘겨주기
                  size={68}
                  color="black"
                />
              </View>
              <Text style={styles.desc}>{day.weather[0].main}</Text>
              <Text style={styles.tinyText}>{day.weather[0].description}</Text>
            </View>
            );
            ))}
)