회사에서 설문 제작 시 선택할 수 있는 새로운 문항 종류로 `서명 입력`을 받을 수 있는 모듈을 구현하게 됐다. 서명 입력 받는 라이브러리 중 가장 대표적으로 사용되고 있는 signature-pad를 리액트에서 좀 더 간편하게 사용할 수 있도록, React wrapper로 감싸주는 `react-signature-canvas`를 사용하기로 결정~ 해당 라이브러리를 사용하면서 고민했던 부분, 그리고 해결 방법에 대해서 글을 써보고자 한다.
https://www.pocketsurvey.co.kr/survey1/ExBZRsY 여기서 완성된 기능을 사용해볼 수 있다.
(포켓서베이 많이 사용해주세요😃) 오랜만에 링크 클릭해보니 서베이가 삭제된거 같다ㅠㅠ 404 뜨네ㅠㅠ
우선, 리액트 시그니쳐 패드를 사용하기 위해 react-signature-canvas를 설치한다.
$ yarn add react-signature-canvas
react-signature-canvas의 사용법은 쏘 심플하다. 그냥 가져다 쓰면됌.
import React from 'react';
import SignatureCanvas from 'react-signature-canvas';
const Signature = () => {
return (
<SignatureCanvas
canvasProps={{
className: 'signature-canvas',
width: 500,
height: 250,
}}
/>
);
};
export default Signature;
이건 진짜 기본 코드! 하지만 우리는 서명 초기화도 하고 싶고, 입력값이 있는지 없는지 확인도 해서 버튼 disabled도 주고 싶고, placeholder도 넣고 싶고, canvas 입력값을 dataURL로 저장하고 싶고... 등등 다양한 기능들을 덧붙이고 싶다. react-signature-canvas에서 제공하는 다양한 props와 API 메소드를 사용해보자.
문제와 해결방법
1. 화면 사이즈 변화할때마다 react-signature-canvas 입력값이 리셋됌ㅠ
화면 사이즈 변화할 때마다 canvas 입력값이 리셋되는 것을 막기 위해 clearOnResize를 사용.
import React from 'react';
import SignatureCanvas from 'react-signature-canvas';
const Signature = () => {
return (
<SignatureCanvas
canvasProps={{
className: 'signature-canvas',
width: 500,
height: 250,
}}
clearOnResize={false}
/>
);
};
export default Signature;
2. react-signature-canvas의 placeholder 구현은 어떻게 하지?
input이나 textarea였다면 정말 뚝딱이었겠지만... placeholder 속성이 없는 곳에 플레이스홀더를 넣어보려니 이런저런 삽질을 했다.
wrapper로 감싸서 div에 contentEditable 속성을 주고 데이터의 변경사항을 파악해서 pad가 empty일 때 placeholder를 띄워볼까 했다. 하지만 pad가 가지고 있는 isEmpty가 내가 원하는대로 동작하지 않았고 좀 더 깔끔하고 명확한 방법이 없을까 여러 시도 끝에 찾아낸 가장 깔끔하고 간편하게 placeholder 넣는 방법을 공유한다.
placeholder를 구현하기 위해 해당 canvas에 입력할 그 시작점을 잡아낼 수 있는 onBegin을 사용했다. 서명 데이터가 있는지 없는지 파악하기 위해 제공되는 isEmpty() API를 사용해려 했는데, 내가 원하는 시점에 동작하지 않아서 다른 구현방법이 필요했다. onBegin의 시점에 useState의 값을 업데이트해주고 해당 값을 통해서 placeholder 렌더 유무를 결정하도록 구현했다. 이 useState 상태값으로 button의 disabled 유무도 판별할 수 있으니 일석이조쓰~👍
import React, { useState, useEffect, useRef } from 'react';
import SignatureCanvas from 'react-signature-canvas';
import styled from 'styled-components';
const CanvasWrapper = styled.div`
position: relative;
`;
const CanvasPlaceholder = styled.div`
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: black;
`;
const Signature = () => {
const canvasRef = useRef<any>(null);
const [signatureData, setSignatureData] = useState<string>('');
const [isSigned, setIsSigned] = useState<boolean>(false);
return (
<>
<CanvasWrapper>
{!isSigned && (
<CanvasPlaceholder>
여기에 서명을 해주세요.
</CanvasPlaceholder>
)}
<SignatureCanvas
ref={canvasRef}
onBegin={() => {
setIsSigned(true);
}}
/>
</CanvasWrapper>
<button
onClick={() => {
canvasRef.current.clear(); // 리셋
setIsSigned(false);
}}
>
서명 초기화 버튼
</button>
<button
disabled={!isSigned} // 버튼 disabled
onClick={() => save()}
>
저장 버튼
</button>
</>
);
};
export default Signature;
3. dataURL을 file로 변환시키는 방법이 뭐지? convert dataURL to file
서명 입력값을 dataURL로 변경시키기 위해서는 react-signature-canvas에서 제공하는 toDataURL을 사용하면 되는데, 해당 dataURL을 file로 만들어 서버에 저장시키고 싶었다. dataURL을 file 로 만들기 위해 꽤 헤맸다. convert dataURL to file 로 얼마나 스택오버플로우를 뒤졌던지~ㅎㅎㅎ 내가 찾은 해결방법은 이렇다.
const convertDataUrlToFile = (name: string) => {
const dataURL = canvasRef.current.toDataURL('image/png');
const decodedURL = dataURL.replace(/^data:image\/\w+;base64,/, '');
const buf = Buffer.from(decodedURL, 'base64');
const blob = new Blob([buf], { type: 'image/png' });
return new File([blob], `${name}.png`, { type: 'image/png' });
};
'FE > etc' 카테고리의 다른 글
Nextjs의 Link component (1) | 2023.03.12 |
---|---|
프로젝트 eslint 적용하기 (1) | 2022.10.08 |
yarn과 dependencies / devDepedencies / peerDependencies (0) | 2022.10.03 |