From 3f464c5c51434b0970054ae2d88aadf61c4d0cb2 Mon Sep 17 00:00:00 2001 From: MyosoonHwang <132902744+MyosoonHwang@users.noreply.github.com> Date: Mon, 26 May 2025 15:57:02 +0900 Subject: [PATCH] =?UTF-8?q?feat:=207=EC=A3=BC=EC=B0=A8=20=EA=B3=BC?= =?UTF-8?q?=EC=A0=9C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/cloth/api/ClothRestController.java | 37 ++++++++ .../application/ClothImageQueryService.java | 9 ++ .../ClothImageQueryServicelmpl.java | 30 ++++++ .../cloth/application/ClothService.java | 13 +++ .../cloth/application/ClothServicelmpl.java | 91 +++++++++++++++++++ .../cloth/converter/ClothConverter.java | 30 ++++++ .../domain/cloth/dto/ClothRequestDTO.java | 48 ++++++++++ .../domain/cloth/dto/ClothResponseDTO.java | 45 +++++++++ .../cloth/exception/ClothException.java | 10 ++ .../annotation/CheckLowerUpperTempBound.java | 19 ++++ .../CheckLowerUpperTempBoundValidator.java | 29 ++++++ .../domain/exception/MemberException.java | 10 ++ .../goorm/domain/model/Enum/ClothSort.java | 12 +++ .../model/exception/annotation/CheckPage.java | 19 ++++ .../exception/annotation/CheckPageSize.java | 19 ++++ .../validator/CheckPageSizeValidator.java | 4 + .../validator/CheckPageValidator.java | 27 ++++++ .../goorm/global/config/SwaggerConfig.java | 54 +++++++++++ 18 files changed, 506 insertions(+) create mode 100644 src/main/java/study/goorm/domain/cloth/api/ClothRestController.java create mode 100644 src/main/java/study/goorm/domain/cloth/application/ClothImageQueryService.java create mode 100644 src/main/java/study/goorm/domain/cloth/application/ClothImageQueryServicelmpl.java create mode 100644 src/main/java/study/goorm/domain/cloth/application/ClothService.java create mode 100644 src/main/java/study/goorm/domain/cloth/application/ClothServicelmpl.java create mode 100644 src/main/java/study/goorm/domain/cloth/converter/ClothConverter.java create mode 100644 src/main/java/study/goorm/domain/cloth/dto/ClothRequestDTO.java create mode 100644 src/main/java/study/goorm/domain/cloth/dto/ClothResponseDTO.java create mode 100644 src/main/java/study/goorm/domain/cloth/exception/ClothException.java create mode 100644 src/main/java/study/goorm/domain/cloth/exception/annotation/CheckLowerUpperTempBound.java create mode 100644 src/main/java/study/goorm/domain/cloth/exception/validator/CheckLowerUpperTempBoundValidator.java create mode 100644 src/main/java/study/goorm/domain/member/domain/exception/MemberException.java create mode 100644 src/main/java/study/goorm/domain/model/Enum/ClothSort.java create mode 100644 src/main/java/study/goorm/domain/model/exception/annotation/CheckPage.java create mode 100644 src/main/java/study/goorm/domain/model/exception/annotation/CheckPageSize.java create mode 100644 src/main/java/study/goorm/domain/model/exception/validator/CheckPageSizeValidator.java create mode 100644 src/main/java/study/goorm/domain/model/exception/validator/CheckPageValidator.java create mode 100644 src/main/java/study/goorm/global/config/SwaggerConfig.java diff --git a/src/main/java/study/goorm/domain/cloth/api/ClothRestController.java b/src/main/java/study/goorm/domain/cloth/api/ClothRestController.java new file mode 100644 index 0000000..259b928 --- /dev/null +++ b/src/main/java/study/goorm/domain/cloth/api/ClothRestController.java @@ -0,0 +1,37 @@ +package study.goorm.domain.cloth.api; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import study.goorm.domain.cloth.application.ClothService; +import study.goorm.domain.cloth.dto.ClothResponseDTO; +import study.goorm.global.common.response.BaseResponse; +import study.goorm.global.error.code.status.SuccessStatus; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/cloth-") +@Validated +public class ClothRestController { + + private final ClothService clothService; + + @GetMapping("/{cloth-id}/edit-view") + @Operation(summary = "특정 Cloth에 대한 정보를 수정용으로 조회하는 API", description = "Path Variable로 clothId를 던져주세요.") + @ApiResponse({ + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "CLOTH_200", description = "OK, 성공적으로 조회되었습니다."), + }) public BaseResponse( + @PathVariable(name = "cloth-id") Long clothId + ) { + ClothResponseDTO.ClothEditViewResult result = clothService.getClothEditView(clothId); + + return BaseResponse.onSuccess(SuccessStatus.CLOTH_VIEW_SUCCESS, result); + + } +} + diff --git a/src/main/java/study/goorm/domain/cloth/application/ClothImageQueryService.java b/src/main/java/study/goorm/domain/cloth/application/ClothImageQueryService.java new file mode 100644 index 0000000..d76ea9e --- /dev/null +++ b/src/main/java/study/goorm/domain/cloth/application/ClothImageQueryService.java @@ -0,0 +1,9 @@ +package study.goorm.domain.cloth.application; + +import study.goorm.domain.cloth.domain.entity.Cloth; + +import java.util.Map; + +public interface ClothImageQueryService { + Map getFirstImageUrlMap(Iterable clothes); +} diff --git a/src/main/java/study/goorm/domain/cloth/application/ClothImageQueryServicelmpl.java b/src/main/java/study/goorm/domain/cloth/application/ClothImageQueryServicelmpl.java new file mode 100644 index 0000000..eaf23a7 --- /dev/null +++ b/src/main/java/study/goorm/domain/cloth/application/ClothImageQueryServicelmpl.java @@ -0,0 +1,30 @@ +package study.goorm.domain.cloth.application; + +import study.goorm.domain.cloth.domain.entity.Cloth; +import study.goorm.domain.cloth.domain.entity.ClothImage; +import study.goorm.domain.cloth.domain.repository.ClothImageRepository; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + +public class ClothImageQueryServicelmpl implements ClothImageQueryService{ + private final ClothImageRepository clothImageRepository; + + @Override + public Map getFirstImageUrlMap(Iterable clothes) { + List clothIds = StreamSupport.stream(clothes.spliterator(), false) + .map(Cloth::getId) + .toList(); + + // cloth_id 기준으로 첫 이미지만 가져오는 쿼리 (IN 절 + group by 또는 distinct 필요) + List firstImages = clothImageRepository.findFirstImagesByClothIds(clothIds); + + return firstImages.stream() + .collect(Collectors.toMap( + image -> image.getCloth().getId(), + ClothImage::getImageUrl + )); + } +} diff --git a/src/main/java/study/goorm/domain/cloth/application/ClothService.java b/src/main/java/study/goorm/domain/cloth/application/ClothService.java new file mode 100644 index 0000000..900da94 --- /dev/null +++ b/src/main/java/study/goorm/domain/cloth/application/ClothService.java @@ -0,0 +1,13 @@ +package study.goorm.domain.cloth.application; + +import org.springframework.web.multipart.MultipartFile; +import study.goorm.domain.cloth.dto.ClothRequestDTO; +import study.goorm.domain.cloth.dto.ClothResponseDTO; +import study.goorm.domain.model.Enum.ClothSort; + +public interface ClothService { + ClothResponseDTO.ClothEditViewResult getClothEditView(Long clothId); + ClothResponseDTO.MemberCloseResult getMemberCloset(String clokeyId, ClothSort sort, int page, int size); + ClothResponseDTO.ClothCreateResult createCloth(ClothRequestDTO.ClothCreateRequest clothCreateResult, MultipartFile image); +} + diff --git a/src/main/java/study/goorm/domain/cloth/application/ClothServicelmpl.java b/src/main/java/study/goorm/domain/cloth/application/ClothServicelmpl.java new file mode 100644 index 0000000..ecfa598 --- /dev/null +++ b/src/main/java/study/goorm/domain/cloth/application/ClothServicelmpl.java @@ -0,0 +1,91 @@ +package study.goorm.domain.cloth.application; + +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; +import study.goorm.domain.cloth.converter.ClothConverter; +import study.goorm.domain.cloth.domain.entity.Category; +import study.goorm.domain.cloth.domain.entity.Cloth; +import study.goorm.domain.cloth.domain.entity.ClothImage; +import study.goorm.domain.cloth.domain.repository.ClothImageRepository; +import study.goorm.domain.cloth.domain.repository.ClothRepository; +import study.goorm.domain.cloth.dto.ClothRequestDTO; +import study.goorm.domain.cloth.dto.ClothResponseDTO; +import study.goorm.domain.cloth.exception.ClothException; +import study.goorm.domain.member.domain.entity.Member; +import study.goorm.domain.member.domain.exception.MemberException; +import study.goorm.domain.model.Enum.ClothSort; +import study.goorm.global.error.code.status.ErrorStatus; + +import java.util.List; +import java.util.Map; + +@Service +@RequiredArgsConstructor +public class ClothServicelmpl implements ClothService { + private final ClothRepository clothRepository; + private final ClothImageRepository clothImageRepository; + + @Override + @Transactional(readOnly = true) + public ClothResponseDTO.ClothEditViewResult getClothEditView(Long clothId) { + + Cloth cloth = clothRepository.findById(clothId).orElseThrow(()-> new ClothException(ErrorStatus.NO_SUCH_CLOTH)); + List clothImageUrls = clothImageRepository.findAllByCloth(cloth); + + String firstImageUrl = clothImageUrls.stream() + .findFirst() + .map(ClothImage::getImageUrl) + .orElseThrow(() -> new ClothException(ErrorStatus.NO_ClOTH_IMAGE)); + + return ClothConverter.toClothEditViewResult(cloth, clothImageUrl); + } + + @Override + public ClothResponseDTO.MemberCloseResult getMemberCloset(String clokeyId, ClothSort sort, int page, int size) { + return null; + } + + @Override + @Transactional + public ClothResponseDTO.ClothCreateResult createCloth(ClothRequestDTO.ClothCreateRequest clothCreateResult, MultipartFile image) { + + Member member = memberRepository.findById(clothCreateResult.getMemberId()) + .orElseThrow(()-> new MemberException(ErrorStatus.NO_SUCH_MEMBER)); + + Category category = categoryRepository.findById(clothCreateResult.getCategoryId()) + .orElseThrow(()-> new ClothException(ErrorStatus.NO_SUCH_CATEGORY)); + + Cloth newCloth = Cloth.builder() + .name(clothCreateResult.getName()) + .wearNum(0) + .season(clothCreateResult.getSeasons()) + .tempUpperBound(clothCreateResult.getTempUpperBound()) + .tempLowerBound(clothCreateResult.getTempLowerBound()) + .thicknessLevel(clothCreateResult.getThicknessLevel()) + .clothUrl(clothCreateResult.getClothUrl()) + .brand(clothCreateResult.getBrand()) + .category(category) + .member(member) + .build(); + + clothRepository.save(newCloth); + + ClothImage newClothImage = ClothImage.builder() + .cloth(newCloth) + .imageUrl("아직 S3를 구현하지 않아서 url이 없어용") + .build(); + + clothImageRepository.save(newClothImage); + + return ClothConverter.toClothCreateResult(newCloth); + } + + + + + + +} diff --git a/src/main/java/study/goorm/domain/cloth/converter/ClothConverter.java b/src/main/java/study/goorm/domain/cloth/converter/ClothConverter.java new file mode 100644 index 0000000..19b15c5 --- /dev/null +++ b/src/main/java/study/goorm/domain/cloth/converter/ClothConverter.java @@ -0,0 +1,30 @@ +package study.goorm.domain.cloth.converter; + +import lombok.Getter; +import study.goorm.domain.cloth.domain.entity.Cloth; +import study.goorm.domain.cloth.dto.ClothResponseDTO; + +@Getter +public class ClothConverter { + public static ClothResponseDTO.ClothEditViewResult toClothEditViewResult(Cloth cloth, String clothImageUrl) { + return ClothResponseDTO.ClothEditViewResult.builder() + .id(cloth.getId()) + .brand(cloth.getBrand()) + .categoryId(cloth.getCategory().getId()) + .clothUrl(cloth.getClothUrl()) + .imageUrl(clothImageUrl) + .name(cloth.getName()) + .seasons(cloth.getSeason()) + .tempLowerBound(cloth.getTempLowerBound()) + .tempUpperBound(cloth.getTempUpperBound()) + .thicknessLevel(cloth.getThicknessLevel()) + .build(); + } + + + public static ClothResponseDTO.ClothCreateResult toClothCreateResult(Cloth cloth){ + return ClothResponseDTO.ClothCreateResult.builder() + .id(cloth.getId()) + .build(); + } +} diff --git a/src/main/java/study/goorm/domain/cloth/dto/ClothRequestDTO.java b/src/main/java/study/goorm/domain/cloth/dto/ClothRequestDTO.java new file mode 100644 index 0000000..3cd7d52 --- /dev/null +++ b/src/main/java/study/goorm/domain/cloth/dto/ClothRequestDTO.java @@ -0,0 +1,48 @@ +package study.goorm.domain.cloth.dto; + +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.springframework.stereotype.Component; +import study.goorm.domain.cloth.exception.annotation.CheckLowerUpperTempBound; +import study.goorm.domain.model.Enum.Season; +import study.goorm.domain.model.Enum.ThicknessLevel; + +import java.util.List; + + +public class ClothRequestDTO { + @Builder + @Getter + @NoArgsConstructor + @AllArgsConstructor + @CheckLowerUpperTempBound + public static class ClothCreateRequest { + private Long memberId; + + private Long categoryId; + + private String name; + + private List seasons; + + @Max(40) + @Min(-20) + private Integer tempUpperBound; + + @Max(40) + @Min(-20) + private Integer tempLowerBound; + + private ThicknessLevel thicknessLevel; + + private String clothUrl; + + private String brand; + + } + +} diff --git a/src/main/java/study/goorm/domain/cloth/dto/ClothResponseDTO.java b/src/main/java/study/goorm/domain/cloth/dto/ClothResponseDTO.java new file mode 100644 index 0000000..3f7bfb5 --- /dev/null +++ b/src/main/java/study/goorm/domain/cloth/dto/ClothResponseDTO.java @@ -0,0 +1,45 @@ +package study.goorm.domain.cloth.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import study.goorm.domain.model.Enum.Season; +import study.goorm.domain.model.Enum.ThicknessLevel; + +import java.util.List; + +@Getter +public class ClothResponseDTO { + + @Builder + @Getter + @NoArgsConstructor + @AllArgsConstructor + public class ClothCreateResult { + private Long id; + } + + @Builder + @Getter + @NoArgsConstructor + @AllArgsConstructor + public class ClothEditViewResult { + private Long id; + private String name; + private List seasons; + private int tempUpperBound; + private int tempLowerBound; + private ThicknessLevel thicknessLevel; + private String clothUrl; + private String brand; + private String imageUrl; + private Long categoryId; + } + + + public class MemberCloseResult{ + + } + +} diff --git a/src/main/java/study/goorm/domain/cloth/exception/ClothException.java b/src/main/java/study/goorm/domain/cloth/exception/ClothException.java new file mode 100644 index 0000000..fd3db1b --- /dev/null +++ b/src/main/java/study/goorm/domain/cloth/exception/ClothException.java @@ -0,0 +1,10 @@ +package study.goorm.domain.cloth.exception; + +import study.goorm.global.error.code.BaseErrorCode; +import study.goorm.global.exception.GeneralException; + +public class ClothException extends GeneralException { + public ClothException(BaseErrorCode code) { + super(code); + } +} diff --git a/src/main/java/study/goorm/domain/cloth/exception/annotation/CheckLowerUpperTempBound.java b/src/main/java/study/goorm/domain/cloth/exception/annotation/CheckLowerUpperTempBound.java new file mode 100644 index 0000000..e66c073 --- /dev/null +++ b/src/main/java/study/goorm/domain/cloth/exception/annotation/CheckLowerUpperTempBound.java @@ -0,0 +1,19 @@ +package study.goorm.domain.cloth.exception.annotation; + +import jakarta.validation.Constraint; +import jakarta.validation.Payload; +import study.goorm.domain.cloth.exception.validator.CheckLowerUpperTempBoundValidator; + +import java.lang.annotation.*; + +@Documented +@Constraint(validatedBy = CheckLowerUpperTempBoundValidator.class) +@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER}) +@Retention(RetentionPolicy.RUNTIME) +public @interface CheckLowerUpperTempBound { + String message() default "상한 온도는 하한 온도보다 높아야 합니다."; + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/src/main/java/study/goorm/domain/cloth/exception/validator/CheckLowerUpperTempBoundValidator.java b/src/main/java/study/goorm/domain/cloth/exception/validator/CheckLowerUpperTempBoundValidator.java new file mode 100644 index 0000000..901e353 --- /dev/null +++ b/src/main/java/study/goorm/domain/cloth/exception/validator/CheckLowerUpperTempBoundValidator.java @@ -0,0 +1,29 @@ +package study.goorm.domain.cloth.exception.validator; + +import jakarta.validation.ConstraintValidator; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import study.goorm.domain.cloth.dto.ClothRequestDTO; +import study.goorm.domain.cloth.exception.annotation.CheckLowerUpperTempBound; + +@Component +@RequiredArgsConstructor +public class CheckLowerUpperTempBoundValidator { + + @Override + public void initialize(CheckLowerUpperTempBound constraintAnnotation) { + ConstraintValidator.super.initialize(constraintAnnotation); + } + + @Override + public boolean isValid(ClothRequestDTO.ClothCreateRequest request, ConstraintValidatorContext context) { + boolean isValid = request.getTempLowerBound() <= request.getTempUpperBound(); + + if (!isValid) { + context.disableDefaultConstraintViolation(); + context.buildConstraintViolationWithTemplate(ErrorStatus.LOWER_TEMP_BIGGER_THAN_UPPER_TEMP.toString()).addConstraintViolation(); + } + + return isValid; + } +} diff --git a/src/main/java/study/goorm/domain/member/domain/exception/MemberException.java b/src/main/java/study/goorm/domain/member/domain/exception/MemberException.java new file mode 100644 index 0000000..7d4f7d1 --- /dev/null +++ b/src/main/java/study/goorm/domain/member/domain/exception/MemberException.java @@ -0,0 +1,10 @@ +package study.goorm.domain.member.domain.exception; + +import study.goorm.global.error.code.BaseErrorCode; +import study.goorm.global.exception.GeneralException; + +public class MemberException extends GeneralException { + public MemberException(BaseErrorCode code) { + super(code); + } +} diff --git a/src/main/java/study/goorm/domain/model/Enum/ClothSort.java b/src/main/java/study/goorm/domain/model/Enum/ClothSort.java new file mode 100644 index 0000000..58f322a --- /dev/null +++ b/src/main/java/study/goorm/domain/model/Enum/ClothSort.java @@ -0,0 +1,12 @@ +package study.goorm.domain.model.Enum; + +public enum ClothSort { + //착용순 + WEAR, + //미착용순 + NOT_WEAR, + //최신 등록순 + LATEST, + //오래된 순 + OLDEST +} diff --git a/src/main/java/study/goorm/domain/model/exception/annotation/CheckPage.java b/src/main/java/study/goorm/domain/model/exception/annotation/CheckPage.java new file mode 100644 index 0000000..23cf1ef --- /dev/null +++ b/src/main/java/study/goorm/domain/model/exception/annotation/CheckPage.java @@ -0,0 +1,19 @@ +package study.goorm.domain.model.exception.annotation; + +import jakarta.validation.Constraint; +import jakarta.validation.Payload; +import study.goorm.domain.model.exception.validator.CheckPageValidator; + +import java.lang.annotation.*; + +@Documented +@Constraint(validatedBy = CheckPageValidator.class) +@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER}) +@Retention(RetentionPolicy.RUNTIME) +public @interface CheckPage { + + String message() default "페이지는 1이상 부터 입력이 가능합니다."; + + Class[] groups() default {}; + Class[] payload() default {}; +} diff --git a/src/main/java/study/goorm/domain/model/exception/annotation/CheckPageSize.java b/src/main/java/study/goorm/domain/model/exception/annotation/CheckPageSize.java new file mode 100644 index 0000000..5ecb0c0 --- /dev/null +++ b/src/main/java/study/goorm/domain/model/exception/annotation/CheckPageSize.java @@ -0,0 +1,19 @@ +package study.goorm.domain.model.exception.annotation; + +import jakarta.validation.Constraint; +import jakarta.validation.Payload; +import study.goorm.domain.model.exception.validator.CheckPageSizeValidator; + +import java.lang.annotation.*; + +@Documented +@Constraint(validatedBy = CheckPageSizeValidator.class) +@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER}) +@Retention(RetentionPolicy.RUNTIME) +public @interface CheckPageSize { + String message() default "페이지 크기는 1이상 부터 입력이 가능합니다."; + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/src/main/java/study/goorm/domain/model/exception/validator/CheckPageSizeValidator.java b/src/main/java/study/goorm/domain/model/exception/validator/CheckPageSizeValidator.java new file mode 100644 index 0000000..0a33b72 --- /dev/null +++ b/src/main/java/study/goorm/domain/model/exception/validator/CheckPageSizeValidator.java @@ -0,0 +1,4 @@ +package study.goorm.domain.model.exception.validator; + +public class CheckPageSizeValidator { +} diff --git a/src/main/java/study/goorm/domain/model/exception/validator/CheckPageValidator.java b/src/main/java/study/goorm/domain/model/exception/validator/CheckPageValidator.java new file mode 100644 index 0000000..9345cdb --- /dev/null +++ b/src/main/java/study/goorm/domain/model/exception/validator/CheckPageValidator.java @@ -0,0 +1,27 @@ +package study.goorm.domain.model.exception.validator; + +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import study.goorm.domain.model.exception.annotation.CheckPage; +import study.goorm.global.error.code.status.ErrorStatus; + +public class CheckPageValidator implements ConstraintValidator { + + @Override + public void initialize(CheckPage constraintAnnotation) { + ConstraintValidator.super.initialize(constraintAnnotation); + } + + @Override + public boolean isValid(Integer page, ConstraintValidatorContext context) { + boolean isValid = page >= 1; + + if (!isValid) { + context.disableDefaultConstraintViolation(); + context.buildConstraintViolationWithTemplate(ErrorStatus.PAGE_UNDER_ONE.toString()).addConstraintViolation(); + } + + return isValid; + + } +} diff --git a/src/main/java/study/goorm/global/config/SwaggerConfig.java b/src/main/java/study/goorm/global/config/SwaggerConfig.java new file mode 100644 index 0000000..80d0178 --- /dev/null +++ b/src/main/java/study/goorm/global/config/SwaggerConfig.java @@ -0,0 +1,54 @@ +package study.goorm.global.config; + +import io.swagger.v3.oas.models.Components; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.security.SecurityRequirement; +import io.swagger.v3.oas.models.security.SecurityScheme; +import io.swagger.v3.oas.models.servers.Server; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.MediaType; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; + +import java.util.ArrayList; +import java.util.List; + +@Configuration +public class SwaggerConfig { + + /** + * Multipart + JSON 동시에 Swagger에서 테스트할 때 + * content-type null → application/octet-stream 으로 처리되어 Jackson에서 인식 못하는 문제를 해결 + */ + @Autowired + public void configureMessageConverter(MappingJackson2HttpMessageConverter converter) { + List supportMediaTypes = new ArrayList<>(converter.getSupportedMediaTypes()); + supportMediaTypes.add(MediaType.APPLICATION_OCTET_STREAM); + converter.setSupportedMediaTypes(supportMediaTypes); + } + + @Bean + public OpenAPI goormStudyAPI() { + Info info = new Info() + .title("GOORM API") + .description("GOORM API 명세서") + .version("1.0.0"); + + String jwtSchemeName = "JWT TOKEN"; + SecurityRequirement securityRequirement = new SecurityRequirement().addList(jwtSchemeName); + Components components = new Components() + .addSecuritySchemes(jwtSchemeName, new SecurityScheme() + .name(jwtSchemeName) + .type(SecurityScheme.Type.HTTP) + .scheme("bearer") + .bearerFormat("JWT")); + + return new OpenAPI() + .addServersItem(new Server().url("/")) + .info(info) + .addSecurityItem(securityRequirement) + .components(components); + } +}