모바일 개발의 커스텀 뷰 및 그리기: 핵심, API 및 작동 방식

저자: IT Sectr 게시일: 2026-08-01 읽는 시간: 11 분

커스텀 뷰는 모든 모바일 앱의 고유한 인터페이스의 기초입니다. 모바일 앱의 커스텀 뷰를 사용하면 표준 컴포넌트에서 사용할 수 없는 고유한 그래픽, 애니메이션 및 요소를 만들 수 있습니다. Apple Developer, 2025에 따르면, drawRect는 iOS 모바일 개발에서 커스텀 그리기의 주요 메서드로 남아 있습니다. Android에서는 Canvas와 함께 onDraw가 동일한 역할을 수행하며, Flutter에서는 CustomPainter가 그 역할을 합니다.

핵심 포인트

  • 커스텀 뷰 — 모바일 앱에서 고유한 그리기, 크기 및 동작 로직을 가진 사용자 정의 컴포넌트.
  • drawRect — iOS에서 Core Graphics 및 CGContext를 통한 커스텀 그리기를 위한 UIView 메서드.
  • onDraw — Android에서 onMeasure 측정 시스템과 함께 Canvas 및 Paint를 통한 그리기를 위한 View 메서드.
  • CustomPainter — paint 및 shouldRepaint 메서드로 Canvas에 그리기 위한 Flutter 클래스.
  • 측정 — iOS의 sizeThatFits, Android의 onMeasure 및 Flutter의 computeDryLayout을 통해 뷰의 치수를 결정하는 프로세스.

커스텀 뷰란 무엇이며 모바일 앱에서 왜 필요한가

커스텀 뷰는 고유한 그래픽을 만들기 위해 표준 그리기 및 측정 메서드를 재정의하는 사용자 정의 인터페이스 컴포넌트입니다. 차트, 애니메이션, 다이어그램, 커스텀 채트 요소 및 AR 마스크 — 이것들은 모바일 앱에서 커스텀 뷰의 일반적인 사용 사례입니다. 표준 UI 컴포넌트(Button, TextView, Label)는 80%의 작업을 커버하지만, 나머지 20%는 모바일 개발에서 그리기에 대한 완전한 제어가 필요합니다. 모바일 개발에서 그리기는 주니어 개발자와 시니어 개발자를 구분하는 기술입니다.

커스텀 뷰가 정당화되는 경우

모바일 앱의 커스텀 뷰는 고유한 2D 그래픽, 복잡한 애니메이션, 차트 및 데이터 시각화에 필요합니다. 표준 UIKit이 필요한 요소를 제공하지 않는 경우 — 커스텀 뷰가 생성됩니다. 모바일 앱에서 커스텀 뷰는 진행 표시줄, 커스텀 버튼, 그라데이션 채우기, 애니메이션 아이콘 및 손가락 그리기 캔버스에 사용됩니다. 과도하게 사용하지 않는 것이 중요합니다 — 간단한 스타일의 경우 Appearance API를 통해 표준 컴포넌트를 구성하는 것으로 충분합니다.

iOS의 커스텀 뷰: drawRect 및 Core Graphics

iOS에서 커스텀 뷰는 UIView를 상속하고 drawRect 메서드를 재정의하여 생성됩니다. drawRect는 첫 번째 렌더링 시와 각 setNeedsDisplay() 호출 후에 호출됩니다. drawRect 내부에서 개발자는 UIGraphicsGetCurrentContext()를 통해 CGContext를 얻습니다 — 선, 도형, 그라데이션 및 텍스트를 위한 캔버스입니다. Core Graphics(Quartz 2D)는 모든 픽셀을 완전히 제어할 수 있는 저수준 CPU 기반 렌더링 엔진입니다. UIBezierPath는 간결한 구문으로 경로를 생성하기 위한 편리한 래퍼입니다.

swift
class CircleView: UIView {
    private var fillColor: UIColor = .systemBlue

    override func drawRect(_ rect: CGRect) {
        guard let ctx = UIGraphicsGetCurrentContext() else { return }
        ctx.setFillColor(fillColor.cgColor)
        ctx.addEllipse(in: bounds.insetBy(dx: 4, dy: 4))
        ctx.drawPath(using: .fill)
    }

    func updateColor(_ newColor: UIColor) {
        fillColor = newColor
        setNeedsDisplay()
    }
}

Core Graphics: CGContext, CGGradient 및 변환

Core Graphics는 모든 그리기 작업을 위한 캔버스인 CGContext를 제공합니다. CGGradient는 CGColorSpace(sRGB, Display P3)를 지원하여 선형 및 방사형 그라데이션을 생성합니다. 변환은 CGAffineTransform을 통해 수행됩니다 — 회전, 크기 조정 및 이동을 위한 3x3 행렬입니다. CGContextTranslateCTM, CGContextRotateCTM 및 CGContextScaleCTM은 좌표계를 수정합니다. Core Graphics는 CPU에서 실행되므로 복잡한 그래픽은 프로세서에 부하를 줄 수 있습니다 — GPU 가속을 위해 Metal을 사용하세요.

도형 생성을 위한 UIBezierPath

UIBezierPath는 CGPath 위의 객체 지향 래퍼입니다. addArc(withCenter:radius:startAngle:endAngle:clockwise:) 메서드는 호와 원을 생성하고, addCurve는 베지어 곡선을 생성합니다. 경로는 addClip을 통해 합집합, 교집합 및 차집합 연산을 지원합니다. UIBezierPath는 커스텀 마스크(maskView), 클리핑 경로 및 복잡한 도형 — 별, 하트, 커스텀 아이콘에 편리합니다.

Android의 커스텀 뷰: onDraw 및 Canvas

Android에서 커스텀 뷰는 onDraw(Canvas)를 재정의하여 View를 확장합니다. Canvas에는 모바일 개발의 모든 그리기 메서드가 포함되어 있습니다: drawCircle, drawRect, drawLine, drawText, drawBitmap, drawPath 및 drawRoundRect. Paint는 매개변수 — 색상, 선 두께, 스타일(FILL, STROKE, FILL_AND_STROKE), 앤티앨리어싱(isAntiAlias) 및 텍스트 크기를 구성합니다. onDraw는 매 invalidate() 시 호출되며 무거운 작업을 포함해서는 안 됩니다 — 모든 계산은 캐시됩니다.

kotlin
class CustomCircleView(context: Context, attrs: AttributeSet?)
    : View(context, attrs) {

    private val paint = Paint().apply {
        style = Paint.Style.FILL
        color = Color.BLUE
        isAntiAlias = true
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        val cx = width / 2f
        val cy = height / 2f
        val radius = minOf(cx, cy) - 4f
        canvas.drawCircle(cx, cy, radius, paint)
    }
}

Android의 Path 및 애니메이션

Android의 Path는 UIBezierPath와 동등합니다: addCircle, addRect, addOval, lineTo, cubicTo 및 quadTo. PathMeasure는 경로 길이와 임의 지점의 좌표를 계산합니다 — 경로를 따라 객체의 움직임을 애니메이션하는 데 유용합니다. 애니메이션의 경우 콜백에서 invalidate()와 함께 ValueAnimator를 사용합니다. 애니메이터는 색상, 반경, 위치를 변경할 수 있습니다 — 그리기에 영향을 미치는 모든 속성입니다. Android의 커스텀 뷰는 XML을 통한 애니메이션을 위해 StateListDrawable 및 animated-vector도 지원합니다.

Canvas의 Shader 및 그라데이션

Android의 그라데이션은 Shader를 통해 설정됩니다 — LinearGradient, RadialGradient 및 SweepGradient. Shader는 paint.setShader(shader)를 통해 Paint에 할당됩니다. LinearGradient는 두 점 사이에 선택적 색상 위치가 있는 선형 그라데이션을 생성합니다. RadialGradient — 중심에서 가장자리로 방사형. SweepGradient — 중심점 주위의 원뿔형. Shader.TileMode(CLAMP, REPEAT, MIRROR)는 영역 경계를 넘는 동작을 정의합니다.

Flutter의 CustomPainter: Canvas에 그리기

Flutter는 CustomPainter를 사용합니다 — paint(Canvas canvas, Size size) 및 shouldRepaint 메서드가 있는 클래스입니다. Flutter의 Canvas는 Android와 동일한 메서드를 제공합니다: drawCircle, drawRect, drawLine, drawPath, drawParagraph 및 drawImage. Flutter의 Paint는 색상, 두께, 스타일 및 앤티앨리어싱을 구성합니다. Flutter는 drawRect나 onDraw를 사용하지 않습니다 — 모든 그리기는 CustomPaint 위젯에 내장된 CustomPainter를 통해 이루어집니다.

dart
class CirclePainter extends CustomPainter {
  final Color color;

  CirclePainter({required this.color});

  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()
      ..color = color
      ..style = PaintingStyle.fill
      ..isAntiAlias = true;
    final radius = size.shortestSide / 2 - 4;
    canvas.drawCircle(size.center(Offset.zero), radius, paint);
  }

  @override
  bool shouldRepaint(CirclePainter oldDelegate) =>
    oldDelegate.color != color;
}

shouldRepaint 및 RepaintBoundary

shouldRepaint는 CustomPainter를 다시 그려야 하는지 여부를 결정합니다. 렌더링에 영향을 미치는 데이터가 변경된 경우에만 true를 반환합니다 — 색상, 크기, 차트 데이터. RepaintBoundary는 다시 그리기를 별도 레이어로 격리합니다 — 드물게 변경되는 위젯에 사용합니다. Flutter에는 setNeedsDisplay나 invalidate가 없습니다 — 다시 그리기는 setState 또는 변경 사항을 수신하는 ValueNotifier를 통해 트리거됩니다.

Flutter의 그라데이션 및 변환

Flutter는 LinearGradient, RadialGradient 및 SweepGradient를 지원합니다 — Android와 동일한 유형입니다. Gradient는 createShader(Rect bounds)를 통해 Paint의 Shader를 생성합니다. 변환은 canvas.rotate, canvas.scale, canvas.translate 및 canvas.skew를 통해 수행됩니다. Flutter는 Transform 위젯에 4x4 변환 행렬(Matrix4)을 사용합니다. Flutter의 그리기 애니메이션은 CustomPainter를 다시 그리기 위해 setState와 함께 AnimationController를 사용하여 만들어집니다.

커스텀 뷰 측정: sizeThatFits, onMeasure 및 computeDryLayout 메서드

커스텀 뷰의 올바른 측정은 예측 가능한 인터페이스 동작의 기초입니다. iOS에서 뷰의 크기는 sizeThatFits 및 intrinsicContentSize를 통해 결정됩니다. sizeThatFits는 주어진 제약 조건에 대한 최적의 크기를 반환합니다. intrinsicContentSize — 외부 제약 조건이 없는 콘텐츠의 자연스러운 크기입니다. Android에서 측정은 MeasureSpec 매개변수(UNSPECIFIED, EXACTLY, AT_MOST)와 함께 onMeasure에서 구현됩니다. Flutter에서 크기는 paint(Canvas, Size)로 전달되며 computeDryLayout 또는 LayoutBuilder를 통해 계산할 수 있습니다.

iOS의 sizeThatFits 및 intrinsicContentSize

intrinsicContentSize는 CGSize를 반환합니다 — 콘텐츠를 기반으로 한 뷰의 자연스러운 크기입니다. setContentHuggingPriority 및 setContentCompressionResistancePriority는 늘어남과 압축 시 동작을 제어합니다. sizeThatFits는 Auto Layout에서 크기를 계산하기 위해 시스템에 의해 호출됩니다. 커스텀 뷰의 경우 컴포넌트에 자연스러운 크기가 있으면 intrinsicContentSize를 재정의합니다 — 예를 들어, 원은 항상 100x100이어야 합니다.

Android의 onMeasure 및 MeasureSpec

onMeasure는 widthMeasureSpec 및 heightMeasureSpec을 허용합니다. MeasureSpec에는 모드와 크기가 포함됩니다: UNSPECIFIED(제약 없음, 뷰가 자체 크기를 선택), EXACTLY(부모의 정확한 크기), AT_MOST(최대 크기). setMeasuredDimension(width, height)는 측정 결과를 저장합니다. 데이터가 변경되면 재측정을 위해 requestLayout()을 호출합니다. 중첩된 ViewGroup은 자식 요소를 배치하기 위해 onLayout을 재정의해야 합니다.

플랫폼측정 메서드변경 시 호출반환
iOSsizeThatFits / intrinsicContentSizesetNeedsLayout / invalidateIntrinsicContentSizeCGSize
AndroidonMeasure / setMeasuredDimensionrequestLayout / invalidatevoid (크기 저장)
FluttercomputeDryLayoutsetState / LayoutBuilderSize

Flutter의 computeDryLayout

computeDryLayout는 전체 레이아웃 주기 없이 크기를 계산하기 위한 RenderObject 메서드입니다. LayoutBuilder.getSize — build 메서드에서 사용 가능한 공간을 얻기 위한 대안입니다. Flutter에서 캔버스 크기는 Size로 paint에 전달되며 별도 측정이 필요하지 않습니다 — CustomPainter는 전달된 경계 내에서 그립니다. 그러나 커스텀 RenderObject의 경우 performLayout 및 computeDryLayout을 재정의해야 합니다.

자주 묻는 질문

iOS의 drawRect란 무엇이며 언제 호출되나요?

drawRect는 Core Graphics를 통한 커스텀 그리기를 위한 UIView 메서드입니다. 뷰의 첫 번째 렌더링 시와 각 setNeedsDisplay() 호출 후에 호출됩니다. 선, 도형 및 텍스트에 대해 drawRect 내에서 CGContext를 사용하세요.

Canvas를 사용하여 Android에서 그리는 방법은?

View를 확장하는 클래스를 만들고, onDraw(Canvas canvas)를 재정의하고 Canvas API를 사용하세요: drawCircle, drawRect, drawPath. Paint는 색상, 두께 및 스타일을 구성합니다. 업데이트하려면 invalidate()를 호출하세요.

Flutter의 CustomPainter란 무엇인가요?

CustomPainter는 Canvas에 커스텀 그리기 위한 Flutter 클래스입니다. paint(Canvas, Size) 및 shouldRepaint 메서드를 구현하세요. CustomPaint 위젯을 통해 배치됩니다. Flutter에는 drawRect가 없습니다 — 모든 그리기는 CustomPainter를 통해 이루어집니다.

커스텀 뷰의 크기를 측정하는 방법은?

iOS에서는 sizeThatFits 및 intrinsicContentSize를 사용하세요. Android에서는 — MeasureSpec과 함께 onMeasure를 사용하세요. Flutter에서는 크기가 paint(Canvas, Size)로 전달되거나 computeDryLayout을 통해 계산됩니다. 각 플랫폼은 자체 측정 메서드를 사용합니다.

표준 컴포넌트 대신 커스텀 뷰를 사용해야 하는 경우는?

커스텀 뷰는 표준 UIKit 또는 View로 구현할 수 없는 고유한 그래픽, 애니메이션, 차트 및 요소에 필요합니다. 간단한 스타일의 경우 표준 컴포넌트를 구성하는 것으로 충분합니다 — 불필요하게 아키텍처를 복잡하게 만들지 마세요.

요약

  • 커스텀 뷰 — 모바일 앱에서 차트, 애니메이션 및 비표준 요소를 위한 고유한 인터페이스의 기초.
  • drawRect — CGContext 및 UIBezierPath로 Core Graphics를 통한 그리기를 위한 iOS 메서드.
  • onDraw — Path 및 Shader 지원으로 Canvas 및 Paint를 통한 그리기를 위한 Android 메서드.
  • CustomPainter — shouldRepaint 및 RepaintBoundary로 Canvas에 그리기 위한 Flutter 클래스.
  • 측정 — sizeThatFits(iOS), onMeasure(Android) 및 computeDryLayout(Flutter)을 통한 크기 검증.
  • 모바일 개발의 그리기는 모든 플랫폼에서 통일된 패턴을 따릅니다: 컨텍스트 얻기, 스타일 구성 및 프리미티브 그리기.
  • 그리기 객체(Paint, Path)를 캐시하세요 — 안정적인 성능을 위해 drawRect, onDraw 또는 paint 내에서 생성하지 마세요.

턴키 방식의 모바일 애플리케이션을 개발해 드립니다

IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.

프로젝트 논의