# 문제
이미지 태거에서는 현재 앱을 실행하면 자동으로 해당 어플의 모든 캡쳐사진들을 서버로 전송하도록 되어있다.
가지고 있는 공기계로 테스트하면서 개발할 때는 문제 없었는데, 실제 기기로 테스트하자 무려 3000장이 넘는 사진들에 timeout으로 전송이 실패해버렸다..
사실 앱을 실행할 때 마다 보냈던 이미지를 또 보내는 게 비효율적이라고 계속 생각 중이었는데, 일단 전체적인 기능 완성 때문에 그냥 뒀었다.
이제는 그냥 이대로 둘 수 없어서 보냈던 이미지는 다시 서버로 보내지 않도록 하는 로직을 추가하려 한다!
사실 지금까지 시도를 안하고 있었던 이유가 왠지 캐시를 사용해서 이미지를 어떻게 해야만 할 것 같아서, 복잡하지 않을까 하는 생각에 일단 급한 것 부터 처리하려고 미룬 것이었다.
그런데 막상 하려고 생각하니 별로 어렵지 않을 것 같다.
# 해결시도
1. async storage에 이미지 저장
일단 캐시를 사용하지 않고, async storage에 저장할 것이다.
이미지가 몇천장을 넘어가는데 이걸 프론트에 저장하는게 무리지 않을까 싶었지만,
이미지를 식별할 수 있는 name만 뽑아서 저장하면 될 것 같다.
안드로이드에서 캡쳐 이미지의 name은 아래와 같은 형식이다.
Screenshot_20230529-134026_Instagram.jpg
위와 같은 string을 저장할 때 몇바이트가 필요할지 좀 찾아보다가 평소 익숙한 utf에 대해 더 알게 되었다.
.
unicode란?
컴퓨터와 사람이 소통하기 위해서는 사람의 언어를 컴퓨터의 언어인 bit 숫자로 변환해야 한다.
unicode는 이렇게 전 세계의 언어를 전부 index에 매핑한 것을 말한다.
utf란?
utf는 위에서 말한 것처럼 사람의 언어를 컴퓨터의 언어로 변경하기 위한 문자열 인코딩 규칙이다.
utf-8로 인코딩 할 경우, 1개의 index 당 8bit를 사용한다
한글은 3byte, 영어는 1byte, 이모티콘 같은 문자는 4byte가 필요하다.
(한글이 3byte인 이유는 모든 한글이 각각 bit로 표현된 숫자에 매핑되는데, 이게 3byte이기 때문이다.)
utf-16으로 인코딩 할 경우, 1개의 index 당 16bit를 사용한다.
한글은 2byte, 영어는 2byte가 필요하다.
따라서 영어권에서는 utf-8을 사용하는 게, 한국어는 utf-16을 사용하는 게 더 유리하다.
js에서는 기본적으로 utf-16를 사용하므로 한 문자당 2byte가 사용된다.
영어와 숫자는 한 글자 당 2bytes이고, 총 40자 이므로 이미지 한 개 당 80bytes의 데이터를 저장해야 한다.
만약 이미지가 3000장이라면 240kb가 필요하다.
지금 가지고 있는 스크린샷 파일 중 가벼운 파일이 360kb 정도 하므로, 사진 한장에 못미치는 크기이다!
2. 저장된 배열 탐색 (set)
그리고 다음으로 저장되어 있는 여러 문자열들을 전부 보며 해당 이미지가 이미 저장되어있는지 확인해야 한다.
가장 먼저 떠오른 방법은 기본적인 Array.prototype.includes()을 사용하는 것이었는데, 이렇게 하면 배열에 있는 모든 문자열을 순회하기 때문에 너무 비효율 적이다.
(사진이 3000장 있는 사람이 100장의 사진을 추가해서 총 3100장이 되었을 때, 현재 3000장은 이미 저장되어 있고, 나머지 100장을 찾아내기 위해서는 3000*3100번의 연산이 필요할 것이다.)
그래서 set을 이용하는 방법을 찾았다.
set자료형의 has()를 사용하면 array의 includes() 보다 효율적으로 값이 존재하는지 확인할 수 있다.
(# 참고 4 : 나 대신 다른 사람들이 직접 비교해본 내용이다.)
Set.prototype.has() - JavaScript | MDN (mozilla.org)
참고로 set을 사용하면 배열에 중복된 원소가 들어갈 수 없다. 나의 경우에는 어차피 사진은 전부 유일하므로 상관 없지만, 그렇지 않은 경우에는 당연히 set을 사용하면 안된다.
지금까지 한번도 이용해 본 적 없는 함수인데, 이런게 있다는게 알았으니 앞으로 잘 쓸 것 같다.
그렇게해서 set을 적용해보려고 했는데..
async-storage에 set을 저장할 수 없는 것 같다.
string 형태로만 저장이 가능하기 때문에 보통 object를 저장할 때는 JSON.stringify() 를 사용해야 하는데, set 자료형은 이렇게 하면 제대로 string으로 바뀌지 않고, '{}' 이렇게 빈 object만 출력됐다..
어디엔가 방법이 있는데 내가 못찾는건지 모르겠지만, 일단 set을 저장할 수 없으니 원래 계획대로 includes()를 사용하는 수 밖에 없다.
방법이 생각났다! 일단 array로 async-storage에 저장을 한 다음, 필요할때 array를 다시 set으로 형변환해서 사용하면 된다.
// 캡쳐사진 저장 여부 확인
const checkSavedImages = async images => {
const imageArr = await AsyncStorage.getItem('imageArr');
const savedImageSet = new Set(JSON.parse(imageArr));
console.log('old', savedImageSet);
console.time('checkImages');
const newImages = new Array();
images.forEach(image => {
console.log(savedImageSet.has(image.name));
if (!savedImageSet.has(image.name)) {
newImages.push(image);
}
});
console.timeEnd('checkImages');
return newImages;
};
이렇게 하면 이제 초반에만 로딩이 오래 걸리고, 다음부터는 아주 빠르게 앱에 진입할 수 있다.
작동 시간 측정
이미 보낸 사진들을 전부 확인하는데 어느정도 시간이 걸릴지 체크해 본 결과 아래와 같이 사진 55장을 확인할 때 기준 0,004초가 걸린다. 사진이 10배 더 늘어나서 5000장이 있다고 해면 0.4초 정도가 예상된다.
끝!
이제 다음 할 일은 사진이 너무 많을 경우에 timeout으로 연결이 끊기는 것을 막기 위해 사진을 50장 단위로 끊어서 보내는 작업을 해야할 것 같다.
일단 오늘은 여기까지..
# 참고
1)
JavaScript의 타입과 자료구조 - JavaScript | MDN (mozilla.org)
2)
ECMAScript® 2024 Language Specification (tc39.es)
3)
자바스크립트 문자열의 바이트수 계산 - 제타위키 (zetawiki.com)
4)
javascript - Is the Set.has() method O(1) and Array.indexOf O(n)? - Stack Overflow
'코딩일지' 카테고리의 다른 글
[2023-06-06] 이미지태거 오류 수정 (0) | 2023.06.06 |
---|---|
[2023-06-06] 이미지태거 리팩토링 - 크기가 큰 배열 나누어서 전송하기 (0) | 2023.06.06 |
[2023-06-01] 블록체인 해커톤 위한 near protocal 세팅 (window 환경) (0) | 2023.06.01 |
[2023-05-31] 이미지 태거 코드 리팩토링 (0) | 2023.05.31 |
[2023-05-28] 이미지 태거 디버깅 (0) | 2023.05.28 |