본문 바로가기
리액트(React)

React Native - 위치기반 날씨앱 만들기(2)

by 즐거운코딩 2024. 11. 10.
반응형

이전 포스트에 이어서 날씨 정보 API를 사용하여 현재 위치 날씨를 보여주는 앱 페이지를 만들어 보겠습니다.

 

날씨 정보는 OpenWeatherMap을 사용합니다. 기본적인 사용법 및 API key 생성은 제 이전 포스트를 참고하기 바랍니다.

2023.12.13 - [개발활용툴] - 날씨 API - OpenWeather 사용하기(1)

 

날씨 API - OpenWeather 사용하기(1)

날씨 정보는 다양한 종류에 활용도가 높은 컨텐츠 중에 하나입니다.본인이 제공하는 주요 컨텐츠에 맞게 적절하게 사용한다면 좀 더 풍부한 정보를 가진 사이트를 만들 수 있겠습니다.여러 종

peter-codinglife.tistory.com

 

1.  기본 기능 리스트

  • 처음 앱 구동시 위치정보 동의 여부 확인하기
  • 현재 핸드폰 위치정보 조회 하기
  • 조회한 경위도 위치정보로 Openweathermap API 로 현재 위치 날씨 조회하기

2.  화면 UI 구성

  • 지역명 - 시군구 표출
  • 현재 시간 - 연월일 및 시각
  • 현재 온도 - 단위 ℃
  • 날씨 Icon - 날씨 정보에 매칭된 아이콘 표출
  • 날씨 설명

날씨 앱 화면 UI

 

3.  기능 구현

  • 날씨 Icon 
    • expo/vector-icons 의 Fontisto 사용
    • icons 에서 openweathermap에서 조회된 날씨에 해당하는 icon name를 지정
  • 날씨 표출 명칭
    • weather_name에서 openweathermap에서 조회된 날씨에 해당하는 한글 표출 명칭 지정
  • 핸드폰 화면 크기 설정
    • Dimensions 이용 화면의 폭(width)을 가져옴
  • API_KEY
    • openweathermap의 API 조회 key로 간단한 화면 구현을 위해 별도 환경변수로 빼지 않고 직접 App.js 파일내 작성
  • 초기에 현재 위치 정보 불러오는데 시간 소요를 고려하여 city의 useState에 초기값으로 "Loading..." 이 표시되도록 함
  • getWeather 함수에 필요한 기능 정의
    • 위치정보 조회 승인 여부 확인
    • accuracy 5 수준으로 현재위치 조회하여 조회된 값중 경위도(longitude, latitude) 값을 저장
    • 경위도 값으로 주소지 가져와 city 값을 설정
    • 경위도 값으로 현위치 날씨정보 조회하기
    • Date 함수 이용하여 현재 시간 정보 가져오기
  • useEffect 이용하여 앱 초기 구동시 getWeather가 한번만 실행되도록 함
  • Activity Indicator 이용 초기 날씨 정보 없을 때 Loading indicator가 보이도록 함
    size와 color로 크기와 색상값을 조절하여 표출 가능

Activity Indicator

 

 

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

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

const API_KEY = "f47e5f61ac1c885d01fb7*********";

const icons = {
  Clear: "day-sunny",
  Clouds: "cloudy",
  Atomsphere: "cloudy-gusts",
  Snow: "snow",
  Rain: "rains",
  Drizzle: "rain",
  Thunderstorm: "lighting",
};

const weather_name = {
  Clear: "맑음",
  Clouds: "구름",
  Atomsphere: "구름많음",
  Snow: "눈",
  Rain: "비",
  Drizzle: "이슬비",
  Thunderstorm: "폭풍우",
};

const koreaTimeDiff = 9 * 60 * 60 * 1000;

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: { longitude, latitude },
    } = await Location.getCurrentPositionAsync({ accuracy: 5 });
    const location = await Location.reverseGeocodeAsync(
      { latitude, longitude },
      { useGoogleMaps: false }
    );
    setCity(location[0].district);
    console.log(location);
    const response = await fetch(
      `https://api.openweathermap.org/data/2.5/weather?lat=${latitude}&lon=${longitude}&appid=${API_KEY}&units=metric`
    );
    const json = await response.json();
    setDays(json);
    console.log(json);
  };

  const date = new Date(days.dt * 1000).toLocaleString("en-US", {
    timeZone: "Asia/Seoul",
  });
  console.log("date", date);

  useEffect(() => {
    getWeather();
  }, []);

  return (
    <View style={styles.container}>
      <View style={styles.city}>
        <Text style={styles.cityName}>{city}</Text>
      </View>
      <View>
        <Text>{date}</Text>
      </View>
      <ScrollView
        pagingEnabled
        horizontal
        showsHorizontalScrollIndicator={false}
        contentContainerStyle={styles.weather}
      >
        {days.length === 0 ? (
          <View style={{ ...styles.day, alignItems: "center" }}>
            <ActivityIndicator
              color="white"
              style={{ marginTop: 10 }}
              size="large"
            />
          </View>
        ) : (
          <View style={styles.day}>
            <View
              style={{
                flexDirection: "row",
                alignItems: "center",
                width: "100%",
                justifyContent: "space-between",
              }}
            >
              <Text style={styles.temp}>
                {parseFloat(days.main.temp).toFixed(1)}
              </Text>
              <Fontisto
                style={{ marginRight: 20 }}
                name={icons[days.weather[0].main]}
                size={68}
                color="white"
              />
            </View>
            <Text style={styles.description}>
              {weather_name[days.weather[0].main]}
            </Text>
            <Text style={styles.tinyText}>{days.weather[0].description}</Text>
          </View>
        )}
      </ScrollView>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "tomato",
  },
  city: {
    flex: 1.2,
    justifyContent: "center",
    alignItems: "center",
  },
  cityName: {
    fontSize: 68,
    fontWeight: "500",
    color: "white",
  },
  weather: {},
  day: {
    width: SCREEN_WIDTH,
    alignItems: "center",
  },
  temp: {
    marginTop: 50,
    marginHorizontal: 20,
    fontSize: 138,
    color: "white",
  },
  description: {
    marginTop: -30,
    marginLeft: -200,
    fontSize: 60,
    color: "white",
  },
  tinyText: {
    marginLeft: -250,
    fontSize: 20,
    color: "white",
  },
});

 

👉 위치조회 결과 (샘플)

[{"city": null, "country": "대한민국", "district": "양천구", "formattedAddress": "대한민국 서울특별시 양천구 남부순환로  1", "isoCountryCode": "KR", "name": "남부순환로 1", "postalCode": null, "region": "서울특별시", "street": null, "streetNumber": "83길 18", "subregion": null, "timezone": null}]

 

👉 날씨API 조회 결과 (샘플)

{"base": "stations", "clouds": {"all": 0}, "cod": 200, "coord": {"lat": 37.5354, "lon": 126.8523}, "dt": 1731219725, "id": 1948005, "main": {"feels_like": 19.19, "grnd_level": 1018, "humidity": 45, "pressure": 1021, "sea_level": 1021, "temp": 19.96, "temp_max": 20.87, "temp_min": 17.9}, "name": "Kwangmyŏng", "sys": {"country": "KR", "id": 8105, "sunrise": 1731190017, "sunset": 1731227174, "type": 1}, "timezone": 32400, "visibility": 10000, "weather": [{"description": "clear sky", "icon": "01d", "id": 800, "main": "Clear"}], "wind": {"deg": 320, "speed": 2.57}}

     날씨정보로 습도, 기압, 최대/최저 온도, 일출시간, 일몰시간, 풍속 등 다양한 정보가 있으므로 화면 구성에 따라 다양한 정보 표출 가능

반응형