---
id: game-skia-native-2d
title: Skia / Native 2D — Mobile / Cross-platform 그림
category: Coding
status: draft
source_trust_level: B
verification_status: conceptual
created_at: 2026-05-09
updated_at: 2026-05-09
tags: [game, skia, 2d, mobile, vibe-coding]
tech_stack: { language: "TS / Dart", applicable_to: ["Mobile", "Frontend"] }
applied_in: []
aliases: [Skia, react-native-skia, CanvasKit, Flutter, Path, paint, fast 2D]
---

# Skia / Native 2D

> Google Skia = Chrome/Flutter/Android/Firefox 의 그림 엔진. **GPU-accelerated 2D**. RN Skia / CanvasKit / Flutter Canvas 가 wrapper.

## 📖 핵심 개념
- Path: 선 + 곡선.
- Paint: 색 + style.
- Canvas: drawable surface.
- Shader / image filter: Path 위 효과.

## 💻 코드 패턴

### React Native Skia
```bash
yarn add @shopify/react-native-skia
```

```tsx
import { Canvas, Circle, Path, Skia, useClockValue, useComputedValue, useValue } from '@shopify/react-native-skia';

function App() {
  const clock = useClockValue();
  const cx = useComputedValue(() => 100 + Math.sin(clock.current / 500) * 50, [clock]);
  
  return (
    <Canvas style={{ flex: 1 }}>
      <Circle cx={cx} cy={100} r={40} color="hotpink" />
    </Canvas>
  );
}
```

### Path (복잡 도형)
```tsx
const path = Skia.Path.Make();
path.moveTo(50, 50);
path.lineTo(150, 50);
path.quadTo(200, 100, 150, 150);
path.close();

<Path path={path} color="lightblue" style="fill" />
<Path path={path} color="darkblue" style="stroke" strokeWidth={2} />
```

### Gradient
```tsx
import { LinearGradient, vec } from '@shopify/react-native-skia';

<Rect x={0} y={0} width={200} height={200}>
  <LinearGradient
    start={vec(0, 0)}
    end={vec(200, 200)}
    colors={['#00ffff', '#ff00ff']}
  />
</Rect>
```

### Image filter (blur, etc)
```tsx
import { Blur, ColorMatrix } from '@shopify/react-native-skia';

<Image image={img} x={0} y={0} width={300} height={300}>
  <Blur blur={10} />
  <ColorMatrix matrix={[
    1, 0, 0, 0, 0,
    0, 1, 0, 0, 0,
    0, 0, 1, 0, 0,
    0, 0, 0, 0.5, 0,  // 50% alpha
  ]} />
</Image>
```

### Shader (GPU)
```tsx
import { Shader, Skia } from '@shopify/react-native-skia';

const source = Skia.RuntimeEffect.Make(`
  uniform float2 iResolution;
  uniform float iTime;
  
  half4 main(float2 fragCoord) {
    float2 uv = fragCoord / iResolution;
    return half4(uv.x, uv.y, sin(iTime), 1.0);
  }
`);

const clock = useClockValue();
const uniforms = useComputedValue(() => ({
  iResolution: [width, height],
  iTime: clock.current / 1000,
}), [clock]);

<Canvas>
  <Fill>
    <Shader source={source!} uniforms={uniforms} />
  </Fill>
</Canvas>
```

→ Web 의 Three.js shader 같은 power.

### Animation (declarative)
```tsx
import { useSpring, withTiming, withRepeat, withSequence } from '@shopify/react-native-skia';

const x = useSharedValue(0);
useEffect(() => {
  x.value = withRepeat(
    withSequence(
      withTiming(200, { duration: 1000 }),
      withTiming(0, { duration: 1000 }),
    ),
    -1
  );
}, []);

<Circle cx={x} cy={100} r={20} color="red" />
```

→ Reanimated 통합 — UI thread 60fps.

### Text
```tsx
import { Text, useFont } from '@shopify/react-native-skia';

const font = useFont(require('./Roboto.ttf'), 24);
if (!font) return null;

<Text x={50} y={100} text="Hello" font={font} color="white" />
```

### CanvasKit (web)
```ts
import CanvasKitInit from 'canvaskit-wasm';

const CanvasKit = await CanvasKitInit({ locateFile: f => `/${f}` });
const surface = CanvasKit.MakeWebGLCanvasSurface('canvas')!;
const canvas = surface.getCanvas();

const paint = new CanvasKit.Paint();
paint.setColor(CanvasKit.Color4f(0.9, 0.5, 0.2, 1.0));
paint.setStyle(CanvasKit.PaintStyle.Fill);

canvas.drawCircle(100, 100, 50, paint);
surface.flush();
```

→ Web 에서 Skia 그대로 (Flutter web 이 사용).

### Flutter Canvas
```dart
class MyPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()
      ..color = Colors.blue
      ..style = PaintingStyle.fill;
    
    canvas.drawCircle(Offset(size.width / 2, size.height / 2), 50, paint);
    
    final path = Path()
      ..moveTo(0, 0)
      ..quadraticBezierTo(100, 200, 200, 0);
    canvas.drawPath(path, paint);
  }
  
  @override
  bool shouldRepaint(covariant CustomPainter old) => false;
}

CustomPaint(painter: MyPainter(), size: Size.infinite);
```

### Game-like usage (RN Skia)
```tsx
function Game() {
  const clock = useClockValue();
  const playerY = useComputedValue(() => 200 + Math.sin(clock.current / 500) * 50, [clock]);
  
  // Bullets — array of values
  const bullets = useMemo(() => Array.from({ length: 20 }, () => ({ x: 0, y: 0 })), []);
  
  return (
    <Canvas style={{ flex: 1, backgroundColor: 'black' }}>
      <Circle cx={100} cy={playerY} r={20} color="green" />
      {bullets.map((b, i) => (
        <Circle key={i} cx={b.x} cy={b.y} r={5} color="red" />
      ))}
    </Canvas>
  );
}
```

### Performance
- Path / shape 재사용 (useMemo).
- Reanimated 의 SharedValue 가 UI thread.
- `useComputedValue` 가 derive.
- 큰 image = `Image` component + cache.
- 너무 많은 element = Canvas 가 한 번에 그림.

### Web Canvas API (대안)
```ts
const ctx = canvas.getContext('2d')!;
ctx.fillStyle = 'red';
ctx.fillRect(10, 10, 100, 100);
ctx.beginPath();
ctx.arc(150, 150, 50, 0, Math.PI * 2);
ctx.fill();
```

→ Skia 만큼 강력 X. 단순 OK.

### vs WebGL (Three.js)
```
2D = Skia / Canvas API
3D = WebGL / WebGPU / Three.js

Skia 도 GPU 가속 — Canvas API 보다 빠름 (canvaskit).
```

## 🤔 의사결정 기준
| 상황 | 추천 |
|---|---|
| RN custom drawing | RN Skia |
| Flutter | Canvas / CustomPainter |
| Web 2D | Canvas API (간단) / CanvasKit (강력) |
| Game UI | Skia / 자체 canvas |
| 차트 | Recharts / Visx (위 문서) |
| 매우 simple shape | SVG |

## ❌ 안티패턴
- **매 frame Path 새로**: useMemo / 외부.
- **JS thread 에 animation**: reanimated SharedValue.
- **큰 Bitmap 매번 decode**: cache.
- **너무 많은 Element (1000+)**: 한 Canvas 안 묶기.
- **Shader 매 frame compile**: useMemo.
- **CanvasKit web bundle 인지**: 큰 (~3MB). Lazy.

## 🤖 LLM 활용 힌트
- RN = RN Skia (modern).
- Flutter = built-in Canvas.
- Web 2D = Canvas API 보통 충분.
- Reanimated 통합으로 60fps.

## 🔗 관련 문서
- [[RN_Reanimated_3_Patterns]]
- [[Mobile_Flutter_Patterns]]
- [[Game_Shader_Patterns]]
