캉골과 1주일의 방학 중 토이 프로젝트를 시작했다. 이름하야 [행바타]! ㅎㅎ
우테코 마스코트 캐릭터인 행성이를 꾸미고 png로 파일을 다운 받을 수 있는 사이트이다.
4일 만에 완성한 간단한 프로젝트였다.
행바타 깃허브 주소: https://github.com/Woowa-Toy-Lab/hangbata/tree/develop

크루들 사이에서 왼손이 샤라웃해준 나만의 행성이 만들기가 유행한 적이 있다. 우테코 선릉캠 칠판엔 항상 행성이 그림들이 가득했고 크루들의 슬랙 프사도 하나 둘 행성이가 되어갔다.

공유해주신 파일에 들어가보니 귀여운 행성이 사진 파일들이 가득했다.
그렇게 시작하게된 행바타 프로젝트~

초안으로 그려본 피그마 행바타 와이어프레임이다.

그리고 나온 행바타 UI 디자인.
대충 [몸, 표정, 소품, 특수효과, 배경]으로 나누어서 저 캔버스 안에 아바타를 꾸미고 이미지를 다운받을 수 있는 걸로 구상했다.
이를 위해 우리는 행성이 svg 파일이 필요했는데... 왼손이 공유해주신 행성이 그림들은 누끼도 따져있지않은 행성이 jpg 사진이었다.
물론 나는 일러스트레이터를 다룰 줄 아니까 벡터로 선을 따는 방법도 있었지만...주어진 시간은 적고 이것도 상당히 귀찮은 작업이었다. 그래서 일러스트레이터의 "이미지 추적 후 확장" 기능을 활용해 소스 작업 시간을 단측시키고 싶었다. 하지만 저 손그림 이미지를 이미지 추적시키면 패스들이 너무 여러갈래로 쪼개져서 쓸 수 없는 수준으로 추출이 된다. 그래서 생각해낸 것이~

이렇게 깔끔하게 다시 그려달라고 GPT한테 요구하는 것이다 ㅎㅎ

되게 잘 따준다. 하지만 이렇게 깔끔하게 그려줘봤자 여전히 흰색 배경이 섞인 픽셀 파일의 JPG 사진일 뿐이다. 이것을 이제 어도비 일러스트레이터를 켜서 갖고와보자. 이렇게 이미지를 선택한 상태에서

오브젝트 - 이미지 추적 - 만든 후 확장
을 누르면 ~

짜잔 깔끔한 svg 상태가 되었다!

이 툴로 간단하게

면도 채워줄 수 있고

패스가 이렇게나 깔쌈하게 나뉜 것을 확인할 수 있다.
이제 이 것을 svg로 내보내기만 하면 코드에서 화질 깨짐 없이 동적으로 웹에 그려낼 수 있는 것이다. 굳굳~
캉골과 나는 레벨 1 동안 한 번도 같이 페어로 매칭된 적이 없었다. 그래서 토이프로젝트를 시작할 때만 해도 대화를 많이 한 것과는 별개로 서로의 코드 스타일이나 컨벤션은 전혀 모르는 상태였다. 그래서 처음 대략적인 아바타 UI를 구성하고, 상태를 어떻게 관리할 거다하는 식의 큰 틀 로직은 함께 페어 프로그래밍으로 구현하기로 해보았다. 나중에 세부 기능을 구현하면서 분업을 하더라도 서로의 스타일을 맞춰보고 우리만의 컨벤션이 생긴 상태에서 분업을 하는 게 효율적일 것 같았다.
우리의 토이 프로젝트의 가장 큰 목적은 레벨 1 바닐라 자바스크립트에 대한 복습에 있었기 때문에 외부 라이브러리를 쓰지 않고 순수 우리만의 코드로 작성하기로 하였다.
그리고 캉골과 코드를 작성하면서 정말 많은 인사이트를 얻고 또 한번 성장 및 복습이 견고해질 수 있었다!
캉골은 레벨 1 동안 엘레강트 오브젝트 스터디를 하며 객체지향, 객체란 무엇인가에 대한 공부를 많이 한 것 같았다. 그리고 나는 리뷰어 해먼드와 하리를 거쳐, 프론트엔드 크루원들의 코드들을 보는 PR 리뷰 스터디도 진행하면서 선언적인 프로그래밍이란 무엇일까에 대해 고민을 많이 해보았었다.
캉골과 나는 서로 다른 부분에 집중해서 자신만의 기준을 세우고 넘어갔던만큼 서로의 다른 인사이트를 공유할 수 있었고 자칫 고민해보고 넘어가지 못할 뻔 했던 것들을 함께 고민해보며 더욱 깔끔하게 레벨 1을 회고할 기회가 되었던 것 같다.
...
(5/19) 이어서 작성...
레벨 2가 시작되고 너무 바빠져서 행바타 회고를 여태 마무리하지 못했었다. 이미 프로젝트를 끝낸지 오래 됐기도하고 코드가 잘 기억나지 않는다... 그래서 그냥 프로젝트를 진행하며 만들었던 부산물인, createElement() 함수 코드를 가져와봤는데,
function toElement<K extends keyof HTMLElementTagNameMap>(
template: string,
tag: K
): HTMLElementTagNameMap[K] {
const container = document.createElement("div");
container.innerHTML = template;
const el = container.firstElementChild;
if (!el) {
throw new Error("toElement 유틸 에러: element가 없습니다.");
}
return el as HTMLElementTagNameMap[K];
}
interface IArguments {
id?: string;
class?: string;
type?: string;
name?: string;
value?: string;
src?: string;
alt?: string;
style?: string;
for?: string;
}
export function createElement<K extends keyof HTMLElementTagNameMap>(
tag: K,
args: IArguments,
...children: string[] | Element[]
): HTMLElementTagNameMap[K] {
const attribute = Object.entries(args)
.map(([key, value]) => `${key}="${value}"`)
.join(" ");
const template = `<${tag} ${attribute}></${tag}>`;
const element = toElement(template, tag);
children.forEach((child) => {
if (typeof child === "string") element.textContent = child;
else element.appendChild(child);
});
return element;
}
이 코드이다. 캉골의 toElement와 나의 기존 createElement 함수를 합쳐서 만들게된 함수인데, 자바스크립트에서 시멘틱 태그와 agrs, children 을 받아 자식을 가진 엘리먼트를 만들어낼 수 있다.
const logoBox = createElement(
"div",
{ class: "logo-box" },
createElement("img", {
src: "./img/logo-alpha.svg",
alt: "hangbata logo image",
})
);
const logoContainer = createElement(
"div",
{ class: "logo-container" },
logoBox,
titleBox
);
사용처는 약간 이런 느낌.
이게 되돌아보니 더 마음에 드는 이유가, 레벨 2에서 리액트 딥다이브 스터디를 하며 알게된 건데, 이게 사용처의 모습과 내부 구현 로직이 리액트의 실제 createElement()와 유사했다.
뭔가 리액트에 createElement라는 게 있는지도 몰랐고 내부 구현은 더더욱 몰랐는데 함께 고민해서 정리하고 만든 함수가 리액트의 코드와 비슷했다니까 신기하고 뿌듯한 느낌...ㅎㅎ
이 외에 기능 구현을 하면서, 바닐라 자바스크립트로 아바타 사이트를 구현하는 동안 고민했던 지점과 새로 쌓은 인사이트가 굉장히 많았는데...시간이 너무 지나버렸다...
간단하게 돌아보자면, 드래그로 아바타의 요소를 꾸미고 수정하는 기능과 그렇게 꾸민 파일을 svg/png 파일로 저장하는 로직을 구현할 때 많은 고민을 하기도 하고 애를 먹었던 것 같다.
드래그로 아바타의 요소 위치를 수정하고 꾸밀 수 있는 기능의 경우
<canvas> 태그를 사용하기 vs DOM을 순수 조작하게 하기
사이에서 고민하다가 후자의 방법을 선택한 뒤, 파일을 저장할 때는 png 파일 다운로드 시에 svg를 canvas에 모아 렌더링하는 방식을 선택하였다.

그렇게 완성된 사이트와 우테코 채널 홍보글 캡쳐이다.
방학이 끝나고도 1달이 넘게 지나 기억이 많이 휘발된 상태에서 마무리하는 회고글이다보니 뭔가 급마무리되는 느낌인데...ㅠㅠ 다음 프로젝트를 할 때는 정말 매일 회고를 남겨야겠다...