프로젝트/Next+TypeScript

[Taskify] Tag 이슈 수정

dev-hpk 2024. 12. 23. 14:59

카드의 태그 관련 이슈가 발생했습니다.

카드 생성 POST API에 태그 색상과 관련된 속성이 없어서 생긴 문제인데 확인해 보겠습니다.

 

🚫 문제 상황

화면이 리렌더링 될 때마다 태그의 색상이 랜덤 하게 변경됩니다.

카드를 생성하는 POST API에 태그 색상에 대한 옵션이 없어서 발생한 문제입니다.

처음 작업할 때 태그 컴포넌트가 렌더링 될 때 정해진 5개 색상 중 랜덤하게 설정되도록 만들었거든요...😅

const getTagColor = (styles: Record<string, string>): string => {
  if (!bgTag || bgTag.length === 0) return '';
  const idx = Math.floor(Math.random() * bgTag.length);
  return styles[bgTag[idx]];
};
function Chip({ children, chipType }: PropsWithChildren<ChipProps>) {
  const className = clsx(
    styles[chipType],
    chipType === 'tag' && getTagColor(styles),
  );

  return (
    <span className={className}>
      {children}
    </span>
  );
}

 

스크럼 회의 때 많은 논의를 했고 결론이 나진 않았지만, 다음과 같은 의견들이 나왔습니다.

  • 태그의 배경 색상을 제거하자.
  • 카드 정보를 Redux로 전역 관리하자.
  • CSS의 nth-child() 선택자로 태그의 순서에 따라 색상을 결정하자.

의견이 좁혀지지 않았고, 그 이유는 다음과 같았습니다.

  • 태그의 배경 색상을 제거하면 기획 요건을 충족하지 못하니 기획서대로 구현하자.
  • 카드 정보를 Redux로 전역 관리하는 것은 불필요한 리소스를 증가시키는 것 같다.
  • CSS의 nth-child() 선택자를 사용하면, 태그를 삭제하면 색상이 변경되어 일관성이 없는 것 같다.

💡 해결 방법

현재 태그(Chip 컴포넌트)가 렌더링 될 때 랜덤으로 색상을 생성하는 유틸 함수를 이용하면 될 것 같다는 아이디어가 생각나서 팀원분들께 적용해 보겠다고 말씀드렸습니다😄

 

서버에 카드 생성(POST) 요청을 보낼 때 태그만 보내는 것이 아니라 태그에 색상을 추가해서 보내는 것입니다.

간단히 말하자면 태그를 생성하면 태그 문자열 뒤에 랜덤 한 색상을 추가하는 거죠!

 

Chip 컴포넌트

const getTagColor = (): string => {
  if (!bgTag || bgTag.length === 0) return '';
  const idx = Math.floor(Math.random() * bgTag.length);
  return bgTag[idx];
};
function Chip({ children, chipType, color }: PropsWithChildren<ChipProps>) {
  const className = clsx(styles[chipType], chipType === 'tag' && styles[color]);

  return (
    <span className={className}>
      {children}
    </span>
  );
}

 

기존 Chip 컴포넌트와 다르게 color를 props로 받아오게 수정했습니다.

태그(Chip 컴포넌트)를 렌더링 하는 페이지들도 수정해 보겠습니다.

 

Chip 컴포넌트 렌더링 페이지

const handleTagInput = (e: React.ChangeEvent<HTMLInputElement>) => {
    const newTag = e.target.value.trim();
    const color = getTagColor();
    const coloredTag = `${newTag} ${color}`;

    if (!newTag || tags.includes(newTag)) return; // 빈 문자열 또는 중복 태그 방지
    onAddTag(coloredTag);
    e.target.value = '';
};

 

  • const coloredTag = `${newTag} ${color}` : 태그를 템플릿 리터럴(``)을 사용해 태그 색상 형식으로 tag 배열에 저장했습니다. tag 배열은 카드 생성(POST) 요청에 request body에 포함되어 전송됩니다.
{tags.map((tag) => {
    const [tagText, tagColor] = tag.split(' ');
	return (
    	<Chip key={`${cardId}_tag_${tag}`} chipType="tag" color={tagColor}>
            {tagText}
        </Chip>
    );
})}

 

  • const [tagText, tagColor] = tag.split(' ') : 서버에서 받은 tag 데이터를 공백을 기준으로 text와 color로 구조 분해 할당 했습니다.

해결한 줄 알았지만, 이 방법도 문제가 있네요. 사용자가 태그를 입력할 때 공백을 추가해서 보내는 경우를 고려 못했습니다. 

공백을 추가해서 입력하면 아래와 같이 나옵니다...

 

✨ 최종 해결 방법

사용자가 태그에 사용하지 않을 것 같은 특수 문자를 구분자로 사용하겠습니다.

태그 입력 시 구분자로 사용한 특수 문자를 포함 못하게 하는 것도 필수겠죠?

const handleTagInput = (e: React.ChangeEvent<HTMLInputElement>) => {
    const newTag = e.target.value.trim();
    const color = getTagColor();
    const coloredTag = `${newTag}^${color}`;

    if (!newTag || tags.includes(newTag) || newTag.includes('^')) return; // 빈 문자열 또는 중복 태그 방지
    onAddTag(coloredTag);
    e.target.value = '';
  };
{tags.map((tag) => {
    const [tagText, tagColor] = tag.split('^');
	return (
    	<Chip key={`${cardId}_tag_${tag}`} chipType="tag" color={tagColor}>
            {tagText}
        </Chip>
    );
})}

 

 

 

[Taskify] 이미지 확장자 제한 추가

오전 스크럼 회의 때 카드 이미지에 대한 이슈가 있었습니다. 담당 팀원분이 바쁜 관계로 제가 수정하기로 했습니다.  제가 작성한 로직은 아니지만, 서로서로 돕는 게 팀이죠😊   🚫 문제 상

dev-hpk.tistory.com

 

 

[Taskify] 할 일 카드 모달 컴포넌트 (feat. optimistic update)

오늘은 대시보드 상세에서 카드의 상세 모달을 작업해 봤습니다! 우선 전반적인 코드를 먼저 보여드리고 작업하면서 있었던 문제들과 해결한 방법에 대해서 설명해 볼게요!(컴포넌트 구조보다

dev-hpk.tistory.com