ShowYourTime 개발후기

2024.01.01
profile_imagew0nder
<link-preview url="https://showyourti.me" title="Show Your Time" target="_blank" image="https://www.showyourti.me/images/og.png"> </link-preview> # 시작하며 `fi-workers`에서는 2023년 하반기에 `ShowYourTime` 앱을 만들었습니다. `ShowYourTime` 은 사진에 타임스탬프를 찍어주는 앱입니다. 앱에 대한 더 자세한 스토리는 `fi-workers` 의 프로덕트 디자이너이신 `나나산` 님이 쓰신 글을 보시면 좋습니다. <link-preview url="https://www.nanasan.co.kr/showyourtime-app-story/" title="INTJ와 INTJ가 2주 만에 만든 Show Your Time 앱 제작기" target="_blank" image="https://www.nanasan.co.kr/static/096e1edb9550b472bf19c0bd199e9fe0/e5166/nanasan.jpg"> </link-preview> 어떤 프로젝트를 진행할 때, 저는 중요한 내적인 목표 하나를 설정합니다. 이는 제가 이론으로만 공부했던 내용을 실제 상황에 적용하여, 지식과 경험을 연결하는 데 도움이 됩니다. 이번 프로젝트에서의 목표는 "빠른 출시"였습니다. 일반적으로 사용자는 원하는 기능을 명확히 인지하지 못합니다. 제공된 기능이 자신의 원래 의도와 부합하는지 여부만을 파악할 수 있습니다. 간단한 컨셉 제시 후, 구체적인 사항이 확정되기 전에 프로토타입을 통해 사용자가 실제 원하는 방향을 찾을 수 있다고 생각합니다. 이번에는 개밥먹기와 비슷하게 제품의 메이커와 실제 사용자가 같았기 때문에, 프로토타입으로 컨셉이 구현된 상태를 기반으로 신속하게 피드백을 주고 받을 수 있었습니다. 이러한 장점은 "빠른 출시" 목표를 달성하는데 결정적인 역할을 했습니다. 이렇게 빠른 피드백 루프를 통해, 사용자의 필요와 기대를 정확히 파악하고, 그에 맞춰 제품을 빠르고 효율적으로 개발할 수 있었습니다. # 개발 과정에서의 고민 빠른 출시를 달성하기 위해 이번 프로젝트에서 가장 중요했던 요소는 개발 생산성이었습니다. 주로 주말에만 앱 개발과 배포를 진행하는 등 시간이 매우 부족했기 때문입니다. 이러한 시간적 제약으로 인해 생산성을 위해 몇 가지 후회가 남는 설계를 하기도 했습니다. 또한, 개발 시간의 부족으로 인해 사용자 경험(UX)과 성능 측면에서 어느 정도 타협을 할 수밖에 없었습니다. ## 사진에 타임스탬프 넣기 ![](/posts/1/assets/fe9af87f16c040ada0139646b8e73641.jpg) 이 프로젝트에서 가장 큰 도전은 사진에 타임스탬프를 추가하는 기능이었습니다. 사진에 시간 텍스트를 추가하기 위해 ImageMagic이나 다른 라이브러리의 사용을 고려하였으나, 스타일링에 제한이 많고 React Native에서 ImageMagic 설정에 시간이 소요될것 같았습니다. 이에 대한 해결책으로, [react-native-view-shot](https://github.com/gre/react-native-view-shot) 라이브러리를 사용하여 스타일링된 컴포넌트를 사진이나 카메라 영상 위에 배치하고, 이 상태로 스크린샷을 찍어 이미지를 생성하는 방법을 선택했습니다. 이 방식은 화면 크기에 따라 해상도가 결정되는 문제가 있었는데, 이를 해결하기 위해 이미지 사이즈와 동일한 View 컴포넌트를 생성하고 화면에 보이지 않는 곳에 배치했습니다. ```jsx { position: "absolute", top: -99999, left: -99999 } ``` 처음에는 고해상도 이미지가 좋다고 생각하여 크게 설정했으나, 결과물의 용량이 커진다는 사용자 의견을 받고 원본 사진 사이즈에 맞추어 스크린샷을 찍도록 변경했습니다. 그러나 스크린샷을 사용하는 방법은 UI 사이클에 영향을 받아 시점 문제로 인한 크래시나 잘못된 스크린샷이 찍히는 문제가 발생했습니다. 여러 미봉책으로 처리하긴 했지만, 이는 실제 이미지 파일 조작을 통해 개선되어야 할 부분으로, 적용이 된다면 UI를 그리는 시간을 절약하면서 성능 향상에도 도움이 될 것 같습니다. ## 앨범에서 사진 불러오기 ![](/posts/1/assets/fe9af87f16c040ada0139646b8e73642.png) 앨범에서 사진을 불러오는 과정은 단순하지 않았습니다. Android에서는 로컬 저장된 사진들이 신속하게 로드되었지만, iOS에서는 iCloud에 저장된 사진들을 네트워크를 통해 다운로드하는 데 시간이 오래 걸렸습니다. 이 과정에서 네트워크 비용이 발생했고, 인터넷 연결이 없으면 사진이 제대로 로드되지 않는 문제가 있었습니다. 또한, 사진 목록에서 가상스크롤을 사용해 실제 관리하는 컴포넌트의 개수를 최적화 하더라도, 원본 크기의 사진을 UI에 로드하면 메모리 문제가 발생해, 썸네일 크기로 사진을 축소하여 로드해야 했습니다. 이 썸네일 생성 과정은 CPU 연산을 많이 필요로 해 배터리 소모가 크게 되었으며, 썸네일을 로컬에 캐싱하면 저장 공간을 많이 차지하는 문제도 있었습니다. 인스타그램 같은 앱들이 자체적으로 구현한 PhotoPicker를 보며 그들이 해결한 이러한 문제점들을 직접 다뤄보고 싶은 생각이 들었습니다. 하지만, 비슷한 기능을 제공하는 다른 라이브러리를 분석하거나 여러 시도를 해볼 시간이 부족했습니다. 결국, 순수한 React Native로는 한계가 있음을 인지하고, 각 운영체제에서 제공하는 PhotoPicker를 사용하기로 결정했습니다. ## 프레임 스와이프 ![](/posts/1/assets/fe9af87f16c040ada0139646b8e73644.webp) UI 디자인에서 Opacity와 Shadow 효과는 시각적 깊이와 계층 구조를 나타내는 데 중요한 역할을 합니다. 이러한 효과는 사용자가 인터페이스를 이해하고 상호 작용하는 방법에 대한 시각적 단서를 제공합니다. 예를 들어, Google의 머티리얼 디자인에서는 객체와 그 배경 사이의 거리를 나타내는 그림자의 크기와 흐림을 조절하여 차원감을 부여합니다. 그러나 이러한 효과는 성능에 부담을 줄 수 있습니다. 렌더링 과정에서 일반적으로 맨 위의 컴포넌트부터 그리고, 이미 그려진 영역 뒤의 컴포넌트는 무시합니다. 하지만 불투명도와 그림자 효과는 상위 컴포넌트를 그릴 때 모든 하위 컴포넌트에 영향을 주므로 더 많은 컴포넌트를 확인해야 합니다. 또한, 이러한 효과는 모든 픽셀에 대해 별도의 연산을 요구하기 때문에 추가적인 부하를 발생시킬 수 있습니다. 이러한 효과의 성능 영향은 플랫폼에 따라 다를 수 있습니다. iOS는 이러한 효과의 처리에 있어 상대적으로 더 나은 성능을 보일 수 있지만, Android에서는 더 심각한 성능 문제가 발생할 수 있습니다. ShowYourTime 에서는 미려한 결과물을 위해 Opacity와 Shadow 효과를 대량으로 사용했습니다. 이러한 디자인 선택이 시각적으로는 매력적이지만 성능 문제를 야기할 수 있습니다. ScrollSnap 기능과 두 개의 스와이프 컴포넌트의 스크롤 위치를 동기화하는 과정, 그리고 컴포넌트 크기의 Transform을 수행할 때 이러한 성능 문제가 두드러졌습니다. 특히 스와이프 동작 중 두 컴포넌트의 스크롤 위치를 동기화하는 과정에서 프레임 끊김 문제가 발생했습니다. 이 문제를 해결하기 위해 여러 전략을 사용했습니다. 첫째, 컴포넌트 캐싱을 통해 반복적으로 렌더링되는 컴포넌트의 재사용을 최적화함으로써 렌더링 성능을 향상시켰습니다. 둘째, 리소스 관리 측면에서 SVG를 사용하여 벡터로 관리하고 있었지만, 로고와 같이 변하지 않는 영역을 온디맨드로 이미지화하여 불필요한 렌더링 작업을 줄였습니다. 셋째, 이벤트 처리에서는 throttle과 debounce 기법을 적용해 이벤트의 과도한 발생을 조절하고, 스크롤과 같은 연속적인 이벤트에서 성능 저하를 방지했습니다. 마지막으로, ScrollSnap 기능을 자체적으로 구현함으로써 더 효율적인 스크롤 관리가 가능해졌고, 이를 통해 프레임 끊김 문제를 해결했습니다. 이 실시간 스크롤 위치 동기화는 성능 문제로 인해 앱 초기 버전에서는 제공하지 못했습니다. 처음에는 각 컴포넌트에서 변경이 이루어지고 나면 비동기로 동기화를 시켜주었습니다. 이 기능은 앱에서 중요한 UX 를 담당한다고 판단했기 때문에 시간을 들여서 개선했습니다. # 회고 처음엔 iOS만을 대상으로 출시했지만, 향후 Android 플랫폼 지원의 가능성을 염두에 두었습니다. 개발자 한명으로 모든 작업을 수행하기 어려웠기에, 이러한 요구사항을 충족시키기 위해 크로스 플랫폼 개발이 필수적이었습니다. Flutter와 고민을 했지만 React와의 친숙함을 고려하여, React Native를 선택했습니다. React Native는 React에 익숙한 상태에서 개발 생산성이 매우 뛰어났습니다. 실제로, 첫 앱 릴리즈는 불과 24시간 만에 완료되었으며, 이후로도 1\~2주마다 지속적인 릴리즈 주기를 유지할 수 있었습니다. 그러나 React Native를 사용하여 카메라와 카메라롤 같은 네이티브 기능을 조작하려 할 때 예상치 못한 어려움이 발생했고, 라이브러리 지원 역시 품질 면에서 만족스럽지 못했습니다. 그로 인해 여기에 모두 기술하지 못한, 제공되지 못했던 기능들이 많았습니다. 더불어, 약간의 잘못된 구현으로도 앱의 성능이 저하될 수 있다는 점을 경험했습니다. 앱의 최적화 작업에는 개발 과정 자체보다 더 많은 시간을 할애해야 했습니다. 이러한 점들을 고려해 볼 때, Flutter로의 전환은 좋은 대안이었을 수도 있습니다. 비록 Flutter를 통한 개발 경험이 비즈니스 수준까지는 아니었지만, 간단한 토이 프로젝트를 통해 경험한 바로는 Flutter가 제공하는 기본 도구의 품질이 우수하여, React Native에서 겪었던 문제들을 보다 쉽게 해결할 수 있지 않았을까 합니다. 그래서 다음 프로젝트에서는 기회가 된다면 Flutter 로 개발을 해보려고 합니다. #개발 #ReactNative #RN #카메라 #타임스탬프 #ShowYourTime