React Native - 기초1
Feb 6, 2025
»
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>
);
))}
)