웹에서 클립보드에 텍스트 저장하기 (Clipboard API) with 리액트 + TypeScript
요즘 내 트리를 꾸며줘
, 롤링페이퍼
처럼 링크 공유형 서비스가 유행 중이다.
나 또한 그러한 서비스인 너가소개서
라는 프로젝트에 참여 중인데, 링크를 생성하는 뷰를 맡아 클립보드에 텍스트를 저장하는 기능을 개발하게 되었다.
비슷한 서비스를 개발해보고 싶은 프론트엔드 개발자라면 도움이 될 수도 있는 내용이다!
본론
웹 브라우저에서 클립보드에 텍스트를 저장하게 해주는 API는 Document.execCommand(), Clipboard가 있는데 전자는 deprecated 되었으며 이를 대체하기 위해 후자가 고안되었다.
따라서 이 글에서는 Clipboard API를 이용하는 방법을 소개하겠다.
Clipboard API는 navigator.clipboard
를 통해 전역에서 접근할 수 있다.
몇 가지 메소드가 있는데 이곳에서 확인할 수 있고, 우리는 그 중에서 writeText()
를 이용할 것이다.
이는 클립보드에 파라미터로 들어온 string 값을 저장해주는 메소드이다.
copyClipboard.ts
export const copyClipboard = async (
text: string,
successAction?: () => void,
failAction?: () => void,
) => {
try {
await navigator.clipboard.writeText(text);
successAction && successAction();
} catch (error) {
failAction && failAction();
}
};
나는 위와 같이 copyClipboard
라는 유틸 함수를 만들어 프로젝트 내에서 필요한 곳에 가져다 썼다.
복사할 string 값과 복사가 성공/실패했을 때 실행할 함수를 파라미터로 받는다.
이 함수들은 따로 지정해주지 않아도 된다. (obtional 파라미터)
writeText()
는 클립보드의 내용이 업데이트 되기까지의 과정을 Promise를 통해 비동기로 처리한다.
따라서 copyClipboard를 async 함수로 만들어 성공/실패 여부를 처리해주었다.
함수 내부에서 action을 다루지 않고, 요청 성공 여부에 따라 true/false 값을 주는 state를 반환하여 이 값을 통해 외부에서 자유롭게 action을 결정하는 hook(리액트)을 만들어 보았었는데
문제되는 점이 있어 이렇게 수정하였다. 이 내용은 마지막에 다뤄보겠다!
활용 예시
나는 링크 생성 성공/실패 시 토스트로 그 결과를 알려줘야 했기 때문에 action 함수로 서진이가 만든 토스트 함수를 넣어줬다.
import { IcLinkCopy } from '@assets/icons'; // svg 컴포넌트
생략
<StLinkBox>
<input type="text" value={link} disabled />
<div></div>
<IcLinkCopy
onClick={() =>
copyClipboard(
link,
() => fireToast({ content: '링크가 클립보드에 저장되었습니다.', bottom: 190 }),
() => fireToast({ content: '다시 시도해주세요.', bottom: 190 }),
)
}
/>
</StLinkBox>
생략
위 코드의 스타일드 컴포넌트, link 값 생성 등에 관련된 내용을 다 올리기에는 너무 코드가 광범위 해서 포함하지 않겠지만, 호옥시 궁금하신 분이 계시다면 댓글 남겨주시길 바란다!
결과
복사가 실제로 돼서 붙여넣기를 하는 것까지는 캡처 못했지만 실제로 복사가 잘 된다!
시도했다가 실패한 방법
위에서 언급했던 것처럼 처음에는 공통으로 사용할 함수는 복사 성공 여부만 알려주고 외부에서 자유롭게 action을 처리할 수 있도록 했다.
이는 isCopy라는 state를 반환하는 hook을 만들어 구현했다.
useCopyClipboard.ts
import { useState } from 'react';
export default function useCopyClipboard(): [
boolean,
React.Dispatch<React.SetStateAction<boolean>>,
(text: string) => Promise<boolean>,
] {
const [isCopy, setIsCopy] = useState<boolean>(false);
const copyClipboard = async (text: string) => {
try {
await navigator.clipboard.writeText(text);
setIsCopy(true);
return true;
} catch (error) {
console.error(error);
setIsCopy(false);
return false;
}
};
return [isCopy, setIsCopy, copyClipboard];
}
그런데 이 방법은 한 뷰에서 연속적으로 복사하려면 isCopy를 false로 초기화해주는 과정이 필요했다.
또한, isCopy에 따라 액션을 실행하기 위해 useEffect를 활용했는데 마운트 될 때 쓸데없이 해당 액션이 실행되는 버그를 잡기가 까다로웠다.ㅠ
const [isCopy, setIsCopy, copyClipboard] = useCopyClipboard();
useEffect(() => {
if (isCopy) {
fireToast({ content: '링크가 클립보드에 저장되었습니다.', bottom: 190 });
setIsCopy(false);
} else {
fireToast({ content: '다시 시도해주세요.', bottom: 190 });
}
}, [isCopy]);
Author And Source
이 문제에 관하여(웹에서 클립보드에 텍스트 저장하기 (Clipboard API) with 리액트 + TypeScript), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@treejy/웹에서-클립보드에-텍스트-저장하기-Clipboard-API-with-리액트-TypeScript저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)