halang-log
💻 Frontend

velog에서 사용하는 태그 입력 기능 만들기

date
Sep 16, 2022
slug
make-tag
author
status
Public
tags
tag
summary
velog에서 사용하는 태그 입력 기능 만들기
type
Post
thumbnail
category
💻 Frontend
updatedAt
Nov 22, 2023 12:54 AM
언어
 
notion image
 

개요

이전 포스팅에 보여드렸던 모달창입니다. 여기서 태그를 입력하는 필드가 있습니다. 이를 velog에 있는 태그 입력하는 것과 비슷하게 만들어보겠습니다.

구현하기

CreateRoomForm.tsx

우선 모달창 안에 있는 form에 대한 컴포넌트입니다. 본문 내용과 관련 없는 부분들은 생략 및 삭제하였습니다.
import styled from 'styled-components'; import React from 'react'; import TagsComponent from './Tag'; export default function CreateRoomForm({ inputs, onChange, onKeyPress, onDeleteTag, }) { const { newTag, tags } = inputs; return ( <Form onSubmit={mutate}> ... <Section> <TagsComponent newTag={newTag} tags={tags} onChange={onChange} onKeyPress={onKeyPress} onDeleteTag={onDeleteTag} /> </Section> ... </Form> ); } const Form = styled.form` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; width: 100%; color: #b0b8c1; font-size: 1.4rem; font-family: IBMPlexSansKRRegular, Arial; overflow-y: auto; ::-webkit-scrollbar { display: none; } `; const Section = styled.div` display: flex; flex-direction: column; align-items: center; margin-top: 2.9rem; width: 100%; `;

TagComponent.tsx

import styled from 'styled-components'; import { ReactComponent as DeleteTag } from '../../../assets/svg/deleteTag.svg'; export default function TagsComponent({ newTag, tags, onChange, onKeyPress, onDeleteTag, }) { return ( <> <Label htmlFor="tag">태그</Label> <TagComponent> {tags.map((myTag, index) => ( <Tag onClick={(e) => onDeleteTag(e, index)} key={myTag.concat(`${index}`)} > <TagName key={myTag}>#{myTag}</TagName> <TagButton type="button"> <DeleteTag /> </TagButton> </Tag> ))} <Input id="tag" name="newTag" type="text" value={newTag} onChange={onChange} onKeyPress={onKeyPress} placeholder="태그를 입력하세요." /> </TagComponent> </> ); } const Label = styled.label` width: 100%; line-height: 2.9rem; `; const TagComponent = styled.div` display: flex; align-items: center; border-radius: 0.6rem; width: 100%; flex-wrap: wrap; gap: 1.2rem; `; const Input = styled.input` height: 4.9rem; min-width: 15rem; max-width: 15rem; background: transparent; outline: none; color: #f9fafb; font-size: 1.5rem; background-color: #191f28; border-radius: 0.6rem; padding: 0 1.6rem; `; const Tag = styled.div` display: flex; align-items: center; border-radius: 0.6rem; background-color: rgba(69, 178, 107, 0.1); padding: 0.5rem 1rem; cursor: pointer; &:hover { background-color: rgba(69, 178, 107, 0.2); } `; const TagButton = styled.button` cursor: pointer; margin-left: 1rem; display: flex; flex-direction: column; align-items: center; `; const TagName = styled.div` color: rgba(69, 178, 107, 1); font-family: IBMPlexSansKRRegular, Arial; `;
Tag는 태그가 추가되는 곳이고 Input은 태그를 입력할수 있는 입력창입니다. 이 둘을 감싸고 있는 TagComponent는 flex로 설정하였고 태그를 많이 추가하여 길어질때 여러 줄로 생기게 하기 위해 flex-wrap속성을 wrap으로 해주었습니다.
각 Tag에 onClick 이벤트를 추가하여 클릭할 때 해당 태그가 삭제되게 하였습니다. Input에 onKeyPress 속성으로 사용자가 스페이스바나 엔터키를 누를경우 해당 input값을 tags 배열에 추가하였습니다.
const onKeyPress = (e: React.KeyboardEvent<HTMLElement>) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); if (tags.includes(newTag)) { toast.error('이미 추가된 태그입니다.'); setInputs((prev) => { return { ...prev, newTag: '', }; }); return; } if (newTag !== '') { onAddTag(); } } };
코드로 좀 더 자세히 살펴봅시다.
  1. e.preventDefault()를 작성한 이유는 이벤트 버블링으로 인해 발생되는 폼 제출을 막기위함 입니다.
  1. 입력한 태그가 이미 존재하는 태그인지 확인해줍니다. 만약 이미 존재한다면 toast 라이브러리를 이용해 알림 메세지를 보여주고 newTag(사용자가 input창에 입력한 값)를 ''로 초기화해줍니다.
  1. 입력한 태그가 이미 존재하지 않는 태그라면 newTag가 빈 스트링인지 확인 후 아닐 경우 tags배열에 추가해줍니다.
toast라이브러리는 공식 사이트를 참고하시면 좋을 것 같습니다.

마치며

원래는 방 이름을 입력하는 칸처럼 input을 설정하고 그 아래에 사용자가 추가하는 태그들을 보여주었는데 이렇게 바꾼게 더 이쁜것 같네요ㅎㅎ