본문 바로가기
개발 기록

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

by 유세지 2021. 1. 2.

A Line Translate : 한줄 번역기를 만들었어요. #2 에 이어지는 포스팅입니다.

마지막으로 사용자들이 번역 된 결과를 확인할 수 있도록 하는 기능들을 만들어봅시다.

 

우선 지난시간에 호스팅 했던 api 서버를 그냥 사용하게 되면, 정상적으로 작동하지 않습니다. 그 이유는 연결을 요청하는 위치와 데이터를 공급해주는 주소가 다르기 때문인데요, 이를 CORS (Cross-origin resource sharing) 에러라고 합니다.

 

CORS policy에 의해 차단당했습니다

 

간단히 말하면, 서버의 입장에서 불쑥 낯선 사람이 찾아와 데이터를 달라고 요청한 것과 같습니다. 이때 함부로 데이터를 제공하게되면 어떤 보안상의 문제가 발생할지 모르기 때문에 서버가 데이터를 주고받는 범위는 오직 본인의 영역에서만 이루어져야 안전하다고 할 수 있습니다. 이를 동일 출처 정책(Same-origin policy, SOP) 라고 합니다.

 

이 동일 출처 정책의 예외가 바로 CORS인데요, 허가된 주소에 한해서만 리소스를 주고 받을 수 있도록 예외를 두는 것입니다. 비록 자신이 아니더라도, 얼굴을 아는 친구나 지인에게는 정보를 전달해 줄 수 있는 것이죠.

 

문제는 바로 이것입니다.

API와의 통신을 담당하는 서버는 확장 프로그램인 클라이언트와 독립되어 있습니다. 따라서 서로 전혀 다른 주소로 인식하고 서로의 통신을 허용하지 않는 것이죠.

 

더군다나 이 프로그램의 클라이언트는 일정한 주소를 가진 페이지가 아닌 확장 프로그램입니다. 몇몇 주소만을 허용하는 방법으로는 통신이 불가능합니다. 따라서 모든 URL에 대한 접근을 허용할 필요가 있습니다.

 

실제로는 어떤 접근이 들어올지 모르니 지금 사용하는 방법은 그리 적절한 방법은 아닌 것이라고 생각됩니다. 저는 이 프로그램만을 위해서 빈 공간을 사용했지만, 혹여나 중요한 정보들을 저장해야 하는 곳이라면 이런 식의 방법은 사용하지 않는 것을 권장합니다.

 

CORS 관련 옵션들은 cors 모듈을 이용하여 처리합니다.

npm cors

 

npm 문서를 참고하면 사용법이 친절히 나와있습니다. 특정 도메인에서만 접근 가능하도록 할 수도 있지만, 모든 도메인에서 접근시키려면... 오히려 더 쉽습니다.

 

var cors = require('cors');
var app = express();
app.use(cors());

 

추가적인 설정을 할 필요 없이, require('cors')를 통해 모듈을 불러오고, express를 할당한 변수에서 .use(cors()); 를 주기만 하면 모든 주소로부터의 요청을 허용하게 됩니다. 이제 API단에서의 준비는 다 된 것 같습니다.

 

이제 클라이언트단으로 넘어가겠습니다.

지금까지 만들어 둔 서버와의 통신을 통해 번역된 값을 가져와야합니다. 번역해야 할 대상을 쿼리스트링의 SourceText 값에 넣고 GET 형식으로 전송해주면 됩니다.

 

const targetUrl = "http://서버주소/translate?sourceText=";

function getTranslate(text) {
    console.log(targetUrl+text);
    let url = targetUrl + text;
    fetch(url, {mode: 'cors'})
        .then((response) => response.json())
        .then((data) => (function() {
            alert(data.message.result.translatedText);
            console.log(data.message.result.translatedText);
        })())
        .catch((error) => console.log(error))
}

getTranslate(text)라는 함수를 만들어 fetch를 통해 서버 주소로 요청을 보내고, 응답한 데이터(data.message.result.translatedTest)를 alert과 console.log에 인자로 넣어 데이터가 정상적으로 송수신되는지 확인해보았습니다.

 

document.onmouseup = function() {
    if(dragText()) {
        getTranslate(dragText());
        console.log("working");
    }
}

위로 올라가서, Drag 여부를 확인하는 코드를 살짝 수정하여 지금 만든 getTranslate()로 드래그 내용을 전달하도록 하였습니다. 이제 드래그를 하게 되면 그 내용이 getTranslate로 전달되어 실행될 것 입니다.

 

바로 테스트를 해보겠습니다. 다음은 통계청 홈페이지에서 아무 영단어나 드래그 해 본 결과입니다.

 

 

Korea -> 한국

 

드래그 했던 영단어인 Korea한국으로 번역되어 경고창에 나타난 모습입니다.

콘솔창에 로그도 띄우도록 했으니 확인해보겠습니다.

 

요청을 보낸 서버의 주소와 번역된 단어가 로그에 찍혀있는 모습

 

문제없이 잘 작동하고 있습니다.

이제 경고창이 아닌 별도의 엘리먼트에서 표시되도록 바꿔주겠습니다.

 

 

function displayText(translated) {
    let newDIV = document.createElement("div");
    let newP = document.createElement("p");
    let closeButton = document.createElement("span");

    closeButton.innerHTML = "X";
    closeButton.addEventListener('click', function() {
        this.parentElement.style.display = "none";
    });
    newP.innerHTML = translated;

    newDIV.appendChild(closeButton);
    newDIV.appendChild(newP);

    newDIV.setAttribute("class","translatedTextView");
    newDIV.style.padding = "1rem";
    newDIV.style.position = "fixed";
    newDIV.style.zIndex = "1";
    newDIV.style.right = "0";
    newDIV.style.top = "0";
    newDIV.style.textAlign = "right";
    newDIV.style.background = "#FFFFFF";
    newDIV.style.border = "2px solid #CEECF5";
    newDIV.style.borderRadius = "1em 0 0 1em";

    document.body.appendChild(newDIV);
}

 

createElement를 이용해 새 엘리먼트를 만들어주고, element.style으로 만들어 준 엘리먼트에 스타일을 적용시켜주었습니다. 화면 한 쪽 구석에 조그맣게 알림이 뜨는 효과를 원했기때문에 포지션은 fixed, 위치는 top, right를 0으로 설정하였습니다. 번역된 데이터를 인자로 받아 newP 엘리먼트에 innerHTML으로 넣어주고, 닫을 수 있는 버튼인 closeButton과 함께 newDIV로 묶어 body 아래에 appendChild로 추가해줍니다.

 

만들어 준 displayText() 함수는 getTranslate() 함수에서 호출해주겠습니다.

 

function getTranslate(text) {
    console.log(targetUrl+text);
    let url = targetUrl + text;
    fetch(url, {mode: 'cors'})
        .then((response) => response.json())
        .then((data) => (function() {
            let translatedText = data.message.result.translatedText;
            displayText(translatedText);
        })())
        .catch((error) => console.log(error))
}

 

npm run build 명령어로 지금까지의 내용을 빌드해주고, 확장 프로그램 탭에서 한 후 이제 실행되는 모습을 직접 확인해보겠습니다.

 

 

우측 상단에 메시지 창으로 번역이 표시되었습니다.

 

성공입니다! 깔끔하게 표시가 되었네요.

 

이렇게 몇 번의 삽질 끝에 확장 프로그램 하나를 만들어 보았습니다. 중간중간 중계용 API 서버를 만든다던지 하는 사소한 문제가 있었지만, 어찌저찌 완성이 되었습니다. 밤새며 코딩했을때는 피곤했었는데 막상 다 만들고 나니 뿌듯하네요. 재미있는 경험이었고 앞으로 이걸 활용해서 더 재미있는걸 만들 수 있지 않을까 하는 생각이 듭니다!

 

 

전체 소스는 아래 Github에서 확인하실 수 있습니다!

 

A-Line Translate Github

A-Line Translate API Github

 

P.S. 

이 프로그램의 치명적인 문제가 있다면,

 

 

http와 https간에는 리소스를 요청할 수 없습니다

 

 

호스트 서버가 http라서 https가 적용된 사이트에서는 사용할 수 없다는 점...

https 도메인만 있었어도...

 

 

읽어주셔서 감사합니다.

반응형

댓글