사전 렌더링
개념

렌더링

gabury1

무엇을 어떻게 보이게 할지 적어 둔 것을 받아 실제 결과물로 바꾸는 일입니다. 결과물을 미리 만들어 두지 않고 그때그때 만들어 냅니다. 어떤 조건에서 보게 될지가 미리 정해지지 않기 때문입니다.

상세

식당 주방에 있는 것은 조리법입니다. 무엇을 얼마나 넣고 어떤 순서로 익히는지가 적혀 있을 뿐 접시에 담긴 음식은 거기 없습니다. 몇 인분인지 얼마나 맵게 할지는 주문이 들어와야 정해집니다. 그래서 주문이 올 때마다 적힌 대로 그 자리에서 만들어 냅니다.

렌더링은 무엇을 어떻게 보이게 할지 적어 둔 기술(記述)을 받아 실제 결과물로 바꿔 내는 일입니다. 받는 쪽은 선언입니다. 무엇이 있는지와 어떤 성질을 갖는지만 적혀 있습니다. 내놓는 쪽은 그대로 소비되는 형태입니다. 화면에 찍히는 픽셀일 수도, 받는 쪽에 그대로 전달되는 문서일 수도 있습니다. 그 둘 사이를 잇는 절차 전체가 이 낱말이 가리키는 것입니다.

결과물을 미리 만들어 통째로 저장해 두면 될 것 같지만 그렇게 하지 못하는 이유가 있습니다. 결과물의 모양을 정하는 조건이 미리 정해지지 않습니다. 보는 창의 크기, 바라보는 시점, 그 순간의 상태가 그렇습니다. 조건 하나가 바뀌면 저장해 둔 결과물은 쓰지 못합니다. 그래서 조건이 정해지는 시점까지 결과물 만들기를 미룹니다. 미뤄 둔 그 일을 실제로 수행하는 것이 렌더링입니다.

배경

보이는 결과물 하나를 통째로 저장해 두면 그 결과물은 자신을 만들어 낸 조건에 묶입니다. 크기가 다른 화면, 다른 시점, 내용이 하나 바뀐 상태에는 그대로 쓰지 못합니다. 조건 조합마다 결과물을 하나씩 쌓아 두는 방법도 조합이 늘면 감당하기 어렵습니다.

그래서 「무엇이 있는지」와 「어떻게 보이는지」를 갈라 둘 필요가 생겼습니다. 표준 문서가 이 갈라짐을 그대로 적습니다. CSS(Cascading Style Sheets)는 구조화된 문서의 렌더링을 화면이나 종이 등에 대해 기술하는 언어입니다. 원본 문서를 요소들의 트리로 받아 화면, 종이, 오디오 스트림 같은 캔버스 위에 렌더합니다. 그 사이에 박스 트리라는 중간 구조를 만듭니다. 박스 트리는 렌더된 문서의 서식 구조를 나타냅니다. 원본 트리는 무엇이 있는지를 담습니다. 어떻게 보이는지는 렌더링이 만들어 냅니다.

사전은 render 를 두 갈래로 싣습니다. 예술적 또는 언어적 수단으로 무언가를 재현하거나 표현한다는 뜻이 하나입니다. 이미지나 텍스트를 화면에 표시되게 한다는 컴퓨터 쪽 뜻이 그 옆에 나란히 놓입니다. 이 일을 적분 방정식 하나로 묶은 자리는 1986년 SIGGRAPH 에 실린 The Rendering Equation 입니다. Kajiya 는 그 방정식이 알려진 여러 렌더링 알고리즘을 일반화한다고 적었습니다. 몬테카를로 해법을 논하는 과정에서 계층적 표본추출이라는 새 분산 감소 기법도 함께 내놓았습니다. 그것이 다양한 몬테카를로 절차에 효율적인 새 기법이 될 수 있다고 적었습니다.

갈래

무엇을 받아 무엇으로 내놓느냐가 축입니다. 받는 것과 내놓는 것이 달라지면 중간 절차가 통째로 달라집니다. 다만 뜻 자체는 갈리지 않습니다. 어느 자리에서나 적어 둔 기술로부터 최종 산출물을 만들어 낸다는 것입니다. 대상만 바뀝니다.

3차원 장면의 이미지 변환

flowchart TD
    A[정점과 프리미티브] --> B[셰이더 프로그램과 고정 기능 처리 단위]
    B --> C[프레임버퍼]
    C --> D[값 읽기]

받는 것은 정점으로 정의된 프리미티브입니다. 내놓는 것은 프레임버퍼의 값입니다. OpenGL 명세는 이 범위를 못 박습니다. 명세는 OpenGL 을 이하 GL 로 줄여 부릅니다. GL 은 GPU(Graphics Processing Unit) 메모리 안의 데이터를 처리하는 것만 다룹니다. 프레임버퍼로 렌더링하는 것과 그 프레임버퍼에 저장된 값을 읽는 것이 거기 들어갑니다. 다른 입출력 장치는 지원하지 않습니다. GL 은 컨텍스트 상태가 제어하는 여러 셰이더 프로그램과 고정 기능 처리 단위가 처리한 프리미티브를 그립니다. 프리미티브 하나는 점, 선분, 패치, 다각형 중 하나입니다. 프리미티브는 하나 이상의 정점 무리로 정의됩니다. 위치 좌표나 색, 법선, 텍스처 좌표 같은 데이터가 정점마다 붙습니다. 각 정점은 순서대로 독립적으로 같은 방식으로 처리됩니다.

이 갈래가 따로 서게 된 자리는 장면을 적는 일과 그 장면을 어떻게 그릴지 적는 일이 갈리는 지점입니다. 같은 명세가 그것을 직접 적습니다. OpenGL 은 복잡한 기하 객체를 어떻게 렌더링할지 기술하는 수단을 제공하지, 그 복잡한 객체 자체를 기술하는 수단을 제공하지는 않습니다.

문서 기술의 화면 픽셀 변환

flowchart TD
    A[원본 문서 트리] --> B[계산된 값 배정]
    B --> C[박스 트리]
    C --> D[캔버스]

받는 것은 요소와 텍스트 노드로 이루어진 원본 문서 트리입니다. 내놓는 것은 캔버스 위에 렌더된 문서입니다. 캔버스는 화면일 수도, 종이 한 장일 수도, 오디오 스트림일 수도 있습니다. 그렇게 조직된 원본 문서라면 무엇이든 CSS 로 렌더할 수 있지만 가장 흔히 쓰이는 종류는 DOM(Document Object Model)입니다.

이 갈래가 따로 서게 된 불편은 원본 트리가 담지 않는 것에 있습니다. 원본 트리에는 요소와 텍스트 노드만 들어 있습니다. 각 요소가 캔버스 위 어느 공간과 시간을 차지하는지는 거기 적혀 있지 않습니다. 그래서 중간 구조를 하나 세워야 했습니다. 먼저 캐스케이딩과 상속으로 원본 트리의 각 요소와 텍스트 노드에 CSS 속성마다 계산된 값을 배정합니다. 그 다음에 박스 트리를 만듭니다. 박스 트리의 박스 하나는 캔버스 위의 공간이나 시간에서 자기에게 대응하는 요소를 나타냅니다. 박스 트리 안의 텍스트 시퀀스는 마찬가지로 자기 텍스트 노드의 내용에 대응합니다.

데이터와 틀의 문서 변환

flowchart TD
    A[데이터와 컴포넌트] --> B[서버 또는 빌드 환경]
    B --> C[문서]

받는 것은 데이터와 그 데이터를 끼울 틀입니다. 내놓는 것은 문서입니다. 이 갈래에서는 만들어 내는 자리가 어디냐가 다시 갈립니다. React Server Components 는 번들링 전에 미리 렌더되는 컴포넌트입니다. 도는 자리는 클라이언트 앱이나 SSR(Server-Side Rendering, 서버 사이드 렌더링) 서버와 분리된 환경입니다. 빌드 시점에 CI(Continuous Integration) 서버에서 한 번 돌 수도 있습니다. 요청마다 웹 서버에서 돌 수도 있습니다.

이 갈래가 따로 서게 된 불편은 만드는 자리를 잘못 고르면 따라오는 비용에 있습니다. Server Components 는 빌드 시점에 돌아 파일 시스템을 읽거나 정적 콘텐츠를 가져올 수 있습니다. 그래서 웹 서버가 필요하지 않기도 합니다. 이것이 없을 때는 정적 데이터를 클라이언트에서 이펙트로 가져오는 방식이 흔합니다. 그러면 마크다운 변환기나 새니타이저 같은 라이브러리가 번들에 그대로 실립니다.

예시

glDrawArrays 호출

C
void glDrawArrays(GLenum mode, GLint first, GLsizei count);

배열 데이터로부터 프리미티브를 렌더하는 호출입니다. mode 는 어떤 종류의 프리미티브를 렌더할지 정합니다. GL_POINTS · GL_LINES · GL_TRIANGLES · GL_PATCHES 같은 심볼릭 상수를 받습니다. first 는 활성화된 배열에서 시작할 인덱스입니다. count 는 렌더할 인덱스 개수입니다.

이 호출은 아주 적은 수의 서브루틴 호출로 여러 기하 프리미티브를 지정합니다. 정점, 법선, 텍스처 좌표, 에지 플래그, 색을 하나씩 넘기려고 GL 프로시저를 부르지 않아도 됩니다. 정점 배열과 법선 배열과 색 배열을 미리 따로 지정해 둔 뒤 호출 한 번으로 프리미티브 열을 만듭니다. 호출되면 활성화된 각 배열에서 first 번째 원소부터 count 개를 연속으로 써서 기하 프리미티브 열을 구성합니다.

update the rendering 절차

flowchart TD
    A[렌더링 기회] --> B[렌더 불가 문서 걸러내기]
    B --> C[불필요한 렌더링 걸러내기]
    C --> D[크기 변경과 스크롤과 미디어 쿼리]
    D --> E[애니메이션 갱신과 프레임 콜백]
    E --> F[스타일 재계산과 레이아웃 갱신]

HTML(HyperText Markup Language, 하이퍼텍스트 마크업 언어) 표준이 문서를 화면에 올리는 단계에 붙인 이름과 순서입니다. 렌더링 기회가 있는 각 navigable 마다 그 활성 윈도우에 대해 렌더링 태스크 소스에 전역 태스크를 큐에 넣습니다.

먼저 렌더할 수 없는 문서를 목록에서 뺍니다. 렌더가 막힌 문서, 가시성 상태가 "hidden" 인 문서, 뷰 트랜지션 때문에 렌더링이 억제된 문서, 노드 navigable 이 지금 렌더링 기회를 갖지 못한 문서가 그것입니다. 이어 불필요한 렌더링도 뺍니다. 첫째 조건은 사용자 에이전트가 그 문서의 렌더링을 갱신해도 눈에 보이는 효과가 없다고 믿는 것입니다. 둘째 조건은 애니메이션 프레임 콜백 맵이 비어 있는 것입니다. 둘 다 참인 문서가 목록에서 빠집니다. 남은 문서마다 크기 변경 단계, 스크롤 단계, 미디어 쿼리 평가와 변경 보고, 애니메이션 갱신과 이벤트 발송, 전체화면 단계를 차례로 돌립니다. 그 뒤에 애니메이션 프레임 콜백을 돌립니다. 마지막은 스타일 재계산과 레이아웃 갱신입니다.

renderToString 호출

JavaScript
const html = renderToString(<App />);

React 트리를 HTML 문자열로 렌더하는 호출입니다. 반환값은 HTML 문자열입니다. 서버에서 이 호출로 앱을 HTML 로 렌더해 응답으로 보냅니다.

JavaScript
app.use('/', (request, response) => {
  const html = renderToString(<App />);
  response.send(html);
});

이렇게 하면 컴포넌트의 초기 HTML 출력이 나옵니다. 그 출력은 상호작용하지 않습니다. 클라이언트에서 hydrateRoot 를 불러야 서버가 만든 HTML 이 상호작용 가능해집니다.

관련 항목

이것의 하위 종류

래스터화 · 광선 추적 · 광선 투사 · 경로 추적 · 분산 광선 추적 · 라디오시티

이것을 계산하고 다듬어 내는 이론·기법

렌더링 방정식 · 몬테카를로 · 헤미큐브 · 안티에일리어싱 · 텍스처 필터링

이것을 이루는 구성 요소

GPU · 셰이더 · 프레임버퍼 · 정점 · 프리미티브 · 법선 · 텍스처 · 깊이 버퍼

문서 렌더링에서 쓰는 중간 구조

박스 트리 · 원본 트리 · 렌더 트리 · DOM · 캔버스

문서 렌더링이 거치는 처리 단계

캐스케이딩 · 레이아웃 · 리플로우 · 페인트 · 합성 · 애니메이션 프레임 콜백 · navigable · 서버 사이드 렌더링 · 하이드레이션

이것을 정의하는 표준·명세

OpenGL · CSS · HTML

문서 렌더링을 실제로 구현하는 도구

브라우저 엔진 · 가상 DOM · React · CI

이 결과물의 표시 성능을 정하는 요소

프레임률 · 프레임 예산 · 해상도 · 수직 동기화

이 출력의 색을 정의하는 표준

색 공간 · sRGB · 감마 보정

이것에서 자주 나는 오류·장애

레이아웃 이동 · 티어링 · 프레임 드롭

다른 이름: rendering · 렌더