SwiftUI 렌더링

낯선 언어를 매일 학습하며 몰입과 성장을 기록하는 일지

  ·  2 min read

문제 상황 #

  • UI에 blur 및 gradient 적용 시 버벅임 발생
  • UX에 매우 큰 영향 발생
  • SwiftUI의 작동 방식에 대한 학습 필요

SwiftUI 렌더링 파이프라인 #

  • SwiftUI는 벡터 방식이 기본

기본 파이프 라인 #

  1. 레이아웃: 위치 잡기
  2. 그리기(CPU): View Hierarchy의 모든 요소(Circle, Shadow, Text)를 개별 레이어로 취급, 레이어를 얹어가면서 취합
  3. 디스플레이: 모두 합쳐서 화면에 보여줌
graph TD
    User[👆 User Scroll Action] -->|1. Trigger Redraw| ViewHierarchy

    subgraph ViewHierarchy [SwiftUI View Layer]
        direction TB
        Ball[Circle View]
        Shadow[Effect: Shadow]
        Blur[Effect: Blur]
        Text[Text View]
    end

    ViewHierarchy -->|2. Calculate Layout & Effects| CPU

    subgraph CPU_Process [🔥 CPU: Main Thread]
        Calc1[Rasterize Vector Path]
        Calc2[Compute Blur Radius]
        Calc3[Composite Layers]
    end

    CPU_Process -->|3. Heavy Load| GPU[⚠️ GPU: Laggy Display]

    %% 스타일링: 붉은색 계열로 부하 강조
    style CPU_Process fill:#ffcccc,stroke:#d32f2f,stroke-width:2px
    style GPU fill:#ffebee,stroke:#d32f2f,stroke-width:2px,stroke-dasharray: 5 5
    style ViewHierarchy fill:#fff3e0,stroke:#ff9800,stroke-width:1px

문제 원인 #

  • 스크롤을 할 때마다 실시간으로 레이어들을 모두 다시 그림
  • Shodow, blur와 같은 복잡한 연산은 높은 CPU 점유율을 발생시킴

해결 방법 #

  • .drawingGroup()을 통해 파이프라인 변경(Flattening)

변경된 파이프라인 #

  1. Off-Screen Rendering
    • 백그라운드에서 모든 효과를 합쳐 한 장의 이미지(비트맵)으로 만듦
    • 레이어를 모두 겹쳐 캡처하여 JPG 파일로 만드는 것과 비슷
  2. GPU Handover
    • 만들어진 이미지를 GPU에 전달
    • GPU는 이미지 연산은 매우 빠름
  3. Display
    • 스크롤 시 CPU는 연산하지 않고 GPU가 이미지만 옮김
graph TD
    Init[🚀 Initial Load] -->|1. Apply .drawingGroup| Metal

    subgraph OffScreen [Off-screen Rendering]
        Metal[⚙️ Metal Engine] -->|Flattening| Bitmap[🖼️ Single Bitmap / Texture]
    end

    Bitmap -->|2. Cache Result| GPUMemory[💾 GPU Memory]

    User[👆 User Scroll Action] -->|3. Zero Calculation| GPU

    subgraph GPU_Process [🚀 GPU: Render Server]
        GPUMemory -->|Simply Move Coordinates| Display[✨ Smooth 120Hz Display]
    end

    %% 스타일링: 초록/파란색 계열로 최적화 강조
    style OffScreen fill:#e3f2fd,stroke:#1976d2,stroke-width:2px
    style GPU_Process fill:#e8f5e9,stroke:#388e3c,stroke-width:2px
    style Bitmap fill:#fffde7,stroke:#fbc02d,stroke-width:2px
    style Display fill:#00c853,stroke:#00695c,stroke-width:2px,color:white

Trade-off #

.drawingGroup() 사용 시 #

  • RAM 사용량 증가
    • 벡터는 수식이 작지만, 비트맵은 용량이 큼
  • 화질 저하 가능성 있음
    • 확대 시 화질 깨질 수 있음

따라서 단순 텍스트나 버튼엔 사용하지 않는 것이 유리하고, 그림자나 그라데이션이 들어간 복잡한 뷰의 경우 사용이 권장된다.

배운 점과 인사이트 #

  • Swift 문법/프레임워크에서 새롭게 이해한 부분
  • 기존 언어/경험과 비교했을 때의 차이점