14 KiB
PWA (Progressive Web App) 가이드
이 문서는 Tr_Code 웹 애플리케이션을 안드로이드 앱으로 패키징하여 Google Play Store에 배포하는 방법을 설명합니다.
📋 목차
🌐 PWA란?
**Progressive Web App(PWA)**는 웹 기술로 만들어졌지만 네이티브 앱처럼 동작하는 애플리케이션입니다.
PWA의 장점
✅ 설치 가능 - 홈 화면에 아이콘 추가
✅ 오프라인 작동 - 네트워크 없이도 사용 가능
✅ 빠른 로딩 - 캐시를 통한 빠른 시작
✅ 푸시 알림 - 사용자에게 알림 전송
✅ 자동 업데이트 - 별도 앱 업데이트 불필요
✅ 크로스 플랫폼 - Android, iOS, Desktop 모두 지원
Tr_Code PWA 특징
- 완전한 오프라인 지원: 한 번 방문 후 네트워크 없이도 사용 가능
- 빠른 응답속도: HTMX 기반 부분 렌더링
- 모바일 최적화: 반응형 디자인
- 다크모드: 자동 다크모드 지원
📱 TWA (Trusted Web Activity)
TWA는 PWA를 안드로이드 네이티브 앱으로 패키징하는 기술입니다.
TWA vs WebView
| 특징 | TWA | WebView |
|---|---|---|
| 성능 | Chrome 브라우저 엔진 사용 (빠름) | 앱 내장 (느림) |
| 업데이트 | 자동 (서버만 업데이트) | 앱 재배포 필요 |
| 보안 | Chrome 보안 정책 적용 | 개발자 구현 필요 |
| 캐시 | Service Worker | 수동 구현 |
| 크기 | 작음 (~2MB) | 큼 (WebView 포함) |
🛠️ 준비사항
1. 도메인 및 HTTPS
PWA는 HTTPS가 필수입니다 (localhost 제외).
# 현재 구조
http://your-domain.com → NPM (Proxmox) → M1T nginx → App
NPM(Nginx Proxy Manager)에서 SSL 인증서를 설정하세요:
- Let's Encrypt 자동 발급
- 또는 기존 인증서 업로드
2. Android Studio
# 시스템 요구사항
- OS: Windows 10/11, macOS, Linux
- RAM: 8GB 이상 권장
- 저장공간: 10GB 이상
3. Java Development Kit (JDK)
Android Studio에 포함되어 있지만, 별도 설치도 가능:
# Ubuntu/Linux
sudo apt install openjdk-17-jdk
# 확인
java -version
4. Google Play Console 계정
- Google Play Console 가입
- 일회성 등록 비용: $25 (평생 사용)
- 개발자 계정 승인까지 1-2일 소요
🏗️ Android Studio 설정
1. 프로젝트 생성
이미 build.gradle 파일이 있으므로 다음 단계를 진행합니다.
2. 기존 프로젝트 구조
Tr_Code/
├── app/ # Android 앱 소스
│ ├── src/
│ │ └── main/
│ │ ├── AndroidManifest.xml
│ │ ├── res/ # 리소스 (아이콘, 문자열 등)
│ │ └── java/ # Java/Kotlin 코드
├── build.gradle # 프로젝트 빌드 설정
├── settings.gradle # 프로젝트 설정
└── twa-manifest.json # TWA 설정
3. twa-manifest.json 설정
{
"packageId": "com.trcode.app",
"host": "your-domain.com",
"name": "1호선 고장코드",
"launcherName": "고장코드",
"display": "standalone",
"themeColor": "#1f2937",
"backgroundColor": "#111827",
"enableNotifications": false,
"startUrl": "/",
"iconUrl": "https://your-domain.com/static/icon.png",
"maskableIconUrl": "https://your-domain.com/static/icon-maskable.png",
"splashScreenFadeOutDuration": 300,
"signingKey": {
"path": "android.keystore",
"alias": "trcode"
},
"features": {
"locationDelegation": {
"enabled": false
},
"playBilling": {
"enabled": false
}
}
}
4. AndroidManifest.xml 주요 설정
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.trcode.app">
<!-- 권한 -->
<uses-permission android:name="android.permission.INTERNET" />
<application
android:label="1호선 고장코드"
android:icon="@mipmap/ic_launcher"
android:theme="@style/Theme.TrCode">
<!-- TWA Activity -->
<activity
android:name="com.google.androidbrowserhelper.trusted.LauncherActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- Digital Asset Links (중요!) -->
<meta-data
android:name="asset_statements"
android:resource="@string/asset_statements" />
</application>
</manifest>
5. Digital Asset Links 설정
TWA가 작동하려면 도메인과 앱을 연결해야 합니다.
서버 측: .well-known/assetlinks.json
# M1T 서버에 파일 생성
sudo mkdir -p /home/ckh08045/Tr_Code/static/.well-known
sudo nano /home/ckh08045/Tr_Code/static/.well-known/assetlinks.json
[{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.trcode.app",
"sha256_cert_fingerprints": [
"YOUR_SHA256_FINGERPRINT_HERE"
]
}
}]
SHA256 Fingerprint 얻는 방법:
# 키스토어에서 추출
keytool -list -v -keystore android.keystore -alias trcode
# 출력에서 SHA256 찾기
# 예: AA:BB:CC:DD:EE:FF:...
# 콜론(:) 제거하고 입력
nginx 설정 추가
# /etc/nginx/sites-available/tr_code에 추가
location /.well-known/assetlinks.json {
alias /home/ckh08045/Tr_Code/static/.well-known/assetlinks.json;
default_type application/json;
add_header Access-Control-Allow-Origin *;
}
# nginx 재시작
sudo systemctl reload nginx
# 테스트
curl https://your-domain.com/.well-known/assetlinks.json
🔨 앱 빌드
1. 키스토어 생성 (처음 한 번만)
cd /home/ckh08045/Tr_Code
# 키스토어 생성
keytool -genkey -v -keystore android.keystore \
-alias trcode \
-keyalg RSA -keysize 2048 -validity 10000
# 정보 입력
# - 비밀번호 (잘 기억하세요!)
# - 이름, 조직, 도시, 국가 등
⚠️ 중요: android.keystore 파일과 비밀번호를 안전하게 백업하세요!
2. Gradle 빌드 설정
build.gradle 파일 확인:
android {
compileSdkVersion 33
defaultConfig {
applicationId "com.trcode.app"
minSdkVersion 21
targetSdkVersion 33
versionCode 1
versionName "1.0"
}
signingConfigs {
release {
storeFile file('android.keystore')
storePassword 'your_password'
keyAlias 'trcode'
keyPassword 'your_password'
}
}
buildTypes {
release {
signingConfig signingConfigs.release
minifyEnabled false
}
}
}
3. 빌드 실행
Android Studio에서:
Build→Select Build Variant→release선택Build→Build Bundle(s) / APK(s)→Build Bundle(s)- 완료되면:
app/build/outputs/bundle/release/app-release.aab
명령줄에서:
cd /home/ckh08045/Tr_Code
# Clean
./gradlew clean
# AAB (Play Store용) 빌드
./gradlew bundleRelease
# 또는 APK (테스트용) 빌드
./gradlew assembleRelease
# 출력 위치
# AAB: app/build/outputs/bundle/release/app-release.aab
# APK: app/build/outputs/apk/release/app-release.apk
4. 테스트
# APK 설치 (USB 디버깅 활성화된 안드로이드 기기)
adb install app/build/outputs/apk/release/app-release.apk
# 또는 Android Studio에서 Run
🚀 Play Store 배포
1. Google Play Console 준비
- Google Play Console 로그인
모든 앱→앱 만들기- 앱 세부정보 입력:
- 앱 이름:
1호선 고장코드 - 기본 언어:
한국어 - 앱/게임:
앱 - 무료/유료:
무료
- 앱 이름:
2. 앱 콘텐츠 작성
개인정보처리방침
- URL 제공 (필수)
- 예:
https://your-domain.com/privacy-policy
앱 카테고리
- 카테고리:
비즈니스또는생산성 - 콘텐츠 등급: 설문조사 진행
광고
- 광고 포함 여부 선택
3. 프로덕션 릴리스 생성
프로덕션→새 릴리스 만들기app-release.aab업로드- 출시 노트 작성:
첫 번째 릴리스
- 고장코드 조회
- TCMS 신호 조회
- MMI 코드 조회
- 오프라인 지원
- 검토 제출
4. 스토어 등록정보
스크린샷 (필수)
- 휴대전화: 2개 이상 (16:9 비율)
- 7인치 태블릿: 선택사항
- 10인치 태블릿: 선택사항
그래픽 애셋
- 앱 아이콘: 512x512 PNG
- 기능 그래픽: 1024x500 PNG
설명
1호선 철도 차량 고장코드 및 TCMS 신호 조회 애플리케이션
주요 기능:
✓ 제조사별 고장코드 검색
✓ TCMS 신호 조회
✓ MMI 코드 데이터베이스
✓ 오프라인 지원
✓ 빠른 검색 및 필터링
철도 유지보수 및 정비 업무를 위한 필수 도구입니다.
5. 심사 및 배포
- 제출 후 1-7일 심사 기간
- 승인되면 자동으로 Play Store에 배포
- 거부 시 피드백 확인 후 수정하여 재제출
💾 오프라인 기능
Service Worker
PWA의 오프라인 기능은 Service Worker로 구현됩니다.
static/sw.js 예시:
const CACHE_NAME = 'tr-code-v1';
const urlsToCache = [
'/',
'/static/css/main.css',
'/static/js/main.js',
'/static/icon.png',
];
// 설치
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => cache.addAll(urlsToCache))
);
});
// 요청 가로채기
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request)
.then(response => response || fetch(event.request))
);
});
// 업데이트
self.addEventListener('activate', event => {
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.map(cacheName => {
if (cacheName !== CACHE_NAME) {
return caches.delete(cacheName);
}
})
);
})
);
});
manifest.json
static/manifest.json:
{
"name": "1호선 고장코드",
"short_name": "고장코드",
"start_url": "/",
"display": "standalone",
"background_color": "#111827",
"theme_color": "#1f2937",
"orientation": "portrait-primary",
"icons": [
{
"src": "/static/icon-192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "/static/icon-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any maskable"
}
]
}
HTML에서 등록
templates/base.html:
<head>
<link rel="manifest" href="/static/manifest.json">
<meta name="theme-color" content="#1f2937">
<link rel="apple-touch-icon" href="/static/icon.png">
</head>
<body>
<!-- ... -->
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/static/sw.js')
.then(reg => console.log('SW registered', reg))
.catch(err => console.log('SW registration failed', err));
}
</script>
</body>
❓ FAQ
Q1: localhost로 테스트할 수 있나요?
A: 네! PWA는 localhost에서 HTTPS 없이도 작동합니다. 하지만 TWA는 실제 도메인이 필요합니다.
Q2: 앱 업데이트는 어떻게 하나요?
A: 서버만 업데이트하면 됩니다! 앱을 다시 빌드/배포할 필요가 없습니다. 사용자가 앱을 열면 자동으로 최신 버전을 로드합니다.
단, 앱 아이콘이나 이름 변경 시에는 Play Store에 새 버전을 제출해야 합니다.
Q3: iOS는 지원되나요?
A: PWA 자체는 iOS에서도 작동합니다 (Safari). 하지만 TWA는 Android 전용이므로, iOS 앱을 만들려면:
- 웹 앱 추가: Safari에서 "홈 화면에 추가"
- App Store: Apple Developer Program ($99/년) 가입 후 네이티브 앱 개발 필요
Q4: 서버가 다운되면 앱도 작동하지 않나요?
A: Service Worker로 캐시를 구현하면 오프라인에서도 기본 기능은 사용할 수 있습니다. 단, 새 데이터는 서버 연결이 필요합니다.
Q5: 데이터는 어디에 저장되나요?
A:
- 서버: Supabase (PostgreSQL)
- 클라이언트: 브라우저 캐시 (Service Worker)
- 앱 내부: 없음 (모든 데이터는 웹에서 로드)
Q6: 보안은 어떻게 되나요?
A:
- HTTPS 필수 (데이터 암호화)
- Digital Asset Links (앱-도메인 인증)
- Chrome 브라우저의 보안 정책 적용
- Supabase Row Level Security
Q7: 앱 크기는 얼마나 되나요?
A: TWA 앱은 매우 작습니다:
- APK/AAB: 약 2-5MB
- 설치 후: 10-20MB (캐시 포함)
비교: 일반 네이티브 앱은 20-100MB 이상
Q8: Play Store 심사에서 거부될 수 있나요?
A: 가능성 있는 거부 사유:
- Digital Asset Links 미설정
- 개인정보처리방침 누락
- 앱 콘텐츠 설명 불충분
- 스크린샷 부족
모두 수정 후 재제출 가능합니다.
Q9: 현재 서버 구조에서 PWA가 잘 작동하나요?
A: 네! 현재 구조는 PWA에 최적화되어 있습니다:
- ✅ nginx 리버스 프록시
- ✅ HTMX (빠른 로딩)
- ✅ 반응형 디자인
- ✅ localhost Supabase (빠른 응답)
NPM(Proxmox)에서 HTTPS만 설정하면 완벽합니다!
Q10: 비용은 얼마나 드나요?
A:
- Google Play Console: $25 (평생)
- 도메인: 연간 $10-20
- SSL 인증서: 무료 (Let's Encrypt)
- 서버: 기존 사용 중
총 초기 비용: 약 $25-50
📚 추가 자료
🆘 도움이 필요하신가요?
문제가 발생하거나 질문이 있으면 시스템 관리자에게 문의하세요.
작성일: 2025-10-13
버전: 1.0