본문 바로가기

MLOps/Development

ChatGPT를 사용한 뉴스 요약 : 크롬 익스텐션 개발기

ChatGPT를 사용한 뉴스 요약 : 크롬 익스텐션 개발기


서론

아래 포스팅을 보면 알 수 있듯이, ChatGPT를 사용해 해외 뉴스를 매일매일 요약하고 있다.

 

미증권 뉴스 스크랩핑(Node.js , Express, Puppeteer, Koyeb)

미 증권 뉴스 스크랩핑 하기 푼돈으로 주식하는 서학개미로서, 주식 뉴스는 조금이라도 읽자라 생각해서 주식 블로그를 하고 있는데 블로그에 글이 올라가는 루틴은 아래와 같다 기사의 본문을

junnyhi.tistory.com


불편했던 점은

ChatGPT가 대화의 히스토리를 기억한다고 하지만, 대화가 길어지면 처음 요구 했던 내용을 종종 잊어버린다


그렇기 때문에

뉴스 요약을 하는 과정 중간 중간 마다 엉뚱하게 요약을 하는 일이 발생했다.

그래서 매 번 대화할때마다 원하는 프롬프트를 계속 집어넣기로 했다.


하지만

해당 프롬프트를 매번 Ctrl + C/V 하는 과정은 놀랍게도 대한민국에서는 매우 귀찮은 일이 아닌가 싶다.

그래서 해당 프롬프트 내용을 앞에 붙여서 보내주는 버튼을 확장프로그램으로 만들기로 했다.

  • 확장 프로그램 팝업창

  • 생성 된 버튼


버튼의 역할은 매우 간단하다


버튼의 역할은 단순하게

"뉴스를 요약할 때 어쩌꼬 저쩌고"를  입력된 뉴스 내용 앞에 붙여서 전송해 주는 역할이다


개발

1. Manifest V3

manifest_version은 3으로 사용했다

"manifest_version": 3

2. 권한

2.1 Permissions

"permissions": [
    "activeTab",
    "storage"
  ],

ActiveTab

  • 현재 활성중인 탭에 대해 다룰 수 있는 API 사용 권한

Storage

  • 일종의 브라우저 데이터베이스 API 사용 권한인데, 토글스위치의 On/Off 상태 정보를 저장하기 위해 사용

2.2 Host_permissions

  • 특정 사이트에서만 크롬 익스텐션이 동작할 수 있도록 호스트 권한 설정
"host_permissions": [
    "https://chat.openai.com/*"
  ],

3. popup.js

1. 토글 스위치 상태 확인:

  • chrome.storage.local.get 함수를 사용해서 storage 내부 isButtonEnabled  키의 값을 가져옴
  • 스토리지에 isButtonEnabled 키의 값이 있는 경우, 이 값을 사용하여 토글스위치의 체크 상태를 설정

2. 토글 스위치 이벤트 리스너:

  • 토글 스위치에 'change' 이벤트 리스너를 추가해서. 사용자가 스위치를 토글 하면 이 리스너 호출
  • 이 리스너는 먼저 chrome.storage.local.set  함수를 사용하여 `isButtonEnabled 키의 값 변경. 
  • 그 다음 활성화된 탭을 찾아서 `TOGGLE_BUTTON` 메시지를 보냈음.  메시지는 `content.js` 스크립트에서 처리
document.addEventListener('DOMContentLoaded', function () {
  var toggleSwitch = document.querySelector('#toggleSwitch');

  if (toggleSwitch) {
    // 팝업이 열릴 때마다 토글의 상태를 확인합니다.
    chrome.storage.local.get('isButtonEnabled', function(data) {
      if (data.hasOwnProperty('isButtonEnabled')) {
        toggleSwitch.checked = data.isButtonEnabled;
      }
    });

    toggleSwitch.addEventListener('change', function () {
      // 토글의 상태를 변경하고, background script에 메시지를 보냅니다.
      chrome.storage.local.set({isButtonEnabled: toggleSwitch.checked}, function() {
        console.log('Value is set to ' + toggleSwitch.checked);
      });

      chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
        if (tabs.length > 0) {
          chrome.runtime.sendMessage({cmd: 'TOGGLE_BUTTON', value: toggleSwitch.checked, tabId: tabs[0].id});
        }
      });
    });
  }
});

4. Service-Worker.js

해당 스크립트에서는 일부 코드 중, Promise를 사용해서 수정한 코드

Promise를 사용하지 않은 원래의 코드
  • `chrome.storage.local.set` 함수의 콜백 함수 안에서 `chrome.tabs.query` 함수를 호출하고 있는데,
  • 이는 `chrome.storage.local.set` 함수의 작업이 끝나고 나서 `chrome.tabs.query` 함수의 작업을 수행
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  if (request.cmd === 'TOGGLE_BUTTON') {
    chrome.storage.local.set({isButtonEnabled: request.value}, () => {
      console.log('Value is set to ' + request.value);

      chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
        if (tabs.length > 0) {
          try {
            chrome.tabs.sendMessage(tabs[0].id, {cmd: 'TOGGLE_BUTTON', value: request.value});
          } catch (error) {
            console.error('Failed to send message to tab', tabs[0].id, error);
          }
        }
      });

      // sendResponse 호출
      sendResponse({status: 'success'});
    });

    return true;
  }
});

Promise를 사용한 수정된 코드:
  • Promise를 사용한 수정된 코드에서는, `chrome.storage.local.set` 함수의 콜백 함수 안에서 Promise를 생성하고, 이 Promise가 해결(resolve)될 때까지 기다림
  • 이는 `chrome.storage.local.set` 함수의 작업이 끝나고 나서 다음 작업을 수행하기 위함
  • 비동기 작업흐름이 명확해져서 좋고 오류 처리가 더 간편하다는 장점이 있는 것 같음
chrome.runtime.onMessage.addListener(async (request, sender, sendResponse) => {
  if (request.cmd === 'TOGGLE_BUTTON') {
    await new Promise(resolve => {
      chrome.storage.local.set({isButtonEnabled: request.value}, () => {
        console.log('Value is set to ' + request.value);

        chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
          if (tabs.length > 0) {
            try {
              chrome.tabs.sendMessage(tabs[0].id, {cmd: 'TOGGLE_BUTTON', value: request.value});
            } catch (error) {
              console.error('Failed to send message to tab', tabs[0].id, error);
            }
          }
        });

        // sendResponse 호출
        sendResponse({status: 'success'});
            resolve();
          });
        });

        // 리스너가 비동기 응답을 보낼 것임을 나타냅니다.
        return true;
    }
});

Prommise를 사용하게 된 이유

아래 줌 인터넷 테크 블로그를 참조하다가

 

크롬 확장프로그램 개발⛏️ 회고

Vue.js를 이용하여 줌 확장프로그램을 개발하는 과정에 대한 회고입니다.

zuminternet.github.io

 

다음과 같은 문구를 발견해서 찾아보고 바로 사용한듯..


content.js

  • 뉴스 내용 앞에 들어갈 프롬프트를 아래처럼 정의해 놨음
newButton.onclick = function() {
    inputField.value = `
너의 역할은 해외 증권 뉴스를 한국어로 요약하는 한국 증권사 직원이다
너가 요약한 뉴스는 일반인들이 읽기때문에 이해하기 쉽도록 한국어로 요약해야한다
70단어 이내로 요약해라
2가지 포맷을 지켜라
- 제목 : <제목>
- 요약 :  <요약>
${inputField.value}`;
    sendButton.click();
};

뉴스를 복사 붙여 넣기만 하면, 앞에 프롬프트를 같이 보내주는 용도 


스토리지에서'isButtonEnabled'라는 키의 값을 가져와서, 그 값이 true인 경우 addButton 함수를 호출하고, 

그렇지 않은 경우 removeButton 함수를 호출

chrome.storage.sync.get('isButtonEnabled', function(data) {
    if (data.isButtonEnabled) {
        addButton();
    } else {
        removeButton();
    }
});

결과

 

팝업 창


끝으로

몇 가지 에러가 남아있어서 해결해야 하는 데, 쉽지가 않다


참조

 

크롬 익스텐션 개발기 (feat. Manifest V3)

안녕하세요. 이번에 원티드 프론트엔드팀에 새로 합류하게된 임성현입니다. 제가 팀에 합류해서 처음으로 받은 미션인 크롬 익스텐션 개발 관련 경험을 공유드리고자 합니다. 정책상 자세한 서

medium.com

 

 

크롬 확장프로그램 개발⛏️ 회고

Vue.js를 이용하여 줌 확장프로그램을 개발하는 과정에 대한 회고입니다.

zuminternet.github.io

 

 

Welcome to Chrome Extensions - Chrome Developers

Documentation for Chrome extensions developers.

developer.chrome.com