💡 아웃바운드를 위하여 후보자를 외부 사이트(ex. 링크드인)를 통해 소싱하려고 할 때, 위하이어 사이트와 소싱을 위한 외부 사이트를 번갈아가며 작업하는 것이 번거롭기에 이를 해결하기 위해 확장 프로그램을 개발하였습니다.
iframe을 통해, 확장 프로그램을 웹뷰로써 동작하도록 합니다.
확장 프로그램의 재배포 과정을 거치지 않고, 웹 서비스의 업데이트를 통해 유저에게 최신 버전의 서비스를 제공할 수 있습니다.
function createIframe(
id: string,
url: string,
styles: Record<string, string>
): HTMLIframeElement {
const iframe = document.createElement('iframe');
iframe.id = id;
iframe.setAttribute('src', url);
Object.assign(iframe.style, styles);
return iframe;
}
function insertIframe() {
const wehireIframe = createIframe(
IFRAME_ID,
WEHIRE_URL,
IFRAME_STYLES
);
wehireIframe.className = 'wehire-iframe';
document.body.prepend(wehireIframe);
// ...
}
window의 message 이벤트를 통해 상호작용합니다.
웹뷰의 picker 버튼을 클릭했을 때, 클라이언트 브라우저에 마우스 이벤트를 등록합니다.
// 웹뷰
<button onClick={() => {
window.parent.window.postMessage({
type: 'activatePicker',
pickerType: 'name',
}, "*");
}}>
Name Picker
</button>
// 확장 프로그램
let pickerType = null;
const onMessage = (
event: MessageEvent<IframeEventMessage>
) => {
const eventType = event.data.type;
switch (eventType) {
case 'activatePicker': {
pickerType = event.data.pickerType;
addMouseEvent();
}
case 'autoScraping': {
// ...
}
}
};
window.addEventListener('message', onMessage);
유지보수를 위해 스택을 자바스크립트에서 타입스크립트로 전환하고, 빌드를 위해 웹팩을 사용했습니다. (웹팩 설정)
const path = require('path');
const { CleanWebpackPlugin } =
require('clean-webpack-plugin');
const TerserPlugin =
require('terser-webpack-plugin');
module.exports = {
entry: {
background: './src/background.ts',
script: './src/script.ts',
},
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/,
},
],
},
resolve: {
extensions: ['.tsx', '.ts', '.js'],
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'dist'),
publicPath: '/',
},
plugins: [
// 빌드할 때마다 dist 폴더를 정리
new CleanWebpackPlugin(),
],
// 디버깅을 위하여, 소스맵 추가
devtool: 'source-map',
optimization: {
minimize: true,
minimizer: [
// 코드 압축을 위해 TerserPlugin 추가
new TerserPlugin({
terserOptions: {
compress: {
drop_console: true,
},
},
}),
],
},
};
targetWindow.postMessage(message, targetOrigin, [transfer]);
메시지 출처 검증 및 targetOrigin
명시
메시지를 수신할 때, 반드시 메시지의 출처(origin)를 검증해야 합니다. 이를 통해 신뢰할 수 없는 도메인에서 전송된 메시지를 처리하지 않도록 할 수 있습니다.
메시지를 전송할 때는 postMessage
의 두 번째 인자로 targetOrigin
을 명시하여, 메시지가 전송될 대상의 출처를 지정합니다.
targetOrigin
의 정보와 targetWindow
의 정보가 맞지 않다면 이벤트는 전송되지 않습니다.
// 확장 프로그램
const TARGET_ORIGIN = 'https://wehire.kr';
// 메시지 수신 (메시지 출처 검증)
window.addEventListener('message', (event) => {
// 보안 검증: 메시지의 출처(origin) 확인하기
if (event.origin !== TARGET_ORIGIN) {
// 신뢰할 수 없는 출처에서 온 메시지를 무시합니다.
return;
}
});
// 메시지 발송 (targetOrigin 명시)
function sendMessageToIframe(message) {
iframe.contentWindow.postMessage(message, TARGET_ORIGIN);
}
// 웹뷰
const TARGET_ORIGIN = 'chrome-extension://<EXTENSION_ID>'
// 메시지 수신
window.addEventListener('message', (event) => {
if (event.origin !== TARGET_ORIGIN) {
return;
}
});
// 메시지 발송
function sendMessageToExtension(message) {
window.parent.window.postMessage(message, TARGET_ORIGIN);
};