본문으로 건너뛰기
Advertisement

3.6 파일 업로드 처리 (MultipartFile)

웹 애플리케이션에서 이미지나 문서를 서버로 업로드할 때 사용하는 MultipartFile 처리 방법과 관련 설정을 알아봅니다.

1. 파일 업로드의 원리 (multipart/form-data)

파일은 텍스트가 아닌 대용량 바이너리 데이터입니다. 따라서 일반적인 application/json으로는 효율적으로 보내기 어렵습니다. HTTP는 이를 위해 multipart/form-data 라는 특수한 인코딩 방식을 사용하며, 스프링 매개변수에서는 이를 MultipartFile 인터페이스로 받습니다.

2. 파일 업로드 구현 예제

컨트롤러 구현

@RestController
@Slf4j
public class FileUploadController {

@PostMapping("/api/upload")
public String uploadFile(@RequestParam("file") MultipartFile file) throws IOException {
if (file.isEmpty()) {
return "파일이 없습니다.";
}

log.info("파일명: {}", file.getOriginalFilename());
log.info("파일 크기: {} bytes", file.getSize());
log.info("컨텐츠 타입: {}", file.getContentType());

// 로컬 디렉토리에 저장 예시
String fullPath = "C:/uploads/" + file.getOriginalFilename();
file.transferTo(new File(fullPath));

return "업로드 성공: " + fullPath;
}
}

3. 업로드 용량 제한 설정 (application.yml)

스프링 부트는 기본적으로 파일 업로드 용량이 작게 설정되어 있습니다. 대용량 파일을 허용하려면 application.yml 파일에 설정을 추가해야 합니다.

spring:
servlet:
multipart:
max-file-size: 10MB # 파일 하나당 최대 용량
max-request-size: 50MB # 한 번의 HTTP 요청에 담긴 전체 최대 용량

4. 실무 고려 사항

  1. 파일명 중복 방지: 사용자가 올린 파일명을 그대로 쓰면 덮어쓰기가 발생할 수 있습니다. UUID 등을 사용하여 고유한 파일명을 생성하는 것이 안전합니다.
  2. 보안: 업로드된 파일이 실행 가능한 스크립트(.php, .sh)인 경우 서버가 해킹될 수 있습니다. 확장자 검증이나 이미지 프로세싱을 거치는 것이 좋습니다.
  3. 저장소 선택: 초기에는 서버 로컬 디렉토리에 저장하지만, 서비스 규모가 커지면 ** AWS S3**같은 전용 클라우드 스토리지에 저장하는 것이 일반적입니다.


3.9 OpenAPI(Swagger)로 API 문서 자동화

REST API를 제공할 때 요청/응답 스펙을 문서로 유지하고, 프론트엔드·외부 연동 개발자가 바로 호출해 볼 수 있게 하는 것이 실무 표준입니다. OpenAPI 3(구 Swagger)와 스프링 부트를 연동하면 컨트롤러 기반으로 API 문서를 자동 생성할 수 있습니다.

작성 기준: Spring Boot 3.2.x, springdoc-openapi-starter-webmvc-ui

1. 왜 API 문서 자동화인가?

  • 수동 문서: 코드와 문서가 어긋나기 쉽고, 버전 업데이트 시 누락이 생깁니다.
  • 자동 생성: 컨트롤러·DTO·애너테이션을 기반으로 문서가 생성되므로, 코드가 곧 문서가 됩니다.
  • Swagger UI: 브라우저에서 API 목록을 보고 바로 요청을 보내 테스트할 수 있습니다.

2. 의존성 추가 (springdoc-openapi)

Spring Boot 3에서는 springdoc-openapi를 사용합니다. (기존 springfox는 Boot 3 미지원)

dependencies {
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.3.0'
}

별도 설정 없이도 애플리케이션 기동 후 다음 URL로 접근할 수 있습니다.

  • Swagger UI: http://localhost:8080/swagger-ui.html
  • OpenAPI JSON: http://localhost:8080/v3/api-docs

3. 기본 설정 (application.yml)

springdoc:
api-docs:
path: /v3/api-docs
swagger-ui:
path: /swagger-ui.html
operationsSorter: method
tagsSorter: alpha

4. API 정보 및 태그 보강

전역 API 설명·버전·태그는 @OpenAPIDefinition, @Info로 보강할 수 있습니다.

import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class OpenApiConfig {

@Bean
public OpenAPI openAPI() {
return new OpenAPI()
.info(new Info()
.title("My REST API")
.version("1.0")
.description("서비스 API 명세"));
}
}

5. 컨트롤러·DTO에 설명 추가

@Operation, @Parameter, @Schema로 요청/응답 의미를 문서에 반영할 수 있습니다.

@RestController
@RequestMapping("/api/users")
@Tag(name = "User API", description = "회원 관련 API")
public class UserController {

@Operation(summary = "회원 단건 조회", description = "ID로 회원 정보를 조회합니다.")
@Parameter(name = "id", description = "회원 ID", required = true)
@ApiResponses({
@ApiResponse(responseCode = "200", description = "성공"),
@ApiResponse(responseCode = "404", description = "회원 없음")
})
@GetMapping("/{id}")
public ResponseEntity<ApiResponse<UserResponseDto>> getUser(@PathVariable Long id) {
// ...
}
}
@Schema(description = "회원 응답 DTO")
public record UserResponseDto(
@Schema(description = "회원 ID") Long id,
@Schema(description = "이메일") String email,
@Schema(description = "이름") String name
) {}

6. Spring Security와 함께 사용할 때

Actuator와 마찬가지로 Swagger UI·api-docs 경로를 인증에서 제외하거나, 개발 환경에서만 노출하는 경우가 많습니다.

@Configuration
public class SecurityConfig {

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(auth -> auth
.requestMatchers("/swagger-ui/**", "/v3/api-docs/**").permitAll()
.anyRequest().authenticated()
);
return http.build();
}
}

실무에서는 API 버전(예: /api/v1/)과 OpenAPI 문서 버전을 맞추고, CI에서 v3/api-docs를 JSON으로 추출해 API 컨트랙트 테스트나 클라이언트 코드 생성에 활용합니다.

Advertisement