본문 바로가기
개발/리액트

map 함수를 이용한 객체 배열에 새로운 속성 추가

by ^..^v 2023. 2. 27.
728x90
반응형

서버로 부터 받아 온 객체 배열 형식의 데이터에 새로운 속성을 추가해서 사용하는 예시입니다. 

 

데이터 파일 정의

먼저 서버에서 제공하는 데이터를 파일로 만듧니다. 실제 환경에서는 별도의 REST API를 통해서 제공되나 여기에서는 편의를 위해 정적 리소스를 저장하는 public 디렉터리에 JSON 파일을 추가해서 구현합니다. 해당 파일은 http://localhost:3000/images.json 형식으로 요청해 사용할 수 있습니다.

[
    {
        "fileSizeBytes": 1479118,
        "url": "https://random.dog/831a74df-8de4-4150-a70f-12bd984f4bb4.JPG"
    },
    {
        "fileSizeBytes": 75613,
        "url": "https://random.dog/2b77b03c-3073-454e-957b-867580b3d005.jpg"
    },
    {
        "fileSizeBytes": 75171,
        "url": "https://random.dog/b1a59f58-452a-4d97-82bb-a70d75c33090.JPG"
    },
    {
        "fileSizeBytes": 144344,
        "url": "https://random.dog/ba7d232f-e40c-40bb-a26b-3f0b5cd0e68a.jpg"
    },
    {
        "fileSizeBytes": 71939,
        "url": "https://random.dog/a4c58dab-d556-4629-a7f6-194be963564e.jpg"
    },
    {
        "fileSizeBytes": 941170,
        "url": "https://random.dog/1d5a4ce5-8854-46ce-97d5-82f7e4cc7296.png"
    },
    {
        "fileSizeBytes": 137760,
        "url": "https://random.dog/0f476473-2d8b-415e-b944-483768418a95.jpg"
    },
    {
        "fileSizeBytes": 4246562,
        "url": "https://random.dog/09d5ab57-963d-450b-95e4-7a6955bc38ba.gif"
    },
    {
        "fileSizeBytes": 57171,
        "url": "https://random.dog/b8acf898-dc54-4ff3-973b-a0e00e567b8c.jpg"
    }
]

public/images.json 파일

 

스타일 정의

App.css 파일에 출력 스타일을 정의합니다. selected 클래스가 설정되면 테두리의 색깔이 빨간색으로 변경됩니다. 

body {
  padding: 10px;
}

* { 
  box-sizing: border-box;
}

img {
  width: 100px;
  height: 100px;
  margin: 3px;
  border: 3px solid gray;
  border-radius: 6px;
  cursor: pointer;
}

.selected {
  border: 3px solid red;
}

 

상태 변수 정의 및 화면 구성

App.js 파일에 서버로 부터 가져 온 데이터를 저장할 상태 변수와 셋터 함수를 정의하고, 상태 변수의 값을 출력하는 코드를 추가합니다. images는 객체 배열 형식의 값을 저장하는 상태 변수로, 앞에서 정의한 데이터 파일을 가져와서 저장하고,  저장된 값은 map 함수를 이용해 <img> 태그를 출력하는데 사용됩니다.

import './App.css';
import { useState } from 'react';

const App = () => {
  const [images, setImages] = useState([]);

  return (
    <>
      <div>
      {
        images.map((image, index) => <img key={index} src={image.url} className={image.selected ? 'selected' : ''} />)
      }
      </div>
      <div>
        <button>선택한 이미지 전송</button>
      </div>
    </>
  );    
};

export default App;

 

데이터 조회 및 상태 변수 설정

컴포넌트가 마운트될 때 서버로 부터 데이터를 가져와 셋터 함수를 이용해서 상태 변수 images의 값을 변경합니다. 이때, map 함수와 객체 비구조화를 이용해 서버에서 가져 온 값에 새로운 seleced 속성을 추가합니다. 

import axios from 'axios';
import { useEffect } from 'react';

	... (생략) ...
  
useEffect(() => {
    axios.get('http://localhost:3000/images.json')
    .then(response => {
        const newImages = response.data.map(image => ({...image, selected: false}));
        setImages(newImages);
    })
    .catch(error => {
        console.log(error);
    });
}, []);

 

이 과정을 통해서 images 상태 변수의 모든 항목에 새로운 selected 속성이 추가된 것을 확인할 수 있습니다. 

 

이미지 클릭 핸들러 함수 정의 및 이벤트 핸들러 등록

이미지를 클릭했을 때 상태 변수를 변경하는 핸들러 함수를 정의하고 이벤트 핸들러로 등록합니다. 핸들러 함수는 images 상태 변수에 정의된 이미지 배열에서 이미지를 하나씩 가져와 새로운 이미지 배열을 만든 후 셋터 함수를 이용해 상태 변수를 업데이트 합니다. 이때, 클릭된 이미지의 seleced 속성 값을 토글해 줍니다. 

const handlerSelect = e => {
    const newImages = [];
    images.forEach(image => {
      if (image.url === e.target.src) {
        image = {...image, selected: !image.selected};
      } 
      newImages.push(image);
      setImages(newImages);
    });
};

return (
  <>
      <div>
      {
        images.map((image, index) => <img key={index} src={image.url} className={image.selected ? 'selected' : ''} onClick={handlerSelect} />)
      }
      </div>
  </>
);

 

클릭한 이미지의 seleced 속성이 true로 변경된 것을 확인할 수 있습니다.

 

map 함수로 단순화

이미지 배열에서 이미지를 하나씩 가져와 새로운 이미지 배열을 만드는 과정을 map 함수와 삼항 연산자를 이용해 간략화할 수 있습니다.

const handlerSelect = e => {
    const newImages = images.map(image => ({...image, selected: image.url === e.target.src ? !image.selected : image.selected}));
    setImages(newImages);
};

 

전송 버튼 클릭 핸들러 함수 정의 및 이벤트 핸들러 등록

"선택한 이미지 전송 버튼"을 클릭하면 filter 함수를 이용해 images 상태 변수에서 selected 속성이 true인 객체를 추출해 alert 창으로 출력합니다. 실제 서비스에서는 해당 정보를 요청 본문에 담아서 서버로 전달합니다. 

  const handlerSend = () => {
    const selectedImages = images.filter(image => image.selected);
    alert(`선택 이미지: ${selectedImages.length}개\n${JSON.stringify(selectedImages)}`);
  };

  return (
    <>
				... (생략) ... 
      <div>
        <button onClick={handlerSend}>선택한 이미지 전송</button>
      </div>
    </>
  );    
};

 

실행 결과

 

전체 코드

import './App.css';
import axios from 'axios';
import { useState } from 'react';
import { useEffect } from 'react';

const App = () => {
  const [images, setImages] = useState([]);

  useEffect(() => {
    axios.get('http://localhost:3000/images.json')
    .then(response => {
      const newImages = response.data.map(image => ({...image, selected: false}));
      console.log(newImages)
      setImages(newImages);
    })
    .catch(error => {
      console.log(error);
    });
  }, []);

  const handlerSelect = e => {
    // const newImages = images.map(image => ({...image, selected: image.url === e.target.src ? !image.selected : image.selected}));
    // setImages(newImages);

    const newImages = [];
    images.forEach(image => {
      if (image.url === e.target.src) {
        image = {...image, selected: !image.selected};
      } 
      newImages.push(image);
      console.log(newImages)
      setImages(newImages);
    });
  };

  const handlerSend = () => {
    const selectedImages = images.filter(image => image.selected);
    alert(`선택 이미지: ${selectedImages.length}개\n${JSON.stringify(selectedImages)}`);
  };

  return (
    <>
      <div>
      {
        images.map((image, index) => <img key={index} src={image.url} className={image.selected ? 'selected' : ''} onClick={handlerSelect} />)
      }
      </div>
      <div>
        <button onClick={handlerSend}>선택한 이미지 전송</button>
      </div>
    </>
  );    
};

export default App;
728x90
반응형

댓글