-
Notifications
You must be signed in to change notification settings - Fork 2
기능 테스트 (socket)
Hyein Jeong edited this page Nov 5, 2024
·
1 revision
- Socket 사용하여 테스트
- 위치의 제한 사항 → 로컬호스트로 테스트 중이다보니 여러명의 위치를 확인할 수는 없었다
- 접속중인 사용자 수를 나타내는 코드를 구현하여 보이게 만들었다
- 창을 3개 열고 구현한 결과
## 1. 서버 코드 수정
```jsx
// server.js
const users = {}; // 사용자 위치를 저장하는 객체
io.on('connection', (socket) => {
console.log('A user connected:', socket.id);
// 접속한 사용자 수를 모든 클라이언트에게 브로드캐스트
io.emit('userCount', Object.keys(users).length + 1);
// 사용자 위치 수신 및 저장
socket.on('sendLocation', (data) => {
users[socket.id] = data;
io.emit('updateLocations', users); // 모든 클라이언트에 위치 정보 전송
});
// 사용자 연결 해제 시 위치 정보 제거
socket.on('disconnect', () => {
delete users[socket.id];
io.emit('updateLocations', users); // 모든 클라이언트에 업데이트된 위치 정보 전송
io.emit('userCount', Object.keys(users).length); // 접속한 사용자 수 전송
console.log('User disconnected:', socket.id);
});
});
server.listen(3001, () => {
console.log('Server running on http://localhost:3001');
});
```
- `userCount` 이벤트를 추가하여 현재 접속 중인 사용자 수를 모든 클라이언트에 전송합니다.
- 사용자가 연결되거나 연결을 끊을 때마다 `Object.keys(users).length`를 통해 현재 접속 중인 인원 수를 계산합니다.
---
## 2. 클라이언트 코드 수정
클라이언트에서는 `userCount`와 `updateLocations` 이벤트를 통해 접속 인원 수와 위치 정보를 받아 화면에 표시하고, 마커를 동적으로 렌더링합니다.
```jsx
// App.js
import React, { useState, useEffect } from 'react';
import { MapContainer, TileLayer, Marker, Popup } from 'react-leaflet';
import 'leaflet/dist/leaflet.css';
import io from 'socket.io-client';
const socket = io.connect('http://localhost:3001');
function App() {
const [location, setLocation] = useState({ latitude: null, longitude: null });
const [otherLocations, setOtherLocations] = useState({});
const [userCount, setUserCount] = useState(0); // 접속 인원 수 상태 추가
// 현재 위치 추적 및 서버 전송
useEffect(() => {
if (navigator.geolocation) {
navigator.geolocation.watchPosition(
(position) => {
const { latitude, longitude } = position.coords;
setLocation({ latitude, longitude });
socket.emit('sendLocation', { latitude, longitude });
},
(error) => console.error(error),
{ enableHighAccuracy: true, maximumAge: 10000, timeout: 5000 }
);
}
}, []);
// 서버로부터 업데이트된 사용자 위치 수신
useEffect(() => {
socket.on('updateLocations', (users) => {
setOtherLocations(users);
});
// 접속 인원 수를 서버로부터 수신
socket.on('userCount', (count) => {
setUserCount(count);
});
return () => {
socket.off('updateLocations');
socket.off('userCount');
};
}, []);
return (
<div>
<h1>실시간 위치 공유</h1>
<p>접속 중인 사용자 수: {userCount}명</p> {/* 사용자 수 표시 */}
<MapContainer
center={[location.latitude || 0, location.longitude || 0]}
zoom={13}
style={{ height: '80vh', width: '100%' }}
>
<TileLayer
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
{/* 내 위치 표시 */}
{location.latitude && location.longitude && (
<Marker position={[location.latitude, location.longitude]}>
<Popup>나의 위치</Popup>
</Marker>
)}
{/* 다른 사용자의 위치 표시 */}
{Object.keys(otherLocations).map((key) => (
key !== socket.id && otherLocations[key].latitude && (
<Marker
key={key}
position={[otherLocations[key].latitude, otherLocations[key].longitude]}
>
<Popup>상대방의 위치</Popup>
</Marker>
)
))}
</MapContainer>
</div>
);
}
export default App;
```
### 코드 설명
- **`userCount` 상태**: 서버에서 `userCount` 이벤트를 통해 접속 중인 사용자 수를 받아서 `userCount` 상태에 저장합니다.
- **인원 수 표시**: 접속 중인 사용자 수를 `<p>접속 중인 사용자 수: {userCount}명</p>` 부분에 표시하여 실시간으로 보여줍니다.
- **마커 표시**: `otherLocations`에 저장된 다른 사용자 위치 정보에 따라 각 사용자 마커를 지도에 동적으로 표시합니다.
실시간 위치 파악 : navigator.geolocation.watchPosition
### 작동 과정 요약
1. **위치 감지 및 업데이트**: `watchPosition`은 사용자가 이동할 때마다 `position` 객체를 통해 새로운 `latitude`와 `longitude` 값을 제공합니다.
2. **위치 정보 서버 전송**: 새로운 위치 정보가 서버에 `sendLocation` 이벤트로 전송되고, 서버는 해당 사용자의 위치를 업데이트한 후 모든 클라이언트에 `updateLocations` 이벤트로 새로운 위치 정보를 브로드캐스트합니다.
3. **클라이언트 지도 업데이트**: 각 클라이언트는 `updateLocations` 이벤트를 수신하여, 지도에 표시된 마커 위치를 최신으로 유지합니다. 이로 인해 실시간으로 사용자의 위치가 지도 위에서 변경됩니다.
### 주의사항
- **watchPosition 옵션**: `enableHighAccuracy`와 같은 옵션으로 정확도를 높일 수 있지만, 배터리 소모량이 커질 수 있으므로 주의가 필요합니다.
- **빈번한 위치 업데이트**: 이동 속도와 설정에 따라 위치 업데이트가 매우 빈번하게 일어날 수 있으므로, 서버와 클라이언트에서의 성능을 고려해 필요에 따라 위치 변경을 제한하는 로직을 추가할 수 있습니다.
- Socket을 사용하지 않는 경우
- 다음과 같이 2개를 접속할 경우 2개의 마크가 표시되는 것을 확인할 수 있습니다
Socket을 사용하지 않고도 **HTTP 요청**을 주기적으로 보내어 위치 정보를 업데이트하는 방법으로 구현할 수 있습니다. 이 경우, 클라이언트에서 **정기적인 폴링(polling)**을 통해 서버에 위치 정보를 업데이트하고, 다른 사용자들의 위치 정보를 받아오는 방식을 사용할 수 있습니다.
이 방법은 웹소켓보다 실시간성은 떨어지지만, 서버와의 연결이 간단하고 사용하기 쉬운 장점이 있습니다.
아래는 HTTP 폴링을 사용하여 위치를 업데이트하고, 여러 사용자의 위치를 지도에 표시하는 방법입니다.
---
## 1. 서버 코드 (Express 사용)
간단한 REST API 서버를 만들고, 클라이언트에서 정기적으로 위치 정보를 업데이트하도록 구성합니다.
```jsx
javascript
코드 복사
// server.js
const express = require('express');
const cors = require('cors');
const app = express();
app.use(cors());
app.use(express.json());
let users = {}; // 사용자 위치 정보를 저장할 객체
// 위치 정보 업데이트 API
app.post('/update-location', (req, res) => {
const { userId, latitude, longitude } = req.body;
users[userId] = { latitude, longitude }; // 사용자 위치 정보 업데이트
res.sendStatus(200);
});
// 모든 사용자 위치 가져오기 API
app.get('/locations', (req, res) => {
res.json(users); // 모든 사용자 위치 정보 반환
});
// 서버 실행
app.listen(3001, () => {
console.log('Server running on http://localhost:3001');
});
```
- **`/update-location` 엔드포인트**: 클라이언트가 자신의 위치를 업데이트할 때 POST 요청을 보내는 API입니다.
- **`/locations` 엔드포인트**: 모든 사용자의 위치 정보를 JSON 형식으로 반환하는 API입니다.
---
## 2. 클라이언트 코드
클라이언트에서는 `navigator.geolocation.watchPosition`을 사용하여 정기적으로 자신의 위치를 서버에 전송하고, 일정 간격으로 서버에서 모든 사용자의 위치 정보를 가져옵니다.
```jsx
javascript
코드 복사
// App.js
import React, { useState, useEffect } from 'react';
import { MapContainer, TileLayer, Marker, Popup } from 'react-leaflet';
import 'leaflet/dist/leaflet.css';
import axios from 'axios';
import { v4 as uuidv4 } from 'uuid';
function App() {
const [location, setLocation] = useState({ latitude: null, longitude: null });
const [otherLocations, setOtherLocations] = useState({});
const [userId] = useState(uuidv4()); // 고유 사용자 ID 생성
// 현재 위치 추적 및 서버에 전송
useEffect(() => {
if (navigator.geolocation) {
navigator.geolocation.watchPosition(
async (position) => {
const { latitude, longitude } = position.coords;
setLocation({ latitude, longitude });
// 서버에 위치 정보 업데이트
await axios.post('http://localhost:3001/update-location', {
userId,
latitude,
longitude,
});
},
(error) => console.error(error),
{ enableHighAccuracy: true, maximumAge: 10000, timeout: 5000 }
);
}
}, [userId]);
// 일정 간격으로 모든 사용자 위치 정보 가져오기
useEffect(() => {
const fetchLocations = async () => {
try {
const response = await axios.get('http://localhost:3001/locations');
setOtherLocations(response.data); // 모든 사용자 위치 정보 업데이트
} catch (error) {
console.error('Error fetching locations:', error);
}
};
// 3초마다 위치 정보 가져오기
const interval = setInterval(fetchLocations, 3000);
return () => clearInterval(interval);
}, []);
return (
<div>
<h1>실시간 위치 공유</h1>
<MapContainer
center={[location.latitude || 0, location.longitude || 0]}
zoom={13}
style={{ height: '80vh', width: '100%' }}
>
<TileLayer
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
{/* 내 위치 표시 */}
{location.latitude && location.longitude && (
<Marker position={[location.latitude, location.longitude]}>
<Popup>나의 위치</Popup>
</Marker>
)}
{/* 다른 사용자의 위치 표시 */}
{Object.entries(otherLocations).map(([key, loc]) => (
key !== userId && loc.latitude && loc.longitude && (
<Marker
key={key}
position={[loc.latitude, loc.longitude]}
>
<Popup>상대방의 위치</Popup>
</Marker>
)
))}
</MapContainer>
</div>
);
}
export default App;
```
### 코드 설명
- **고유 ID 생성**: `uuid` 라이브러리를 사용하여 각 사용자를 식별할 고유 ID를 생성합니다.
- **위치 업데이트**: `watchPosition` 메서드로 사용자의 위치가 변경될 때마다 서버로 위치를 POST 요청으로 전송합니다.
- **폴링으로 위치 정보 가져오기**: `setInterval`을 사용하여 3초마다 모든 사용자 위치 정보를 서버에서 가져와 업데이트합니다.
- **마커 표시**: 내 위치는 하나의 마커로, 나머지 사용자의 위치는 서버에서 가져온 위치 정보를 바탕으로 지도에 표시됩니다.
---
### 폴링 방식의 한계
- **실시간성**: 폴링 주기에 따라 서버의 데이터가 업데이트되므로, 웹소켓보다 위치 업데이트에 약간의 지연이 발생할 수 있습니다.
- **서버 부하**: 많은 사용자가 접속 중인 경우 폴링 요청이 많아져 서버에 부담이 될 수 있습니다.
위와 같은 방식으로 소켓 없이도 실시간에 가까운 위치 공유 기능을 구현할 수 있습니다.