fix(site,skill): 모바일 상단 바 수리 — 탭 전환 스크롤 리셋과 단일 sticky 헤더
사용자 보고 '상단 바가 페이지마다 왔다갔다하며 패딩이 없고 포지션도 이상하다'를 계측으로 원인 규명: - 탭 전환 시 이전 뷰의 window.scrollY 잔류 — 짧은 뷰에선 클램프(600→49)되어 헤더가 반쯤 잘림. setCurrentView 에 scrollTo(0,0) 리셋(양앱) - ≤900 헤더 이중 스택(브랜드 줄 43px+크럼 52px)·비sticky·safe-top 무 — .side 숨기고 .topbar 를 sticky+safe-top 단일 헤더로(두레 앱바 문법). 데스크톱 무변경 - 재발 방지: e2e M5 헤더·스크롤 스위트 10단언(스크롤 300px+에서 탭 전환 → scrollY=0·헤더 top=0), 시각 기준 갱신(390px 9화면 의도 변경), verify L0~L6 재통과 - 스킬 mobile-app-ux.md 에 '뷰 전환은 스크롤을 리셋·헤더는 하나' 규칙 반영. 가온 CSS v23
|
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 70 KiB |
|
Before Width: | Height: | Size: 149 KiB After Width: | Height: | Size: 149 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 67 KiB |
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 117 KiB After Width: | Height: | Size: 117 KiB |
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 53 KiB |
BIN
apps/site/tools/__shots__/diag-gaon-390-dashboard.png
Normal file
|
After Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 42 KiB |
|
|
@ -1,14 +1,15 @@
|
|||
# E2E 시나리오 게이트 — 2026-08-23 05:32
|
||||
# E2E 시나리오 게이트 — 2026-08-23 05:48
|
||||
|
||||
단언 1762 · 통과 1762 · 실패 0 — **GREEN — 전 단언 통과**
|
||||
단언 1754 · 통과 1754 · 실패 0 — **GREEN — 전 단언 통과**
|
||||
|
||||
| 스위트 | 통과 | 실패 |
|
||||
|---|---|---|
|
||||
| M4 앱 기반 | 22 | 0 |
|
||||
| M5 헤더·스크롤 | 10 | 0 |
|
||||
| M320 매트릭스 | 90 | 0 |
|
||||
| M390 매트릭스 | 105 | 0 |
|
||||
| M1 터치타깃 | 231 | 0 |
|
||||
| M2 대비 | 1120 | 0 |
|
||||
| M1 터치타깃 | 222 | 0 |
|
||||
| M2 대비 | 1111 | 0 |
|
||||
| M768 매트릭스 | 90 | 0 |
|
||||
| S7 뒤로 가기 | 6 | 0 |
|
||||
| S7 뒤로 가기·모달 | 5 | 0 |
|
||||
|
|
|
|||
|
|
@ -187,6 +187,37 @@ for (const [app] of Object.entries(APPS)) {
|
|||
ok(await page.evaluate(() => !!document.querySelector(".bottom-nav")), S, `${app} 바텀 내비 존재`);
|
||||
}
|
||||
|
||||
// ════════ M5 — 헤더·스크롤(탭 전환은 화면 맨 위에서, 헤더는 상단에 남는다) ════════
|
||||
// 실측 사고: 이전 뷰의 스크롤이 남아 짧은 뷰에서 클램프되면 헤더가 반쯤 잘리고,
|
||||
// 뷰마다 크롬 위치가 제각각이 되어 "상단 바가 왔다갔다한다"는 보고로 나타났다
|
||||
for (const [app, cfg] of Object.entries(APPS)) {
|
||||
const S = "M5 헤더·스크롤";
|
||||
await page.setViewport({ width: 390, height: 844, isMobile: true, hasTouch: true });
|
||||
await openApp(app);
|
||||
const headerSel = app === "gaon-lms" ? ".topbar" : ".appbar";
|
||||
const head = await page.evaluate((sel) => {
|
||||
const el = document.querySelector(sel);
|
||||
if (!el) return null;
|
||||
const cs = getComputedStyle(el);
|
||||
const r = el.getBoundingClientRect();
|
||||
return { pos: cs.position, top: Math.round(r.top), h: Math.round(r.height) };
|
||||
}, headerSel);
|
||||
ok(!!head && (head.pos === "sticky" || head.pos === "fixed"), S, `${app} 헤더 sticky/fixed`, head ? head.pos : "요소 없음");
|
||||
ok(head && head.top === 0 && head.h >= 44, S, `${app} 헤더 상단 밀착·높이`, head ? head.top + "px/" + head.h + "px" : "-");
|
||||
// 스크롤 내린 상태에서 탭 전환 — 새 화면은 맨 위에서 시작해야 한다
|
||||
await page.evaluate(() => window.scrollTo(0, 500));
|
||||
await new Promise((r) => setTimeout(r, 60));
|
||||
const scrolled = await page.evaluate(() => window.scrollY);
|
||||
await switchView(app, cfg.views[1]);
|
||||
const after = await page.evaluate((sel) => ({
|
||||
y: window.scrollY,
|
||||
top: Math.round(document.querySelector(sel).getBoundingClientRect().top),
|
||||
}), headerSel);
|
||||
ok(scrolled > 300, S, `${app} 사전 조건: 300px+ 스크롤`, scrolled + "px");
|
||||
ok(after.y === 0, S, `${app} 탭 전환 후 스크롤 리셋`, after.y + "px");
|
||||
ok(after.top === 0, S, `${app} 탭 전환 후 헤더 상단 밀착`, after.top + "px");
|
||||
}
|
||||
|
||||
// ════════ M3+M1+M2 — 구조·터치타깃·대비 매트릭스(전 뷰 × 320/390/768) ════════
|
||||
for (const [app, cfg] of Object.entries(APPS)) {
|
||||
for (const w of [320, 390, 768]) {
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
# 검증 리포트 — 2026-08-23 05:32
|
||||
# 검증 리포트 — 2026-08-23 05:48
|
||||
|
||||
게이트: **전 계층 통과 — 배포 가능** · 총 84.4초
|
||||
게이트: **전 계층 통과 — 배포 가능** · 총 85.9초
|
||||
|
||||
| 계층 | 결과 | 항목 | 소요 |
|
||||
|---|---|---|---|
|
||||
| L0-정적/stylelint | PASS | PASS — | 1.7s |
|
||||
| L0-정적/stylelint | PASS | PASS — | 1.4s |
|
||||
| L0-정식/html-validate | PASS | PASS — | 1.2s |
|
||||
| L1-단위/계산로직 | PASS | PASS 41 | 0.0s |
|
||||
| L2-기능/감사 | PASS | PASS 72 | 9.7s |
|
||||
| L2-불변식/렌더 | PASS | PASS 10 | 16.7s |
|
||||
| L3-시각회귀/diff | PASS | PASS 30 | 11.4s |
|
||||
| L4-WebKit | PASS | PASS 8 | 3.9s |
|
||||
| L5-탐색/키보드 | PASS | PASS 14 | 6.1s |
|
||||
| L6-시나리오/E2E | PASS | PASS — | 33.6s |
|
||||
| L2-기능/감사 | PASS | PASS 72 | 9.6s |
|
||||
| L2-불변식/렌더 | PASS | PASS 10 | 16.9s |
|
||||
| L3-시각회귀/diff | PASS | PASS 30 | 11.3s |
|
||||
| L4-WebKit | PASS | PASS 8 | 3.5s |
|
||||
| L5-탐색/키보드 | PASS | PASS 14 | 6.3s |
|
||||
| L6-시나리오/E2E | PASS | PASS — | 35.5s |
|
||||
|
|
|
|||