스크롤 이벤트
일부 로직이 로컬 스토리지를 사용하는 경우, 메인 스레드를 차단할 우려가 있어 requestIdleCallback을 통해 브라우저가 유휴 상태일 때만 실행되도록 고려하였으나, 현재 실질적으로 사용하고 있는 로직은 간단한 연산 후 DOM에 스타일만 반영하는 수준이므로 쓰로틀 + 디바운싱 구조만으로도 충분하다고 판단하였다.
오히려 requestIdleCallback을 사용하면 불필요한 지연이 발생할 수 있다. 메인 스레드를 막을 정도로 무거운 작업이 추가된다면 그때 다시 고려해야겠다. 현재로서는 성능보다 UX 측면에서 지연으로 인한 부작용이 생길 가능성이 다분하다.
*Progress Bar가 실질적으로 사용자 경험 향상에 도움이 되지 않는다면, 완전 삭제도 고려하고 있다. 스크롤 이벤트 자체가 발생시키는 비용이 적지 않기 때문이다.
불필요한 분기 및 순회
isManuallySaved라는 boolean으로 분기하여 restoreScrollPositionFromLocal() vs restoreScrollPositionFromSync()로 분리된 복원 로직을 갖지 않고, 하나의 로직으로 복원을 통일한다. boolean으로 분기하고 필터링하는 것 자체가 단일 연산으로 보면 가볍지만, 실제 사용 환경에서는 성능 병목 포인트가 될 가능성이 다분하다.
현재 리스트를 사용하고 있는 것이 주요 원인이겠으나, 현재로서는 아래와 같은 부정적 흐름이 예상된다.
매뉴얼 세이브된 항목만 필터링한다. O(n) 순회 필터링이 복원 시마다 발생한다(사실상 load 이벤트마다). 그 이후에 복원 로직이 분기 처리되며 로직이 중복되고 렌더링 복잡도가 증가한다. 전체 아이템 리스트가 많아질 경우, 필터링 + 분기 + 저장소 접근이 겹치며 퍼포먼스에 영향을 준다. content script나 popup에서 UI 렌더링까지 붙는다면, 불필요한 로직 분기 자체가 브라우저 스레드 점유 증가 요인이 될 수 있다.
실제 성능 최적화는 불필요한 분기/순회 제거부터 시작한다. 즉, 복원은 local에 캐싱된 것을 기준으로 통일하고, sync는 동기화 + 백업만을 담당하도록 한다. boolean 분기도, restore 로직 분기도 걷어냄으로써 훨씬 간단한 흐름을 확보할 수 있다.
다만, 이를 적용하려면 대대적인 구조 변경이 필요하다. 현재는 popup에서 버튼 클릭 시 sync에 저장하고 갱신하도록 설정되어있으나 beforeunload 이벤트로 sync 데이터를 갱신시키려면 백그라운드 스크립트로 의존성을 분리해야 한다. popup은 일시적이고 상태를 유지하지 않기 때문이다. 필요할 때마다 백그라운드에서 비동기적으로 처리하도록 하여 더 안정적이고 직관적이게 변경한다. popup에서는 단순히 local 데이터를 읽는다.
병렬 처리
현재 구조에서는 병렬 처리가 이루어지지 않고 있다. 병렬 처리는 성능을 크게 향상시킬 수 있지만, 그만큼 구현 복잡도가 증가하고, 동기화와 같은 문제를 잘 처리해야 하므로 신중하게 접근해야 한다.
병렬 처리가 주는 이점은 두 가지다. 여러 작업을 동시에 처리할 수 있기 때문에 처리 속도가 빨라져 전체적인 UX가 개선될 수 있다. 여러 페이지 데이터를 한 번에 동기화하거나 복원하는 작업을 동시에 할 수 있다. 또한, 여러 작업이 동시에 진행되므로 CPU 코어를 효율적으로 분배하여 리소스를 더 잘 활용할 수 있다.
하지만 병렬 처리 시 공유 데이터에 대한 동기화 문제를 해결해야 한다. 여러 쓰레드나 프로세스가 동시에 데이터를 수정하려고 하면 race condition이 발생할 수 있다. 또한 많은 병렬 처리로 인해 메모리 사용량이 증가할 수 있고, 과한 병렬 처리는 메모리 오버플로우를 일으킬 수 있다. 오류가 발생했을 때 문제를 추적하는 것이 더 복잡해지기 때문에 디버깅하기 어려워지는 것은 덤이다.
크롬 익스텐션에서는 백그라운드 스크립트에서 이런 처리를 하게 된다. UI는 메인 스레드에서 처리하고, 비즈니스 로직은 Web Worker에서 처리하도록 분리할 수 있다. 또한 Promise.all을 사용해 여러 비동기 작업을 병렬로 처리할 수 있는데 이 방법은 특히 API 요청이나 데이터 처리에 유용하다고 한다.
그러나 현재 Where-Was-I는 비교적 가볍고 단순한 구조를 이룬다. 대부분 비동기 I/O이지만, 한번에 다량의 연산을 하거나 무거운 API 요청이 발생하는 것은 아니기 때문에 병렬 처리까지 필요해 보이진 않는다는 판단이다.
*저장/복원 타이밍도 유저 행동에 기반하기 때문에 부하가 클 수 없다. 사용자 경험상 프리징이 발생하는 경우 다시 고려하자.
'성장 과정 > 개인 프로젝트' 카테고리의 다른 글
[업데이트] 세이브 취소/타이틀 축약/잔여 용량 체크/기억 잔존율 시각화 (0) | 2025.04.24 |
---|---|
배열 순회 문제와 팝업 진행률 이슈 (1) | 2025.04.23 |
WWI: Where-Was-I? (1) | 2025.04.22 |
깃 히스토리와 짧은 회고 (0) | 2025.04.21 |
실수를 통해 성장하는 개발자가 되자 (rebase? cherry-pick?) (0) | 2025.04.20 |