아임웹 레거시 스택, 아직 많은 영역을 담당하는 PHP
그 동안, 우리는 성능 개선을 인프라 개선과 SQL 최적화로 접근 했죠.
PHP의 성능 개선은 opcache
, apcu
등 주로 렌더링 캐싱으로 속도를 내는 방식 입니다.
앞서 렌더링 한 데이터가 있으면 그냥 내보내면 되니까요.
그런데, PHP는 왜 느릴까요
느리다는 것은 상대적입니다. 개발 당시에는 빨랐죠.
문제는 10년이 지났다는 겁니다.
PHP는 스크립트 언어이며, 위에서부터 아래로 내려오며 HTML을 렌더링 하는 서버사이드 스크립트 입니다.
아임웹의 PHP 렌더링은 그렇게 빠르지 않습니다.
일단 HTML의 처음부터 끝까지 다 그려야 브라우저로 내려보내는데,
브라우저에서 그 HTML을 받아 또 클라이언트 사이드 렌더링을 추가(JS, Image 등)로 하기 때문에 체감은 더 느려집니다.
현재 아임웹 PHP에서 아임웹 고객사이트를 렌더링 하는 방식은
애초에 병렬로 렌더링은 불가능 하며, 모든 과정을 직렬로 처리하기 때문이죠. (PHP의 특징이긴 하지만)
그렇다고 PHP 바깥 쪽에서 CDN으로 캐싱하는 것도 전체 캐싱이 불가능 해요.
이것은 동적 컨텐츠 렌더링의 한계죠.
이런 것들은 PHP 같은 서버사이드 언어가 가진 개발이 쉽고 직관적인데서 오는 필연적인 것들 입니다. JSP도 같은 문제에 직면해 있고, next.js 같은 모던 스택에서 SSR 도구가 나온 것은 비교적 최근 입니다.
각설 하고,
SSR
에서는 TTFB (Time to First Byte)
라는 지표가 있습니다.
어떤 요청의 첫번째 바이트가 브라우저에 도착 까지 걸린 시간 입니다.
이 시간이 PHP 렌더링 후, 내 브라우저에 HTML 첫 글자가 오기까지 걸린 시간 입니다. 이것을 줄여야 결과적으로 모든 것이 나아집니다.
PHP는 캐싱 기술이 많이 발전 했습니다.
opcache
, apcu
, zend cache
등, 종류도 다양하고 캐싱 대상도 다양 합니다.
근데 캐시가 효과를 발휘하려면 캐시 적중률, 즉 재-사용률
이 좋아야 하는데
아임웹은 70만 사이트가, 서로 다른 내용을 렌더링 하기에 적중률이 그다지 좋지 못해요.
그러면, 속도를 올리기 위한 무슨 방법이 있을까요?
일단 DB 쿼리와 인프라를 더 빠르게 만드는 것은 PHP 뿐만 아니라 모든 언어가 마찬가지 인데요.
쿼리튜닝, 인프라개선 등은 아임웹도 이미 매우 많이 했죠.
이 외에도 로직에 따라 시간이 걸리는 기능들이 있습니다.
이런 부분들의 처리 속도를 더 개선 할 수 있다면, 더할 나위가 없겠죠.
다만, PHP 만 가지고는 한계가 있습니다.
같은 알고리즘을 구현해도 애초에 동작이 빠른 c
나 rust
같이 저수준 언어를 써야 합니다.
다행히 PHP는 확장모듈 기능이 있어 타 언어로 만든 모듈을 붙이는 것이 가능 합니다.
PHP를 사용 하셨다면 *.so
같은 모듈을 불러오는 것을 보셨을 겁니다.
so
는 Shared Object
의 약자로 공통 함수 라이브러리를 뜻합니다.
이것을 PHP 모듈로 공급하면, PHP에서 그 모듈 안의 함수를 호출 할 수 있게 됩니다.
모듈을 PHP가 사용할 수 있는 형태로 만들기만 한다면요.
이 글은 바로 이것에 대한 글 입니다.
c
가 전통적이긴 한데요, 개발 과정이 정말 처참합니다. 괴로워요.
디버깅도 어렵고 개발환경 준비도 힘듭니다. 90년대 스타일 입니다.
근데 요즘 더 편하고 빠른게 있죠.
바로 rust
입니다.
배우기가 어렵다는데, 글쎄요. 목적이 있으면 방법은 문제가 아닙니다.
그리고 c
에 비하면 rust
는 정말 편하고 멋진 언어고, 그 빠르다는 golang
보다 더 빠르다고 알려져 있죠.
요걸 써보자고요.
rust
로 구현하면 빨라질 수 있는 지 검토 합니다.rust
로 해당 로직의 일부를 대체할 함수를 개발 합니다.php extension
으로 빌드 하고rust
로 php
확장을 만드는 방법이 여러개 있습니다.
rust 커뮤니티에 상당히 많은 시도가 있었어요.
우리는 phper
를 쓰기로 했습니다.
보통 대량의 데이터가 포함되면, 엑셀 파일 만드는게 느리죠?
대량의 루프 + 느린 처리속도의 어셈블 입니다.
이것을 rust
로 바꿔보죠.
xlsx 생성 모듈
을 사용하고rust
로 옮겨온 후6배 이상의 성능 향상이 있었습니다.
1분이 걸렸다면, 이제 10초 밖에 안 걸린다는거죠.
물론 때에 따라 조금 차이는 있을 수 있어요.
우리는 이제 rust 를 익히고 php 확장을 만들 충분한 이유가 있습니다.
다만, rust
학습 난이도가 쎄긴 합니다.
컴파일 타임에 오류를 감지할 수 있는 문법적 특이점이 있는 언어라서
다소 문법이 난해해 보일수 있습니다.
아래처럼요.
use std::fmt::Display;
fn longest_with_an_announcement<'a, T>(x: &'a str, y: &'a str, ann: T) -> &'a str
where T: Display
{
println!("Announcement! {}", ann);
if x.len() > y.len() {
x
} else {
y
}
}
새로운 언어를 익힐때는 보통 책을 많이 보시죠.
그런데, 책보다 훌륭한 한국어 가이드가 있어요.
들어가기에 앞서 - The Rust Programming Language
러스트는 잘 만들면, C/golang
보다 높은 성능을 보다 쉽게 개발 할 수 있고, 어떤 환경에서도 일정한 성능을 내는 훌륭한 저수준 언어 입니다.
어떤 환경에서도 일정한 성능을 낼수 있다는 것은 GC (가비지컬렉터)
가 없음에서 오는 언어적 성능 보장 장치 입니다.
문법은 생각보다 TS
나 golang
과 비슷한데, 새로운 개념들(소유권, 트레잇, 트레잇 바운드, 라이프타임)이 rust
적응을 어렵게 만듭니다. 그런데, 이런 규칙들을 지켜가며 개발한다면, 결국 어마어마한 성능으로 돌아옵니다.
이 모든 개념은 런타임이 아니라 컴파일 타임에 검증하기 위한 것으로, 결국 버그가 적고 고성능 코드를 만들 수 밖에 없게 하는 rust
의 특징 입니다.
이 언어에 시간을 투자할 충분한 가치가 있다고 생각 합니다.
특히, 우리는 PHP 확장을 통해 즉시 성능 이득을 얻을 수 있는 환경이죠.
아직 rust
만으로 서비스를 전체를 만드는 경우는 많지 않습니다.
아무래도 생산성이 낮고 편의기능이 부족 합니다.
그래서 핵심적인 코어 로직 개발이나, 웹 어셈블리 개발, 게임 서버 등에 성능이 최우선 되는 환경에 사용 됩니다.
rust
로 전환 한 후 극적인 성능 변화가 있는 사례가 많습니다.
deno
(겁나빠른 node.js 런타임, rust 로 개발)turbopack
(next.js 빌드 툴, rust 로 개발, 터보팩 이후 빌드속도 어마어마 해졌죠.)즉, 그냥 rust
로 만들기만 하면 빨라집니다.
rust
가 언어 차원에서 오류가 적고 고성능을 내도록 설계 됐고,
이를 컴파일 타임에 강제하여 누구나 그렇게 만들 수 밖에 없도록 흘러가기 때문 입니다. 참 좋은 언어 입니다.
자, 우리도 해볼까요?
거창한 무언가를 하기보다는, 현재 백로그에서 성능 개선이 두드러지게 효과를 보는
아주 작은 부분을 하나 골라서 PoC
를 해보시기를 권장 합니다.
분명 좋은 성과로 돌아올 것입니다.
도전!
매튜 드림.