개인 프로젝트

Spring + React 파일 업로드 통합 구현 (korean-romanizer 라이브러리를 활용한 한글 파일명 오류 해결)

J2ong 2025. 4. 21. 17:51

안녕하세요!
이번 글에서는 Spring BootReact 환경에서 파일 업로드 기능을 구현하는 방법을 공유합니다. 특히 한글 파일명을 업로드할 때 생길 수 있는 문제를 해결하는 방법도 함께 다룹니다.


📌  Spring 파일 업로드 및 로컬 저장

한글 파일명 문제 해결

한글 파일명을 그대로 저장하면 시스템에 따라 파일 접근 시 문제가 생길 수 있습니다. 이를 해결하기 위해 korean-romanizer 라이브러리를 사용해 로마자로 변환해줍니다.

 

자세한 설명은 korean-romanizer 문서를 확인해주세요.

 

GitHub - crizin/korean-romanizer: 한국어를 입력하면 로마자로 변환해주는 Java 라이브러리

한국어를 입력하면 로마자로 변환해주는 Java 라이브러리. Contribute to crizin/korean-romanizer development by creating an account on GitHub.

github.com

 

gradle 설정

Maven 중심의 문서가 제공되지만, Gradle 환경에서는 다음과 같이 추가해줍니다.

버전은 mvn repository에서 확인 가능합니다.

https://mvnrepository.com/artifact/net.crizin/korean-romanizer

implementation 'net.crizin:korean-romanizer:jar:2.0.1'

 

application.propertice
file.path=D:/fileUpload/
file.url=http://localhost:8080/file/
  • file.path: 실제 파일이 저장될 경로
    • 예) file.path=D:/fileUpload/
  • file.url: 클라이언트가 접근할 파일 URL
    • 예) file.url=http://localhost:8080/file/

 

Service (파일 업로드)
    @Value("${file.path}")
    private String filePath;
    @Value("${file.url}")
    private String fileUrl;

    @Override
    @Transactional
    public String upload(MultipartFile file) {
        if (file.isEmpty()) return null;

        String originalFileName = file.getOriginalFilename();
        String fileNameWithoutExt = originalFileName.substring(0, originalFileName.lastIndexOf("."));
        String ext = originalFileName.substring(originalFileName.lastIndexOf("."));
        // 1. 로마자로 변환
        String romanized = KoreanRomanizer.romanize(fileNameWithoutExt);

        // 2. 특수문자 제거
        String sanitized = romanized.replaceAll("[^a-zA-Z0-9]", "_");
        String uuid = UUID.randomUUID().toString();

        // 3. UUID와 합쳐 저장할 이름 생성
        String saveFileName = uuid + "_" + sanitized + ext;
        
        String savePath = filePath + saveFileName;

        try {
            file.transferTo(new File(savePath));
        } catch (Exception exception) {
            exception.printStackTrace();
            return null;
        }

        String url = fileUrl + saveFileName;
        return url;
    }
  • 파일명을 로마자로 변환 → 특수문자 제거 → UUID 추가 → 저장
  • 업로드된 파일 URL 반환

 

Service (파일 다운로드)
    @Override
    public Resource getFile(String fileName) {

        Resource resource = null;

        try {
            resource = new UrlResource("file:" + filePath + fileName);
        } catch (Exception exception) {
            exception.printStackTrace();
            return null;
        }
        return resource;

    }

 

Controller
    @PostMapping("/upload")
    public String upload (
        @RequestParam("file") MultipartFile file
    ) {
        String url = fileService.upload(file);
        return url;
    }
    
    @GetMapping(value = "{fileName}", produces = { 
        MediaType.IMAGE_JPEG_VALUE,
        MediaType.IMAGE_PNG_VALUE,
        MediaType.APPLICATION_PDF_VALUE,
        MediaType.APPLICATION_OCTET_STREAM_VALUE
    })
    public Resource getFile (
        @PathVariable("fileName") String fileName
    ) {
        Resource resource = fileService.getFile(fileName);
        return resource;
    }
  • MediaType 설명:
MediaType 상수 설명
MediaType.IMAGE_JPEG_VALUE JPEG 이미지 파일 형식 (예: .jpg, .jpeg)
MediaType.IMAGE_PNG_VALUE PNG 이미지 파일 형식 (예: .png)
MediaType.APPLICATION_PDF_VALUE PDF 문서 형식 (예: .pdf)
MediaType.APPLICATION_OCTET_STREAM_VALUE 일반적인 바이너리 파일 형식. MIME 타입이 정확히 지정되지 않은 파일도 이 형식으로 반환 가능

 


 

📌 Postman 테스트

백엔드 코드를 완성했으니 제대로 동작하는지 postman을 통해 테스트를 진행하겠습니다.

 

이미지는 구글에서 임의로 검색후 진행하겠습니다.

이미지 출처: https://www.google.com/url?sa=i&url=https%3A%2F%2Fcatfriendly.com%2F&psig=AOvVaw0MZcwgr8-H0RMxLfKy952g&ust=1745307106112000&source=images&cd=vfe&opi=89978449&ved=0CBAQjRxqFwoTCOD5zuLN6IwDFQAAAAAdAAAAABAE

 

API 요청 (업로드)
POST http://{서버 URL}/upload

 

  • Body → form-data
    • Key: file
    • Type: File
    • Value: 업로드할 파일

응답으로 업로드된 파일의 URL 문자열이 반환되면 성공입니다.

 

지정한 file.path 위치에 이미지가 잘 저장되는지 확인

 

API 요청 (파일 가져오기)
GET http://{서버 URL}/{파일명}

업로드 시 반환된 URL 경로에서 마지막 "/" 이후 경로만(확장자명 포함) 추출해 테스트하세요.

 

응답이 성공적으로 반환되는지 확인합니다.

 


📌 React 파일 업로드

API 요청 (Axios)
const FILE_UPLOAD_URL = () => `${DOMAIN}/upload`;
const multipartFormData = { headers: { 'Content-Type': 'multipart/form-data' } };

export const fileUploadRequest = async (data: FormData) => {
    const result = await axios.post(FILE_UPLOAD_URL(), data, multipartFormData)
        .then(response => {
            const responseBody: string = response.data;
            return responseBody;
        })
        .catch(error => {
            return null;
        })
    return result;
}

 

파일 업로드 함수
const onFileUploadHandler = (event: ChangeEvent<HTMLInputElement>) => {

      if (!event.target.files || !event.target.files.length) return;

      const file = event.target.files[0];
      const data = new FormData();
      data.append('file', file);

      fileUploadRequest(data)
      	.then(url => {
            if (url) {
                console.log("업로드된 파일 URL:", url);
                // 상태 저장 or 미리보기 렌더링 가능
            }
        });
      
}

 

업로드 입력 필드
<input ref={fileInputRef} type="file" accept='image/*' style={{ display: 'none' }} onChange={onFileUploadHandler} />

버튼 클릭 시 fileInputRef.current.click()을 호출하면 사용자에게 파일 선택창을 열 수 있습니다.

 


 

이제 Spring + React 환경에서 한글 파일명도 안정적으로 처리되는 파일 업로드 기능을 완성할 수 있습니다.

추가로 업로드한 파일 미리보기, 다중 파일 처리 기능으로 확장해볼 수도 있겠죠. 오늘도 즐거운 개발 되시길 바랍니다!