티스토리 뷰

사진을 업로드 했으니 경로를 통해서 가져와 렌더링 해줘야겠죠?

 

[ 전체코드 ] 

GalleryController.java

더보기
package com.example.demo.controller;

import com.example.demo.dto.GalleryDTO;
import com.example.demo.entity.GalleryEntity;
import com.example.demo.service.GalleryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;

@RestController
@RequestMapping("/api/v1/auth/y")
public class GalleryController {

	@Autowired
	GalleryService galleryService;

	/**
	 * 기능 : 사진첩 작성  ( 훈련사 계정 )
	 * url : /createGallery
	 * request data : 날짜(글제목), 글 내용, 작성 날짜, 작성 시간
	 * response data : 사진첩 작성 성공
	 */
	@PostMapping("/createGallery")
	public List<GalleryDTO> createGallery(@RequestPart (value = "file", required = false) List<MultipartFile> uploadFiles) {
		return galleryService.createGallery(uploadFiles);
	}
    
	
	/**
	  기능 : 사진첩 목록
	  url : /gallery
	  request data : 
	  response data : 사진첩 목록
	 */
	@GetMapping("/gallery")
	public List<GalleryDTO> gallery() {
		return galleryService.galleryList();
	}
    
	@GetMapping(value = "/galleryView", produces={MediaType.IMAGE_PNG_VALUE, MediaType.IMAGE_JPEG_VALUE})
	public ResponseEntity<byte[]> findById(@RequestParam("id") Long id) throws IOException {
		// id에 해당하는 이미지 정보를 서비스로부터 가져옴
		GalleryEntity galleryEntity = galleryService.findById(id);
		// 파일 경로를 이용하여 이미지 파일을 읽기 위한 InputStream을 생성
		InputStream inputStream = new FileInputStream(galleryEntity.getGallImg());
		//  InputStream에서 모든 바이트를 읽어와 byte 배열에 저장
		byte[] bytes = inputStream.readAllBytes();
		// 스트림을 닫아 리소스를 해제
		inputStream.close();
		// 이미지를 byte 배열로 변환하여 ResponseEntity에 담아서 반환
		return new ResponseEntity<byte[]>(bytes, HttpStatus.OK);
 	}
	
		
}

 

[ 추가된 부분 ] 

사진첩 목록을 가져오는 엔드포인트

	@GetMapping("/gallery")
	public List<GalleryDTO> gallery() {
		return galleryService.galleryList();
	}

 

특정 데이터를 요청 본문에 포함할 필요가 없기 때문에 요청 데이터는 없고, 응답 데이터는 List<GalleryDTO> 입니다.

요청이 들어오면 사진첩 목록을 가져오고, 그 목록을 클라이언트에게 반환합니다.

 

 

이미지를 가져오는 엔드포인트

	@GetMapping(value = "/galleryView", produces={MediaType.IMAGE_PNG_VALUE, MediaType.IMAGE_JPEG_VALUE})
	public ResponseEntity<byte[]> findById(@RequestParam("id") Long id) throws IOException {
		// id에 해당하는 이미지 정보를 서비스로부터 가져옴
		GalleryEntity galleryEntity = galleryService.findById(id);
		// 파일 경로를 이용하여 이미지 파일을 읽기 위한 InputStream을 생성
		InputStream inputStream = new FileInputStream(galleryEntity.getGallImg());
		//  InputStream에서 모든 바이트를 읽어와 byte 배열에 저장
		byte[] bytes = inputStream.readAllBytes();
		// 스트림을 닫아 리소스를 해제
		inputStream.close();
		// 이미지를 byte 배열로 변환하여 ResponseEntity에 담아서 반환
		return new ResponseEntity<byte[]>(bytes, HttpStatus.OK);
 	}

 

여기서 요청 데이터는 이미지의 id 입니다. 

galleryService.findById(id) 메서드를 호출하여 ID에 해당하는 이미지 정보를 가져옵니다.

이미지 파일 경로를 사용하여 FileInputStream을 생성합니다.

FileInputStream에서 모든 바이트를 읽어와 byte[] 배열에 저장합니다.

그렇게 byte[] 배열로 변환한 이미지를 ResponseEntity에 담아 클라이언트에 반환합니다.

 


 

 

[ 전체코드 ] 

GalleryService.java

더보기
package com.example.demo.service;

import com.example.demo.dto.GalleryDTO;
import com.example.demo.entity.GalleryEntity;
import com.example.demo.mapper.GalleryMapper;
import com.example.demo.repository.GalleryRepository;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.sql.Date;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;


@Service
@Log4j2
public class GalleryService {
	
	@Autowired
	private GalleryRepository galleryRepository;

	// 업로드 할 위치
	@Value("${part.upload.path}")
	private String uploadPath;
    private String fileExtension;
    private Path savePath;

    // 사진첩 작성
	public List<GalleryDTO> createGallery(List<MultipartFile> uploadFiles) {
        List<GalleryDTO> galleryDTOList = new ArrayList<>();

        for (MultipartFile uploadFile : uploadFiles) {
            // 이미지 파일만 업로드
            if (!Objects.requireNonNull(uploadFile.getContentType()).startsWith("image")) {
                log.warn("this file is not an image type");
                continue;
            }

            // 파일명: 업로드 된 파일의 원래 파일 이름을 추출
            String originalName = uploadFile.getOriginalFilename();

            // 마지막으로 온 "/"부분으로부터 +1 해준 부분부터 출력
            // originalName이 "/path/to/file/example.txt"와 같은 문자열이라면, "example.txt" 부분만 (파일명만) 추출
            String fileName = originalName.substring(originalName.lastIndexOf("//") + 1);
            log.info("fileName: " + fileName);

			// 파일 확장자
            fileExtension = StringUtils.getFilenameExtension(originalName);

            // 날짜 폴더 생성
            String folderPath = makeFolder();

            // UUID
            String uuid = UUID.randomUUID().toString();

            // 저장할 파일 이름 중간에 "_"를 이용하여 구분
            String saveName = uploadPath + File.separator + folderPath + File.separator + uuid + "_" + fileName;

            // Paths.get() 메서드는 특정 경로의 파일 정보를 가져옴 (경로 정의하기)
            savePath = Paths.get(saveName);

            try {
                // transferTo(file) : uploadFile에 파일을 업로드 하는 메서드
                uploadFile.transferTo(savePath);
                galleryDTOList.add(new GalleryDTO(fileName, uuid, folderPath));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        GalleryEntity galleryEntity = GalleryEntity.builder()
                .gallDate(Date.valueOf(LocalDate.now()))
                .gallImg(String.valueOf(savePath))
                .gallExtension(fileExtension)
                .build();

        galleryRepository.save(galleryEntity);

        return galleryDTOList;
    }

	// 날짜 폴더 생성
	public String makeFolder() {
		//LocalDate를 문자열로 포멧
		String str = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd"));
		String folderPath = str.replace("/", File.separator);

		// make folder --------
		// File newFile= new File(dir,"파일명");
		// -> 부모 디렉토리를 파라미터로 인스턴스 생성
		File uploadPathFolder = new File(uploadPath, folderPath);

		// 만약 uploadPathFolder가 존재하지않는다면 makeDirectory하라는 의미
		// mkdir(): 디렉토리에 상위 디렉토리가 존재하지 않을경우에는 생성이 불가능한 함수
		// mkdirs(): 디렉토리의 상위 디렉토리가 존재하지 않을 경우에는 상위 디렉토리까지 모두 생성하는 함수
		if(!uploadPathFolder.exists()) {
			boolean mkdirs = uploadPathFolder.mkdirs();
			log.info("-------------------makeFolder------------------");
			log.info("uploadPathFolder.exists(): "+uploadPathFolder.exists());
			log.info("mkdirs: "+mkdirs);
		}

		return folderPath;

	}


// 사진첩 목록 조회

// Mapstruct 사용 x
//	public List<GalleryDTO> galleryList() {
//		List<GalleryEntity> galleryEntities = galleryRepository.findAll();
//		List<GalleryDTO> galleryDTOList = new ArrayList<>();
//
//		for (GalleryEntity entity : galleryEntities) {
//			GalleryDTO dto = new GalleryDTO();
//
//			dto.setGallImg(entity.getGallImg());
//			dto.setGallExtension(entity.getGallExtension());
//
//			galleryDTOList.add(dto);
//		}
//
//		return galleryDTOList;
//	}

	// mapstruct 사용 o
	public List<GalleryDTO> galleryList() {
		List<GalleryEntity> galleryEntities = galleryRepository.findAll();   // 갤러리엔터티를 리스트에 저장
		return galleryEntities.stream()  // 스트림 사용
				.map(GalleryMapper.instance::galleryToDTO)  // Mapstruct 사용하여 갤러리 entity -> DTO로 변환
				.collect(Collectors.toList());  //  변환된 갤러리 DTO들을 리스트로 수집하여 반환
	}

    // 해당 유저의 사진첩 상세조회
    public GalleryEntity findById(Long id) {
        return galleryRepository.findById(id).get();

    }

}

 

 

[ 추가된 부분 ] 

사진첩 목록 조회 ( Mapsturct를 사용안한 코드 )

// Mapstruct 사용 x
	public List<GalleryDTO> galleryList() {
		List<GalleryEntity> galleryEntities = galleryRepository.findAll();
		List<GalleryDTO> galleryDTOList = new ArrayList<>();

		for (GalleryEntity entity : galleryEntities) {
			GalleryDTO dto = new GalleryDTO();

			dto.setGallImg(entity.getGallImg());
			dto.setGallExtension(entity.getGallExtension());

			galleryDTOList.add(dto);
		}

		return galleryDTOList;
	}

 

galleryRepository.findAll() 메서드를 호출하여 모든 GalleryEntity를 가져옵니다.

각 GalleryEntity를 GalleryDTO로 변환하여 galleryDTOList 리스트에 추가합니다.

최종적으로 galleryDTOList를 반환합니다.

 

 

하지만 Mapstruct를 학습했으니 써먹어야겠죠?

다음은 Mapstruct를 사용한 버전의 코드 입니다.

 

 

사진첩 목록 조회 ( Mapsturct를 사용안한 코드 )

// 사진첩 목록 조회 (MapStruct 사용 O)
public List<GalleryDTO> galleryList() {
    // 갤러리 엔티티 리스트를 가져옴
    List<GalleryEntity> galleryEntities = galleryRepository.findAll();
    // 스트림을 사용하여 각 엔티티를 MapStruct를 사용해 DTO로 변환하고 리스트로 수집하여 반환
    return galleryEntities.stream()
            .map(GalleryMapper.instance::galleryToDTO)  // MapStruct 매퍼를 사용하여 엔티티를 DTO로 변환
            .collect(Collectors.toList());  // 변환된 DTO 리스트를 수집하여 반환
}

 

galleryRepository.findAll() 메서드를 호출하여 모든 GalleryEntity를 가져옵니다.

Java Stream API를 사용하여 각 GalleryEntity를 MapStruct 매퍼(GalleryMapper.instance::galleryToDTO)를 사용해 GalleryDTO로 변환합니다.

변환된 GalleryDTO들을 리스트로 수집하여 반환합니다.

 

 


 

 

[ 전체코드 ] 

GalleryList.js

더보기
import React, { useEffect, useState } from "react";
import { useNavigate, Link } from 'react-router-dom';
import axios from "axios";
import 'bootstrap/dist/css/bootstrap.min.css';
import { Button, Card, Col, Container, Row } from 'react-bootstrap';
import { useCookies } from "react-cookie";
import AxiosInstance from "../api/AxiosInstance";
import '../css/GalleryList.css'

let GalleryList = (props) => {

    let [cookies] = useCookies(['accessToken']);
    let [dataList, setDataList] = useState([]);
    let [imageMap, setImageMap] = useState({}); // 이미지 데이터를 저장할 상태 변수
    let navigate = useNavigate();


    useEffect(() => {
        axios({
            url: `http://localhost:8082/api/v1/auth/y/gallery`,
            method: 'GET',
            headers: {
                'Authorization' : 'Bearer '+ cookies.accessToken
              }
        })
        .then((res) => {
            console.log(res.data)
            if (res.status === 200) {
                console.log("이미지 불러오기 성공")
                setDataList(res.data); // 응답 데이터 설정
                // 각 이미지 데이터를 요청하여 상태로 저장
                res.data.forEach(data => {
                    requestImage(data.id).then(imageSrc => {
                        setImageMap(prevState => ({ ...prevState, [data.id]: imageSrc }));
                    });
                });
            }
        })
        .catch((error) => {
            alert("이미지 불러오기 실패");
            console.error("Error:", error);
        });
    }, []); // 의존성 배열을 빈 배열로 전달하여 최초 렌더링 시에만 실행되도록 설정


    // 이미지 URL을 가져오는 requestImage 함수
    let requestImage = async (imageId) => {
        try {
            const res = await axios({
                url: `http://localhost:8082/api/v1/auth/y/galleryView?id=${imageId}`,
                method: 'GET',
                headers: {
                    'Authorization': 'Bearer ' + cookies.accessToken
                },
                responseType: 'arraybuffer'  // 바이트 배열로 응답 받기
            });
            
            const base64 = btoa(
                new Uint8Array(res.data)
                    .reduce((data, byte) => data + String.fromCharCode(byte), '')
            );
            const contentType = res.headers['content-type'];
            return `data:${contentType};base64,${base64}`;
        } catch (error) {
            console.error("Error:", error);
        }
    };
    
    let createGallery = () => {
        navigate('/createGallery');
    };

    
    // 사진첩 삭제
    let deleteGallery = (id) => {
        axios({
            url: `http://localhost:8082/api/v1/auth/y/gallery?id=${id}`,
            method: 'DELETE',
            headers: {
                'Authorization' : 'Bearer '+ cookies.accessToken
              }
        })
        .then((res) => {
            console.log("갤러리 삭제 성공");
            alert("해당 사진을 삭제하였습니다.")
            // fetchData();
            // 갤러리 목록 갱신
            setDataList(res.data); // 응답 데이터 설정
        })
        .catch((error) => {
            console.log("Error:", error);
            alert("갤러리 삭제 실패!")
        });
    };
   

    return (
        <>
            <h3>📷사진첩</h3>
            <div className="container">
                {dataList.length > 0 ? (
                    <div className="row justify-content-center">
                        {dataList.slice(0, 9).map((data, index) => (
                            <div key={index} className="col-md-4">
                                <div className="card">
                                    {imageMap[data.id] ? (
                                        <img className="card-img-top" src={imageMap[data.id]} alt="Gallery Image"/>
                                    ) : (
                                        <div>Loading...</div>
                                    )}
                                    <div className="card-body">
                                        <h5 className="card-title">{`${data.gall_date}`}</h5>
                                        <p className="card-text">{`${data.id}`}</p>
                                        
                                        <div className="button-group">
                                            <Link to={`/galleryView/${data.id}`} className="btn btn-primary">상세보기</Link>
                                            <button onClick={() => deleteGallery(data.id)} className="btn btn-danger">삭제하기</button>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        ))}
                    </div>
                ) : (
                    <div style={{ textAlign: 'center', marginTop: '50px' }}>
                        <h3>아직 작성된 이미지가 없습니다🙄</h3>
                    </div>
                )}
            </div>
            <div className="button-container">
                <div className="button-wrapper">
                    <button className="creat-board-btn" onClick={createGallery}>📝작성하기</button>
                </div>
            </div>
        </>
    );

}


export default GalleryList;

 

[ 추가된 부분 ] 

 requestImage 함수 

let requestImage = async (imageId) => {
    try {
        const res = await axios({
            url: `http://localhost:8082/api/v1/auth/y/galleryView?id=${imageId}`,
            method: 'GET',
            headers: {
                'Authorization': 'Bearer ' + cookies.accessToken
            },
            responseType: 'arraybuffer'  // 바이트 배열로 응답 받기
        });
        
        const base64 = btoa(
            new Uint8Array(res.data)
                .reduce((data, byte) => data + String.fromCharCode(byte), '')
        );
        const contentType = res.headers['content-type'];
        return `data:${contentType};base64,${base64}`;
    } catch (error) {
        console.error("Error:", error);
    }
};
  • 특정 이미지 ID를 받아 해당 이미지를 요청합니다.
  • axios를 사용하여 GET 요청을 보내고, 응답 유형을 arraybuffer로 설정하여 바이트 배열로 데이터를 받습니다.
  • 응답 데이터를 base64로 인코딩하여 데이터 URL을 생성합니다.
  • 생성된 데이터 URL을 반환합니다.

 

 useEffect 훅 

useEffect(() => {
    axios({
        url: `http://localhost:8082/api/v1/auth/y/gallery`,
        method: 'GET',
        headers: {
            'Authorization': 'Bearer ' + cookies.accessToken
        }
    })
    .then((res) => {
        console.log(res.data);
        if (res.status === 200) {
            console.log("이미지 불러오기 성공");
            setDataList(res.data); // 응답 데이터를 상태로 설정
            // 각 이미지 데이터를 요청하여 상태로 저장
            res.data.forEach(data => {
                requestImage(data.id).then(imageSrc => {
                    setImageMap(prevState => ({ ...prevState, [data.id]: imageSrc }));
                });
            });
        }
    })
    .catch((error) => {
        alert("이미지 불러오기 실패");
        console.error("Error:", error);
    });
}, []); // 의존성 배열을 빈 배열로 전달하여 최초 렌더링 시에만 실행되도록 설정
  • useEffect 훅을 사용하여 컴포넌트가 처음 렌더링될 때 API 요청을 보냅니다.
  • axios를 사용하여 GET 요청을 보내고, 헤더에 인증 토큰을 포함합니다.
  • 요청이 성공하면 상태 변수 setDataList를 사용하여 데이터를 저장합니다.
  • 응답 데이터의 각 항목에 대해 requestImage 함수를 호출하여 이미지를 가져오고, setImageMap 상태 업데이트 함수를 사용하여 이미지 맵을 갱신합니다.

 


 

 

💻 [ 결과 ]  

 

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/07   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
글 보관함