페이지 최초 로드시 받아온 csv 파일의 통계 데이터를 버튼 클릭 등의 특정 이벤트 발생시 사용하기 위해서는 어떤 로직이 가장 효율적일까?
예시는 2000개의 배열이지만 어떤 통계 데이터냐에 따라 개수는 계속해서 늘어날 수 있다.
따라서 최적의 방식을 생각할 필요가 있다고 생각했다.
요구사항 정리
페이지 로드시 받아온 데이터를 미리보기 버튼을 클릭시 원하는 형태의 데이터로 매핑하여 세슘js 지도 위에 표시하여야한다.
이를 위해 최초로 받아온 데이터를 따로 저장해둘 필요가 있다.
이때 데이터를 어디에 저장해두는 것이 가장 효율적일까? 통계 데이터이므로 데이터 양이 커질 수 있어 메모리나 시간복잡도가 가장 낮은 방법에 대해 고민했다.
데이터 저장 방식
떠올린 저장 방식은 크게 전역 객체를 만들어서 저장하는 방식과 웹 스토리지를 저장하는 방식을 떠올렸다.(현재 제이쿼리 환경에서 개발을 진행한다.)
이 중에서 전역 객체를 사용하는 방식을 채택했다.
왜냐하면 페이지 최초 로드시 데이터를 불러와 저장할 것이기에 새로고침을 하더라도 데이터를 다시 저장하기 때문에 새로고침시 초기화되어도 상관없었기 때문이다.
왜 웹 스토리지보다 전역 객체 방식이 유리한지?
전역 객체를 사용했을 때 장점은 아래와 같다.
- 접근 속도 : O(1) 수준의 빠른 메모리 접근이 가능, JSON 파싱도 없음
- 복잡도 최소화 : 데이터를 따로 불러오거나 직렬화/역직렬화하지 않아도 됨
- 유연한 조작 : 필터링, 정렬, 검색 등 JS에서 바로 사용 가능(filter, map, find 등)
- 유지보수성 : 함수형 유틸리티나 상태 관리 객체로 구조화 가능
그럼 반대로 왜 웹스토리지가 부적합할까?
- 저장 전에 직렬화가 필요 : 대용량에서는 속도 저하가 있을 수 있다.(JSON.stringofy())
- 꺼낼 때마다 파싱 : 사용할 때 파싱해주는 작업이 들어가면 성능적으로 손해가 있다.(JSON.parse())
- 저장 크기 제한 : 로컬스토리지가 보통 5MB 이내이므로 대용량이면 넘칠 수 있다.
- 민감한 정보는 웹스토리지 이용 불가 : 클라이언트의 디스크에 저장한다는거부터 보안에 취약하다.
만약 새로고침시 데이터 유지가 필요하거나 페이지 이동 간 데이터 공유가 필요하다면 웹 스토리지를 부분적으로라도 이용할 수 있겠지만 현재 요구사항에서는 필요없을거 같으니 그냥 전역 객체를 만들어 필요할 때 꺼내 쓰는 방식으로 구현하고자한다.
로직 구현 시작
지도 위에 원하는 형태의 데이터를 뿌려주기 위해서 아래와 같이 구현했다.
먼저 CSV 통계 정보와 이에 해당하는 위치정보를 각각 불러와준 후, 통계 정보에서 유의미한 값(지도에 표시해야하는 값)을 필터링하여 위치 정보와 결합해 하나의 객체로 만들어준다.
// 통계 정보 전역 객체
const global_csv_manager = {
load_data: [], // 데이터 최초 로드시 불러온 데이터
location_data: [], // 위치정보 데이터
load_set(load_data) {
this.load_data = load_data;
},
load_get() {
return this.load_data;
},
location_set(location_data) {
this.location_data = location_data;
},
location_get() {
return this.location_data;
},
// 주요 맵핑필드, 정보제공필드를 위치 정보와 매핑하여 미리보기에 필요한 데이터로 매핑
mapping_preview_data() {
let mapping_data = location_data; // 위치정보 데이터 + 미리보기에 필요한 데이터 추가
//TODO - 위치 정보에 주요 맵핑필드 데이터 추가
//TODO - 추가한 데이터에 정보제공필드 데이터 추가
return mapping_data;
}
};
// 미리보기를 위한 데이터 매핑
if (data.item_kind == 'mark') {
global_csv_manager.set(result.csv_map);
}
// 위치정보 가져오기
async function getPoint(no) {
try {
const response = await fetch('../road_admin/location/point?no=' + no);
const data = await response.json();
console.log("point data", data);
if (data.status === 500) {
alert("해당 데이터는 마크 아이콘이 없는 데이터입니다.");
return;
}
global_csv_manager.location_set(data); // 전역 객체에 위치 정보 저장
return data;
} catch (e) {
console.error("Error fetching point data:", e);
}
}
기본적인 구조는 위처럼 잡았다. 이제 가져온 location_data에 지도 위에 표시해야하는 데이터를 매핑해주는 작업을 해주면 된다.
먼저 사용자가 지정한 지도 위에 표시되어야하는 데이터가 어떤 것인지를 가져온다.
// 주요 맵핑필드
const target_data_name = $("#name_field").val();
const target_tel_data = $("#tel_field").val();
const target_time_data = $("#open_time").val();
이제 위의 속성과 같은 데이터를 매핑한다.
// load_data를 map을 변환 -> 탐색시 시간 복잡도 감소(O(n^2) -> O(n))
const load_data_map = {};
load_data.forEach((item) => {
const name_key = item[target_data_name];
if (name_key) {
load_data_map[name_key] = item;
}
});
const mapping_data = location_data.map((item) => {
const match_key = item.name_data;
const matched_load_item = load_data_map[match_key];
if (matched_load_item) {
const metched_feild = {
tel: matched_load_item[target_tel_data],
time: matched_load_item[target_time_data],
}
target_selected_values.forEach((target) => {
item[target] = matched_load_item[target];
});
return {
...item,
...metched_feild,
};
}
}).filter(item => item !== undefined);;;
console.log("mapping_data", mapping_data);
우선 좌표가 있는 데이터와 이에 해당하는 csv 파일의 데이터에서 사용자가 원하는 데이터를 가져와 매핑하여 하나의 객체로 만들었다.
이때 csv 파일의 데이터를 탐색하는 부분은 map의 형태인 key value 형태로 수정하여 탐색에 필요한 시간 복잡도를 줄였다.(csv는 대용량일 수 있는 가능성이 있으므로 탐색 시간을 최소화하는데 집중했다.
만약 find()를 통해 찾았다면 O(n*m)의 시간복잡도가 나왔을 것이다.
이제 매핑된 데이터에서 좌표 값을 활용해 세슘js 위에 표시하면 된다.
정리
- 페이지 로드 시에 불러온 통계 데이터(여기서는 지도 맵핑 데이터)를 전역 객체를 활용하여 특정 이벤트에 사용
- 이때 전역 객체 내부에서 기존의 데이터를 원하는 형태로 매핑해주는 작업 진행
- 객체 하나에서 관련된 작업들을 모두 진행하므로 다른 코드에 영향을 주지 않도록 설계
'트러블 슈팅 > 트러블 슈팅' 카테고리의 다른 글
canvas에서 echart의 pie 차트가 흐리게 보이는 현상 해결 (0) | 2025.06.17 |
---|---|
class별 필터링 검색 기능 구현 (0) | 2025.06.17 |
formData 전송시 유효성 검사 로직 구현 (1) | 2025.06.17 |
Flash of Unstyled Content(FOUC) 현상 해결 (0) | 2025.06.17 |
Next.js에서 유니티 빌드 파일 접근 수정 (2) | 2024.12.09 |