프로젝트 설명
무엇을 하는 앱이고, 어떤 기술로 만들어졌는가.
한 줄 요약
경희대학교의 3개 AI 사이트 콘텐츠를 오프라인 번들로 내장하여 하나의 네이티브 앱에서 열람하는 앱입니다. 목록·상세 열람에는 네트워크가 필요하지 않으며, 원본 자료(PDF·영상·가이드)와 챗봇만 외부로 연결됩니다.
화면 구성
| 탭 / 화면 | 원본 사이트 | 내용 |
|---|---|---|
| AI 멘토링 | aikhu.khu.ac.kr | 생성형 AI 멘토링 교육과정 — 입문·활용·심화 16개 소주제, 강의노트·교재·실습예제·강의영상 |
| AI 허브 | dx.khu.ac.kr/aihub.html | 학술 AI 서비스 포털 — 6개 워크플로 카테고리, 18개 플랫폼, 페르소나 필터 |
| AI 팩트체커 | dx.khu.ac.kr/aifactchecker.html | AI 현황 분석 RAG 챗봇 소개 + 6개 공신력 보고서 |
| 그 외 화면 | — | 홈, 통합 검색, 즐겨찾기, 앱 정보(라이선스 포함) |
아키텍처
- 하이브리드 네이티브 — 콘텐츠는 네이티브 Flutter 위젯으로 재구성. 강의자료 PDF·영상·가이드는 외부 브라우저(
url_launcher)로, 챗봇은 인앱 WebView(iOS/Android) 또는 브라우저(Web/데스크톱)로 연다. - 오프라인 번들 — 3개 사이트 콘텐츠를
assets/data/*.json으로 내장.tool/extract_content.py가 원본 HTML에서 이 JSON을 재생성한다. - 디자인 — iOS(Apple) 스타일.
CupertinoApp, inset-grouped 리스트, 접히는 large title, 세그먼트 필터, 라이트/다크 자동 대응. 경희 레드#B1201E는 tint 컬러로만 사용. - 반응형 내비게이션 —
lib/widgets/common.dart의Breakpoints(600 / 840px)로 창 폭을 3단계로 나눔. 600px 미만은 하단CupertinoTabBar, 그 이상은 iPad/데스크톱 스타일 사이드바(lib/main.dart의_Sidebar). 두 경우 모두 홈·멘토링·허브·팩트체커 4개 탭은 동일하며,IndexedStack으로 탭 상태를 유지한다. - 폰트 — Pretendard(OFL)를 번들해 모든 플랫폼에서 한글 렌더링을 통일.
- 상태 저장 — 즐겨찾기는
FavoritesManager가shared_preferences에 로컬 저장.
의존성 (pubspec.yaml)
| 패키지 | 버전 | 용도 |
|---|---|---|
cupertino_icons | ^1.0.8 | SF 스타일 아이콘 |
url_launcher | ^6.3.1 | 외부 브라우저·앱으로 링크 열기 |
webview_flutter | ^4.10.0 | 인앱 WebView (챗봇, 모바일 전용) |
shared_preferences | ^2.5.5 | 즐겨찾기 로컬 저장 |
webview_flutter가 iOS 15 / Android API 21 이상을 요구하기 때문에
iOS 배포 타깃이 15.0으로 설정되어 있습니다. WebView는 모바일에서만 동작하며,
Web·macOS에서는 챗봇을 외부 브라우저로 엽니다.
폴더 구조
어디에 무엇이 있고, 수정할 때 어디를 건드리는가.
khu_ai/
├─ lib/ # ← Dart 소스. 앱 로직·화면은 전부 여기
│ ├─ main.dart # 진입점. CupertinoApp + 반응형 4탭 내비게이션(하단 탭바/사이드바)
│ ├─ theme.dart # iOS 디자인 토큰 (tint, 팔레트, 아이콘 매핑)
│ ├─ models.dart # JSON → 데이터 모델
│ ├─ repository.dart # 번들 로드 + 통합 검색
│ ├─ favorites_manager.dart # 즐겨찾기 저장/불러오기 (shared_preferences)
│ ├─ screens/ # 화면 단위 위젯
│ │ ├─ home_screen.dart
│ │ ├─ mentoring_screen.dart / mentoring_detail_screen.dart
│ │ ├─ hub_screen.dart / hub_detail_screen.dart
│ │ ├─ factchecker_screen.dart
│ │ ├─ search_screen.dart
│ │ ├─ favorites_screen.dart
│ │ ├─ chatbot_screen.dart # 인앱 WebView / 외부 브라우저 분기
│ │ └─ about_screen.dart # 앱 정보·라이선스
│ └─ widgets/
│ └─ common.dart # 공통 위젯 (LargeTitlePage, Section, Row류, GlyphTile 등)
│
├─ assets/
│ ├─ data/ # 내장 콘텐츠 — extract_content.py 산출물
│ │ ├─ mentoring.json
│ │ ├─ hub.json
│ │ └─ factchecker.json
│ └─ fonts/ # Pretendard 4종 + LICENSE
│
├─ tool/
│ └─ extract_content.py # 원본 HTML(../aikhu.html 등) → assets/data/*.json
│
├─ pubspec.yaml # 의존성·에셋·폰트·앱 버전 선언
├─ pubspec.lock # 잠긴 버전 (커밋됨, 수동 편집 X)
├─ analysis_options.yaml # 린트 규칙 (flutter_lints)
├─ README.md
│
├─ android/ # Android 러너 — app/build.gradle.kts, AndroidManifest.xml
├─ ios/ # iOS 러너 — Runner.xcworkspace, Info.plist
├─ macos/ # macOS 러너 — Runner.xcworkspace, *.entitlements
├─ web/ # Web 러너 — index.html, manifest.json, icons/
│
├─ test/ # 위젯 테스트
├─ build/ # 빌드 산출물 (gitignore, 삭제 가능)
└─ .dart_tool/ .metadata # 도구 메타데이터 (수동 편집 X)
수정 시 자주 찾는 위치
| 바꾸고 싶은 것 | 파일 |
|---|---|
| 탭 구성·순서 | lib/main.dart의 _tabs |
| 반응형 전환 폭(하단 탭바 ↔ 사이드바) | lib/widgets/common.dart의 Breakpoints |
| 색상·간격·아이콘 등 디자인 | lib/theme.dart, lib/widgets/common.dart |
| 특정 화면의 내용·레이아웃 | lib/screens/*.dart |
| 내장되는 콘텐츠 데이터 | assets/data/*.json (원칙적으로 tool/extract_content.py로 재생성) |
| 앱 표시 이름 | Android: android/app/src/main/AndroidManifest.xml · iOS: ios/Runner/Info.plist · macOS: macos/Runner/Configs/AppInfo.xcconfig · Web: web/index.html, web/manifest.json |
| 앱 버전 | pubspec.yaml의 version: (그리고 lib/screens/about_screen.dart의 kAppVersion) |
| 패키지 추가/제거 | pubspec.yaml → flutter pub get |
플랫폼별 빌드
배포 가능한 산출물을 만드는 방법. 먼저 공통 준비를 끝낸 뒤 플랫폼별 절차를 따른다.
0. 공통 준비 (모든 플랫폼)
- Flutter SDK 3.47.2 이상(stable) 설치 — docs.flutter.dev/get-started/install
- 환경 점검:
flutter doctor— 사용할 플랫폼 항목에 체크가 있어야 함 - 프로젝트 루트에서 의존성 설치
cd khu_ai
flutter --version # Flutter 3.47.2 / Dart 3.13.2 확인
flutter doctor
flutter pub get
기본은 --release입니다. 성능 측정용은 --profile, 디버그 빌드는 --debug.
아래 flutter build 명령은 별도 지정이 없으면 release로 빌드됩니다.
플랫폼별 요약
| 플랫폼 | 필수 도구 | 빌드 명령 | 산출물 위치 |
|---|---|---|---|
| Web | 없음 (브라우저) | flutter build web | build/web/ |
| macOS | Xcode 15+, CocoaPods | flutter build macos | build/macos/Build/Products/Release/khu_ai.app |
| iOS | Xcode 15+, Apple 개발자 계정, CocoaPods | flutter build ipa | build/ios/ipa/*.ipa |
| Android | Android Studio (Android SDK, JDK 17) | flutter build apk / appbundle | build/app/outputs/flutter-apk/ · bundle/release/ |
앱 식별자 (현재 설정값)
| 플랫폼 | 식별자 | 최소 OS | 표시 이름 |
|---|---|---|---|
| iOS | kr.ac.khu.khuAi | iOS 15.0 | Khu Ai |
| Android | kr.ac.khu.khu_ai | flutter.minSdkVersion | KHU AI |
| macOS | kr.ac.khu.khuAi | macOS 12.0 | KHU AI |
Web
flutter build web
# 산출물: build/web/ (정적 파일 — 웹 서버에 그대로 업로드)
# 하위 경로에 배포한다면 base-href 지정
flutter build web --base-href /khu-ai/
# 로컬에서 결과물 확인
cd build/web && python3 -m http.server 8000
file://로 직접 열면 동작하지 않습니다. 반드시 HTTP 서버로 서빙하세요.
macOS
flutter build macos
# 산출물: build/macos/Build/Products/Release/khu_ai.app
open build/macos/Build/Products/Release/
- App Sandbox가 켜져 있고 네트워크 엔트리틀먼트가 설정되어 있습니다(
macos/Runner/*.entitlements). - 공증(notarization)·배포 서명은 Xcode에서
macos/Runner.xcworkspace를 열어 진행합니다.
iOS
flutter build ios # .app 빌드 (시뮬레이터/개발용)
flutter build ipa # 배포용 .ipa (자동 서명 필요)
# 산출물: build/ios/ipa/*.ipa, build/ios/archive/
open ios/Runner.xcworkspace # 서명 팀·프로비저닝 설정, Archive 업로드
실기기 빌드·배포에는 Apple 개발자 계정과 서명 설정이 필요합니다.
Xcode에서 Runner 타깃 → Signing & Capabilities에서 본인 팀을 선택하세요.
Bundle ID kr.ac.khu.khuAi는 필요 시 변경합니다.
Android
flutter build apk # 단일 APK
flutter build apk --split-per-abi # ABI별 APK (용량↓)
flutter build appbundle # Play 스토어 업로드용 .aab
# 산출물: build/app/outputs/flutter-apk/ · build/app/outputs/bundle/release/
현재 android/app/build.gradle.kts의 release 빌드는 디버그 키로 서명됩니다
(flutter run --release가 되도록). 실제 배포 전에는
키스토어를 만들고 key.properties를 추가해
release signingConfig를 교체해야 합니다.
Android SDK 경로는 android/local.properties의 sdk.dir 또는
ANDROID_HOME 환경변수로 잡습니다. JDK는 17을 사용합니다.
플랫폼별 실행
개발 중 앱을 띄우고 즉시 반영해 보는 방법 (hot reload).
연결된 기기 확인
flutter devices
# 예:
# macOS (desktop) • macos • darwin-arm64
# Chrome (web) • chrome • web-javascript
# iPhone 15 (mobile) • 00008120-... • ios
# sdk gphone64 arm64 • emulator-5554 • android-arm64
실행 명령
flutter run # 기기 1개면 자동 선택, 여러 개면 목록에서 선택
flutter run -d chrome # Web (Chrome)
flutter run -d macos # macOS 데스크톱
flutter run -d ios # 연결된 iPhone / 시뮬레이터
flutter run -d android # 연결된 Android 기기 / 에뮬레이터
flutter run -d emulator-5554 # flutter devices의 ID로 특정 기기 지정
flutter run --release # 릴리스 모드로 실행 (hot reload 없음)
실행 중 단축키 (터미널)
rRpoq플랫폼별 준비 사항
| 플랫폼 | 실행 전 준비 |
|---|---|
| Web | Chrome 설치. 추가 설정 없음. |
| macOS | Xcode + Command Line Tools. 첫 실행 시 CocoaPods 설치가 필요할 수 있음. |
| iOS 시뮬레이터 | open -a Simulator로 시뮬레이터 실행 후 flutter run -d ios. |
| iOS 실기기 | USB 연결 + 기기에서 신뢰. Xcode에서 서명 팀 선택. 설정 → 개인정보 보호 → 개발자 모드 켜기. |
| Android 에뮬레이터 | Android Studio → Device Manager에서 AVD 생성·실행. |
| Android 실기기 | USB 디버깅 켜기. adb devices로 인식 확인. |
IDE에서 실행
- VS Code — Flutter 확장 설치 후 오른쪽 아래에서 기기 선택,
F5(디버그) 또는 Ctrl/Cmd+F5. - Android Studio / IntelliJ — Flutter 플러그인 설치, 상단 기기 선택 후 Run ▶. 프로젝트에
.idea/설정이 포함되어 있습니다. - Xcode (iOS/macOS 세부 설정) —
ios/Runner.xcworkspace/macos/Runner.xcworkspace를 연다..xcodeproj가 아닌 .xcworkspace를 열어야 합니다.
부록 · 콘텐츠 갱신 & 문제 해결
수정 작업에서 자주 만나는 상황.
내장 콘텐츠 갱신
원본 HTML(../aikhu.html, ../aihub.html, ../aifactchecker.html — 프로젝트 상위 폴더)이
바뀌면 JSON을 다시 생성합니다. assets/data/*.json을 직접 손대기보다 스크립트로 재생성하는 것이 원칙입니다.
python3 tool/extract_content.py # assets/data/*.json 재생성
flutter pub get # 에셋 목록 갱신
자주 쓰는 점검 명령
flutter doctor -v # 환경 상세 진단
flutter analyze # 정적 분석 (린트)
flutter test # 위젯 테스트 실행
flutter clean # build/, .dart_tool/ 삭제 후 재빌드
flutter pub get
빌드가 꼬였을 때
- 일반:
flutter clean && flutter pub get - iOS / macOS:
cd ios(또는macos) →pod repo update && pod install. 그래도 안 되면Podfile.lock,Pods/삭제 후 재설치. - Android:
cd android && ./gradlew clean.android/local.properties의sdk.dir확인. - 버전 불일치: 이 프로젝트는 Flutter
3.47.2/.metadatarevisiond3b14c8769에서 생성됨. 다른 버전이면flutter upgrade또는 팀에서 쓰는 버전으로 맞추기(fvm 권장).
콘텐츠 원본: aikhu.khu.ac.kr · dx.khu.ac.kr/aihub.html · dx.khu.ac.kr/aifactchecker.html. 개발: 경희대학교 모바일랩 (mobilelab.khu.ac.kr). 번들 폰트 Pretendard는 OFL 1.1.