✅ 처음부터 끝까지 그대로 따라치는 것은 의미없습니다. 영상과 제 코드 참고하셔서
Expo, React Native, API 등 공식 문서 보면서 본인이 만들고 싶은 대로 만들면 됩니다.
1. 사전 설정
npm
npm i npm
expo
npm i -g expo-cli
전부 설치하시면 사전 설정이 완료됩니다.
2. 앱 생성 & 임시 테스트
expo 공식 문서를 참고하여 원하는 폴더에
expo init 앱이름
을 해주시면 앱이 생성이 됩니다.
잘 생성되었는지 테스트하기 위해
expo start
를 해주시면 Metro Bundler라는 창이 나옵니다.
웹으로 간단히 테스트 해보시고 Expo 사이트에서 Sign up 한 다음
모바일 Expo Go를 다운받고 로그인하고 콘솔에서
expo login
을 통해 로그인해줍니다.
그러면 모바일에서 실행중인 프로젝트를 실행해볼 수 있습니다.
대신, 같은 네트워크를 사용해야되는 것으로 알고 있습니다.
3. React-Native Tips
- <div></div>가 아닌 <View></View>를 사용한다.
- 모든 text들은 <Text></Text> 사이에 위치해야한다.
- StyleSheet.create({ ~~ })로 css와 비슷하게 style을 담고 있는 객체를 만들 수 있다.
(그냥 객체로 생성해도 되지만 자동완성이 되지 않아 불편할 수 있다.)
→ 위의 세 가지를 위해
import { StyleSheet, View, Text } from 'react-native';
를 해줘야합니다.
📎 StatusBar는 상단의 시간, 와이파이, 배터리와 관련된 컴포넌트입니다.
→ 이를 위해
import { StatusBar } from 'expo-status-bar';
를 해줘야합니다.
위는 하나의 예시이고 이 외에도 다양한 컴포넌트들이 존재하니 공식 문서를 참고하면 됩니다.
위의 StatusBar는 expo에서 제공하는 것이고 React Native에서 제공하는 Core Components들도 있습니다.
4. Layout
모든 View들은 flexbox이며 flex direction의 기본값은 column입니다.
아이폰 11과 아이폰 13의 크기는 다르므로 width, height와 같이 고정된 크기가 아닌 flex를 통해
주로 레이아웃을 구성합니다. 영상과 공식 문서를 참고하여 원하는 레이아웃을 구성해봅시다.
5. Expo location
먼저 위치 정보를 얻기 위해서는 사용자에게 위치 정보 권한을 얻어야합니다.
공식 문서에 쓰여있는 것 처럼 import 해주고
import React, { useState, useEffect } from 'react';
import * as Location from 'expo-location';
권한 여부에 대한 useState를 설정한 뒤
const [permission, setPermission] = useState(true);
useEffect도 설정합니다.
useEffect(() => {
(async () => {
let { status } = await Location.requestForegroundPermissionsAsync();
if (status !== 'granted') {
setPermission(false);
}
const { coords: {latitude, longitude} } = await Location.getCurrentPositionAsync({ accuracy: 5 });
const location = await Location.reverseGeocodeAsync({latitude, longitude}, {useGoogleMaps: false});
console.log(location);
})();
}, []);
useState와 useEffect에 대한 내용은 React의 공식 문서를 참고하고
requestForegroundPermissionAsync(), reverseGeocodeAsync()와 같은 메소드에 대한 내용은
expo의 geolocation 문서를 참고하시면 됩니다.
콘솔 로그로 도시가 제대로 찍히면
const [city, setCity] = useState('Seoul');
.. 생략
//console.log(location);
setCity(location[0].city);
와 같이 city의 useState 기본값을 서울로 설정하고 도시 정보가 받아와지면 city의 state가 변경됩니다.
아이폰 시뮬레이터의 경우 샌프란시스코가 뜹니다.
이제 위치 권한 허용 여부와 그에 따른 위치 정보를 얻었으니 날씨 정보를 가져와보겠습니다.
구글에 OpenWeathermap api를 검색하여 들어가보면 가격 정책이 나와있습니다.
저희는 무료로 이용할 것이기 때문에 분당 60개의 API 호출, 달마다 100만 개를 사용할 수 있으며
현재 날씨, 3시간 간격의 5일 예보, 대기 오염 등의 API를 사용할 수 있습니다.
가입 후 API key를 받아오시면 됩니다.
const API_KEY = 'blahblah';
와 같이 사용할 수도 있지만 깃허브같은 곳에 API key를 올리는 것은 안전하지 않습니다.
따라서, 따로 keys.js 와 같이 파일을 만든 뒤
module.exports = {
API_KEY: 'blahblah';
}
와 같이 선언해두고 App.js에서 import하여 사용하는 것을 권장합니다.
깃허브에 업로드할 때에는 .gitignore에 keys.js를 적어두면 됩니다.
일기예보 정보를 갖고오기 위해 Call 5 day / 3 hour forecast data API 문서를 열어보면
API 호출 방법이 있으니 그대로 가져와서 찾아놓은 위도, 경도와 API key로 호출하여 잘 찍히는지 확인해봅시다.
const response = await fetch(`https://api.openweathermap.org/data/2.5/forecast?lat=${latitude}&lon=${longitude}&appid=${API_KEY}&units=metric`);
const json = await response.json();
console.log(json);
잘 찍힌다면 json을 살펴봅시다. json 안의 list 객체에 5일간의 날씨 정보가 3시간 간격으로 찍혀있습니다.
저는 이 정보를 이용하여 가로로 된 ScrollView에 하나씩 넣을 생각입니다.
이를 위해 days를 useState로 생성하고 빈 배열로 초기화한 뒤 json.list로 값을 바꿔줍니다.
const [days, setDays] = useState([]);
.. 생략
setDays(json.list);
하루를 3시간으로 나누면 8개로 나눠지고 5일이므로 배열에는 5*8 = 40개의 인덱스가 들어있을 것입니다.
이를 map 함수로 매핑하여 스크롤뷰에 넣어주겠습니다. days 배열의 원소는 day로 표시됩니다.
만약 days의 길이가 0이라면 로딩중이므로 로딩 아이콘을 넣어주고
아니라면 온도와 설명을 보여주게 하였습니다. 필요한 부분만 json을 살펴보며 갖고오시면 됩니다.
<ScrollView pagingEnabled horizontal
showsHorizontalScrollIndicator={false}
contentContainerStyle={styles.weather}>
{days.length === 0 ? (
<View style={styles.day}>
<ActivityIndicator color="white" size="large"/>
</View>
) : (
days.map((day, index) =>
<View key={index} style={styles.day}>
<Text style={styles.temp}>{day.main.temp}</Text>
<Text style={styles.description}>{day.weather[0].description}</Text>
</View>)
)}
</ScrollView>
이제 기본은 거의 다 완성된 상태입니다.
다음으로 해야할 것들은
1. permission === false일 때 어떻게 처리할지?
2. 이미지, 아이콘 등으로 UI를 보기 좋게 꾸미기
정도입니다.
이것들을 포함해서 나머지 부가적인 부분들은 본인의 취향대로 각자 구현해보시면 됩니다!
감사합니다. 👏