728x90

"맥북 한 대를 빌드 서버로 책상 밑에 두는 것"만큼 인프라 담당자 속을 긁는 게 없다. 랙에 못 넣고, 이미지 관리가 안 되고, OS 업데이트 한 번에 파이프라인이 통째로 멎는다. 그래서 주기적으로 "Linux 러너에서 macOS 툴체인 못 돌리나?"라는 질문이 올라오고, 그 답변 후보 중 하나가 Darling이다.

1. 왜 지금 다시 Darling 이야기가 나오나

Darling은 하드웨어 에뮬레이션 없이 macOS(Darwin) 바이너리를 Linux에서 직접 실행하는 변환 계층이다. QEMU처럼 CPU를 흉내내는 게 아니라, x86_64 명령어는 그대로 호스트 CPU에서 돌리고 OS 경계에서만 번역한다. 포지션은 Wine과 같다. Wine이 Windows에 대해 하는 일을 Darling이 macOS에 대해 하는 것이고, 프로젝트 스스로도 그렇게 설명한다.

실무에서 이 주제가 반복해서 소환되는 이유는 명확하다.

  • iOS/macOS 빌드 때문에 CI 풀 전체가 이원화된다. Linux 러너는 Kubernetes로 오토스케일하는데, macOS 러너만 고정 대수 + 수동 프로비저닝.
  • 클라우드 macOS 인스턴스는 비싸고, 전용 호스트 기반이라 최소 할당 시간 같은 제약이 붙는다(정확한 조건은 각 클라우드 공식 문서 확인 필요).
  • Apple 실리콘 전환 이후 x86 맥 미니 재고로 버티던 조직들이 하드웨어 교체 압박을 받고 있다.

결론부터 말하면 Darling으로 Xcode 빌드 파이프라인을 대체하는 건 현재로선 불가능하다. 다만 "왜 불가능한가"를 이해하면 대안 설계가 훨씬 정확해진다. 그리고 CLI 도구 수준에서는 실제로 쓸 구석이 있다.

2. 동작 원리 — Wine과 결정적으로 다른 지점

바이너리 포맷부터 다르다

Linux는 ELF를 로드한다. macOS 바이너리는 Mach-O다. 커널의 binfmt 핸들러가 Mach-O를 모르니, Darling은 자체 로더(ELF로 빌드된 실행 파일)를 먼저 띄우고 그 프로세스 주소 공간에 Mach-O 이미지와 Darling판 dyld를 매핑한다. 그다음부터는 dyld가 /System/Library/Frameworks/... 경로의 dylib들을 링크한다. 여기서 dyld는 Apple 것이 아니라 Darling이 재구현/포팅한 것이다.

Wine은 API 레벨, Darling은 syscall 레벨까지 내려간다

이 차이가 핵심이다. Windows는 syscall 번호가 버전마다 바뀌는 비공개 인터페이스라, 정상적인 Windows 앱은 절대 syscall을 직접 호출하지 않고 kernel32.dll 같은 DLL을 거친다. 그래서 Wine은 DLL을 갈아끼우는 것만으로 대부분 해결된다.

macOS도 공식적으로는 "libSystem을 통해서만 호출하라"고 하지만, 실제 바이너리는 libsystem_kernel.dylib를 통해 BSD syscall과 Mach trap을 직접 때린다. Mach 포트, IPC, 태스크/스레드 추상화는 Linux에 대응물이 없다. 그래서 Darling은 다음을 다 만들어야 한다.

  • libsystem_kernel 대체 — Darwin syscall을 Linux syscall로 번역
  • Mach IPC / 포트 서브시스템
  • launchd, notifyd 같은 Darwin 데몬
  • Foundation, CoreFoundation 등 프레임워크 (상당 부분 오픈소스 Darwin 코드 기반)

Mach IPC 때문에 Darling은 오랫동안 커널 모듈을 요구했고, 이후 이 의존을 줄이려는 방향의 작업이 진행돼 왔다. 현재 릴리스에서 커널 모듈이 필수인지 여부는 버전에 따라 다르므로 공식 문서 확인 필요다. 인프라 관점에서는 이 한 줄이 도입 가능성을 좌우한다. 커널 모듈이 필요하면 관리형 Kubernetes 노드나 대부분의 SaaS CI에서는 그냥 끝이다.

prefix 개념

Wine에 ~/.wine이 있듯 Darling에는 ~/.darling 프리픽스가 있고, 그 안에 Darwin 파일시스템 레이아웃(/System, /usr/lib, /Users)이 구성된다. 마운트 네임스페이스와 오버레이를 써서 호스트 파일시스템 위에 Darwin 뷰를 씌우는 구조다. 컨테이너 안에서 굴릴 때 권한 문제가 터지는 이유가 바로 여기다.

3. 실무 관점 — 확인 명령, 흔한 함정, 대안

먼저 확인할 것: 내 바이너리가 애초에 후보인가

Darling 셸에 들어가기 전에 호스트 Linux에서 아키텍처와 의존성부터 본다. Apple 실리콘용 arm64 전용 바이너리라면 x86_64 Linux에서는 시작조차 못 한다.

$ file ./MyTool
./MyTool: Mach-O 64-bit x86_64 executable, flags:<NOUNDEFS|DYLDLINK|PIE>

$ file ./MyToolUniversal
./MyToolUniversal: Mach-O universal binary with 2 architectures:
  [x86_64:Mach-O 64-bit x86_64 executable]
  [arm64:Mach-O 64-bit arm64 executable]

$ darling shell
Darwin ... x86_64
bash-3.2$ otool -L /Users/me/MyTool
/Users/me/MyTool:
  /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, ...)
  /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation

(uname/버전 문자열은 Darling 빌드 시점에 따라 달라진다.) otool -L에 나온 프레임워크 목록이 사실상 성공 확률표다. Foundation/CoreFoundation 수준이면 해볼 만하고, AppKit·Metal·Security 같은 게 줄줄이 나오면 접는 게 낫다.

CI에 넣는다면 스모크 테스트를 반드시 앞단에

Darling은 "일부는 되고 일부는 안 되는" 계층이다. 파이프라인 중간에서 죽으면 원인 파악에 하루가 날아간다. job 맨 앞에 5초짜리 게이트를 둔다.

#!/usr/bin/env bash
# darling-smoke.sh — 파이프라인 최상단에서 실행
set -euo pipefail

command -v darling >/dev/null || { echo "darling 미설치"; exit 1; }

# 1) 런타임이 살아있는지
darling shell true || { echo "darling 런타임 기동 실패"; exit 1; }

# 2) 대상 바이너리 아키텍처 확인
file "$1" | grep -q 'x86_64' || { echo "x86_64 슬라이스 없음: $1"; exit 1; }

# 3) 실제 링크 가능한지 (여기서 dyld 에러가 잡힌다)
if ! darling shell otool -L "$1" >/dev/null 2>&1; then
  echo "의존성 확인 실패 — 미구현 프레임워크 가능성"; exit 1
fi
echo "smoke ok"

흔한 함정 1 — dyld image not found

가장 자주 보게 될 실패다. 해당 프레임워크가 Darling에 아직 없거나, 스텁만 있고 심볼이 비어 있는 경우다.

$ darling shell ./MyTool
dyld: Library not loaded: /System/Library/Frameworks/CoreServices.framework/Versions/A/CoreServices
  Referenced from: /Users/me/MyTool
  Reason: image not found
Trace/breakpoint trap

해결책이랄 게 별로 없다. macOS 실기에서 dylib를 복사해 넣는 방법이 떠오르겠지만, Apple 프레임워크 재배포는 라이선스 문제라 사내 CI 이미지에 굽는 순간 법무 이슈가 된다. 실무적으로는 그 기능을 쓰지 않는 빌드 옵션을 찾거나 포기하는 쪽이 빠르다.

흔한 함정 2 — 컨테이너 안에서 Bad system call

Docker 기본 seccomp 프로파일은 흔치 않은 syscall과 일부 네임스페이스 조작을 막는다. Darling은 프리픽스를 만들 때 마운트 네임스페이스를 쓰기 때문에 정면으로 충돌한다.

$ docker run --rm -it my/darling:latest darling shell uname -a
Bad system call (core dumped)

또는 이런 계열도 본다.

mount: permission denied
darling: failed to set up prefix at /root/.darling

완화 방법은 --security-opt seccomp=unconfined, --cap-add SYS_ADMIN, 최악의 경우 --privileged다. 문제는 이게 "CI 러너 컨테이너에 특권을 준다"는 뜻이라는 것. 멀티테넌트 CI에서는 보안팀이 승인 안 해준다. 전용 노드풀 + taint로 격리하는 정도가 현실적 타협선인데, 그 시점이면 "그냥 mac mini 한 대 두는 것"과 운영 부담을 비교해봐야 한다. 정확히 어떤 capability가 필요한지는 버전에 따라 다르므로 공식 문서 확인 필요.

흔한 함정 3 — 결국 Xcode가 필요하다

iOS 앱 빌드는 xcodebuild, 코드 서명은 codesign과 키체인, 배포는 공증(notarization)까지 엮인다. Xcode는 Darling에서 돌지 않고, 배포도 못 한다. 즉 "Linux에서 iOS 앱 CI"라는 원래 목표에는 Darling이 답이 아니다.

bash-3.2$ xcodebuild -version
bash: xcodebuild: command not found

그럼 뭘 쓰나

  • osxcross: Linux에서 clang으로 macOS 타깃 크로스컴파일. 실행이 아니라 빌드가 목적이면 이쪽이 훨씬 실용적이다. 단 macOS SDK를 Xcode에서 추출해야 하므로 라이선스 검토는 똑같이 필요하다.
  • zig cc: 간단한 C/C++ 프로젝트라면 크로스 타깃 지정만으로 macOS 바이너리를 뽑을 수 있어 세팅이 가볍다.
  • 실제 macOS 러너 + 역할 분리: 가장 현실적. 유닛 테스트·정적 분석·의존성 캐시 같은 플랫폼 무관 단계는 Linux에서 돌리고, macOS 러너는 컴파일·서명·공증만 담당하게 줄인다. macOS 러너 점유 시간을 절반 이하로 줄이는 게 비용 관점에서 Darling 도입보다 확실한 성과다.

반대로 Darling이 실제로 값어치를 하는 구간도 있다. macOS용 CLI 유틸리티를 만드는 팀이 PR 단계 스모크 테스트(바이너리가 뜨는지, --version이 나오는지)를 Linux 러너에서 저렴하게 돌리는 용도, 그리고 Mach-O 로딩이나 dyld 동작을 뜯어보는 리버싱·학습 환경이다.

4. 정리

한 줄 요약: Darling은 "macOS용 Wine"이지만, Wine보다 훨씬 아래(Mach trap·syscall)까지 재구현해야 해서 커버리지가 좁다. CLI 도구 실행에는 쓸 만하고, Xcode 기반 iOS 파이프라인 대체용으로는 아직 아니다.

  • 써볼 만한 경우: macOS용 CLI 바이너리의 가벼운 실행 검증, Mach-O/dyld 학습과 리버싱, GUI 없는 오픈소스 도구의 크로스 플랫폼 확인.
  • 쓰면 안 되는 경우: 프로덕션 iOS/macOS 앱 빌드·서명·배포, GUI 앱 테스트, 특권 컨테이너를 허용할 수 없는 멀티테넌트 CI.
  • 도입 전 체크리스트: ① 현재 버전이 커널 모듈을 요구하는가 ② 대상 바이너리에 x86_64 슬라이스가 있는가 ③ otool -L 의존성이 Foundation 수준에서 끝나는가 ④ Apple 프레임워크/SDK 취급에 대한 법무 검토가 끝났는가.

개인적으로는 "Linux에서 macOS를 돌린다"보다 "macOS 러너가 꼭 해야 하는 일만 남기고 다 걷어낸다"가 인프라팀이 먼저 손대야 할 문제라고 본다. 그래도 Darling 같은 프로젝트가 살아있는 건 반갑다. 언젠가 Wine처럼 "대부분 그냥 된다"가 되면, 그때는 정말 판이 바뀐다.

참고 자료

728x90
728x90

도입: 시뮬레이터가 아니라 "진짜 iOS"가 뜬다

모바일 앱 CI 파이프라인 붙여본 사람은 다 아는 통증이 있다. Xcode Simulator는 x86/arm64 macOS 바이너리를 돌리는 물건이라 진짜 iOS 커널이 아니다. 그래서 코드사이닝, 키체인, 백그라운드 태스크, 디바이스 특성 타는 SDK(결제, 지문/FaceID, DRM)는 시뮬레이터에서 재현이 안 된다. 결국 랙에 실기기를 꽂아놓고 USB 허브와 씨름하게 된다. 나도 사무실 구석에 아이폰 8대 물린 맥미니를 몇 년 굴려봤는데, 케이블 접촉 불량과 배터리 스웰링이 진짜 장애 원인 1, 2위였다.

vphone-cli가 HN 상위에 오르면서 9천 스타를 넘긴 이유가 이거다. Apple이 macOS에 넣어둔 Virtualization.framework — 원래 macOS/Linux 게스트 부팅용으로 공개된 그 프레임워크 — 안에 Apple의 PCC(Private Cloud Compute) 리서치 VM 인프라 경로가 있고, 이걸 타고 실제 iPhone IPSW를 게스트로 부팅시킨다. 시뮬레이터가 아니라 iOS 커널이 뜨고, SSH가 붙고, VNC로 화면이 나온다.

brew install zqxwce/tap/vphone-cli

# 다운로드 → 패치 → DFU 복원 → CFW 설치 → 첫 부팅까지 한 방
vphone-cli vm create myphone -V jb
vphone-cli vm launch myphone

핵심: PCC 리서치 VM이라는 뒷문과 "패치 개수"의 의미

동작을 거칠게 비유하면 이렇다. Apple은 PCC 보안 검증을 위해 연구자에게 "제한된 Apple OS를 VM으로 띄워 뜯어보라"는 경로를 열어뒀다. 이때 하이퍼바이저 쪽에 PV=3 같은 사적(private) entitlement가 필요하고, 이건 원래 Apple이 서명한 바이너리만 쓸 수 있다. vphone-cli는 이 문을 열기 위해 호스트에서 SIP/AMFI를 완화하고, 서명 안 된 자기 바이너리에 그 entitlement를 얹는다. 즉 게스트가 아니라 호스트의 보안 정책을 먼저 깎는 구조다. 이 지점이 실무 도입의 핵심 판단 포인트다.

그 다음이 게스트 쪽. 그냥 iPhone IPSW를 던진다고 부팅되지 않는다. iBoot → 커널 → AMFI/SSV(서명된 시스템 볼륨)/Img4/TXM으로 이어지는 부트체인 검증이 VM 환경을 거부한다. 그래서 도구가 IPSW를 받아 부트체인 바이너리를 패치하고, DFU 모드로 띄운 뒤 SHSH를 받아 복원하고, CFW(커스텀 펌웨어)를 호스트 마운트로 밀어 넣는다. README의 수동 절차를 보면 파이프라인이 그대로 드러난다.

vphone-cli vm new myphone                          # 1. 빈 번들
vphone-cli fw prepare myphone --iphone-version 26.1 # 2. IPSW 다운로드 + 병합
vphone-cli fw patch myphone --variant jb            # 3. 부트체인 패치
vphone-cli vm launch myphone --dfu                  # 4. DFU 부팅(백그라운드)
vphone-cli restore myphone --get-shsh                # 5. SHSH 획득
vphone-cli restore myphone                           #    DFU 복원
vphone-cli vm stop myphone
vphone-cli cfw install myphone --variant jb          # 6. CFW 설치 (sudo 요구)
vphone-cli vm launch myphone                         # 7. 첫 부팅

재밌는 건 --variant다. 보안 우회 강도를 5단계로 나눠놨고, README에 패치 개수와 CFW 단계 수가 명시돼 있다.

  • less — 4패치 / 2단계. 패치리스에 가깝고 iOS 완화 기능 대부분 유지
  • regular — 42패치 / 10단계. AMFI/SSV/Img4/TXM 우회
  • dev — 53패치 / 12단계. TXM entitlement·디버그 우회 추가
  • jb — 113패치 / 14단계. 풀 탈옥, 첫 부팅 시 Sileo·TrollStore 자동 설치
  • exp — 141패치 / 18단계. jb 상위집합 + 안티-VM-디텍션 리서치 패치

이 숫자를 "많을수록 좋다"로 읽으면 안 된다. 패치를 많이 할수록 부팅한 iOS는 실기기와 멀어진다. 앱 동작 검증이 목적이면 오히려 lessregular가 진실에 가깝고, 내부를 뜯어보거나 파일시스템에 자유롭게 접근해야 하면 jb가 편하다. 안티-VM-디텍션이 붙은 exp는 이름 그대로 리서치용이다.

실무 관점: 도입 전에 계산해야 할 비용과 함정

1) 호스트 보안을 깎는다 = CI 러너로 쓰기 곤란하다

README가 제시하는 두 가지 길 중 A안은 SIP를 완전히 끄고 AMFI까지 부트아그로 무력화한다.

# 복구 모드 터미널
csrutil disable
csrutil allow-research-guests enable

# macOS 재부팅 후
sudo nvram boot-args="amfi_get_out_of_my_way=1 -v"

이 상태 머신을 사내 CI 러너로 쓰겠다는 건, 서명 검증이 꺼진 맥에 사내 코드사이닝 인증서와 App Store Connect API 키를 올려놓겠다는 뜻이다. 보안팀이 있는 조직이라면 그대로는 못 통과한다. B안(csrutil enable --without debug + vphone-amfidont로 해당 바이너리만 허용)이 그나마 낫지만, "debug 제한만 뺀 SIP"도 완전한 SIP는 아니다. 현실적인 타협은 격리 VLAN에 물린 전용 맥, 시크릿 없이, 결과물만 아티팩트로 뽑아내는 구조다.

2) 중첩 가상화 불가 — 클라우드 맥 대부분 탈락

이게 제일 크다. 실무에서 맥 인프라는 보통 클라우드 맥(호스팅 업체의 가상 맥)으로 굴리는데, 그런 환경에서는 아예 뜨지 않는다.

Virtualization is not available on this hardware

README FAQ가 명확히 짚는다. 호스트 맥 자체가 VM이면 PV=3 게스트 부팅을 중첩할 수 없다. 즉 비중첩 물리 Apple Silicon 맥 + macOS 15(Sequoia) 이상이 최소 조건이다. 베어메탈 맥미니를 새로 사거나, 물리 맥을 제공하는 업체를 찾아야 한다. 도입 검토서 첫 줄에 이 문장을 박아두는 게 좋다.

3) 실제로 만나는 에러들

가장 흔한 첫 관문은 이거다. 빌드도 됐고 실행했는데 그냥 죽는다.

$ ./vphone-cli --help
zsh: killed     ./vphone-cli

커널이 SIGKILL을 쐈다는 뜻이고, 원인은 십중팔구 AMFI다. amfi_get_out_of_my_way=1 부트아그가 안 먹었거나(=SIP가 완전히 꺼져 있지 않았거나), vphone-amfidont로 allowlist를 안 걸었거나. 특히 A안에서 csrutil disable 없이 nvram만 건드리면 부트아그 자체가 무시된다. nvram boot-args로 값이 남아 있는지, csrutil status로 SIP 상태가 어떤지 둘 다 확인해야 한다.

다음으로 삽질 유발 1위는 cfw install이 시스템 바이너리 재서명 중에 멈추면서 메모리가 무한정 올라가는 현상이다. README에 원인이 꽤 상세히 적혀 있는데, ldid-procursus 2.1.5-procursus7까지의 버그다. bytes(uint64_t)__builtin_clzll(0)을 제로 가드 없이 호출해 UB가 되고, 그 결과 길이가 0으로 잡히면서 unsigned 루프 카운터가 언더플로 → 1바이트씩 무한히 버퍼에 쓰며 종료하지 않는다. entitlements plist에 정수 값 0이 들어 있는 Apple 시스템 바이너리를 만나면 터진다. 대응은 이렇다.

sudo kill -9 <hung ldid pid>      # 이미 걸렸으면 먼저 죽이고
brew install --HEAD ldid-procursus
brew link --overwrite ldid-procursus

업스트림에서 고쳐졌지만 아직 태그된 릴리스에 안 들어가서, Homebrew stable을 쓰면 그대로 밟는다. 나 같으면 사내 프로비저닝 스크립트에 이 --HEAD 설치를 아예 박아두겠다.

그 밖에 실제로 자주 걸리는 것들:

  • "Press home to continue"에서 멈춤 — VNC(vnc://vm-ip:5901)로 붙어서 우클릭(두 손가락 클릭)이 홈 버튼이다. 하드웨어 버튼이 없으니 당연한데 모르면 5분 날린다.
  • 시스템 앱이 설치되지 않음 — iOS 초기 설정에서 지역을 일본이나 EU로 고르면 안 된다. VM이 만족시킬 수 없는 규제 검사가 추가로 붙는다. 미국 같은 지역을 선택하라는 게 README 권고다. 이거 모르고 "한국 옆이니까 일본" 골랐다가 재설치하는 사람 나온다.
  • 앱이 EXC_GUARD / GUARD_TYPE_MACH_PORT로 크래시vphone-cli fw patch <name> --variant <v> --force-exc-guard로 다시 패치한 뒤 재복원·재설치. iOS 18 베이스에서는 항상 켜야 한다고 명시돼 있다.

4) 그래도 매력적인 지점: 자동화 소켓

실무자 입장에서 제일 눈이 간 건 사실 이 부분이다. VM 번들 안에 vphone.sock 호스트 컨트롤 소켓이 있어서 스크린샷·터치·스와이프·하드웨어 키·클립보드를 프로그래밍으로 조작할 수 있고, 각 액션이 스크린샷을 인라인으로 반환한다. AI 기반 E2E 테스트를 염두에 둔 설계고, 이를 MCP 서버로 감싼 vphone-mcp도 같이 언급돼 있다.

실기기 팜에서 이걸 하려면 WebDriverAgent 띄우고 USB 세션 관리하고 스크린샷 파이프 뚫느라 인프라가 꽤 나온다. VM이면 vm clone이 APFS 클론이라 빠르고 디바이스 아이덴티티도 새로 발급된다. 테스트 케이스마다 깨끗한 기기를 몇 초 만에 찍어내는 그림은 확실히 탐난다.

vphone-cli vm clone myphone myphone-2   # APFS 클론 + 새 디바이스 아이덴티티
vphone-cli vm list --json               # 스크립팅용
vphone-cli vm export myphone --out ./backup/   # zstd, 기본 fast (--max = xz -9)

5) 트레이드오프와 대안

정리하면 이렇다. 얻는 것은 진짜 iOS 커널, 스냅샷/클론, 케이블 없는 팜, 루트 셸(ssh -p 22222 mobile@<vm-ip>, 비밀번호 alpine). 내주는 것은 호스트 보안 정책, 물리 맥 하드웨어 비용, 그리고 법적·정책적 리스크다. IPSW와 iOS는 Apple EULA 대상이고, 이 도구는 부트체인 패치와 탈옥을 포함한다. 회사 제품 테스트 인프라로 상용 도입하겠다면 법무 검토 없이는 못 간다. 개인 리서치나 보안 분석 목적과, 사내 CI 표준화는 완전히 다른 문제다.

대안은 여전히 셋이다. (1) 순수 기능 검증은 Xcode Simulator + XCUITest, (2) 실기기 커버리지는 클라우드 디바이스 팜(BrowserStack, Firebase Test Lab 계열) 또는 자체 실기기 팜, (3) 보안 리서치·펌웨어 분석이면 vphone-cli. 세 번째 칸의 대안이 사실상 없었다는 게 이 프로젝트의 진짜 가치다.

정리

한 줄로: Apple의 PCC 리서치 VM 경로를 이용해 물리 Apple Silicon 맥 위에 실제 iPhone 펌웨어를 부팅시키는 도구이고, 그 대가로 호스트의 SIP/AMFI를 깎는다.

  • 지금 써볼 사람 — iOS 보안 리서처, 펌웨어/커널 분석가, 탈옥 생태계 도구 개발자, "실기기 없이 iOS 내부를 뜯어보고 싶다"는 사람. 여기엔 대체재가 사실상 없다.
  • 관망할 사람 — 앱 CI/CD 담당자. 아이디어는 매력적이지만 SIP off 머신을 파이프라인에 넣는 순간 보안 리스크가 이득을 넘는다. 자동화 소켓 + MCP 조합이 어떻게 성숙하는지 6개월쯤 지켜볼 가치는 충분하다.
  • 쓰면 안 되는 사람 — 클라우드 맥(중첩 VM)만 있는 팀. 하드웨어부터 다시 사야 한다.

테스트된 조합도 README에 표로 정리돼 있는데, 호스트 macOS 26대, 게스트 iPhone 펌웨어 18.6.2부터 27.0 베타대까지 꽤 넓다. 다만 표에 없는 조합은 언제든 깨질 수 있는 성격의 프로젝트로 보이니, 실제 검증 전에 Tested Environments 표부터 확인하는 게 맞다.

참고 자료

728x90

+ Recent posts