앱 도메인
웹 앱에 적용된 미니 앱 프로그래밍 방식을 보여주려면 작지만 충분히 완성된 앱 아이디어가 필요했습니다. 고강도 인터벌 트레이닝(HIIT)은 짧은 시간 동안 강렬한 무산소 운동과 덜 강렬한 회복 시간을 번갈아 가며 진행하는 심혈관 운동 전략입니다. 많은 HIIT 트레이닝에서 HIIT 타이머를 사용합니다. 예를 들어 The Body Coach TV YouTube 채널의 30분 온라인 세션을 들 수 있습니다.
HIIT Time 예시 앱
이 챕터에서는 사용자가 항상 고강도 및 저강도 간격으로 구성된 다양한 타이머를 정의하고 관리할 수 있는 'HIIT Time'이라는 적절한 이름을 가진 이러한 HIIT 타이머 애플리케이션의 기본 예를 빌드한 후 교육 세션 중 하나를 선택할 수 있도록 했습니다. 탐색 메뉴, 탭 메뉴, 세 페이지가 있는 반응형 앱입니다.
- 운동: 운동 중 활성 페이지입니다. 사용자가 타이머 중 하나를 선택할 수 있으며 세트 수, 활동 시간, 휴식 시간 등 세 가지 진행률 링이 있습니다.
- 타이머: 기존 타이머를 관리하고 사용자가 새 타이머를 만들 수 있도록 허용합니다.
- 환경설정: 음향 효과 및 음성 출력을 전환하고 언어 및 테마를 선택할 수 있습니다.
다음 스크린샷은 애플리케이션의 느낌을 보여줍니다.
앱 구조
위에서 설명한 대로 앱은 탐색 메뉴, 탭 메뉴, 그리드로 정렬된 세 페이지로 구성됩니다.
탐색 메뉴와 탭 바는 <div>
컨테이너가 사이에 있는 iframe으로 구현되며, 페이지의 iframe이 세 개 더 있습니다. 이 중 하나는 항상 표시되며 탭 바의 활성 선택에 따라 다릅니다.
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 Time은 화면 깨우기 잠금을 통해 이를 실현합니다. 아래 스니펫은 이를 수행하는 방법을 보여줍니다.
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 삽입에서 바로 데모를 사용해 볼 수 있습니다.