미니 프로젝트 [MbtiColor] (3)

박기범·2024년 9월 22일
0

등록페이지에서 간단한 기능을 구현해보았다.

import AssignColorButton from '../components/AssignColorButton';
import AssignColorHeader from '../components/AssignColorHeader';
import ColorSelectContent from '../components/ColorSelectContent';
import MbtiSelectContent from '../components/MbtiSelectContent';

const AssignColorPage = () => {
  return (
    <div className="flex flex-col h-screen px-[24px] py-[42px] tablet:px-[288px] gap-[50px]">
      <AssignColorHeader />
      <MbtiSelectContent />
      <ColorSelectContent />
      <AssignColorButton />
    </div>
  );
};

export default AssignColorPage;

위와 같이 컴포넌트들을 나누어주었다.

헤더 컴포넌트

X 버튼을 누르면 이전 페이지(메인 페이지)로 넘어가도록 구현해주었다.

MBTI 선택 컴포넌트

MBTI 들을 선택할 때 EI SN TF JP를 따로 분리하여 중복 선택이 되지 않도록 구현해주었다.

export interface MbtiOptions {
  EI: string | null;
  SN: string | null;
  TF: string | null;
  JP: string | null;
} //타입 정의

const mbtiCategories = [
  {
    category: 'EI',
    options: [
      { type: 'E', name: '외향형' },
      { type: 'I', name: '내향형' },
    ],
  },
  {
    category: 'SN',
    options: [
      { type: 'S', name: '감각형' },
      { type: 'N', name: '직관형' },
    ],
  },
  {
    category: 'TF',
    options: [
      { type: 'T', name: '사고형' },
      { type: 'F', name: '감정형' },
    ],
  },
  {
    category: 'JP',
    options: [
      { type: 'J', name: '판단형' },
      { type: 'P', name: '인식형' },
    ],
  },
];

export default mbtiCategories;
//카테고리 생성

//컴포넌트 내부 
...
  return (
    <div className="flex flex-col w-[100%] gap-[20px]">
      <p className="text-[#464E5E] text-[24px] font-medium">MBTI</p>
      {mbtiCategories.map((category) => (
        <div key={category.category} className="grid grid-cols-2 gap-[10px]">
          {category.options.map((item) => (
            <button
              key={item.type}
              type="button"
              onClick={() => handleSelect(category.category as keyof MbtiOptions, item.type)}
              className={`flex rounded-[16px] border-[2px] border-solid h-[76px] py-[14px] px-[40px] justify-center items-center gap-[32px] hover:bg-[#F5F7FB]
                ${
                  selectedOptions[category.category as keyof MbtiOptions] === item.type
                    ? 'bg-[#464E5E] border-[#464E5E] ' // 선택된 항목 스타일
                    : 'bg-white border-[#E7EBF2]' // 비활성화된 항목 스타일
                }`}
            >
              <p
                className={` text-[40px] font-medium 
              ${
                selectedOptions[category.category as keyof MbtiOptions] === item.type
                  ? 'text-white' // 선택된 항목 스타일
                  : 'text-[#464E5E] ' // 비활성화된 항목 스타일
              }
              `}
              >
                {item.type}
              </p>
              <p className="text-[#8892A6] text-[24px] font-medium">{item.name}</p>
            </button>
          ))}
        </div>
      ))}
    </div>
  );

타입과 카테고리를 분리하여
이중 map을 사용하여 중복 선택이 불가하도록 구현해주었다.

컬러 선택 컴포넌트

기존 요구사항은 아이콘을 누르면 랜덤으로 색상을 정해주는 기능이였다.
하지만 내 생각은 이 와는 다르게 내가 원하는 색상을 지정할 수 있도록 해야 MBTI별로 원하는 색깔에 대한 조사가 될 것이다.

따라서 색깔을 선택할 수 있는 라이브러리 'react-colorful'가 있어 이것을 사용해야겠다고 생각했다.

따라서 아이콘을 누르면 모달창 -> react-colorful HexColorPicker 컴포넌트 -> 색깔 선택 -> 랜더링 흐름을 생각해놓고 구현하였다.

또한 toastify를 사용하여 사용자로 하여금 선택하였는지 판단을 하게끔 해주었다.

모달 코드

import React, { useState, useEffect } from 'react';
import { HexColorPicker } from 'react-colorful';
...

interface SelectColorModalProps {
  closeModal: () => void;
}

const SelectColorModal = ({ closeModal }: SelectColorModalProps) => {
 ...

  return (
    <div className="fixed inset-0 bg-black bg-opacity-50 flex justify-center items-center">
      <div className="bg-white p-8 rounded-lg shadow-lg max-w-sm w-full h-auto">
        <h2 className="text-[30px] font-bold mb-4">컬러 선택하기</h2>
        <p className="text-gray-600 mb-4">원하시는 컬러를 선택해주세요</p>
        <div className="flex justify-center items-center mt-12 mb-12" style={{ transform: 'scale(1.2)' }}>
          <HexColorPicker color={color} onChange={setColor} />
        </div>
        <button
          type="button"
          className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
          onClick={closeModal}
        >
          선택
        </button>
      </div>
    </div>
  );
};

export default SelectColorModal;

컴포넌트 코드

import React, { useState } from 'react';
import { toast } from 'react-toastify';
import SelectColorModal from './modal/SelectColorModal';
...

const ColorSelectContent = () => {
  const [isOpenModal, setIsOpenModal] = useState(false);
 ...

  const handleOpenModal = () => {
    setIsOpenModal(true);
  };

  const handleCloseModal = () => {
    toast.success('색상이 선택되었습니다.');
    setIsOpenModal(false);
  };

  return (
    <div className="flex flex-col gap-[15px]">
      <div className="flex gap-[3px] items-center">
        <p className="text-[#464E5E] text-[24px] font-medium">컬러</p>
        <button type="button" className="p-[8px] inline-flex rounded-[8px] bg-[#F5F7FB]" onClick={handleOpenModal}>
          <img src="/icons/repeat.svg" alt="랜덤 아이콘" />
        </button>
      </div>
      <div className="inline-flex p-[25px] justify-between items-center rounded-[16px] border-[2px] border-solid border-[#E7EBF2]">
        <p className="text-[#8892A6] text-[24px] font-medium">{selectedColor}</p>
        <div
          style={{ backgroundColor: selectedColor }}
          className="w-[40px] h-[40px] rounded-[8px] border-[3px] border-solid border-[#E8E8E8]"
        />
      </div>
      {isOpenModal && <SelectColorModal closeModal={handleCloseModal} />}
    </div>
  );
};

export default ColorSelectContent;

마지막 남은 버튼 컴포넌트는 다음 게시물에서 zustand와 같이 설명할 것이다.

profile
프론트엔드 개발공부를 하고있습니다.

0개의 댓글

관련 채용 정보