Files
DTGAPK/WEB_INTEGRATION.md

565 lines
15 KiB
Markdown

# 웹 페이지 통합 가이드
이 문서는 https://logistream.kpslp.kr 웹사이트에서 모바일 앱과 통신하는 방법을 설명합니다.
## 개요
모바일 앱은 웹뷰를 통해 웹사이트를 표시하며, JavaScript 브릿지를 통해 양방향 통신이 가능합니다.
## 1. 네이티브 기능 사용하기
### 1.1 로그인 성공 알림
사용자가 로그인에 성공하면 다음 함수를 호출하여 앱에 알립니다:
```javascript
// 로그인 API 응답 후
fetch('/api/login', {
method: 'POST',
body: JSON.stringify({ username, password })
})
.then(response => response.json())
.then(data => {
if (data.success) {
// 네이티브 앱에 로그인 성공 알림
if (window.notifyLoginSuccess) {
window.notifyLoginSuccess({
userId: data.user.id,
userName: data.user.name,
userEmail: data.user.email,
token: data.token // JWT 토큰
});
}
// 위치 추적 자동 시작
if (window.startLocationTracking) {
window.startLocationTracking();
}
}
});
```
### 1.2 로그아웃 알림
```javascript
function handleLogout() {
// 로그아웃 처리
localStorage.clear();
sessionStorage.clear();
// 네이티브 앱에 로그아웃 알림
if (window.notifyLogout) {
window.notifyLogout();
}
// 로그인 페이지로 리다이렉트
window.location.href = '/login';
}
```
### 1.3 현재 위치 요청
```javascript
// 현재 위치가 필요한 경우
function getCurrentLocation() {
return new Promise((resolve, reject) => {
// 네이티브 메시지 리스너 등록
const handler = (event) => {
if (event.detail.type === 'LOCATION_UPDATE') {
window.removeEventListener('nativeMessage', handler);
resolve(event.detail.location);
}
};
window.addEventListener('nativeMessage', handler);
// 위치 요청
if (window.requestCurrentLocation) {
window.requestCurrentLocation();
} else {
// 웹 브라우저에서 실행 중인 경우 fallback
navigator.geolocation.getCurrentPosition(
(position) => {
window.removeEventListener('nativeMessage', handler);
resolve({
latitude: position.coords.latitude,
longitude: position.coords.longitude,
accuracy: position.coords.accuracy
});
},
(error) => {
window.removeEventListener('nativeMessage', handler);
reject(error);
}
);
}
// 타임아웃 설정 (5초)
setTimeout(() => {
window.removeEventListener('nativeMessage', handler);
reject(new Error('위치 요청 타임아웃'));
}, 5000);
});
}
// 사용 예시
async function showCurrentLocation() {
try {
const location = await getCurrentLocation();
console.log('현재 위치:', location);
// 지도에 표시 등
displayOnMap(location.latitude, location.longitude);
} catch (error) {
console.error('위치 가져오기 실패:', error);
}
}
```
### 1.4 위치 추적 제어
```javascript
// 위치 추적 시작
function startTracking() {
if (window.startLocationTracking) {
window.startLocationTracking();
console.log('위치 추적 시작');
}
}
// 위치 추적 중지
function stopTracking() {
if (window.stopLocationTracking) {
window.stopLocationTracking();
console.log('위치 추적 중지');
}
}
// UI 토글 버튼 예시
const trackingButton = document.getElementById('tracking-toggle');
let isTracking = false;
trackingButton.addEventListener('click', () => {
if (isTracking) {
stopTracking();
trackingButton.textContent = '추적 시작';
} else {
startTracking();
trackingButton.textContent = '추적 중지';
}
isTracking = !isTracking;
});
```
## 2. 네이티브 메시지 수신
### 2.1 메시지 리스너 설정
```javascript
// 페이지 로드 시 리스너 등록
window.addEventListener('nativeMessage', function(event) {
const data = event.detail;
console.log('네이티브 메시지 수신:', data);
switch(data.type) {
case 'NATIVE_INFO':
handleNativeInfo(data);
break;
case 'LOCATION_UPDATE':
handleLocationUpdate(data.location);
break;
default:
console.log('알 수 없는 메시지 타입:', data.type);
}
});
```
### 2.2 네이티브 정보 처리
```javascript
function handleNativeInfo(data) {
console.log('플랫폼:', data.platform); // 'android' 또는 'ios'
console.log('버전:', data.version);
console.log('위치 권한:', data.hasLocationPermission);
// 플랫폼별 UI 조정
if (data.platform === 'ios') {
document.body.classList.add('platform-ios');
} else if (data.platform === 'android') {
document.body.classList.add('platform-android');
}
// 위치 권한이 없는 경우 안내
if (!data.hasLocationPermission) {
showAlert('위치 권한이 필요합니다. 설정에서 권한을 허용해주세요.');
}
}
```
### 2.3 위치 업데이트 처리
```javascript
function handleLocationUpdate(location) {
console.log('위치 업데이트:', location);
// 지도에 현재 위치 표시
updateMapMarker(location.latitude, location.longitude);
// UI 업데이트
document.getElementById('latitude').textContent = location.latitude.toFixed(6);
document.getElementById('longitude').textContent = location.longitude.toFixed(6);
document.getElementById('accuracy').textContent = location.accuracy.toFixed(2) + 'm';
if (location.speed !== null) {
document.getElementById('speed').textContent = (location.speed * 3.6).toFixed(1) + 'km/h';
}
}
```
## 3. 환경 감지
### 3.1 모바일 앱 환경 확인
```javascript
// 모바일 앱에서 실행 중인지 확인
function isRunningInApp() {
return typeof window.ReactNativeWebView !== 'undefined';
}
// 사용 예시
if (isRunningInApp()) {
console.log('모바일 앱에서 실행 중');
// 앱 전용 기능 활성화
enableAppFeatures();
} else {
console.log('웹 브라우저에서 실행 중');
// 웹 전용 기능 활성화
enableWebFeatures();
}
```
### 3.2 플랫폼별 처리
```javascript
let currentPlatform = 'web';
window.addEventListener('nativeMessage', function(event) {
if (event.detail.type === 'NATIVE_INFO') {
currentPlatform = event.detail.platform;
}
});
function getPlatform() {
return currentPlatform;
}
// 사용 예시
if (getPlatform() === 'ios') {
// iOS 전용 스타일 적용
applyIOSStyles();
} else if (getPlatform() === 'android') {
// Android 전용 스타일 적용
applyAndroidStyles();
}
```
## 4. 완전한 통합 예제
### 4.1 로그인 페이지
```html
<!DOCTYPE html>
<html>
<head>
<title>로그인</title>
</head>
<body>
<form id="login-form">
<input type="text" id="username" placeholder="아이디" required>
<input type="password" id="password" placeholder="비밀번호" required>
<button type="submit">로그인</button>
</form>
<script>
document.getElementById('login-form').addEventListener('submit', async (e) => {
e.preventDefault();
const username = document.getElementById('username').value;
const password = document.getElementById('password').value;
try {
const response = await fetch('/api/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password })
});
const data = await response.json();
if (data.success) {
// 로컬 스토리지에 저장
localStorage.setItem('user', JSON.stringify(data.user));
localStorage.setItem('token', data.token);
// 네이티브 앱에 알림
if (window.notifyLoginSuccess) {
window.notifyLoginSuccess({
userId: data.user.id,
userName: data.user.name,
userEmail: data.user.email,
token: data.token
});
}
// 위치 추적 시작
if (window.startLocationTracking) {
window.startLocationTracking();
}
// 메인 페이지로 이동
window.location.href = '/dashboard';
} else {
alert('로그인 실패: ' + data.message);
}
} catch (error) {
console.error('로그인 오류:', error);
alert('로그인 중 오류가 발생했습니다.');
}
});
</script>
</body>
</html>
```
### 4.2 대시보드 페이지
```html
<!DOCTYPE html>
<html>
<head>
<title>차량 위치 추적</title>
<style>
.location-info {
padding: 20px;
background: #f5f5f5;
border-radius: 8px;
margin: 10px 0;
}
.tracking-controls {
margin: 20px 0;
}
.btn {
padding: 10px 20px;
margin: 5px;
border: none;
border-radius: 4px;
cursor: pointer;
}
.btn-primary { background: #007bff; color: white; }
.btn-danger { background: #dc3545; color: white; }
</style>
</head>
<body>
<h1>차량 위치 추적 시스템</h1>
<div class="location-info">
<h2>현재 위치</h2>
<p>위도: <span id="latitude">-</span></p>
<p>경도: <span id="longitude">-</span></p>
<p>정확도: <span id="accuracy">-</span></p>
<p>속도: <span id="speed">-</span></p>
</div>
<div class="tracking-controls">
<button id="get-location" class="btn btn-primary">현재 위치 가져오기</button>
<button id="start-tracking" class="btn btn-primary">추적 시작</button>
<button id="stop-tracking" class="btn btn-danger">추적 중지</button>
<button id="logout" class="btn btn-danger">로그아웃</button>
</div>
<script>
// 네이티브 메시지 리스너
window.addEventListener('nativeMessage', function(event) {
const data = event.detail;
if (data.type === 'LOCATION_UPDATE') {
updateLocationDisplay(data.location);
} else if (data.type === 'NATIVE_INFO') {
console.log('앱 정보:', data);
}
});
// 위치 정보 표시 업데이트
function updateLocationDisplay(location) {
document.getElementById('latitude').textContent = location.latitude.toFixed(6);
document.getElementById('longitude').textContent = location.longitude.toFixed(6);
document.getElementById('accuracy').textContent = location.accuracy.toFixed(2) + 'm';
if (location.speed !== null && location.speed > 0) {
document.getElementById('speed').textContent = (location.speed * 3.6).toFixed(1) + 'km/h';
} else {
document.getElementById('speed').textContent = '0 km/h';
}
}
// 현재 위치 가져오기 버튼
document.getElementById('get-location').addEventListener('click', () => {
if (window.requestCurrentLocation) {
window.requestCurrentLocation();
} else {
alert('네이티브 앱에서만 사용 가능합니다.');
}
});
// 추적 시작 버튼
document.getElementById('start-tracking').addEventListener('click', () => {
if (window.startLocationTracking) {
window.startLocationTracking();
alert('위치 추적을 시작했습니다.');
} else {
alert('네이티브 앱에서만 사용 가능합니다.');
}
});
// 추적 중지 버튼
document.getElementById('stop-tracking').addEventListener('click', () => {
if (window.stopLocationTracking) {
window.stopLocationTracking();
alert('위치 추적을 중지했습니다.');
} else {
alert('네이티브 앱에서만 사용 가능합니다.');
}
});
// 로그아웃 버튼
document.getElementById('logout').addEventListener('click', async () => {
try {
// 서버에 로그아웃 요청
await fetch('/api/logout', { method: 'POST' });
// 로컬 데이터 삭제
localStorage.clear();
sessionStorage.clear();
// 네이티브 앱에 알림
if (window.notifyLogout) {
window.notifyLogout();
}
// 로그인 페이지로 이동
window.location.href = '/login';
} catch (error) {
console.error('로그아웃 오류:', error);
}
});
// 페이지 로드 시 저장된 사용자 정보 확인
window.addEventListener('load', () => {
const user = localStorage.getItem('user');
if (!user) {
window.location.href = '/login';
}
});
</script>
</body>
</html>
```
## 5. 서버 API 구현 예시
### 5.1 위치 정보 수신 API (Node.js/Express)
```javascript
// routes/location.js
const express = require('express');
const router = express.Router();
// 위치 정보 수신 엔드포인트
router.post('/api/location', async (req, res) => {
try {
const {
userId,
userName,
userEmail,
location,
deviceInfo
} = req.body;
// 데이터 검증
if (!userId || !location || !location.latitude || !location.longitude) {
return res.status(400).json({
success: false,
message: '필수 데이터가 누락되었습니다.'
});
}
// 데이터베이스에 저장
const locationRecord = await db.locations.create({
user_id: userId,
user_name: userName,
user_email: userEmail,
latitude: location.latitude,
longitude: location.longitude,
accuracy: location.accuracy,
altitude: location.altitude,
heading: location.heading,
speed: location.speed,
platform: deviceInfo.platform,
platform_version: deviceInfo.version,
timestamp: new Date(location.timestamp),
created_at: new Date()
});
console.log(`위치 업데이트: ${userName} (${userId}) - ${location.latitude}, ${location.longitude}`);
res.json({
success: true,
message: '위치 정보가 저장되었습니다.',
id: locationRecord.id
});
} catch (error) {
console.error('위치 저장 오류:', error);
res.status(500).json({
success: false,
message: '서버 오류가 발생했습니다.'
});
}
});
module.exports = router;
```
## 6. 주의사항
1. **보안**: JWT 토큰은 안전하게 저장하고 전송해야 합니다.
2. **배터리**: 위치 추적은 배터리를 많이 소모하므로 필요할 때만 활성화하세요.
3. **권한**: 사용자에게 위치 권한의 필요성을 명확히 설명하세요.
4. **오류 처리**: 네트워크 오류나 권한 거부 상황을 적절히 처리하세요.
5. **Fallback**: 웹 브라우저에서도 작동할 수 있도록 fallback 로직을 구현하세요.
## 7. 디버깅
### 7.1 웹뷰 디버깅 (Android)
```bash
# Chrome에서 chrome://inspect 접속
# 연결된 디바이스의 웹뷰 선택
```
### 7.2 웹뷰 디버깅 (iOS)
```
Safari > 개발 > [디바이스 이름] > [앱 이름]
```
### 7.3 로그 확인
```javascript
// 콘솔 로그는 네이티브 디버거에서 확인 가능
console.log('디버그 메시지');
console.error('오류 메시지');
```