멘토링

[SpringBoot] 날짜 별 폴더생성 및 이미지 정렬 (서버->프론트 UI)

TMJeti 2025. 5. 12. 17:41

먼저 구조부터 정리해보고 구현해보겠다.

에피소드마다 폴더가 생성되고, 그 폴더 이름은 연월일(생성날짜)로 만들어진다.

해당 날짜에 폴더가 없다면 자동으로 생성한다.

 

그 안에 만화 컷 씬 이미지들이 있고,  /comics/20250509/1.jpg 이런식으로 저장된다. 또한 해당 폴더의 파일 목록을 서버가 숫자 순서대로 반환해야 한다.

 

폴더와 이미지가 들어가는 springBoot 쪽 Controller 부터 건드려보겠다.

//ComicController.java

@RestController
@RequestMapping("/comics")

public class ComicController {
    @GetMapping("/episode/{comicId}/{date}/images")
    public ResponseEntity<List<String>> getEpisodeImages(
            @PathVariable String comicId,
            @PathVariable String date) {

        String basePath = "D:/comics";
        Path episodeDir = Paths.get(basePath, comicId, date);

        try {
            // 1 폴더가 없으면 생성
            if (!Files.exists(episodeDir)) {
                System.out.println("폴더 없음, 새로 생성: " + episodeDir.toAbsolutePath());
                Files.createDirectories(episodeDir);

                // 2(옵션) 기본 이미지 생성 or 복사
                Path defaultImage = Paths.get("comics/default-placeholder.jpg");
                if(Files.exists(defaultImage)) {
                    Path targetImage = episodeDir.resolve("1.jpg");
                    Files.copy(defaultImage, targetImage, StandardCopyOption.REPLACE_EXISTING);
                }
            }

            // 3 파일 목록 가져오기 (숫자 순서로 정렬)
            List<String> imageFiles = Files.list(episodeDir)
                    .filter(Files::isRegularFile)
                    .map(path -> path.getFileName().toString())
                    .sorted(Comparator.comparingInt(name -> {
                        // 파일명에서 숫자 추출 (예: 1.jpg -> 1)
                        String num = name.replaceAll("\\D", "");
                        return num.isEmpty() ? 0 : Integer.parseInt(num);
                    }))
                    .toList();

            return ResponseEntity.ok(imageFiles);
        } catch (IOException e) {
            e.printStackTrace();
            return ResponseEntity.status(500).build();
        }
    }
}

 

다음은 프론트(React) 의 이미지 요청 부분이다.

//EpisodeViewer.jsx

const EpisodeViewer = () => {
    const { id: comicId, epId } = useParams();
    const [images, setImages] = useState([]);

    // 해당 에피소드 정보 찾기
    const episode = episodesData
        .find(c => c.comicId === parseInt(comicId, 10))
        ?.episodes.find(e => e.ep === parseInt(epId, 10));

    const date = episode?.date;

    useEffect(() => {
        if(!date) return;

        fetch(`/comics/episode/${comicId}/${date}/images`)
            .then(res => res.json())
            .then(setImages)
            .catch(err => console.error('이미지 불러오기 실패:', err));
    }, [comicId, date]);

    return (
        <div className="episode-content">
            <h2>{epId}화 보기</h2>
            {images.length === 0 ? (
                <p>이미지를 불러오는 중...</p>
            ) : (
                images.map((img, idx) => (
                    <img
                        key={idx}
                        // 캐시 무력화 쿼리스트링 추가
                        src={`/comics/${comicId}/${date}/${img}?t=${Date.now()}`}
                        alt={`Page ${idx + 1}`}
                        style={{ width: '100%', marginBottom: '20px' }}
                    />
                ))
            )}
        </div>
    );
};

export default EpisodeViewer;

 

이미지 폴더는 정적 리소스로 매핑해야 프론트에서 직접 접근이 가능하다고 한다.

Spring부의 WebConfig 부분에 한번 설정해줘야한다.

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/comics/**")
                .addResourceLocations("file:comics/"); // 로컬 폴더 매핑, 루트 폴더의 comics
    }
}

 

파일경로는 spring 프로젝트 루트단에 만들어주었다.


실행이 안될 수도 있는건 React가 백엔드 API를 호출하고 있는게 맞는지 확인해봐야한다. 필자의 경우 

fetch(`/comics/episode/${epId}/images`)

이렇게 상대 경로로 요청하고 있는데 React 개발 서버는 3000포트, Spring Boot 서버는 8080 번이라 포트가 다르기 때문에 이 요청은 React 개발 서버의 8080 포트에만 보내고 끝나버린다 (...)

 

해결방법은 React package.json에

"proxy": "http://localhost:8080"

이거 입력해주면 자동으로 요청을 Spring Boot로 프록시 해주게된다.


다만 이 방법은 서버에서 UI로 사진을 불러오는 처리만 가능하다. 즉, 서버에 이미 사진이 있다는 걸 전제로 하기 때문에 일반적인 상용 서비스들의 기능에 쓸만한 코드는 못 된다. (보통 사용자가 ui에서 파일 선택 후 서버에 저장 -> 서버가 저장된 사진을 ui로 롤백하는 구조이다.) 

 

이걸 생각 안 해보고 단편적으로 코드짜서 멘토 아조씨한테 혼났다 (데헿) 다음 포스팅에서 위 기능은

(Front ui->서버->ui) 다시 코드 짜보겠다.