본문 바로가기
source-code/FrontEnd

[chrome extension] 이미지 파일 안전하게 불러오기

by mattew4483 2024. 8. 9.
728x90
반응형

Backgrounds

일반적인 웹개발 환경에서는, 아래와 같이 이미지를 불러올 수 있습니다.

import someImg from "./some-img.png";

<img src={someImg} />

만약 이 상황에서 해당 경로에 파일이 존재하지 않는다면, 빌드 시 Module not found 발생합니다.

해당 경로에 파일이 존재하지 않는 경우, 빌드 에러 발생

대부분의 번들러들은 소스코드를 분석할 때 require, import로 참조된 모든 모듈과 자원을 탐지하며,

이 덕분에 존재하지 않는 파일을 불러오는 등의 실수를 미연에 방지할 수 있습니다.

 

chrome extension에서는 이미지를 불러오기 위해 조금 다른 방법을 사용해야 합니다.

익스텐션의 content_scripts는 현재 실행되는 웹페이지를 기본 context로 가지기 때문에

위와 같이 작성할 경우, 현재 웹페이지/some-img.png 경로로 파일을 조회합니다.

(파일이 있을 리가 없으니, 이미지 로드가 안 되겠죠!)

 

실행 중인 각 익스텐션은 별도의 자체 보안 원본을 가지고 있으며

우리가 업로드한 애플리케이션 파일 역시, 해당 위치에 보관되어 있습니다.

→ 해당 경로로 프로젝트 내 자원을 조회해줘야 하는 것이죠!


 

매니페스트 - 웹 액세스 가능한 리소스  |  Chrome Extensions  |  Chrome for Developers

manifest.json의 web_access_resources 속성에 대한 참조 문서입니다.

developer.chrome.com

chrome은 설치 디렉터리 내의 상대 경로를 정규화된 URL로 반환하는, getURL API를 제공합니다.

import someImg from chrome.runtime.getURL('some-img.png')
// chrome-extension://익스텐션ID/some-img.png 경로로 자원 조회

<img src={someImg} />

위와 같이 작성하면, 정상적으로 이미지를 불러올 수 있습니다.

(공식 문서처럼, manifest.json내 web_accessible_resource에 해당 파일을 추가하는 것도 잊지 말아야겠죠)

 

Problems

하지만 위와 같은 방식은 해당 경로 내 파일 존재 여부를, 빌드 시점에 알 수 없다는 문제가 존재합니다!

import someImg from chrome.runtime.getURL('wrong-path/some-img.png') // 정상 번들링

<img src={someImg} />

이유는 간단합니다.

getURL API는 말 그대로 chrome.runtime 객체의 메서드에 불과하며,

번들러 입장에서 '.some-img.png'는 단순한 문자열에 불과하므로, 해당 위치에 자원이 존재하는지 검사할 필요가 없는 것이죠.

 

Solutions

파일의 존재 여부를 번들러가 검사하게 만들기 위해서는 → require, import를 통해 해당 모듈을 참조해야 합니다.

하지만 그와 동시에, 실제 img src에는 익스텐션 설치 디렉터리(getURL로 조회된)를 작성해줘야 하겠죠

 

따라서, 이미지 식별자 ↔ 프로젝트 내 파일 경로 ↔ chrome 정규화 URL 간 변환 함수를 구현해 이를 해결하고자 했습니다.

// lib/chrome-extension/image-url-loader.ts

// 1. 이미지 식별자와 실제 파일 경로를 매핑하는 객체 생성
const IMAGE_SOURCE_PATH_MAP = {
  SOME_IMG: require("./some-img.png"), // require()를 사용해 번들러가 파일 존재 여부를 검사하게 함
};

export function getImageURL(key: keyof typeof IMAGE_SOURCE_PATH_MAP) {
  // 2. IMAGE_SOURCE_PATH_MAP에서 해당 이미지 모듈 가져오기
  const imageModule = IMAGE_SOURCE_PATH_MAP[key];

  // 3. require()로 불러온 모듈에서 파일 경로 추출
  // ES 모듈의 경우 기본적으로 default 속성에 경로가 저장됨
  const imagePath = imageModule.default || imageModule;
  
  // 4. 파일 경로에서 파일명만 추출
  const filename = imagePath.split("/").pop();

  // 5. chrome.runtime.getURL()을 사용해 정규화된 URL 반환
  return chrome.runtime.getURL(`${filename}`);
}

가장 먼저 IMAGE_SOURCE_PATH_MAP 객체를 생성해 줬습니다.

이는 이미지 식별자와 실제 프로젝트 내 이미지 파일의 경로를 mapping 하고 있으며

해당 데이터 내 require 구문을 통해, 번들링 시 해당 파일이 실제로 존재하는지 여부를 번들러가 검사하게 됩니다.

 

실제 이미지 경로 사용처에서는, getImageURL 함수를 사용합니다!

앞서 작성한 IMAGE_SOURCE_PATH_MAP을 통해 입력받은 이미지 식별자(key)에 해당하는 모듈을 조회한 뒤,

모듈 파일명을 통해 익스텐션 디렉터리 경로를 생성해 반환해 주는 것이죠.

import { getImageURL } from "@/lib/chrome-extension/image-url-loader";

<img src={getImageURL('SOME_IMG'} />

→ 이를 통해 1) 번들링 시 모듈 존재 X 에러 방지 2) 이미지 파일 정상 load 가 가능해집니다!

 

+ getImageURL은 keyof typeof IMAGE_SOURCE_PATH_MAP 타입을 인자로 받기 때문에,

사용처에서도 호출 가능한 이미지 목록들을 타입 추론할 수 있다는 장점도 가져갈 수 있겠죠.

(반대로 단점은 사용할 이미지가 추가될 때마다, IMAGE_SOURCE_PATH_MAP에 해당 key-value를 추가해줘야 한다는 것?)

728x90
반응형