웹 어셈블리용 Qt

웹어셈블리용 Qt를 사용하면 웹에서 Qt 애플리케이션을 실행할 수 있습니다.

WebAssembly(약칭 Wasm)는 웹 브라우저와 같은 가상 머신에서 실행하기 위한 바이너리 명령어 형식입니다.

웹어셈블리용 Qt를 사용하면 브라우저 샌드박스에서 실행되는 웹 애플리케이션으로 애플리케이션을 배포할 수 있습니다. 이 접근 방식은 호스트 디바이스 기능에 대한 전체 액세스 권한이 필요하지 않은 웹 분산 애플리케이션에 적합합니다.

참고: 웹 어셈블리용 Qt는 지원되는 플랫폼이지만 일부 모듈은 아직 지원되지 않거나 테크 프리뷰에 있습니다. 지원되는 Qt 모듈을 참조하십시오.

웹어셈블리용 Qt 시작하기

WebAssembly용 Qt 애플리케이션 빌드는 다른 플랫폼용 Qt 빌드와 유사합니다. SDK(엠스크립텐)를 설치하고, Qt를 설치(또는 소스에서 Qt 빌드)한 다음, 마지막으로 애플리케이션을 빌드해야 합니다. 몇 가지 차이점이 있는데, 예를 들어 WebAssembly용 Qt는 다른 Qt 빌드보다 더 적은 수의 모듈과 더 적은 기능을 지원합니다.

엠스크립트 설치하기

엠스크립텐은 웹어셈블리 컴파일을 위한 툴체인입니다. 이를 사용하면 브라우저 플러그인 없이도 웹에서 네이티브에 가까운 속도로 Qt를 실행할 수 있습니다.

엠스크립텐 SDK 설치에 대한 자세한 내용은 엠스크립텐 설명서를 참조하세요.

설치가 완료되면 경로에 Emscripten 컴파일러가 있어야 합니다. 다음 명령으로 이를 확인하세요:

em++ --version

Qt의 각 부 버전은 패치 릴리스에서 변경되지 않는 특정 Emscripten 버전을 대상으로 합니다. Qt의 바이너리 패키지는 대상 Emscripten 버전을 사용하여 빌드됩니다. Emscripten은 버전 간 ABI 호환성을 보장하지 않으므로 애플리케이션은 동일한 버전을 사용해야 합니다.

엠스크립텐 버전은 다음과 같습니다:

  • Qt 6.2: 2.0.14
  • Qt 6.3: 3.0.0
  • Qt 6.4: 3.1.14
  • Qt 6.5: 3.1.25
  • Qt 6.6: 3.1.37
  • Qt 6.7: 3.1.50
  • Qt 6.8: 3.1.56

특정 Emscripten 버전을 설치하려면 emsdk 을 사용하세요. 예를 들어 Qt 6.8용으로 설치하려면 다음과 같이 입력합니다:

  • ./emsdk 설치 3.1.56
  • ./emsdk 활성화 3.1.56

Windows에서는 설치 후 경로에 엠스크립텐이 포함됩니다. macOS 또는 Linux에서는 다음과 같이 경로에 추가해야 합니다:

source /path/to/emsdk/emsdk_env.sh

다음 명령으로 이를 확인합니다:

em++ --version

엠스크립텐 버전을 선택할 때 더 많은 유연성이 필요한 경우 소스에서 Qt를 빌드할 수 있습니다. 이 경우 위의 버전은 최소 버전입니다. 이후 버전은 작동할 것으로 예상되지만 Qt를 변경해야 하는 동작 변경이 발생할 수 있습니다.

Qt 설치하기

Qt 계정의 다운로드 섹션에서 Qt를 다운로드하세요. 개발 플랫폼으로 Linux, macOS 및 Windows용 빌드를 제공합니다.

바이너리 빌드는 가능한 한 많은 브라우저에서 실행되도록 설계되었으며 싱글 스레드 및 멀티 스레드 버전으로 제공됩니다. Wasm SIMDWasm exceptions 같은 비표준 기능은 바이너리 빌드에서 지원되지 않습니다.

소스에서 Qt 빌드하기

소스에서 빌드하면 스레드 지원, Qt OpenGL ES 레벨 또는 SIMD 지원과 같은 Qt 구성 옵션을 설정할 수 있습니다. Qt 계정의 다운로드 섹션에서 Qt 소스를 다운로드하세요.

Qt를 wasm-emscripten 플랫폼용 크로스 컴파일 빌드로 구성합니다. 이렇게 하면 -static, -no-feature-thread, -no-make examples 구성 옵션이 설정됩니다. -feature-thread , 구성 옵션으로 스레드 지원을 활성화할 수 있습니다. 공유 라이브러리 빌드는 지원되지 않습니다.

동일한 버전의 Qt 호스트 빌드가 필요하며 QT_HOST_PATH CMake 변수에서 또는 -qt-host-path configure 인수를 사용하여 해당 경로를 지정합니다.

./configure -qt-host-path /path/to/Qt -platform wasm-emscripten -prefix $PWD/qtbase

참고: ninja 실행 파일을 사용할 수 있는 경우 configure는 항상 Ninja 생성기 및 빌드 도구를 사용합니다. Ninja는 크로스 플랫폼이며 기능이 풍부하고 성능이 뛰어나며 모든 플랫폼에서 사용할 것을 권장합니다. 다른 생성기를 사용할 수도 있지만 공식적으로 지원되지는 않습니다.

Windows의 경우 PATH 에 Mingw-w64가 있는지 확인하고 다음을 사용하여 구성합니다:

configure -qt-host-path C:\Path\to\Qt -no-warnings-are-errors -platform wasm-emscripten -prefix %CD%\qtbase

그런 다음 필요한 모듈을 빌드합니다:

cmake --build . -t qtbase -t qtdeclarative [-t another_module]

명령줄에서 애플리케이션 빌드하기

웹어셈블리용 Qt는 qmake와 make, 또는 ninja 또는 make와 함께 CMake를 사용하여 애플리케이션을 빌드할 수 있습니다.

$ /path/to/qt-wasm/qtbase/bin/qt-cmake .
$ cmake --build .

참고: 바닐라 CMake (Linux의 qt-cmake 또는 Windows의 qt-cmake.bat )를 사용할 때는 다른 크로스 플랫폼 빌드와 마찬가지로 "-DCMAKE_TOOLCHAIN_FILE"로 툴체인 파일을 지정해야 합니다. 자세한 내용은 여기를 참조하세요: CMake 시작하기.

애플리케이션을 빌드하면 애플리케이션과 Qt 코드(정적으로 링크됨)가 포함된 .wasm 파일, 브라우저에서 열어 애플리케이션을 실행할 수 있는 .html 파일 등 여러 출력 파일이 생성됩니다.

참고: 엠스크립텐은 "-g" 디버그 수준에서 비교적 큰 .wasm 파일을 생성합니다. 디버그 빌드의 경우 "-g2"로 링크하는 것을 고려하세요.

애플리케이션 실행

애플리케이션을 실행하려면 웹 서버가 필요합니다. 빌드 출력 파일은 모두 정적 콘텐츠이므로 모든 웹 서버에서 사용할 수 있습니다. 일부 사용 사례에서는 멀티스레딩 지원을 활성화하는 데 필요한 https 인증서 제공 또는 http 헤더 설정과 같은 특별한 서버 구성이 필요할 수 있습니다.

Emrun

엠스크립텐은 애플리케이션을 테스트 실행할 수 있는 엠런 유틸리티를 제공합니다. Emrun은 웹 서버를 시작하고 브라우저를 실행하며 (일반적으로 JavaScript 콘솔로 이동하는) stdout/stderr도 캡처하여 전달합니다.

/path/to/emscripten/emrun --browser=firefox appname.html

Python http.server

또 다른 옵션은 개발 웹 서버를 시작한 다음 웹 브라우저를 별도로 실행하는 것입니다. 가장 간단한 옵션 중 하나는 Python의 http.server입니다:

python -m http.server

이는 단순한 웹 서버일 뿐이며 아래에 언급된 필수 COOP 및 COED 헤더가 전송되지 않으므로 스레딩에 필요한 SharedArrayBuffer를 지원하지 않습니다.

qtwasmserver

Qt는 mkcert를 사용하여 https 인증서를 생성하는 개발자 웹 서버를 제공합니다. 이를 통해 보안 컨텍스트가 필요한 웹 기능을 테스트할 수 있습니다. http://localhost 을 통한 전송도 인증서 없이도 안전한 것으로 간주됩니다.

또한 웹 서버는 COOPCOEP 헤더를 SharedArrayBuffer 및 멀티스레딩을 지원할 수 있는 값으로 설정합니다.

qtwasmserver 스크립트는 기본적으로 로컬 호스트에 바인딩되는 하나의 서버를 시작합니다. 명령줄 인수 -를 사용하여 주소를 추가하거나 --all을 사용하여 사용 가능한 모든 주소에 바인딩할 수 있습니다.

python /path/to/qtbase/util/wasm/qtwasmserver/qtwasmserver.py --all

다음을 사용하여 애플리케이션 빌드 Qt Creator

WebAssembly용 Qt Creator 설정하기.

웹에 애플리케이션 배포하기

애플리케이션을 빌드하면 여러 개의 파일이 생성됩니다(다음 표에서 "앱"을 애플리케이션 이름으로 대체).

생성된 파일간단한 설명
app.htmlHTML 컨테이너
qtloader.jsQt 애플리케이션 로딩을 위한 자바스크립트 API
app.js엠스크립텐에서 생성된 자바스크립트 런타임
app.wasm앱 바이너리

app.html을 그대로 배포하거나 사용자 지정 HTML 파일 대신 삭제할 수 있습니다. 스플래시 화면 이미지를 Qt 로고에서 앱 로고로 변경하는 등의 작은 조정도 가능합니다. 두 경우 모두 qtloader.js는 애플리케이션 로딩을 위한 JavaScript API를 제공합니다.

다른 도구보다 압축률이 더 높으므로 배포하기 전에 gzip 또는 brotli 을 사용하여 Wasm 파일을 압축하세요. 자세한 내용은 바이너리 크기 최소화를 참조하세요.

멀티스레딩 및 SIMD와 같은 특정 기능을 활성화하면 활성화된 기능을 지원하지 않는 브라우저와 호환되지 않는 .wasm 바이너리가 생성됩니다. 여러 개의 .wasm 파일을 빌드한 다음 JavaScript 기능 감지를 사용하여 올바른 파일을 선택하면 이 제한을 해결할 수 있지만, Qt는 이를 위한 기능을 제공하지 않는다는 점에 유의하세요.

qtloader 사용

Qt는 웹 어셈블리 애플리케이션용 Qt를 다운로드, 컴파일 및 인스턴스화하기 위한 JavaScript API를 제공합니다. 이 로딩 API는 Emscripten에서 제공하는 로딩 기능을 래핑하고 Qt 기반 애플리케이션에 유용한 추가 기능을 제공합니다. 이는 qtloader.js 파일에 구현되어 있습니다. 이 파일의 복사본은 빌드 시 빌드 디렉토리에 기록됩니다.

일반적인 사용법은 다음과 같습니다:

const app_container_element = ...;
const instance = await qtLoad({
    qt: {
        containerElements: [ app_container_element ],
        onLoaded: () => { /* handle application load completed */  },
        onExit: () => {  /* handle application exit */ },
    }
});

이 코드는 구성 객체를 사용하여 qtLoad() 로더 함수를 호출합니다. 이 구성 객체에는 특수한 "qt" 구성 객체뿐만 아니라 모든 임스크립트 구성 옵션이 포함될 수 있습니다. qt 구성 객체는 다음과 같은 속성을 지원합니다:

속성간단한 설명
containerElementsHTML 컨테이너 요소의 배열입니다. 애플리케이션은 이를 Q스크린으로 간주합니다.
onLoaded애플리케이션이 로드를 완료했을 때 호출되는 콜백입니다.
onExit애플리케이션이 종료될 때 호출되는 콜백입니다.

containerElements 배열은 Qt와 웹 페이지 사이의 주요 인터페이스로, 이 배열의 html 요소(일반적으로 <div> 요소)는 웹 페이지에서 애플리케이션 콘텐츠의 위치를 지정합니다.

애플리케이션은 각 컨테이너 요소를 QScreen 인스턴스로 간주하고 평소처럼 화면 인스턴스에 애플리케이션 창을 배치할 수 있습니다. Qt::WindowFullScreen 상태가 설정된 창은 전체 화면 영역을 사용하며 "전체 화면"이 아닌 창에는 창 장식이 적용됩니다.

qtLoad() 함수는 대기 중일 때 엠스크립트 인스턴스를 반환하는 프로미스를 반환합니다. 이 인스턴스는 내보낸 엠바인드 함수에 대한 액세스를 제공합니다. Qt는 이러한 함수를 여러 개 내보내고, 이러한 함수가 인스턴스 API를 구성합니다.

Qt 인스턴스 API 사용하기

Qt는 여러 인스턴스 함수를 제공합니다. 현재 이 함수는 런타임에 컨테이너 요소 추가 및 제거를 지원합니다.

Property간단한 설명
qtAddContainerElement컨테이너 엘리먼트를 추가합니다. 요소를 추가하면 새로운 QScreen 이 추가됩니다.
qtRemoveContainerElement컨테이너 요소와 해당 화면을 제거합니다.
qtSetContainerElements모든 컨테이너 요소를 설정합니다.
qtResizeContainerElementQt가 컨테이너 요소의 크기를 변경하도록 합니다.

Qt 6.6 qtloader로 포팅하기

Qt 6.6에는 구현이 간소화되고 범위가 더 작은 새로운 qtloader가 포함되어 있습니다. 여기에는 애플리케이션 자바스크립트 코드 포팅이 필요할 수 있는 API 변경 사항이 포함됩니다. Qt는 전환을 쉽게 하기 위해 호환성 API를 제공합니다. 사용 사례에 따라 여러 가지 방법이 있습니다:

  • 생성된 app.html 파일을 직접 사용하는 경우 빌드 시점에 이 파일도 업데이트됩니다. 별도의 조치가 필요하지 않습니다.
  • 기본 qtloader 기능 세트를 사용하는 경우 임시 방편으로 Qt 6.6에 포함된 호환성 API를 사용할 수 있습니다. 이 API는 향후 릴리스에서 제거될 예정이므로 새 qtloader를 사용하려면 업데이트를 계획해야 합니다. 아래 1단계 포팅이 필요합니다.
  • 고급 기능(예: 런타임에 컨테이너 요소 추가)을 사용하는 경우 새 로더 또는 인스턴스 API로 포팅해야 합니다. 아래 1단계와 2단계 포팅이 필요합니다.

포팅 단계

  1. 로딩하는 html 파일에서 app.js (Emscripten에서 생성한 JavaScript 런타임)을 포함합니다.
    <script src="app.js"></script>

    Qt 6.6 이전에는 qtloader가 이 자바스크립트 파일을 로드하고 평가했습니다. 이 작업은 더 이상 수행되지 않으며 <script> 태그를 사용하여 파일을 포함해야 합니다.

  2. 새로운 JavaScript 및 인스턴스 API를 사용하도록 포팅합니다.

위의 문서 섹션을 참조하세요.

지원되는 브라우저

데스크톱

웹어셈블리용 Qt는 다음 브라우저에서 개발 및 테스트되었습니다:

  • Chrome
  • Firefox
  • Safari
  • Edge

브라우저가 WebAssembly를 지원하는 경우 Qt가 실행되어야 합니다. 응용 프로그램 자체가 하드웨어 가속 그래픽을 사용하지 않더라도 Qt에는 고정된 WebGL 요구 사항이 있습니다. 일부 브라우저는 구형 또는 지원되지 않는 GPU를블랙리스트에 올리기도 하지만, WebAssembly를 지원하는 브라우저는 대부분 WebGL을 지원합니다.

Qt는 운영 체제 기능을 직접 사용하지 않으며, 예를 들어 FireFox가 Windows나 macOS에서 실행되더라도 아무런 차이가 없습니다. Qt는 일부 운영 체제 적응을 사용하지만, 예를 들어 macOS에서 ctrl/cmd 키 처리를 위해 일부 운영 체제 적응을 사용합니다.

모바일

웹 어셈블리 애플리케이션용 Qt는 모바일 사파리 및 안드로이드 크롬과 같은 모바일 브라우저에서 실행됩니다.

지원되는 Qt 모듈

웹어셈블리용 Qt는 Qt 모듈 및 기능의 하위 집합을 지원합니다. 테스트가 완료된 모듈은 아래에 나열되어 있으며 다른 모듈은 작동하거나 작동하지 않을 수 있습니다.

모든 경우에 모듈 지원이 완전하지 않을 수 있으며 브라우저 샌드박스 또는 Qt 플랫폼 포트의 불완전성으로 인해 추가적인 제한이 있을 수 있습니다. 자세한 내용은 웹어셈블리용 Qt로 개발하기를 참조하세요.

웹어셈블리용 Qt로 개발하기

CMake로 빌드하기

CMake에서 엠스크립텐 전용 구성이 필요한 경우 다음 코드를 활용할 수 있습니다:

if(EMSCRIPTEN)
    # WebAssembly specific code
else()
    # other platforms
endif()

이 코드를 사용하면 다른 플랫폼과의 호환성을 보장하면서 엠스크립텐 전용 구성을 수용할 수 있습니다.

OpenGL 및 WebGL

웹어셈블리용 Qt는 https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API WebGL을 사용하여 하드웨어 가속 렌더링을 지원합니다.

WebGL은 다음과 같은 버전 매핑을 통해 OpenGL ES를 밀접하게 준수합니다:

OpenGLWebGL
OpengL ES 2WebGL 1
OpengL ES 3WebGL 2

Qt는 사용 가능한 가장 높은 WebGL 버전을 사용합니다. 이는 일반적으로 오늘날의 브라우저에서 WebGL 2이지만 하드웨어에 따라 제한되는 경우 WebGL 1일 수도 있습니다. 웹어셈블리용 Qt로 WebGL 2를 지원하는 기기를 타겟팅하는 것이 좋습니다.

웹과 데스크톱 OpenGL의 차이점은 WebGL과 OpenGL 차이점에 문서화되어 있습니다. WebGL 1.0과 WebGL 2.0 사이에는 WebGL 2.0 사양에 문서화된 추가적인 차이점이 있습니다.

기본적으로 ES2(및 ES3)의 WebGL 친화적인 하위 집합이 사용됩니다. 바인딩된 버퍼 없이 glDrawArraysglDrawElements 을 사용해야 하는 경우 다음을 추가하여 전체 ES2 지원을 사용 설정할 수 있습니다.

target_link_options(<your target> PRIVATE -s FULL_ES2=1)

를 추가하여 전체 ES3 에뮬레이션을 사용하거나

target_link_options(<your target> PRIVATE -s FULL_ES3=1)

를 프로젝트의 CMakeLists.txt 에 추가하면 됩니다.

OpenGL 컨텍스트 공유

WebGL은 네이티브 서페이스 당 하나의 컨텍스트만 허용하며 OpenGL 컨텍스트 공유는 지원하지 않습니다. Qt는 여러 개의 OpenGLContexts를 사용할 때 네이티브 컨텍스트를 재사용하는 방식으로 이 문제를 해결합니다.

예를 들어 QOpenGLWidget 대신 QOpenGLWindow 을 사용하는 것과 같이 네이티브 서페이스당 하나의 컨텍스트로 OpenGL 컨텍스트 사용을 제한하는 것이 좋습니다. QOpenGLWidget::paintGL() 호출 시 애플리케이션 코드가 모든 관련 OpenGL 상태를 복원하고 QOpenGLWidget::initializeGL() 및 QOpenGLWidget::paintGL() 호출 사이에 상태를 유지하지 않는다면 QOpenGLWidget 이 작동할 수 있습니다.

멀티스레딩

웹어셈블리용 Qt는 각 스레드가 웹 워커에 의해 지원되는 엠스크립텐의 Pthreads 지원을 사용하여 멀티스레딩을 지원합니다. 멀티스레딩을 활성화하려면 Qt Maintenance Tool 에서 "WebAssembly(멀티스레드)" 컴포넌트를 설치하거나 소스에서 Qt를 빌드하고 "-feature-thread" 플래그를 전달하여 구성합니다.

일반적으로 기존 스레딩 코드를 재사용할 수 있지만, pthread 구현의 특정 사항을 해결하기 위해 수정해야 할 수도 있습니다. 스레드 프록시 기능 및 Qt Quick 스레드 렌더링 루프 등 일부 Emscripten 및 Qt 기능은 지원되지 않습니다.

보조 스레드의 요청을 처리하기 위해 메인 스레드가 필요할 수 있으므로 특히 웹어셈블리용 Qt에서 메인 스레드를 차단하지 않는 것이 중요합니다. 예를 들어, Qt의 모든 타이머는 메인 스레드에서 스케줄링되며 메인 스레드가 차단되면 실행되지 않습니다. 또 다른 예로 새 웹 워커(스레드용)를 생성하는 작업은 메인 스레드에서만 수행할 수 있습니다.

Emscripten은 이에 대한 몇 가지 완화 기능을 제공합니다. 뮤텍스 잠금 획득과 같은 단기 대기는 잠금을 기다리는 동안 바쁘게 대기하고 이벤트를 처리하는 방식으로 지원됩니다. 메인 스레드에서 더 오래 기다리는 것은 피해야 합니다. 특히, 애플리케이션이 대기() 또는 조인() 호출 시점에 스레드(및 웹 워커)가 이미 시작되어 메인 스레드의 도움 없이 완료할 수 있다는 것을 보장할 수 있는 경우가 아니라면 QThread::wait() 또는 pthread_join()을 호출하여 보조 스레드를 대기하는 일반적인 관행은 작동하지 않습니다.

멀티스레딩 기능을 사용하려면 브라우저에서 SharedArrayBuffer API를 지원해야 합니다. (일반적으로 Emscripten은 힙을 ArrayBuffer 객체에 저장합니다. 멀티스레딩을 위해서는 힙을 웹 워커와 공유해야 하며 SharedArrayBuffer가 필요합니다.) 이 API는 일반적으로 모든 최신 브라우저에서 사용할 수 있지만 특정 보안 요구 사항이 충족되지 않으면 비활성화될 수 있습니다. 그러면 스레드 지원이 활성화된 WebAssembly 바이너리가 실제로 스레드를 시작하지 않는 경우에도 실행에 실패합니다.

SharedArrayBuffer를 사용하려면 보안 브라우징 컨텍스트(페이지가 https:// 또는 http://localhost 을 통해 제공되는 경우)와 페이지가 교차 출처 격리 모드에 있어야 합니다. 후자는 웹 서버에서 소위 COOP 및 COEP 헤더를 설정하여 수행할 수 있습니다:

  • 교차 출처-개방자 정책: 동일 출처
  • 교차 오리진 임베더 정책: require-corp

SIMD

엠스크립텐은 웹어셈블리용 128비트 SIMD 유형과 연산을 제공하는 웹어셈블리 SIMD를 지원합니다.

소스에서 Qt를 빌드하고 -feature-wasm-simd128 플래그로 구성하여 활성화하면 컴파일 및 링크 시 -msimd128 플래그가 전달됩니다. 현재 Qt에는 최적화된 코드 경로가 포함되어 있지 않지만, 와즘-심드를 활성화하면 컴파일러가 SIMD 명령어를 사용할 수 있는 컴파일러 자동 벡터화를 활성화할 수 있습니다.

GCC/Clang SIMD 벡터 익스텐션 또는 WASM SIMD128 내재성을 사용하여 WebAssembly SIMD를 직접 타겟팅할 수 있습니다. 자세한 내용은 Emscripten SIMD 문서를 참조하세요.

또한, 엠스크립텐은 x86 SSE 명령어를 Wasm SIMD 명령어로 에뮬레이션/변환하는 기능도 지원합니다. 이 에뮬레이션을 사용하지 않는 이유는 네이티브 Wasm SIMD에 상응하는 SSE SIMD 명령어를 사용하면 성능이 저하될 수 있기 때문입니다.

SIMD 지원 바이너리는 런타임에 SIMD 코드 경로가 호출되지 않는 경우에도 웹 어셈블리 SIMD를 지원하지 않는 브라우저와 호환되지 않는다는 점에 유의하십시오. 브라우저의 고급 설정(예: 'about:config' 또는 'chrome:flags')에서 SIMD 지원을 활성화해야 할 수 있습니다.

네트워킹

Qt는 네트워킹에 대한 제한된 지원을 제공합니다. 일반적으로 웹에서 이미 사용 중인 네트워크 프로토콜은 Qt에서도 사용할 수 있지만, 웹 샌드박스로 인해 직접 사용할 수 없는 프로토콜도 있습니다.

지원되는 프로토콜은 다음과 같습니다:

  • QNetworkAccessManager 웹 페이지 원본 서버 또는 CORS를 지원하는 서버에 대한 http 요청. 여기에는 QML의 XMLHttpRequest 가 포함됩니다.
  • QWebSocket 모든 호스트에 대한 연결. 보안 https 프로토콜을 통해 제공되는 웹 페이지는 보안 wss 프로토콜을 통한 웹소켓 연결만 허용합니다.
  • 엠스크립텐에서 제공하는 기능을 사용하여 웹소켓을 통해 에뮬레이트된 POSIX TCP 소켓. 이 경우 소켓 변환을 처리하는 포워딩 서버를 실행해야 한다는 점에 유의하세요.

다른 모든 네트워크 프로토콜은 지원되지 않습니다.

로컬 파일 액세스

파일 시스템 액세스는 웹에서 샌드박스가 적용되며, 이는 애플리케이션의 파일 작업 방식에 영향을 미칩니다. 웹 플랫폼은 사용자가 제어할 수 있는 방식으로 로컬 파일 시스템에 액세스하기 위한 API와 영구 저장소에 액세스하기 위한 API를 제공합니다. Emscripten과 Qt는 이러한 기능을 래핑하여 C++ 및 Qt 기반 애플리케이션에서 더 쉽게 사용할 수 있는 API를 제공합니다.

웹 플랫폼은 로컬 파일과 영구 저장소에 액세스하기 위한 기능을 제공합니다:

  • 사용자가 파일을 선택할 수 있는 기본 파일 열기 대화 상자를 표시하는 <입력 유형="file">.
  • IndexedDB는 영구 로컬 저장소를 제공합니다(브라우저 외부에서는 액세스할 수 없음).

엠스크립텐은 POSIX와 유사한 API를 통해 여러 파일 시스템을 제공합니다. 여기에는 다음이 포함됩니다:

  • 파일을 인메모리에 저장하는 MEMFS 임시 파일 시스템
  • IndexedDB를 사용하여 파일을 저장하는 IDBFS 영구 파일 시스템

엠스크립텐은 앱 시작 시 임시 MEMFS 파일 시스템을 "/"에 마운트합니다. 즉, QFile 을 사용할 수 있으며 기본적으로 메모리에 파일을 읽고 쓸 수 있습니다. Qt는 다른 API도 제공합니다:

클립보드 액세스

Qt는 웹 샌드박스로 인해 약간의 차이가 있지만 시스템 클립보드에 텍스트 복사 및 붙여넣기를 지원합니다. 일반적으로 클립보드 액세스는 사용자 권한이 필요하며, 이는 입력 이벤트(예: CTRL+c)를 처리하거나 클립보드 API를 사용하여 얻을 수 있습니다.

클립보드 API를 지원하는 브라우저를 사용하는 것이 좋습니다. 이 API의 요구 사항은 웹 페이지가 보안 연결(예: https)을 통해 제공되어야 하며 일부 브라우저에서는 구성 플래그를 변경해야 한다는 점입니다.

  • Chrome 버전 66 및 Safari 버전 13.1은 클립보드 API를 지원합니다
  • Firefox 버전 90은 'about:config'에서 다음 플래그를 활성화하면 클립보드 API를 지원합니다:
    dom.events.asyncClipboard.read
    dom.events.asyncClipboard.clipboardItem

글꼴

Qt WASM 모듈에는 3가지 내장 글꼴이 포함되어 있습니다: "비트스트림 베라 산스"(대체 글꼴), "DejaVu 산스", "DejaVu 산스 모노".

이러한 글꼴은 제한된 문자 집합을 제공합니다. Qt는 추가 글꼴을 추가하기 위한 몇 가지 옵션을 제공합니다:

하나는 URL로 글꼴을 가져오거나 일반적인 데스크톱 앱이 작동하는 방식과 동일한 Qt Resource System을 사용하는 QML의 FontLoader 을 사용하는 것입니다.

다른 글꼴 사용 방법은 QFontDatabase::addApplicationFontFromData를 통해 글꼴을 추가하는 것입니다.

접근성 및 화면 리더

웹어셈블리용 Qt는 스크린 리더에 대한 기본적인 지원을 제공합니다. 버튼이나 체크 박스와 같은 간단한 UI 요소는 작동하지만 표나 트리 뷰와 같은 복잡한 UI 요소는 지원이 누락될 수 있습니다. Qt WidgetsQt Quick 모두 지원됩니다.

다음 스크린 리더/브라우저 구성이 테스트되었으며 작동하는 것으로 알려져 있습니다. 다른 브라우저 및 스크린 리더에서도 작동할 수 있습니다.

  • macOS의 Safari에서 VoiceOver 사용
  • macOS에서 Chrome을 사용한 보이스오버

접근성 구현은 Qt UI 요소에 대한 접근성 정보를 제공하는 "섀도" HTML 요소를 생성하는 방식으로 작동합니다. 이 기능은 기본적으로 비활성화되어 있습니다. 최종 사용자는 스크린 리더를 사용하여 "스크린 리더 활성화" 버튼을 선택하여 활성화할 수 있습니다. 활성화하면 웹 페이지가 접근성 요소로 채워집니다.

애플리케이션 시작 및 이벤트 루프

웹어셈블리용 Qt는 애플리케이션이 QApplication 객체를 생성하고 실행 함수를 호출하는 표준 Qt 시작 방식을 지원합니다:

int main(int argc, char **argv)
{
    QApplication app(argc, argv);

    QWindow appWindow;

    return app.exec();
}

위의 exec() 호출은 일반적으로 애플리케이션이 종료될 때까지 이벤트를 차단하고 처리합니다. 안타깝게도 메인 스레드 차단이 허용되지 않는 웹 플랫폼에서는 이 방법이 불가능합니다. 대신 각 이벤트를 처리한 후 브라우저의 이벤트 루프로 제어권을 반환해야 합니다.

Qt는 스택을 유지하면서 실행()이 메인 스레드 제어권을 브라우저에 반환하도록 함으로써 이 문제를 해결합니다. 애플리케이션 코드의 관점에서 보면 실행() 함수를 입력하면 평소와 같이 이벤트 처리가 이루어집니다. 그러나 실행() 호출은 절대 반환되지 않으며 애플리케이션 종료 시에도 반환되지 않습니다.

이 동작은 일반적으로 앱 종료 시 브라우저가 애플리케이션 메모리를 확보하기 때문에 허용됩니다. 하지만 애플리케이션 객체가 유출되고 해당 소멸자가 실행되지 않기 때문에 종료 코드가 실행되지 않는다는 의미입니다.

main()을 비동기식으로 재작성하면 이 문제를 방지할 수 있는데, Emscripten은 main()이 반환될 때 런타임을 종료하지 않기 때문에 가능합니다. 그러면 응용 프로그램 코드는 exec() 호출을 생략하고 최상위 창과 응용 프로그램 객체를 삭제하여 Qt를 깔끔하게 종료할 수 있습니다.

QApplication *g_app = nullptr;
AppWindow *g_appWindow = nullptr;

int main(int argc, char **argv)
{
    g_app = new QApplication(argc, argv);
    g_appWindow = new AppWindow();
    return 0;
}

비동기화

웹 어셈블리용 Qt의 기본 빌드는 웹 플랫폼의 제한으로 인해 QEventLoop::exec() 또는 QDialog::exec() 호출과 같은 이벤트 루프 재진입을 지원하지 않습니다.

엠스크립텐의 비동기화 기능은 QEventLoop::exec() 및 QDialog::exec()와 같은 동기 호출이 이벤트 루프에 양보하도록 허용하여 이러한 제한을 해제합니다. 중첩 호출은 지원되지 않으므로 최상위 수준 QApplication::exec() 호출에는 asyncify가 사용되지 않습니다.

비동기화가 필요한 기능은 다음과 같습니다:

  • 반환 값이 있는 QDialogs, QMessageBox.
  • 드래그 앤 드롭(특히 드래그).
  • 중첩/보조 이벤트 루프 실행().

링커 옵션에 "-sASYNCIFY --Os" 플래그를 추가하여 비동기화를 활성화합니다:

CMake:

target_link_options(<your target> PUBLIC -sASYNCIFY -Os)

qmake:

QMAKE_LFLAGS += -sASYNCIFY -Os

비동기화를 활성화하면 바이너리 크기 증가와 CPU 사용량 증가의 형태로 오버헤드가 추가됩니다. 오버헤드를 최소화하려면 최적화를 활성화하여 빌드하세요.

JSPI 비동기화(자바스크립트 프로미스 통합)

JSPI는 코드 변환 대신 기본 브라우저 지원을 사용하여 구현된 새로운 비동기화 백엔드입니다. Emscripten 비동기화에 비해 애플리케이션 링크 시간을 개선하고 코드 크기를 늘리지 않습니다.

JSPI 웹어셈블리 기능은 현재 '구현' 단계에 있으며 아직 표준화되지 않았습니다.

링커 옵션에 "-sJSPI" 플래그를 추가하여 JSPI를 활성화하세요:

CMake:

target_link_options(<your target> PUBLIC -sJSPI)

qmake:

QMAKE_LFLAGS += -sJSPI

디버깅 및 프로파일링

Wasm 디버깅은 브라우저 JavaScript 콘솔에서 수행되며, Qt Creator 내에서 직접 Wasm의 애플리케이션을 디버깅하는 것은 불가능합니다.

엠스크립트 링커 인수를 사용하여 디버깅을 돕기 위해 더 자세한 정보를 추가할 수 있습니다:

  • -s LIBRARY_DEBUG=1(라이브러리 호출 출력)
  • -s SYSCALL_DEBUG=1(시스템 호출 출력)
  • -s FS_LOG=1 (파일 시스템 작업 출력)
  • -s SOCKET_DEBUG(소켓, 네트워크 데이터 전송 출력)

CMake:

target_link_options(<your target> PRIVATE -s LIBRARY_DEBUG=1)

qmake:

QMAKE_LFLAGS_DEBUG += -s LIBRARY_DEBUG=1

최적화

웹 어셈블리용 Qt는 엠스크립텐 툴체인을 사용하여 바이너리를 생성하며, 성능과 바이너리의 크기에 영향을 줄 수 있는 많은 플래그가 있습니다. 자세한 내용은 엠스크립텐을 참조하십시오 : 코드 최적화를 참조하세요.

일반 C++ 애플리케이션과 마찬가지로 링커 및 컴파일러 플래그를 전달할 수 있습니다: qmake#tab-qmake

target_compile_options(<your target> PRIVATE -oz -flto)
target_link_options(<your target> PRIVATE -flto)
QMAKE_CXXFLAGS += -oz -flto
QMAKE_LFLAGS += -flto

바이너리 크기 최소화하기

원활한 사용자 경험을 제공하려면 WebAssembly 애플리케이션을 다운로드하고 로드하는 시간을 줄이는 것이 중요합니다. 애플리케이션 바이너리 크기를 줄이는 것이 빠른 다운로드를 가능하게 하는 중요한 요소 중 하나입니다. 다음 대안을 사용하여 바이너리 크기를 줄이세요:

  • 릴리스 빌드를 배포하세요. 디버그 빌드는 디버그 심볼을 포함하며 훨씬 더 큽니다.
  • 서버에서 압축을 활성화합니다. gzip 및 Brotli와 같은 가장 일반적인 알고리즘은 Wasm 바이너리에서 잘 작동하며 크기를 크게 줄일 수 있습니다.
  • 컴파일러 및 링커 플래그를 사용하여 더 작은 바이너리를 생성할 수 있습니다(예: '-os', '-oz'). 결과는 특정 애플리케이션에 따라 달라질 수 있습니다.
  • 소스에서 웹어셈블리용 Qt를 컴파일할 때 사용하지 않는 기능을 비활성화하세요(아래 참조).
기능 옵트아웃하기

웹어셈블리 애플리케이션은 기본적으로 Qt 라이브러리에 정적으로 링크되므로 컴파일러가 데드 코드를 제거할 수 있습니다. 그러나 Qt의 동적 특성으로 인해 컴파일러가 항상 이러한 최적화를 수행할 수 있는 것은 아닙니다.

소스에서 웹 어셈블리용 Qt를 빌드하는 경우 기능을 비활성화하여 Qt 바이너리의 크기를 줄이고 결과적으로 .wasm 바이너리의 크기를 줄일 수 있습니다. Qt는 WebAssembly 플랫폼에 대해 기본적으로 일부 기능을 비활성화하지만 애플리케이션에서 사용하지 않는 기능을 비활성화할 수도 있습니다. 자세한 내용은 비활성화된 기능을 참조하십시오.

다음 기능을 비활성화하여 바이너리 크기를 줄일 수 있습니다(보통 10~15%):

인수 구성간단한 설명
-no-feature-cssparser캐스케이딩 스타일 시트용 구문 분석기.
-no-feature-datetimeedit날짜 및 시간 편집(날짜 구문 분석기에 따라 다름).
-no-feature-datetimeparser날짜/시간 텍스트 구문 분석.
-no-feature-dockwidgetQMainWindow 안에 위젯을 도킹하거나 바탕화면의 최상위 창으로 띄웁니다.
-no-feature-gestures제스처를 위한 프레임워크.
-no-feature-mimetype모방 유형 처리.
-no-feature-qml-network네트워크 투명성.
-no-feature-qml-list-modelListModel QML 유형.
-no-feature-qml-table-modelTableModel QML 유형.
-no-feature-quick-canvas캔버스 항목.
-no-feature-quick-path경로 요소.
-기능 없음-퀵-경로 보기PathView 항목.
-기능 없음-퀵 트리뷰TreeView 항목.
-기능-스타일-스타일시트 없음CSS를 통해 구성할 수 있는 위젯 스타일입니다.
-no-feature-tableview테이블 보기의 기본 모델/보기 구현.
-no-feature-texthtmlparserHTML용 파서.
-no-feature-textmarkdownreader마크다운(CommonMark 및 GitHub) 리더.
-no-feature-textodfwriterODF 작성기.

Wasm 예외

Qt는 기본적으로 예외를 지원하지 않고 빌드되며, 예외를 던지면 프로그램이 중단됩니다. 웹 어셈블리 예외는 소스에서 빌드하고 -feature-wasm-exceptions 플래그를 Qt 구성에 전달하여 활성화할 수 있습니다. 이렇게 하면 컴파일 및 링크 시 컴파일러에 -fwasm-exceptions 플래그가 전달됩니다. Qt는 이전의 자바스크립트 기반 예외 구현에 대한 엠스크립텐의 지원을 활성화하는 것을 지원하지 않습니다.

내부 구현 세부 사항으로 인해 예외가 활성화된 경우 QApplication::exec() 호출은 지원되지 않습니다. 대신 애플리케이션 시작 및 이벤트 루프에 설명된 대로 메인()을 일찍 반환하고 실행()을 호출하지 않는 형태로 작성하세요.

공유 라이브러리 및 동적 연결 개발자 미리보기

웹 어셈블리용 Qt는 기본적으로 정적 링크를 사용하며, 애플리케이션은 Qt 라이브러리와 애플리케이션 코드가 포함된 단일 웹 어셈블리 파일로 배포됩니다. 동적 링크는 각 라이브러리와 플러그인이 개별적으로 배포되는 대체 빌드 모드입니다.

예를 들어 Qt Quick 을 사용하는 애플리케이션은 다음과 같은 라이브러리와 플러그인을 사용할 수 있습니다:

  • <qtpath>/lib/libQt6Core.so
  • <qtpath>/lib/libQt6Gui.so
  • <qtpath>/lib/libQt6Qml.so
  • <qtpath>/lib/libQt6Quick.so
  • <qtpath>/plugins/imageformats/libqjpeg.so
  • <qtpath>/plugins/imageformats/libqjgif.so
  • <qtpath>/qml/QtQuick/Window/libquickwindowplugin.so

동적 링크 지원은 현재 개발자 프리뷰 버전에 있습니다. 이 구현은 프로토타이핑 및 평가에는 적합하지만 프로덕션 사용에는 적합하지 않습니다. 현재 제한 사항 및 제한 사항은 다음과 같습니다:

  • 엠스크립텐 SDK를 패치해야 합니다. 엠에스디케이 3.1.52를 사용하고 패치#1패치#2를 적용하세요.
  • 멀티스레딩은 지원되지 않습니다.
  • 비동기화는 지원되지 않습니다.

빠른 시작

빌드 및 배포 절차는 정적 WASM 및 공유 데스크톱 빌드와는 약간 다릅니다. 전체 애플리케이션 빌드로 진행하기 전에 작은 예제부터 시작하는 것이 좋습니다.

  1. 소스에서 Qt를 빌드하고 Qt 구성 스크립트에 "-shared" 옵션을 전달합니다.
  2. 1단계의 Qt를 사용하여 애플리케이션을 빌드합니다.
  3. 애플리케이션 디렉터리에서 "qt"라는 디렉터리를 복사하거나 링크하여 Qt 설치를 배포합니다.
    • ln -s <qtpath> qt
    • cp -r <qtpath> qt
  4. 배포 스크립트를 실행하여 플러그인 사전 로드 목록을 만듭니다.
    • <qtpath>/qtbase/util/wasm/preload/deploy_qt_plugins.py <qtpath>
    • <qtpath>/qtbase/util/wasm/preload/deploy_qml_imports.py <qthostpath> <qtpath>

공유 라이브러리 배포 자세히 알아보기

Qt의 공유 라이브러리 빌드는 두 단계로 배포되는데, 첫 번째 단계에서는 웹 서버에서 Qt 및 애플리케이션 빌드를 다운로드할 수 있도록 하고 두 번째 단계에서는 애플리케이션 시작 시 필요한 Qt 플러그인 및 Qt Quick 임포트를 다운로드합니다.

첫 번째 단계에서는 웹 서버에서 Qt 설치를 다운로드할 수 있도록 설정합니다. 웹 서버 설정의 세부 사항에 따라 이 작업을 수행하는 방법이 다를 수 있습니다. 공통적으로 Qt 로더는 애플리케이션을 로딩하는 html 파일을 기준으로 디렉토리 이름 "qt"에서 Qt 라이브러리와 플러그인을 찾습니다.

배포의 일부로 이미 애플리케이션을 웹 서버에 복사하고 있다면 Qt도 복사할 수 있습니다. 애플리케이션을 빌드 디렉터리에서 직접 서비스하는 경우(개발 단계에서 자주 발생하는 경우) Qt에 대한 심볼릭 링크를 생성하는 것이 효과적일 수 있습니다.

플러그인 및 Qt Quick 임포트와 같은 Qt 컴포넌트에 대한 사전 로드 목록을 생성하여 두 번째 단계를 준비합니다. 사전 로딩은 애플리케이션 시작 시 필요한 모든 Qt 컴포넌트를 사용할 수 있도록 합니다. 필요에 따라 컴포넌트를 다운로드하는 지연 로딩도 가능하지만 여기서는 다루지 않습니다.

사전 로딩은 웹 서버에서 Emscripten이 제공하는 인메모리 파일 시스템으로 파일을 다운로드하는 Qt JavaScript 로더에 의해 구현됩니다. 다운로드할 파일은 json 형식의 다운로드 목록을 사용하여 지정합니다. Qt는 사전 로드 목록을 생성하기 위한 두 가지 스크립트를 제공합니다(위의 빠른 시작 섹션 참조).

알려진 문제

  • 중첩된 이벤트 루프는 지원되지 않습니다. 애플리케이션은 QDialog::exec() 및 QEventLoop::exec()와 같은 API를 호출해서는 안 됩니다. 실험적 기능인 비동기화를 사용할 수 있습니다.
  • 인쇄가 지원되지 않습니다.
  • QDnsLookup 조회, QTcpSocket, QSsl 은 작동하지 않으며 웹 샌드박스로 인해 지원되지 않습니다.
  • 글꼴: Wasm 샌드박스는 시스템 글꼴에 대한 액세스를 허용하지 않습니다. 글꼴 파일은 애플리케이션과 함께 배포해야 합니다(예: Qt 리소스 또는 다운로드). 웹어셈블리용 Qt 자체에는 그러한 폰트 하나가 포함되어 있습니다.
  • 체크박스와 같은 일부 Qt Quick Controls 2 컴포넌트에서 초기화되지 않은 그래픽 메모리의 아티팩트가 있을 수 있습니다. 이는 때때로 HighDPi 디스플레이에서 볼 수 있습니다.
  • Windows 및 macOS용 기본 스타일은 플랫폼에서 해당 기능을 제공하지 않기 때문에 Wasm에서 지원되지 않습니다.
  • "wasm-ld: 오류: 초기 메모리가 너무 작습니다"와 같은 링크 시간 오류가 발생하면 초기 메모리 크기를 조정해야 합니다. QT_WASM_INITIAL_MEMORY를 사용하여 초기 크기를 KB 단위로 설정하며, 이 크기는 64KB(65536)의 배수여야 합니다. 기본값은 50MB입니다. CMakeLists.txt에서: set_target_properties(<target> PROPERTIES QT_WASM_INITIAL_MEMORY "150MB")
  • CMakeLists.txt의 add_executable은 <target>.html을 생성하거나 qtloader.js를 복사하지 않습니다. 대신 qt_add_executable을 사용하십시오.
  • QWebSocket 연결은 메인 스레드에서만 엠스크립텐에서 지원됩니다.
  • 웹 페이지 및 브라우저에서 사용할 수 있는 API가 이 기능을 노출하지 않기 때문에 WebAssembly용 QWebSockets는 핑 또는 퐁 프레임 전송을 지원하지 않습니다.
  • 런타임 오류(예: "RangeError: 메모리 부족"과 같은 런타임 오류는 MAXIMUM_MEMORY를 다음과 같이 장치가 지원하는 값으로 설정하여 해결할 수 있습니다.
    target_link_options(<your target> PRIVATE -s MAXIMUM_MEMORY=1GB)
  • Qt웹소켓을 사용하려면 QtMqtt 을 사용하기 위해 서브프로토콜을 'mqtt'로 설정해야 할 수 있습니다. QWebSocket 을 열 때 QWebSocketHandshakeOptions 을 사용합니다.

기타 주제

Qt 구성 옵션 레퍼런스

다음 구성 옵션은 소스에서 웹 어셈블리용 Qt를 빌드할 때 관련이 있습니다.

구성 인수간단한 설명
-feature-thread멀티 스레드 Wasm.
-feature-wasm-simd128웹어셈블리 SIMD 지원을 활성화합니다.
-feature-wasm-exceptions웹어셈블리 예외 지원을 사용하도록 설정합니다.
-feature-opengles3기본 opengles2에 추가로 opengles3을 사용합니다.
-device-option QT_EMSCRIPTEN_ASYNCIFY=1비동기화 지원을 활성화합니다.
-device-option QT_EMSCRIPTEN_ASYNCIFY=2비동기화(JSPI) 지원을 활성화합니다.

Qt는 바이너리 크기를 줄이기 위해 웹어셈블리 플랫폼에서 기본적으로 일부 기능을 비활성화합니다. 웹어셈블리용 Qt를 구성할 때 명시적으로 기능을 활성화할 수 있습니다:

인수 구성간단한 설명
-feature-topleveldomain도메인이 최상위 도메인인지 확인하기 위한 지원을 제공합니다.

일반적인 다운로드 크기

예상 설치 공간(다운로드 크기): 컴파일러에서 생성된 Wasm 모듈은 용량이 클 수 있지만 압축이 잘됩니다:

예제gzipbrotli
헬로글윈도우 (QtCore + QtGui)2.8M2.1M
위글리 위젯 (QtCore + QtGui + QtWidgets)4.3M3.2M
센서태그 (QtCore + QtGui + QtWidgets + QtQuick + QtCharts)8.6M6.3M

압축은 일반적으로 표준 압축 기능을 사용하여 웹 서버 측에서 처리되며, 서버가 자동으로 압축하거나 미리 압축된 버전의 파일을 선택합니다. 일반적으로 Wasm 파일을 특별히 처리할 필요는 없습니다.

자세한 내용은 바이너리 크기 최소화하기를 참조하세요.

예제

외부 리소스

라이선스

웹어셈블리용 Qt는 The Qt Company의 상용 라이선스에 따라 제공됩니다. 또한 GNU 일반 공중 사용 허가서 버전 3에 따라 사용할 수 있습니다. 자세한 내용은 Qt 라이선스를 참조하십시오.

© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.