앱 도메인
웹 앱에 적용된 미니 앱 프로그래밍 방식을 보여주려면 작지만 충분히 완전한 앱 아이디어가 필요했습니다. 고강도 인터벌 트레이닝 (HIIT)은 짧은 시간 동안의 격렬한 무산소 운동과 강도가 낮은 회복 기간을 번갈아 가며 진행하는 심혈관 운동 전략입니다. 많은 HIIT 트레이닝에서 HIIT 타이머를 사용합니다. 예를 들어 The Body Coach TV YouTube 채널의 30분 온라인 세션을 참고하세요.
HIIT Time 예시 앱
이 장에서는 사용자가 고강도 및 저강도 인터벌로 구성된 다양한 타이머를 정의하고 관리한 다음 훈련 세션에 사용할 타이머를 선택할 수 있는 'HIIT Time'이라는 HIIT 타이머 애플리케이션의 기본 예를 빌드했습니다. 탐색 메뉴, 탭 표시줄, 세 페이지가 있는 반응형 앱입니다.
- 운동: 운동 중 활성 페이지입니다. 사용자가 타이머 중 하나를 선택할 수 있으며 세트 수, 활동 기간, 휴식 기간의 세 가지 진행률 표시기가 표시됩니다.
- 타이머: 기존 타이머를 관리하고 사용자가 새 타이머를 만들 수 있도록 지원합니다.
- 환경설정: 음향 효과 및 음성 출력을 전환하고 언어 및 테마를 선택할 수 있습니다.
다음 스크린샷을 통해 애플리케이션을 파악할 수 있습니다.
앱 구조
위에서 설명한 것처럼 앱은 탐색 메뉴, 탭 표시줄, 세 개의 페이지로 구성되어 있으며 그리드로 배치되어 있습니다.
탐색 메뉴와 탭 표시줄은 iframe으로 구현되며, 그 사이에 <div> 컨테이너가 있고 페이지용 iframe이 3개 더 있습니다. 이 중 하나는 항상 표시되며 탭 표시줄의 활성 선택에 따라 달라집니다.
about:blank를 가리키는 최종 iframe은 기존 타이머를 수정하거나 새 타이머를 만드는 데 필요한 동적으로 생성된 인앱 페이지에 사용됩니다.
이 패턴을 다중 페이지 단일 페이지 앱 (MPSPA)이라고 합니다.
구성요소 기반 lit-html 마크업
각 페이지의 구조는 런타임에 동적으로 평가되는 lit-html 스캐폴드로 구현됩니다.
lit-html은 JavaScript를 위한 효율적이고 표현력이 뛰어나며 확장 가능한 HTML 템플릿 라이브러리입니다.
HTML 파일에서 직접 사용하면 정신 프로그래밍 모델이 직접 출력 지향적입니다.
프로그래머는 최종 출력의 모양을 나타내는 템플릿을 작성하고 lit-html은 데이터에 따라 간격을 동적으로 채우고 이벤트 리스너를 연결합니다.
앱이 Shoelace의 <sl-progress-ring>와 같은 서드 파티 맞춤 요소나 <human-duration>라는 자체 구현 맞춤 요소를 사용합니다.
맞춤 요소에는 선언적 API (예: 달성률 링의 percentage 속성)가 있으므로 아래 목록에서 볼 수 있듯이 lit-html과 함께 잘 작동합니다.
<div>
<button class="start" @click="${eventHandlers.start}" type="button">
${strings.START}
</button>
<button class="pause" @click="${eventHandlers.pause}" type="button">
${strings.PAUSE}
</button>
<button class="reset" @click="${eventHandlers.reset}" type="button">
${strings.RESET}
</button>
</div>
<div class="progress-rings">
<sl-progress-ring
class="sets"
percentage="${Math.floor(data.sets/data.activeTimer.sets*100)}"
>
<div class="progress-ring-caption">
<span>${strings.SETS}</span>
<span>${data.sets}</span>
</div>
</sl-progress-ring>
</div>
프로그래밍 모델
각 페이지에는 이벤트 핸들러의 구현을 제공하고 각 페이지의 데이터를 제공하여 lit-html 마크업을 채우는 해당 Page 클래스가 있습니다.
이 클래스는 onShow(), onHide(), onLoad(), onUnload()와 같은 수명 주기 메서드도 지원합니다.
페이지는 페이지별로 선택적으로 지속되는 상태와 전역 상태를 공유하는 데 사용되는 데이터 스토어에 액세스할 수 있습니다.
모든 문자열이 중앙에서 관리되므로 국제화가 내장되어 있습니다.
앱에서 하는 일은 iframe 공개 상태를 전환하고 동적으로 생성된 페이지의 경우 자리표시자 iframe의 src 속성을 변경하는 것이 전부이므로 라우팅은 기본적으로 무료로 브라우저에서 처리합니다.
아래 예는 동적으로 생성된 페이지를 닫는 코드를 보여줍니다.
import Page from '../page.js';
const page = new Page({
eventHandlers: {
back: (e) => {
e.preventDefault();
window.top.history.back();
},
},
});
스타일 지정
페이지 스타일 지정은 자체 범위가 지정된 CSS 파일에서 페이지별로 발생합니다.
즉, 다른 페이지와의 충돌이 발생할 수 없으므로 일반적으로 요소 이름으로 요소를 직접 처리할 수 있습니다.
전역 스타일이 각 페이지에 추가되므로 font-family 또는 box-sizing과 같은 중앙 설정을 반복적으로 선언할 필요가 없습니다.
테마와 어두운 모드 옵션도 여기에서 정의됩니다.
아래 목록은 그리드에 다양한 양식 요소를 배치하는 환경설정 페이지의 규칙을 보여줍니다.
main {
max-width: 600px;
}
form {
display: grid;
grid-template-columns: auto 1fr;
grid-gap: 0.5rem;
margin-block-end: 1rem;
}
label {
text-align: end;
grid-column: 1 / 2;
}
input,
select {
grid-column: 2 / 3;
}
화면 wake lock
운동 중에는 화면이 꺼지면 안 됩니다. 이를 지원하는 브라우저에서 HIIT 시간은 wake lock을 통해 이를 실현합니다. 아래 스니펫은 이 작업을 수행하는 방법을 보여줍니다.
if ('wakeLock' in navigator) {
const requestWakeLock = async () => {
try {
page.shared.wakeLock = await navigator.wakeLock.request('screen');
page.shared.wakeLock.addEventListener('release', () => {
// Nothing.
});
} catch (err) {
console.error(`${err.name}, ${err.message}`);
}
};
// Request a screen wake lock…
await requestWakeLock();
// …and re-request it when the page becomes visible.
document.addEventListener('visibilitychange', async () => {
if (
page.shared.wakeLock !== null &&
document.visibilityState === 'visible'
) {
await requestWakeLock();
}
});
}
애플리케이션 테스트
HIIT Time 애플리케이션은 GitHub에서 확인할 수 있습니다. 새 창에서 데모를 사용해 보거나 아래 iframe 삽입에서 휴대기기를 시뮬레이션해 볼 수 있습니다.