Dazz6/ABOUT

고마워써 프로젝트 - #2-6. 연하장 신청받기

dazz6 2025. 1. 13. 18:00

연하장을 신청받기 위해 질문을 등록하는 화면이다.

예를 들어 나이 질문을 추가해 두면, 신청하는 사람에게 나이 질문이 출력되고 답변을 등록할 수 있는 식.

구현하면서 답변 방식도 지정하고 싶은 욕심이 있었는데 (날짜, 객관식 등...) 

일단은 전부 주관식으로 간단하게 구현해 보았다

 

질문 추가하기 버튼을 눌러 새로운 질문을 등록하거나,

삭제하고 싶은 질문의 삭제 버튼을 눌러 지울 수 있다.

그때마다 데이터베이스, 서버에 접속하는 건 너무 비효율적이라는 판단이 들어서

  // form이 submit 되었을 때
  // 기존 데이터와 비교하여 수정 / 추가 / 삭제 구분
  // 구분에 맞춰 처리함
  const onSubmit = (formData: { questions: IQuestionItem[] }) => {

    const currentQuestions = formData.questions;

    // 수정된 항목
    const modifiedQuestions = currentQuestions.filter((current) => {
      // id가 있고, 내용이 달라졌을 경우
      const original = initialData.find((item) => item.id === current.id);
      return (
        original &&
        (original.question !== current.question ||
          original.description !== current.description)
      );
    });

    // 추가된 항목
    // id 없는 경우
    const addedQuestions = currentQuestions.filter((current) => !current.id);

    // 삭제된 항목
    // 기존에는 있는 id가 없어진 경우
    const deletedQuestions = initialData.filter(
      (original) =>
        !currentQuestions.some((current) => current.id === original.id)
    );

    try {
      if (modifiedQuestions.length > 0) {
        modifiedQuestions.map((question) =>
          axios.put(`/api/inquiry/${userId}`, question)
        );
      }

      if (addedQuestions.length > 0) {
        addedQuestions.map((question) =>
          axios.post(`/api/inquiry/${userId}`, question)
        );
      }

      if (deletedQuestions.length > 0) {
        deletedQuestions.map((question) =>
          axios.delete(`/api/inquiry/${userId}/${question.id}`)
        );
      }
      setCompleteModal(true);
    } catch (err) {
      console.error(err);
    }
  };

 

이런 방식을 사용했다.

현재 데이터베이스와 사용자가 등록 / 수정 / 삭제한 최종값을 비교하여 axios로 통신하는 방식이다.

모든 값을 서버에 넘기고 서버에서 대조하는 방식도 사용할 수 있을 것 같지만...

웬만하면 클라이언트에서 해결하고 싶었다

 

데이터 처리를 위한 useQuery, useState 사용은

const { isLoading, error } = useQuery<IQuestionItem[]>(
    ["question", userId],
    () => createQuestion(userId),
    {
      // data를 가져오는 데 성공하면 아래 함수 실행
      onSuccess: (data) => {
        if (data.length === 0) {
          // 질문이 하나도 없을 경우 기본 틀 input 세트 하나 제공
          setValue("questions", [
            {
              question: "",
              description: "",
            },
          ]);
        } else {
          setInitialData(data);
          // 수정 / 추가 / 삭제 구분하기 위해 useState에도 저장해 두기
          setValue(
            "questions",
            data.map((item) => ({
              id: item.id,
              inquiryId: item.inquiryId,
              question: item.question,
              description: item.description,
            }))
          );
        }
      },
    }
  );

 

이렇게 설정했다.

useQuery를 간단한 방식으로, 아주 기본적인 기능만을 사용하다가

onSuccess 안에 조건을 주면서 코드가 길어져서 조금 복잡하게 느껴지긴 했다 ㅎ ㅠ

 

그리고 사용자가 신청 링크를 공유해야 하므로 복사 버튼을 만들었다.

링크를 외부로 공유할 때는 보안을 위해 암호화를 권장한다!

링크에 회원 정보의 일부가 들어갈 수도 있기 때문에 웬만하면 난수로 이루어진 고유 링크를 공유하는 것이 좋다.

이는 따로 작성해 두었다!

AES를 사용하여 url 암호화 구현해 보기 https://dazz6study.tistory.com/96

react-copy-to-clipboard 사용해 보기 https://dazz6study.tistory.com/105