본문 바로가기
개발 기록

A Line Translate : 한줄 번역기를 만들었어요. (크롬 확장 프로그램 개발 과정, Chrome extensions) #1

by 유세지 2020. 11. 17.

학기가 시작하고 과제와 자료 조사 및 필기를 하며 영어로 된 자료들을 볼 때가 많은데, 한 두 단어를 몰라서 구글 번역기를 켜고 작은 화면에서 옮겨적는 과정이 상당히 번거로웠습니다. 물론 모니터 하나를 더 쓰면 될 일이지만 들고 다니기엔 여분의 모니터를 휴대하고 다니기에는 부담스러워서... 영단어나 문장을 바로 번역해서 보게 해주는 서비스가 있다면 어떨까 생각해봤습니다.

 

영어를 보고 이게 무슨 뜻일까? 라고 생각했을때 바로 번역이 된다면 좋겠지만 그런 하이테크까지는 모르고... 그 과정을 최대한 줄일 수만 있다면 그걸로 충분할 것 같았습니다. 어떤 방법으로 번역을 할 수 있을까 고민해보니, 드래그를 했을때 자동으로 컨텍스트가 번역된다면 꽤 멋진 기능이 될 것 같았습니다.

 

그런데 이런 기능이 작동하려면 프로그램이 계속 실행중인 상태로 드래그를 하는지 안하는지 여부를 지켜봐야했습니다. 그렇다고 독립된 프로그램으로 실행 상태가 유지된다면 그건 그거대로 바람직하지 못한 느낌이 들었습니다. 작업 관리자에 한 줄을 차지하고 있다니... 제 프로그램이 뭐라고 감히ㅠㅠ

 

생각해보니 어차피 이런 필기를 하거나 자료를 찾아보는 상황은 모두 브라우저에서 일어나고 있었고, 마침 브라우저는 js가 동작하는데 최적의 환경이었습니다. 더군다나 저는 크롬을 기본 브라우저로 사용하고 있기에 이미 확장 프로그램이라는 좋은 기능도 함께 사용하고 있었죠. 고마워요, 애드블록!

 

한 번도 만들어 본 적이 없는 크롬 확장 프로그램이라니, 너무 재미있을 것 같아서 바로 웹스톰을 켜고 프로젝트를 생성하였습니다. 그렇게 A Line Translate라는 이름의 크롬 확장 프로그램을 만들게 되었습니다.

 

제가 한창 복무하고 있을때, 개발을 하시다가 입소하게 되신 한 공익근무요원 분의 포스트를 본 적이 있었습니다. 업무를 하며 불편했던 점을 해결하기 위해서 크롬 확장 프로그램을 직접 만들어서 사용하셨다는 내용이었는데 굉장히 재미있게 읽었던 기억이 있어 이 프로젝트도 그 글에 영감을 받았는지도 모르겠습니다. Vue.js와 Electron을 이용해서 만드셨던걸로 기억을 하는데 이 포스트를 작성하기전에 다시 찾아보고 싶었지만 결국 찾지 못했습니다ㅠㅠ.. 혹시 아시는 분이 계시다면 덧글로 링크를 달아주시면 감사하겠습니다.

 

한 줄 번역기는 대략 이런 배경을 가지고 탄생하게 되었습니다. 이제부터는 개발 과정을 정리해보겠습니다.

 

 

어떤 기술을 이용해서 만들까?

프로젝트를 시작하기 전, 가장 먼저 구상했던 내용입니다. 어떤 기술을 사용해서 만들어야 효율적이고 즐거운 개발을 할 수 있을지 생각해보았는데 역시 한참 익숙해지던 React를 이용해서 해보기로 하였습니다. 사실 바닐라 자바스크립트와 리액트 둘 사이의 고민이었던터라... 최근까지 진행했던 프로젝트들이 모두 리액트를 기반으로 돌아갔었고, 위쪽에서 언급했던 분께서 Vue를 사용하셨다는 점도 한 몫을 하지 않았을까 생각합니다.

 

 

개발 과정

create-react-app A-Line-Translate

 

우선 Create-React-App 명령어를 사용하여 기본적인 리액트 프로젝트를 만들어주었습니다. 시간이 걸리는 작업이라 잠시간 기다리면 필요한 파일들이 자동으로 생성됩니다. Create-React-App 명령어를 사용하면 manifest.json 파일이 자동으로 만들어집니다. 프로젝트의 특성에 맞게 살짝 손을 봐주었습니다.

 

매니페스트 파일도 설정해주고

 

환경 변수 파일도 만들어줍니다. 프로젝트 최상위 디렉토리에 .env 라는 이름으로 넣어줍니다.

 

환경 변수 파일도 만들어줍니다.

 

npm run build 명령어를 이용하여 빌드 파일을 만들어줍니다. 앞으로는 이 파일(폴더)이 프로젝트의 실행 파일이 될 것입니다.

npm run build

 

이제 크롬에서 확인해봅시다. 주소창에 chrome://extensions를 입력하거나, 우측 상단 플러그인 탭에 있는 확장 프로그램 관리를 눌러 들어갑니다.

 

개발자 모드를 활성화시키고, 압축해제된 확장 프로그램을 로드합니다. 버튼을 눌러 빌드 폴더를 연결해주면,

 

build 폴더를 확장 프로그램으로 업로드한 모습

 

이렇게 내가 만든 프로그램이 목록에 보이게 됩니다. 한 번 실행시켜볼까요?

 

작고 귀여운 확장 프로그램이 만들어졌습니다

 

처음 CRA 명령어를 이용해 생성하면 볼 수 있는 화면이 띄워지게 되었습니다. 생각보다 작게 나와서 귀엽네요. 나중에 css를 통해 조절할 수 있으니 우선은 넘어가겠습니다.

 

확장 프로그램이 정상적으로 구성된 것을 확인했으니, 이제 본격적인 기능을 구현할 차례입니다.

 

우선 우리가 원하는 기능을 위해서는, 확장 프로그램이 현재 띄워져있는 브라우저와 서로 통신할 수 있어야합니다. 연결 되어있는 것처럼 보이지만 서로 독립된 페이지이기 때문에 브라우저 자체 기능을 이용하여 통신해야 합니다.

 

이때 전역변수인 chrome을 사용하면 브라우저에서 다양한 작업을 할 수 있게 됩니다.

그러나 우리 프로젝트에 그냥 사용하게 되면 오류를 뱉어 build를 할 수 없게됩니다.

 

not defined error.

 

프로젝트 내부에 chrome이 선언되지 않았기 때문에 eslint에서 문제를 삼는 것인데, 크롬 브라우저에서 실행하면 정상적으로 작동하니 걱정말고 무시해주셔도 됩니다. 하지만 이대로 두면 테스트를 할 수가 없기 때문에 린트의 눈을 속일 필요가 있습니다.

 

/* global chrome */

 

파일의 최상단에 위 코드를 추가해주면 린트가 chrome 변수를 문제 삼지 않게됩니다. 이제 화면의 텍스트들을 긁어 확장 프로그램에 띄워주는 코드를 넣어 테스트 해보겠습니다.

 

function get_source(document_body) {
    return document_body.innerText;
}

chrome.extension.sendMessage({
    action: "getSource",
    source: get_source(document.body)
});

function __onWindowLoad() {
    chrome.extension.onMessage.addListener(function(request, sender) {
        if(request.action == "getSource") {
            document.body.innerText = request.source;
        }
    });

    function onWindowLoad() {
        chrome.tabs.executeScript(null, {
            file: "getSource.js"
        }, function() {
            if(chrome.extension.lastError) {
                document.body.innerText = 'Error : \n' + chrome.extension.lastError.message;
            }
        });
    }

    window.onload = onWindowLoad;
}

 

 

이대로 실행을 해보았으나... 오류가 발생했습니다.

 

Cannot access contents of the page. Extension manifest must request permission to access the respective host.

 

이 오류는 해당 페이지에 접근할 권한이 없어 발생하는 문제입니다. 확장 프로그램 쪽에 페이지에 접근할 수 있는 권한을 추가해주면 정상적으로 작동하게됩니다. manifest.json 파일의 permissions 속성을 다음과 같이 수정해줍니다.

 

"permissions": [
    "http://*/",
    "https://*/"
]

 

와일드카드(*)를 이용해 http:// 와 https:// 로 시작하는 모든 페이지에 접근할 수 있는 권한을 설정해줍니다.

 

github에서 확장프로그램을 사용해본 결과

 

훌륭합니다. 페이지의 내용을 제대로 긁어오고 있네요.

 

이렇게 getSource.js 파일을 페이지에 넣어서 (injection) 코드를 실행시키는 과정까지는 확인했습니다. 하지만 우리의 확장 프로그램은 클릭해보기 전까진 어떻게 동작하는 것인지 확실히 알 수가 없습니다. 콘솔로그를 통해 정확히 언제 실행되는 것인지, 생명주기를 일부 확인해보겠습니다.

 

 

로드되자마자 실행될 수 있는 콘솔로그를 추가합니다.

위와 같이 getSource.js가 로드되면 바로 콘솔로그가 출력될 수 있도록 코드를 추가해주고, 개발자 모드를 켠 채로 확장프로그램을 실행해보았습니다.

 

확장프로그램을 실행하는 순간에 콘솔이 출력됩니다.

 

이걸로 우리는 별다른 조작이 없다면 확장프로그램을 눌러서 실행해야 비로소 함수가 실행된다는 것을 알게 되었습니다. 그러나 우리가 원하는 기능은 항상 옵져버가 드래그 유무를 관찰하고 있어야 하니 눌러서 실행하지 않더라도 백그라운드에서 상시 동작하도록 만들어주어야 합니다. 많은 분들이 사용하고 계시는 애드블록 같은 경우도 백그라운드에서 상주하며 자동으로 광고를 차단해주고 있으니 분명히 가능해보입니다.

 

그럼 새로운 파일인 background.js 를 하나 만들겠습니다. 이 파일에는 백그라운드에서 동작하게 될 코드들이 들어가게 됩니다.

 

console.log("background running");

chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
    alert(changeInfo.url);
});

 

파일이 실행되면 콘솔창에 background running 이라는 로그가 남게됩니다.

그리고 탭이 업데이트 되면, 경고창을 띄워서 바뀐 url을 보여주게 되는 코드를 작성하였습니다.

 

이제 매니페스트 파일에서 이 파일을 백그라운드에서 실행할 파일로 연결해줍니다.

 

"background": {
    "scripts": ["background.js"],
    "persistent": false
},

...


"permissions": [
    "tabs",
    "http://*/",
    "https://*/"
],

 

persistent의 값은 false를 기본값으로 주시면 되겠습니다. 구글에서도 네트워크 동작을 계속 하는 경우가 아니라면 false로 두는 것을 권장하고 있습니다.

 

그리고 tab 액션을 사용하였기 때문에 permissions에 tabs를 추가해줍니다.

 

이제 테스트 해볼까요? 새롭게 빌드 후 확장 프로그램에서 업데이트를 적용해주고 다른 페이지로 이동해봅시다.

 

깃허브 프로필 화면으로 이동하니 경고창이 나왔습니다.

 

 

이렇게 확장 프로그램을 개발할 모든 준비가 끝났습니다.

처음 시도해보는거라 신기하기도 하고 하나부터 알아가는 재미가 있네요.

 

 

본격적인 기능 구현은 다음 포스트에서 진행해보도록 하겠습니다.

긴 글 읽으시느라 고생 많으셨습니다.

 

 

 

 

참고했던 글

멍개님 - blog.naver.com/pjt3591oo/220893971798

Google extension guide - developer.chrome.com/extensions

권영재님 - www.letmecompile.com/chrome-extension-with-react/

ICHI.PRO- ichi.pro/ko/2020-nyeon-e-reactlo-chrome-hwagjang-peulogeulaem-eul-mandeuneun-bangbeob-103001569235806

How do I use chrome.tabs.onUpdated.addListener? - stackoverflow.com/questions/11156479/how-do-i-use-chrome-tabs-onupdated-addlistener

반응형

댓글