{"id":16921874,"url":"https://github.com/bugoverdose/mini-chrome","last_synced_at":"2026-04-28T15:40:51.037Z","repository":{"id":109989010,"uuid":"451066448","full_name":"bugoverdose/mini-chrome","owner":"bugoverdose","description":"Google Chrome clone coding (feat. Electron)","archived":false,"fork":false,"pushed_at":"2022-12-24T10:11:41.000Z","size":687511,"stargazers_count":0,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-06-23T04:43:28.381Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bugoverdose.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-01-23T10:11:08.000Z","updated_at":"2023-07-31T12:25:08.000Z","dependencies_parsed_at":"2023-03-08T13:45:12.976Z","dependency_job_id":null,"html_url":"https://github.com/bugoverdose/mini-chrome","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/bugoverdose/mini-chrome","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bugoverdose%2Fmini-chrome","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bugoverdose%2Fmini-chrome/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bugoverdose%2Fmini-chrome/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bugoverdose%2Fmini-chrome/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bugoverdose","download_url":"https://codeload.github.com/bugoverdose/mini-chrome/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bugoverdose%2Fmini-chrome/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32387918,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-28T14:34:11.604Z","status":"ssl_error","status_checked_at":"2026-04-28T14:32:37.009Z","response_time":56,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-13T19:53:22.785Z","updated_at":"2026-04-28T15:40:51.021Z","avatar_url":"https://github.com/bugoverdose.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Mini Chrome\n\nChromium에 사용자 인터페이스(UI)를 추가한 구글 크롬 클론 코딩 프로젝트\n\n## 데모\n\n![데모](./mini-chrome-demo.gif)\n\n## 상태 관리 (State Management)\n\n- 메인 프로세스에서 싱글톤 패턴으로 전역 상태 관리\n\n  - 앱 실행 시점에 초기화되는 데이터\n\n- windows : 모든 Window 인스턴스. 각 윈도우에는 현재 실행 중인 BrowserWindow 인스턴스와 tabs 정보 등이 함께 관리됨.\n\n  - id : browserWindow.id를 uid로 사용. 프로세스 간 통신을 위해 일치시키는 것이 용이함.\n  - tabs : 특정 윈도우의 모든 Tab 인스턴스. 하나의 Tab 인스턴스에는 하나의 BrowserView 인스턴스와 tabId 정보가 함께 관리됨.\n\n- tabId : 하나의 탭이 생성될 때마다 1씩 증가. 각 탭의 uid로 배정되는 숫자 데이터.\n\n## 데이터베이스\n\n- 로컬 머신에 JSON 파일로 저장하고 읽는 데이터 (custom-database.json)\n\n- 즐겨찾기 데이터는 favorites key에 배열로 저장\n  - 개별 즐겨찾기 데이터 구조: ({ title, url, favicon })\n\n## IPC(프로세스 간 통신) 로직 핵심 요약\n\n- 특정 윈도우의 헤더 프로세스에서 메인 프로세스에 해당 윈도우 제거, 최소화, 최대화 토글 요청\n\n- 특정 윈도우의 헤더 프로세스에서 메인 프로세스에 tabs 정보를 요청하고, 데이터를 응답받아 화면의 html 태그 추가 및 내용 수정\n\n- 특정 윈도우의 뷰 프로세스에서 메인 프로세스에 개발자 도구 열기, 탭 제거 등의 요청하고, 메인 프로세스는 헤더 프로세스 혹은 해당 뷰 프로세스로 응답\n\n## 구현할 기능 목록 (우테코 스타일)\n\n### 윈도우\n\n- [x] 앱 실행시, 맥에서 아이콘 클릭시 새 윈도우 생성\n\n- [x] 창이 생성되면 생성된 창에 focus되도록 명시\n\n  - [x] 앱 실행시, 생성된 창에 focus시키고, 새 탭인 경우 omnibox에 초점이 오도록 설정\n\n- [x] 창에 focus가 들어온 경우 창(browser window)으로 간 focus를 헤더(browser view)로 그대로 이동시키기 (개발자 입장에서는 포함 관계지만, 실질적으로 서로 별개의 프로세스)\n\n  - [x] 새 탭인 경우 화면의 어디를 클릭했던지와 무관하게, 무조건 omnibox로 초점이 오도록 설정\n\n  - 새 탭이 아니라면 마지막으로 있던 위치에 초점 가도록 내버려두기\n\n  - [x] focus 탭 정보 업데이트 : 다른 창에서의 작업으로 즐겨찾기 정보가 변했을 수 있기 때문에 필요.\n\n- [x] blur된(focus를 잃은) 윈도우는 deactivate시키기 (창이 1개일 때도 포함)\n\n  - [x] 헤더의 상단 부분의 배경색 회색으로\n  - [x] 모든 헤더의 버튼들 회색으로(traffic lights \u0026 view utils)\n\n  - [x] focus 다시 얻으면 원상복구\n\n- [x] 페이지 뷰 내에서 새 창을 여는 경우 (링크를 shift + 클릭, target=\"\\_blank\" 속성의 anchor 클릭 등), 디폴트 윈도우가 아닌 커스텀 윈도우를 생성하고 렌더링하도록\n\n  - 디버깅: 순환참조 문제 때문에 app에 등록. 페이지 뷰만이 아니라 모든 web contents에 설정됨. (리소스 낭비)\n\n### 헤더\n\n#### 헤더 배경\n\n- [x] 마우스로 텍스트 부분만 따로 선택할 수 없도록 설정\n- [x] 헤더 내 스크롤 생성 방지\n- [x] 더블클릭시 toggle maximize\n\n  - [x] event delegation으로 다른 버튼들 더블클릭된 경우에는 동작하지 않도록 명시\n\n#### 신호등 컨트롤러\n\n- [x] 윈도우든 맥이든 맥의 traffic lights 기능 그대로 구현\n  - [x] 화면 닫기\n  - [x] 화면 최소화\n  - [x] 전체화면 모드 토글\n- [x] 특정 버튼을 누르고 있으면 해당 버튼만 살짝 어둡게 색상 변경\n- [x] 특정 버튼 누른 상태로 마우스 이동시, 드래그되지 않도록 설정\n- [x] 특정 버튼에 마우스 hover시 클릭했을 때 발생할 이벤트에 대한 설명 표시\n- [x] focus되지 않은 윈도우에서는 세 버튼 모두 회색으로 설정\n- hover했을 때 관련 아이콘 보여주기 (보류)\n\n- [x] title 속성으로 클릭하면 발생할 이벤트 설명 보여주기\n\n#### 탭\n\n- [x] 현재 윈도우에 존재하는 모든 view들에 대한 간략한 정보 표시\n  - [x] favicon\n  - [x] 디폴트 favicon\n  - [x] title 태그\n- [ ] 특정 탭에 마우스 hover시 탭에 대한 정보 출력\n  - [ ] title 태그\n  - [ ] url 정보\n- [x] 새 탭 생성 기능 구현\n\n  - [x] 새 탭 생성 시점에 뷰 프로세스 갈아끼우기\n  - [x] 새 탭 생성 시점에 input에 키보드 자동 focus\n  - [x] 새 탭(=현재 화면에 보여지는 탭)은 다르게 보이도록 (focus)\n  - [x] 새 탭 생성시, 메인 프로세스로부터 응답받을 때까지 로딩 상태. 탭 영역 클릭 방지.\n\n- [x] 기존 탭 삭제 기능 구현\n\n  - [x] 현재 focus된 탭을 닫는 경우 옆의 탭으로 focus 이동 + 페이지도 이동\n\n- [x] 탭 클릭을 통해 화면에 보여주는 탭 변경\n\n  - [x] 현재 화면에 대응되는 탭은 다르게 보이도록 (focus)\n\n- [ ] 드래그 앤 드롭 기능 구현\n\n  - [ ] 탭들 사이에서 좌우 이동시, 탭들의 순서 변경\n  - [ ] 탭들의 영역을 벗어나는 경우 새로운 윈도우 생성\n\n- [x] 반응형 디자인 구현\n  - [x] 탭의 최소 width는 창 닫는 버튼 크기\n  - [x] focus된 탭은 창 닫는 x 버튼을 위로\n  - [x] z-index: focus되지 않은 탭은 favicon + title을 위로\n  - [x] 탭의 개수가 너무 많으면 새 탭 생성 버튼이 위로 오도록\n\n#### omnibox\n\n- [x] input에 focus된 상태에서는 border를 파란색으로\n- [x] input에 url 입력하여 해당 탭에 페이지 로드\n- [x] url 대신 키워드 입력하는 경우 구글 검색으로 이동\n\n- [x] 로드된 페이지의 title로 해당 탭 업데이트\n\n- [x] 엔터 누르면 곧바로 omnibox에서 unfocus되어 동일한 url 반복 입력 방지 - blur()\n\n- [x] 현재 view에 표시되는 주소가 변한 경우 omnibox에 해당 url 출력\n\n  - [x] url 입력에 따른 변화\n  - [x] 페이지 내 이동에 따른 변화\n\n- omnibox에 자체적으로 입력만 하고 전송하지 않은 데이터도 그대로 저장 (보류)\n\n- https:// 부분은 보이지 않도록 설정\n\n#### view utils\n\n- [x] 뒤로 버튼 구현\n\n  - [x] 뒤로 못 가는 경우 버튼 클릭 방지 및 어둡게\n\n- [x] 앞으로 버튼 구현\n\n  - [x] 앞으로 못 가는 경우 버튼 클릭 방지 및 어둡게\n\n- [x] 뒤로/앞으로 가는 경우 탭의 정보와 뒤로/앞으로 갈 수 있는지의 여부 업데이트\n\n- [x] 새로고침/중지 버튼 구현\n\n  - [x] 로딩 중일 때는 중지 버튼으로 임시 변화\n  - [x] 로딩 종료 후 새로고침 버튼으로 다시 변화\n\n- [x] title 속성으로 클릭하면 발생할 이벤트 설명 보여주기\n\n#### favorites\n\n- [x] 해당 데스크탑 내부에 자체적으로 즐겨찾기 목록 저장\n\n  - [ ] 폴더 형식으로 즐겨찾기 목록 구조화\n\n- [x] omnibox 내에 별 모양 아이콘을 통해 즐겨찾기 여부 확인 가능\n\n- [x] 각 url에 대해 별 모양 토글하여 즐겨찾기 추가 및 제거\n\n  - [x] title 속성으로 클릭하면 발생할 이벤트 설명 보여주기\n\n### 페이지\n\n#### 새 탭 화면\n\n- [x] 새 창 생성 시점에 생성되는 탭에 url이 없는 경우에 보이는 디폴트 화면\n\n- [x] 새 탭 생성 시점에 url이 없는 경우에만 보이는 디폴트 화면\n\n- [x] 새 탭의 화면 내부에서 즐겨찾기 목록 보이도록\n\n  - [x] 즐겨찾기 favicon을 로드 실패하는 경우, 새 탭, 연결 실패한 경우의 아이콘인 경우 무조건 디폴트 아이콘을 보여주도록\n\n  - [x] 각 즐겨찾기 항목 클릭시, 현재 탭은 해당 url로 이동\n\n- [x] title 속성으로 클릭하면 발생할 이벤트 설명 보여주기\n\n- [ ] 새 탭의 화면 내부에서 즐겨찾기 목록 수정 기능\n  - [ ] 즐겨찾기 title 수정\n  - [ ] 즐겨찾기 제거\n  - [ ] 드래그 앤 드롭으로 즐겨찾기 목록 순서 수정\n\n#### 연결 실패 화면\n\n- [x] 입력된 url로 페이지 로드 실패시, 실패 화면으로 이동\n\n  - [x] 실패화면에 omnibox에 입력한 url 정보와 에러 코드 출력\n    - [x] URI 디코딩을 통해 한국어 검색어 등 대응\n  - [x] 실패화면의 title에는 omnibox에 입력했던 검색어 그대로 출력\n    - [x] URI 디코딩을 통해 한국어 검색어 등 대응\n  - [x] favicon은 별도로 생성\n\n#### 뷰 페이지 context menu\n\n- [ ] 화면 내에서 우클릭시 기본 도구\n\n  - [ ] 새 탭 열기\n  - [ ] 새 창 열기\n  - [ ] 개발자 도구 열기 기능\n  - [ ] 새로고침\n  - [ ] 뒤로\n  - [ ] 앞으로\n\n- [ ] a 태그 우클릭시 링크 관련 도구\n\n  - [ ] 새 탭에서 열기 기능\n  - [ ] 새 창에서 열기 기능\n  - [ ] 링크 주소 복사 기능\n\n#### 단축키\n\n- 최종적으로 Menu으로 마이그레이션\n\n- [x] 개발자 도구 토글 : 현재 focus된 창에서 열린 탭의 View에 개발자 도구를 열고 닫기\n\n  - [x] 맥 : COMMAND+OPTION+I\n  - [x] 윈도우 : Ctrl+Shift+I\n  - [x] 공통 : F12\n\n  - [x] header : 개발자 도구 열릴 수 없도록 방지\n  - 새 탭과 연결 실패 화면도 그냥 개발자 도구 열 수 있도록 설정. 굳이 막을 이유도 없고, 뒤로/앞으로 가는 경우 열려있던 개발자도구를 닫는 등 별도의 작업이 필요함.\n\n- [x] 탭 닫기 : focus된 창의 focus된 탭을 닫고 다른 탭으로 focus를 이동시키는 단축키\n\n  - [x] 맥 : COMMAND+W\n  - [x] 윈도우 : CTRL+W\n  - [x] 마지막 탭이면 현재 창 닫고 다른 창으로 이동하도록 기존 api 그대로 사용\n\n- [ ] 새로고침, 뒤로가기, 앞으로가기, 로드 중지 등\n\n- 일단 event.preventDefault 설정하지 말고 디폴트 설정 그대로 사용\n\n  - TAB으로 focus된 화면 내의 조작 가능한 영역들(anchor, input, etc) 순회\n\n  - 선택된 a 태그를 엔터 입력으로 클릭\n\n  - 텍스트 선택 후 잘라내기/복사/붙여넣기\n\n- 단축키: 구현 방법들과 각자의 특성\n\n  1. global shortcut : 운영체제에 단축키 등록.\n\n     - 문제점: 해당 앱이 선택되지 않은 상황에서도 실행됨. 다른 앱에 설정된 단축키를 덮어쓰게 됨.\n\n     - 응용: 운영체제에 global shortcut을 등록하고 제거하는 작업을 앱에 초점이 들어오고 나갈 때마다 반복\n\n     ```\n       app.on('focus', registerAllGlobalShortcuts())\n       app.on('blur', globalShortcut.unregisterAll())\n     ```\n\n  2. menu : 해당 앱에 초점이 맞춰진 상황에서만 실행 가능한 단축키 등록 가능. (권장사항)\n\n     - 초점이 맞추어진 browserWindow를 변수로 받아 작업 가능.\n     - 맥에서는 앱 외부의 상단의 메뉴를 클릭하여 단축키 목록 조회 및 실행 가능.\n     - 윈도우와 리눅스에서는 목록을 숨길 수 있음.\n\n  3. 로드되는 파일 자체에 renderer로 이벤트리스너 추가: 가장 구체적인 방법.\n\n  4. webcontents에 사용자 입력에 대한 이벤트리스너 등록\n\n     - 주의. 창에 등록되는 것이 아니라 특정 webcontents에 focus가 간 상태에서만 동작. browserWindow에 초점이 간 경우, browserView에 등록된 단축키는 동작하지 않음.\n\n     - 주의: fn 없이 F1~F12 등의 function key를 실행할 수 있도록 운영체제가 설정된 경우 function key 입력 인식 불가능. (Mac)\n\n     ```\n     // 모든 webcontents에 동일한 단축키 등록하는 예제\n     app.on('web-contents-created', function (event, wc) {\n       wc.on('before-input-event', function (event, input) {\n         if (input.key === 'x' \u0026\u0026 input.ctrl \u0026\u0026 !input.alt \u0026\u0026 !input.meta \u0026\u0026 !input.shift) {\n           // Do something for Ctrl-X\n           event.preventDefault()\n         }\n       })\n     })\n     ```\n\n## 디버그\n\n- [x] windowId 필요 : 2번째 창을 여는 경우 ipcRenderer 중복 등록 문제 발생\n\n- [x] 탭을 무한 생성하고 focus tab을 제거하는 경우, 다음 focus tab id로 자기 자신을 선택하는 버그 - 구조 뜯어고칠 때 수정 누락됨\n\n- [ ] 네트워크 불안정에 대한 예외처리 화면 추가\n\n- webContents.openDevTools()를 통해 개발자도구를 여는 경우\n  SyntaxError: Unexpected end of JSON input 경고문 출력\n\n- [x] favicon 로드 실패에 대한 예외처리 필요\n\n  - [x] 디폴트 탭 favicon을 대신 보여주기\n  - [x] 이에 따라 복수의 favicon이 생성될 수 있는 상황 디버깅: 1개의 favicon만 존재하도록 cleanse\n  - [x] favicon 로딩을 실패한 프로세스에서 omnibox에 url을 입력하는 경우, 앱 전체가 다운되는 문제\n\n- [ ] 모든 기능에 대한 기기 호환성 체크 필요\n\n- [x] 더블 클릭 방지 : loading 여부에 따라 잠시 동안 조작 불가능하도록\n\n- [x] 메인 프로세스에서는 당연히 focusTabIdx 대신 focusTabId를 관리\n\n- [x] 이미 focus된 탭 클릭하는 경우 토글하는 것으로 간주하지 말기. 무의미한 재렌더링으로 인한 성능 저하.\n\n- [x] cover up ugly border left of focus tab\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbugoverdose%2Fmini-chrome","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbugoverdose%2Fmini-chrome","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbugoverdose%2Fmini-chrome/lists"}