UI 미리보기 우선 (TinyCorn Studio 표준)
규칙: 모든 UI 변경 사항은 코드에 적용되기 전에 사용자가 미리보기 이미지를 승인해야 합니다.
워크플로우
-
오프스크린 렌더링 스크립트 작성
tools/preview_<이름>.swift— 앱을 실행할 필요 없이, 2x PNG로 직접 렌더링합니다. -
실행 후 사용자에게 이미지 열기:
swift tools/preview_<이름>.swift ~/Desktop/<App>-<이름>-Preview.png open ~/Desktop/<App>-<이름>-Preview.png -
승인 요청 — 여러 디자인 선택지가 있을 경우, 2~4개의 방안을 나란히 렌더링하고 사용자에게 선택(A/B/C/D)을 요청합니다.
-
사용자가 OK한 후에만 적용. 미리보기 스크립트를 feature와 함께 커밋합니다.
-
사용자가 수정을 요청하면: 스크립트를 수정하고, 다시 렌더링하며, 승인될 때까지 반복합니다.
코드 스켈레톤 (SwiftUI, macOS)
import AppKit
import SwiftUI
struct PreviewRoot: View {
var body: some View {
// 앱의 실제 컴포넌트/색상을 사용하여 UI를 모의 구성
VStack { /* ... */ }
.frame(width: 900, height: 600)
.background(Color(nsColor: .windowBackgroundColor))
}
}
let host = NSHostingView(rootView: PreviewRoot())
host.frame = NSRect(x: 0, y: 0, width: 900, height: 600)
host.layoutSubtreeIfNeeded()
let rep = host.bitmapImageRepForCachingDisplay(in: host.bounds)!
host.cacheDisplay(in: host.bounds, to: rep)
let png = rep.representation(using: .png, properties: [:])!
let out = CommandLine.arguments.count > 1
? CommandLine.arguments[1]
: "preview.png"
try! png.write(to: URL(fileURLWithPath: out))
print("Wrote \(out)")
주의사항 (실제 경험담)
- SwiftUI modifier/뷰와 이름이 겹치는 전역 변수명을 사용하지 마세요 (예:
border) → 오버로드 오류 발생.borderColor를 사용하세요. - 여러 메서드에서 공통으로 사용하는 헤더는 같은 파일 내 다른 곳에서 중복 선언하지 마세요.
- 2x 렌더링: 위 코드처럼
rep.size를 설정하거나bitmapImageRepForCachingDisplay+cacheDisplay를 사용하세요. 캐시 전에layoutSubtreeIfNeeded()를 잊지 마세요. - 앱을 캡처하기 위해
screencapture/AppleScript를 사용하지 마세요 — 머신에 Screen Recording 권한이 없는 경우가 많습니다(오류-1743). 오프스크린 렌더링이 유일하게 확실한 방법입니다. - 다크 테마 앱: 실제와 일치하는 미리보기를 위해 앱의
Color(nsColor:)및 asset을 동일하게 사용하세요. - 이미지는 사용자가 쉽게 찾을 수 있도록
~/Desktop/에 저장하고, 파일명은<App>-<Feature>-Preview.png형식으로 지정하세요.
SwiftUI 외의 경우
- 웹: 브라우저에서 HTML 목업을 연 후 캡처하거나, Playwright를 사용하여 스크린샷을 찍으세요.
- CLI/TUI: 터미널에서 ANSI를 사용하여 렌더링하거나, 색상이 있는 텍스트로 설명하세요.
변하지 않는 일반 원칙: 이미지 먼저, 코드 나중, 사용자 승인 후 적용.