본문 바로가기
개발개발/WorkRoot_워크루트

[리팩토링] LinkHere : 모달 UX를 개선해보자

by yelimu 2025. 2. 4.

LinkHere 서비스에서 모달에서 사용자 경험을 개선하기 위해 추가한 기능들은 아래 4가지 이다. (기능이라고 하기에도 소소한..ㅎ)

 

1. 링크 추가 시 폴더 default 선택 

2. 링크 추가 시 폴더가 없다면 폴더 생성 모달을 띄우기

3. 모달에 값 입력 시 input에 포커스 

4. 모달에 값 입력 후 '엔터' 눌렀을때도 제출하기 


1. 링크 추가 시 폴더 default 선택 

2. 링크 추가 시 폴더가 없다면 폴더 생성 모달을 띄우기

기존 서비스에서 유저가 회원가입을 하고 서비스를 이용하려고 할때 마주치는 화면은 바로 이것이다.

당연히 추가하기 버튼을 누르게되고, 그 다음에는 링크가 추가되는 것이 유저가 기대하는 자연스러운 동작이다.

그러나 링크를 추가할 폴더를 선택해야하기 때문에 에러 토스트를 만나게 된다. 

게다가 다시 링크를 추가하기 위해서는, 모달을 끄고, 폴더를 추가하고, 다시 똑같은 링크를 입력하고, 그 다음에 아래와 같은 모달에서 폴더를 선택하게 된다. 

 

링크를 저장할 폴더를 선택한다

적어놓고 보니 너무너무 번거로운 일이다. 

게다가 한번 더 폴더를 선택해줘야한다.

요러케

 

생성되어있는 폴더가 없는 동일한 상황에서

링크 입력창을 눌렀을때 폴더 생성창이 먼저 뜨고, 폴더가 생성되면 링크를 입력하도록 한다. 

(시연하다 보니 한단계 더 줄여야겠다는 생각이 든다. 링크 입력 -> 폴더 생성 -> 바로 폴더 선택)

또한 폴더 이름은 더 길게 해도 되겠다 ㅎㅎ

3. 모달에 값 입력 시 input에 포커스 

4. 모달에 값 입력 후 '엔터' 눌렀을때도 제출하기

 

위 이미지에서도 확인할 수 있는 부분이다. 

 

다른 서비스를 사용하면서 밴, 유저로서의 습관이라고 할까? 

입력창이 나타나면 키보드로 손이 바로 향한다. 값을 입력하면 엔터를 치게된다.

입력창을 한번 클릭해야 한다거나, 엔터가 먹지않아서 마우스로 버튼을 클릭하는 것이 

별거 아닌데, 손을 멈칫 - 하게 되는 포인트랄까...? 

사소한것들이지만 그렇기에 더욱 추가해야겠다 생각했다

아, 추가로 모달을 부드럽게 띄우는 fade in 애니메이션도 추가했다 ^- ^



<코드 / >

링크 추가 시 폴더 default 선택 

 

폴더 디폴트값을 주기 위해 null 로 관리하던 selectedId (폴더 id ) state 초기값을 

폴더 list 의 첫번째 요소로 변경해주었다. 

 

selectedId 상태값은 folderList 컴포넌트로 전달되고

해당 아이템에 음영 스타일이 적용된다 

// AddLinkModal.tsx

const AddLinkModal = ({
  list,
  link,
} : Props) => {
  const [selectedId, setSelectedId] = useState<number | null>(
    list[0]?.id || null
  );

  const handleSubmit = async () => {
  // api 요청 로직 
      }

  const handleClickFolderItem = (id: number) => {
    if (selectedId === id) {
      setSelectedId(null);
    } else {
      setSelectedId(id);
    }
  };
  return (
    <ModalContainer title="폴더에 추가" subtitle={link}>
      <FolderList
        list={list}
        selectedId={selectedId}
        onClick={handleClickFolderItem}
      />
      <SubmitButton
        type="button"
        onClick={handleSubmit}
      >
        추가하기
      </SubmitButton>
    </ModalContainer>
  );
};
export default AddLinkModal;

 

 

링크 추가 시 폴더가 없다면 폴더 생성 모달을 띄우기

// AddLinkInput.tsx

const handleClickWithout = () => {
    if (!user) {
      toast.error("로그인이 필요합니다.");
      toggleExpanded();
    }
    if (user && (!folderList || folderList.length < 1)) {
      openModal("AddFolderModal");
    }
  };

 

링크를 입력하는 인풋을 클릭했을때 user 정보가 없다면 로그인 할 수 있는 사이드바를 열도록 했다. 

또한 폴더 정보가 없다면 폴더 추가하는 모달을 호출한다. 

 

 

모달에 값 입력 시 input에 포커스 

& 모달에 값 입력 후 '엔터' 눌렀을때도 제출하기

 

모달에 들어가는 input은 모든 모달에서 공통으로 쓰이고 있다.

import { bindClass } from "@/util/bindClass";
import { ChangeEvent, KeyboardEvent, useEffect, useRef } from "react";

const ModalInput = ({
  placeholder,
  name,
  value,
  onChange,
  onEnter,
}: Props) => {

  const ref = useRef<HTMLInputElement>(null);
  useEffect(() => {
    ref.current?.focus();
  }, []);

  const handleKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
    if (e.key === "Enter" && onEnter) {
      onEnter();
    }
  };
  return (
    <input
      ref={ref}
      type="text"
      name={name}
      id={name}
      value={value}
      onChange={onChange}
      onKeyDown={handleKeyDown}
      className={// 생략      )}
      placeholder={placeholder}
    ></input>
  );
};

export default ModalInput;

모달이 열렸을때 인풋에 포커스를 주기 위해서 ref 와 useEffect 를 활용했다. 

컴포넌트가 호출될 때 focus()가 실행된다.

 

input에 onKeyDown 속성에 HandleKeyDown 이벤트핸들러를 추가했다. 

키보드 이벤트를 받아서 e.key = 엔터키라면 상위 컴포넌트에서 받은 onEnter를 실행한다.

return (
    <ModalContainer title="폴더 추가">
      <ModalInput
        placeholder="이름을 입력해주세요"
        name={folderName}
        value={value}
        onChange={handleChange}
        onEnter={handleSubmit}
      />
      <SubmitButton
        type="button"
        onClick={handleSubmit}
        disabled={disabled}
      >
        추가하기
      </SubmitButton>
    </ModalContainer>
  );

onEnter에는 버튼 클릭 시 호출하는 handleSubmit 함수가 동일하게 전달된다.


분명 코드는 간단한거 같은데 매번 코드 작성할때마다 이전에 작성한 코드를 찾아보거나 검색하거나 gpt의 도움을 받게 된다.

언젠가 슈슈슉 이정도쯤이야 하면서 작성하고 싶다.