[JavaScript] Secure Context 이슈

Jnary·2025년 1월 27일
1

Web Application

목록 보기
15/15
post-thumbnail

두가지 에러가 발생했다.

  1. Uncaught (in promise) TypeError: self.crypto.randomUUID is not a function
  2. Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'writeText')

1. crypto.randomUUID 오류

우리 프로젝트에서는 아래와 같은 토스트 훅을 사용하고 있었다.

export const useToast = () => {
  const setToastList = useSetAtom(toastAtom);

  const showToast = (
    message: string,
    variant?: "error" | "success" | "default",
    duration?: number,
  ) => {
    const added = {
      id: self.crypto.randomUUID(),
      message,
      variant,
      duration: duration ?? 2000,
    };

    setToastList({ toast: added });
  };

  return { showToast };
};

crypto.randomUUID 가 일부 환경에서 지원하지 않아 발생한 오류라고 판단하였다.

"randomUUID" | Can I use... Support tables for HTML5, CSS3, etc

94.86% 의 사용을 봐서 브라우저 호환성 문제는 아닐 것 같다고 생각했지만, 이것이 지원되지 않는 환경에선 어떻게 대처할 수 있을 지를 고민했다.

  1. uuid 라이브러리 사용: 브라우저 호환성 문제가 거의 발생하지 않아 안정적으로 사용 가능

    import { v4 as uuidv4 } from "uuid";
    
    export const generateUUID = (): string => {
      if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
        return crypto.randomUUID();
      }
      return uuidv4();
    };
  2. 폴리필 작성

    • 폴리필(Polyfill): 브라우저나 런타임에서 특정 기능이 지원되지 않을 때 해당 기능을 흉내내거나 대체 기능을 제공하여 동일한 API를 사용할 수 있게 해주는 코드
    • 방법: 대체 로직 추가 or 기존 API 모방
    // crypto.randomUUID 폴리필
    if (!crypto.randomUUID) {
      crypto.randomUUID = () => {
        // UUID v4 생성 로직
        return ([1e7] as any + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, (c) =>
          (
            Number(c) ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (Number(c) / 4)))
          ).toString(16)
        );
      };
    }

브라우저 호환성 문제일까 싶어서 브라우저 호환성을 해결할 방법을 찾아봤지만, 아무리 검토해도 환경 문제는 아니라고 판단했다.

그럼 대체 뭐가 문제일까 !!!

2. navigator.clipboard.writeText 오류

prod 서버는 정상적으로 작동한다는 것을 알았다. 왜 rc 서버에서만 작동이 되지 않는걸까?

prod 서버는 https://algohub.kr 을 사용하고 있지만, rc 서버에서는 http://rc.algohub.kr 을 사용하고 있다.

혹시 http라서 그런걸까?

https://developer.mozilla.org/ko/docs/Web/API/Navigator/clipboard

정답이었다. Navigator Clipboard API는 HTTPS, LOCAL 환경에서만 지원해서 안 되는 것이었다.

https://developer.mozilla.org/en-US/docs/Web/API/Crypto/randomUUID

1번에서 적은 오류 또한 HTTP 에서 사용할 수 없는 문제로 기인한 것이었다.

https를 사용하면 해결되는 문제 !

그럼 http에서는 해결책이 없을까?

3. navigator.clipboard 대체 로직

execCommand 를 사용하면 된다.

const copy = async (code: string) => {
  const inviteWriting = `[AlgoHub 알림]\n\n'${ownerNickname}'님께서 회원님을 ‘${groupName}’ 그룹에 초대하셨습니다!\n\n아래 링크를 클릭하셔서 초대를 수락해 주세요!\n함께 도전하며 발전하는 시간을 보내시길 기대합니다.\n\n지금 바로 참여하세요:\nalgohub.kr/join-group/${code}`;

  if (navigator.clipboard && typeof navigator.clipboard.writeText === "function") {
    try {
      await navigator.clipboard.writeText(inviteWriting);
      showToast("성공적으로 복사되었습니다.", "success");
      setIsCopied(true);
    } catch (err) {
      showToast("복사에 실패했습니다. 다시 시도해 주세요.", "error");
    }
  } else {
    // Clipboard API 미지원 시 대체 로직
    const textarea = document.createElement("textarea");
    textarea.value = inviteWriting;
    document.body.appendChild(textarea);
    textarea.select();

    try {
      document.execCommand("copy");
      showToast("성공적으로 복사되었습니다.", "success");
      setIsCopied(true);
    } catch (err) {
      showToast("복사에 실패했습니다. 다시 시도해 주세요.", "error");
    } finally {
      document.body.removeChild(textarea);
    }
  }
};
  • 값 저장할 textarea 요소 생성
  • 복사할 값을 textareavalue 속성에 저장
  • 해당 요소를 html 문서에 추가 후 선택
  • 선택된 부분을 execCommand 로 복사
profile
숭실대학교 컴퓨터학부 21

2개의 댓글

comment-user-thumbnail
2025년 2월 9일

잘 읽었습니다!
execCommand의 MDN(https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand)에 가보면 Deprecated 경고가 나와요. 모든 페이지에 https를 적용하는 방법이 가장 베스트일 것 같다고 느껴집니다 :)

1개의 답글

관련 채용 정보