<b>OLEDi Commander</b>
Software Requirements Specification
Windows 전용 시맨틱 런처 & 워크스페이스 매니저
| 항목 | 내용 |
|---|---|
| 문서 버전 | v1.0 |
| 작성일 | 2026-03-21 |
| 최종 검토일 | 2026-03-21 |
| 상태 | 초안 (Draft) |
| 대상 플랫폼 | Windows 10 / 11 (x64) |
| 구현 언어 | C# / .NET 8 / WPF |
<br/>
<br/>
OLEDi Commander는 macOS 생산성 도구 Alfred에서 영감을 받아, Windows 환경에서 동등하거나 그 이상의 생산성을 제공하기 위해 설계된 키보드 우선(Keyboard-first) 런처 겸 워크스페이스 매니저입니다.
전통적인 마우스 중심 UI 조작을 Alt+Space 단축키 하나로 대체하여, 개발자·파워유저가 컨텍스트 전환 비용 없이 빠르게 작업을 수행할 수 있도록 합니다.
본 문서는 OLEDi Commander 1.0 릴리스를 대상으로 하며, 아래 두 핵심 모듈을 포함합니다.
아래 항목은 v1.0 범위에서 제외됩니다.
| 용어 | 정의 |
|---|---|
| HWND | Windows API에서 창(Window)을 식별하는 핸들 값 |
| Rect | 창의 좌상단·우하단 좌표로 이루어진 사각형 구조체 |
| Profile | 특정 시점의 창 배치 및 크기 정보를 저장한 JSON 객체 |
| Alias | 긴 경로·명령을 짧게 치환하는 사용자 정의 키워드 |
| Fuzzy Search | 오타·부분 입력도 유사 항목을 찾아주는 비정확 검색 |
| Global Hook | 어떤 앱이 포커스를 갖고 있어도 키 입력을 감지하는 OS 훅 |
| Plugin | CommandResolver에 새로운 ActionHandler를 추가하는 확장 단위 |
| Skill | Plugin의 다른 표현. 사용자 정의 실행 규칙 묶음 |
| settings.json | 사용자 설정 및 Alias를 저장하는 로컬 JSON 파일 |
<br/>
OLEDi Commander는 단일 프로세스(Single-process) 구조이며, 아래 5개 핵심 모듈로 구성됩니다. 모든 모듈은 의존성 주입(DI) 방식으로 연결되어 테스트 및 확장이 용이합니다.
| 모듈 | 역할 | 핵심 기술 |
|---|---|---|
| Input Listener | 글로벌 키 훅 – 어떤 앱에서도 Alt+Space 감지 | WH_KEYBOARD_LL, RegisterHotKey |
| Context Manager | 열린 창의 HWND, Rect, 프로세스 경로 수집 및 복원 | EnumWindows, GetWindowPlacement, SetWindowPos |
| Command Resolver | 입력 텍스트 파싱 → ActionHandler 라우팅 | Prefix 테이블, ActionHandler 인터페이스 |
| Fuzzy Engine | 파일명·키워드 부분 입력으로 빠른 유사 항목 탐색 | Fuse.js 알고리즘 포팅 또는 FuzzySharp |
| Plugin Host | 외부 .dll 또는 JSON 기반 스킬을 로드·실행 | Reflection, IActionHandler 인터페이스 |
[사용자 키 입력] → Input Listener → Command Resolver → ActionHandler (Shifter / Alfred / Plugin)
[창 배치 저장] → Context Manager → Profile JSON → settings.json
[창 배치 복원] → settings.json → Context Manager → User32 API
Command Resolver는 입력 텍스트의 첫 번째 토큰(prefix)을 기준으로 ActionHandler를 선택합니다. 빌트인 prefix는 다음과 같습니다.
| Prefix | 타입 | 예시 | 동작 |
|---|---|---|---|
| ! | 워크스페이스 | !dev | 저장된 "dev" 프로필 즉시 복원 |
| @ | URL / 웹 | @blog | settings.json의 해당 URL 브라우저로 오픈 |
| # | 동적 API | #jira | 설정된 API 어댑터 호출 후 결과 표시 |
| > | 터미널 명령 | >git status | Windows Terminal / PowerShell에서 실행 |
| ~ | 파일 경로 | ~projects | 등록된 폴더 경로를 탐색기로 오픈 |
| (없음) | Fuzzy 검색 | vsc | 인덱스에서 유사 파일·앱·Alias 검색 |
| 확장 가능성 위 prefix 목록은 settings.json의 "prefixMap" 배열을 통해 사용자가 직접 추가·변경할 수 있습니다. 플러그인 개발자는 IActionHandler 인터페이스를 구현하는 .dll을 제공하여 새로운 prefix 동작을 등록합니다. 자세한 내용은 섹션 7 개발자 확장 가이드를 참조하십시오. |
|---|
<br/>
"어떤 위치에 어떤 크기로" 떠 있는지까지 관리하는 워크스페이스 레이아웃 엔진입니다. 단순 앱 실행을 넘어 창의 상태(State)를 완전히 복원합니다.
키보드만으로 마우스 클릭 수십 번의 가치를 만들어냅니다. Alt+Space → 명령어 입력의 단일 패턴으로 모든 작업을 처리합니다.
긴 경로나 복잡한 명령을 짧은 별칭으로 등록합니다. settings.json의 "aliases" 배열로 관리됩니다.
| Alias 타입 | 예시 | 동작 설명 |
|---|---|---|
| url | @blog → https://swarchitect.net/admin | 브라우저로 URL 오픈 |
| folder | ~proj → C:\\Dev\\Projects | 탐색기로 폴더 오픈 |
| app | @term → C:\\Windows\\wt.exe | 지정 실행파일 실행 |
| batch | >build → cmd /c build.bat | 커맨드 실행 (출력 창 선택) |
| api | #jira → JiraAdapter (토큰 설정 필요) | 동적 데이터 조회 후 결과 표시 |
| clipboard | $upper → UPPER_CASE 변환 | 현재 클립보드 텍스트 변환 |
복사한 텍스트를 규칙에 따라 가공합니다. $ prefix로 호출하며, 변환 결과를 클립보드에 덮어쓴 후 활성 창에 붙여넣기(Ctrl+V) 시뮬레이션을 수행합니다.
| 빌트인 변환 | 명령 | 예시 |
|---|---|---|
| JSON 포맷팅 | $json | {"a":1} → 들여쓰기 적용 JSON |
| 유닉스 타임스탬프 → 날짜 | $ts | 1700000000 → 2023-11-14 22:13:20 |
| 날짜 → 유닉스 타임스탬프 | $epoch | 2023-11-14 → 1700000000 |
| 대문자 변환 | $upper | hello → HELLO |
| 소문자 변환 | $lower | HELLO → hello |
| URL 인코딩 | $urle | 한글 → %ED%95%9C%EA%B8%80 |
| URL 디코딩 | $urld | %ED%95%9C%EA%B8%80 → 한글 |
| Base64 인코딩 | $b64e | text → dGV4dA== |
| Base64 디코딩 | $b64d | dGV4dA== → text |
| 마크다운 → 텍스트 | $md | **bold** → bold |
사용자 정의 변환 규칙은 settings.json의 "clipboardTransformers" 배열에 추가하며, 정규식 기반 변환과 외부 스크립트 호출을 지원합니다. 자세한 내용은 섹션 7.4를 참조하십시오.
# prefix를 사용하는 Alias는 외부 API를 호출하여 동적 결과를 표시합니다. 각 API 연결은 "apiAdapters" 설정으로 정의합니다.
| 항목 | 요구사항 |
|---|---|
| 인증 | Personal Access Token(PAT) 또는 OAuth 2.0. 토큰은 Windows Credential Manager에 저장 |
| 네트워크 오류 | 3초 내 응답 없으면 타임아웃, 오프라인 캐시(최대 1시간) 표시 |
| 결과 표시 | 최대 10개 항목을 런처 결과 리스트에 표시, Enter로 기본 동작(URL 오픈) 실행 |
| 빌트인 어댑터 | Jira Cloud, GitHub Issues (v1.0). 추가 어댑터는 플러그인으로 제공 |
<br/>
| 요소 | 명세 |
|---|---|
| 형태 | 화면 중앙 상단 1/3 지점에 위치하는 반투명 바(Bar) 형태 오버레이 |
| 기본 크기 | 너비: 680px, 높이: 54px (입력 상태). 결과 표시 시 높이 자동 확장 |
| 투명도 | Opacity 0.96 (기본). settings.json에서 0.7~1.0 조정 가능 |
| 테마 | 시스템 다크/라이트 모드 자동 감지(WMI 또는 레지스트리 감시). 수동 고정 설정 가능 |
| 폰트 | Segoe UI (영문), Malgun Gothic (한글), 16px |
| 애니메이션 | 호출: 상단 20px에서 Fade-in + SlideDown (120ms). 닫힘: Fade-out (80ms) |
| 항상 최상위 | Topmost = true. 다른 창에 가려지지 않음 |
| 포커스 처리 | 런처 외부 클릭 시 자동 닫힘 (LostFocus 이벤트) |
<br/>
| 항목 | 목표 수치 | 측정 방법 |
|---|---|---|
| Fuzzy 검색 응답 | < 100ms (p95) | 로컬 인덱스 10,000건 기준 자동화 벤치마크 |
| Instant Restore (5창 기준) | < 1.5초 | 스톱워치 측정, 5회 평균 |
| 앱 시작 시간 | < 800ms (백그라운드 Ready 상태) | 프로세스 시작 → 트레이 아이콘 표시까지 |
| 메모리 사용량 | 유휴 시 < 80MB RSS | Process Explorer 모니터링 |
| CPU 점유율 | 유휴 시 < 0.5% | 10분 유휴 후 평균 |
| 인덱싱 시간 | 10,000파일 < 3초 | 초기 기동 시 측정 |
| 보안 주의사항 – Windows Defender / EDR 탐지 WH_KEYBOARD_LL 훅은 일부 보안 소프트웨어에서 키로거로 오탐될 수 있습니다. 대응 방안: 앱 배포 시 코드 서명 인증서 적용, Microsoft Store 등록 검토. 기업 환경 배포 시에는 GPO 화이트리스트 등록 가이드를 별도 제공합니다. |
|---|
<br/>
| 시나리오 | 원인 | 처리 방침 |
|---|---|---|
| Restore 시 앱 미실행 | 저장된 EXE가 닫혀 있음 | 자동 EXE 실행 → 3초 대기 → 실패 시 건너뜀, 결과 알림 |
| 모니터 구성 변경 | 모니터 추가/제거/해상도 변경 | settings.json의 "monitorMismatch" 정책에 따라 fit/skip/warn |
| 단축키 충돌 | 타 앱이 Alt+Space 선점 | 등록 실패 감지 → 트레이에 경고 → 대체 단축키 제안 |
| 관리자 권한 창 제어 | 타 프로세스가 elevated | SetWindowPos 예외 캐치 → 해당 창 건너뜀, 로그 기록 |
| settings.json 손상 | JSON 파싱 오류 | 원본 .bak 저장 → 기본값 로드 → 복구 안내 메시지 |
| Alias 중복 등록 | 동일 키워드 중복 | 나중에 추가된 항목이 우선. 경고 로그 기록 |
| 플러그인 로드 실패 | .dll 서명 불일치 또는 예외 | 해당 플러그인 건너뜀, 오류 로그. 앱은 계속 실행 |
| API 타임아웃 | 네트워크 불안정 | 3초 타임아웃 → 오프라인 캐시 표시 (1시간 유효) → 캐시 없으면 오류 메시지 |
| 앱 인덱스 폴더 접근 불가 | 권한 없는 폴더 | 해당 폴더 스킵, 인덱싱 완료 후 경고 로그 |
| Fuzzy 검색 결과 없음 | 입력에 일치 항목 없음 | "일치하는 항목이 없습니다" 표시, 입력 지속 허용 |
<br/>
모든 사용자 설정과 Profile, Alias는 단일 JSON 파일로 관리됩니다. 파일 위치: %APPDATA%\OLEDiCommander\settings.json
{
"version": "1.0",
"hotkey": "Alt+Space",
"launcher": {
"opacity": 0.96,
"maxResults": 7,
"theme": "system",
"position": "center-top"
},
"indexPaths": [
"%USERPROFILE%\\Desktop",
"%APPDATA%\\Microsoft\\Windows\\Start Menu"
],
"monitorMismatch": "warn",
"profiles": [
{
"name": "dev",
"windows": [
{
"exe": "C:\\Program Files\\Microsoft VS Code\\Code.exe",
"title": "Visual Studio Code",
"rect": { "x": 0, "y": 0, "width": 1280, "height": 1080 },
"showCmd": "Normal",
"monitor": 0
}
]
}
],
"aliases": [
{ "key": "@blog", "type": "url", "target": "https://swarchitect.net/admin" },
{ "key": "#jira", "type": "api", "adapter": "jira", "query": "assignee=currentUser() ORDER BY updated DESC" },
{ "key": "~proj", "type": "folder", "target": "C:\\Dev\\Projects" },
{ "key": ">build", "type": "batch", "target": "cmd /c C:\\Dev\\build.bat", "showWindow": false }
],
"clipboardTransformers": [
{ "key": "$myRule", "type": "regex", "pattern": "(\\d{4})-(\\d{2})-(\\d{2})", "replace": "$3/$2/$1" }
],
"apiAdapters": [
{ "id": "jira", "baseUrl": "https://yourorg.atlassian.net", "credentialKey": "jira_pat" }
],
"plugins": [
{ "path": "C:\\OLEDiPlugins\\MyPlugin.dll", "enabled": true }
]
}
<br/>
이 섹션은 OLEDi Commander에 새로운 기능, 명령어, API 연결, 변환 규칙을 추가하려는 개발자를 위한 문서입니다. 확장 방법은 세 가지 경로로 제공됩니다.
| 확장 방법 | 난이도 | 추천 대상 |
|---|---|---|
| settings.json 수정 | 쉬움 | URL Alias, 폴더 단축키, 배치 명령 추가 |
| JSON 스킬 파일 (.skill.json) | 보통 | API 어댑터, 커스텀 변환 규칙 |
| .dll 플러그인 (C# IActionHandler) | 어려움 | 완전히 새로운 명령 타입, UI 커스터마이징 |
가장 간단한 확장 방법입니다. settings.json 파일을 텍스트 에디터로 열어 "aliases" 배열에 항목을 추가합니다.
// URL 열기 Alias 추가
{ "key": "@notion", "type": "url", "target": "https://notion.so/your-workspace" }
// 폴더 열기 Alias 추가
{ "key": "~dl", "type": "folder", "target": "%USERPROFILE%\\Downloads" }
// 배치 파일 실행 Alias 추가 (출력 창 표시)
{ "key": ">deploy", "type": "batch", "target": "cmd /c C:\\deploy.bat", "showWindow": true }
// 배치 파일 실행 Alias 추가 (백그라운드 실행)
{ "key": ">silent", "type": "batch", "target": "powershell -File C:\\task.ps1", "showWindow": false }
| 팁: 환경변수 사용 "target" 값에 %USERPROFILE%, %APPDATA%, %TEMP% 등 Windows 환경변수를 사용할 수 있습니다. 앱 실행 시 자동으로 확장됩니다. |
|---|
settings.json의 "clipboardTransformers" 배열에 변환 규칙을 추가합니다. 정규식(regex) 또는 외부 스크립트(script) 타입을 지원합니다.
// 정규식 변환: 날짜 형식 YYYY-MM-DD → DD/MM/YYYY
{
"key": "$date",
"type": "regex",
"pattern": "(\\d{4})-(\\d{2})-(\\d{2})",
"replace": "$3/$2/$1",
"description": "ISO 날짜를 로컬 날짜 형식으로 변환"
}
// PowerShell 스크립트 호출 변환
{
"key": "$ps",
"type": "script",
"command": "powershell -NoProfile -Command \"$input | ConvertTo-Json\"",
"timeout": 5000,
"description": "클립보드 텍스트를 PowerShell로 처리"
}
스크립트 타입의 경우, 앱은 클립보드 텍스트를 stdin으로 전달하고 stdout 결과를 클립보드에 저장합니다. timeout(ms) 초과 시 원본 텍스트를 유지합니다.
새로운 API 서비스(예: Notion, Linear, Slack)를 연결하려면 .skill.json 파일을 작성합니다. 파일은 %APPDATA%\OLEDiCommander\skills\ 폴더에 저장합니다.
// %APPDATA%\OLEDiCommander\skills\notion.skill.json
{
"id": "notion",
"name": "Notion 페이지 검색",
"version": "1.0",
"prefix": "#notion",
"credential": {
"type": "bearer_token",
"credentialKey": "notion_api_token"
},
"request": {
"method": "POST",
"url": "https://api.notion.com/v1/search",
"headers": { "Notion-Version": "2022-06-28" },
"body": { "query": "{{INPUT}}", "page_size": 10 }
},
"response": {
"resultsPath": "results",
"titleField": "properties.title.title[0].plain_text",
"subtitleField": "url",
"actionUrl": "url"
},
"cache": { "ttl": 300 }
}
{{INPUT}}은 사용자가 # 이후에 입력한 텍스트로 치환됩니다. 토큰은 앱 내 설정 화면에서 입력하며 Windows Credential Manager에 암호화 저장됩니다.
| 필드 | 필수 | 설명 |
|---|---|---|
| id | 예 | 스킬 고유 식별자 (영문 소문자, 하이픈 허용) |
| prefix | 예 | 런처에서 이 스킬을 호출하는 명령어 (예: #notion) |
| credential.type | 예 | bearer_token / basic_auth / oauth2 중 선택 |
| request.url | 예 | API 엔드포인트. {{INPUT}}, {{TOKEN}} 템플릿 변수 사용 가능 |
| response.resultsPath | 예 | JSON 응답에서 결과 배열 경로 (dot notation) |
| response.actionUrl | 예 | Enter 시 열리는 URL 필드 경로 |
| cache.ttl | 아니오 | 결과 캐시 유효 시간(초). 기본값 0 (캐시 없음) |
완전히 새로운 명령 타입, 복잡한 UI, 또는 OS 레벨 기능이 필요한 경우 C# 플러그인을 개발합니다.
// OLEDiCommander.SDK NuGet 패키지 설치 후 사용
using OLEDiCommander.SDK;
/// <summary>
/// 모든 플러그인은 이 인터페이스를 구현해야 합니다.
/// </summary>
public interface IActionHandler
{
// 이 핸들러가 처리할 prefix (예: "@", "#", "!") 또는 null (Fuzzy 결과에만 등록)
string? Prefix { get; }
// 런처 결과 리스트에 표시할 항목을 반환합니다.
// query: prefix 이후의 입력 텍스트
Task<IEnumerable<LauncherItem>> GetItemsAsync(string query, CancellationToken ct);
// 사용자가 항목을 선택(Enter)했을 때 실행됩니다.
Task ExecuteAsync(LauncherItem item, CancellationToken ct);
// 플러그인 메타데이터
PluginMetadata Metadata { get; }
}
public record LauncherItem(
string Title,
string Subtitle,
string? IconPath, // null이면 기본 아이콘 사용
object? Data // ExecuteAsync에 전달되는 임의 데이터
);
public record PluginMetadata(
string Id,
string Name,
string Version,
string Author
);
using OLEDiCommander.SDK;
using System.Data;
[Export(typeof(IActionHandler))]
public class CalculatorHandler : IActionHandler
{
public string? Prefix => "="; // "=2+3" 입력 시 이 핸들러 호출
public PluginMetadata Metadata => new("calculator", "계산기", "1.0", "YourName");
public async Task<IEnumerable<LauncherItem>> GetItemsAsync(string query, CancellationToken ct)
{
try
{
var result = new DataTable().Compute(query, null);
return [new LauncherItem($"= {result}", "Enter로 복사", null, result.ToString())];
}
catch
{
return [new LauncherItem("수식 오류", "올바른 수식을 입력하세요", null, null)];
}
}
public async Task ExecuteAsync(LauncherItem item, CancellationToken ct)
{
if (item.Data is string val)
Clipboard.SetText(val); // 결과를 클립보드에 복사
}
}
{ "path": "C:\\OLEDiPlugins\\MyPlugin.dll", "enabled": true }
| 개발 팁: 핫 리로드 개발 중에는 시스템 트레이 → "개발자 모드" 활성화 시 플러그인 파일 변경 감지 후 자동 재로드됩니다. OLEDiCommander.SDK.dll은 NuGet 패키지(OLEDiCommander.SDK)로 배포됩니다. 단위 테스트 시 MockLauncherContext를 주입하여 UI 없이 IActionHandler를 테스트할 수 있습니다. |
|---|
새 기능을 추가하기 전에 아래 항목을 확인하십시오.
| 체크 항목 | 확인 기준 |
|---|---|
| Prefix 충돌 확인 | 기존 Prefix 테이블(섹션 2.2)과 중복 없는지 확인 |
| 에러 처리 | 네트워크 오류, 타임아웃, null 결과 등 모든 예외 처리 구현 |
| CancellationToken 사용 | 사용자가 ESC 입력 시 진행 중인 비동기 작업 즉시 취소 |
| 캐싱 고려 | API 호출 결과는 TTL 캐시 적용으로 불필요한 네트워크 요청 방지 |
| 로깅 추가 | ILogger |
| 단위 테스트 | GetItemsAsync, ExecuteAsync 각각 최소 1개 이상 테스트 케이스 작성 |
| 아이콘 제공 | 32x32 PNG 아이콘을 LauncherItem.IconPath에 포함 |
| 설명 작성 | PluginMetadata.Name, settings.json의 description 필드 작성 |
<br/>
| 항목 | 내용 |
|---|---|
| 배포 형태 | MSIX 패키지 (Microsoft Store) 또는 Squirrel.Windows 인스톨러 (.exe) |
| Self-contained | .NET 8 Self-contained 배포 – 별도 런타임 설치 불필요 |
| 설치 경로 | %LOCALAPPDATA%\Programs\OLEDiCommander\ |
| 설정 경로 | %APPDATA%\OLEDiCommander\ |
| 자동 시작 | HKCU Run 레지스트리 키 등록 (설치 시 옵션 선택) |
| 자동 업데이트 | 앱 실행 시 GitHub Releases API 버전 확인, 백그라운드 다운로드 |
| 제거 | 일반 "앱 및 기능"으로 제거 가능. 설정 파일 유지 여부 선택 |
| 항목 | 우선순위 | 비고 |
|---|---|---|
| 플러그인 마켓플레이스 UI | 높음 | 서드파티 스킬 검색/설치/업데이트 통합 UI |
| OAuth 2.0 인증 흐름 | 높음 | GitHub, Google 등 OAuth 기반 API 어댑터 지원 |
| AI 자연어 명령 | 중간 | 예: "1주일 전 내 Jira 티켓 열어줘" → #jira 쿼리 자동 생성 |
| 클라우드 설정 동기화 | 중간 | OneDrive / iCloud Drive를 통한 settings.json 동기화 |
| 플러그인 서명 강제화 | 높음 | v2.0부터 서명 없는 .dll 실행 차단 |
| 스크립트 언어 지원 | 낮음 | Python / Node.js 스크립트를 스킬로 직접 등록 |
| 음성 명령 입력 | 낮음 | Windows Speech API 연동 |
| 버전 | 날짜 | 작성자 | 변경 내용 |
|---|---|---|---|
| v1.0 | 2026-03-21 | — | 초안 작성 (SRS 전체 구조 + 개발자 확장 가이드 포함) |