많은 웹 애플리케이션에서 종종 한 드롭다운에서 옵션을 선택하면 다른 드롭다운에서 새로운 옵션 세트가 해제되는 양식을 만납니다. 종속 또는 계층적 드롭다운으로 일반적으로 알려진 이러한 연결된 드롭다운은 원활하고 직관적인 양식 작성 경험을 만드는 데 중요한 역할을 합니다.

나라를 선택하여 해당 주를 나타내거나 특정 항목을 표시하기 위해 제품 범주를 선택하는 경우, 이러한 드롭다운은 모든 사람을 위해 복잡한 선택을 간단하게 만듭니다. 개발자들에게는 종속 드롭다운을 구현하는 것이 논리, 사용성 및 동적 데이터 처리를 결합한 실용적인 도전 과제입니다.

이 자습서에서는 React 애플리케이션에서 이러한 유형의 드롭다운을 구현하는 방법을 배우게 됩니다.

목차

의존 드롭다운이란?

의존 드롭다운은 한 드롭다운에서 선택한 내용에 따라 다른 드롭다운의 가능한 옵션을 결정하는 UI 요소입니다. 예를 들어, 두 개의 드롭다운을 갖는 시나리오를 고려해보세요:

  1. 국가 드롭다운: 사용자가 나라를 선택합니다.

  2. 도시 드롭다운: 선택한 나라에 따라 두 번째 드롭다운에 표시될 가능한 도시 목록이 그에 따라 필터링됩니다.

이러한 상호작용은 복잡하고 맥락에 맞는 데이터 입력이 필요한 양식에 중요합니다.

의존 드롭다운은 어떻게 작동하나요?

의존 드롭다운은 첫 번째 드롭다운에서 선택한 값에 따라 두 번째 드롭다운의 옵션을 동적으로 업데이트하여 작동합니다. 이 동적 변경은 일반적으로 다음을 통해 달성됩니다:

  1. 사용자 입력을 수신: 사용자가 첫 번째 드롭다운에서 옵션을 선택하면(onChange와 같은) 이벤트가 발생하여 상태를 업데이트하는 함수가 트리거됩니다.

  2. 새 데이터 가져오기: 이 업데이트된 상태는 기존 데이터를 필터링하거나 새 옵션 목록을 가져오기 위해 API 호출을 할 수 있습니다.

  3. 새 데이터 렌더링: 그런 다음 두 번째 드롭다운이 새로운 옵션으로 업데이트되어 사용자에게 관련된 선택지를 제공합니다.

리액트에서 종속 드롭다운 만들기 단계

단계 1: 리액트 프로젝트 설정

리액트가 처음이고 따라가고 싶다면, Vite 문서를 확인하고 단계를 따라 리액트 프로젝트를 만드세요. 완료되면 여기로 돌아와 계속 진행합시다.

이미 사용할 리액트 프로젝트가 있다면, 좋습니다.

단계 2: 컴포넌트 구조화

간단하게, 첫 번째 드롭다운에서 국가를 선택하고 선택한 국가에 기반한 도시를 표시하는 두 수준 종속 드롭다운을 만든다고 가정해 봅시다.

또한 국가 드롭다운에서, 국가 옵션에 포함되지 않은 국가 이름을 입력할 수 있는 다른 옵션을 추가할 것입니다. 사용자는 그 후 국가를 텍스트 입력란에 입력할 수 있습니다.

먼저, DependentDropdown.js 또는 DependentDropdown.jsx라는 새 파일을 생성하십시오. 이 파일 안에 DependentDropdown이라는 함수형 컴포넌트를 정의하십시오.

이제 의존 드롭다운을 구축하기 위해 다음 단계를 따라갈 것입니다:

데이터 저장을 위한 변수 선언

국가 및 도시의 값에 대한 정적 데이터를 생성해야 합니다:

  // 정적 국가 데이터
  const countries = [
    { id: 1, name: 'USA' },
    { id: 2, name: 'Canada' },
    { id: 3, name: 'Other' },
  ];

  // 국가에 해당하는 정적 도시 데이터
  const cities = {
    USA: ['New York', 'Los Angeles', 'Chicago'],
    Canada: ['Toronto', 'Vancouver', 'Montreal'],
  };
  • countriesidname 속성을 가진 객체 배열입니다.

  • cities는 국가 이름을 키로, 도시 배열을 값으로 가지는 객체입니다.

상태 변수 선언

국가 또는 도시 선택마다 선택한 값을 추적할 수 있어야 합니다. 국가 선택 후 도시 옵션을 채울 수 있어야 합니다. 이를 위해 몇 가지 상태를 선언해야 합니다.

만약 상태 개념이 새로운 것이라면, 상태에 대한 내 기사를 읽어볼 수 있습니다 여기.

  const [selectedCountry, setSelectedCountry] = useState('');
  const [availableCities, setAvailableCities] = useState([]);
  const [selectedCity, setSelectedCity] = useState('');
  const [otherCountry, setOtherCountry] = useState('');
  • selectedCountry 상태가 선언되고 초기 값이 빈 문자열로 설정됩니다.

  • availableCities 상태가 선언되고 초기 값이 빈 배열로 설정됩니다.

  • selectedCity 상태가 선언되고 초기 값이 빈 문자열로 설정됩니다.

  • otherCountry 상태가 선언되고 초기 값이 빈 문자열로 설정됩니다.

이벤트 처리

드롭다운에서 선택을 하는 과정에서 몇 가지 동작을 수행하고 싶습니다. 이벤트 핸들러는 이 경우 이벤트인 onChange 이벤트에 대해 그것을 할 수 있게 해줍니다.

  const handleCountryChange = (e) => {
    const country = e.target.value;
    setSelectedCountry(country);
    setAvailableCities(cities[country] || []);
    setSelectedCity(''); 
     if (country !== 'Other') {
      setOtherCountry('');
    }
  };

handleCountryChange 함수에서 다음과 같은 일이 벌어집니다:

  • 드롭다운에서 선택한 옵션의 값(선택된 국가)을 가져옵니다.

  • setSelectedCountry는 새로 선택된 국가로 상태 변수(selectedCountry)를 업데이트합니다.

  • cities[country]는 선택된 국가에 대한 도시 목록을 cities 객체에서 찾습니다.

    • cities[country]를 찾으면 해당 도시 목록을 가능한 도시로 설정합니다.

    • 선택한 국가에 대해 도시를 찾을 수 없는 경우(cities[country]가 정의되지 않음) || []는 도시를 표시할 때 오류를 방지하기 위해 빈 배열([])을 후퇴로 사용합니다.

  • 사용자가 국가 선택을 변경하면, setSelectedCity 함수는 selectedCity를 빈 문자열로 재설정합니다.

  • 선택된 국가가 ‘Other’가 아닌 경우, otherCountry 상태는 빈 문자열로 재설정됩니다. 이는 사용자가 이전에 “Other” 입력란에 무언가를 입력했을 때, 다른 국가(예: “USA” 또는 “Canada”)를 선택하면 해당 텍스트가 지워지도록 보장합니다.

‘Other’ 국가 선택의 경우, 입력란에 입력된 값만 추적하면 됩니다. setOtherCountry 함수는 입력된 값을 업데이트합니다. 이렇게 수행됩니다:

  const handleOtherCountryChange = (e) => {
    setOtherCountry(e.target.value);
  };

도시 변경에 대해 많은 작업이 필요하지 않습니다. 선택한 국가에 따라 표시되는 도시가 결정되기 때문입니다. 수행할 일은 드롭다운에서 선택한 옵션의 값인 도시를 선택한 값인 selectedCity를 업데이트하는 것뿐입니다.

React에서 업데이터 함수는 상태 변수의 업데이트를 처리하므로, 이 경우에는 setSelectedCity가 이를 처리합니다.

handleCityChange 함수는 다음과 같을 것입니다:

  const handleCityChange = (e) => {
    setSelectedCity(e.target.value);
  };

JSX 반환

DependentDropdown 컴포넌트는 세 가지 주요 요소를 렌더링합니다: 국가 드롭다운, 도시 드롭다운, 그리고 국가 텍스트 입력란입니다.

HTML에서 드롭다운은 <select><option> 요소의 조합입니다. 요소의 값을 추적하기 위해 상태 변수를 연결하여 제어할 수 있습니다. 이를 ‘요소 제어’라고 하며, 요소 자체는 React에서 ‘제어 요소’로 참조됩니다.

국가의 <select> 요소를 제어하기 위해 selectedCountry 속성에 value 속성을 부여하고 handleCountryChange 함수를 연결할 것입니다.

     <label htmlFor="country" className='font-bold'>Select Country: </label>
      <select id="country" value={selectedCountry} onChange={handleCountryChange}>
        <option value="">Select a country</option>
        {countries.map((country) => (
          <option key={country.id} value={country.name}>
            {country.name}
          </option>
        ))}
      </select>

또한,

  • <option> 내부에서 countries 배열을 매핑하고 배열의 각 국가 객체에 대해 동적으로 <option>을 생성합니다.

  • 각 국가의 name이 옵션의 텍스트로 표시됩니다.

  • 각 옵션의 key는 해당 국가의 id로 설정되고 value는 해당 국가의 name으로 설정됩니다.

  • key는 다시 렌더링 시 React가 목록을 효율적으로 관리하는 데 도움이 됩니다.

선택한 국가에 따라 도시 드롭다운이 조건부로 렌더링됩니다. ‘기타’ 국가 옵션을 선택한 경우, 사용자가 국가를 지정할 수 있는 텍스트 입력 필드가 표시됩니다. 그렇지 않고 유효한 국가가 선택된 경우 해당하는 옵션을 가진 도시 드롭다운이 표시됩니다.

{selectedCountry === 'Other' ? (
        <>
          <label htmlFor="other-country" className='font-bold'>Please specify the country: </label>
          <input
            id="other-country"
            type="text"
            value={otherCountry}
            onChange={handleOtherCountryChange}
            placeholder="Enter country name"
          />
        </>
      ) : (
        selectedCountry && (
          <>
            <label htmlFor="city" className='font-bold'>Select City: </label>
            <select id="city" value={selectedCity} onChange={handleCityChange}>
              <option value="">Select a city</option>
              {availableCities.map((city, index) => (
                <option key={index} value={city}>
                  {city}
                </option>
              ))}
            </select>
          </>
        )
      )
}

추가로:

  • selectedCountry가 ‘기타’ 옵션인지 확인하고 텍스트 입력을 표시합니다.

  • 텍스트 입력에는 otherCountry 상태와 이에 연결된 handleOtherCountryChange 핸들러 함수가 있습니다.

  • selectedCity 상태 변수에 설정된 상태 변수를 사용하여 도시 <select> 요소를 제어합니다. handleCityChange 이벤트 핸들러도 onChange 이벤트를 처리하기 위해 연결됩니다.

  • availableCities 배열을 매핑하고 배열의 각 도시에 대해 동적으로 <option>을 생성합니다.

  • 각 옵션의 keyindex로 설정되고, valuecity로 설정됩니다.

  • 각 도시는 옵션의 텍스트로 표시됩니다.

우리의 정적 데이터를 사용하여 기능적인 종속 드롭다운을 갖게 되기 위해 해야 할 일은 이것뿐입니다.

여기에 모든 코드가 함께 있습니다:

import React, { useState } from 'react';

const DependentDropdown = () => {
  // 정적 국가 데이터
  const countries = [
    { id: 1, name: 'USA' },
    { id: 2, name: 'Canada' },
    { id: 3, name: 'Other' },
  ];

  // 국가에 해당하는 정적 도시 데이터
  const cities = {
    USA: ['New York', 'Los Angeles', 'Chicago'],
    Canada: ['Toronto', 'Vancouver', 'Montreal'],
  };

  // 선택된 국가, 도시 및 다른 국가 텍스트를 보유하는 상태
  const [selectedCountry, setSelectedCountry] = useState('');
  const [availableCities, setAvailableCities] = useState([]);
  const [selectedCity, setSelectedCity] = useState('');
  const [otherCountry, setOtherCountry] = useState(''); 

  // 국가 변경 처리
  const handleCountryChange = (e) => {
    const country = e.target.value;
    setSelectedCountry(country);
    setAvailableCities(cities[country] || []);
    setSelectedCity(''); 
    if (country !== 'Other') {
      setOtherCountry('');
    }
  };

  // 도시 변경 처리
  const handleCityChange = (e) => {
    setSelectedCity(e.target.value);
  };

  // 다른 국가 입력 변경 처리
  const handleOtherCountryChange = (e) => {
    setOtherCountry(e.target.value);
  };

  return (
    <div className='text-center text-3xl'>
      <h1 className='font-extrabold text-5xl p-10'>Dependent Dropdown Example</h1>

      {/* 국가 드롭다운 */}
      <label htmlFor="country" className='font-bold'>Select Country: </label>
      <select id="country" value={selectedCountry} onChange={handleCountryChange}>
        <option value="">Select a country</option>
        {countries.map((country) => (
          <option key={country.id} value={country.name}>
            {country.name}
          </option>
        ))}
      </select>

      {/* 도시 또는 다른 국가 입력 */}
      {selectedCountry === 'Other' ? (
        <>
          <label htmlFor="other-country" className='font-bold'>Please specify the country: </label>
          <input
            id="other-country"
            type="text"
            value={otherCountry}
            onChange={handleOtherCountryChange}
            placeholder="Enter country name"
          />
        </>
      ) : (
        selectedCountry && (
          <>
            <label htmlFor="city" className='font-bold'>Select City: </label>
            <select id="city" value={selectedCity} onChange={handleCityChange}>
              <option value="">Select a city</option>
              {availableCities.map((city, index) => (
                <option key={index} value={city}>
                  {city}
                </option>
              ))}
            </select>
          </>
        )
      )}
    </div>
  );
};

export default DependentDropdown;

단계 3: 구성 요소 사용

최종 결과를 얻으려면 DependentDropdown 구성 요소를 App.js 또는 App.jsx로 가져와서 App 구성 요소의 반환 섹션에 배치해야 합니다.

import DependentDropdown from './DependentDropdown'

function App() {

  return (
    <DependentDropdown/>
  )
}

export default App

어떤 명령어를 입력하여 애플리케이션을 실행하는 것을 잊지 마세요:

npm start // create react app용
npm run dev // react vite app용

마지막으로, 브라우저에 렌더링되어야 하는 내용은 다음과 같습니다:

동적 데이터 처리 (API 요청)

실제 응용 프로그램에서 드롭다운 목록은 정적이 아닐 수 있습니다. 대신, API로부터 또는 API 역할을 하는 JSON 파일에서 가져올 수 있습니다.

이 예시에서는 종속 드롭다운을 채우기 위해 JSON 파일에서 데이터를 읽어옵니다. 이 방법에는 몇 가지 이점이 있습니다.

  • 데이터베이스 부하 감소: 정적 JSON 파일(또는 미리 로드된 파일)을 사용함으로써 드롭다운을 채우는 데 필요한 데이터베이스 쿼리 수를 줄일 수 있습니다. 특히 드롭다운 옵션이 상당히 정적이고 자주 변경되지 않는 경우에 유용합니다.

  • UI 렌더링 속도 향상: 데이터가 이미 클라이언트 측에 있기 때문에 사용자가 드롭다운과 상호 작용할 때마다 서버에 왕복 요청할 필요가 없습니다. 이로써 인터페이스가 반응이 더 빠르게 느껴질 수 있습니다.

우리의 JSON 파일에는 주(State)와 지방정부 지역(Local Government Areas)이 포함되어 있습니다. 이는 국가와 도시의 해당 항목입니다.

JSON 파일의 데이터는 객체 배열로 표현되며, 각 객체는 state, alias, 그리고 lgas 키를 가지고 있습니다. ‘lgas’ 키는 배열을 포함합니다.

다음은 그 표현 방법입니다:

[
  {
    "state": "Adamawa",
    "alias": "adamawa",
    "lgas": [
      "Demsa",
      "Fufure",
      "Toungo",
      "Yola North",
      "Yola South"
    ]
  },
  {
    "state": "Akwa Ibom",
    "alias": "akwa_ibom",
    "lgas": [
      "Abak",
      "Uruan",
      "Urue-Offong/Oruko",
      "Uyo"
    ]
  },
//나머지 객체들
]

이 API를 사용하여 동적 종속 드롭다운을 만드는 방법은 이전 예제와 크게 다르지 않지만 약간의 수정이 있습니다.

다음은 JSON 파일에서 데이터를 가져와 사용하는 방법입니다:

import React, { useEffect, useState } from "react";

function DependentDropdown() {
//전역 상태 변수 선언
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);

//useEffect 훅을 사용하여 데이터 가져오기
  useEffect(() => {
    fetch("nigeria-state-and-lgas.json") //URL로 설정된 JSON 파일
      .then((res) => res.json())
      .then((data) => {
        setData(data);
        setLoading(false);
      })
      .catch((error) => {
        console.error("Error fetching data:", error);
        setLoading(false);
      });
  }, []);
  return loading ? <div>Loading...</div> : <Form data={data} />;

}
//데이터를 props로 수신하는 form
function Form({ data }) {

//로컬 상태 변수 선언
  const [selectedState, setSelectedState] = useState("");
  const [selectedLga, setSelectedLga] = useState("");
  const [showList, setShowList] = useState(false);
  let sortedData = data.slice().sort((a, b) => a.state.localeCompare(b.state));
  const selectedData = sortedData.find((item) => item.state === selectedState);

//상태를 위한 핸들러 함수
  function handleClickState(e) {
    setSelectedState(e.target.value);
    setShowList(true);
  }
//LGA를 위한 핸들러 함수
  function handleClickLga(e) {
    setSelectedLga(e.target.value);
  }

  return (
    <div>
  <form onSubmit={handleFormSubmit}>
    <div>
      {/* 이름 */}
      <div>
        <label htmlFor="firstName">First Name</label>
        <input type="text"
          id="firstName"
          name="firstName"
          placeholder="Enter your first name"/>
      </div>

      {/* 성 */}
      <div>
        <label htmlFor="lastName">
          Last Name
        </label>
        <input
          type="text"
          id="lastName"
          name="lastName"
          placeholder="Enter your last name"/>
      </div>
    </div>

    <div>
      <div>
        <select value={selectedState} onChange={handleClickState} name="state">
          <option value="" disabled>Choose your state</option>
          {sortedData.map((data) => (
            <option key={data.alias} value={data.state}>
              {data.state}
            </option>
          ))}
        </select>
      </div>
      {selectedData && showList && (
        <select value={selectedLga} onChange={handleClickLga} name="lga">
          <option value="" disabled>{`Choose your LGA in ${selectedState}`}</option>
          {selectedData.lgas.map((lgass) => (
            <option key={lgass} value={lgass}>
              {lgass}
            </option>
          ))}
        </select>
      )}

    </div>
    <div>
        <button type="submit">
          Submit
        </button>
      </div>
  </form>
</div>
  );
}

export default DependentDropdown;

여기서 주요 수정 사항은 useEffect 훅을 사용하여 데이터를 가져오는 것으로, 초기 렌더링 시에만 상태와 LGA 데이터를 가져옵니다

브라우저에서 이렇게 렌더링됩니다:

결론

이 튜토리얼에서 React를 사용하여 정적 및 동적 데이터를 활용한 종속 드롭다운을 만드는 방법을 배웠습니다. 이제 React 애플리케이션에서 이러한 종류의 드롭다운을 사용할 수 있습니다.

이 문서가 도움이 되었다면 프로그래밍 관련 기사와 포스트를 더 읽으려면 LinkedIn에서 저와 연락하실 수 있습니다.

다음 글에서 뵙겠습니다!