[Spring Boot] Windows10 WebDAV 서버 파일 업로드 및 다운로드

2025. 5. 4. 17:55·Spring Boot

예전에 팀 개발 서버에서 파일 업로드 기능을 사용할 때 각자의 PC에 파일이 저장되어 팀원이 업로드한 파일을 다른 팀원이 다운로드할 수 없는 문제가 있었다. 모든 팀원이 한 공간에 파일을 업로드하고 동일한 공간에서 파일을 다운로드할 수 있도록 개선해야 한다는 요청을 받았고 그 당시 선임님께서 WebDAV라는 것이 있으니 한 번 확인해 보라고 하셨다.

 

WebDAV 서버 구축은 선임님께서 한다고 하셔서 소스 작업만 했었지만 생각난 김에 WebDAV 설정도 해보고 이전에 작업했던 내용들을 정리할 겸 포스팅을 작성한다.

 

 

 

WebDAV란?

WebDAV(Web Distributed Authoring and Versioning)는 HTTP 프로토콜을 확장한 기술로, 인터넷을 통해 원격 서버의 파일과 폴더를 마치 내 컴퓨터처럼 업로드, 다운로드, 수정, 삭제할 수 있게 해주는 표준이다.
간단하게는 여러 사용자가 웹 서버에 접속해서 파일을 함께 관리하고 협업할 수 있도록 만든 네트워크 프로토콜이다.

 

대표적인 특징은 다음과 같다.

 

  • 파일 업로드/다운로드, 복사, 이동, 삭제 등 다양한 파일 관리 기능 제공
  • 파일 잠금(동시 수정 방지), 메타데이터 관리(작성자, 수정일 등), 폴더 구조 관리 등 협업에 필요한 기능 지원
  • 윈도우, 맥 등 주요 운영체제에서 기본적으로 지원되어 네트워크 드라이브처럼 쉽게 사용할 수 있다

 

1. IIS와 WebDAV 설치

윈도우에서 WebDAV를 쓰려면 IIS(인터넷 정보 서비스)와 WebDAV 기능을 설치해야 한다.

  • 제어판 → 프로그램 및 기능 → Windows 기능 켜기/끄기 들어가서 아래 이미지와 같이 체크한다.
  • Windows 인증 항목을 체크한다.

IIS 및 WebDAV 설정

 

  • 해당 설정 후 윈도우 작업 팝업이 완료되면 윈도우에서 IIS 관리자를 검색한다.

IIS 관리자

 

2. WebDAV 가상 디렉토리 생성

  • IIS 관리자(인터넷 정보 서비스 관리자)를 실행하고 좌측 메뉴에서 아래 순서대로 진행한다.
  1. Default Web Site 마우스 우클릭
  2. 가상 디렉터리 추가 클릭
  3. 별칭: 원하는 이름 작성
  4. 실제 경로: 실제로 공유할 폴더 경로 지정

 

디렉토리 추가

 

  • 이제 좌측 메뉴에서 생성한 디렉토리를 클릭하여 디렉터리 검색 아이콘을 클릭한다.
  • 이후 우측 메뉴에 보이는 사용을 클릭한다.

디렉토리 검색 사용 선택

 

  • 이후 기본 문서 아이콘 클릭
  • 우측 메뉴에서 사용안함 클릭

 

기본 문서 사용안함 선택

 

 

  • 이제 http://127.0.0.1/webdav URL에 접속하면 아래와 같은 화면이 나타난다.

 

 

3. 인증 및 보안 설정

  • 윈도우 컴퓨터 관리 메뉴에 접속하여 그룹을 추가한다.

그룹 추가

 

 

  • 방금 추가한 그룹에 윈도우 사용자 정보를 추가한다.

 

 

 

  • 다시 IIS 관리자 화면에서 사용자 그룹과 사용 권한 정보를 아래 이미지와 같이 설정한다.
  • 모든 사용자에 대한 제작규칙이 이미 등록되어 있다면 삭제한다.

제작 규칙 추가

 

 

추가한 가상 디렉터리가 아닌 Default Web Site에서 WebDAV 제작 규칙을 추가해야 하위 모든 경로에도 설정된다.

필자의 경우 위 이미지 좌측 webdav를 클릭하고 설정하여 문제가 있었고 해당 포스팅의 하단 테스트 문단에 기록해 두었다.

 

  • 인증 메뉴에서 Windows 인증만 활성화시키고 나머지 인증은 모두 사용안함으로 변경한다.

  • 여기까지 진행하면 WebDAV 설정은 완료된 것이다.

 

 

WebDAV는 윈도우 탐색기에서 바로 파일을 다룰 수 있다는 점이 큰 장점이다.
그리고 내부(집/회사 네트워크)에서만 사용할 때는 SSL 인증서나 포트포워딩이 필요 없지만
외부(인터넷)에서 접속하려면 SSL 인증서와 공유기 포트포워딩도 신경 써야 한다.

 

 

 

이제 Springboot와 java를 사용하여 WebDAV로 파일을 업로드하고 다운로드를 해보자.

 

WebDAV로 JAVA를 사용한 파일 업로드 및 다운로드

1. Sardine 라이브러리 세팅

implementation 'com.github.lookfirst:sardine:5.13'
  • 해당 라이브러리 github 주소는 아래를 확인하면 된다.
  • 2025년 5월 기준 5.13 버전이 latest 버전이다. 
https://github.com/lookfirst/sardine/releases
 

Releases · lookfirst/sardine

an easy to use webdav client for java. Contribute to lookfirst/sardine development by creating an account on GitHub.

github.com

https://github.com/lookfirst/sardine/wiki/UsageGuide

 

UsageGuide

an easy to use webdav client for java. Contribute to lookfirst/sardine development by creating an account on GitHub.

github.com

 

 

내가 Sardine 라이브러리를 사용한 이유는 다음과 같다.

  • WebDAV 서버와의 파일 업로드/다운로드/삭제 정도의 단순 파일 관리 기능을 구현해야 했음
  • Spring, JAVA 환경에서 해당 기능들을 제공해 주는 대표적인 라이브러리 (사용중인 사례가 많음)
  • Stack Overflow에 해당 라이브러리에 대한 레퍼런스가 가장 많이 조회됨
  • 다른 팀의 요청과 팀 내부 개발 일정이 많아 Spring 환경에서 별도 복잡한 설정 없이 적용 가능한 해당 라이브러리를 선택

 

WebDAV 라이브러리 비교

  • 위 표는 WebDAV 라이브러리를 비교한 결과이다.

내가 필요한 기능은 파일 업로드 및 다운로드, 삭제 정도이기 때문에 Sardine을 사용하면 될 것으로 판단했다.

Sardine 라이브러리가 가장 레퍼런스가 많았고 Seanox Spring WebDAV 라이브러리의 경우 클라이언트가 아닌 서버용이라 사용할 수 없었다.

  • 아래 링크는 Seanox Spring WebDAV 라이브러리 깃허브 주소이다.

https://github.com/seanox/spring-webdav

 

GitHub - seanox/spring-webdav: WebDAV mapping for Spring Boot -- use an API like a network drive, open data as files, edit and s

WebDAV mapping for Spring Boot -- use an API like a network drive, open data as files, edit and save them. - seanox/spring-webdav

github.com

 

 

YML 파일 설정

Sardine 라이브러리 세팅 시 사용할 속성값들을 작성해 둔다.

webdav:
  username: "jihun"
  password: "12341234"
  base-url: "http://127.0.0.1/webdav" # PC IP를 작성해도 됨

 

 

WebDAVConfig

sardine 빈을 생성하고 위에서 작성한 yml 속성들을 선언한다.

import com.github.sardine.Sardine;
import com.github.sardine.SardineFactory;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * WebDAV 서버 연결을 위한 Sardine 클라이언트 설정 클래스
 */
@Getter
@Setter
@Configuration
@ConfigurationProperties(prefix = "webdav")
public class WebDAVConfig {

    /** ID */
    private String username;

    /** PW */
    private String password;

    /** WebDAV Base URL */
    private String baseUrl;

    /**
     * WebDAV 클라이언트 라이브러리 Bean
     *
     * @return WebDAV 클라이언트 빈
     */
    @Bean
    public Sardine sardine() {
        Sardine sardine = SardineFactory.begin(username, password);

        // 사전 인증 적용 (반드시 적용해야 함)
        sardine.enablePreemptiveAuthentication(baseUrl, 80, -1);

        return sardine;
    }

}

 

 

Sardine 빈 생성 시 사전 인증 적용

여기서 사전 인증이란 WebDAV 서버에 요청을 보낼 때마다 매번 요청 헤더에 인증 정보를 미리 포함시키는 방식을 의미한다.

즉, 서버가 인증을 요구(HTTP 401 Unauthorized)하기 전에 클라이언트가 먼저 인증 정보를 보내는 것이다.

 

Sardine 라이브러리의 기본 동작은 서버가 401 오류를 반환하면 그때서야 인증 헤더를 추가해 요청을 다시 보내는 것이다.

하지만 파일을 업로드하는 경우 InputStream을 사용한다면 다음과 같은 문제가 발생할 수 있다.


첫 번째 요청이 실패(401)하고 인증 정보를 추가해 재요청을 시도할 때 이미 스트림이 닫혀버려 데이터를 다시 읽을 수 없게 되어

NonRepeatableRequestException과 같은 예외가 발생한다.

그래서 사전 인증을 적용하면 이러한 문제를 방지할 수 있기 때문에 반드시 적용해야 한다.

첫 요청부터 인증 정보를 포함하므로, 서버에서 401 오류를 반환할 일이 없어지고, 스트림이 닫혀서 재전송이 불가능한 상황도 발생하지 않는다.

 

또한 인증 실패 후 재시도하는 추가 네트워크 왕복(Round-Trip)이 사라지기 때문에 업로드 성능도 자연스럽게 향상된다고 한다.

 

 

 

2. 파일 업로드 및 다운로드 API 구현

FileController.java

  • 파일 업로드 및 다운로드 API
import com.example.userservice.com.config.WebDAVConfig;
import com.github.sardine.Sardine;
import com.github.sardine.impl.SardineException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
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.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;

@Slf4j
@RestController
@RequestMapping("/api/v1/files")
@RequiredArgsConstructor
public class FileController {

    /** WebDAV 서버와의 HTTP 통신을 위한 Sardine 클라이언트 */
    private final Sardine sardine;

    /** WebDAV 관련 설정 정보 */
    private final WebDAVConfig webDAVConfig;

    /**
     * WebDAV 파일 업로드
     *
     * @param file 업로드할 파일
     * @return 업로드 완료된 파일 경로
     */
    @PostMapping("/upload")
    public ResponseEntity<String> uploadFile(@RequestParam("file") MultipartFile file) {
        if (file.isEmpty()) {
            return ResponseEntity.badRequest().body("File is empty");
        }

        if (file.getSize() > FileConstant.MAX_FILE_SIZE) {
            return ResponseEntity.status(HttpStatus.PAYLOAD_TOO_LARGE).body("File size exceeds limit (1GB)");
        }

        try (InputStream inputStream = file.getInputStream()) {
            String filePath = FileUtil.buildFilePath(file.getOriginalFilename());
            String webDAVFileUrl = webDAVConfig.getBaseUrl() + "/" + filePath;
            log.info("Uploading file to WebDAV: {}", webDAVFileUrl);

            sardine.put(webDAVFileUrl, inputStream);

            return ResponseEntity.ok(filePath);
        } catch (IOException e) {
            log.error("Sardine file upload failure: ", e);
            return ResponseEntity.internalServerError().body("Upload failed: " + e.getMessage());
        }

    }

    /**
     * WebDAV 서버에서 파일을 스트리밍 다운로드하는 엔드포인트
     * ex) GET /api/files/download?filePath=user-docs/2025/05/04/test.jpg
     *
     * @param filePath WebDAV 기본 URL 기준 상대 파일 경로 (예: "user-docs/2025/05/04/test.jpg")
     * @return 파일 스트림이 포함된 ResponseEntity (자동 다운로드 트리거)
     */
    @GetMapping("/download")
    public ResponseEntity<InputStreamResource> downloadFile(@RequestParam String filePath) {

        if (filePath.contains("..") || filePath.contains("\\")) {
            return ResponseEntity.badRequest().build();
        }

        String fullUrl = webDAVConfig.getBaseUrl() + "/" + filePath;

        try (InputStream is = sardine.get(fullUrl)) {
            String filename = FileUtil.extractFilename(filePath);
            String encodedFilename = URLEncoder.encode(filename, StandardCharsets.UTF_8)
                    .replaceAll("\\+", "%20");

            return ResponseEntity.ok()
                    .header(HttpHeaders.CONTENT_DISPOSITION,
                            "attachment; filename*=UTF-8''" + encodedFilename)
                    .contentType(MediaType.APPLICATION_OCTET_STREAM)
                    .body(new InputStreamResource(is));
        } catch (SardineException e) {
            if (e.getStatusCode() == 404) {
                return ResponseEntity.notFound().build();
            }

            log.error("Sardine file download failure: ", e);
            return ResponseEntity.status(e.getStatusCode()).build();
        } catch (IOException e) {
            log.error("File download failure: ", e);
            return ResponseEntity.internalServerError().build();
        }
    }

}

 

 

FileUtil 클래스

package com.example.fileservice.com.util;

import java.time.LocalDate;

public class FileUtil {

    /**
     * 파일명을 오늘 날짜(yyyy/MM/dd) 디렉터리 경로와 결합하여 파일 경로 생성.
     * 경로 내에 중복된 슬래시("//")가 있을 경우 하나의 슬래시("/")로 치환.
     *
     * @param filename 파일명 (예: "example.txt")
     * @return 날짜 디렉터리 경로와 결합된 파일 경로 (예: "2025/05/15/example.txt")
     */
    public static String buildFilePath(String filename) {
        String path = generateDateDirectory() + "/" + filename;
        return path.replace("//", "/");
    }

    /**
     * 오늘 날짜를 기준으로 연/월/일(yyyy/MM/dd) 형태의 디렉터리 경로 생성.
     *
     * @return 오늘 날짜의 디렉터리 경로 (예: "2025/05/15")
     */
    public static String generateDateDirectory() {
        LocalDate now = LocalDate.now();
        return now.getYear() + "/"
                + String.format("%02d", now.getMonthValue()) + "/"
                + String.format("%02d", now.getDayOfMonth());
    }

    /**
     * 파일 경로에서 실제 파일명 추출
     *
     * @param filePath 전체 파일 경로 문자열
     * @return 순수 파일명 (예: "test.jpg")
     */
    public static String extractFilename(String filePath) {
        return filePath.substring(filePath.lastIndexOf('/') + 1);
    }
}

 

 

3. 파일 업로드 및 다운로드 테스트

  • postman을 사용하여 해당 기능을 테스트한다.

 

HTTP 405 Method Not Allowed 오류가 반환되어 intellij 콘솔을 확인해 본다.

 

 

 

Sardine 라이브러리 로그에서는 자세히 보여주는 내용이 없으니까 CURL을 사용해서 파일 업로드 요청을 해보자.

 

curl -v -u jihun:12341234 -T "C:\Users\95_cs\Desktop\MSA_Architecture.png" http://127.0.0.1/webdav/2025/05/17/MSA_Architecture.png

-v: 동작 과정 상세 출력
-u: 인증 정보 지정
-T: HTTP PUT 메서드 사용
"C:\Users\사용자명\Desktop\MSA_Architecture.png": 파일 경로 및 파일명
http://127.0.0.1/webdav/2025/05/17/MSA_Architecture.png: 업로드할 WebDAV 서버 URL 및 저장 경로

 

 

요청결과 1

 

내용을 확인해 보니 Allow 메서드 종류에 PUT 메서드가 없다.

아래 요청결과 2는 위 이미지 하단의 자세한 오류 내용이다.

 

요청결과 2

<div class="content-container">
  <h3>HTTP 오류 405.0 - Method Not Allowed</h3>
  <h4>잘못된 메서드(HTTP 동사)가 사용되고 있으므로 지금 찾고 있는 페이지를 표시할 수 없습니다.</h4>
</div>
<div class="content-container">
 <fieldset><h4>가능성이 높은 원인:</h4>
  <ul>  <li>요청 처리 모듈에서 허용되지 않는 HTTP 동사가 웹 서버에 보낸 요청에서 사용되었습니다.</li>   <li>잘못된 HTTP 동사가 포함된 요청이 서버에 전송되었습니다.</li>        <li>요청이 정적 콘텐츠를 위한 것이 며 GET 또는 HEAD 이외의 HTTP 동사를 포함하고 있습니다.</li>     <li>HTTP 동사 POST를 사용하여 가상 디렉터리에 요청이 전송되었으며 기본 문서는 GET 또는 HEAD 이외의  HTTP 동사를 지원하지 않는 정적 파일입니다.</li> </ul>
 </fieldset>
</div>
<div class="content-container">
 <fieldset><h4>가능한 해결 방법:</h4>
  <ul>  <li>이 요청이 전송되는 모듈 처리기에서 사용할 수 있는 동사 목록을 검사하여 이 동사가 해당 웹 사이트에서 허용되는지 확인하십시오.</li>   <li>IIS 로그 파일에서 요청에 허용되지 않는 동사를 확인하십시오.</li>       <li>실패한 요청에서 이 HTTP 상태 코드를 추적하는 추적 규칙을 만드십시오. 실패한 요청에 대한 추적 규칙을 만드는 방법을 자세하게 보려면 <a href="http://go.microsoft.com/fwlink/?LinkID=66439">여기</a>를 클 릭하십시오. </li> </ul>
 </fieldset>
</div>

<div class="content-container">
 <fieldset><h4>자세한 오류 정보:</h4>
  <div id="details-left">
   <table border="0" cellpadding="0" cellspacing="0">
    <tr class="alt"><th>모듈</th><td>&nbsp;&nbsp;&nbsp;WebDAVModule</td></tr>
    <tr><th>알림</th><td>&nbsp;&nbsp;&nbsp;MapRequestHandler</td></tr>
    <tr class="alt"><th>처리기</th><td>&nbsp;&nbsp;&nbsp;WebDAV</td></tr>
    <tr><th>오류 코드</th><td>&nbsp;&nbsp;&nbsp;0x00000000</td></tr>

   </table>
  </div>
  <div id="details-right">
   <table border="0" cellpadding="0" cellspacing="0">
    <tr class="alt"><th>요청한 URL</th><td>&nbsp;&nbsp;&nbsp;http://127.0.0.1:80/webdav/2025/05/17/MSA_Architecture.png</td></tr>
    <tr><th>실제 경로</th><td>&nbsp;&nbsp;&nbsp;D:\webdav_files\2025\05\17\MSA_Architecture.png</td></tr>
    <tr class="alt"><th>로그온 방법</th><td>&nbsp;&nbsp;&nbsp;Basic</td></tr>
    <tr><th>로그온 사용자</th><td>&nbsp;&nbsp;&nbsp;jihun</td></tr>

   </table>
   <div class="clear"></div>
  </div>
 </fieldset>
</div>

<div class="content-container">
 <fieldset><h4>추가 정보:</h4>
  이 오류는 요청 처리 모듈의 처리기에서 허용하지 않는 HTTP 동사가 웹 서버에 전송된 요청에 들어 있다는 것을 의미합니다.
  <p><a href="https://go.microsoft.com/fwlink/?LinkID=62293&amp;IIS70Error=405,0,0x00000000,19045">상세 정보 보기 &raquo;</a></p>

 </fieldset>
</div>

 

 

PUT 메서드가 허용되지 않았는지 확인하기 위해 IIS 설정에서 요청 필터링 메뉴에 접속하여 확인해 보니

PUT 메서드는 이미 허용돼 있었다.

 

 

 

일단 마이크로소프트에 접속하여 HTTP 405 에러와 관련된 IIS 정보를 찾아보자.

 

https://learn.microsoft.com/ko-kr/troubleshoot/developer/webapps/iis/health-diagnostic-performance/http-status-code

 

HTTP 상태 코드 개요 - Internet Information Services

이 문서에서는 IIS의 HTTP 상태 코드 목록을 제공합니다.

learn.microsoft.com

https://learn.microsoft.com/ko-kr/troubleshoot/developer/webapps/iis/site-behavior-performance/http-error-405-website

 

인터넷 정보 서비스(IIS) 웹 사이트를 방문할 때 HTTP 오류 405.0 - Internet Information Services

클라이언트 요청이 HTTP 사양을 준수하지 않는 HTTP 동사를 사용하거나 클라이언트가 POST 메서드를 사용하여 정적 HTML 페이지로 요청을 보내기 때문에 발생하는 문제를 설명합니다.

learn.microsoft.com

 

 

해당 문제에 대해 자료를 찾아봤을 때 놓친 것이 있을 수 있는 windows 기능과 IIS의 요청 필터링, 모듈, 인증, 처리기 부분에서 수정하라는 내용이 많아 모두 확인하여 수정해 보았지만 해결되지 않았다.

 

그래서 뭐가 문제인지 처음부터 다시 생각해 보기로 했다.

 

windows 기능 켜기/끄기에서 필요한 설정을 모두 체크했는지, IIS 설정에서 인증 방법, WebDAV 제작 규칙 등을 잘 설정했는지를 생각하다가 파일탐색기에서 직접 추가한 폴더들을 보는 순간 "아... 내가 설정한 모든 것들이 webdav 폴더에만 적용이 됐겠구나"라는 생각이 들어 Default Web Site를 선택하고 인증, 제작 규칙을 위에서 했던 설정들과 동일하게 등록한 후 다시 테스트를 진행했다.

 

(Default Web Site 설정 시 webdav 폴더에 적용한 인증, 제작 규칙 등의 정보를 삭제해주어야 설정 정보들이 충돌되지 않는다)

 

처음에 2025, 05, 18 디렉터리가 없어 webdav 가상 디렉터리만 있는 것을 보고 저기에만 등록하면 될 줄 알았던 것이 문제였다.

 

 

 

테스트 결과

이미지 파일 1개를 업로드했다.

 

 

웹에서도 업로드한 파일이 정상적으로 조회된다.

 

 

파일 업로드가 정상적으로 동작하였으니 파일 다운로드 기능을 테스트한다.

 

 

파일 다운로드 테스트

 

 

파라미터는 방금 업로드한 파일의 파일경로로 설정한다.

파일을 다운로드할 것이기 때문에 우측 파란 Send 버튼 옆을 클릭하고 Send and Download를 클릭한다.

 

 

포스트맨으로 테스트하기 때문에 다운로드 받는 파일의 파일명을 지정해야 한다.

 

 

 

이전에 업로드한 이미지 파일이 정상적으로 다운로드된 것을 확인할 수 있다. 

 

 

 

 

Reference

https://www.elfarchive.org/2018/08/windows-10-webdav.html
https://srvr.tistory.com/34
저작자표시 비영리 변경금지 (새창열림)

'Spring Boot' 카테고리의 다른 글

[IntelliJ] Gradle 빌드 오류: metadata.bin 파일을 찾을 수 없습니다 - 문제 해결  (4) 2024.10.20
[Spring Boot] PowerShell에서 springboot 앱 실행 (maven spring-boot:run)  (0) 2024.04.26
'Spring Boot' 카테고리의 다른 글
  • [IntelliJ] Gradle 빌드 오류: metadata.bin 파일을 찾을 수 없습니다 - 문제 해결
  • [Spring Boot] PowerShell에서 springboot 앱 실행 (maven spring-boot:run)
masjeong
masjeong
교양이란 화를 내지 않으면서도 자신의 신념을 잃지 않은 채 어떤 얘기라도 들을 수 있는 능력을 말한다 - 로버트 프로스트
  • masjeong
    나자바바라쿠배
    masjeong
  • 전체
    오늘
    어제
    • 전체보기 (28)
      • Spring Boot (3)
      • Spring Batch (1)
      • MSA (3)
      • Docker (1)
      • 백준 (16)
      • 자료구조 (3)
      • Kafka (0)
      • DB (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    MSA
    백준11723
    백준3041
    2449
    큐
    진법변환
    18111
    11561
    이진탐색
    Cloud Native Architecture
    executortype
    정렬
    이분탐색
    알고리즘
    spring batch
    ExecutionContext
    mariadb
    spring cloud Gateway
    Java
    18110
    오블완
    15829
    ratelimiter
    SpringCloud
    cloud native application
    springboot
    gradle 오류
    백준
    Queue
    티스토리챌린지
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
masjeong
[Spring Boot] Windows10 WebDAV 서버 파일 업로드 및 다운로드
상단으로

티스토리툴바