stack navigation 사용
import React, { useEffect } from 'react';
import { Alert, BackHandler, Button, FlatList, SafeAreaView, StyleSheet, Text, TextInput, View } from 'react-native';
import { createStackNavigator } from '@react-navigation/stack';
import { CommonActions, NavigationContainer, StackActions, useNavigation } from '@react-navigation/native';
import Login from './src/screens/Login';
import List from './src/screens/List';
import DetailA from './src/screens/DetailA';
import DetailBC from './src/screens/DetailBC';
import Camera from './src/components/Camera';
import Ionicons from '@expo/vector-icons/Ionicons';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { tr } from 'date-fns/locale';
import { useReactQueryDevTools } from '@dev-plugins/react-query';
import { QueryClient, useQuery } from '@tanstack/react-query';
const Stack = createStackNavigator();
const queryClient = new QueryClient();
export default function App({ route }) {
useEffect(() => {
// const gestureHandler = navigation.addListener('beforeRemove', (e) => {
// e.preventDefault();
// alert('뒤로가기 금지');
// });
const backAction = () => {
Alert.alert('앱 종료', '앱 종료하시겠습니까?', [
{ text: '취소', onPress: () => {}, style: 'cancel' },
{ text: '확인', onPress: () => BackHandler.exitApp() },
]);
return true;
};
const backHandler = BackHandler.addEventListener('hardwareBackPress', backAction);
return () => {
backHandler.remove();
};
});
return (
<NavigationContainer>
<Stack.Navigator
screenOptions={({ navigation }) => ({
// headerLeft: () => null,
headerRight: () => {
return (
<Ionicons
name="exit"
size={30}
color="#fff"
onPress={() => {
Alert.alert('로그아웃', '로그아웃하시겠어요?', [
{
text: '취소',
onPress: () => {},
style: 'cancel',
},
{
text: '확인',
onPress: async () => {
try {
await AsyncStorage.removeItem('id');
await AsyncStorage.removeItem('accesstoken');
await AsyncStorage.clear();
navigation.dispatch(
CommonActions.reset({
index: 0,
routes: [{ name: 'Login' }],
})
);
} catch (error) {
console.log(error);
}
},
},
]);
}}
style={{ marginRight: 20 }}
/>
);
},
headerTitleAlign: 'center',
headerStyle: {
backgroundColor: '#096e5b',
},
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold',
overflow: 'scroll',
},
})}
initialRouteName="Login"
>
<Stack.Screen
name="Login"
component={Login}
options={{
headerShown: false,
}}
/>
<Stack.Screen
name="List"
component={List}
options={(route) => ({
headerTitle: () => {
return (
// ${route.route.params}
<Text
style={styles.headerTitle}
numberOfLines={2}
>{`${route.route.params.id}\n목록화면`}</Text>
);
},
})}
/>
<Stack.Screen
name="DetailA"
component={DetailA}
options={(route) => ({
headerTitle: () => {
return (
<Text style={styles.headerTitle} numberOfLines={2}>
{`${route.route.params.id}\n${`상세화면`} `}
</Text>
);
},
})}
/>
<Stack.Screen
name="DetailBC"
component={DetailBC}
options={(route) => ({
headerTitle: () => {
return (
<Text style={styles.headerTitle} numberOfLines={2}>
{`${route.route.params.id}\n${`${route.route.params.item.boardState} 화면`} `}
</Text>
);
},
})}
/>
<Stack.Screen
name="Camera"
component={Camera}
options={({ navigation }) => ({
headerLeft: () => {
return (
<Ionicons
name="arrow-back"
size={30}
color="#fff"
onPress={() => {
console.log('headerLeft goBack');
if (navigation.canGoBack()) {
navigation.goBack();
}
}}
style={{ marginLeft: 20 }}
/>
);
},
})}
/>
</Stack.Navigator>
</NavigationContainer>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
headerTitle: {
textAlign: 'center',
fontSize: 18,
color: 'white',
fontWeight: 'bold',
paddingBottom: 15,
},
});
token을 받아와 로그인
카메라, 위치 허가 받기
import AsyncStorage from '@react-native-async-storage/async-storage';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import axios from 'axios';
import React, { useEffect, useState } from 'react';
import { Alert, Button, SafeAreaView, StyleSheet, Text, TextInput, View } from 'react-native';
import { Platform, PermissionsAndroid } from 'react-native';
export default function Login({ navigation }) {
const [id, onChangeId] = useState('admin');
const [password, onChangePassword] = useState('1');
const [login, setLogin] = useState(false);
const handleLogin = async () => {
if (id !== '' && password !== '') {
//member 정보가 미입력 상태면 함수 발동 x
const formData = new FormData();
formData.append('username', id);
formData.append('password', password);
await axios
.post('http://10.0.2.2:8080/login', formData, {
headers: {
'Content-Type': 'multipart/form-data',
// 'Content-Type': 'application/json', // 서버가 JSON 형식 기대
},
})
.then(async (response) => {
//응답결과 정상적으로 넘어온 경우 아래 로직 수행
Alert.alert('로그인', '로그인 성공');
await AsyncStorage.setItem('accessToken', response.headers.authorization); // 토큰 저장
await AsyncStorage.setItem('id', id); // 토큰 저장
navigation.navigate('List', { id: id });
})
.catch((err) => {
//서버에서 에러가 발생한 경우 경고창 알림
Alert.alert('에러', `err : ${err.response}`);
});
} else {
Alert.alert('로그인', 'ID 혹은 비밀번호를 입력하세요.'); // member 정보 미입력 시 알림
}
};
useEffect(() => {
if (Platform.OS === 'android') {
const requestCameraPermission = async () => {
try {
const granted = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.CAMERA, {
title: 'Camera Permission',
message: 'App needs permission for camera access',
buttonPositive: 'OK',
});
// if (granted === PermissionsAndroid.RESULTS.GRANTED) {
// Alert.alert('접근 허가', '카메라 접급 허가');
// } else {
// Alert.alert('접근 허가 필요', '카메라 접근 허가 필요');
// }
if (granted !== PermissionsAndroid.RESULTS.GRANTED) {
Alert.alert('접근 허가 필요', '카메라 접근 허가 필요');
}
} catch (err) {
Alert.alert('카메라 접근 허가 오류');
console.warn(err);
}
};
requestCameraPermission();
const requestGPSPermission = async () => {
try {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
{
title: 'GPS Permission',
message: 'App needs permission for gps access',
buttonPositive: 'OK',
}
);
if (granted !== PermissionsAndroid.RESULTS.GRANTED) {
Alert.alert('접근 허가 필요', '위치 접근 허가 필요');
// Alert.alert('접근 허가', '위치 접근 허가');
}
// else {
// Alert.alert('접근 허가 필요', '위치 접근 허가 필요');
// }
} catch (err) {
Alert.alert('위치 접근 허가 오류');
console.warn(err);
}
};
requestGPSPermission();
}
});
return (
<SafeAreaView style={styles.container}>
<View style={styles.row}>
<View style={styles.column}>
<TextInput style={styles.input} onChangeText={onChangeId} value={id} placeholder="아이디" />
<TextInput
style={styles.input}
onChangeText={onChangePassword}
value={password}
secureTextEntry={true}
placeholder="비밀번호"
/>
<View style={styles.buttonItem}>
<Button color={'#096e5b'} title="로그인" onPress={(e) => handleLogin()} />
</View>
</View>
</View>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
input: {
width: 150,
borderWidth: 1,
padding: 10,
marginBottom: 20,
},
row: {
flexDirection: 'row',
},
column: {
flexDirection: 'column',
gap: 5,
height: 100,
width: 200,
alignItems: 'center',
justifyContent: 'center',
},
buttonItem: {
width: '75%',
alignItems: 'center',
justifyContent: 'center',
},
button: {},
});
import AsyncStorage from '@react-native-async-storage/async-storage';
import { useFocusEffect } from '@react-navigation/native';
import axios from 'axios';
import { format } from 'date-fns';
import React, { useEffect, useRef, useState } from 'react';
import { Alert, AppState, FlatList, SafeAreaView, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
export default function List({ navigation }) {
const [data, setData] = useState([]);
const GETLIST_URL = 'http://10.0.2.2:8080/api/getList';
const [id, setId] = useState('');
const [ok, setOk] = useState(true);
// 앱 상태 테스트
const appState = useRef(AppState.currentState);
const [appStateVisible, setAppStateVisible] = useState(appState.current);
const getList = async () => {
const id = await AsyncStorage.getItem('id');
setId(id);
const token = await AsyncStorage.getItem('accessToken');
// console.log('list token :: ', token);
axios
.get(GETLIST_URL, {
headers: {
Authorization: `${token}`,
},
})
.then((res) => {
let tmpData = res.data.data;
tmpData.map((e) => {
if (e.boardState == 'B' || e.boardState == 'C') {
setOk(false);
navigation.navigate('DetailBC', { item: e, id: id });
}
});
setData(res.data.data);
})
.catch((err) => console.log(err));
};
useEffect(() => {
// const subscription = AppState.addEventListener('change', (nextAppState) => {
// if (appState.current.match(/inactive|background/) && nextAppState === 'active') {
// console.log('App has come to the foreground!');
// }
// appState.current = nextAppState;
// setAppStateVisible(appState.current);
// console.log('AppState : ', appState.current);
// });
getList();
return () => {
// subscription.remove();
};
}, []);
const Item = ({ onPress, title, contents, writer, inDate, backgroundColor }) => (
<TouchableOpacity onPress={onPress} style={[styles.item, { backgroundColor }]}>
<Text style={styles.title}>제목 : {title}</Text>
<Text style={styles.title} numberOfLines={1}>
내용 : {contents}
</Text>
<Text style={styles.title}>작성자 : {writer}</Text>
<Text style={styles.title}>작성일자 : {format(inDate, 'yyyy-mm-dd HH:mm')}</Text>
</TouchableOpacity>
);
const renderItem = ({ item }) => {
const backgroundColor = item.boardState == 'A' || item.boardState == 'D' ? '#c4c2c2' : '#FFF';
// console.log('backgroundColor : ',backgroundColor)
return (
<Item
onPress={() => {
console.log('onclick : ', item);
if (item.boardState == 'D') {
Alert.alert('진입불가', '상태 값이 D입니다.');
} else if (item.boardState == 'A') {
navigation.navigate('DetailA', { item: item, id: id });
} else {
navigation.navigate('DetailBC', { item: item, id: id });
}
}}
title={item.boardTitle}
contents={item.boardContents}
writer={item.boardWriter}
inDate={new Date(item.boardIndate)}
backgroundColor={backgroundColor}
/>
);
};
if (ok) {
return (
<SafeAreaView style={styles.container}>
<FlatList data={data} renderItem={renderItem} keyExtractor={(data) => data.boardSeq.toString()} />
</SafeAreaView>
);
} else {
return <View></View>;
}
}
const styles = StyleSheet.create({
container: {
// flex: 1,
// backgroundColor: '#fff',
// alignItems: 'center',
// justifyContent: 'center',
},
input: {
height: 30,
width: 100,
borderWidth: 1,
padding: 10,
marginBottom: 20,
},
row: {
flexDirection: 'row',
gap: 10,
},
button: {
width: 200,
},
item: {
backgroundColor: '#fff',
padding: 20,
marginVertical: 8,
marginHorizontal: 16,
},
title: {
fontSize: 20,
},
});
import axios from 'axios';
import { format } from 'date-fns';
import { CameraView } from 'expo-camera';
import React, { useCallback, useEffect, useState } from 'react';
import { Button, FlatList, Image, SafeAreaView, StyleSheet, Text, View } from 'react-native';
import MapView, { Marker } from 'react-native-maps';
import TmapView from '../components/tmap';
import Ionicons from '@expo/vector-icons/Ionicons';
import AsyncStorage from '@react-native-async-storage/async-storage';
import * as Location from 'expo-location';
import { useFocusEffect } from '@react-navigation/native';
export default function List({ route, navigation }) {
const { item, otherParam } = route.params;
const [data, setData] = useState(item);
const [id, setId] = useState('');
const UPDATE_URL = 'http://10.0.2.2:8080/api/updateAtoB';
const DETAIL_URL = 'http://10.0.2.2:8080/api/getDetail';
const [city, setCity] = useState('');
useEffect(() => {
getId();
}, []);
const getId = async () => {
const Tid = await AsyncStorage.getItem('id');
setId(Tid);
};
//A -> B 업데이트
const updateAtoB = async () => {
const token = await AsyncStorage.getItem('accessToken');
console.log('update token : ', token);
await axios
.post(
UPDATE_URL,
{
boardSeq: data.boardSeq,
boardLoc: data.boardLoc,
},
{
headers: {
Authorization: `${token}`,
'Content-Type': 'application/json',
},
}
)
.then((res) => {
console.log('update res', res);
if (res.data >= 0) {
getDetail();
}
})
.catch((err) => console.log('update err :', err));
};
// 상세화면 불러오기
const getDetail = async () => {
const token = await AsyncStorage.getItem('accessToken');
console.log('getDEtail boardSeq : ', data.boardSeq);
await axios
.post(DETAIL_URL, data.boardSeq, {
headers: {
Authorization: `${token}`,
'Content-Type': 'application/json',
},
})
.then((res) => {
console.log('getDetail res.data : ', res.data);
navigation.navigate('DetailBC', { item: res.data, id: id });
})
.catch((err) => console.log('getDetail err : ', err));
};
const getCity = (e) => {
setCity(e);
// console.log('City : ', e);
setData({
boardSeq: data.boardSeq,
boardTitle: data.boardTitle,
boardContents: data.boardContents,
boardWriter: data.boardWriter,
boardIndate: data.boardIndate,
boardLoc: e,
boardState: data.boardState,
});
};
return (
<SafeAreaView style={styles.container}>
<View style={styles.item}>
<Text style={styles.title}>제목 : {data.boardTitle}</Text>
<Text style={styles.title}>내용 : {data.boardContents}</Text>
<Text style={styles.title}>작성자 : {data.boardWriter}</Text>
<Text style={styles.title}>작성일자 : {format(data.boardIndate, 'yyyy-mm-dd HH:mm')}</Text>
</View>
<View style={[styles.cameraItem]}>
<Ionicons name="camera" size={40} onPress={(e) => navigation.navigate('Camera')} />
{/* <Image source={{ uri: fileUri }} style={{ width: 200, height: 200, resizeMode: 'contain' }} /> */}
</View>
<View style={[styles.mapItem]}>
<TmapView getCity={getCity}></TmapView>
</View>
<View style={[styles.item]}>
<Text style={styles.title}>주소 : {data.boardLoc}</Text>
</View>
<View style={styles.buttonItem}>
<Button style={styles.button} color={'#096e5b'} title="상태변경(A -> B)" onPress={updateAtoB} />
</View>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
input: {
height: 30,
width: 100,
borderWidth: 1,
padding: 10,
marginBottom: 20,
},
row: {
flexDirection: 'row',
gap: 10,
},
button: {
fontSize: 30,
fontWeight: 200,
},
buttonItem: {
paddingHorizontal: 15,
},
textItem: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
text: {
fontStyle: 'bold',
fontSize: 400,
},
item: {
backgroundColor: '#fff',
padding: 20,
marginVertical: 8,
marginHorizontal: 16,
},
title: {
fontSize: 20,
},
cameraItem: {
paddingHorizontal: 15,
justifyContent: 'center',
alignItems: 'center',
height: 160,
},
mapItem: {
paddingHorizontal: 15,
height: 300,
},
map: {
width: '100%',
height: '100%',
},
});
import axios from 'axios';
import { format } from 'date-fns';
import { CameraView } from 'expo-camera';
import React, { useCallback, useEffect, useState } from 'react';
import { Button, FlatList, SafeAreaView, StyleSheet, Text, View } from 'react-native';
import MapView, { Marker } from 'react-native-maps';
import TmapView from '../components/tmap';
import Ionicons from '@expo/vector-icons/Ionicons';
import AsyncStorage from '@react-native-async-storage/async-storage';
import * as Location from 'expo-location';
import { useFocusEffect } from '@react-navigation/native';
export default function List({ route, navigation }) {
const { item, otherParam } = route.params;
const [data, setData] = useState(item);
const DETAIL_URL = 'http://10.0.2.2:8080/api/getDetail';
const [city, setCity] = useState('');
useEffect(() => {
getRefresh();
}, []);
const getRefresh = () => {
if (data.boardState == 'B' || data.boardState == 'C') {
//B,C는 10초마다 데이터 갱신
setInterval(() => {
getDetail();
}, 10000);
}
};
// 상세화면 불러오기
const getDetail = async () => {
const token = await AsyncStorage.getItem('accessToken');
console.log('getDEtail boardSeq : ', data.boardSeq);
await axios
.post(DETAIL_URL, data.boardSeq, {
headers: {
Authorization: `${token}`,
'Content-Type': 'application/json',
},
})
.then((res) => {
console.log('getDetail res.data : ', res.data);
setData(res.data);
})
.catch((err) => console.log('getDetail err : ', err));
};
return (
<SafeAreaView style={styles.container}>
<View style={styles.item}>
<Text style={styles.title}>제목 : {data.boardTitle}</Text>
<Text style={styles.title}>내용 : {data.boardContents}</Text>
<Text style={styles.title}>작성자 : {data.boardWriter}</Text>
<Text style={styles.title}>작성일자 : {format(data.boardIndate, 'yyyy-mm-dd HH:mm')}</Text>
</View>
<View style={styles.textItem}>
<Text style={styles.text}>{data.boardState}</Text>
</View>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
input: {
height: 30,
width: 100,
borderWidth: 1,
padding: 10,
marginBottom: 20,
},
row: {
flexDirection: 'row',
gap: 10,
},
button: {
fontSize: 30,
fontWeight: 200,
},
buttonItem: {
paddingHorizontal: 15,
},
textItem: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
text: {
fontStyle: 'bold',
fontSize: 400,
},
item: {
backgroundColor: '#fff',
padding: 20,
marginVertical: 8,
marginHorizontal: 16,
},
title: {
fontSize: 20,
},
cameraItem: {
paddingHorizontal: 15,
justifyContent: 'center',
alignItems: 'center',
height: 160,
},
mapItem: {
paddingHorizontal: 15,
height: 300,
},
map: {
width: '100%',
height: '100%',
},
});
import React, { useEffect, useRef, useState } from 'react';
import { View, StyleSheet, Text, TouchableOpacity, Image, Alert } from 'react-native';
import { CameraView, useCameraPermissions } from 'expo-camera';
import * as ImageManipulator from 'expo-image-manipulator';
import * as FileSystem from 'expo-file-system';
import AsyncStorage from '@react-native-async-storage/async-storage';
import axios from 'axios';
export default function CameraScreen(props) {
const [permission, requestPermission] = useCameraPermissions();
const cameraRef = useRef(null);
const [photoUri, setPhotoUri] = useState(null); // 원본 사진
const [rotatedUri, setRotatedUri] = useState(null); // 회전된 사진
const [rotation, setRotation] = useState(0); // 회전 각도
// 권한 체크
const checkPermissions = async () => {
if (!permission) return;
if (permission.status !== 'granted') {
if (!permission.canAskAgain) {
Alert.alert('권한 필요', '앱 설정에서 카메라 권한을 변경해주세요.', [
{ text: '설정 열기', onPress: () => Linking.openSettings() },
]);
} else {
await requestPermission();
}
}
};
useEffect(() => {
checkPermissions();
}, [permission]);
// 사진 촬영
const takePhoto = async () => {
if (cameraRef.current) {
try {
const photo = await cameraRef.current.takePictureAsync(); // 사진 촬영
setPhotoUri(photo.uri); // 사진 URI 저장
setRotatedUri(photo.uri); // 초기 회전된 사진 URI 저장
setRotation(0); // 초기 회전값
} catch (error) {
console.error('사진 촬영 실패:', error);
Alert.alert('사진 촬영 실패');
}
}
};
// 이미지 회전
const rotateImage = async () => {
if (!rotatedUri) return;
try {
const result = await ImageManipulator.manipulateAsync(rotatedUri, [
{ rotate: 90 }, // 90도 회전
]);
setRotatedUri(result.uri); // 회전된 이미지 URI 저장
setRotation((prev) => (prev + 90) % 360); // 현재 회전 각도 업데이트
} catch (error) {
console.error('이미지 회전 실패:', error);
Alert.alert('이미지 회전 실패');
}
};
// 이미지 저장
const saveImage = async () => {
// console.log('save!@@!@');
if (!rotatedUri) return;
// const TOKEN = await AsyncStorage.getItem('accessToken');
// console.log('camera TOKEN : ', TOKEN);
// const formData = new FormData();
// formData.append('file', {
// rotatedUri,
// name: 'photo.jpg',
// type: 'image/jpeg',
// });
// try {
// const response = await axios.post('http://10.0.2.2:8080/api/saveImage', formData, {
// headers: {
// Authorization: `${TOKEN}`,
// 'Content-Type': 'multipart/form-data',
// },
// });
// if (response.status === 200) {
// Alert.alert('사진', '사진이 서버에 저장되었습니다!');
// } else {
// console.error('서버 응답 에러: ', response.data);
// Alert.alert('사진', '사진 업로드 실패');
// }
// } catch (err) {
// console.error('사진 업로드 중 에러 발생: ', err);
// Alert.alert('사진', '사진 업로드 실패');
// }
try {
// 1. 로컬에 저장
const fileName = `rotated-image-${Date.now()}.jpg`;
const fileUri = `${FileSystem.documentDirectory}${fileName}`;
await FileSystem.copyAsync({
from: rotatedUri,
to: fileUri,
});
Alert.alert('저장 완료', `이미지가 로컬에 저장되었습니다:\n${fileUri}`);
console.log('저장된 로컬 이미지 경로:', fileUri);
// 2. 서버에 저장
const TOKEN = await AsyncStorage.getItem('accessToken');
console.log('camera TOKEN : ', TOKEN);
const formData = new FormData();
formData.append('file', {
uri: rotatedUri,
name: 'photo.jpg',
type: 'image/jpeg',
});
const response = await axios.post('http://10.0.2.2:8080/api/saveImage', formData, {
headers: {
Authorization: `${TOKEN}`,
'Content-Type': 'multipart/form-data',
},
});
if (response.status === 200) {
Alert.alert('업로드 완료', '이미지가 서버에 저장되었습니다!');
} else {
console.error('서버 응답 에러:', response.data);
Alert.alert('업로드 실패', '이미지를 서버에 저장하는 중 문제가 발생했습니다.');
}
// props.setImage(fileUri);
} catch (error) {
console.error('이미지 저장 실패:', error);
Alert.alert('이미지 저장 실패');
}
};
return (
<View style={styles.container}>
{!photoUri ? (
<CameraView style={styles.camera} facing="back" ref={cameraRef}>
<View style={styles.buttonContainer}>
<TouchableOpacity style={styles.button} onPress={takePhoto}>
<Text style={styles.text}>사진 찍기</Text>
</TouchableOpacity>
</View>
</CameraView>
) : (
<View style={styles.imageContainer}>
<Image source={{ uri: rotatedUri }} style={styles.imagePreview} />
<View style={styles.controls}>
<TouchableOpacity style={styles.button} onPress={rotateImage}>
<Text style={styles.text}>회전</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.button} onPress={saveImage}>
<Text style={styles.text}>저장</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.button, styles.cancelButton]}
onPress={() => setPhotoUri(null)}
>
<Text style={styles.text}>다시 찍기</Text>
</TouchableOpacity>
</View>
</View>
)}
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
backgroundColor: '#000',
},
camera: {
flex: 1,
},
buttonContainer: {
flex: 1,
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'flex-end',
paddingBottom: 20,
},
button: {
backgroundColor: '#444',
padding: 10,
marginHorizontal: 10,
borderRadius: 5,
},
text: {
color: '#fff',
fontSize: 16,
},
imageContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
imagePreview: {
width: '90%',
height: '70%',
resizeMode: 'contain',
},
controls: {
flexDirection: 'row',
justifyContent: 'center',
marginTop: 20,
},
cancelButton: {
backgroundColor: '#c00',
},
});
import React, { useEffect, useState } from 'react';
import { StyleSheet, View, PermissionsAndroid, Platform, Alert, Text } from 'react-native';
import { WebView } from 'react-native-webview';
import * as Location from 'expo-location';
export default function TmapView(props) {
const [currentLocation, setCurrentLocation] = useState({ latitude: 37.5665, longitude: 126.978 }); // 기본 위치: 서울시청
const [loading, setLoading] = useState(true); // 로딩 상태 관리
useEffect(() => {
const getCurrentLocation = async () => {
try {
const { status } = await Location.requestForegroundPermissionsAsync();
if (!status) {
return;
}
const {
coords: { latitude, longitude },
} = await Location.getCurrentPositionAsync({ accuracy: Location.Accuracy.High });
setCurrentLocation({
latitude: latitude,
longitude: longitude,
});
// console.log('currentLocation :', currentLocation);
const locationName = await Location.reverseGeocodeAsync(
{ latitude, longitude },
{ useGoogleMaps: false }
);
// console.log('locationName : ', locationName);
if (locationName) {
props.getCity(locationName[0].formattedAddress);
}
} catch (error) {
console.error('위치 정보를 가져오는 중 오류 발생:', error);
Alert.alert('오류', '위치 정보를 가져오는 중 문제가 발생했습니다.');
} finally {
setLoading(false);
}
};
getCurrentLocation();
}, [currentLocation]);
// const handleWebViewMessage = (event) => {
// try {
// const data = JSON.parse(event.nativeEvent.data);
// if (data.type === 'address') {
// Alert.alert('클릭한 위치의 주소', data.address);
// }
// } catch (error) {
// console.error('Error parsing message from WebView:', error);
// }
// };
const tmapHtml = `
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<script src="https://apis.openapi.sk.com/tmap/jsv2?version=1&appKey=rhnIyM1eA07OkeSGBqHFy9D5WEGTwcSJ5UAMgMvZ"></script>
<style>
html, body, #map_div { width: 100%; height: 100%; margin: 0; padding: 0; }
</style>
</head>
<body>
<div id="map_div"></div>
<script>
var map = new Tmapv2.Map("map_div", {
center: new Tmapv2.LatLng(${currentLocation.latitude}, ${currentLocation.longitude}),
zoom: 18,
zoomControl: true,
});
var clickMarker = new Tmapv2.Marker({
position: new Tmapv2.LatLng(${currentLocation.latitude}, ${currentLocation.longitude}),
map: map,
iconSize: new Tmapv2.Size(48, 76)
});
// var clickMarker = null;
map.addListener("click", function getCurrentLocation(e) {
console.log('MAP CLICK');
var lat = e.latLng.lat();
var lng = e.latLng.lng();
if (clickMarker) {
clickMarker.setMap(null);
}
clickMarker = new Tmapv2.Marker({
position: new Tmapv2.LatLng(lat, lng),
map: map,
iconSize: new Tmapv2.Size(50, 50)
});
fetch(\`https://apis.openapi.sk.com/tmap/geo/reversegeocoding?version=1&lat=\${lat}&lon=\${lng}&appKey=rhnIyM1eA07OkeSGBqHFy9D5WEGTwcSJ5UAMgMvZ&format=json\`)
.then(response => {
console.log('response: ', response)
response.json()
})
.then(data => {
console.log('fetch data: ',data)
var address = data.addressInfo.fullAddress || "주소 정보를 찾을 수 없습니다.";
window.ReactNativeWebView.postMessage(JSON.stringify({ type: 'address', address }));
})
.catch(error => console.error('Error:', error));
});
</script>
</body>
</html>
`;
``;
return (
<View style={styles.container}>
<WebView
source={{ html: tmapHtml }}
style={styles.webview}
javaScriptEnabled={true}
domStorageEnabled={true}
// onMessage={handleWebViewMessage}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
webview: {
flex: 1,
},
});