엉차엉차
[React] Spring API 사진 파일 업로드 후 리액트에서 호출하기 본문
[ fetch() 함수를 통한 이미지 API 서버 요청하기 ]
리액트(React) API 호출을 통해
데이터베이스(DB)에 저장되어 있는 사진 데이터들을 화면에 출력하는 과정을 기록하고자 한다.
사진 파일들을 DB에 저장하기 위해 devdo님의 아래 게시글을 참고하여
[1. DB에 저장] Byte 데이터를 DB에 바로 저장하는 방식의 파일 업로드와 다운로드를 구현 후,
API 호출을 진행하였다.
[SpringBoot] REST API 파일업로드/다운로드 구현
Entity : ImageDataControllerServiceRepositoryUtils : ImageUtils결과POST 이미지 첨부GET파일 이미지을 Body를 통해 보여줌.
velog.io
https://velog.io/@mooh2jj/SpringBoot-File-uploaddownload-구현
참고로 나의 경우,
다운로드의 경우 파일이름(fileName)이 아닌 파일번호(id)의 형식으로 불러오도록 수정하였다.
위의 사진은 피그마(Figma)를 통해 프로젝트 [퇴근길]의 UI를 구상한 화면으로,
해당 포스팅은 첫 번째 사진(Social 메인화면)에 보여지는 사진들을 호출하기 위한 과정들을 기록한다.
첫 번째 시도 - 실패
나의 첫 번째 시도는
http://localhost:8001/socials/image/${imgcode}
형식의 요청으로 냅다 json 요청을 통해 사진을 불러오고자 했다.
TestImage.js
import { useEffect, useState } from "react";
function TestImage({ imgcode }) {
const [image, setImage] = useState();
useEffect(() => {
fetch(`http://localhost:8001/socials/image/${imgcode}`)
.then(response => response.json())
.then(data => setImage(data));
}, []);
return (
<>
{image}
</>
)
}
export default TestImage;
http://localhost:8001/socials/image/${imgcode} 으로 imgcode를 받아와
ex. http://localhost:8001/socials/image/1 일 경우, 해당 주소를 return 시킨다.
SocialMainCard.js
import { useEffect, useState } from "react";
import TestImage from "./TestImage";
import MainStyle from './css/SocialMainCard.module.css';
import DetailsStyle from './css/SocialDetails.module.css';
function SocialMainCard() {
const [socials, setSocials] = useState([{}]);
useEffect(() => {
fetch("http://localhost:8001/socials")
.then(response => response.json())
.then(data => setSocials(data));
}, []);
return (
<>
<div className={MainStyle.socialMainCardBoard}>
<div className={MainStyle.container}>
{
(Object.keys(socials[0]) <= 0) ? null : socials.map((r, i) =>
<div key={i} className={MainStyle.socialMainCard}>
{/* 이미지 */}
<TestImage key={i} imgcode={r.fileNum}/>
</div>
)
}
</div>
</div>
</>
)
}
export default SocialMainCard;
이후 map()으로 리턴 받은 image를 뿌려주었지만
json 형식의 파일이 아니라며 이미지를 불러오지 못했다... (실패)
그럼.. 이렇게 해볼까? 두 번째 시도 - 실패
imgcode만 받아와서 하드 코딩 형식으로 이미지를 요청하는 건 가능하려나..?
TestImage.js
import { useEffect, useState } from "react";
function TestImage({ imgcode }) {
const [image, setImage] = useState();
useEffect(() => {
fetch(`http://localhost:8001/socials/image/${imgcode}`)
.then(response => response.json())
.then(data => setImage(data));
}, []);
return (
<>
{/* <img src={image} /> */}
{image.imageId}
</>
)
}
export default TestImage;
http://localhost:8001/socials/image/${imgcode} 으로 imgcode를 받아와
ex. http://localhost:8001/socials/image/1 일 경우, 해당 API의 imageID(사진번호)를 return 시킨다.
SocialMainCard.js
import { useEffect, useState } from "react";
import TestImage from "./TestImage";
import MainStyle from './css/SocialMainCard.module.css';
import DetailsStyle from './css/SocialDetails.module.css';
function SocialMainCard() {
const [socials, setSocials] = useState([{}]);
useEffect(() => {
fetch("http://localhost:8001/socials")
.then(response => response.json())
.then(data => setSocials(data));
}, []);
return (
<>
<div className={MainStyle.socialMainCardBoard}>
<div className={MainStyle.container}>
{
(Object.keys(socials[0]) <= 0) ? null : socials.map((r, i) =>
<div key={i} className={MainStyle.socialMainCard}>
{/* 이미지 */}
<img src={`http://localhost:8001/keyword/${<TestImage key={i} imgcode={r.fileNum}/>}`}/>
</div>
)
}
</div>
</div>
</>
)
}
export default SocialMainCard;
[API 요청 주소 + 리턴 받은 imageID(사진 번호)] 의 형태로 다시 시도한 결과..
ex. http://localhost:8001/socials/image/1 형식의 결과를 기대했지만,
http://localhost:8001/socials/image/[Object object] 타입으로 결과가 출력되었다... (또 실패)
JSON.stringify
JSON.parse
[Object object] 핸들링을 검색하여 json의 타입 변경 또한 시도해 보았지만
문제점은 byte 값으로 저장한 이미지를 Json 형식으로 뿌려주려고 하니 오류가 나는 것이었다..
(json의 형식으로 저장된 데이터가 아니기 때문이라는데 왜 자꾸 이렇게 시도했던 건지...)
그렇게 세 번째 시도 - 성공 🐢
SocialImage.js
import { useEffect, useState } from "react";
function SocialImage({ imgcode }) {
const [image, setImage] = useState();
useEffect(() => {
fetch(`http://localhost:8001/socials/image/${imgcode}`)
.then(response => response.blob())
.then(data => {
const objectUrlImg = URL.createObjectURL(data)
setImage(objectUrlImg);
});
}, []);
return (
<>
<div>
<img src={image}/>
</div>
</>
)
}
export default SocialImage;
이렇게 blob()를 이용하여 setImage에 담아준 뒤, image로 저장하는 코드를 작성하여
Web: JavaScript Blob 알아보기
파일 데이터를 읽고 쓸 수 있게 해주는 Blob에 대해 알아보자
medium.com
SocialMainCard.js
import { useEffect, useState } from "react";
import SocialKeyword from "./componentAPI/SocialKeyword";
import SocialMainImage from "./componentAPI/SocialMainImage";
import MainStyle from './css/SocialMainCard.module.css';
import DetailsStyle from './css/SocialDetails.module.css';
import TestImage from "./TestImage";
function SocialMainCard() {
const [socials, setSocials] = useState([{}]);
useEffect(() => {
fetch("http://localhost:8001/socials")
.then(response => response.json())
.then(data => setSocials(data));
}, []);
return (
<>
<div className={MainStyle.socialMainCardBoard}>
<div className={MainStyle.container}>
{
(Object.keys(socials[0]) <= 0) ? null : socials.map((r, i) =>
<div key={i} className={MainStyle.socialMainCard}>
{/* 이미지 */}
<SocialMainImage key={i} imgcode={r.fileNum} />
</div>
)
}
</div>
</div>
</>
)
}
export default SocialMainCard;
main 화면에서 map()을 통해 뿌려주면 되는 것이었다... 그럼 아래와 같은 결과가 나온다. (성공)
이렇게 메인화면 속 메인 카드 컴포넌트가 끝난다.
나머지는 CSS 속성을 통해 미리 구상해 둔 UI와 비슷하도록 화면을 구성한 결과물이며,
이렇게 포스팅을 마친다.
.
.
.
또한, 이렇게 byte 데이터로 파일을 불러올 경우 용량을 많이 잡아먹기 때문에
다시 리팩토링을 통해 json 형식의 파일로 이미지 호출 방법을 기록해 볼 예정이다.
오늘의 코드 일기 끝_ 🐢