CGContext는 iOS 및 macOS에서 2D 드로잉 명령을 실행하기 위한 Core Graphics의 주요 객체입니다. 이는 현재 상태(채우기 색상, 선 두께, 변환, 클리핑 영역, 글꼴)를 저장하는 그래픽 컨텍스트를 나타냅니다. 선, 사각형, 텍스트, 이미지 등 모든 드로잉 작업은 CGContext를 통해 수행됩니다. Apple Developer Documentation, 2026에 따르면, CGContext는 Apple 생태계의 모든 래스터 및 벡터 그래픽의 기초입니다.
핵심 사항
CGContext는 Core Graphics 프레임워크의 불투명 타입(opaque type)으로, 2D 렌더링 명령을 실행하기 위한 환경을 나타냅니다. 상태 스택, 현재 색상 공간, 좌표 변환 및 클리핑 영역을 관리합니다. CGContext는 모든 Quartz 2D 드로잉 함수가 상호 작용하는 유일한 객체이며, 모든 그래픽 작업은 이를 통해 이루어집니다.
CGContext는 목적에 따라 여러 변형이 있습니다: bitmap context는 메모리에서 래스터 이미지를 작업하기 위한 것, PDF context는 PDF 문서 생성을 위한 것, window context는 화면 드로잉을 위한 것(UIView가 draw(_:)에서 제공)입니다. iOS 개발자는 draw(_:) 메서드 내부의 UIGraphicsGetCurrentContext() 또는 오프라인 렌더링을 위한 UIGraphicsBeginImageContextWithOptions에서 얻은 컨텍스트를 가장 자주 사용합니다.
컨텍스트는 상태 스택을 통해 모든 설정을 적용합니다. 개발자는 현재 상태를 저장하고(saveGState), 매개변수를 수정하고, 이전 상태를 복원(restoreGState)할 수 있습니다. 이는 다른 요소에 다른 색상, 선 두께 및 변환이 필요한 복잡한 장면을 그릴 때 매우 중요합니다. Apple WWDC 2023에 따르면, 상태 스택의 올바른 사용은 코드 가독성을 향상시키고 시각적 아티팩트를 방지합니다.
CGContextSaveGState는 현재 컨텍스트 상태의 복사본을 스택에 푸시합니다. 이 상태에는 색상, 선 두께, 변환, 그림자, 클리핑 영역, 글꼴 등 모든 설정이 포함됩니다. 매개변수를 수정한 후 CGContextRestoreGState를 호출하면 컨텍스트가 저장된 상태로 돌아가 모든 중간 변경 사항이 취소됩니다. 스택은 iOS에서 최대 32개의 상태를 보유할 수 있습니다.
다른 스타일의 요소 그룹(예: 여러 색상의 차트 섹터)을 그릴 때 상태 스택은 각 매개변수를 수동으로 기억하고 복원할 필요를 없앱니다. saveGState/restoreGState가 없으면 개발자는 각 요소 후에 색상, 그림자 및 변환을 재설정해야 합니다. 스택 사용은 코드를 줄이고 복원 오류를 방지합니다.
저장과 복원의 균형을 유지하는 것이 중요합니다. 각 saveGState에는 일치하는 restoreGState가 있어야 합니다. 균형이 깨지면 상태 누수 또는 조기 복원이 발생하여 예측할 수 없는 렌더링이 발생합니다. Xcode의 정적 분석기는 CGContext 스택 균형을 확인하지 않으므로 정확성은 개발자의 책임입니다. do/finally 블록에서 쌍을 그룹화하는 것이 좋습니다.
override func draw(_ rect: CGRect) {
guard let ctx = UIGraphicsGetCurrentContext() else { return }
ctx.saveGState()
ctx.setStrokeColor(UIColor.red.cgColor)
ctx.setLineWidth(3)
ctx.addRect(CGRect(x: 50, y: 50, width: 200, height: 100))
ctx.strokePath()
ctx.restoreGState()
}
CGContextSetFillColorWithColor는 후속 작업의 채우기 색상을 설정합니다. 색상은 색상 공간 및 색상 구성 요소에 대한 정보를 포함하는 CGColorRef로 전달됩니다. CGContextSetStrokeColorWithColor는 선 색상에 대해 유사하게 작동합니다. Apple은 성능 향상을 위해 자주 사용되는 CGColorRef 값을 캐싱할 것을 권장합니다.
CGContextDrawLinearGradient는 지정된 색상으로 두 점 사이의 선형 그라디언트를 그립니다. CGContextDrawRadialGradient는 한 원에서 다른 원으로 전환되는 방사형 그라디언트를 만듭니다. 그라디언트로 작업하려면 먼저 색상 배열과 중지 위치를 포함하는 CGGradientRef 객체가 생성됩니다. CGContext의 그라디언트는 RGB 및 그레이스케일 색상 공간을 모두 지원합니다.
그라디언트로 영역을 채우려면 먼저 클리핑 영역을 설정해야 합니다. clip이 없으면 그라디언트는 원하는 모양뿐만 아니라 전체 컨텍스트를 채웁니다. 일반적인 패턴: 컨텍스트에 패스를 추가하고, 영역을 제한하기 위해 clip을 호출한 다음 그라디언트를 그립니다. 이 패턴은 버튼에서 배경에 이르기까지 복잡한 모양의 그라디언트 채우기가 필요한 모든 응용 프로그램에서 사용됩니다.
| 함수 | 목적 | 색상 공간 |
|---|---|---|
| setFillColor | 채우기 색상 설정 | 모두 (CGColor 통해) |
| setStrokeColor | 선 색상 설정 | 모두 (CGColor 통해) |
| drawLinearGradient | 선형 그라디언트 그리기 | RGB 또는 그레이스케일 |
| drawRadialGradient | 방사형 그라디언트 그리기 | RGB 또는 그레이스케일 |
CGContextAddPath는 이후 렌더링을 위해 이전에 생성된 CGPathRef를 컨텍스트에 추가합니다. 패스를 추가한 후에는 선(strokePath), 채우기(fillPath) 또는 둘 다 할 수 있습니다. CGContextBeginPath는 이전 패스를 지우고 새 패스를 시작합니다. 컨텍스트는 한 번에 하나의 활성 패스만 지원합니다.
CGContextMoveToPoint는 현재 윤곽선 점을 지정된 좌표로 이동합니다. CGContextAddLineToPoint는 현재 점에서 지정된 점까지 직선을 그립니다. CGContextAddCurveToPoint는 3차 베지어 곡선을 추가하고, CGContextAddQuadCurveToPoint는 2차 곡선을 추가합니다. CGContextClosePath는 마지막 점과 첫 번째 점 사이를 직선으로 연결하여 윤곽선을 닫습니다.
UIBezierPath에서는 이러한 모든 메서드가 패스 객체에서 호출되는 반면, CGContext에서는 컨텍스트에서 직접 호출됩니다. 이 차이점은 접근 방식을 선택할 때 중요합니다. UIBezierPath는 패스 재사용에 더 편리하고, CGContext는 최소 오버헤드로 일회성 드로잉에 더 적합합니다. CGContext는 또한 CGContextSetLineDash를 통한 점선, CGContextSetLineCap을 통한 유연한 끝, CGContextSetLineJoin을 통한 연결을 지원합니다.
let ctx = UIGraphicsGetCurrentContext()!
ctx.beginPath()
ctx.moveTo(x: 50, y: 100)
ctx.addLineTo(x: 200, y: 100)
ctx.addCurveTo(x1: 250, y1: 50, x2: 300, y2: 150, x3: 350, y3: 100)
ctx.setStrokeColor(UIColor.blue.cgColor)
ctx.setLineWidth(5)
ctx.strokePath()
CGContextClip은 드로잉 영역을 현재 패스로 제한합니다. 이후의 모든 드로잉 작업은 이 영역 내에서만 표시됩니다. 이것은 마스킹, 모서리 둥글게 하기 및 사용자 정의 요소 경계 생성을 위한 기본 메커니즘입니다. 클리핑 영역은 saveGState/restoreGState를 통해 저장 및 복원됩니다.
CGContextTranslateCTM은 컨텍스트 좌표계의 원점을 이동합니다. CGContextScaleCTM은 X 및 Y 축을 확대/축소합니다. CGContextRotateCTM은 현재 원점을 중심으로 좌표계를 회전합니다. 변환은 누적되며, 각 후속 변환은 현재 좌표계를 기준으로 적용됩니다. 복합 변환(이동 + 회전 + 확대/축소)은 애니메이션 및 요소 위치 지정에 사용됩니다.
클리핑과 변환의 결합은 강력한 기능을 제공합니다. 클리핑을 통해 복잡한 마스크를 그린 다음 전체 콘텐츠에 변환을 적용할 수 있습니다. 일반적인 예는 둥근 모서리와 함께 각도가 있는 이미지 부분을 표시하는 것입니다. 작업 순서가 중요합니다. 먼저 클리핑 영역을 설정하고, 그런 다음 변환을 적용하고, 그 다음 콘텐츠를 그립니다. Apple은 드로잉 루프에서 CTM(현재 변환 행렬) 변경을 최소화할 것을 권장합니다.
override func draw(_ rect: CGRect) {
guard let ctx = UIGraphicsGetCurrentContext() else { return }
ctx.saveGState()
// clipping region - circle
ctx.addEllipse(in: CGRect(x: 50, y: 50, width: 200, height: 200))
ctx.clip()
// content transformation
ctx.translateBy(x: 150, y: 150)
ctx.rotate(by: .pi / 4)
ctx.translateBy(x: -150, y: -150)
UIImage(named: "photo")?.draw(at: CGPoint(x: 50, y: 50))
ctx.restoreGState()
}
CGContext를 사용하여 그라디언트 차트를 만드는 전체 예제를 살펴보겠습니다. 차트에는 그라디언트 채우기와 텍스트 레이블이 있는 세 가지 다른 색상의 섹터가 포함됩니다. 이 코드는 단일 컨텍스트에서 saveGState, clip, 그라디언트 및 텍스트 렌더링의 조합을 보여줍니다.
class PieChartView: UIView {
private struct Slice {
let color: UIColor
let value: CGFloat
let label: String
}
override func draw(_ rect: CGRect) {
guard let ctx = UIGraphicsGetCurrentContext() else { return }
let slices = [
Slice(color: .systemRed, value: 0.4, label: "iOS"),
Slice(color: .systemGreen, value: 0.35, label: "Android"),
Slice(color: .systemBlue, value: 0.25, label: "Other")
]
let center = CGPoint(x: bounds.midX, y: bounds.midY)
let radius = bounds.width * 0.35
var startAngle: CGFloat = -.pi / 2
for slice in slices {
let endAngle = startAngle + .pi * 2 * slice.value
ctx.saveGState()
ctx.moveTo(x: center.x, y: center.y)
ctx.addArc(center: center, radius: radius,
startAngle: startAngle, endAngle: endAngle, clockwise: false)
ctx.closePath()
ctx.setFillColor(slice.color.cgColor)
ctx.fillPath()
// text label
let midAngle = startAngle + endAngle / 2
let labelPos = CGPoint(
x: center.x + radius * 0.7 * cos(midAngle),
y: center.y + radius * 0.7 * sin(midAngle)
)
slice.label.draw(at: labelPos, withAttributes: [
.foregroundColor: UIColor.white,
.font: UIFont.boldSystemFont(ofSize: 14)
])
ctx.restoreGState()
startAngle = endAngle
}
}
}
이 예제에서 차트의 각 섹터는 자체 saveGState/restoreGState 블록에서 그려집니다. 이 접근 방식은 한 섹터의 색상 및 패스 설정이 다른 섹터에 영향을 미치지 않도록 보장합니다. addArc 메서드는 시작 각도에서 끝 각도까지 호를 만들고, closePath는 섹터를 중심 방향으로 닫습니다. 레이블 텍스트는 삼각 함수 cos과 sin을 사용하여 위치를 계산하여 섹터의 중간 각도에 배치됩니다.
빈번한 다시 그리기 중 성능을 향상시키려면 각도 및 위치 계산을 별도 배열에 캐싱하는 것이 좋습니다. CGContext는 모든 작업을 CPU에서 실행하므로 수십 개의 섹터가 있는 복잡한 장면은 최적화가 필요할 수 있습니다(예: CGLayer에서 사전 렌더링 또는 GPU 가속을 위한 Metal 사용). 5–10개 요소의 대부분 UI 작업에서는 CGContext 성능이 허용 가능한 수준으로 유지됩니다.
자주 묻는 질문
UIView의 draw(_:) 메서드 내부 또는 UIGraphicsBeginImageContextWithOptions 블록 내부에서 UIGraphicsGetCurrentContext()를 호출합니다. CALayer 레이어의 경우 CALayerDelegate의 draw(in:) 메서드를 사용하면 준비된 컨텍스트를 받을 수 있습니다.
fillPath는 현재 패스의 내부를 setFillColor의 색상으로 채웁니다. strokePath는 setStrokeColor, setLineWidth 및 setLineCap의 매개변수로 패스를 따라 선을 그립니다. 두 작업은 순차적으로 수행됩니다: 먼저 채우기, 그런 다음 필요한 경우 선.
Bitmap CGContext는 CGBitmapContextCreate를 통해 생성된 컨텍스트로, 화면 대신 메모리 버퍼에 그립니다. 백그라운드 이미지 생성, 표시 없는 그래픽 처리 및 Metal 또는 OpenGL용 텍스처 생성에 사용됩니다.
Core Graphics는 모든 설정을 재설정하는 메서드를 제공하지 않습니다. 변경 전에 saveGState를 사용하고 원래 상태로 돌아가려면 restoreGState를 사용합니다. 또는 동일한 매개변수로 새 컨텍스트를 만듭니다.
drawLinearGradient를 호출하기 전에 원하는 패스에서 clip을 통해 클리핑 영역을 설정합니다. clip이 없으면 그라디언트가 전체 컨텍스트를 채웁니다. 순서: addPath → clip → drawLinearGradient — 도형 내부에 그라디언트가 보장됩니다.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.