<aside> <img src="/icons/bookmark_purple.svg" alt="/icons/bookmark_purple.svg" width="40px" />
목차
</aside>
jQCloud 1.0.4 라이브러리의 워드클라우드 UI 구현 로직 분석
jQCloud가 키워드 배열을 워드 클라우드로 표시하는 과정을 다시 정리해보면 이렇다.
hitTest
함수를 사용하여 다른 키워드 엘리먼트와 겹치는지 확인한다. 겹치지 않을 때까지 계속 새 좌표에 배치해본다.
hitTest
함수는 렌더링된 두 요소 각각의 중심 x좌표 사이의 거리와 y좌표 사이의 거리를 참고하여 겹침을 판단한다.already_placed_words
배열에 저장한다.이제 우리 프로젝트의 KeywordsView
컴포넌트에서, 키워드명과 빈도수 값을 가진 keyword 객체들을 담은 keywords
배열을 워드클라우드 형태로 표현해야 한다.
위 로직을 리액트 프로젝트에 적용하려면 두 가지 방법이 있을 것 같다.
useRef
로 참조하고, keywords
배열의 변경을 감지할 때마다 컨테이너 내부 innerHTML를 싹 비우고 jQcloud에서 하는 것처럼 단어들을 다시 하나씩 중앙부터 스파이럴 형태로 바깥쪽까지 배치한다.keywords
각각의 키워드들이 배치될 좌표를 jQcloud 로직을 활용하여 계산해둔 다음, map
으로 렌더링할 때 각 키워드들이 지정된 좌표에 배치되도록 한다.2번 방법을 사용하고 싶었던 이유는, 이미 유진님이 각 키워드들을 map으로 렌더링하면서 빈도수와 선택(공감/취소) 여부에 따른 스타일 및 애니메이션을 적용해두신 상태였기 때문이다.
1번 방법을 사용하면, 일일이 키워드가 표시될 span
같은 엘리먼트를 생성한 다음 스타일과 이벤트 핸들러를 property로 설정해줘야 해서 우리가 원하는 UI의 아래 조건들을 만족시키기에 불편하고 코드 가독성도 나빠질 것 같았다.
문제는, 미리 키워드들의 좌표계산을 하려면 두 엘리먼트가 겹치는지 확인해야 하는데, 이때 엘리먼트의 offsetLeft, offsetTop, offsetWidth, offsetHeight
값을 사용하기 때문에 화면에 한 번은 그려야 한다
는 것이었다.
그래서, 보이지 않는(opacity: 0) hidden
컨테이너를 하나 만들어 키워드들을 렌더링하면서 keywordsCoordinates
배열에 키워드가 배치될 좌표를 저장해서, real
컨테이너에서 실제 화면에 보이는 키워드들의 absolute position
으로 지정하는 데 활용하게끔 했다.
결론적으로 1번 방법을 좌표 계산에만 활용하고, 화면에 보이는 워드 클라우드를 띄울 때는 유진님이 작성하신 코드를 확장하는 2번 방향으로 구현했다고 할 수 있겠다.
return (
<div css={KeywordsViewContainer}>
**<div css={HiddenKeywordsContainer} ref={containerRef}></div>**
**<div css={RealKeywordsContainer}>**
{**Object.keys(keywordsCoordinates)**.map((keyword) => {
const keywordObject: Keyword = { keyword, count: keywordsCoordinates[keyword].count };
return (
<div
key={`${questionId}-${keyword}`}
css={[
KeywordStyle,
keywordStyleMap(selectedKeywords.has(keyword))[
getKeywordGroup(keywordObject, Object.keys(keywordsCoordinates).length, prefixSumMap[questionId])
],
**{
left: keywordsCoordinates[keyword].x,
top: keywordsCoordinates[keyword].y
}**
]}
onClick={() => {
selectedKeywords.has(keyword) ? unpickKeyword(keyword) : pickKeyword(keyword);
}}
>
{keywordObject.keyword}
</div>
);
})}
</div>
</div>
);
이렇게 하니 hidden 컨테이너에서와 달리 real 컨테이너에서는 jsx map 문법으로 렌더링하기 때문에 가중치에 따른 커스텀 스타일과 애니메이션을 적용하기 편리했다는 장점은 있었는데, 화면에 워드 클라우드를 두 개 렌더링하고 있어서 흔히들 이야기하듯 야매로 해결했다는 느낌을 지울 수 없어 더 좋은 방법을 고민하고 있다..
그래도 별도의 워드클라우드 라이브러리에 의존하지 않고 아래 조건들을 가진 UI를 css + js로 구현하면서 css position과 element offset 등의 개념에 대해 학습할 수 있는 유익한 도전이었다!