이 페이지에서

커피 머신

상태 기반 맞춤형 사용자 인터페이스를 갖춘 Qt Quick 애플리케이션입니다.

'시작하기' 버튼이 표시된 커피 머신 UI

이 앱은 제품 주문을 위한 일반적인 사용자 인터페이스를 크로스 플랫폼, 다양한 화면 크기에 대응하며 반응형 방식으로 구현하는 방법을 보여줍니다.

예제 실행

다음에서 예제를 실행할 수 있습니다.

UI 안내

이 섹션에서는 애플리케이션의 일반적인 사용자 흐름을 안내합니다.

레시피 선택

구입 가능한 커피 종류 목록

커피 머신 애플리케이션을 사용하면 커피 종류를 선택할 수 있으며, 해당 종류의 커피를 몇 잔 더 주문할 수 있는지 표시해 줍니다.

커피 맞춤 설정

커피 블렌드의 비율을 맞춤 설정할 수 있는 슬라이더

레시피를 선택하면 앱은 커피 블렌드의 구성 비율을 다음과 같이 표시합니다:

  • 추출된 커피
  • 뜨거운 우유
  • 우유 거품
  • 설탕

이 비율은 슬라이더를 사용하여 조정할 수 있습니다.

추출 상태 표시

사용자에게 컵을 넣으라고 안내

사용자가 컵이 기계에 놓여 있음을 확인하면 추출이 시작됩니다.

추출이 시작되면 앱에 추출 과정이 애니메이션으로 표시됩니다.

커피가 준비되었습니다

추출 과정이 끝나면 앱은 몇 초 동안 원하는 커피 블렌드가 담긴 커피잔을 표시한 후 시작 페이지로 돌아갑니다.

작동 원리

여기서는 이러한 기능들이 어떻게 구현되는지 설명합니다.

화면 크기와 방향에 따른 조정

애플리케이션 창의 루트 객체에는 높이와 너비에 대한 초기값이 설정되어 있으며, 이 값은 데스크톱 플랫폼에서 창 크기로 사용됩니다. 기본 QWindow::AutomaticVisibility 는 모바일 플랫폼과 같이 필요한 플랫폼에서 창이 최대화되거나 전체 화면으로 표시되도록 보장합니다.

ApplicationWindow {
    visible: true
    width: 1000
    height: 600
    title: qsTr("Coffee")

    background: Rectangle {
        color: Colors.currentTheme.background
    }

여기서 ` ApplicationWindow `의 자식 객체들은 ` ApplicationWindow `의 크기를 가져와 그에 따라 자체 크기를 결정합니다.

이 앱은 화면 방향 변화에 쉽게 적응할 수 있도록 애플리케이션 페이지 구성 요소 전반에 걸쳐 ` GridLayout `를 사용합니다.

안드로이드용 커피 머신 선택 페이지의 가로 방향 화면

{Android 기기의 방향이 가로 모드일 때 화면이 가로 모드로 전환됩니다}

Windows용 커피 머신 선택 페이지의 가로 방향

{Windows 데스크톱에서는 가로 모드가 기본 방향입니다}

참고: 데스크톱버전에서는 너비가 높이보다 커야 합니다.

이 애플리케이션은 앱의 여러 페이지를 표시하기 위해 ` StackView ` QML 타입을 사용합니다. 다양한 페이지 컴포넌트를 처리하는 ` StackView `는 ` ApplicationFlowForm.ui.qml`에 구현되어 있습니다.

한 페이지에서 다음 페이지로 이동하면 ` ApplicationFlow.qml `의 상태 변경이 트리거되며, 이때 필요한 속성 변경은 ` PropertyChanges ` QML 타입에 의해 처리됩니다:

states: [
    State {
        name: "Home"
        PropertyChanges {
            target: toolbar
            backButton.opacity: 0
            backButton.enabled: false
            themeButton.opacity: 0
            themeButton.enabled: false
            logo.sourceSize.width: 70
            logo.sourceSize.height: 50
        }

이러한 상태 변경 중에 발생하는 애니메이션은 ApplicationFLowForm.ui.qml 의 StackView 컴포넌트 내부에서 Transition 및 PropertyAnimation 을 사용하여 구현됩니다.

시작 페이지 구현

애플리케이션은 StackView 의 초기 항목으로 사용자에게 Home 페이지를 표시하는 것으로 시작합니다:

StackView {
    id: stack
    anchors.top: parent.top
    anchors.bottom: parent.bottom
    anchors.left: parent.left
    anchors.right: parent.right
    anchors.topMargin: parent.height / 20

    pushEnter: Transition {
        PropertyAnimation {
            property: "x"
            from: stack.width
            to: 0
            duration: 400
        }
    }

Home 컴포넌트는 상태 머신과 적절한 속성 별칭을 가진 Item 컴포넌트를 루트 객체로 배치하고, 그 뒤에 GridLayout 를 배치하는 방식으로 구성됩니다. 이러한 구조는 모든 앱 페이지 컴포넌트에 동일하게 적용됩니다.

Home 페이지에는 상단에 Qt 로고가 있는 커피잔 이미지가 표시되며, 제목으로 ‘Coffee Machine’이, 사용자가 기대할 수 있는 내용에 대한 간결하고 흥미로운 설명이 담긴 캡션이, 그리고 ‘ getStartedButton ’ 버튼이 표시됩니다.

사용자는 ‘ getStartedButton ’ 버튼을 눌러 다음 단계로 진행할 수 있으며, ‘ onClicked ’ 버튼의 기능은 ApplicationFlow.qml 에서 구현되어 있습니다:

getStartedbutton.onClicked: {
    applicationFlow.state = "Coffee-selection"
    applicationFlow.stack.pushItem(applicationFlow.choosingCoffee, {appFlow: applicationFlow})
}

이렇게 하면 ApplicationFlow.qml 에서 상태가 "Coffee-selection"으로 변경되고, StackView 에서 choosingCoffee 컴포넌트가 Home 컴포넌트 위에 푸시됩니다.

커피 선택 기능 구현

커피 선택 페이지( ChoosingCoffee.qml )에는 사용자가 선택할 수 있는 4가지 커피 옵션이 표시됩니다. 이 옵션들은 ChoosingCoffee.qml 의 GridLayout 내부에 위치한 CoffeeCards 형태로 표시됩니다:

GridLayout {
    id: cards
    anchors.horizontalCenter: parent.horizontalCenter
    anchors.top: parent.top
    rowSpacing: 20
    columnSpacing: 20
    CoffeeCard {
        id: cappuccino
        coffeeName: "Cappuccino"
        ingredients: "Milk, Espresso, Foam"
        time: 2
    }
    CoffeeCard {
        id: latte
        coffeeName: "Latte"
        ingredients: "Coffee, Foam"
        time: 3
    }
    CoffeeCard {
        id: espresso
        coffeeName: "Espresso"
        ingredients: "Milk, Espresso"
        time: 2
    }
    CoffeeCard {
        id: macchiato
        coffeeName: "Macchiato"
        ingredients: "Milk foam, Espresso"
        time: 4
    }
}

CoffeeCard 의 구현은 CoffeeCard.qml 에 있습니다.

이 카드들은 ApplicationWindow 루트 객체가 따르는 사용 가능한 화면 너비 및 높이 속성에 따라 그리드 형식이나 행 형식으로 표시될 수 있으며, 이 속성은 상태 머신을 통해 ChoosingCoffee.qml 및 그 안의 GridLayout 으로 전달됩니다.

CoffeeCards 각 카드의 이름, 추출 시간, 재료, 그리고 현재 이용 가능한 컵 수는 서로 다릅니다.

이 페이지에서 사용자는 화면 오른쪽 상단에 있는 작은 태양 모양의 아이콘 버튼을 눌러 애플리케이션 테마를 처음 변경할 수도 있습니다. 테마 버튼을 누르면 ApplicationFlow.qml 의 themeButton 함수가 호출됩니다:

function themeButton() {
    if (Colors.currentTheme == Colors.dark) {
        Colors.currentTheme = Colors.light
    } else {
        Colors.currentTheme = Colors.dark
    }
}

이 함수는 Colors.qml 의 currentTheme 속성을 변경하며, 속성 바인딩을 통해 앱 전체의 색상이 자동으로 변경됩니다. 앱에서 사용되는 모든 색상은 Colors.qml 에 정의되어 있습니다.

테마가 밝은 테마로 전환되면 테마 변경 아이콘이 반달 모양으로 바뀝니다.

커피 카드 중 하나를 누르면 CoffeeCard.qml 내의 AbstractButton 에서 상태 변경이 발생하며, 이는 Transition 을 통해 NumberAnimation 을 트리거합니다:

AbstractButton {
    width: parent.width - 2
    height: parent.height - 2
    anchors.horizontalCenter: parent.horizontalCenter
    anchors.verticalCenter: parent.verticalCenter
    id: button
    hoverEnabled: true
    checkable: true
    enabled: (root.cupsLeft != 0) ? true : false
    transitions: Transition {
        NumberAnimation {
            properties: "scale"
            duration: 50
            easing.type: Easing.InOutQuad
        }
    }

이렇게 하면 선택된 커피 카드가 더 작은 크기로 축소되고 카드의 가장자리가 녹색으로 바뀌어, 사용자에게 해당 커피가 실제로 선택되었음을 알립니다.

모든 커피 카드의 button 속성 별칭인 onClicked 함수는 ApplicationFlow.qml 에 위치한 함수와 바인딩됩니다. 사용자가 커피 카드 중 하나를 누르면, 선택된 커피 옵션과 일치하는 이름을 가진 함수가 호출됩니다. 이 함수는 ApplicationFlow.qml 의 상태를 Settings 로 설정하고, StackView 에 새로운 컴포넌트를 추가하며, 이에 따라 재료 속성을 설정합니다.

커피 설정 구현

이 페이지에서 사용자는 사용자 정의 Slider QML 유형을 조정하여 자신의 취향에 맞게 커피 옵션을 맞춤 설정할 수 있습니다. 슬라이더의 값을 변경하면 커피잔 안에 보이는 액체 양에 영향을 미치는데, 이는 Cup.qml 내부의 액체 이미지 높이를 해당 슬라이더의 값에 바인딩함으로써 가능해집니다.

Settings 페이지의 슬라이더 값은 Synchronizer QML 유형을 사용하여 나중에 활용할 수 있도록 CoffeeConfig 에 저장됩니다.

Synchronizer {
     sourceObject: settingsForm.coffeeConfig
     sourceProperty: "sugarAmount"
     targetObject: settingsForm.sugarSlider
     targetProperty: "value"
}

시작(Start) 버튼을 클릭하면 ApplicationFlow.qml 의 상태가 "삽입(Insert)"으로 변경되고, 앱에서 Insert.qml 가 표시됩니다.

컵 삽입 기능 구현

이 페이지에서는 추출 과정이 시작되기 전에 사용자가 머신에 컵을 삽입하도록 안내합니다.

Continue 버튼을 누르면 앱이 Progress 페이지로 이동합니다.

커피 추출 구현

진행 상황 페이지에는 커피잔과 진행률 표시줄이 표시되며, 둘 다 각자의 방식으로 추출 과정을 실시간으로 알려줍니다.

여기서 커피잔은 채워지면 사용자가 Settings 페이지에서 선택한 설정과 정확히 동일한 구성을 표시하게 되며, 이는 Cup 속성 변수를 ApplicationFlow.qml 에 저장된 해당 값에 바인딩함으로써 보장됩니다.

커피잔을 채우는 애니메이션은 상태 엔진 Transition 및 SmoothedAnimation 을 통해 구현됩니다.

Cup {
    id: cup
    Layout.alignment: Qt.AlignHCenter | Qt.AlignTop
    state: "0"
}

진행률 표시줄 애니메이션은 Behavior 를 사용하여 구현됩니다.

Behavior on greenBar.width {
    SmoothedAnimation {
        easing.type: Easing.Linear
        velocity: (progressForm.contentItem.width / progressForm.brewTime) * 1000
    }
}

Timer 추출이 완료되면 애플리케이션 상태 업데이트를 담당합니다.

Timer {
    id: timer
    interval: progressForm.brewTime
    running: true
    onTriggered: {
        progressForm.appFlow.onFinished()
    }
}
커피 준비 완료 구현

'준비 완료' 페이지에는 사용자가 선택한 구성에 따라 채워진 커피잔과 "커피가 준비되었습니다"라는 텍스트, 그리고 체크 아이콘이 표시됩니다.

이 페이지가 표시되면 Timer 가 실행되기 시작하고, 설정된 간격이 지나면 사용자는 Home.qml 로 다시 이동합니다.

소스 파일

Squish GUI 테스트

이 애플리케이션에는 Android용 Qt GUI를 대상으로 하는 Squish 테스트가 포함되어 있습니다. 테스트는 Squish for Qt로 제작되었으며 Python으로 작성되었고, 애플리케이션 테스트 디렉터리에서 확인할 수 있습니다.

Android용 Squish for Qt를 사용할 때는 Qt 내장 후크를 사용하고 adb를 통해 Squish 포트를 전달해야 합니다. dlopen 이 실패하면서 "Cannot load library" 오류가 발생하면, CMake 구성에서 QT_USE_TARGET_ANDROID_BUILD_DIR 를 비활성화하거나 Qt Creator 의 Projects->Build Settings->CMake->Current Configuration 에서 해당 항목을 삭제하십시오.

Squish 라이선스가 없는 경우, 무료 평가판을 이용할 수 있습니다.

code.qt.io의 예제 프로젝트

모든 Qt 예제도참조하십시오 .

© 2026 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.