리액트 프로젝트
블로그를 3년 이상 운영해 오면서 한 가지 느낀점이 있었습니다.
예전에도 언급했지만 글을 쓸 때 이미지가 있으면 개행 효과와 함께 글의 집중력을 높여준다고 느꼈기에 썸네일
을 글 상단에 넣기 시작했습니다.
확실히 글에 생기를 불어주는 역할을 하고 있지만, 한 가지 문제점이 있었습니다. 바로 썸네일 제작이 쉽지는 않다는 것이었습니다.
저는 그래픽 툴을 다룰 줄 모르기 때문에 썸네일을 만드는게 어려웠었습니다. 그래서 저는 두가지 방식을 선택했습니다.
이 두 방식은 간단한 썸네일을 쉽게 만들 수 있었지만 그림판은 가운데 정렬이 힘들었고, 파워포인트는 이미지화 시키는 방식이 번거로웠습니다. 그래서 이런 경험에 비추어 누구나 간단하게 심플한 디자인의 썸네일을 만들 수 있는 웹 페이지를 제작할 수 있도록 만들어진 것이 Thumbnail Maker
프로젝트입니다.
이 프로젝트는 다음과 같은 분들에게 추천합니다.
- 심플한 디자인의 썸네일을 사용하고 싶으신 분.
- 썸네일을 만들고 싶은데, 디자인 감각이 부족하거나 디자인툴을 다루기 어려우신 분.
react-icons
, prettier
를 사용했습니다.상세 코드는 리포지토리에서 확인하실 수 있습니다.
프로젝트에서 사용한 상태들은 배경색, 폰트색, 폰트 크기, 폰트, 텍스트, width, height입니다. 즉, 썸네일을 구성하는 구성요소들이 우리가 관리할 상태가 됩니다.
이 프로젝트는 단일 페이지이고, 그다지 복잡한 상태가 아니기 때문에 Context API
를 활용해서 상태들을 관리했습니다.
const ThumbnailContentsContext = createContext({
state: {
width: '',
height: '',
text: '',
textColor: '',
fontSize: '',
fontFamily: '',
backgroundColor: '',
},
actions: {},
});
아래에서 작성할 폼요소의 입력을 토대로 배너를 미리보기할 수 있는 컴포넌트입니다.
사용자에겐 단순히 미리보기의 역할을 하지만, 내부적으로는 이 미리보기를 div로 잘라내어 다운로드 하도록 작동하고 있습니다.
<Preview
id={'thumbnail'}
className={'thumbnail'}
ref={previewRef}
spellCheck={false}
height={height}
suppressContentEditableWarning={true}
style={{
width: `${width}px`,
height: `${height}px`,
backgroundColor: backgroundColor,
backgroundSize: 'cover',
backgroundPosition: 'center',
color: textColor,
fontSize: `${fontSize}px`,
fontFamily: fontFamily,
lineHeight: `${height}px`,
}}>
{text}
</Preview>
style
속성에 lineHeight
를 heigth state
와 동일하게 맞춤으로써, 글자가 배너의 정중앙에 위치하도록 만들었습니다. 그리고 각 요소들은 입력한 상태들을 받아와서 스타일이 적용되게 하고 있습니다.
const capturingThumbnail = () => {
html2canvas(document.getElementById('thumbnail'), {
width: width,
height: height,
scale: 1,
})
.then((canvas) => {
saveImg(canvas.toDataURL('image/jpg'), 'image.jpg', text);
})
.catch(e => {
console.error(e);
});
};
만들어진 썸네일을 캡쳐하고 다운로드하는 코드입니다. 여기서는 html2canvas
라이브러리를 통해서 PreviewPalette
의 요소를 캡쳐만 하고 실질적인 다운로드 과정은 saveImg()
함수가 수행합니다.
export const saveImg = (uri, filename, text) => {
let link = document.createElement('a');
document.body.appendChild(link);
link.href = uri;
link.download = `${text.split(' ').join('_')}.png`;
link.click();
document.body.removeChild(link);
};
사용자가 입력한 텍스트가 썸네일 이미지 파일의 이름이 되고, 다운로드하게 동작합니다.
색상 선택은 폰트 색상과 배경색 두 가지로 나뉘어 집니다. 두 컴포넌트 동작원리는 같습니다.
export const ColorPicker = ({ buttonType }) => {
const [selectedColor, setSelectedColor] = useState();
const { actions } = useContext(ThumbnailContentsContext);
const changeColor = (color) => {
setSelectedColor(color);
if (buttonType === 'background') {
actions.setBackgroundColor(selectedColor);
} else {
actions.setTextColor(selectedColor);
}
};
return (
<ColorPickerBox className={'color-picker-box'}>
<HexColorPicker
color={selectedColor}
onChange={changeColor}
/>
</ColorPickerBox>
);
};
Color Picker에서 선택된 색상의 값을 버튼 타입에 맞는 state에 업데이트합니다.
- 타입스크립트를 공부해서 js 코드를 ts화
- 배경에 이미지도 첨부할 수 있는 기능
- 디자인을 좀 더 개선
TextInput
컴포넌트가 입력, 폰트 크기, 폰트 종류 세 가지의 일을 한다. 한 가지의 일만 하도록 분리하는 리팩토링을 수행해야할 것 같다.처음으로 누군가가 진짜 사용하길 바라며 만들어본 프로젝트였다. 거기에 내가 필요로로 하기 때문에 제작한 프로젝트기도 해서 진행하는 내내 이것저것 연구하고 코드를 작성해본 것이 매우 즐거웠다. 대학생부터 지금까지 약 5년간 프로그래밍을 해오면서 가장 즐겁게 진행했던 것 같다.
이 프로젝트는 1차 완성이 아닌 배포 이후가 진짜라고 생각한다. 타입스크립트 코드로 마이그레이션한다는 계획을 갖고 있기에 새로운 도전이 될 것이고, 나의 성장에 큰 밑거름이 된 프로젝트라고 생각한다.