docs: [rd] 온보딩 도움말기능 문서 갱신 (알려진 제약, 이관 방향, 스크립트 주의사항)
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
# 온보딩 도움말기능 (Driver.js 기반)
|
||||
|
||||
> **작성일**: 2026-03-22
|
||||
> **상태**: 운영 중
|
||||
> **상태**: 운영 중 (알려진 제약 있음)
|
||||
> **프로젝트**: SAM MNG (관리자 웹)
|
||||
> **라우트**: `/rd/onboarding-guide`
|
||||
|
||||
@@ -19,10 +19,52 @@ SAM MNG의 각 화면에 처음 진입하는 사용자에게 주요 기능을 **
|
||||
- `SamOnboarding` JS 래퍼 클래스로 프로젝트 전역에서 일관된 사용
|
||||
- `localStorage`로 완료 상태 저장 (첫 방문 시에만 자동 시작)
|
||||
- 각 페이지 Blade 파일에서 간단한 코드 추가로 적용 가능
|
||||
- Driver.js CSS는 **동적 로드** (가이드 `start()` 시점에만 로드, 전역 레이아웃 충돌 방지)
|
||||
|
||||
---
|
||||
|
||||
## 2. 기술 스택
|
||||
## 2. 알려진 제약 및 주의사항
|
||||
|
||||
> **경고: MNG 환경에서 Driver.js + HTMX 조합의 구조적 제약이 있다.**
|
||||
|
||||
### 2.1 HTMX 레이아웃 충돌
|
||||
|
||||
| 접근 방식 | 레이아웃 | 가이드 동작 |
|
||||
|----------|:--------:|:----------:|
|
||||
| **URL 직접 입력 / F5 새로고침** | ✅ 정상 | ✅ 정상 |
|
||||
| **사이드바 메뉴 클릭 (HTMX 네비게이션)** | ❌ 깨짐 | - |
|
||||
|
||||
### 2.2 원인
|
||||
|
||||
MNG는 **HTMX 듀얼 로드** 환경이다:
|
||||
- CDN: `htmx.org@1.9.10` (레이아웃 `<head>`에서 동기 로드)
|
||||
- NPM: `htmx.org@2.0.8` (Vite 번들에서 모듈 로드)
|
||||
|
||||
사이드바의 `hx-boost="true"` + `hx-swap="outerHTML"` + `HX-Redirect` 응답 조합에서 두 HTMX 인스턴스 간 처리 충돌이 발생하여, 페이지 전체 리로드 시 레이아웃이 깨진다.
|
||||
|
||||
### 2.3 현재 대응
|
||||
|
||||
- 컨트롤러에서 `HX-Redirect`로 전체 페이지 리로드를 강제한다 (다른 RD 페이지와 동일 패턴)
|
||||
- Driver.js CSS는 `start()` 호출 시에만 동적 `import()`로 로드한다
|
||||
- SAM 커스텀 테마는 JS에서 `<style>` 태그를 동적 생성하여 주입한다
|
||||
|
||||
### 2.4 이관 시 해결 방향
|
||||
|
||||
서비스 이관(API + React) 시 다음을 적용하면 문제가 해결된다:
|
||||
|
||||
1. **HTMX 버전 단일화**: CDN 1.9.10 제거, NPM 2.0.8만 사용
|
||||
2. **React 환경**: HTMX 대신 React Router 사용 → `hx-boost` 충돌 자체가 없음
|
||||
3. **Driver.js React 래퍼**: React 컴포넌트로 래핑하여 lifecycle과 통합
|
||||
|
||||
```
|
||||
이관 우선순위:
|
||||
1단계 — HTMX 단일화 (MNG 내에서 해결 가능)
|
||||
2단계 — React 이관 시 온보딩 컴포넌트로 재구현
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 기술 스택
|
||||
|
||||
| 항목 | 내용 |
|
||||
|------|------|
|
||||
@@ -31,19 +73,23 @@ SAM MNG의 각 화면에 처음 진입하는 사용자에게 주요 기능을 **
|
||||
| 래퍼 모듈 | `resources/js/onboarding-guide.js` |
|
||||
| 상태 저장 | `localStorage` (`sam_onboarding_{pageKey}`) |
|
||||
| 전역 객체 | `window.SamOnboarding`, `window.GuideRegistry` |
|
||||
| CSS 로드 | 동적 `import('driver.js/dist/driver.css')` (가이드 시작 시에만) |
|
||||
| 커스텀 테마 | JS 동적 `<style>` 주입 (`loadDriverCss()` 함수 내) |
|
||||
|
||||
---
|
||||
|
||||
## 3. 파일 구조
|
||||
## 4. 파일 구조
|
||||
|
||||
```
|
||||
mng/
|
||||
├── resources/
|
||||
│ └── js/
|
||||
│ ├── app.js ← Driver.js import, 전역 등록
|
||||
│ └── onboarding-guide.js ← SamOnboarding 클래스, GuideRegistry
|
||||
│ ├── js/
|
||||
│ │ ├── app.js ← SamOnboarding, GuideRegistry 전역 등록
|
||||
│ │ └── onboarding-guide.js ← SamOnboarding 클래스, GuideRegistry, CSS 동적 로드
|
||||
│ └── css/
|
||||
│ └── app.css ← #app-container overflow 보호 규칙
|
||||
├── app/Http/Controllers/
|
||||
│ └── RdController.php ← onboardingGuide() 메서드
|
||||
│ └── RdController.php ← onboardingGuide() (HX-Redirect 사용)
|
||||
├── routes/
|
||||
│ └── web.php ← /rd/onboarding-guide 라우트
|
||||
└── resources/views/rd/
|
||||
@@ -54,53 +100,49 @@ mng/
|
||||
|
||||
---
|
||||
|
||||
## 4. 사용법
|
||||
## 5. 사용법
|
||||
|
||||
### 4.1 기본 사용 (Blade 파일)
|
||||
### 5.1 기본 사용 (Blade 파일)
|
||||
|
||||
```javascript
|
||||
// @push('scripts') 안에 추가
|
||||
const guide = new SamOnboarding('page-key', [
|
||||
{
|
||||
element: '#target-id',
|
||||
title: '제목',
|
||||
description: '설명 텍스트 (HTML 가능)',
|
||||
side: 'bottom', // top, bottom, left, right (선택)
|
||||
align: 'start', // start, center, end (선택)
|
||||
},
|
||||
{
|
||||
element: '.another-element',
|
||||
title: '두 번째 스텝',
|
||||
description: '다음 설명',
|
||||
},
|
||||
]);
|
||||
// ❌ <script type="module"> 사용 금지 — HTMX 스왑 시 실행 안됨
|
||||
// ✅ <script> 사용 + SamOnboarding 로드 대기 패턴
|
||||
|
||||
// 첫 방문 시에만 자동 시작
|
||||
guide.startIfFirstVisit();
|
||||
var _guide = null;
|
||||
function startGuide() { if (_guide) { _guide.reset(); _guide.start(); } }
|
||||
|
||||
// 또는 버튼 클릭으로 시작
|
||||
document.getElementById('help-btn').onclick = () => guide.start();
|
||||
(function waitForOnboarding() {
|
||||
if (typeof SamOnboarding === 'undefined') { setTimeout(waitForOnboarding, 100); return; }
|
||||
|
||||
_guide = new SamOnboarding('page-key', [
|
||||
{ element: '#target-id', title: '제목', description: '설명', side: 'bottom' },
|
||||
{ element: '.another', title: '두 번째', description: '설명' },
|
||||
]);
|
||||
|
||||
// 첫 방문 시에만 자동 시작
|
||||
_guide.startIfFirstVisit();
|
||||
})();
|
||||
```
|
||||
|
||||
### 4.2 도움말 버튼 패턴
|
||||
### 5.2 도움말 버튼 패턴
|
||||
|
||||
```html
|
||||
<!-- 페이지 헤더에 ? 버튼 추가 -->
|
||||
<button onclick="guide.start()" class="bg-white hover:bg-gray-100 text-gray-500 px-3 py-2 rounded-lg border transition" title="도움말 보기">
|
||||
<button onclick="startGuide()" class="bg-white hover:bg-gray-100 text-gray-500 px-3 py-2 rounded-lg border transition" title="도움말 보기">
|
||||
<i class="ri-question-line"></i>
|
||||
</button>
|
||||
```
|
||||
|
||||
### 4.3 SamOnboarding 클래스 API
|
||||
### 5.3 SamOnboarding 클래스 API
|
||||
|
||||
| 메서드 | 설명 |
|
||||
|--------|------|
|
||||
| `start()` | 가이드 즉시 시작 |
|
||||
| `start()` | 가이드 즉시 시작 (Driver.js CSS 동적 로드 + 커스텀 테마 주입) |
|
||||
| `startIfFirstVisit()` | 첫 방문(미완료) 시에만 자동 시작 (500ms 딜레이) |
|
||||
| `reset()` | 완료 상태 초기화 (다시 보기) |
|
||||
| `isCompleted()` | 완료 여부 반환 (`boolean`) |
|
||||
|
||||
### 4.4 스텝 옵션
|
||||
### 5.4 스텝 옵션
|
||||
|
||||
| 속성 | 필수 | 설명 |
|
||||
|------|:----:|------|
|
||||
@@ -112,7 +154,7 @@ document.getElementById('help-btn').onclick = () => guide.start();
|
||||
|
||||
---
|
||||
|
||||
## 5. 적용 현황
|
||||
## 6. 적용 현황
|
||||
|
||||
| 페이지 | pageKey | 스텝 수 | 자동 시작 | 도움말 버튼 |
|
||||
|--------|---------|:-------:|:---------:|:-----------:|
|
||||
@@ -121,62 +163,61 @@ document.getElementById('help-btn').onclick = () => guide.start();
|
||||
|
||||
---
|
||||
|
||||
## 6. 커스터마이징
|
||||
## 7. 스크립트 작성 시 주의사항
|
||||
|
||||
### 6.1 SAM 테마 스타일
|
||||
### 7.1 `<script type="module">` 금지
|
||||
|
||||
Driver.js 기본 스타일을 SAM UI에 맞게 커스텀한다. `sam-onboarding-popover` CSS 클래스를 사용.
|
||||
|
||||
```css
|
||||
.sam-onboarding-popover .driver-popover {
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 20px 60px rgba(0,0,0,0.15);
|
||||
}
|
||||
.driver-popover-next-btn {
|
||||
background-color: #4f46e5; /* indigo-600 */
|
||||
border-radius: 8px;
|
||||
}
|
||||
```
|
||||
❌ <script type="module"> — Vite 모듈보다 늦게 실행되지만 HTMX 스왑 시 실행 안됨
|
||||
✅ <script> + SamOnboarding 로드 대기 패턴 (setTimeout 폴링)
|
||||
```
|
||||
|
||||
### 6.2 한국어 버튼 텍스트
|
||||
### 7.2 전역 함수 즉시 정의
|
||||
|
||||
SamOnboarding 기본 설정:
|
||||
`onclick` 핸들러에서 사용하는 함수는 스크립트 최상단에 즉시 정의한다. `SamOnboarding` 로드 전 클릭 시 `ReferenceError` 방지.
|
||||
|
||||
| 버튼 | 텍스트 |
|
||||
|------|--------|
|
||||
| 다음 | `다음` |
|
||||
| 이전 | `이전` |
|
||||
| 완료 | `완료` |
|
||||
| 진행률 | `{{current}} / {{total}}` |
|
||||
```javascript
|
||||
// ✅ 즉시 정의 (SamOnboarding 로드 전에도 에러 없음)
|
||||
var _guide = null;
|
||||
function startGuide() { if (!_guide) return; _guide.start(); }
|
||||
|
||||
// ❌ init() 안에서 window.startGuide = ... (로드 전 클릭 시 에러)
|
||||
```
|
||||
|
||||
### 7.3 Driver.js CSS 동적 로드
|
||||
|
||||
Driver.js CSS를 전역 번들에 포함하면 `all:unset`, `:has()` 등 공격적 CSS 규칙이 MNG 레이아웃(flex, overflow)과 충돌한다. 반드시 `start()` 시점에만 동적 로드한다.
|
||||
|
||||
```
|
||||
❌ import 'driver.js/dist/driver.css' — app.js 전역 import 금지
|
||||
✅ import('driver.js/dist/driver.css') — start() 내에서 동적 import
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. GuideRegistry (선택 사용)
|
||||
## 8. GuideRegistry (선택 사용)
|
||||
|
||||
여러 페이지의 가이드를 중앙에서 관리할 때 사용한다.
|
||||
|
||||
```javascript
|
||||
// 가이드 등록
|
||||
GuideRegistry.register('my-page', [
|
||||
{ element: '#btn', title: '버튼', description: '설명' },
|
||||
]);
|
||||
|
||||
// 등록된 가이드 인스턴스 생성 및 시작
|
||||
const guide = GuideRegistry.create('my-page');
|
||||
var guide = GuideRegistry.create('my-page');
|
||||
guide.start();
|
||||
|
||||
// 등록된 모든 가이드 키 조회
|
||||
const keys = GuideRegistry.keys(); // ['my-page', ...]
|
||||
var keys = GuideRegistry.keys(); // ['my-page', ...]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. 새 페이지에 가이드 추가하기
|
||||
## 9. 새 페이지에 가이드 추가하기
|
||||
|
||||
1. 가이드 대상 요소에 `id` 부여 (예: `id="btn-create"`, `id="data-table"`)
|
||||
2. Blade 파일의 `@push('scripts')` 안에 `SamOnboarding` 코드 추가
|
||||
3. (선택) 페이지 헤더에 `?` 도움말 버튼 추가
|
||||
4. 온보딩 관리 페이지(`/rd/onboarding-guide`)에서 적용 확인
|
||||
2. Blade 파일의 `@push('scripts')` 안에 일반 `<script>` + 로드 대기 패턴 추가
|
||||
3. `onclick` 핸들러 함수를 스크립트 최상단에 즉시 정의
|
||||
4. (선택) 페이지 헤더에 `?` 도움말 버튼 추가
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user