[Dalendar DevLog 6] 더 나은 코드를 위하여 - 앱 성능 개선과 코드 리팩토링
초기 프로토타입을 넘어, 논문 기반의 최적화 알고리즘을 적용하여 코드를 리팩토링하고 성능을 개선한 경험을 나눕니다.
![[Dalendar DevLog 6] 더 나은 코드를 위하여 - 앱 성능 개선과 코드 리팩토링](/images/blog/dalendar_dev_6_refactoring.png)
6편: 더 나은 코드를 위하여 - 앱 성능 개선과 코드 리팩토링
Introduction
‘더 나은 코드를 위하여’ 시리즈의 이전 글에서는 기본적인 기능을 갖춘 캘린더 앱을 만드는 과정을 다루었습니다. 일단 작동하는 프로토타입을 완성한 후, 저의 다음 목표는 성능 최적화와 코드 리팩토링이었습니다. 이번 글에서는 단순히 작동하는 것을 넘어, 앱의 핵심 로직을 더 효율적이고 우아하게 개선하는 제 여정을 공유하고자 합니다.
1. 초기 구현의 한계: ‘그저 달력’의 코어 로직
초기 캘린더 앱은 안드로이드 개발의 표준적인 방식을 따랐습니다. RecyclerView를 사용하여 UI 렌더링 자체는 효율적으로 이루어졌지만, 문제는 그 아래에 있는 날짜 계산 로직에 있었습니다. 표준적인 방식으로 구현된 계산 로직은 사용자가 월을 빠르게 스크롤할 때마다 수많은 날짜 계산을 반복하며 성능 저하의 주된 원인이 되었습니다.
2. 성능 병목 현상 발견과 해법 탐색
모든 캘린더 알고리즘의 심장에는 ‘라타 다이(rata die)’ 변환 과정이 있습니다. 이 변환 작업은 필연적으로 정수의 나눗셈과 나머지 연산을 많이 사용하게 되는데, 이 연산들이 CPU에서 가장 느린 기본 산술 연산이라는 점이 문제였습니다. 프로파일링 결과, CPU가 단순한 정수 나눗셈과 나머지 연산에 엄청난 시간을 소모하고 있었던 것입니다.
3. 리팩토링의 핵심: 나눗셈을 곱셈으로 바꾸기
논문에서 제시한 핵심 개념은 **‘강도 축소 최적화(strength reduction optimization)‘**입니다. 이는 계산 비용이 비싼 연산을 훨씬 라른 연산으로 대체하는 기법입니다.
3.1. 사례 1: 연도 계산 리팩토링 (Before & After)
Before: 표준적인 나눗셈과 나머지 연산 사용
n2 = 4 * r1 + 3;
q2 = n2 / 1461;
r2 = n2 % 1461 / 4;
After: 매직 넘버를 사용한 곱셈과 비트 시프트 연산으로 대체
n2 = 4 * r1 + 3;
u2 = 2939745 * n2; // 미리 계산된 매직 넘버를 곱함
q2 = u2 / 2^32; // 몫은 64비트 곱셈 결과의 상위 32비트
r2 = u2 % 2^32 / 2939745 / 4; // 나머지는 하위 32비트에서 파생됨
여기서 q2는 64비트 곱셈 결과인 u2의 상위 32비트에서 계산됩니다. 진짜 마법은 r2가 계산되는 방식에 있습니다. u2의 하위 32비트를 사용하여 q2의 계산 결과 없이도 나머지를 정확하게 재구성할 수 있습니다.
3.2. 사례 2: 월 계산 리팩토링 (Before & After)
Before:
n3 = 5 * r2 + 461;
q3 = n3 / 153;
r3 = n3 % 153 / 5;
After:
n3 = 2141 * r2 + 197913;
q3 = n3 / 2^16; // 몫은 결과의 상위 비트에서 계산 (16비트 오른쪽 시프트)
r3 = (n3 % 2^16) / 2141; // 나머지는 하위 비트에서 파생됨
4. 단순한 속도 개선 그 이상: 데이터 종속성 제거
이것은 단순한 명령어 교체가 아니라, 데이터 종속성을 제거하기 위한 계산의 근본적인 재구성이었습니다. 기존 코드에서 CPU는 q2 계산이 끝날 때까지 기다려야만 r2 계산을 시작할 수 있었지만, 리팩토링된 버전에서는 u2 값이 준비되는 즉시 두 계산을 동시에 시작할 수 있습니다. 이것은 현대 슈퍼스칼라 프로세서에서 **‘명령어 수준 병렬성(instruction-level parallelism)‘**을 가능하게 하는 완벽한 실제 사례입니다.
5. 결론: 깔끔하고 빠른 코드를 향한 여정
이번 리팩토링을 통해 캘린더 앱은 더 빠르고 반응성이 좋아졌을 뿐만 아니라, 계산적으로 더 우아하고 깔끔한 코드베이스를 갖추게 되었습니다. 이것이야말로 ‘더 나은 코드’를 작성한다는 원칙을 실현하는 과정이었습니다.
![[Dalendar DevLog 1] 프로젝트의 시작과 기술 스택 선정](/images/blog/dalendar_dev_1_ideation.png)
![[Dalendar DevLog 2] 견고한 앱의 뼈대 - 아키텍처와 데이터 구조 설계](/images/blog/dalendar_dev_2_architecture.png)
![[Dalendar DevLog 3] 핵심 기능 구현 1 (백엔드 & 로직)](/images/blog/dalendar_dev_3_math_logic.png)