첫 번째 OpenReplay 플러그인 만들기
OpenReplay를 셀프 호스팅한다면 커스텀 플러그인을 개발하고 설치할 수 있다는 추가적인 이점이 있습니다. 이를 통해 OpenReplay가 기본으로 제공하지 않는 기능과 호환성을 추가할 수 있습니다.
현재 OpenReplay 팀이 개발하고 유지 관리하는 플러그인으로는 상태 변경을 추적할 수 있는 Redux 플러그인이나 요청(Request) 및 응답(Response) 데이터를 기록할 수 있는 Fetch 플러그인 등이 있습니다. 더 알고 싶다면 전체 플러그인 목록을 확인해 보세요.
이 튜토리얼에서는 나만의 플러그인을 만드는 방법을 알아봅니다. 구체적으로는 JQuery의 GET 메서드를 추적하는 플러그인을 만들어 보겠습니다.
처음부터 플러그인 만들기
Section titled 처음부터 플러그인 만들기만들려는 기능과 비슷한 일을 하는 플러그인의 폴더를 복사해서 시작하는 것을 권장합니다. 그러면 필요한 로직과 설정 코드를 모두 갖춘 상태로 시작할 수 있습니다.
여기서 만들 플러그인은 Fetch 플러그인과 비슷하게 동작할 것이므로, Fetch 플러그인을 복제하겠습니다.
그래도 다음과 같은 작업은 직접 해야 합니다.
- 백엔드 수정
- 메시지 프로토콜에 새 메시지 추가
- 소스에서 일부 백엔드 서비스 빌드
따라서 소스에서 OpenReplay를 빌드하는 과정을 아직 해 본 적이 없다면, 먼저 현재 버전의 OpenReplay로 그 과정을 진행해 보시길 권합니다. 새 코드를 테스트하면서 여러 번 반복하게 될 작업이기 때문입니다.
반대로 이미 익숙하다면 계속 읽어 보세요!
새 플러그인을 만들려면 무엇이 필요한가요?
Section titled 새 플러그인을 만들려면 무엇이 필요한가요?새 플러그인을 만드는 데에는 세 부분이 있습니다.
- 먼저 플러그인이 플랫폼으로 보낼 메시지의 유형을 정의해야 합니다. 이 메시지에는 추적하려는 모든 텔레메트리가 담겨야 합니다.
- 그런 다음 프런트엔드를 업데이트하여 플러그인의 데이터를 표시할 수 있는 컴포넌트를 만들어야 합니다. 이번 예제에서는 JQuery로 수행한 모든 요청을 나열하는 탭을 리플레이어에 추가합니다.
- 마지막으로 플러그인 자체를 만들어야 합니다. 이것이 바로 우리가 여기 있는 이유입니다. 플러그인은 OpenReplay 외부에서 개발해 다른 곳(여러분의 앱)에 설치할 수 있는 유일한 부분입니다.
이후 튜토리얼은 Github 저장소를 포크하여 어딘가에 준비해 두고 정상적으로 동작하는 상태라고 가정합니다. 아직 준비하지 않았다면 먼저 문서의 배포 섹션을 확인하세요.
1단계: 새 메시지 만들기
Section titled 1단계: 새 메시지 만들기가장 먼저 할 일은 플러그인으로 어떤 데이터를 캡처하고 보여 줄지 파악하는 것입니다. 이번에는 GET 요청을 추적하는 기능을 만들 것이므로, 다음 항목을 캡처해야 합니다.
- URL: 당연히 요청의 URL
- Response: 응답 중 일부도 캡처해 봅니다
- Status: 서버에서 받은 상태 코드
요청을 수행하고 데이터를 가져오는 데 걸리는 시간을 추적하기 위해 “duration” 속성도 추가할 것이며, 이를 위해 요청의 “timestamp”가 필요합니다.
이를 위해 mob/messages.rb 파일을 수정해야 합니다. Ruby 파일이지만 Ruby를 몰라도 걱정하지 마세요. 새 메시지를 설명하는 레코드를 다음과 같이 추가하기만 하면 됩니다.
message 112, 'JQueryGET' do
string 'Method'
string 'url'
string 'response'
string 'status'
string 'duration'
int 'timestamp'
end
다음 항목도 함께 정의했습니다.
- 메시지 ID(112): 아직 사용되지 않은 임의의 숫자로, 200 미만이어야 합니다(파일의 다른 메시지를 참고하세요).
- 메시지 이름: 전적으로 여러분이 정하면 되며, 메시지 유형을 잘 설명하는 이름이면 됩니다.
- “method” 필드: 이렇게 하면 나중에 다른 메서드에서도 이 메시지를 재사용할 수 있습니다.
- 속성의 타입은 “string”과 “int”입니다. 인코더가 배열과 객체를 문자열로 변환하기 때문입니다.
이 작업이 끝나면 어디서든 메시지에 접근할 수 있도록, 이 폴더 안에서 두 개의 스크립트를 실행합니다.
$ ruby run.rb
$ sh format.sh
완료되면 메시지와 해당 ID가 백엔드에서 필요한 모든 곳에 복제되어 추가됩니다.
이제 프런트엔드로 넘어가서 무엇을 바꿔야 하는지 살펴보겠습니다.
2단계: 프런트엔드 업데이트
Section titled 2단계: 프런트엔드 업데이트메시지가 준비되었으니, 모든 메시지를 받아 처리 방식을 결정하는 messageDistributor 함수(frontend/app/player/MessageDistributor/MessageDistributor.ts 파일 안에 있습니다)를 업데이트해야 합니다.
함수 안에는 큰 switch 문이 있고, 그 안에 이미 여러분의 메시지에 해당하는 새 case가 있습니다(사용된 이름의 표기 방식이 여러분이 사용한 것과 다르다는 점을 알 수 있습니다). 이 예제에서는 메시지 유형 “JQueryGET”에 대해 “j_query_get” 유형이 생성됩니다.
이제 메시지를 리스트에 추가해야 합니다. 프런트엔드 컴포넌트가 이 리스트를 사용합니다. listAppend 함수를 사용하면 됩니다.
case는 다음과 같아야 합니다.
case 'j_query_get':
listAppend("jquery", Resource({
method: msg.method,
url: msg.url,
payload: {},
response: msg.response,
status: msg.status,
type: TYPES.JQUERY,
time: msg.timestamp - this.sessionStart,
duration: msg.duration,
index
}))
break;
이것이 동작하도록 하려면 몇 가지를 더 해야 합니다.
- “jquery” 리스트를 추가해야 합니다.
frontend/app/player/lists/index.js파일을 수정하여entityNamesWithRed배열에 “jquery”를 추가합니다. TYPES.JQUERY를 추가해야 합니다.frontend/app/types/session/resource.js파일을 수정하면 됩니다.
이렇게 하면, 플레이어 안에서 데이터를 보여 줄 실제 컴포넌트를 만듭니다. 다음과 같은 모습입니다.

이를 위해 기존 컴포넌트를 활용하여 Fetch 컴포넌트를 복사해 붙여넣겠습니다. frontend/app/components/Session_/Fetch 폴더를 복제하여 이름을 “JQuery”로 지정합니다.
새로 만든 폴더 안의 fetch에 대한 모든 참조를 “jquery”로 바꿉니다(검색 및 바꾸기를 사용하는 것이 좋습니다).
이렇게 하면 시각화 컴포넌트가 만들어지며, 이를 직접 추가해야 합니다. 먼저 frontend/app/components/Session_/Player/Controls/Controls.js 파일을 편집하여 모든 컨트롤이 표시되는 화면 오른쪽 아래에 “JQuery” 버튼을 추가합니다.
JSX 코드를 찾아 다음과 같은 블록을 추가합니다.
{showJQuery && (
<ControlButton
disabled={disabled && !inspectorMode}
onClick={() => toggleBottomTools(JQUERY)}
active={bottomBlock === JQUERY && !inspectorMode}
hasErrors={jqueryRedCount > 0}
count={jqueryCount}
label="JQUERY"
noIcon
labelClassName="!text-base font-semibold"
containerClassName="mx-2"
/>
)}
이때 showJQuery, jqueryCount, JQUERY, jqueryRedCount 등 관련 변수를 모두 정의해야 합니다. 방법을 잘 모르겠다면 fetch에 대응하는 변수를 검색해 보세요. 금방 찾을 수 있습니다.
버튼을 추가했다면 frontend/app/components/Session_/Player/Player.js 파일을 편집하여 실제 컴포넌트를 추가합니다.
컴포넌트를 import한 뒤, JSX 섹션 안에 다음과 같은 블록을 추가합니다.
{ bottomBlock === JQUERY &&
<JQuery />
}
이와 함께 frontend/app/duck/components/player.js 파일에서 JQUERY 상수를 export하도록 추가해야 합니다.
이것으로 모든 작업이 끝납니다.
이제 프런트엔드는 정보를 표시할 준비가 되었으니, 정보를 생성할 방법을 찾아야 합니다. 이제 실제 플러그인 코드를 살펴보겠습니다!
3단계: 플러그인 만들기
Section titled 3단계: 플러그인 만들기이 프로젝트는 원하는 곳 어디에나 만들 수 있습니다. 핵심은 다른 프로젝트에서 사용할 수 있는 설치 가능한 패키지를 제공하는 것입니다.
따라서 package.json 파일이 다음과 같도록 하세요.
{
"name": "tracker-jquery",
"version": "1.0.0",
"description": "jquery openreplay tracker",
"main": "src/index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"peerDependencies": {
"@openreplay/tracker": ">=3.6.0"
},
"devDependencies": {
"@openreplay/tracker": "<latest version of the tracker>",
"prettier": "^1.18.2",
"replace-in-files-cli": "^1.0.0"
}
}
그리고 프로젝트의 src 폴더 안에 index.js 파일을 추가합니다. export하는 함수가 미리 정해진 몇 가지 기준을 따르기만 한다면, 프로젝트는 원하는 방식으로 구성해도 됩니다.
플러그인의 구조는 단순합니다. 호출하면 app 속성을 전달받는 새 함수를 반환하는 함수를 정의합니다. 이 app 속성에는 트래커의 인스턴스가 들어 있습니다. 반환된 함수 안에서는 app 객체를 통해 메시지를 보내기만 한다면 원하는 것은 무엇이든 할 수 있습니다. 이 메시지는 플랫폼이 받아서 정해진 규칙에 따라 처리합니다.
메시지 형식은 이 튜토리얼 처음에 정의한 것입니다.
JQuery 플러그인의 코드는 다음과 같습니다.
import { App, Messages } from '@openreplay/tracker';
export default function($) {
const oldGET = $.get;
return (app) => {
const newGET = async (settingsObj) => {
const startTime = performance.now();
let resp = await fetch(settingsObj.url, {
method: 'GET'
})
const duration = performance.now() - startTime;
let valueResp = null;
if(settingsObj.json) {
valueResp = await resp.json()
} else {
valueResp = await resp.text()
}
const getStj = (res) => {
let r = {...res}
if (r && typeof r.body !== 'string') {
try {
r.body = JSON.stringify(r.body)
} catch {
r.body = "<unable to stringify>"
}
}
return JSON.stringify(r)
}
const msg = Messages.JQueryGET(
'GET',
String(settingsObj.url),
getStj(resp),
resp.status,
duration,
(new Date()).getTime()
)
console.log("Sending a message to the tracker....", msg)
app.send(msg, true)
return valueResp;
}
$.get = newGET;
}
}
기본적으로 하는 일은 JQuery의 .get 메서드를 내부적으로 fetch를 사용하는 커스텀 메서드로 교체하는 것입니다(이는 전적으로 선택 사항이며, JQuery의 GET 메서드를 그대로 사용해도 됩니다). 그리고 모든 요청이 수행된 후 코드가 app.send 메서드로 메시지를 보냅니다. 이 메시지는 앞서 Ruby 스크립트가 생성해 준 Messages.JQueryGET 메서드로 만듭니다.
이 메시지가 받는 속성은 당연히 1단계에서 메시지 구조로 정의한 것들입니다. 그리고 가장 중요한 점은 모두 필수라는 것입니다. 빈 값을 처리해야 한다면 빈 배열/객체/문자열 등을 전달하되 키를 절대 생략하지 마세요.
플러그인 게시
Section titled 플러그인 게시로컬 테스트를 마치고 플러그인이 준비되었다고 확신이 들면 NPM에 게시해야 합니다.
방법은 다음 가이드를 참고하세요. https://docs.npmjs.com/creating-and-publishing-unscoped-public-packages
플러그인 테스트
Section titled 플러그인 테스트게시한 후에는 플러그인을 사용해 보고 싶을 것입니다. OpenReplay 트래커에서 플러그인을 사용하는 방식은 항상 같습니다.
- 먼저 트래커를 인스턴스화해야 합니다.
- 그런 다음
use메서드를 호출하고, 플러그인을 호출해 반환된 함수를 전달해야 합니다. - 마지막으로
start메서드로 트래커를 시작합니다.
다음은 React 애플리케이션에서 이 플러그인을 사용하는 코드 예시입니다.
import logo from './logo.svg';
import './App.css';
import tracker from 'openreplay-tracker'
import jqueryTracker from 'tracker-jquery'
import { useEffect } from 'react';
import $ from 'jquery'
const t = new tracker({
__DISABLE_SECURE_MODE: true, //only if you're testing locally
ingestPoint: "openreplay.<your custom domain>/ingest",
projectKey: "<your project key>",
})
function App() {
useEffect(()=> {
t.use(jqueryTracker($))
t.start()
async function doGet() {
let resp = await $.get({
url: "http://localhost:3000"
})
console.log(resp)
}
doGet()
}, [])
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
}
export default App;
이 코드는 페이지가 로드되면 JQuery의 get 메서드로 GET 요청을 수행하며, 앞서 설정한 트래커 덕분에 이 요청을 실제로 추적할 수 있습니다.
최종 결과는 다음과 같이 표시됩니다.

이 튜토리얼의 전체 소스 코드는 이 저장소에서 확인할 수 있습니다.
이 과정의 어느 단계에서든 문제가 생기면 Slack 커뮤니티에 문의하여 개발자에게 직접 질문해 보세요!