Skip to content

Latest commit

 

History

History
316 lines (193 loc) · 12.9 KB

README.md

File metadata and controls

316 lines (193 loc) · 12.9 KB

Mini Chrome

Chromium에 사용자 인터페이스(UI)를 추가한 구글 크롬 클론 코딩 프로젝트

데모

데모

상태 관리 (State Management)

  • 메인 프로세스에서 싱글톤 패턴으로 전역 상태 관리

    • 앱 실행 시점에 초기화되는 데이터
  • windows : 모든 Window 인스턴스. 각 윈도우에는 현재 실행 중인 BrowserWindow 인스턴스와 tabs 정보 등이 함께 관리됨.

    • id : browserWindow.id를 uid로 사용. 프로세스 간 통신을 위해 일치시키는 것이 용이함.
    • tabs : 특정 윈도우의 모든 Tab 인스턴스. 하나의 Tab 인스턴스에는 하나의 BrowserView 인스턴스와 tabId 정보가 함께 관리됨.
  • tabId : 하나의 탭이 생성될 때마다 1씩 증가. 각 탭의 uid로 배정되는 숫자 데이터.

데이터베이스

  • 로컬 머신에 JSON 파일로 저장하고 읽는 데이터 (custom-database.json)

  • 즐겨찾기 데이터는 favorites key에 배열로 저장

    • 개별 즐겨찾기 데이터 구조: ({ title, url, favicon })

IPC(프로세스 간 통신) 로직 핵심 요약

  • 특정 윈도우의 헤더 프로세스에서 메인 프로세스에 해당 윈도우 제거, 최소화, 최대화 토글 요청

  • 특정 윈도우의 헤더 프로세스에서 메인 프로세스에 tabs 정보를 요청하고, 데이터를 응답받아 화면의 html 태그 추가 및 내용 수정

  • 특정 윈도우의 뷰 프로세스에서 메인 프로세스에 개발자 도구 열기, 탭 제거 등의 요청하고, 메인 프로세스는 헤더 프로세스 혹은 해당 뷰 프로세스로 응답

구현할 기능 목록 (우테코 스타일)

윈도우

  • 앱 실행시, 맥에서 아이콘 클릭시 새 윈도우 생성

  • 창이 생성되면 생성된 창에 focus되도록 명시

    • 앱 실행시, 생성된 창에 focus시키고, 새 탭인 경우 omnibox에 초점이 오도록 설정
  • 창에 focus가 들어온 경우 창(browser window)으로 간 focus를 헤더(browser view)로 그대로 이동시키기 (개발자 입장에서는 포함 관계지만, 실질적으로 서로 별개의 프로세스)

    • 새 탭인 경우 화면의 어디를 클릭했던지와 무관하게, 무조건 omnibox로 초점이 오도록 설정

    • 새 탭이 아니라면 마지막으로 있던 위치에 초점 가도록 내버려두기

    • focus 탭 정보 업데이트 : 다른 창에서의 작업으로 즐겨찾기 정보가 변했을 수 있기 때문에 필요.

  • blur된(focus를 잃은) 윈도우는 deactivate시키기 (창이 1개일 때도 포함)

    • 헤더의 상단 부분의 배경색 회색으로

    • 모든 헤더의 버튼들 회색으로(traffic lights & view utils)

    • focus 다시 얻으면 원상복구

  • 페이지 뷰 내에서 새 창을 여는 경우 (링크를 shift + 클릭, target="_blank" 속성의 anchor 클릭 등), 디폴트 윈도우가 아닌 커스텀 윈도우를 생성하고 렌더링하도록

    • 디버깅: 순환참조 문제 때문에 app에 등록. 페이지 뷰만이 아니라 모든 web contents에 설정됨. (리소스 낭비)

헤더

헤더 배경

  • 마우스로 텍스트 부분만 따로 선택할 수 없도록 설정

  • 헤더 내 스크롤 생성 방지

  • 더블클릭시 toggle maximize

    • event delegation으로 다른 버튼들 더블클릭된 경우에는 동작하지 않도록 명시

신호등 컨트롤러

  • 윈도우든 맥이든 맥의 traffic lights 기능 그대로 구현

    • 화면 닫기
    • 화면 최소화
    • 전체화면 모드 토글
  • 특정 버튼을 누르고 있으면 해당 버튼만 살짝 어둡게 색상 변경

  • 특정 버튼 누른 상태로 마우스 이동시, 드래그되지 않도록 설정

  • 특정 버튼에 마우스 hover시 클릭했을 때 발생할 이벤트에 대한 설명 표시

  • focus되지 않은 윈도우에서는 세 버튼 모두 회색으로 설정

  • hover했을 때 관련 아이콘 보여주기 (보류)

  • title 속성으로 클릭하면 발생할 이벤트 설명 보여주기

  • 현재 윈도우에 존재하는 모든 view들에 대한 간략한 정보 표시

    • favicon
    • 디폴트 favicon
    • title 태그
  • 특정 탭에 마우스 hover시 탭에 대한 정보 출력

    • title 태그
    • url 정보
  • 새 탭 생성 기능 구현

    • 새 탭 생성 시점에 뷰 프로세스 갈아끼우기
    • 새 탭 생성 시점에 input에 키보드 자동 focus
    • 새 탭(=현재 화면에 보여지는 탭)은 다르게 보이도록 (focus)
    • 새 탭 생성시, 메인 프로세스로부터 응답받을 때까지 로딩 상태. 탭 영역 클릭 방지.
  • 기존 탭 삭제 기능 구현

    • 현재 focus된 탭을 닫는 경우 옆의 탭으로 focus 이동 + 페이지도 이동
  • 탭 클릭을 통해 화면에 보여주는 탭 변경

    • 현재 화면에 대응되는 탭은 다르게 보이도록 (focus)
  • 드래그 앤 드롭 기능 구현

    • 탭들 사이에서 좌우 이동시, 탭들의 순서 변경
    • 탭들의 영역을 벗어나는 경우 새로운 윈도우 생성
  • 반응형 디자인 구현

    • 탭의 최소 width는 창 닫는 버튼 크기
    • focus된 탭은 창 닫는 x 버튼을 위로
    • z-index: focus되지 않은 탭은 favicon + title을 위로
    • 탭의 개수가 너무 많으면 새 탭 생성 버튼이 위로 오도록

omnibox

  • input에 focus된 상태에서는 border를 파란색으로

  • input에 url 입력하여 해당 탭에 페이지 로드

  • url 대신 키워드 입력하는 경우 구글 검색으로 이동

  • 로드된 페이지의 title로 해당 탭 업데이트

  • 엔터 누르면 곧바로 omnibox에서 unfocus되어 동일한 url 반복 입력 방지 - blur()

  • 현재 view에 표시되는 주소가 변한 경우 omnibox에 해당 url 출력

    • url 입력에 따른 변화
    • 페이지 내 이동에 따른 변화
  • omnibox에 자체적으로 입력만 하고 전송하지 않은 데이터도 그대로 저장 (보류)

  • https:// 부분은 보이지 않도록 설정

view utils

  • 뒤로 버튼 구현

    • 뒤로 못 가는 경우 버튼 클릭 방지 및 어둡게
  • 앞으로 버튼 구현

    • 앞으로 못 가는 경우 버튼 클릭 방지 및 어둡게
  • 뒤로/앞으로 가는 경우 탭의 정보와 뒤로/앞으로 갈 수 있는지의 여부 업데이트

  • 새로고침/중지 버튼 구현

    • 로딩 중일 때는 중지 버튼으로 임시 변화
    • 로딩 종료 후 새로고침 버튼으로 다시 변화
  • title 속성으로 클릭하면 발생할 이벤트 설명 보여주기

favorites

  • 해당 데스크탑 내부에 자체적으로 즐겨찾기 목록 저장

    • 폴더 형식으로 즐겨찾기 목록 구조화
  • omnibox 내에 별 모양 아이콘을 통해 즐겨찾기 여부 확인 가능

  • 각 url에 대해 별 모양 토글하여 즐겨찾기 추가 및 제거

    • title 속성으로 클릭하면 발생할 이벤트 설명 보여주기

페이지

새 탭 화면

  • 새 창 생성 시점에 생성되는 탭에 url이 없는 경우에 보이는 디폴트 화면

  • 새 탭 생성 시점에 url이 없는 경우에만 보이는 디폴트 화면

  • 새 탭의 화면 내부에서 즐겨찾기 목록 보이도록

    • 즐겨찾기 favicon을 로드 실패하는 경우, 새 탭, 연결 실패한 경우의 아이콘인 경우 무조건 디폴트 아이콘을 보여주도록

    • 각 즐겨찾기 항목 클릭시, 현재 탭은 해당 url로 이동

  • title 속성으로 클릭하면 발생할 이벤트 설명 보여주기

  • 새 탭의 화면 내부에서 즐겨찾기 목록 수정 기능

    • 즐겨찾기 title 수정
    • 즐겨찾기 제거
    • 드래그 앤 드롭으로 즐겨찾기 목록 순서 수정

연결 실패 화면

  • 입력된 url로 페이지 로드 실패시, 실패 화면으로 이동

    • 실패화면에 omnibox에 입력한 url 정보와 에러 코드 출력
      • URI 디코딩을 통해 한국어 검색어 등 대응
    • 실패화면의 title에는 omnibox에 입력했던 검색어 그대로 출력
      • URI 디코딩을 통해 한국어 검색어 등 대응
    • favicon은 별도로 생성

뷰 페이지 context menu

  • 화면 내에서 우클릭시 기본 도구

    • 새 탭 열기
    • 새 창 열기
    • 개발자 도구 열기 기능
    • 새로고침
    • 뒤로
    • 앞으로
  • a 태그 우클릭시 링크 관련 도구

    • 새 탭에서 열기 기능
    • 새 창에서 열기 기능
    • 링크 주소 복사 기능

단축키

  • 최종적으로 Menu으로 마이그레이션

  • 개발자 도구 토글 : 현재 focus된 창에서 열린 탭의 View에 개발자 도구를 열고 닫기

    • 맥 : COMMAND+OPTION+I

    • 윈도우 : Ctrl+Shift+I

    • 공통 : F12

    • header : 개발자 도구 열릴 수 없도록 방지

    • 새 탭과 연결 실패 화면도 그냥 개발자 도구 열 수 있도록 설정. 굳이 막을 이유도 없고, 뒤로/앞으로 가는 경우 열려있던 개발자도구를 닫는 등 별도의 작업이 필요함.

  • 탭 닫기 : focus된 창의 focus된 탭을 닫고 다른 탭으로 focus를 이동시키는 단축키

    • 맥 : COMMAND+W
    • 윈도우 : CTRL+W
    • 마지막 탭이면 현재 창 닫고 다른 창으로 이동하도록 기존 api 그대로 사용
  • 새로고침, 뒤로가기, 앞으로가기, 로드 중지 등

  • 일단 event.preventDefault 설정하지 말고 디폴트 설정 그대로 사용

    • TAB으로 focus된 화면 내의 조작 가능한 영역들(anchor, input, etc) 순회

    • 선택된 a 태그를 엔터 입력으로 클릭

    • 텍스트 선택 후 잘라내기/복사/붙여넣기

  • 단축키: 구현 방법들과 각자의 특성

    1. global shortcut : 운영체제에 단축키 등록.

      • 문제점: 해당 앱이 선택되지 않은 상황에서도 실행됨. 다른 앱에 설정된 단축키를 덮어쓰게 됨.

      • 응용: 운영체제에 global shortcut을 등록하고 제거하는 작업을 앱에 초점이 들어오고 나갈 때마다 반복

        app.on('focus', registerAllGlobalShortcuts())
        app.on('blur', globalShortcut.unregisterAll())
      
    2. menu : 해당 앱에 초점이 맞춰진 상황에서만 실행 가능한 단축키 등록 가능. (권장사항)

      • 초점이 맞추어진 browserWindow를 변수로 받아 작업 가능.
      • 맥에서는 앱 외부의 상단의 메뉴를 클릭하여 단축키 목록 조회 및 실행 가능.
      • 윈도우와 리눅스에서는 목록을 숨길 수 있음.
    3. 로드되는 파일 자체에 renderer로 이벤트리스너 추가: 가장 구체적인 방법.

    4. webcontents에 사용자 입력에 대한 이벤트리스너 등록

      • 주의. 창에 등록되는 것이 아니라 특정 webcontents에 focus가 간 상태에서만 동작. browserWindow에 초점이 간 경우, browserView에 등록된 단축키는 동작하지 않음.

      • 주의: fn 없이 F1~F12 등의 function key를 실행할 수 있도록 운영체제가 설정된 경우 function key 입력 인식 불가능. (Mac)

      // 모든 webcontents에 동일한 단축키 등록하는 예제
      app.on('web-contents-created', function (event, wc) {
        wc.on('before-input-event', function (event, input) {
          if (input.key === 'x' && input.ctrl && !input.alt && !input.meta && !input.shift) {
            // Do something for Ctrl-X
            event.preventDefault()
          }
        })
      })
      

디버그

  • windowId 필요 : 2번째 창을 여는 경우 ipcRenderer 중복 등록 문제 발생

  • 탭을 무한 생성하고 focus tab을 제거하는 경우, 다음 focus tab id로 자기 자신을 선택하는 버그 - 구조 뜯어고칠 때 수정 누락됨

  • 네트워크 불안정에 대한 예외처리 화면 추가

  • webContents.openDevTools()를 통해 개발자도구를 여는 경우 SyntaxError: Unexpected end of JSON input 경고문 출력

  • favicon 로드 실패에 대한 예외처리 필요

    • 디폴트 탭 favicon을 대신 보여주기
    • 이에 따라 복수의 favicon이 생성될 수 있는 상황 디버깅: 1개의 favicon만 존재하도록 cleanse
    • favicon 로딩을 실패한 프로세스에서 omnibox에 url을 입력하는 경우, 앱 전체가 다운되는 문제
  • 모든 기능에 대한 기기 호환성 체크 필요

  • 더블 클릭 방지 : loading 여부에 따라 잠시 동안 조작 불가능하도록

  • 메인 프로세스에서는 당연히 focusTabIdx 대신 focusTabId를 관리

  • 이미 focus된 탭 클릭하는 경우 토글하는 것으로 간주하지 말기. 무의미한 재렌더링으로 인한 성능 저하.

  • cover up ugly border left of focus tab