멈췄던 캡스톤 프로젝트 진행을 다시 가속시키기
지난번 팀원과 만나 나간 진도는 CSS 기본 레이아웃 틀 만들고 센서 데이터로 상태 변화시키는 것이었고 어제는 상태 변화 값을 백엔드에서 받아 프엔 화면에 보여주는 것까지 마친 상태입니다
그리고 바뀐 점이 하나 있다면 수광부, 발광부가 있었던 센서 모듈을 단일 모듈로 바꿨습니다 버스 설치하는 데 어려울 것 같기 때문입니다
private final Map<Long, Map<String, Object>> cache = new ConcurrentHashMap<>();
public void updateLocation(Long busId, Map<String, Object> location) {
System.out.println("busId=" + busId + " data=" + location);
cache.put(busId, location);
}
public Integer getCount(Long busId) {
Map<String, Object> location = cache.get(busId);
if (location == null) return null;
Object val = location.get("ppl");
if (val == null) return null;
// 숫자가 Double/String/Integer 어떤 타입이든 안전하게 변환
if (val instanceof Number) {
return ((Number) val).intValue();
} else {
return Integer.parseInt(val.toString());
}
}
사실 챗지피티가 거의 다 해준 거라 설명 적기가 뭐 한데 ㅎㅎ ;; 그래도 이해한 대로 작성을 해보자면 우선 캐시를 사용한 이유는 실시간 데이터는 먼저 온 데이터를 뒤에 온 데이터가 덮는 구조입니다 지금 받고자 하는 데이터는 인원수를 기반으로 한 혼잡도 상태 값*이기 때문에 더더욱 빈에 접근하거나 DB를 쓸 필요 없이 가장 최근에 받은 데이터(현재 상태)가 뭔지만 알면 되는 거죠 따라서 캐시를 사용한 것입니다
*혼잡도 상태 값
- 0: 여유
- 1: 보통
- 2: 혼잡
updateLocation의 Location은 위치를 의미하는 게 아니라 저희의 Map<> 저장소 이름입니다 location 저장소에 esp32로부터 받은 busId를 put해 주는 것입니다 이는 버스가 2대 이상 운행 중이어도 busId로 분별할 수 있는 기반이 됩니다
getCount는 이름 그대로 location에서 busId를 기준으로 ppl(people) 값을 가져옵니다 여기서 챗지피티의 큰 도움을 받았는데 ppl 값이 Number 타입인 경우와 그러지 않은 경우(문자열인 경우)로 나눠 포맷을 하는 코드를 작성해 줬습니다 이로써 0, 1, 2의 상태 값이 문자열로 들어오든 정수나 소수로 들어오든 안전하게 들어오게 됐습니다 (왜 instanceof를 생각 못 한 걸까~~)
// BusService로부터 cofig의 count 값을 받아 fe로 리턴
@GetMapping("/bus/{id}/count")
public Map<String, Object> getBusCount(@PathVariable("id") Long id){ // PathVariable: url 경로의 변수 값을 메서드 파라미터로 바인딩
Map<String, Object> result = new HashMap<>();
result.put("count", busService.getCount(id));
return result; // json 리턴
}
이제 컨트롤러에서 프론트엔드에 응답을 합니다 bus/{id}/count로 result 변수에 count의 이름으로 혼잡도 상태를 담아 보냅니다 (변수명을 바꾸고 싶었지만 이전에 명명해둔 걸 바꾸기에는 헷갈릴 것 같아 그대로 count를 쓰게 됨)
PathVariable은 이름 그대로 경로에 있는 변수 값을 읽는 어노테이션입니다 여기서는 {id}를 읽습니다 이렇게 esp32에서 전송하고 있는 payload를 spring에서 받아 react로 해당 payload를 전송해 주는 코드를 작성했습니다 그리고 결과를 확인해 보았더니

아무래도 그럴 수 밖에 없었던 건 이론상 버스 id로 해당 버스 혼잡도를 판단하는 거라 당연히 bus/{id}/count라고 작성을 해줬는데 esp32에서 busId를 전송하고 있지 않았습니다 하 하하 하
하드웨어마다 기기명이 있듯이 버스 또한 busId를 하드웨어에 작성해 주면 되는 것을 이제야 알게 되어 아두이노에서 gps 전송 때와 센서 전송 때 busId를 함께 전송하도록 수정했습니다
doc["busId"] = BUS_ID; // 버스 ID 추가
doc["lat"] = gps.location.lat();
doc["lng"] = gps.location.lng();

busId가 잘 수신되고 있음을 확인할 수 있습니다
Map<String, Object> pplData = objectMapper.readValue(payload, Map.class);
Map<String, Object> location = busLocation.getLocation();
// count를 Map에 ppl이라는 이름으로 저장
location.put("ppl", pplData.get("count"));
location.put("busId", pplData.get("busId"));
Long busId = ((Number) pplData.get("busId")).longValue();
busService.updateLocation(busId, location);
수신한 데이터를 Map<>에 저장해 줍니다 기존에는 ppl만 처리 중이었기 때문에 busId 값을 추가해 줬습니다 중요한 건 Service에 작성한 updateLocation에 busId를 넘겨 주는 것입니다 그럼 위에 작성한 코드가 busId를 읽어 버스의 {id}를 기준으로 해당 버스의 혼잡도 상태(count)를 읽어옵니다

0 상태임을 확인할 수 있었습니다

혼잡도가 1인 경우와 2인 경우가 잘 분별되어 오는 것을 확인할 수 있었습니다
const [state, setState] = useState([]);
useEffect(() => {
axios.get("http://localhost:8080/bus/1/count")
.then(res => {
setState(res.data.count);
})
.catch(err => console.error(err));
}, []);
현재 esp32는 busId 1번이기 때문에 test로 1번을 지정해 주었습니다 useState로 count 값을 state 변수에 업데이트 하며 state가 0, 1, 2일 때 css에 변화를 줍니다 작아서 잘 안 보이지만 8분 옆 css가 여유(초록) → 보통(노랑) → 혼잡(빨강)으로 바뀌고 있습니다

인원수가 늘어나는 건 1번 2번 순서로 직접 감지시키고 있었기 때문입니다 ㅎ
앞으로의 과제
1. Refresh.js : n초마다 데이터 업데이트
2. Root.js : 사용자 session에 따른 노선A, 노선B 노출 우선 순위 변경
3. Station.js : Root.js에서 Tab 한 노선에 맞는 정보 출력