# πŸ“‹ Project Specification: VOC λͺ¨λ‹ˆν„°λ§ μ‹œμŠ€ν…œ ## 1. ν”„λ‘œμ νŠΈ λΉ„μ „ ### λͺ©ν‘œ 뢀산ꡐ톡곡사 1ν˜Έμ„  μ°¨λŸ‰ κ΄€λ ¨ VOC(Voice of Customer)λ₯Ό μ‹€μ‹œκ°„μœΌλ‘œ λͺ¨λ‹ˆν„°λ§ν•˜κ³ , μžλ™μœΌλ‘œ λ³΄κ³ μ„œλ₯Ό μƒμ„±ν•˜μ—¬ 업무 νš¨μœ¨μ„ κ·ΉλŒ€ν™”ν•©λ‹ˆλ‹€. ### 핡심 κ°€μΉ˜ - **μžλ™ν™”**: μˆ˜λ™μœΌλ‘œ μ›Ήμ‚¬μ΄νŠΈλ₯Ό ν™•μΈν•˜λ˜ μž‘μ—…μ„ μžλ™ν™” - **신속성**: μ‹ κ·œ VOC λ°œμƒ μ‹œ μ¦‰μ‹œ μ•Œλ¦Ό - **μ •ν™•μ„±**: μ—΄μ°¨ 정보 μžλ™ μΆ”μΆœ 및 μ‹œκ°ν‘œ 기반 검증 - **νŽΈμ˜μ„±**: ν•œκΈ€(HWP) λ³΄κ³ μ„œ μžλ™ 생성 ### 핡심 페λ₯΄μ†Œλ‚˜ - **직무**: 뢀산ꡐ톡곡사 μ°¨λŸ‰ λΆ€μ„œ 직원 - **ν™˜κ²½**: Windows 10/11, 사내망 - **μ‚¬μš© νŒ¨ν„΄**: λ°±κ·ΈλΌμš΄λ“œ μƒμ‹œ μ‹€ν–‰, μ•Œλ¦Ό 기반 λŒ€μ‘ --- ## 2. μ‹œμŠ€ν…œ λ²”μœ„ (Scope) ### 포함 κΈ°λŠ₯ - [x] **μ›Ή 크둀링**: 뢀산ꡐ톡곡사 VOC κ²Œμ‹œνŒ μžλ™ μˆ˜μ§‘ - [x] **λ°μ΄ν„°λ² μ΄μŠ€**: SQLite 기반 둜컬 μ €μž₯ - [x] **μ‹€μ‹œκ°„ μ•Œλ¦Ό**: Windows ν† μŠ€νŠΈ μ•Œλ¦Ό - [x] **λ³΄κ³ μ„œ 생성**: HWP/PDF μžλ™ μž‘μ„± - [x] **μ—΄μ°¨ 정보 뢄석**: μ‹œκ°ν‘œ 기반 μ—΄μ°¨λ²ˆν˜Έ, μž…κ³ μ§€ μžλ™ μΆ”μΆœ - [x] **νžˆμŠ€ν† λ¦¬ 관리**: κ³Όκ±° VOC 쑰회 및 필터링 - [x] **μ„€μ • 관리**: 크둀링 μ£ΌκΈ°, μ•Œλ¦Ό ν‚€μ›Œλ“œ λ“± ### μ œμ™Έ κΈ°λŠ₯ (Out of Scope) - ❌ μ›Ή 기반 UI (λ°μŠ€ν¬ν†± μ „μš©) - ❌ 닀쀑 μ‚¬μš©μž 지원 (1인 μ‚¬μš© μ „μ œ) - ❌ ν΄λΌμš°λ“œ 동기화 - ❌ λͺ¨λ°”일 μ•± --- ## 3. 핡심 λΉ„μ¦ˆλ‹ˆμŠ€ 둜직 (Core Logic) ### 3.1 크둀링 사이클 ``` 1. 둜그인 μ„Έμ…˜ 확인 (만료 μ‹œ 재둜그인) 2. λͺ©λ‘ νŽ˜μ΄μ§€ μˆ˜μ§‘ (μ΅œλŒ€ NνŽ˜μ΄μ§€) 3. ν‚€μ›Œλ“œ/λΆ€μ„œ 필터링 4. DB μ €μž₯ (Upsert) 5. 상세 λ‚΄μš© μˆ˜μ§‘ (쑰건뢀) ``` ### 3.2 μ•Œλ¦Ό 사이클 (v2.1 κ°œμ„ ) #### 3.2.1 μ‹ κ·œ μ•Œλ¦Ό 사이클 (κΈ°λ³Έ 3λΆ„, μ„€μ • κ°€λŠ₯) ``` 1. λ§ˆμ§€λ§‰ 체크 이후 μ‹ κ·œ 데이터 쑰회 (get_new_posts_since) - μ„€μ • `noti.use_related_filter = true`: 관심글(is_related=1)만 쑰회 - μ„€μ • `noti.use_related_filter = false`: μ‹ κ·œκΈ€ 전체 쑰회 2. 쀑볡 μ•Œλ¦Ό 필터링 (notified_post_ids 체크) 3. λͺ¨λ“  관심글(is_related=1)에 λŒ€ν•΄ μ•Œλ¦Ό ⭐ λ³€κ²½: ν‚€μ›Œλ“œ 필터링 제거 4. μ†Œλ¦¬ μž¬μƒ (μ„€μ • μ‹œ) 5. ν† μŠ€νŠΈ μ•Œλ¦Ό 및 νŒμ—… ν‘œμ‹œ 6. last_check_time μ˜μ†ν™” μ €μž₯ ⭐ μ‹ κ·œ ``` #### 3.2.2 미확인 μ•Œλ¦Ό 사이클 (κΈ°λ³Έ 10λΆ„, μ„€μ • κ°€λŠ₯) ⭐ μ‹ κ·œ ``` 1. ν™•μΈν•˜μ§€ μ•Šμ€ 관심글 쑰회 (get_unchecked_related_posts) - μ„€μ • `noti.use_related_filter = true`: 관심글(is_related=1)의 λ―Έν™•μΈλ§Œ 쑰회 - μ„€μ • `noti.use_related_filter = false`: 전체 미확인글 쑰회 2. 30λΆ„ κ²½κ³Ό 쑰건 ON/OFF에 따라 필터링 - ON: 30λΆ„ 이상 μ§€λ‚œ κΈ€λ§Œ μ•Œλ¦Ό - OFF: 미확인 κΈ€ 전체 μ•Œλ¦Ό 3. 미확인 μ•Œλ¦Ό λ°œμ†‘ (⚠️ ν‘œμ‹œ) 4. μ‚¬μš©μžκ°€ 확인할 λ•ŒκΉŒμ§€ 반볡 μ•Œλ¦Ό ``` #### 3.2.3 μ•Œλ¦Ό κ°œμ„  사항 - **ν‚€μ›Œλ“œ 필터링 제거**: λͺ¨λ“  관심글에 λŒ€ν•΄ μ•Œλ¦Ό λ°œμ†‘ - **쀑볡 λ°©μ§€**: notified_post_ids μ„ΈνŠΈλ‘œ 동일 κΈ€ 쀑볡 μ•Œλ¦Ό λ°©μ§€ - **μ˜μ†ν™”**: last_check_time을 scheduler_state.json에 μ €μž₯ν•˜μ—¬ ν”„λ‘œκ·Έλž¨ μž¬μ‹œμž‘ μ‹œμ—λ„ μœ μ§€ - **미확인 체크**: μ‚¬μš©μžκ°€ ν™•μΈν•˜μ§€ μ•Šμ€ 글에 λŒ€ν•΄ 주기적 μž¬μ•Œλ¦Ό - **μ£ΌκΈ° μ„€μ •ν™”**: μ‹ κ·œ μ•Œλ¦Ό μ£ΌκΈ° 및 미확인 μ•Œλ¦Ό μ£ΌκΈ°λ₯Ό μ„€μ • UIμ—μ„œ λ³€κ²½ κ°€λŠ₯ - **미확인 쑰건 ν† κΈ€**: "30λΆ„ κ²½κ³Ό ν›„ μ•Œλ¦Ό" μ˜΅μ…˜ ON/OFF 지원 - **관심쑰건 ν† κΈ€**: "관심 쑰건(ν‚€μ›Œλ“œ/λΆ€μ„œ)만 μ•Œλ¦Ό" μ˜΅μ…˜ ON/OFF 지원 ### 3.3 λ³΄κ³ μ„œ 생성 ν”„λ‘œμ„ΈμŠ€ ``` 1. VOC 데이터 μˆ˜μ‹  2. VOCParser둜 κΈ°λ³Έ 정보 μΆ”μΆœ (ν˜Έμ„ , νŽΈμ„±, 호차, μ—΄μ°¨λ²ˆν˜Έ) 3. DateScheduleUtils둜 λ‚ μ§œ νƒ€μž… νŒλ³„ (평일/주말/휴일) 4. TrainAnalyzer둜 μ—΄μ°¨ 정보 뢄석 (쒅별, λ°©ν–₯, μž…κ³ μ§€) 5. TimetableService둜 μ‹œκ°ν‘œ 쑰회 및 검증 6. HWP ν…œν”Œλ¦Ώμ— 데이터 μž…λ ₯ 7. 파일 μ €μž₯ (쀑볡 체크) ``` ### 3.4 톡계 뢄석 ν”„λ‘œμ„ΈμŠ€ (μ‹ κ·œ) ``` 1. μ‚¬μš©μž μž…λ ₯ μˆ˜μ‹  (κΈ°κ°„, 뢄석 μ˜΅μ…˜) 2. μž…λ ₯ 검증 (λ‚ μ§œ ν˜•μ‹, κΈ°κ°„ μ œν•œ) 3. DBμ—μ„œ 데이터 집계 β”œβ”€β”€ 기간별 톡계 (일/μ£Ό/μ›”/λ…„) β”œβ”€β”€ λΆ€μ„œλ³„ 뢄포 β”œβ”€β”€ μƒνƒœλ³„ ν˜„ν™© └── ν‚€μ›Œλ“œ λΉˆλ„ 뢄석 4. 차트 생성 (matplotlib/plotly) β”œβ”€β”€ λ§‰λŒ€ κ·Έλž˜ν”„ (λΆ€μ„œλ³„, μƒνƒœλ³„) β”œβ”€β”€ μ„  κ·Έλž˜ν”„ (μ‹œκ³„μ—΄ 좔이) β”œβ”€β”€ 파이 차트 (λΉ„μœ¨) └── μ›Œλ“œ ν΄λΌμš°λ“œ (ν‚€μ›Œλ“œ) 5. λ³΄κ³ μ„œ 생성 (Excel/PDF) 6. 파일 μ €μž₯ 및 μ—΄κΈ° ``` #### 3.4.1 데이터 집계 둜직 **기간별 톡계**: ```python SELECT DATE(date) as day, COUNT(*) as count FROM voc_posts WHERE date BETWEEN ? AND ? GROUP BY DATE(date) ORDER BY day ``` **λΆ€μ„œλ³„ 뢄포**: ```python SELECT department, COUNT(*) as count, ROUND(COUNT(*) * 100.0 / (SELECT COUNT(*) FROM voc_posts WHERE date BETWEEN ? AND ?), 2) as percentage FROM voc_posts WHERE date BETWEEN ? AND ? GROUP BY department ORDER BY count DESC ``` **ν‚€μ›Œλ“œ λΉˆλ„ 뢄석**: ```python # 1. λͺ¨λ“  제λͺ© + λ‚΄μš© μΆ”μΆœ # 2. ν˜•νƒœμ†Œ 뢄석 (선택, ν•œκΈ€ 처리) # 3. λΆˆμš©μ–΄ 제거 ("의", "λ₯Ό", "이" λ“±) # 4. λΉˆλ„ 계산 # 5. TOP 10 μΆ”μΆœ ``` #### 3.4.2 μ˜ˆμ™Έ μΌ€μ΄μŠ€ 처리 | μ˜ˆμ™Έ 상황 | 처리 방법 | μ‚¬μš©μž λ©”μ‹œμ§€ | |-----------|-----------|---------------| | κΈ°κ°„ λ‚΄ 데이터 μ—†μŒ | 빈 λ³΄κ³ μ„œ 생성 | "μ„ νƒν•œ 기간에 VOC 데이터가 μ—†μŠ΅λ‹ˆλ‹€." | | 기간이 λ„ˆλ¬΄ κΈΊ (1λ…„ 초과) | κΈ°κ°„ μ œν•œ | "μ΅œλŒ€ 1λ…„κΉŒμ§€λ§Œ 쑰회 κ°€λŠ₯ν•©λ‹ˆλ‹€." | | 차트 생성 μ‹€νŒ¨ | ν…μŠ€νŠΈ ν‘œλ§Œ 생성 | "차트 생성 μ‹€νŒ¨. ν‘œ ν˜•μ‹μœΌλ‘œ μ œκ³΅λ©λ‹ˆλ‹€." | | λ©”λͺ¨λ¦¬ λΆ€μ‘± | 데이터 μƒ˜ν”Œλ§ | "데이터가 λ§Žμ•„ μΌλΆ€λ§Œ ν‘œμ‹œλ©λ‹ˆλ‹€." | | Excel/PDF 생성 μ‹€νŒ¨ | μ—λŸ¬ λ‹€μ΄μ–Όλ‘œκ·Έ | "λ³΄κ³ μ„œ 생성 쀑 였λ₯˜ λ°œμƒ: [상세 λ©”μ‹œμ§€]" | #### 3.4.3 μ„±λŠ₯ μ΅œμ ν™” **인덱슀 μΆ”κ°€**: ```sql CREATE INDEX IF NOT EXISTS idx_date ON voc_posts(date); CREATE INDEX IF NOT EXISTS idx_department ON voc_posts(department); CREATE INDEX IF NOT EXISTS idx_status ON voc_posts(status); CREATE INDEX IF NOT EXISTS idx_date_department ON voc_posts(date, department); ``` **쿼리 μ΅œμ ν™”**: - κΈ°κ°„ μ œν•œ: μ΅œλŒ€ 1λ…„ - νŽ˜μ΄μ§•: λŒ€λŸ‰ 데이터 μ‹œ 배치 처리 - 캐싱: 동일 κΈ°κ°„ 재쑰회 μ‹œ μΊμ‹œ μ‚¬μš© (선택) **λ©”λͺ¨λ¦¬ 관리**: - 차트 생성 ν›„ μ¦‰μ‹œ 파일 μ €μž₯ - `plt.close()` ν˜ΈμΆœν•˜μ—¬ λ©”λͺ¨λ¦¬ ν•΄μ œ - λŒ€μš©λŸ‰ 데이터 μ‹œ 청크 λ‹¨μœ„ 처리 --- ## 4. μ—λŸ¬ 처리 μ „λž΅ ### 4.1 λΆ€λΆ„ μ‹€νŒ¨ ν—ˆμš© 원칙 λ³΄κ³ μ„œ 생성 μ‹œ 일뢀 정보 쑰회 μ‹€νŒ¨λŠ” 전체 μž‘μ—…μ„ μ€‘λ‹¨ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. | μ‹€νŒ¨ μœ ν˜• | 처리 방법 | μ‚¬μš©μž κ²½ν—˜ | |-----------|-----------|-------------| | μ—΄μ°¨λ²ˆν˜Έ 쑰회 μ‹€νŒ¨ | ν•„λ“œ 비움 | λ“œλ‘­λ°•μŠ€ λΉ„ν™œμ„±ν™” | | μ‹œκ°ν‘œ 쑰회 μ‹€νŒ¨ | "정보 μ—†μŒ" ν‘œμ‹œ | κ²½κ³  둜그 | | ν…œν”Œλ¦Ώ 파일 μ—†μŒ | 전체 쀑단 | μ—λŸ¬ λ‹€μ΄μ–Όλ‘œκ·Έ | ### 4.2 둜그 레벨 ꡬ뢄 - `INFO`: 정상 λ™μž‘ (크둀링 μ™„λ£Œ, λ³΄κ³ μ„œ 생성 λ“±) - `WARNING`: λΆ€λΆ„ μ‹€νŒ¨ (μ—΄μ°¨ 정보 미쑰회 λ“±) - `ERROR`: 전체 μ‹€νŒ¨ (둜그인 μ‹€νŒ¨, DB 였λ₯˜ λ“±) ### 4.3 μ‚¬μš©μž μΉœν™”μ  λ©”μ‹œμ§€ λͺ¨λ“  μ—λŸ¬ λ©”μ‹œμ§€λŠ” ν•œκ΅­μ–΄λ‘œ μž‘μ„±ν•˜λ©°, 기술적 μš©μ–΄λ₯Ό μ΅œμ†Œν™”ν•©λ‹ˆλ‹€. **μ˜ˆμ‹œ:** - ❌ (Bad) `TimetableServiceError: train_number not found in dataframe` - βœ… (Good) `μ—΄μ°¨ 정보λ₯Ό μ‘°νšŒν•  수 μ—†μŠ΅λ‹ˆλ‹€. λ³΄κ³ μ„œλŠ” μƒμ„±λ˜μ§€λ§Œ μ—΄μ°¨λ²ˆν˜Έ ν•„λ“œκ°€ λΉ„μ–΄μžˆμŠ΅λ‹ˆλ‹€.` --- ## 5. λ³΄κ³ μ„œ 생성 κ·œμΉ™ ### 5.1 ν•„μˆ˜ 정보 vs 선택 정보 | ꡬ뢄 | ν•„λ“œ | μ‹€νŒ¨ μ‹œ 처리 | |------|------|--------------| | **ν•„μˆ˜** | 제λͺ©, λ‚΄μš©, λ‚ μ§œ, λΆ€μ„œ | 전체 쀑단 | | **선택** | μ—΄μ°¨λ²ˆν˜Έ, νŽΈμ„±, 호차 | ν•„λ“œ 비움 | | **선택** | μž…κ³  정보, λΉ„κ³  | "정보 μ—†μŒ" ν‘œμ‹œ | ### 5.2 UI λ™μž‘ κ·œμΉ™ - **μ—΄μ°¨λ²ˆν˜Έ 쑰회 성곡**: λ“œλ‘­λ°•μŠ€μ— 후보 λͺ©λ‘ ν‘œμ‹œ - **μ—΄μ°¨λ²ˆν˜Έ 쑰회 μ‹€νŒ¨**: λ“œλ‘­λ°•μŠ€ λΉ„ν™œμ„±ν™” + μˆ˜λ™ μž…λ ₯ ν—ˆμš© - **μ‹œκ°ν‘œ λ―Έλ‘œλ“œ**: κ΄€λ ¨ κΈ°λŠ₯ 전체 λΉ„ν™œμ„±ν™” ### 5.3 파일λͺ… κ·œμΉ™ ``` [λ‚ μ§œ]_[λΆ€μ„œ]_[제λͺ©(μ •μ œ)].hwp 예: 2026-02-17_μ°¨λŸ‰_1ν˜Έμ„ _μ„œλ©΄μ—­_μ†ŒμŒλ°œμƒ.hwp ``` --- ## 6. 데이터 λͺ¨λΈ ### 6.1 VOCPost (Pydantic) ```python class VOCPost(BaseModel): id: str # κ²Œμ‹œκΈ€ ID title: str # 제λͺ© writer: str # μž‘μ„±μž department: str # λ‹΄λ‹Ή λΆ€μ„œ date: str # μž‘μ„±μΌ (YYYY-MM-DD HH:MM:SS) status: str # 처리 μƒνƒœ channel: str # μ ‘μˆ˜ 경둜 is_public: int # 곡개 μ—¬λΆ€ (0/1) content: Optional[str] # 상세 λ‚΄μš© attachment: Optional[str] # μ²¨λΆ€νŒŒμΌλͺ… is_related: int = 0 # 관심글 μ—¬λΆ€ ``` ### 6.2 λ°μ΄ν„°λ² μ΄μŠ€ μŠ€ν‚€λ§ˆ ```sql CREATE TABLE voc_posts ( id TEXT PRIMARY KEY, title TEXT NOT NULL, writer TEXT, department TEXT, date TEXT, status TEXT, channel TEXT, is_public INTEGER DEFAULT 1, content TEXT, attachment TEXT, is_related INTEGER DEFAULT 0, checked_at TEXT, created_at TEXT DEFAULT CURRENT_TIMESTAMP ); ``` --- ## 7. UI/UX μš”κ΅¬μ‚¬ν•­ ### 7.1 메인 ν™”λ©΄ (트레이 μ•„μ΄μ½˜) - μ‹œμŠ€ν…œ νŠΈλ ˆμ΄μ— 상주 - 우클릭 메뉴: μˆ˜λ™ 크둀링, λͺ©λ‘ 보기, μ„€μ •, μ’…λ£Œ ### 7.2 νžˆμŠ€ν† λ¦¬ λ‹€μ΄μ–Όλ‘œκ·Έ - ν…Œμ΄λΈ” λ·°: ID, 제λͺ©, μž‘μ„±μž, λΆ€μ„œ, λ‚ μ§œ, μƒνƒœ - ν•„ν„°: λΆ€μ„œλ³„, ν‚€μ›Œλ“œ, λ‚ μ§œ λ²”μœ„ - 더블클릭: 상세 νŒμ—… - 우클릭 메뉴: λ³΄κ³ μ„œ 생성, 인쇄, μ²¨λΆ€νŒŒμΌ 보기 ### 7.3 λ³΄κ³ μ„œ μ˜΅μ…˜ λ‹€μ΄μ–Όλ‘œκ·Έ - μ‚¬μ—…μ†Œ 선택 (신평/노포) - νŒ€ 선택 (κ²€μˆ˜1/κ²€μˆ˜2/...) - μž‘μ„±μž 정보 - μ—΄μ°¨λ²ˆν˜Έ λ“œλ‘­λ°•μŠ€ (μžλ™ μΆ”μΆœ μ‹œ) --- ## 8. μ œμ•½ 및 μš”κ΅¬μ‚¬ν•­ ### 8.1 기술 μ œμ•½ - **Python**: 3.8 이상 - **OS**: Windows 10/11 - **ν•„μˆ˜ 라이브러리**: - `customtkinter`: UI - `selenium`: 크둀링 - `pywin32`: HWP μžλ™ν™” - `pydantic`: 데이터 검증 - `pandas`: μ‹œκ°ν‘œ 처리 ### 8.2 μ„±λŠ₯ λͺ©ν‘œ - 크둀링 μ£ΌκΈ°: 10λΆ„ (μ„€μ • κ°€λŠ₯) - μ•Œλ¦Ό μ§€μ—°: 5λΆ„ 이내 - λ³΄κ³ μ„œ 생성: 3초 이내 - DB 쑰회: 1초 이내 ### 8.3 λ³΄μ•ˆ μš”κ΅¬μ‚¬ν•­ - 둜그인 정보: 둜컬 JSON 파일 (평문 μ €μž₯, 사내망 μ „μ œ) - HWP λ³΄μ•ˆ λͺ¨λ“ˆ: λ ˆμ§€μŠ€νŠΈλ¦¬ μžλ™ 등둝 --- ## 9. μ‹œμŠ€ν…œ μ•„ν‚€ν…μ²˜ ### 9.1 계측 ꡬ쑰 (v3.0 - Manager νŒ¨ν„΄ 적용) ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ View (CustomTkinter) β”‚ β”‚ HistoryDialog, SettingsDialog, Notification β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ↕ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Controller (MVC) - μœ„μž„ νŒ¨ν„΄ β”‚ β”‚ β”‚ β”‚ AppController (487쀄) β”‚ β”‚ β”œβ”€β”€ SchedulerManager : 크둀링, DB 체크, μŠ€μΌ€μ€„λ§β”‚ β”‚ β”œβ”€β”€ NotificationManager : ν† μŠ€νŠΈ/νŒμ—… μ•Œλ¦Ό β”‚ β”‚ β”œβ”€β”€ ReportManager : λ³΄κ³ μ„œ 생성/인쇄/PDF β”‚ β”‚ β”œβ”€β”€ FileManager : μ²¨λΆ€νŒŒμΌ λ‹€μš΄λ‘œλ“œ/μ—΄κΈ° β”‚ β”‚ └── UIManager : UI μ°½ 관리 β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ↕ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Services (Business Logic) β”‚ β”‚ ReportService, ScraperService, TimetableService β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ↕ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Utils & Models (Data) β”‚ β”‚ VOCParser, TrainAnalyzer, VOCDatabase, ... β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` ### 9.2 μ£Όμš” λͺ¨λ“ˆ μ±…μž„ | λͺ¨λ“ˆ | μ±…μž„ | 라인 수 | |------|------|---------| | `AppController` | 전체 흐름 μ œμ–΄, Manager μœ„μž„ | 487쀄 | | `SchedulerManager` | 크둀링 사이클, DB 체크, μŠ€μΌ€μ€„λ§ | 399쀄 | | `NotificationManager` | Windows ν† μŠ€νŠΈ μ•Œλ¦Ό, νŒμ—… λ‹€μ΄μ–Όλ‘œκ·Έ | 192쀄 | | `ReportManager` | λ³΄κ³ μ„œ 생성 μš”μ²­, 인쇄, PDF 생성 μœ„μž„ | 283쀄 | | `FileManager` | μ²¨λΆ€νŒŒμΌ λ‹€μš΄λ‘œλ“œ 및 μ—΄κΈ° | 151쀄 | | `UIManager` | UI μ°½ 생성 및 포컀슀 관리 | 198쀄 | | `VOCScraper` | μ›Ή 크둀링, 둜그인 관리 | - | | `ReportService` | HWP/PDF 생성, 인쇄 | 691쀄 | | `TimetableService` | μ‹œκ°ν‘œ 쑰회, μ—΄μ°¨ 검색 | - | | `VOCParser` | VOC ν…μŠ€νŠΈ νŒŒμ‹± | - | | `TrainAnalyzer` | μ—΄μ°¨ 정보 뢄석, μž…κ³ μ§€ 좔적 | 355쀄 | | `DateScheduleUtils` | λ‚ μ§œ 처리, 곡휴일 νŒλ³„ | - | --- ## 10. μžλ™ μ—…λ°μ΄νŠΈ μ‹œμŠ€ν…œ (Auto Update System) ⭐ μ‹ κ·œ ### 10.1 κ°œμš” **λͺ©ν‘œ**: ν”„λ‘œκ·Έλž¨μ˜ μžλ™ μ—…λ°μ΄νŠΈ κΈ°λŠ₯ κ΅¬ν˜„ - Supabase 기반 원격 버전 관리 - 1μ‹œκ°„λ§ˆλ‹€ μžλ™ μ—…λ°μ΄νŠΈ 체크 - μ‚¬μš©μžε‹ε₯½ηš„ μ—…λ°μ΄νŠΈ μ•Œλ¦Ό 및 μ„€μΉ˜ - **λ©€ν‹°ν”„λ‘œμ νŠΈ 지원**: λ‹€λ₯Έ ν”„λ‘œμ νŠΈμ—μ„œλ„ μž¬μ‚¬μš© κ°€λŠ₯ν•œ λͺ¨λ“ˆ 섀계 --- ### 10.2 λ°μ΄ν„°λ² μ΄μŠ€ ν…Œμ΄λΈ” μ •μ˜ (Supabase) **ν…Œμ΄λΈ”λͺ…**: `program_version` | ν•„λ“œλͺ… | νƒ€μž… | μ„€λͺ… | ν•„μˆ˜ | |--------|------|------|------| | `id` | SERIAL | Primary Key | βœ… | | `program_id` | TEXT | ν”„λ‘œκ·Έλž¨ μ‹λ³„μž (예: "voc_monitor") | βœ… | | `version` | TEXT | 버전 번호 (예: "3.1.0") | βœ… | | `is_stable` | BOOLEAN | μ•ˆμ •η‰ˆ μ—¬λΆ€ | βœ… | | `release_note` | TEXT | 배포 λ…ΈνŠΈ | ❌ | | `download_url` | TEXT | λ‹€μš΄λ‘œλ“œ URL | βœ… | | `min_required_version` | TEXT | μ΅œμ†Œ μš”κ΅¬ 버전 (μ—…κ·Έλ ˆμ΄λ“œ μ‹œ) | ❌ | | `created_at` | TIMESTAMP | μƒμ„±μΌμ‹œ | βœ… | | `updated_at` | TIMESTAMP | μˆ˜μ •μΌμ‹œ | βœ… | **μ˜ˆμ‹œ 데이터**: ```json { "program_id": "voc_monitor", "version": "3.1.0", "is_stable": true, "release_note": "- 크둀링 필터링 κ°œμ„ \n- Z-order 문제 ν•΄κ²°\n- μžλ™ μ—…λ°μ΄νŠΈ κΈ°λŠ₯ μΆ”κ°€", "download_url": "https://example.com/releases/voc_monitor_3.1.0.exe", "min_required_version": "2.0.0", "created_at": "2026-02-18T10:00:00Z" } ``` --- ### 10.3 둜컬 버전 관리 파일 **1. `__version__.py`** ```python """ 버전 정보 이 νŒŒμΌμ€ ν”„λ‘œκ·Έλž¨μ˜ ν˜„μž¬ 버전을 μ •μ˜ν•©λ‹ˆλ‹€. μ—…λ°μ΄νŠΈ μ‹œ 이 파일의 VERSIONκ³Ό λΉ„κ΅ν•˜μ—¬θΏ›θ‘Œζ£€ζŸ₯ν•©λ‹ˆλ‹€. """ VERSION = "3.0.0" PROGRAM_ID = "voc_monitor" APP_NAME = "VOC λͺ¨λ‹ˆν„°λ§" ``` **2. `updatelog.md`** (μœ„μΉ˜: `app/updater/updatelog.md`) ```markdown # μ—…λ°μ΄νŠΈ 둜그 ## v3.1.0 (2026-02-18) - 크둀링 필터링 κ°œμ„  (AND/OR λͺ¨λ“œ 지원) - Z-order 문제 ν•΄κ²° - μžλ™ μ—…λ°μ΄νŠΈ κΈ°λŠ₯ μΆ”κ°€ ## v3.0.0 (2026-02-17) - Controller λ¦¬νŒ©ν† λ§ - Manager νŒ¨ν„΄ 적용 ``` > **μ€‘μš”**: `updatelog.md`λŠ” `app/updater/` λͺ¨λ“ˆ 내에 μœ„μΉ˜ν•˜λ©°, 개발 κ³Όμ •μ—μ„œ λ°œμƒν•œ 변경사항을 κΈ°λ‘ν•˜λŠ” μš©λ„μž…λ‹ˆλ‹€. λͺ¨λ“  버전 μ—…λ°μ΄νŠΈ μ‹œ 이 νŒŒμΌμ„ μ΅œμ‹ ν™”ν•΄μ•Ό ν•©λ‹ˆλ‹€. --- ### 10.4 λͺ¨λ“ˆ 섀계 (utils/auto_updater.py) ```python class AutoUpdater: """ μžλ™ μ—…λ°μ΄νŠΈ κ΄€λ¦¬μž Supabaseμ—μ„œ 버전 정보λ₯Ό μ‘°νšŒν•˜κ³  μ—…λ°μ΄νŠΈλ₯Ό μ²˜λ¦¬ν•©λ‹ˆλ‹€. Attributes: program_id: ν”„λ‘œκ·Έλž¨ μ‹λ³„μž current_version: ν˜„μž¬ 버전 supabase_url: Supabase ν”„λ‘œμ νŠΈ URL supabase_key: Supabase API ν‚€ check_interval: μ—…λ°μ΄νŠΈ 체크 간격 (κΈ°λ³Έ: 1μ‹œκ°„) μ£Όμš” λ©”μ„œλ“œ: check_for_updates: μ—…λ°μ΄νŠΈ 확인 download_update: μ—…λ°μ΄νŠΈ 파일 λ‹€μš΄λ‘œλ“œ install_update: μ—…λ°μ΄νŠΈ μ„€μΉ˜ start_background_check: λ°±κ·ΈλΌμš΄λ“œ μ—…λ°μ΄νŠΈ 체크 μ‹œμž‘ """ ``` --- ### 10.5 μ—…λ°μ΄νŠΈ 체크 μ£ΌκΈ° ``` 1μ‹œκ°„λ§ˆλ‹€ (κΈ°λ³Έκ°’, μ„€μ • κ°€λŠ₯) β”œβ”€β”€ Supabaseμ—μ„œ μ΅œμ‹  버전 쑰회 β”œβ”€β”€ ν˜„μž¬ 버전과 비ꡐ β”œβ”€β”€ μ—…λ°μ΄νŠΈ ν•„μš” μ‹œ: β”‚ β”œβ”€β”€ μ•Œλ¦Ό λ‹€μ΄μ–Όλ‘œκ·Έ ν‘œμ‹œ β”‚ β”œβ”€β”€ μ‚¬μš©μžκ°€ "μ§€κΈˆ μ„€μΉ˜" 선택 β”‚ β”‚ β”œβ”€β”€ temp 폴더에 λ‹€μš΄λ‘œλ“œ β”‚ β”‚ β”œβ”€β”€ ν˜„μž¬ ν”„λ‘œμ„ΈμŠ€ μ’…λ£Œ β”‚ β”‚ β”œβ”€β”€ μƒˆ ν”„λ‘œμ„ΈμŠ€ μ‹€ν–‰ (업데이터) β”‚ β”‚ └── 업데이터가 κΈ°μ‘΄ 파일 ꡐ체 β”‚ └── "λ‚˜μ€‘μ—" 선택 β†’ λ‹€μŒ μ£ΌκΈ°κΉŒμ§€ λŒ€κΈ° └── μ—…λ°μ΄νŠΈ μ—†μŒ β†’ 둜그만 기둝 ``` **톡합 λ™μž‘ (v3.2.2 반영):** - μ•± μ‹œμž‘ μ‹œ `UpdateManager` μ΄ˆκΈ°ν™” ν›„ λ°±κ·ΈλΌμš΄λ“œ 체크 μ‹œμž‘ - 트레이 메뉴에 `μ—…λ°μ΄νŠΈ 확인` μˆ˜λ™ ν•­λͺ© 제곡 - μ—…λ°μ΄νŠΈ 발견 μ‹œ μ‚¬μš©μž 확인 λ‹€μ΄μ–Όλ‘œκ·Έ ν‘œμ‹œ - μ‚¬μš©μž 승인 μ‹œ `prepare_update()` -> `launch_updater()` -> 메인 μ•± μ’…λ£Œ **둜그 λͺ…μ„Έ (ν•„μˆ˜ 기둝):** - μ΄ˆκΈ°ν™” 성곡/μ‹€νŒ¨ - λ°±κ·ΈλΌμš΄λ“œ 체크 μ‹œμž‘/였λ₯˜ - μˆ˜λ™ 확인 μˆ˜ν–‰/κ²°κ³Ό(μ΅œμ‹ /μ—…λ°μ΄νŠΈ ν•„μš”) - μ€€λΉ„ μ‹€νŒ¨/μ‹€ν–‰ μ‹€νŒ¨ 원인 --- ### 10.6 μ•„ν‚€ν…μ²˜: updater.exe 뢄리 ⭐ μ€‘μš” **ꡬ쑰**: ``` 메인 ν”„λ‘œμ νŠΈ (cx_freeze νŒ¨ν‚Ή) β”œβ”€β”€ updater.exe (별도 νŒ¨ν‚Ή, μ΅œμ†Œ GUI 포함) β”œβ”€β”€ app/ β”‚ └── updater/ β”‚ β”œβ”€β”€ __init__.py β”‚ β”œβ”€β”€ __version__.py # 버전 정보 β”‚ β”œβ”€β”€ update_manager.py # 메인 ν”„λ‘œκ·Έλž¨μš© μ—…λ°μ΄νŠΈ κ΄€λ¦¬μž β”‚ β”œβ”€β”€ updater_gui.py # updater.exe용 GUI β”‚ └── updatelog.md # μ—…λ°μ΄νŠΈ 둜그 └── ... ``` **μ‹€ν–‰ 흐름**: ``` 1. update_manager.py (메인 ν”„λ‘œκ·Έλž¨ λ‚΄): β”œβ”€β”€ Supabaseμ—μ„œ μƒˆ 버전 확인 β”œβ”€β”€ μ—…λ°μ΄νŠΈ config.json 생성 (%TEMP%/voc_updater_config.json) β”‚ { β”‚ "download_url": "https://supabase.../voc_noti_3.2.0.zip", β”‚ "target_path": "C:/Program Files/voc_noti", β”‚ "version": "3.2.0", β”‚ "restart_exe": "voc_noti.exe" β”‚ } β”œβ”€β”€ updater.exeλ₯Ό %TEMP%둜 볡사 └── updater.exe μ‹€ν–‰ ν›„ 메인 ν”„λ‘œκ·Έλž¨ μ’…λ£Œ 2. updater.exe (별도 ν”„λ‘œμ„ΈμŠ€): β”œβ”€β”€ config.json 읽기 β”œβ”€β”€ zip λ‹€μš΄λ‘œλ“œ (μ§„ν–‰λ₯  ν‘œμ‹œ - CustomTkinter GUI) β”œβ”€β”€ μ••μΆ• ν•΄μ œ β†’ 파일 ꡐ체 └── 메인 ν”„λ‘œκ·Έλž¨ μž¬μ‹€ν–‰ ``` **updater.exe UI**: - CustomTkinter 기반 μ΅œμ†Œ GUI μ°½ - λ‹€μš΄λ‘œλ“œ μ§„ν–‰λ₯  ν‘œμ‹œ (Progress Bar) - μƒνƒœ λ©”μ‹œμ§€ ν‘œμ‹œ (λ‹€μš΄λ‘œλ“œ 쀑, μ••μΆ• ν•΄μ œ 쀑, μ™„λ£Œ) - 성곡/μ‹€νŒ¨ κ²°κ³Ό ν‘œμ‹œ **파일 ꡐ체 방식**: - **전체 ꡐ체**: zip λ‹€μš΄λ‘œλ“œ ν›„ μ••μΆ• ν•΄μ œ - λ°±μ—… 폴더 생성 (이전 버전 보쑴) - μ‹€νŒ¨ μ‹œ λ‘€λ°± κ°€λŠ₯ **톡신 방식**: - μž„μ‹œ JSON 파일 (`%TEMP%/voc_updater_config.json`) - 메인 ν”„λ‘œκ·Έλž¨μ΄ 파일 생성 β†’ updater.exeκ°€ 읽기 --- ### 10.7 μ˜ˆμ™Έ μΌ€μ΄μŠ€ 처리 | 상황 | 처리 방법 | |------|-----------| | λ„€νŠΈμ›Œν¬ 였λ₯˜ | μ—…λ°μ΄νŠΈ 체크 κ±΄λ„ˆλ›°κΈ°, 둜그 기둝 | | Supabase μ—°κ²° μ‹€νŒ¨ | μž¬μ‹œλ„ (3회), μ‹€νŒ¨ μ‹œ λ‹€μŒ μ£ΌκΈ°κΉŒμ§€ λŒ€κΈ° | | λ‹€μš΄λ‘œλ“œ μ‹€νŒ¨ | μ—λŸ¬ λ©”μ‹œμ§€ ν‘œμ‹œ, μž¬μ‹œλ„ μ˜΅μ…˜ 제곡 | | μ„€μΉ˜ 쀑 였λ₯˜ | λ‘€λ°± (λ°±μ—… 파일 ν™œμš©), μ—λŸ¬ 둜그 기둝 | | 자기 μžμ‹  μ—…λ°μ΄νŠΈ | temp 폴더에 볡사 ν›„ 별도 ν”„λ‘œμ„ΈμŠ€λ‘œ μ‹€ν–‰ | | 버전 뢈일치 | min_required_version κΈ°μ€€ νŒλ‹¨ | **μΆ”κ°€ μ•ˆμ •ν™” 반영 (톡합 μ „ 단독 검증 단계)**: - Supabase URL/KEY λˆ„λ½ μ‹œ 사전 μ„€μ • 였λ₯˜ λ°˜ν™˜ - `voc_updater_config.json` μ›μžμ  μ €μž₯(`.tmp` -> ꡐ체) - updater.exe λˆ„λ½ μ‹œ μ€€λΉ„ λ‹¨κ³„μ—μ„œ μ¦‰μ‹œ μ‹€νŒ¨ λ°˜ν™˜ - μ••μΆ• ν•΄μ œ μ‹œ μž„μ‹œ 폴더 μ‚¬μš© ν›„ λŒ€μƒ κ²½λ‘œμ— 단계적 볡사 - μ„€μΉ˜ μ‹€νŒ¨ μ‹œ λ°±μ—… λ‘€λ°± + μž„μ‹œ 폴더 정리 --- ### 10.8 λ©€ν‹°ν”„λ‘œμ νŠΈ 지원 **섀계 원칙**: - `program_id` 기반으둜 동적 μ„€μ • - Supabase ν…Œμ΄λΈ” κ΅¬μ‘°ηš„η»ŸδΈ€ - μ„€μ • 파일둜 URL/Key 관리 **μ‚¬μš© μ˜ˆμ‹œ**: ```python # VOC λͺ¨λ‹ˆν„°λ§ updater = AutoUpdater( program_id="voc_monitor", current_version=VERSION, settings=settings ) # λ‹€λ₯Έ ν”„λ‘œμ νŠΈ updater = AutoUpdater( program_id="other_app", current_version="1.0.0", settings=settings ) ``` --- ### 10.9 μ˜μ‘΄μ„± ```json { "supabase": "^2.0.0", "requests": "^2.31.0" } ``` ### 10.10 μ—°κ²° μ„€μ • 관리 (ν•˜λ“œμ½”λ”© κΈˆμ§€) - Supabase μ—°κ²° μ •λ³΄λŠ” `app/updater/config.json`μ—μ„œ 관리 - `config_url`: 원격 μ„€μ • URL - `fallback.supabase_url`, `fallback.anon_key`: 원격 μ‹€νŒ¨ μ‹œ λŒ€μ²΄κ°’ - λŸ°νƒ€μž„ λ™μž‘ 섀정은 `settings.json`의 `update` μ„Ήμ…˜μ—μ„œ 관리 - `connection_config_path`, `environment`, `check_interval_hours`, `program_id`, `version_table` - 버전 체크 μ‹œ ν…Œμ΄λΈ” μžλ™ 폴백 지원 - `program_version` 쑰회 μ‹€νŒ¨(404) μ‹œ `program_versions` μž¬μ‹œλ„ ### 10.11 ν…ŒμŠ€νŠΈ λͺ¨λ“œ 검증 μ˜΅μ…˜ - `python app/main.py --test --test-update-now` - ν…ŒμŠ€νŠΈ DB λͺ¨λ“œλ‘œ 앱을 μ‹€ν–‰ν•˜λ©΄μ„œ updater μ—°κ²° 확인을 μ‹œμž‘ 직후 1회 μˆ˜ν–‰ - λ„€νŠΈμ›Œν¬/μ„€μ • 였λ₯˜ μ‹œ μ‚¬μš©μž λ©”μ‹œμ§€ + 둜그 기둝 ### 10.12 업데이터 λΉŒλ“œ/톡합 절차 1. 업데이터 단독 λΉŒλ“œ - `python app/update_build_setup.py` - κ²°κ³Ό: `app/updater_build/dist/updater.exe` 2. 메인 νŒ¨ν‚€μ§• - `python app/setup.py build` - 포함 ν•­λͺ©: - `updater.exe` (메인 exe와 동일 경둜) - `app/updater/config.json` (μ—°κ²° μ„€μ •) 3. λŸ°νƒ€μž„ - `UpdateManager.prepare_update()`κ°€ μ„€μΉ˜ 경둜의 `updater.exe`λ₯Ό `%TEMP%`둜 볡사 ν›„ μ‹€ν–‰ --- ## 11. ν–₯ν›„ κ°œμ„  사항 (Roadmap μ°Έμ‘°) - [ ] 닀쀑 ν˜Έμ„  지원 (2ν˜Έμ„ , 3ν˜Έμ„ ) - [ ] 톡계 λŒ€μ‹œλ³΄λ“œ - [ ] μ—‘μ…€ 내보내기 - [ ] μžλ™ 응닡 ν…œν”Œλ¦Ώ --- μž‘μ„±μž: KH.Choi μ΅œμ’… μˆ˜μ •: 2026-02-18 버전: 3.3