Main Process API - App과 BrowserWindow

기운찬곰·2020년 9월 23일
2

Electron

목록 보기
3/5
post-thumbnail

시작하기에 앞서서...

이 시리즈는 유데미의 Master Electron: Desktop Apps with HTML, JavaScript & CSS 강의를 듣고 정리한 글입니다.


🚀 App

Electron 문서를 보면 app이라는 부분이 존재합니다. app은 상당히 중요한 개념인데 바로 application의 이벤트 생명주기를 제어하는 역할을 합니다.

Events

app 객체의 이벤트 종류는 상당히 양이 많습니다. 하나하나 전부 설명하기는 어렵고 몇가지 예를 보면서 간단히 넘어가도록 하겠습니다.

window-all-closed

모든 윈도우가 종료되었을 때 발생하는 이벤트입니다.

const { app, BrowserWindow } = require("electron");

app.on('window-all-closed', () => {
  app.quit()
})

이 이벤트는 말 그대로 현재 애플리케이션에서 윈도우만 완전히 종료됬을 때 발생하는 이벤트입니다. 따라서 애플리케이션을 완전히 종료하려면 이 이벤트에서 app.quit()를 호출해 주어야 합니다.


before-quit

다음은 윈도우 창을 닫을 때 발생하는 이벤트를 처리합니다.

app.on("before-quit", () => {
  console.log("App is quitting");
});

event.preventDefault() 호출은 이벤트의 기본 동작을 방지하기 때문에 이를 통해 애플리케이션의 종료를 방지할 수 있습니다.

app.on("before-quit", (e) => {
  console.log("Preventing app from quitting");
  e.preventDefault();
});

File > Exit(혹은 cmd + Q) 를 해보시면 창이 닫히지 않는것을 볼 수 있습니다. 다만 윈도우 창을 X를 눌러서 닫으면 닫혀집니다. (이게 무슨소리야? 닫혀지기는 하는데 계속실행은 된다 이소리인가?)


browser-window-blur

browserWindow에 대한 포커스가 사라졌을 때 발생하는 이벤트입니다.

app.on("browser-window-blur", (e) => {
  console.log("App unfocused");
});

browser-window-focus

browserWindow에 대한 포커스가 발생했을 때 발생하는 이벤트입니다. 포커스 는 창을 클릭해서 활성화 시켰을 때를 말합니다.

app.on("browser-window-focus", (e) => {
  console.log("App focused");
});


Methods

app 객체는 다음과 같은 메서드를 가지고 있습니다:

app.quit()

모든 윈도우 종료를 시도합니다. before-quit 이벤트가 먼저 발생합니다. 모든 윈도우가 성공적으로 종료되면 will-quit 이벤트가 발생하고 기본 동작에 따라 애플리케이션이 종료됩니다.

다음은 윈도우가 포커스를 잃었을때 3초 후에 종료되도록 하는 코드입니다.

app.on("browser-window-blur", (e) => {
  setTimeout(() => {
    app.quit();
  }, 3000);
});

app.exit(exitCode)

exitCode와 함께 애플리케이션을 즉시 종료합니다. app.quit()와는 다르게 모든 윈도우는 사용자의 동의 여부에 상관없이 즉시 종료되며 before-quit 이벤트와 will-quit 이벤트가 발생하지 않습니다.


app.getPath(name: String)

이 메서드는 운영체제에서 지정한 특수 디렉터리를 가져오는데 사용할 수 있습니다.

  • home : 사용자의 홈 디렉터리
  • desktop : 사용자의 데스크탑 디렉터리
  • music : 사용자의 음악 디렉터리
  • temp : 임시 폴더 디렉터리
  • userData : 애플리케이션 설정을 지정하는 디렉터리
  • 기타 등등...
app.on("ready", () => {
  console.log(app.getPath("desktop"));
  console.log(app.getPath("music"));
  console.log(app.getPath("temp"));
  console.log(app.getPath("userData"));

  createWindow();
});


🪁 BroserWindow

브라우저 윈도우를 생성하고 제어합니다.

loadFile vs loadUrl

loadFile 대신 loadUrl을 사용해서 웹사이트를 불러올 수도 있습니다.

win.loadUrl("http://google.com");

url은 원격 주소 (e.g. http://)가 되거나 file:// 프로토콜을 사용하는 로컬 HTML 경로가 될 수 있습니다.


윈도우 창을 멋있게 보여주기

하지만 문제가 있습니다. 사이트가 나타나는데 기다리는동안 화면에 아무것도 나오지 않는 상태가 꽤 오래 지속되어집니다. 이건 별로 좋지 않은 사용자 경험일 것입니다.

참고 : https://www.electronjs.org/docs/api/browser-window#showing-window-gracefully

위 사이트를 들어가보면 이것에 대한 해결책으로 2가지 방법을 제시하고 있습니다.

ready-to-show 이벤트 사용

const { BrowserWindow } = require('electron')
function createWindow() {
  // 브라우저 창을 생성합니다.
  const win = new BrowserWindow({
    (...생략...)
    show: false,
  });

  (...생략...)

  win.once("ready-to-show", () => {
    win.show();
  });
}

핵심은 show: false를 사용하고, ready-to-show 이벤트를 통해서 renderer process가 해당 페이지 렌더링을 완료했을때 show()를 통해 보여주도록 하는 것입니다.

그로인해 애플리케이션 실행시 바로 켜지지는 않게 됩니다. 대신 화면 딜레이는 없어졌습니다.

backgroundColor속성

function createWindow() {
  // 브라우저 창을 생성합니다.
  const win = new BrowserWindow({
    (...생략...)
    backgroundColor: "#2e2c29",
  });

복잡한 app에서는 ready-to-show 이벤트가 너무 늦게 발생해서, 앱이 느린 것처럼 보일 수 있다. 이런 경우, 윈도우를 즉시 보여주고 backgroundColor를 사용하는 방식이 권장된다. (그런가?)


Parent & Child Windows

부모와 자식 창을 어떻게 만들수 있을까요? 그러려면 먼저 두번째 브라우저 창이 필요합니다. 우리는 BrowserWindow 모듈을 사용해서 멀티 렌더럴 프로세스를 생성할 수 있습니다.

function createWindow() {
  // 브라우저 창을 생성합니다.
  const win1 = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      nodeIntegration: true,
    },
  });

  const win2 = new BrowserWindow({
    width: 600,
    height: 300,
    webPreferences: {
      nodeIntegration: true,
    },
  });

  // and load the index.html of the app.
  win1.loadFile("index.html");
  win2.loadFile("index.html");
}

창 두개를 띄우기는 했는데 이건 완전히 독립된 창인 경우입니다. 따라서 부모창을 끄더라도 자식창은 존재합니다. 만약 부모창을 종료하면 자식도 완전히 종료되도록 하고 싶다면...

(...생략...)

  const win2 = new BrowserWindow({
    width: 600,
    height: 300,
    webPreferences: {
      nodeIntegration: true,
    },
    parent: win1,
    modal: true,
  });

(...생략...)
  • win2의 parent를 win1이라고 지정해주면 부모 자식관계가 형성됩니다.
  • moal을 true로 해주면 자식창이 꺼지기 전까지는 부모창을 건들지 못합니다.

Frameless Window

참고 : https://www.electronjs.org/docs/api/frameless-window

기본적으로 애플리케이션 창에는 기본 인터페이스 요소가 정의되어있습니다. 하지만, 경우에따라서는 이런 기본 인터페이스 요소가 없는게 더 나을 수도 있습니다.

frame을 false로 해볼까요? 그럼 아래 결과와 같이 기본 요소가 다 없어집니다.

const win1 = new BrowserWindow({
  (...생략...)
  frame: false,
});

기본요소가 전부 사라졌지만 창 이동이라던가 창을 끄거나 할 수가 없게되었습니다. 왜냐면 창의 헤더 부분이 없어졌기 때문...

창이동/드래그를 시킬 수 있는 방법은 앱 내에서 드래그 및 선택을 허용하는 겁니다. 창 전체를 드래그 가능하게 만드려면 -webkit-app-region: drag을 body의 스타일에 지정하면 됩니다:

<body style="user-select: none; -webkit-app-region: drag">
</body>

참고로 창 전체를 드래그 영역으로 지정할 경우 사용자가 버튼을 클릭할 수 없게 되므로 버튼은 드래그 불가능 영역으로 지정해야 합니다:

button {
  -webkit-app-region: no-drag;
}

Properties, Methods, Events

보면 알겠지만, 옵션이 많습니다. 그 중에서 몇가지를 써보자.

Properties

minWidth, minHeight 속성 - 적어도 최소한 이정도 크기의 너비와 높이를 가져야 한다.

win = new BrowserWindow({
    width: 1200, height: 800,
    minWidth: 500, minHeight: 500,
    webPreferences: { nodeIntegration: true }
})

반대로 maxWidth, maxHeight 속성은 최대한의 크기의 너비와 높이는 이정도라는것을 제한하는것.

Methods

BrowserWindow.getAllWindows()를 사용해보자

console.log(BrowserWindow.getAllWindows())

Events

예를들어, focus 이벤트를 사용해보자. app에서도 이런 brower-window-focus라는게 존재했다. 차이점은 app은 전체적인 개념이고, BrowserWindow의 focus이벤트는 개별적인 창에대한 이벤트만을 처리한다.

  win1.on('focus', () => {
    console.log('Main win focus')
  })
  win2.on('focus', () => {
    console.log('second win focus')
  })

Window State


Web Contents


마침

References

profile
배움을 좋아합니다. 새로운 것을 좋아합니다.

0개의 댓글