
이번 글을 통해 배워갈 내용
- 리액트 이미지 업로드
- 리액트 이미지 필터
- 리액트 이미지 출력

일단 리액트 프로젝트를 만든 다음
npx create-react-app my-app
cd my-app
npm start
기본으로 생성되는
App.js, App.css
파일만 수정하였습니다
state 들과 canvas ref들입니다
const [rawImages, setRawImages] = useState([])
const [rawPreviews, setRawPreviews] = useState([])
const [filteredPreviews, setFilteredPreviews] = useState([])
const canvasRef = useRef(null)
전체 렌더는 아래와 같습니다
return (
<div className="App">
<FileInputView />
<RawImagesView />
<FilteredImagesView />
<MyCanvas />
</div>
);
파일을 업로드합니다
const FileInputView = ()=>{
return <input type="file" onChange={handleFileInput} accept=".jpg, .jpeg, .png"></input>
}
업로드한 파일을 핸들링합니다
const file = e.target.files[0]
if(!isValidFile(file)){
alert("is not valid file")
return
}
addRawImages(file)
addRawPreviews(file)
}
const addRawImages = (file) => {
setRawImages([...rawImages, file])
}
const addRawPreviews = (file) => {
setRawPreviews([...rawPreviews, URL.createObjectURL(file)])
}
const isValidFile = (file)=>{
// todo file validation
return true
}
(누수방지) 메모리 관리를 위해서 useEffect도 추가합니다
useEffect(() => {
// free memory when ever this component is unmounted
if(rawPreviews != null){
return () => rawPreviews.forEach(rp=>URL.revokeObjectURL(rp))
}
}, [rawPreviews])
자 이제 이미지가 추가되면 rawImage들이 보입니다
const RawImagesView = ()=>{
return <div className='imageCon'>
{ rawPreviews.map((item, idx) => <div key={"raw"+idx}><img src={item} alt="not valid" onClick={(e)=>createFilteredImage(e)}/></div>)}
</div>
}
생성된 rawImage를 클릭하면 필터 이미지가 생성됩니다
const createFilteredImage = async (e) =>{
const image = e.target
const ctx = canvasRef.current.getContext('2d')
// filter
ctx.width = image.width;
ctx.height = image.height;
ctx.filter = 'grayscale(1)';
ctx.drawImage(image, 0, 0, image.width, image.height);
// save
const dataURL = canvasRef.current.toDataURL();
setFilteredPreviews([...filteredPreviews, dataURL])
}
필터 이미지를 보여줍니다
const FilteredImagesView = ()=>{
return <div className='imageCon'>
{ filteredPreviews.map((item, idx) => <div key={"ftr"+idx}><img src={item} alt="not valid" /></div>)}
</div>
}
전체 코드는 아래와 같습니다
(시간 관계상 리닝은 하지 않았습니다)
import './App.css';
import { useState,useEffect,useRef } from 'react';
function App() {
const [rawImages, setRawImages] = useState([])
const [rawPreviews, setRawPreviews] = useState([])
const [filteredPreviews, setFilteredPreviews] = useState([])
const canvasRef = useRef(null)
const handleFileInput = (e) => {
const file = e.target.files[0]
if(!isValidFile(file)){
alert("is not valid file")
return
}
addRawImages(file)
addRawPreviews(file)
}
const addRawImages = (file) => {
setRawImages([...rawImages, file])
}
const addRawPreviews = (file) => {
setRawPreviews([...rawPreviews, URL.createObjectURL(file)])
}
const isValidFile = (file)=>{
// todo file validation
return true
}
useEffect(() => {
// free memory when ever this component is unmounted
if(rawPreviews != null){
return () => rawPreviews.forEach(rp=>URL.revokeObjectURL(rp))
}
}, [rawPreviews])
const createFilteredImage = async (e, imagePath) =>{
const image = e.target
const ctx = canvasRef.current.getContext('2d')
// ctx.drawImage(image, 0, 0, image.width * .6, image.height * .6);
// ctx.drawImage(imagePath, 0,0, )
// filter
ctx.width = image.width;
ctx.height = image.height;
ctx.filter = 'grayscale(1)';
ctx.drawImage(image, 0, 0, image.width, image.height);
// save
const dataURL = canvasRef.current.toDataURL();
setFilteredPreviews([...filteredPreviews, dataURL])
}
const FileInputView = ()=>{
return <input type="file" onChange={handleFileInput} accept=".jpg, .jpeg, .png"></input>
}
const RawImagesView = ()=>{
return <div className='imageCon'>
{ rawPreviews.map((item, idx) => <div key={"raw"+idx}><img src={item} alt="not valid" onClick={(e,item)=>createFilteredImage(e,item)}/></div>)}
</div>
}
const FilteredImagesView = ()=>{
return <div className='imageCon'>
{ filteredPreviews.map((item, idx) => <div key={"ftr"+idx}><img src={item} alt="not valid" /></div>)}
</div>
}
const MyCanvas = ()=>{
return <canvas ref={canvasRef} style={{display:'none'}}></canvas>
}
return (
<div className="App">
<FileInputView />
<RawImagesView />
<FilteredImagesView />
<MyCanvas />
</div>
);
}
export default App;
// https://img.ly/blog/how-to-manipulate-an-image-with-jimp-in-react/
// https://stackoverflow.com/questions/66254514/filter-an-image-on-upload-in-react
// https://www.npmjs.com/package/jimp
// https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/filter
// https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL
// https://stackoverflow.com/questions/6850276/how-to-convert-dataurl-to-file-object-in-javascript
추가한 CSS
.imageCon{
margin-top: 200px;
width: 100%;
display: grid;
height: auto;
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
}
추후에는 JIMP 라이브러리를 사용해서 간편하게 작업해보겠습니다
참조 및 인용
How To Manipulate an Image With Jimp in React
Let's get started with Jimp in React. Easily alter images with a few lines of code.
img.ly
Filter an Image on upload in react
I am looking to upload an image to my React site, where on upload I can add some filters like grayscale, saturation etc. I can't find anything except https://cloudinary.com/ which won't work due to...
stackoverflow.com
jimp
An image processing library written entirely in JavaScript (i.e. zero external or native dependencies). Latest version: 0.16.2, last published: 2 months ago. Start using jimp in your project by running `npm i jimp`. There are 1862 other projects in the npm
www.npmjs.com
CanvasRenderingContext2D.filter - Web APIs | MDN
The CanvasRenderingContext2D.filter property of the Canvas 2D API provides filter effects such as blurring and grayscaling. It is similar to the CSS filter property and accepts the same values.
developer.mozilla.org
HTMLCanvasElement.toDataURL() - Web APIs | MDN
The HTMLCanvasElement.toDataURL() method returns a data URL containing a representation of the image in the format specified by the type parameter.
developer.mozilla.org
How to convert dataURL to file object in javascript?
I need to convert a dataURL to a File object in Javascript in order to send it over using AJAX. Is it possible? If yes, please tell me how.
stackoverflow.com
https://codemasterkimc.tistory.com/50
300년차 개발자의 좋은 코드 5계명 (Clean Code)
이번 글을 통해 배워갈 내용 좋은 코드(Clean Code)를 작성하기 위해 개발자로서 생각해볼 5가지 요소를 알아보겠습니다. 개요 좋은 코드란 무엇일까요? 저는 자원이 한정적인 컴퓨터 세상에서 좋
codemasterkimc.tistory.com
'Javascript > React' 카테고리의 다른 글
| React에 Redux Toolkit 사용해보기 Typescript Vite (0) | 2023.10.05 |
|---|---|
| React에 카카오 지도 넣는 방법 (초간단) (Client Side Render) (0) | 2023.06.18 |
| TypeScript와 함께 useReducer를 사용하는 방법 (0) | 2022.07.16 |
| 리액트에서 여러 체크박스들을 하나의 컴포넌트로 재활용 해보기 ts (0) | 2022.06.22 |
| 김씨가 리액트에서 함수 활용하는 비법 한가지 (0) | 2022.05.31 |