254 lines
15 KiB
HTML
254 lines
15 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="ko"><head><meta charset="utf-8">
|
|
<title>AX_Commander_개발문서</title>
|
|
<style>
|
|
body { font-family: "맑은 고딕", "Malgun Gothic", sans-serif; font-size: 11pt; color: #1a1a1a; line-height: 1.7; max-width: 900px; margin: 0 auto; padding: 40px 20px; }
|
|
h1 { font-size: 22pt; color: #1F3864; border-bottom: 3px solid #4472C4; padding-bottom: 8px; margin-top: 32px; }
|
|
h2 { font-size: 16pt; color: #2E75B6; border-bottom: 2px solid #B8CCE4; padding-bottom: 4px; margin-top: 28px; }
|
|
h3 { font-size: 13pt; color: #44546A; margin-top: 22px; }
|
|
h4 { font-size: 12pt; color: #44546A; margin-top: 18px; }
|
|
p { margin: 6px 0; }
|
|
table { border-collapse: collapse; width: 100%; margin: 12px 0; }
|
|
th { background: #2E75B6; color: white; padding: 8px 10px; text-align: left; font-size: 10pt; }
|
|
td { padding: 6px 10px; border: 1px solid #D6E4F0; font-size: 10pt; }
|
|
tr:nth-child(even) td { background: #EBF3FB; }
|
|
ul, ol { margin: 6px 0 6px 24px; }
|
|
li { margin: 3px 0; }
|
|
code { font-family: Consolas, "D2Coding", monospace; background: #F0F0F0; padding: 1px 4px; border-radius: 3px; font-size: 10pt; }
|
|
.footer { text-align: right; color: #999; font-size: 9pt; margin-top: 40px; border-top: 1px solid #ddd; padding-top: 8px; }
|
|
</style></head><body>
|
|
<p><b>AX Commander</b></p>
|
|
<p>개발 문서</p>
|
|
<p>Windows 전용 시맨틱 런처 & 워크스페이스 매니저</p>
|
|
|
|
<h1>사내 전용 프로그램 개발 원칙</h1>
|
|
<p>AX Commander는 사내 전용 프로그램입니다. 아래 원칙을 반드시 준수하여 개발하세요.</p>
|
|
<h2>외부 연결 금지</h2>
|
|
<table>
|
|
<tr><th><b>금지 항목</b></th><th><b>설명</b></th></tr>
|
|
<tr><td>외부 HTTP/HTTPS 호출</td><td>HttpClient, WebClient, HttpWebRequest 등으로 외부 서버에 요청 금지</td></tr>
|
|
<tr><td>외부 SDK 연동</td><td>서드파티 클라우드 SDK, AI API, SaaS 라이브러리 사용 금지</td></tr>
|
|
<tr><td>원격 플러그인 다운로드</td><td>런타임에 외부에서 DLL/코드 다운로드 금지</td></tr>
|
|
<tr><td>텔레메트리 / 오류 수집</td><td>사용 데이터를 외부 서버로 전송하는 일체의 코드 금지</td></tr>
|
|
<tr><td>NuGet 패키지</td><td>외부 네트워크 통신이 내장된 패키지 신규 도입 금지</td></tr>
|
|
</table>
|
|
<p>허용: 로컬 파일 시스템 접근, Windows API (P/Invoke), 사내 인트라넷 URL, 로컬 프로세스 실행</p>
|
|
|
|
<h1>아키텍처 개요</h1>
|
|
<h2>핵심 컴포넌트</h2>
|
|
<table>
|
|
<tr><th><b>컴포넌트</b></th><th><b>역할</b></th></tr>
|
|
<tr><td>App.xaml.cs</td><td>앱 진입점, 서비스 초기화, 트레이 아이콘 관리</td></tr>
|
|
<tr><td>LauncherWindow</td><td>WPF 메인 UI (AllowsTransparency, WindowStyle=None)</td></tr>
|
|
<tr><td>CommandResolver</td><td>프리픽스 기반 핸들러 라우팅 테이블</td></tr>
|
|
<tr><td>FuzzyEngine</td><td>퍼지 검색 + 한국어 초성(ㅅㄷ) 매칭</td></tr>
|
|
<tr><td>IndexService</td><td>파일 시스템 인덱서 + FileSystemWatcher</td></tr>
|
|
<tr><td>SettingsService</td><td>settings.json 로드/저장 (자동 백업)</td></tr>
|
|
<tr><td>ClipboardHistoryService</td><td>WM_CLIPBOARDUPDATE 훅 기반 히스토리</td></tr>
|
|
<tr><td>PluginHost</td><td>DLL/JSON 스킬 플러그인 로더</td></tr>
|
|
<tr><td>NotificationService</td><td>트레이 풍선 알림 (타이머/알람)</td></tr>
|
|
</table>
|
|
|
|
<h1>프리픽스 핸들러 치트시트</h1>
|
|
<table>
|
|
<tr><th><b>프리픽스</b></th><th><b>핸들러</b></th><th><b>기능</b></th></tr>
|
|
<tr><td>(없음)</td><td>FuzzyEngine</td><td>앱·파일 퍼지 검색 + 한국어 초성</td></tr>
|
|
<tr><td>=</td><td>CalculatorHandler</td><td>수식 계산, 단위/통화 변환</td></tr>
|
|
<tr><td>/</td><td>SystemCommandHandler</td><td>lock, sleep, restart, shutdown, timer, alarm</td></tr>
|
|
<tr><td>;</td><td>SnippetHandler</td><td>텍스트 스니펫 키워드 검색 & 붙여넣기</td></tr>
|
|
<tr><td>#</td><td>ClipboardHistoryHandler</td><td>클립보드 히스토리 검색 & 병합</td></tr>
|
|
<tr><td>@</td><td>UrlAliasHandler</td><td>URL 단축키</td></tr>
|
|
<tr><td>~</td><td>FolderAliasHandler</td><td>폴더 단축키</td></tr>
|
|
<tr><td>></td><td>BatchHandler</td><td>명령 실행 / 배치 단축키</td></tr>
|
|
<tr><td>$</td><td>ClipboardHandler</td><td>클립보드 텍스트 12종 변환</td></tr>
|
|
<tr><td>!</td><td>WorkspaceHandler</td><td>워크스페이스 저장·복원</td></tr>
|
|
<tr><td>emoji</td><td>EmojiHandler</td><td>이모지 피커 (300+)</td></tr>
|
|
<tr><td>color</td><td>ColorHandler</td><td>HEX/RGB/HSL/HSV 색상 변환</td></tr>
|
|
<tr><td>recent</td><td>RecentFilesHandler</td><td>Windows 최근 파일</td></tr>
|
|
<tr><td>note</td><td>NoteHandler</td><td>빠른 메모 저장·조회</td></tr>
|
|
<tr><td>uninstall</td><td>UninstallHandler</td><td>앱 제거 (레지스트리)</td></tr>
|
|
<tr><td>port</td><td>PortHandler</td><td>TCP 포트/프로세스 점검</td></tr>
|
|
<tr><td>env</td><td>EnvHandler</td><td>환경변수 조회</td></tr>
|
|
<tr><td>json</td><td>JsonHandler</td><td>JSON 검증/포맷/미니파이</td></tr>
|
|
<tr><td>encode</td><td>EncodeHandler</td><td>base64/url/hex/md5/sha256 인코딩</td></tr>
|
|
<tr><td>snap</td><td>SnapHandler</td><td>창 배치 레이아웃</td></tr>
|
|
<tr><td>cap</td><td>ScreenCaptureHandler</td><td>화면/창/스크롤 캡처</td></tr>
|
|
<tr><td>help</td><td>HelpHandler</td><td>도움말</td></tr>
|
|
</table>
|
|
|
|
<h1>설정 스키마 (settings.json)</h1>
|
|
<p>경로: %APPDATA%\AxCommander\settings.json</p>
|
|
<table>
|
|
<tr><th><b>키</b></th><th><b>타입</b></th><th><b>설명</b></th></tr>
|
|
<tr><td>version</td><td>string</td><td>설정 버전</td></tr>
|
|
<tr><td>hotkey</td><td>string</td><td>글로벌 단축키 (기본: Alt+Space)</td></tr>
|
|
<tr><td>launcher.opacity</td><td>float</td><td>창 투명도 (0.0~1.0, 기본 0.96)</td></tr>
|
|
<tr><td>launcher.maxResults</td><td>int</td><td>최대 결과 수 (기본 7)</td></tr>
|
|
<tr><td>launcher.theme</td><td>string</td><td>테마 (system|dark|light|oled|nord|monokai|catppuccin|sepia|alfred|alfredlight|custom)</td></tr>
|
|
<tr><td>launcher.position</td><td>string</td><td>런처 위치 (center-top|center|bottom)</td></tr>
|
|
<tr><td>launcher.webSearchEngine</td><td>string</td><td>기본 검색 엔진 (g|n|d|y|w)</td></tr>
|
|
<tr><td>launcher.snippetAutoExpand</td><td>bool</td><td>스니펫 자동 확장 활성화</td></tr>
|
|
<tr><td>indexPaths</td><td>string[]</td><td>인덱싱할 폴더 경로 목록</td></tr>
|
|
<tr><td>aliases</td><td>object[]</td><td>URL/폴더/배치 단축키 목록</td></tr>
|
|
<tr><td>snippets</td><td>object[]</td><td>텍스트 스니펫 목록</td></tr>
|
|
<tr><td>clipboardHistory.enabled</td><td>bool</td><td>클립보드 히스토리 활성화</td></tr>
|
|
<tr><td>clipboardHistory.maxItems</td><td>int</td><td>최대 보관 개수 (기본 50)</td></tr>
|
|
<tr><td>plugins</td><td>object[]</td><td>DLL 플러그인 경로 목록</td></tr>
|
|
</table>
|
|
|
|
<h1>테마 시스템</h1>
|
|
<h2>내장 테마 목록</h2>
|
|
<table>
|
|
<tr><th><b>테마 키</b></th><th><b>이름</b></th><th><b>특징</b></th></tr>
|
|
<tr><td>system</td><td>시스템</td><td>Windows 다크/라이트 모드 자동 감지</td></tr>
|
|
<tr><td>dark</td><td>Dark</td><td>딥 네이비 다크 (기본)</td></tr>
|
|
<tr><td>light</td><td>Light</td><td>클린 화이트 라이트</td></tr>
|
|
<tr><td>oled</td><td>OLED</td><td>순수 블랙 (OLED 절전)</td></tr>
|
|
<tr><td>nord</td><td>Nord</td><td>Arctic 컬러 팔레트</td></tr>
|
|
<tr><td>monokai</td><td>Monokai</td><td>Sublime Text 스타일</td></tr>
|
|
<tr><td>catppuccin</td><td>Catppuccin</td><td>Mocha 따뜻한 파스텔</td></tr>
|
|
<tr><td>sepia</td><td>Sepia</td><td>황갈색 아날로그 감성</td></tr>
|
|
<tr><td>alfred</td><td>Alfred Dark</td><td>Alfred 5 다크 팔레트 (딥 퍼플-차콜)</td></tr>
|
|
<tr><td>alfredlight</td><td>Alfred Light</td><td>Alfred 5 라이트 팔레트 (클린 화이트)</td></tr>
|
|
<tr><td>custom</td><td>커스텀</td><td>14개 색상 완전 커스터마이징</td></tr>
|
|
</table>
|
|
<h2>테마 ResourceKey 목록 (14개)</h2>
|
|
<table>
|
|
<tr><th><b>키</b></th><th><b>용도</b></th></tr>
|
|
<tr><td>LauncherBackground</td><td>전체 배경</td></tr>
|
|
<tr><td>ItemBackground</td><td>항목 기본 배경</td></tr>
|
|
<tr><td>ItemSelectedBackground</td><td>선택된 항목 배경</td></tr>
|
|
<tr><td>ItemHoverBackground</td><td>호버 배경</td></tr>
|
|
<tr><td>PrimaryText</td><td>주 텍스트 색상</td></tr>
|
|
<tr><td>SecondaryText</td><td>보조 텍스트 색상</td></tr>
|
|
<tr><td>PlaceholderText</td><td>플레이스홀더 텍스트</td></tr>
|
|
<tr><td>AccentColor</td><td>강조색 (선택 바, 배지 등)</td></tr>
|
|
<tr><td>SeparatorColor</td><td>구분선 색상</td></tr>
|
|
<tr><td>HintBackground</td><td>ESC 힌트 배경</td></tr>
|
|
<tr><td>HintText</td><td>ESC 힌트 텍스트</td></tr>
|
|
<tr><td>BorderColor</td><td>창 테두리</td></tr>
|
|
<tr><td>ScrollbarThumb</td><td>스크롤바 색상</td></tr>
|
|
<tr><td>ShadowColor</td><td>드롭 섀도우 색상</td></tr>
|
|
</table>
|
|
<h2>새 테마 추가 절차</h2>
|
|
<ul>
|
|
<li>1. Themes/MyTheme.xaml 작성 (위 14개 키 모두 정의)</li>
|
|
<li>2. AxCommander.csproj에 <Resource Include="Themes/MyTheme.xaml"/> 추가</li>
|
|
<li>3. LauncherWindow.xaml.cs → GetEffectiveThemeName() switch에 케이스 추가</li>
|
|
<li>4. SettingsViewModel.ThemeCards 목록에 ThemeCardModel 항목 추가</li>
|
|
</ul>
|
|
|
|
<h1>보안 고려사항</h1>
|
|
<table>
|
|
<tr><th><b>항목</b></th><th><b>구현</b></th></tr>
|
|
<tr><td>URL 스킴 검증</td><td>http, https, ftp, ms-settings, mailto, file 허용. javascript: 등 차단</td></tr>
|
|
<tr><td>PowerShell 인젝션 방지</td><td>> 명령어 입력의 " 이스케이프 처리</td></tr>
|
|
<tr><td>ReDoS 방지</td><td>사용자 정의 Regex에 TimeSpan 타임아웃 적용</td></tr>
|
|
<tr><td>자격증명 저장</td><td>Windows Credential Manager (advapi32.dll, DPAPI 암호화)</td></tr>
|
|
<tr><td>클립보드 민감 데이터</td><td>Regex 기반 제외 패턴 — 신용카드·IP 등 기본 차단</td></tr>
|
|
<tr><td>API 타임아웃</td><td>JSON 스킬 HTTP 요청 3초 제한</td></tr>
|
|
<tr><td>시스템 명령 확인</td><td>재시작·종료·로그아웃 실행 전 MessageBox.Show 2단계 확인</td></tr>
|
|
</table>
|
|
|
|
<h1>단위 테스트</h1>
|
|
<table>
|
|
<tr><th><b>파일</b></th><th><b>테스트 수</b></th><th><b>대상</b></th></tr>
|
|
<tr><td>FuzzyEngineTests.cs</td><td>19</td><td>CalculateScore, FuzzyMatch, 초성 검색</td></tr>
|
|
<tr><td>ClipboardTransformTests.cs</td><td>21</td><td>12개 내장 변환기</td></tr>
|
|
<tr><td>SettingsServiceTests.cs</td><td>17</td><td>기본값, JSON 직렬화 라운드트립</td></tr>
|
|
</table>
|
|
|
|
<h1>v1.6 3차 전체 점검 수정</h1>
|
|
<p>빌드 오류 3건, 컴파일 경고 1건, 런타임 버그 6건, 보안 이슈 2건을 수정하였습니다.</p>
|
|
<h2>컴파일 오류 수정</h2>
|
|
<table>
|
|
<tr><th><b>파일</b></th><th><b>수정 내용</b></th><th><b>오류 코드</b></th></tr>
|
|
<tr><td>UsageRankingService.cs</td><td>using System.IO; 누락 추가</td><td>CS0103</td></tr>
|
|
<tr><td>SettingsWindow.xaml.cs</td><td>Key.Enter or Key.Return 단일 arm으로 통합</td><td>CS8510</td></tr>
|
|
<tr><td>ContextManager.cs</td><td>EnumDisplayMonitors 람다 타입 혼합 수정</td><td>CS0748</td></tr>
|
|
</table>
|
|
<h2>런타임 버그 수정</h2>
|
|
<table>
|
|
<tr><th><b>파일</b></th><th><b>버그</b></th><th><b>수정</b></th></tr>
|
|
<tr><td>JsonSkillLoader.cs</td><td>field[0 형식 오류 방지 (IndexOf 반환 -1)</td><td>closingIdx < 0 가드 추가</td></tr>
|
|
<tr><td>ClipboardHistoryService.cs</td><td>_ignoreNext 스레드 비안전, 이중 초기화</td><td>volatile + 이중 초기화 가드</td></tr>
|
|
<tr><td>IndexService.cs</td><td>ScheduleRebuild 타이머 비원자적 교체</td><td>_timerLock으로 원자적 교체</td></tr>
|
|
<tr><td>SettingsService.cs</td><td>백업 실패 무음 catch</td><td>LogService.Warn으로 기록</td></tr>
|
|
<tr><td>LauncherWindow.xaml.cs</td><td>Window에 RenderTransform 설정 시 InvalidOperationException</td><td>Content(루트 Border)에 적용</td></tr>
|
|
</table>
|
|
<h2>보안 수정</h2>
|
|
<table>
|
|
<tr><th><b>파일</b></th><th><b>수정 내용</b></th></tr>
|
|
<tr><td>JsonSkillLoader.cs</td><td>ActionUrl 실행 전 http/https 스킴 검증 추가</td></tr>
|
|
<tr><td>SettingsWindow.xaml.cs</td><td>NewIndexPathBox null 역참조 방어 처리</td></tr>
|
|
</table>
|
|
|
|
<h1>빌드 및 배포</h1>
|
|
<h2>빌드 명령</h2>
|
|
<p># 개발 실행</p>
|
|
<p>dotnet run --project src/AxCommander</p>
|
|
|
|
<p># Release 단일 파일 빌드 (self-contained)</p>
|
|
<p>dotnet publish src/AxCommander -c Release -r win-x64 --self-contained</p>
|
|
|
|
<p># 단위 테스트</p>
|
|
<p>dotnet test src/AxCommander.Tests</p>
|
|
<h2>데이터 경로</h2>
|
|
<table>
|
|
<tr><th><b>용도</b></th><th><b>경로</b></th></tr>
|
|
<tr><td>설정 파일</td><td>%APPDATA%\AxCommander\settings.json</td></tr>
|
|
<tr><td>로그</td><td>%APPDATA%\AxCommander\logs\app-YYYY-MM-DD.log</td></tr>
|
|
<tr><td>스킬 파일</td><td>%APPDATA%\AxCommander\skills\*.skill.json</td></tr>
|
|
<tr><td>메모</td><td>%APPDATA%\AxCommander\notes.txt</td></tr>
|
|
<tr><td>사용 빈도</td><td>%APPDATA%\AxCommander\usage.json</td></tr>
|
|
<tr><td>크래시 덤프</td><td>%APPDATA%\AxCommander\crashes\</td></tr>
|
|
</table>
|
|
<h2>요구 사항</h2>
|
|
<ul>
|
|
<li>OS: Windows 10 20H2 이상 (Windows 11 권장)</li>
|
|
<li>.NET 8.0 Runtime (Self-contained 빌드 시 불필요)</li>
|
|
</ul>
|
|
|
|
<h1>플러그인 개발</h1>
|
|
<h2>DLL 플러그인</h2>
|
|
<p>AxCommander.SDK를 참조하여 IActionHandler 인터페이스를 구현합니다.</p>
|
|
<p>using AxCommander.SDK;</p>
|
|
|
|
<p>public class MyHandler : IActionHandler</p>
|
|
<p>{</p>
|
|
<p> public string? Prefix => "?"; // 트리거 프리픽스</p>
|
|
<p> public PluginMetadata Metadata => new("MyPlugin", "설명", "1.0");</p>
|
|
|
|
<p> public async Task<IEnumerable<LauncherItem>> GetItemsAsync(string query, CancellationToken ct)</p>
|
|
<p> {</p>
|
|
<p> return [ new LauncherItem("제목", "부제목", null, myData, Symbol: "\uE721") ];</p>
|
|
<p> }</p>
|
|
|
|
<p> public Task ExecuteAsync(LauncherItem item, CancellationToken ct)</p>
|
|
<p> {</p>
|
|
<p> return Task.CompletedTask;</p>
|
|
<p> }</p>
|
|
<p>}</p>
|
|
<h2>JSON 스킬 (코드 없이 HTTP API 연동)</h2>
|
|
<p>경로: %APPDATA%\AxCommander\skills\*.skill.json</p>
|
|
<p>{</p>
|
|
<p> "id": "my-api",</p>
|
|
<p> "name": "API 연동",</p>
|
|
<p> "prefix": "?",</p>
|
|
<p> "request": {</p>
|
|
<p> "method": "GET",</p>
|
|
<p> "url": "https://intranet.example.com/api?q={{INPUT}}"</p>
|
|
<p> },</p>
|
|
<p> "response": {</p>
|
|
<p> "resultsPath": "items",</p>
|
|
<p> "titleField": "name",</p>
|
|
<p> "subtitleField": "description",</p>
|
|
<p> "actionUrl": "url"</p>
|
|
<p> },</p>
|
|
<p> "cache": 30</p>
|
|
<p>}</p>
|
|
|
|
<p>AX Commander · .NET 8.0 · WPF · MIT License</p>
|
|
<p class='footer'>Converted from: AX_Commander_개발문서.docx</p>
|
|
</body></html>
|