
솔직히 고백하자면, 스타트업에서 '야생형 개발자'로 구르던 시절의 저는 성능 최적화를 '돈'으로 해결하려 했습니다. API 응답 속도가 느려지면 쿼리를 튜닝하기보다 AWS EC2 인스턴스 타입을 한 단계 올렸고, 트래픽이 몰리면 오토스케일링 그룹(ASG)의 Max Capacity를 늘리는 게 최선이라고 믿었죠. 그게 가장 빠르고 안전한 '생존 방식'이었으니까요.
하지만 체계가 잡힌 큰 조직으로 넘어오니, 그런 방식은 통하지 않았습니다. 단순히 리소스를 때려붓는 건 엔지니어링이 아니라 비용 낭비라는 지적을 받았을 때, 얼굴이 화끈거렸던 기억이 납니다. 최근 우연히 'GPU 기반 벡터 그래픽스 렌더링'에 관한 기술 아티클을 읽으며, 그때의 부끄러움이 다시금 떠올랐습니다. 그래픽스 렌더링의 발전 과정이 우리가 백엔드에서 겪는 '무식한 확장'과 '스마트한 최적화'의 차이를 적나라하게 보여주고 있었기 때문입니다.
우리가 흔히 쓰는 벡터 그래픽(SVG 등)은 의외로 대부분 CPU가 처리합니다. CPU는 마치 책을 처음부터 끝까지 정독하는 한 명의 독자와 같습니다. 순차적으로 명령을 처리하죠. 반면 GPU는 책을 14,000조각으로 찢어서 동시에 읽어버리는 14,000명의 사람들과 같습니다. 병렬 처리의 끝판왕이죠.
기존의 CPU 방식 래스터화(Rasterization)는 'Winding Number'라는 개념을 사용합니다. 가상의 광선(Ray)을 쏘아서 도형의 경계선과 몇 번 교차하는지를 세어, 픽셀이 도형 내부인지 외부인지를 판별합니다. 이걸 GPU로 가져오려면 사고의 전환이 필요합니다. 도형을 순서대로 그리는 게 아니라, '모든 픽셀이 각자 알아서' 자신이 도형 안에 있는지 검사하게 만드는 겁니다. 수천 개의 스레드가 동시에 각 픽셀의 운명을 결정하는 셈이죠.
여기서 제가 무릎을 탁 쳤던 부분은 '안티앨리어싱(Anti-aliasing)' 처리 방식이었습니다.
도형의 거친 경계면을 부드럽게 만드는 가장 쉬운 방법은 슈퍼샘플링(Supersampling)입니다. 이미지를 4배, 16배 크게 그린 다음 축소해서 평균을 내는 방식이죠. 퀄리티는 좋아지지만, 메모리와 연산 비용이 기하급수적으로 늘어납니다. 이게 딱 제가 예전에 했던 짓입니다. "성능이 안 나와? 서버 4배로 늘려!"라고 외치던 그 무식한 방법론이죠.
반면, 이 글에서 소개하는 'Analytic Anti-aliasing'은 다릅니다. 무작정 해상도를 키우는 게 아니라, 픽셀 하나하나가 도형에 걸쳐진 면적(Area)과 커버리지(Cover)를 수학적으로 계산합니다. 값과 값을 이용해 픽셀의 투명도(Alpha)를 정교하게 산출해내는 것이죠. 리소스를 낭비하지 않으면서도 슈퍼샘플링보다 훨씬 뛰어난 품질을 만들어냅니다. 이것이 진짜 엔지니어링입니다.
백엔드 개발자인 우리가 이 그래픽스 이론에서 배워야 할 점은 명확합니다.
우리는 지금도 툭하면 '슈퍼샘플링'식 해결책을 내놓고 있지 않나요? 레거시 코드의 비효율적인 로직을 수정(Analytic 접근)하는 대신, 쿠버네티스 파드(Pod) 개수만 늘리거나 캐시 레이어를 덕지덕지 붙여서 문제를 덮고 있는 건 아닌지 돌아봐야 합니다.
물론 급할 땐 리소스를 투입해야 합니다. 하지만 그건 임시방편일 뿐, 기술적 해결책이라 부르기엔 민망합니다. 하물며 그래픽스 분야에서도 픽셀 하나를 칠하기 위해 저렇게 치열하게 수학적 최적화를 고민하는데, 비즈니스 로직을 다루는 우리가 "그냥 서버 더 띄우죠"라고 말하는 건 직무 유기 아닐까요.
앞으로는 맹목적인 스케일 아웃(Scale-out)에 의존하기보다, 내 코드가 불필요한 연산을 하고 있지는 않은지, 알고리즘적으로 더 우아한 해결책(Analytic solution)은 없는지 고민하는 개발자가 되어야겠습니다. 결국, 연봉 값은 서버 비용을 태우는 능력이 아니라, 아끼는 능력에서 증명되는 법이니까요.


