너 쌩배지(리그오브레전드 닷지 경보기) 작성일지 - 3

Gomi·2022년 2월 28일
0
post-thumbnail

📚 Electron IPC


갑자기 생각난건데 드디어 &nbsp를 외웠다. 나는 문단 처음이 띄워지지 않으면 굉장히 화가나던데 아무도 그런데서 불편함은 없나보다. 자동으로 첫 스페이스에 넣어줄 순 없는걸까.....


 아무튼 이전에 나는 다른 프로세스들과 상호작용해야 하는 기능들()을 메인프로세스 코드(index.ts)에서 작성했다. 이전에 설정한 LCU코드에의해 픽창에서는 이벤트가 발생할 때 마다 정보가 찍힌다.

setTimeout(async() => {
    const lolWebSocket = await connect(credentials)
    mainWindow.webContents.send("player-status", exampleUserSet)
    lolWebSocket.subscribe(LCU_ENDPOINT_CHAMP_SELECT,(data, e)=>{
      console.log(data)
      mainWindow.webContents.send("player-status", data)
    })
  }, 4000);


data 일부

{
  hasSimultaneousBans: true,
  hasSimultaneousPicks: false,
  isCustomGame: false,
  isSpectating: false,
  localPlayerCellId: 4,
  lockedEventIndex: -1,
  myTeam: [
    {
      assignedPosition: 'jungle',
      cellId: 0,
      championId: 64,
      championPickIntent: 0,
      entitledFeatureType: 'NONE',
      selectedSkinId: 64004,
      spell1Id: 11,
      spell2Id: 4,
      team: 1,
      wardSkinId: -1
    },
    {
      assignedPosition: 'utility',
      cellId: 1,
      championId: 0,
      championPickIntent: 20,
      entitledFeatureType: 'NONE',
      selectedSkinId: 0,
      spell1Id: 14,
      spell2Id: 4,
      team: 1,
      wardSkinId: -1
    },
    {
      assignedPosition: 'top',
      cellId: 2,
      championId: 0,
      championPickIntent: 0,
      entitledFeatureType: 'NONE',
      selectedSkinId: 0,
      spell1Id: 14,
      spell2Id: 4
      team: 1,
      wardSkinId: -1
    },
    {
      assignedPosition: 'bottom',
      cellId: 3,
      championId: 0,
      championPickIntent: 21,
      entitledFeatureType: 'NONE',
      selectedSkinId: 0,
      spell1Id: 7,
      spell2Id: 4,
      team: 1,
      wardSkinId: -1
    },
    {
      assignedPosition: 'middle',
      cellId: 4,
      championId: 0,
      championPickIntent: 0,
      entitledFeatureType: 'NONE',
      selectedSkinId: 0,
      spell1Id: 12,
      spell2Id: 4,
      team: 1,
      wardSkinId: 1
    }
  ],
  recoveryCounter: 0,
  rerollsRemaining: 0,
  skipChampionSelect: false,
  theirTeam: [
    {
      assignedPosition: '',
      cellId: 5,
      championId: 67,
      championPickIntent: 0,
      entitledFeatureType: '',
      selectedSkinId: 67000,
      spell1Id: 0,
      spell2Id: 0,
      summonerId: 0,
      team: 2,
      wardSkinId: -1
    },
    {
      assignedPosition: '',
      cellId: 6,
      championId: 0,
      championPickIntent: 0,
      entitledFeatureType: '',
      selectedSkinId: 0,
      spell1Id: 0,
      spell2Id: 0,
      summonerId: 0,
      team: 2,
      wardSkinId: -1
    },
    {
      assignedPosition: '',
      cellId: 7,
      championId: 0,
      championPickIntent: 0,
      entitledFeatureType: '',
      selectedSkinId: 0,
      spell1Id: 0,
      spell2Id: 0,
      summonerId: 0,
      team: 2,
      wardSkinId: -1
    },
    {
      assignedPosition: '',
      cellId: 8,
      championId: 0,
      championPickIntent: 0,
      entitledFeatureType: '',
      selectedSkinId: 0,
      spell1Id: 0,
      spell2Id: 0,
      summonerId: 0,
      team: 2,
      wardSkinId: -1
    },
    {
      assignedPosition: '',
      cellId: 9,
      championId: 0,
      championPickIntent: 0,
      entitledFeatureType: '',
      selectedSkinId: 0,
      spell1Id: 0,
      spell2Id: 0,
      summonerId: 0,
      team: 2,
      wardSkinId: -1
    }
  ],
  timer: {
    adjustedTimeLeftInPhase: 4063,
    internalNowInEpochMs: 1645922687015,
    isInfinite: false,
    phase: 'BAN_PICK',
    totalTimeInPhase: 30000
  },
  trades: []
}

 위에서는 지웠지만 우리 팀 정보에도 summonerId가 다 존재한다. 이제 이 정보들을 main프로세스에서 renderer프로세스(React component)로 옮겨서 riot API에 의해 전적이 불려오도록 가공해야한다. 따라서 main프로세스와 renderer프로세스의 통신(Inter Process Communication) IPC가 필요하다.

 리액트는 렌더러 프로세스 위에서 동작하지만, ipcRenderer모듈은 리액트 컴포넌트위에서 바로 동작하지 않아서 여러가지 방법을 동원해 볼 수 있다. 이미 overlay때문에 nodeIntegration을 true로 설정해놨기에, easy way를 사용해봤다.... 라고 하려했으나, 일렉트론 공식문서에 nodeIntegration을 절대 허용하지마라고 강력하게 권고를 하는 관계로.... 다른방법들을 찾아봐야겠다....

Electron IPC Right Way


 일렉트론에서 가장 권고되는 방법은 preload와 contextBridge를 이용해 IPC로직을 격리시키는것....이라고 하는데 보안상의 이유(XSS 방어)라고 한다. overlay window는 demo코드에 nodeIntegration이 있어서 그대로 했는데 없어도 잘 도착했다. electron-forge상에서 빌딩한 프로젝트는 간편하게 package.json에서 preload를 등록해 사용할 수 있다.

package.json

"plugins": [
        [
          "@electron-forge/plugin-webpack",
          {
            "mainConfig": "./webpack.main.config.js",
            "renderer": {
              "config": "./webpack.renderer.config.js",
              "entryPoints": [
                {
                  "html": "./src/index.html",
                  "js": "./src/renderer.ts",
                  "name": "main_window",
                  "preload":{
                    "js":"./src/preload.ts"
                  }
                }
              ]
            }
          }
        ]
      ]

또한 preload에 다음과 같이 이벤트들을 등록해놓으면 이제 리액트 컴포넌트에서도 메인프로세스에서 넘어온 데이터들을 사용할 수 있다.

preload.ts

import {ipcRenderer, contextBridge} from 'electron';

declare global {
    interface Window {
     api:{
        send: (channel: string, data:JSON) => void;
        on: (channel: string, func: (event: any, ...arg: any) => void) => void;
      }
  }
}
process.once("loaded", () => {

    contextBridge.exposeInMainWorld(
        "api", {
           
            send: (channel:string, data:JSON) => {
                ipcRenderer.send(channel, data);
            },
            on: (channel:string, func:(event: any, ...arg: any) => void) => {
                ipcRenderer.on(channel, (event, ...args) => func(event, ...args));
            }
        }
    );
});
profile
터키어 배운 롤 덕후

0개의 댓글