엉차엉차

[React] Spring API 사진 파일 업로드 후 리액트에서 호출하기 본문

엉차엉차 [프로젝트]/02_퇴근길

[React] Spring API 사진 파일 업로드 후 리액트에서 호출하기

b_nyong 2023. 9. 18. 00:33

[ fetch() 함수를 통한 이미지 API 서버 요청하기 ]

 

리액트(React) API 호출을 통해

데이터베이스(DB)에 저장되어 있는 사진 데이터들을 화면에 출력하는 과정을 기록하고자 한다.

React API 호출을 통한 이미지 다운로드

 


 

사진 파일들을 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)의 형식으로 불러오도록 수정하였다.


 

프로젝트 [퇴근길] UI 구상 화면

 

위의 사진은 피그마(Figma)를 통해 프로젝트 [퇴근길]의 UI를 구상한 화면으로,

해당 포스팅은 첫 번째 사진(Social 메인화면)에 보여지는 사진들을 호출하기 위한 과정들을 기록한다.

 


 

 

첫 번째 시도 - 실패

포스트맨(Postman) API 요청을 통해 호출한 이미지

 

나의 첫 번째 시도는

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 적용 화면

이렇게 메인화면 속 메인 카드 컴포넌트가 끝난다.

 


 

 

나머지는 CSS 속성을 통해 미리 구상해 둔 UI와 비슷하도록 화면을 구성한 결과물이며,

이렇게 포스팅을 마친다.

.

.

.

또한, 이렇게 byte 데이터로 파일을 불러올 경우 용량을 많이 잡아먹기 때문에

다시 리팩토링을 통해 json 형식의 파일로 이미지 호출 방법을 기록해 볼 예정이다.

 

오늘의 코드 일기 끝_ 🐢