353 lines
10 KiB
Markdown
353 lines
10 KiB
Markdown
# MNG 모바일 반응형 구현 계획
|
|
|
|
> 작성일: 2025-12-19
|
|
> 최종 수정: 2025-12-19
|
|
> 상태: 진행 중 (Phase 1 완료)
|
|
|
|
## 1. 구현 전략
|
|
|
|
### 1.1 기본 방침
|
|
- **Mobile-First 아님**: 기존 데스크톱 레이아웃 유지하며 모바일 대응 추가
|
|
- **점진적 개선**: 핵심 레이아웃부터 단계별 적용
|
|
- **기존 코드 최소 변경**: 새로운 반응형 클래스 추가 방식
|
|
- **테이블 스와이프**: 카드형 변환 없이 수평 스크롤 개선
|
|
|
|
### 1.2 Breakpoint 전략
|
|
```
|
|
< 640px (sm 미만): 모바일 - 사이드바 숨김, 햄버거 메뉴
|
|
640px - 1024px: 태블릿 - 접힌 사이드바 또는 오버레이
|
|
1024px+ (lg): 데스크톱 - 전체 레이아웃 (현재 상태)
|
|
```
|
|
|
|
---
|
|
|
|
## 2. Phase 구성
|
|
|
|
### Phase 1: 핵심 레이아웃 (필수) ✅ 완료
|
|
**목표**: 모바일에서 기본 사용 가능하게
|
|
**완료일**: 2025-12-19
|
|
**커밋**: `093e98b`
|
|
|
|
| 항목 | 작업 내용 | 파일 | 상태 |
|
|
|------|----------|------|------|
|
|
| 1-1 | 사이드바 모바일 오버레이 | `sidebar.blade.php` | ✅ |
|
|
| 1-2 | 햄버거 메뉴 버튼 추가 | `header.blade.php` | ✅ |
|
|
| 1-3 | 레이아웃 반응형 조정 | `app.blade.php` | ✅ |
|
|
| 1-4 | 모바일 오버레이 백드롭 | `app.blade.php` | ✅ |
|
|
|
|
**구현 내용**:
|
|
- 모바일 사이드바 슬라이드 인/아웃 애니메이션
|
|
- 햄버거 버튼 + 모바일 로고 추가
|
|
- 반투명 백드롭 오버레이
|
|
- ESC 키 및 메뉴 클릭 시 자동 닫힘
|
|
|
|
### Phase 2: 헤더 최적화 ✅ 완료
|
|
**목표**: 헤더 요소들의 모바일 배치
|
|
**완료일**: 2025-12-19
|
|
|
|
| 항목 | 작업 내용 | 파일 | 상태 |
|
|
|------|----------|------|------|
|
|
| 2-1 | 사이드바에 모바일 테넌트 셀렉터 | `sidebar.blade.php` | ✅ |
|
|
| 2-2 | 헤더에 모바일 테넌트 뱃지 | `header.blade.php` | ✅ |
|
|
| 2-3 | 사용자 메뉴 모바일 최적화 | `header.blade.php` | ✅ |
|
|
|
|
**구현 내용**:
|
|
- 사이드바 상단에 모바일 전용 테넌트 드롭다운 추가 (lg:hidden)
|
|
- 헤더 로고 옆에 현재 테넌트 뱃지 표시 (클릭 시 사이드바 열림)
|
|
- 사용자 메뉴 chevron 아이콘 모바일에서 숨김, 패딩 축소
|
|
|
|
### Phase 3: 테이블 스와이프 ✅ 완료
|
|
**목표**: 모든 테이블에 스와이프 기능 적용
|
|
**완료일**: 2025-12-19
|
|
|
|
| 항목 | 작업 내용 | 대상 파일 | 상태 |
|
|
|------|----------|----------|------|
|
|
| 3-1 | 테이블 래퍼 컴포넌트 생성 | `components/table-swipe.blade.php` | ✅ |
|
|
| 3-2 | CSS 스크롤바/터치 스타일 | `resources/css/app.css` | ✅ |
|
|
| 3-3 | 11개 테이블 적용 | 각 모듈 테이블 | ✅ |
|
|
| 3-4 | 스와이프 힌트 UI | `table-swipe` 컴포넌트 내장 | ✅ |
|
|
|
|
**구현 내용**:
|
|
- `<x-table-swipe>` Anonymous Blade 컴포넌트
|
|
- `-webkit-overflow-scrolling: touch` 터치 스크롤 최적화
|
|
- sm(640px) 미만에서 좌우 화살표 애니메이션 힌트
|
|
- 스크롤바 얇은 스타일 (4px height)
|
|
|
|
### Phase 4: 폼/필터 반응형
|
|
**목표**: 필터 영역과 버튼 그룹 모바일 최적화
|
|
|
|
| 항목 | 작업 내용 | 적용 범위 |
|
|
|------|----------|----------|
|
|
| 4-1 | 필터 영역 flex-wrap | 각 index 페이지 |
|
|
| 4-2 | 버튼 그룹 스택 배치 | 각 페이지 헤더 |
|
|
| 4-3 | 모바일 필터 접기/펼치기 | 선택적 적용 |
|
|
|
|
**예상 작업량**: 낮음
|
|
**의존성**: 없음
|
|
|
|
### Phase 5: 제외 메뉴 처리
|
|
**목표**: 모바일에서 어려운 메뉴 안내/제한
|
|
|
|
| 항목 | 작업 내용 |
|
|
|------|----------|
|
|
| 5-1 | 데스크톱 전용 배너 컴포넌트 |
|
|
| 5-2 | 권한 매트릭스 페이지 적용 |
|
|
| 5-3 | Flow Tester 페이지 적용 |
|
|
| 5-4 | R&D Labs 영역 처리 |
|
|
|
|
**예상 작업량**: 낮음
|
|
**의존성**: 없음
|
|
|
|
---
|
|
|
|
## 3. 상세 구현 방안
|
|
|
|
### 3.1 사이드바 모바일 (Phase 1-1)
|
|
|
|
**변경 전:**
|
|
```html
|
|
<aside id="sidebar" class="sidebar w-64">
|
|
```
|
|
|
|
**변경 후:**
|
|
```html
|
|
<!-- 모바일 오버레이 백드롭 -->
|
|
<div id="sidebar-backdrop"
|
|
class="fixed inset-0 bg-black/50 z-40 lg:hidden hidden"
|
|
onclick="closeMobileSidebar()">
|
|
</div>
|
|
|
|
<!-- 사이드바: 모바일에서 fixed + transform -->
|
|
<aside id="sidebar"
|
|
class="sidebar fixed inset-y-0 left-0 z-50 w-64
|
|
transform -translate-x-full lg:translate-x-0 lg:static
|
|
transition-transform duration-300 ease-in-out">
|
|
```
|
|
|
|
**JavaScript 추가:**
|
|
```javascript
|
|
function openMobileSidebar() {
|
|
document.getElementById('sidebar').classList.remove('-translate-x-full');
|
|
document.getElementById('sidebar-backdrop').classList.remove('hidden');
|
|
document.body.classList.add('overflow-hidden');
|
|
}
|
|
|
|
function closeMobileSidebar() {
|
|
document.getElementById('sidebar').classList.add('-translate-x-full');
|
|
document.getElementById('sidebar-backdrop').classList.add('hidden');
|
|
document.body.classList.remove('overflow-hidden');
|
|
}
|
|
|
|
// ESC 키로 닫기
|
|
document.addEventListener('keydown', (e) => {
|
|
if (e.key === 'Escape') closeMobileSidebar();
|
|
});
|
|
```
|
|
|
|
### 3.2 헤더 햄버거 버튼 (Phase 1-2)
|
|
|
|
```html
|
|
<header class="h-16 flex items-center justify-between px-4 lg:px-6">
|
|
<div class="flex items-center gap-2 lg:gap-4">
|
|
<!-- 모바일 햄버거 -->
|
|
<button onclick="openMobileSidebar()"
|
|
class="p-2 rounded-lg hover:bg-gray-100 lg:hidden">
|
|
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
|
d="M4 6h16M4 12h16M4 18h16"/>
|
|
</svg>
|
|
</button>
|
|
|
|
<!-- 모바일 로고 -->
|
|
<span class="text-xl font-bold lg:hidden">SAM</span>
|
|
|
|
<!-- 데스크톱 테넌트 셀렉터 (기존 유지) -->
|
|
<div class="hidden lg:flex items-center gap-4">
|
|
...
|
|
</div>
|
|
</div>
|
|
```
|
|
|
|
### 3.3 테이블 스와이프 래퍼 (Phase 3-1)
|
|
|
|
**새 컴포넌트: `components/table-swipe.blade.php`**
|
|
```html
|
|
@props(['showHint' => true])
|
|
|
|
<div class="relative">
|
|
<!-- 스와이프 가능한 컨테이너 -->
|
|
<div class="overflow-x-auto -mx-4 sm:mx-0 scrollbar-thin">
|
|
<div class="inline-block min-w-full align-middle px-4 sm:px-0">
|
|
{{ $slot }}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 스와이프 힌트 (모바일에서만) -->
|
|
@if($showHint)
|
|
<div class="sm:hidden flex items-center justify-center gap-2 mt-2 text-xs text-gray-400">
|
|
<svg class="w-4 h-4 animate-bounce-x" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16l-4-4m0 0l4-4m-4 4h18"/>
|
|
</svg>
|
|
<span>좌우로 스와이프</span>
|
|
<svg class="w-4 h-4 animate-bounce-x" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 8l4 4m0 0l-4 4m4-4H3"/>
|
|
</svg>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
|
|
<style>
|
|
@keyframes bounce-x {
|
|
0%, 100% { transform: translateX(0); }
|
|
50% { transform: translateX(3px); }
|
|
}
|
|
.animate-bounce-x {
|
|
animation: bounce-x 1s ease-in-out infinite;
|
|
}
|
|
</style>
|
|
```
|
|
|
|
**적용 예시:**
|
|
```html
|
|
<!-- 변경 전 -->
|
|
<div class="bg-white rounded-lg shadow-sm overflow-hidden">
|
|
<table class="w-full">...</table>
|
|
</div>
|
|
|
|
<!-- 변경 후 -->
|
|
<div class="bg-white rounded-lg shadow-sm overflow-hidden">
|
|
<x-table-swipe>
|
|
<table class="min-w-full">...</table>
|
|
</x-table-swipe>
|
|
</div>
|
|
```
|
|
|
|
### 3.4 데스크톱 전용 배너 (Phase 5-1)
|
|
|
|
**새 컴포넌트: `components/desktop-only-banner.blade.php`**
|
|
```html
|
|
<div class="lg:hidden bg-amber-50 border border-amber-200 rounded-lg p-4 mb-4">
|
|
<div class="flex items-center gap-3">
|
|
<svg class="w-6 h-6 text-amber-600 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
|
d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"/>
|
|
</svg>
|
|
<div>
|
|
<p class="font-medium text-amber-800">데스크톱 환경 권장</p>
|
|
<p class="text-sm text-amber-600">이 기능은 넓은 화면에서 더 잘 동작합니다.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
```
|
|
|
|
---
|
|
|
|
## 4. CSS 추가 사항
|
|
|
|
### 4.1 tailwind.config.js 수정 (선택)
|
|
```javascript
|
|
module.exports = {
|
|
theme: {
|
|
extend: {
|
|
// 필요시 추가 breakpoint
|
|
screens: {
|
|
'xs': '375px', // 소형 모바일
|
|
},
|
|
},
|
|
},
|
|
plugins: [
|
|
require('@tailwindcss/forms'),
|
|
],
|
|
}
|
|
```
|
|
|
|
### 4.2 app.css 추가
|
|
```css
|
|
/* 스크롤바 스타일링 (선택) */
|
|
.scrollbar-thin::-webkit-scrollbar {
|
|
height: 4px;
|
|
}
|
|
.scrollbar-thin::-webkit-scrollbar-thumb {
|
|
background-color: #d1d5db;
|
|
border-radius: 2px;
|
|
}
|
|
.scrollbar-thin::-webkit-scrollbar-track {
|
|
background-color: #f3f4f6;
|
|
}
|
|
|
|
/* 모바일 사이드바 오버레이 */
|
|
.sidebar-mobile-open {
|
|
transform: translateX(0) !important;
|
|
}
|
|
|
|
/* 터치 스크롤 최적화 */
|
|
.overflow-x-auto {
|
|
-webkit-overflow-scrolling: touch;
|
|
scroll-snap-type: x proximity;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 5. 테스트 체크리스트
|
|
|
|
### 5.1 디바이스별 테스트
|
|
- [ ] iPhone SE (375px)
|
|
- [ ] iPhone 14 (390px)
|
|
- [ ] iPhone 14 Pro Max (430px)
|
|
- [ ] iPad Mini (768px)
|
|
- [ ] iPad Pro (1024px)
|
|
- [ ] 데스크톱 (1280px+)
|
|
|
|
### 5.2 기능별 테스트
|
|
- [ ] 사이드바 열기/닫기
|
|
- [ ] 햄버거 메뉴 동작
|
|
- [ ] 테이블 스와이프
|
|
- [ ] 테넌트 선택
|
|
- [ ] 사용자 메뉴 드롭다운
|
|
- [ ] 폼 입력
|
|
- [ ] 필터 동작
|
|
|
|
### 5.3 성능 테스트
|
|
- [ ] 초기 로딩 속도
|
|
- [ ] 스크롤 부드러움
|
|
- [ ] 애니메이션 프레임 드랍
|
|
|
|
---
|
|
|
|
## 6. 롤백 계획
|
|
|
|
각 Phase는 독립적으로 롤백 가능하도록 설계:
|
|
- Git 브랜치: `feature/mng-mobile-responsive`
|
|
- 각 Phase 완료 시 태그: `v-mobile-phase-1`, `v-mobile-phase-2` 등
|
|
- 문제 발생 시 해당 Phase만 되돌리기
|
|
|
|
---
|
|
|
|
## 7. 추가 고려사항
|
|
|
|
### 7.1 Alpine.js 도입 검토
|
|
현재 vanilla JS로 구현 중이지만, Alpine.js 도입 시:
|
|
- 더 선언적인 코드
|
|
- x-show, x-transition 활용
|
|
- 기존 HTMX와 호환
|
|
|
|
**결정 필요**: Alpine.js 추가 vs vanilla JS 유지
|
|
|
|
### 7.2 PWA 고려
|
|
모바일 경험 개선을 위해 PWA 기능 추가 검토:
|
|
- manifest.json
|
|
- 서비스 워커
|
|
- 오프라인 기본 페이지
|
|
|
|
---
|
|
|
|
## 8. 진행 현황
|
|
|
|
| Phase | 상태 | 완료일 |
|
|
|-------|------|--------|
|
|
| Phase 1 | ✅ 완료 | 2025-12-19 |
|
|
| Phase 2 | ✅ 완료 | 2025-12-19 |
|
|
| Phase 3 | ✅ 완료 | 2025-12-19 |
|
|
| Phase 4 | ⏳ 대기 | - |
|
|
| Phase 5 | ⏳ 대기 | - | |