크롬 확장 프로그램을 아시나요?

크롬 확장프로그램

크롬 확장 프로그램 프로젝트를 진행하기 위한 셋팅을 알아보겠습니다.

 


 

1. 프로젝트 CRA(create-react-app)로 시작

npx create-react-app extension-project

 

설치가 완료되면 구조가 이러합니다.

create-react-app 구조

 

 

 

2. manifest.json 파일 수정

public > manifest.json

{
  "name": "your project name",
  "icons": {
    "16": "icon-16x16.png",
    "48": "icon-32x32.png",
    "64": "icon-64x64.png",
    "128": "icon-128x128.png"
  },
  "permissions": [],
  "manifest_version": 3,
  "version": "0.0.1",
  "action": {
    "default_popup": "index.html",
    "default_title": "your project title"
  }
}

여기서 참고할사항은 manifest_version입니다. 

검색을 해보면 대부분 2 버전으로 되어있는데, 2와 3을 잘 파악해서 쓰시기를 바랍니다.

action과 같은 몇개의 부분들이 변경된 듯합니다.

name에 프로젝트 이름과 함께 icons에 아이콘을 등록해줍시다.

16 사이즈는 아래와 같이 확장 플러그인 액세스 고정하면 나오는 아이콘이고 favicon.ico등 여러 확장자가 가능한 듯합니다.

액세스 고정시 나오는 영역

48, 64, 128 등의 사이즈는 확장 플러그인 관리창이나 다운로드 창에서 사용된다고 합니다.

확장 플러그인 관리창은 chrome://extensions/로 이동하거나 아래와 같이 아이콘을 클릭하면 나옵니다.

확장 프로그램 아이콘 클릭 > 확장 프로그램 관리

이동하면 보이는 아이콘들입니다.

아이콘들

 

permissions는 사용자의 컴퓨터에 권한을 설정하는 곳입니다. 즉 storage를 사용하기 위해서는 permissions에 추가해야합니다.

version은 출시하는 버전을 작성하면되고, 스토어에 버전정보가 노출됩니다.

actions은 2버전에서는 browser_action이었는데 3에서 통합되었다고 합니다.

확장 기능 액션 수행 시 필요한 곳입니다.

 

 

 

3. .env 파일

.env 파일은 루트에 생성하도록 합니다.

.env 생성

.env 파일은 환경 변수를 저장하는 파일인데, 아래와 같은 코드를 추가합니다.

extension에서는 인라인 자바스크립트를 허용하지 않기 때문에 추가해야한다고 하는데, 여튼 이게 없으면 오류가 납니다.

// .env
INLINE_RUNTIME_CHUNK=false

 

 

 

4. 빌드

자 이제 빌드해줍니다.

npm run build

build

 

 

 

5. 확장 프로그램 등록

파일을 등록해주겠습니다.

위에서 설명한 확장 프로그램 관리 페이지로 이동합니다.

chrome://extensions/ 

확장프로그램 페이지

1) 왼쪽 상단의 개발자 모드 버튼 클릭

2) 압축해제된 확장 프로그램을 로드합니다. 등의 버튼이 뜨면 해당 버튼 클릭

3) build 폴더를 찾아서 등록

build 폴더 등록

4) 등록된 것 확인

확장프로그램 등록된 캡쳐

5) 확장 프로그램 아이콘 클릭 > 내가 등록한 프로그램 고정

확장프로그램 고정 캡쳐

6) 등록확인

확장프로그램 등록 확인

 

 


https://developer.chrome.com/docs/extensions/mv3/intro/

자바스크립트 전역 스코프에 정의된 것은 코드 내의 어디서든지 접근이 가능하다는 것이 문제가 되고는 합니다.

이때 외부에 공유되면 안되거나 충돌날 위험이 있는 경우 즉시 실행 함수를 통해 해결하고는 합니다.

 

즉시 실행 함수(IIFE)

단순하게 함수와의 차이를 보자면 즉시 실행되는지 아닌지의 차이입니다.

함수표현식은 함수를 정의하고, 변수에 함수를 저장하고 실행하는 일련의 과정이 있습니다.

하지만 즉시실행함수는 이러한 과정없이 즉시 실행됩니다.

문법의 차이는 단순히 함수를 괄호 "()"로 랩핑한다는 것입니다.

 

형태

(function() {
    console.log('즉시 실행 함수'); 
}());

var now = (function () { 
    console.log('즉시 실행 함수')    
})();

 

익명, 기명 즉시 실행 함수

(function () { 
	console.log('익명 즉시 실행 함수'); 
}());

(function now() { 
	console.log('기명 즉시 실행 함수'); 
}());

 

사용이유

자바스크립트의 모듈 패턴에서 사용할 수 있습니다.

전역 스코프에 정의하는 것이 아닌 즉시실행함수의 스코프에서 실행컨텍스트가 활성화됩니다.

이렇게되면 유효범위가 다른 곳에서 접근하려고 할때 은닉해주는 성질을 지닐 수 있습니다. (캡슐화)

물론 아예 접근이 안되는 것은 아닙니다. 접근하는 방법은 있습니다.

 

즉..

즉시실행함수로 일반적인 유효범위를 설정하는 언어에서와 같이 private와 public등의 캡슐화를 사용할 수 있습니다.

캡슐화를 해야하는 이유는 무엇일까요? 당연히 충돌을 방지하기 위해서입니다.

즉시실행함수는 플러그인이나 라이브러리에서 많이 사용됩니다.

 

예시

jQuery 라이브러리에서 $라는 전역변수를 사용하고 있는데, 또 다른 라이브러리를 도입하게되었을 경우...

추가 된 라이브러리도 $라는 전역변수가 있으면 충돌이 생기게됩니다.

이때 

즉시 실행 함수를 사용하여 $ 전역 변수의 충돌을 피할 수 있습니다.

(fucntion($) {
 	//.....
})(jQuery); // jQuery 인자로 전달

 

 

 


참고 

developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/function

redux-saga 캡쳐

React는 MVC패턴에서 V(View)를 담당하고 있습니다.

앱이 가벼운 경우에는 State로 데이터를 핸들링 할 수 있지만,

앱이 커질 경우에는 데이터 관리를 별도로 해야하는 요구사항이 생기게됩니다.

이때 사용할 수 있는 것이 Redux와 MobX 등, 상태 관리 라이브러리입니다.

 

redux-saga는 redux의 액션 생성자와 reducer의 순수성을 유지하고 사이드 이펙트를 처리하기 위해 사용합니다.

여기서 사이드 이펙트는 부작용을 발생시키는 어떠한 효과가 아니라,

데이터 요청이나 비동기 작업, 브라우저 캐시 같이 순수하지 않은 작업들을 의미합니다.

 

 

Redux-Saga란?

redux-saga는 리덕스의 미들웨어입니다.

리덕스가 액션을 수행하면 redux-saga에서 디스패치하여 redux의 액션을 가로챈 뒤, 액션의 역할을 수행 하고

다시 액션을 발행하여 데이터를 저장하거나 다른 이벤트를 수행시킵니다.

redux-saga 동작 이미지 출처 uzihoon.com/post/181be130-63a7-11ea-a51b-d348fee141c4

 

redux-saga와 비슷한 라이브러리로 redux-thunk가 있지만,

redux-saga는 redux-thunk에 비해 이펙트 함수를 통해 다양한 작업들을 처리할 수 있습니다.

redux-saga가 좀 더 러닝커브가 높습니다.

  • 비동기 작업 시 기존 요청 취소 처리 가능
  • 특정 액션 발생 시 다른 액션이 디스패치 되도록 할 수 있음
  • 웹 소켓 사용 시 channel 기능을 사용하여 효율적 코드 관리가 가능
  • API 요청이 실패 했을때 재 요청하는 작업을 할 수 있음

이 외에도 다양한 비동기 작업들을 처리 할 수 있습니다.

 

 

자주 사용하는 헬퍼 함수

redux-saga는 스토어에 지정된 액션들이 디스패치되었을 때,

task를 만들기 위해 내부 함수들을 감싸는 헬퍼 이펙트를 제공합니다. 

 

all

제너레이터 함수를 배열의 형태로 인자로 넣어주면 병렬로 동시에 실행됩니다.

이때 모든 함수에 대한 결과가 resolve될 때까지 블럭됩니다. 

 

put

특정 액션의 디스패치하도록 합니다.

결과를 스토어에 디스패치(put) 합니다.

 

call, apply

순수 객체만 리턴하는 함수입니다. 오브젝트 메소드 호출을 지원합니다.

첫번째 파라미터는 함수이며 나머지 파라미터는 해당 함수에 넣을 인수 값 입니다.

액션이 발생하면 전달한 함수를 호출하여 실행합니다.

API가 리턴될때까지 블럭되며, 비동기 함수 호출 시 용이합니다.

call과 apply는 두번째 인자 값의 차이만 있습니다.

 

delay

설정된 시간 이후에 resolve를 하는 Promise 객체를 리턴합니다.

제너레이터를 정지하는데 사용할 수 있습니다.

 

takeEvery

액션이 발생하게되면 task를 실행합니다.

task가 종료되기 전에 또 다른 액션이 발생할 경우, 또 하나의 새로운 task를 실행합니다.

 

takeLatest

액션이 발생하게되면 task를 실행합니다.

만약 실행 중인 task가 있다면 기존 task를 종료하고 새로운 task를 실행합니다.

실수로 여러번 클릭했을때를 방지하거나 마지막에 요청된 데이터를 보여줄 때 사용합니다.

 

takeLeading

액션이 발생하게되면 task를 실행합니다.

해당 task의 실행이 완료되기 전까지 뒤에 오는 이벤트들을 블럭합니다.

이후 task가 완료되면 액션에 대해 수신합니다.

 

throttle

초 이내 요청을 한 번만 보냅니다.

마지막 함수가 호출된 후 일정 시간이 지나기 전 재 호출하지 않습니다.

스크롤 이벤트 사용 시 용이합니다.

 

debounce

초 이내 요청을 한 번만 보냅니다.

처음 함수나 마지막 함수만 호출 후 일정시간이 지나기 전 재 호출하지 않습니다.

 

 

제너레이터함수

ES6에 포함된 제너레이터 함수는 function* 키워드로 작성합니다.

redux-saga는 이 제너레이터 함수를 적극 활용한 사례입니다.

제네레이터는 제네레이터함수의 반환이며 redux-saga가 작성한 함수를 호출하여 반환받는 객체가 제너레이터입니다.

*이터레이터 프로토콜과 이터러블 프로토콜을 준수합니다.

// 코드출처: https://mskims.github.io/redux-saga-in-korean/basics/DeclarativeEffects.html 
import { takeEvery } from "redux-saga/effects"
import Api from "./path/to/api"

function* watchFetchProducts() {
  yield takeEvery("PRODUCTS_REQUESTED", fetchProducts)
}

function* fetchProducts() {
  const products = yield Api.fetch("/products")
  console.log(products)
}

yield를 이용하여 이펙트들을 호출하고 수행된 내용을 다시 돌려 받아 그 액션을 수행합니다.

redux-saga는 제네레이터를 통해 이펙트를 수행하는 역할을 합니다.

 


참고, 출처

이터러블 (Iterable)

순회가능한 객체를 말합니다. 

순회 가능한 객체는 Symbol.iterator 심볼 속성을 가지고 있으며 이터레이터 객체를 반환하는 객체를 말합니다.

이러한 것을 이터러블 프로토콜이라고 하며 이터러블 객체라고 합니다.

 

이터레이터 (Iterator)

이터러블 메소드로 반환하는 객체입니다.

next 메소드를 구현하고 있으며 value, done을 반환하는 객체입니다.

반환되는 IteratorResult는 {done: boolean, value: any} 형태의 단순한 객체입니다.

next 메소드를 통해 모든 값을 돌고 나면 done이 true로 나오며 끝납니다.

한번 끝난 이터레이터는 다시 돌아가지 않으며 value가 undefined로 리턴합니다

이러한 것을 이터레이터 프로토콜이라고 합니다.

 

for of 루프는 순회 시작 전 [Symbol.iterator]() 메소드를 호출하여 이터레이터 객체를 얻습니다.

그 후에 순차적으로 next 메소드를 호출하며 하나씩 순회합니다.

보통 이터러블 프로토콜과 이터레이터 프로토콜을 하나의 객체에 모두 구현하는 것이 일반적입니다.

// 영원히 0을 반환하는 무한 이터레이터 예시
var zeroesForeverIterator = {
    [Symbol.iterator]: function () {
        return this;
    },
    next: function () {
        return {done: false, value: 0};
    }
};

 

사용자 이터레이터 객체 생성

이터러블 프로토콜은 obj[Symbol.iterator]: Function => Iterator로 표현할 수 있습니다.

그 후 이터레이터 프로토콜을 따라 value, done가 들어있는 오브젝트를 반환하는 next 메소드를 가진 객체를 반환하면 됩니다. 

const iterable1 = {};

iterable1[Symbol.iterator] = function* () {
  yield 1;
  yield 2;
  yield 3;
};

console.log([...iterable1]); // Array [1, 2, 3]

 

제너레이터 (Generator)  

이터러블, 이터레이터 객체를 만드는 손 쉬운 방법입니다.

동작은 function* 문법을 사용해서 작성하며 next를 통해 실행합니다.

next 메소드를 통해 실행이 되며 yield 문을 만나면 정지합니다. 

next 메소드를 호출함으로써 값을 소비할 수 있습니다. 

function* 문법을 통해 함수가 생성되고 최초로 호출될때 함수 내부의 어떠한 코드도 실행되지 않습니다.

이때 생성자 함수가 반환되는데 이것을 제너레이터 함수라합니다. 그 후 값을 소비하며 함수로 부터 반환되는 객체를 제너레이터라고 합니다.

 

즉, 제너레이터 객체는 이터레이터 객체입니다. 

(이터레이터 프로토콜을 따르고 있습니다.)

 

예시

값이 더이상 업을 경우 value: undefined, done: true가 반환된 것이 확인됩니다.

function* generatorFunc() {
  yield 1;
  yield 2;
  yield 3;
}

const it = generatorFunc();

console.log(it.next()); // { value: 1, done: false }
console.log(it.next()); // { value: 2, done: false }
console.log(it.next()); // { value: 3, done: false }
console.log(it.next()); // { value: undefined, done: true }

 

제너레이터 적극 활용한 예로 redux-saga가 있습니다.

 

 

 

 


 

참고 및 정의 출처

+ Recent posts