Bleeding edge

⚡코딩테스트를 준비하는 프론트엔드 개발자를 위한 크롬 익스텐션 개발기 본문

Side Project

⚡코딩테스트를 준비하는 프론트엔드 개발자를 위한 크롬 익스텐션 개발기

codevil 2022. 10. 29. 19:09

프로젝트를 시작하게 된 계기

처음 이 프로젝트를 시작하게 된 계기는 백준 온라인 저지 사이트에서 오답이 나왔을 때, 문제를 다시 읽으려면 다시 문제 페이지로 이동하는 것이 불편하다는 점에서 시작됐다. 페이지 이동을 줄여주기 위해서 문제 제출 페이지에서 문제도 볼 수 있게 만들기로 했다.

크롬 익스텐션을 선택한 이유

내가 직접 사이트를 수정할 권한이 없기에 이 문제를 해결하기 위해서는 브라우저의 익스텐션을 개발하기로 했다. 브라우저의 익스텐션은 사람들이 가장 많이 이용하는 크롬을 기준으로 만들기로 했다. 다행히도 크롬 익스텐션은 js를 이용하여 구현할 수 있었다.

처음에 만들기로 한 문제 제출 페이지에서 문제를 볼 수 있게 만드는 것은 local-storage를 이용하여 만들었기에 빠르게 만들었다.

새로운 발표 새로운 니즈 그리고 계획

8월부터 백준 코딩 테스트로 스터디를 하면서 그 사이트에 불편한 점을 개선하여 백준 애드온에 반영을 하기로 하였다.

  • 백준 사이트에서 바로 문제를 풀 수 있게 만들자.
  • 이전에 풀이를 하던 코드를 저장하고 값을 불러올 수 있게 만들자.
  • 백준에서 사용하는 예시 입력값 코드를 불러올 수 있게 만들자.

이 불편한 점을 해결하기에 앞서 가장 먼저 값을 출력하기 위해서는 코드를 실행시킬 방법에 대해 고민할 필요가 있었다.

아이디어

브라우저는 자바스크립트 엔진을 이용하고, 백준에서 풀이하는 언어는 Node.js이다.

따라서 자바스크립트 엔진을 통하여 문제를 풀이하도록 만들기로 했다.

목표

  • 사이트 내의 입력값과 출력값을 불러올 것
  • 원하는 테스트케이스를 추가할 수 있을 것
  • 입력값 예시 코드를 쉽게 불러올 수 있게 만들 것
  • 실행을 누르면 입력값을 이용하여 코드 결과를 html에서 볼 수 있을 것
  • 코드 결과와 출력값을 비교하여 풀이가 맞았는지 확인할 수 있을 것
  • 크롬 웹 스토어에 등록할 것

문제점 그리고 해결책

문제점 : manifest v3

자바스크립트 엔진을 이용하여 코드를 실행하기 위해서는 에디터에 string을 실행하는 생성자 function을 사용하는 방법이 있었다. 문제는 생성자 function은 manifest v3에서 사용할 수 없었다.

manifest v3는 원격코드를 허용하지 않았다. 원격코드는 다음과 같다.

  • 개발자의 서버에서 가져오는 자바스크립트 코드
  • CDN에서 가져오는 라이브러리 코드
  • 런타임에 eval() 함수 인자로 제공되는 코드 문자열

이 세 개 중 생성자 function은 3번째 eval의 코드 문자열에 걸린다.

해결책 : iframe

manifest v3는 모든 eval를 막는 것이 아니라 unsave-eval에 대한 제한을 두고 있다. 즉, safe한 eval은 사용이 가능하다는 것을 알 수 있었다. 원격코드의 공통점을 보니, 크롬 익스텐션이 외부 코드를 이용하여 현재 페이지에 대한 영향을 끼치는가 아닌가를 기준으로 만들어진 것 같았다.

고민 : 현재 document에 영향을 미치지 않는 방법?

현재 document에 새로운 iframe을 삽입하면 독립적으로 코드가 실행될 수 있고, 현재 document에 영향을 끼치지 않을 것 같아서 iframe을 이용하였다. 생각한 대로 iframe을 실행하면 manifest v3의 규정에 위배되지 않고 function을 사용할 수 있었다. 이 방법 덕분에 에디터의 CDN 역시 사용할 수 있어서 두 가지의 토끼를 잡을 수 있었다.


문제점 : 입력값 전달

같은 document를 사용하는 경우에는 변수를 이용하여 쉽게 해결할 수 있었지만, iframe을 사용하는 경우에는 값을 넘겨주려면 다른 처리가 필요했다.

해결책 : localStorage

iframe에 입력값과 출력 값을 넣기 위해서는 localStorage와, postMessage와 같은 선택지가 있었다. 두 선택지의 차이점은 localStorage는 값을 저장하여 재활용이 가능하고 postMessage는 큐가 비어있다는 전제하에 값을 바로 보낼 수 있었다. 테스트케이스를 다시 페이지에 들어왔을 때 로드할 수 있게 localStorage를 선택하였다


문제점 : 코드 실행의 UX

프로그래머스를 사용해보면 아주 간단한 케이스라도 코드가 실행된다는 것을 보여준다. 백준 애드온은 에디터에 있는 값만 실행하기 때문에, 이전 값과 현재 값이 같으면 실행이 됐는지 알 수 없었다. 그래서, 실행을 누르면 실행중 이라는 element를 만들도록 만들었다. 문제는 문제를 너무 빨리 풀이하는 경우에 element를 눈으로 보기전에 사라졌다.

해결책 : 의도적인 시간 지연

UI / UX를 보면 이미 화면에 필요한 것들은 모드 로드가 되었지만, 자연스러운 페이지 전환을 위하여 페이지 로딩을 띄우는 방법을 들은 적이 있어서 이 방법을 적용했다. 이 방법을 적용하면, 실행이 빠르게 끝나도 눈으로 실행이 진행 되었는지 확인시키기 위해 실행 중이라는 메시지를 잠시 띄운다. 잠시 띄우는 시간은 너무 많이 기다리지 않고, 너무 빨리 사라지지 않는 0.5초로 정하였다.


문제점 : console.log

백준에서는 console.log로 출력된 값을 기준으로 정답을 채점한다. 문제는 이 값은 콘솔 창으로 전달된다. 값을 육안으로 확인하기 위하여 console.log에 있는 값을 iframe 내의 element 안으로 append해야 했다.

해결책 : window.console.log

console도 사실 윈도우의 객체에 이고 console.log는 객체의 메소드이다. console.log는 실행을 element에 append로 실행을 바꾸어 붙이도록 수정하였다.

console.log = function (...args) {
          console_stack = console_stack +
            (console_stack === '' ? '' : '\\\\n') +
            [...args].join(' ');
        };

문제점 : [Object object]

위의 방법으로 console.log를 수정하였을 때, 일반적인 경우에는 문제가 없었지만 object, array, map, set와 같은 경우에는 값이 [Object object]와 같은 텍스트로 바뀌었다.

해결책 : JSON.stringify

[...args].map(arg=>
       typeof arg ==="object"? JSON.stringify(arg): arg).join(' ');

object 같은 경우, JSON.stringify를 사용하지 않고, HTMLElement로 텍스트 위치에 삽입하면 [Object object]로 삽입된다. 문제는 삽입하는 방법이 중요하다. array와 object는 그냥 삽입해도 상관없지만, map과 set는 다르다. map과 set은 JSON.stringify를 사용하면 모두 값이 사라지는 현상이 있다. 다행히도 이전에 map에 대해서 여러 가지 시도를 해본 것이 있었기 때문에 해결을 빨리할 수 있었다. (참고 : https://codevil.tistory.com/122 ) 참고 주소를 들어가면 map에 값을 저장하는 방법과 프로퍼티를 저장하는 방법이 다르다는 것을 알 수 있다. map과 set은 인스턴스에 저장된 값들이지 프로퍼티가 아니기 때문에 다르게 작동하기 때문이다. (위의 티스토리 블로그에 글을 작성할 때는 기현상이라고 생각을 했었는데 공부하고 다시 보니 당연한 현상이었다) 위의 현상을 고려하여, Map과 set같은경우에는 […arg]를 이용하여 변환을 한 이후에 값을 이용하였다.

[...args].map(arg=>
              arg instanceof Set ||
                arg instanceof Map ? JSON.stringify([...arg]) :
                arg instanceof Array || 
                arg instanceof Object? JSON.stringify(arg):
                arg
              ).join(' ');

개발 후기

8월부터 커넥트 강의에서 들었던 자바스크립트 수업이 백준 애드온에 적용할 수 있어서 재미있고 너무 좋았다. 이번 프로젝트 때는 스코프와 실행 컨텍스트 그리고 window 객체에 있는 메서드들에 대해 공부할 수 있어서 좋았다. 개인적으로 아쉬웠던 점은, 사이드를 하는 과정에서 만드는 결과물도 중요하지만 내가 공부했던 것이나, 문제를 해결하는 과정에서 어떤 고민을 하였는지 메모를 하는 것이 중요하다고 생각이 들었다. 그래서 다음부터 고민에 대한 내용들을 doc 폴더에 하나씩 기록할 필요성을 느꼈다.

첫 배포를 하면서..

크롬 익스텐션은 배포를 하고 나면 바로 적용되는 것이 아니라 심사가 끝난 후에 업데이트를 할 수 있다. 심사가 있기 때문에 오히려 더 크게 느낀 점이 하나 있다. 테스트케이스를 충분히 실행한 후에 배포를 하자! 첫 배포 때 확인 창이 바깥 버튼을 누르면 사라지지 않는 현상이 있었다. 배포 직전에 마지막 문제만 해결하고 올렸기 때문에 이런 현상이 생겼다고 생각한다. 앞으로는 적극적으로 테스트를 하고 신중하게 배포를 해야겠다.