Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 46 additions & 1 deletion src/main/java/zim/tave/memory/config/OpenApiConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
import io.swagger.v3.oas.annotations.info.Info;
import io.swagger.v3.oas.annotations.security.SecurityScheme;
import io.swagger.v3.oas.annotations.servers.Server;
import io.swagger.v3.oas.models.media.Schema;
import org.springdoc.core.customizers.OpenApiCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
Expand All @@ -29,4 +32,46 @@
in = SecuritySchemeIn.HEADER
)
public class OpenApiConfig {
}

@Bean
public OpenApiCustomizer apiResponseDtoVoidSchemaCustomizer() {
return openApi -> {

// Components๊ฐ€ null์ผ ๊ฒฝ์šฐ NPE ๋ฐฉ์ง€
if (openApi.getComponents() == null) {
openApi.setComponents(new Components());
}

// ApiResponseDto<Void> ์Šคํ‚ค๋งˆ๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ๋“ฑ๋ก
Schema<?> voidSchema = new Schema<>();
voidSchema.setType("object");
voidSchema.setDescription("๊ณตํ†ต ์‘๋‹ต ํ˜•์‹ (์—๋Ÿฌ ์‘๋‹ต์šฉ - data ํ•„๋“œ๋Š” null)");

Schema<?> codeSchema = new Schema<>();
codeSchema.setType("integer");
codeSchema.setDescription("์‘๋‹ต ์ฝ”๋“œ");
voidSchema.addProperty("code", codeSchema);

Schema<?> messageSchema = new Schema<>();
messageSchema.setType("string");
messageSchema.setDescription("์‘๋‹ต ๋ฉ”์‹œ์ง€");
voidSchema.addProperty("message", messageSchema);

Schema<?> dataSchema = new Schema<>();
dataSchema.setType("object");
dataSchema.setNullable(true);
dataSchema.setDescription("๋ฐ์ดํ„ฐ (์—๋Ÿฌ ์‘๋‹ต์—์„œ๋Š” ํ•ญ์ƒ null)");
voidSchema.addProperty("data", dataSchema);

// addProperty ์‚ฌ์šฉ ์ œ๊ฑฐ โ†’ Map + setProperties ๋ฐฉ์‹์œผ๋กœ ์•ˆ์ „ํ•˜๊ฒŒ ์„ค์ •
Map<String, Schema<?>> properties = new LinkedHashMap<>();
properties.put("code", codeSchema);
properties.put("message", messageSchema);
properties.put("data", dataSchema);
voidSchema.setProperties(properties);

// ์Šคํ‚ค๋งˆ ๋“ฑ๋ก
openApi.getComponents().addSchemas("ApiResponseDtoVoid", voidSchema);
Copy link
Contributor

@chwwwon chwwwon Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

openApi.getComponents()๊ฐ€ null์ผ ๊ฒฝ์šฐ NPE๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์–ด Components ์กด์žฌ ์—ฌ๋ถ€ ๊ฒ€์‚ฌ/์ดˆ๊ธฐํ™” ํ›„ addSchemas ์‚ฌ์šฉํ•˜๋„๋ก ์ˆ˜์ •์ด ํ•„์š”ํ•˜๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค

// ์‚ฌ์šฉํ•˜๋Š” ๋ฒ„์ „์— ๋”ฐ๋ผ addProperties ์‚ฌ์šฉ ๋˜๋Š” Map์œผ๋กœ setProperties
        voidSchema.addProperties("code", codeSchema);
        voidSchema.addProperties("message", messageSchema);
        voidSchema.addProperties("data", dataSchema);

        openApi.getComponents().addSchemas("ApiResponseDtoVoid", voidSchema);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addProperties ๋ฉ”์„œ๋“œ

Map<String, Schema> props = new LinkedHashMap<>();
props.put("code", codeSchema);
props.put("message", messageSchema);
props.put("data", dataSchema);
voidSchema.setProperties(props);

};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,10 @@ public Operation customize(Operation operation, HandlerMethod handlerMethod) {
apiResponse.getContent().addMediaType("application/json", mediaType);
}

// Schema ์„ค์ • (ApiResponseDto ์ฐธ์กฐ)
// Schema ์„ค์ • (ApiResponseDto<Void> ์ฐธ์กฐ - ์—๋Ÿฌ ์‘๋‹ต์€ data๊ฐ€ null์ด๋ฏ€๋กœ ApiResponseDtoVoid๋กœ ์ƒ์„ฑ๋จ)
if (mediaType.getSchema() == null) {
io.swagger.v3.oas.models.media.Schema<?> schema = new io.swagger.v3.oas.models.media.Schema<>();
schema.set$ref("#/components/schemas/ApiResponseDto");
schema.set$ref("#/components/schemas/ApiResponseDtoVoid");
mediaType.setSchema(schema);
}

Expand Down Expand Up @@ -142,6 +142,8 @@ private ResponseCode mapErrorCodeToResponseCode(ErrorCode errorCode) {
case TRIP_NAME_REQUIRED -> ResponseCode.TRIP_NAME_REQUIRED;
case TRIP_NAME_TOO_LONG -> ResponseCode.TRIP_NAME_TOO_LONG;
case TRIP_DESCRIPTION_TOO_LONG -> ResponseCode.TRIP_DESCRIPTION_TOO_LONG;
case NOT_PAST_TRIP -> ResponseCode.NOT_PAST_TRIP;
case CANNOT_ADD_DIARY_TO_PAST_TRIP -> ResponseCode.CANNOT_ADD_DIARY_TO_PAST_TRIP;
case KAKAO_LOGIN_REQUIRED -> ResponseCode.KAKAO_LOGIN_REQUIRED;
case INCOMPLETE_USER_INFO -> ResponseCode.INCOMPLETE_USER_INFO;
case ALREADY_JOINED -> ResponseCode.ALREADY_JOINED;
Expand All @@ -151,6 +153,29 @@ private ResponseCode mapErrorCodeToResponseCode(ErrorCode errorCode) {
case FILE_TOO_LARGE -> ResponseCode.FILE_TOO_LARGE;
case FILE_TYPE_NOT_ALLOWED -> ResponseCode.FILE_TYPE_NOT_ALLOWED;
case FILE_UPLOAD_FAILED -> ResponseCode.FILE_UPLOAD_FAILED;
// ๋ณด๊ด€
case TRIP_NOT_STORED -> ResponseCode.TRIP_NOT_STORED;
case DIARY_NOT_STORED -> ResponseCode.DIARY_NOT_STORED;
// ์นด์นด์˜ค ๋กœ๊ทธ์ธ
case KAKAO_TOKEN_MISSING -> ResponseCode.KAKAO_TOKEN_MISSING;
case KAKAO_INVALID_TOKEN -> ResponseCode.KAKAO_INVALID_TOKEN;
case KAKAO_UNAUTHORIZED -> ResponseCode.KAKAO_UNAUTHORIZED;
case KAKAO_SERVER_ERROR -> ResponseCode.KAKAO_SERVER_ERROR;
case KAKAO_RESPONSE_PARSING_ERROR -> ResponseCode.KAKAO_RESPONSE_PARSING_ERROR;
case KAKAO_API_UNKNOWN_ERROR -> ResponseCode.KAKAO_API_UNKNOWN_ERROR;
// ๋ณด๋“œ
case BOARD_REQUIRED_FIELDS_MISSING -> ResponseCode.BOARD_REQUIRED_FIELDS_MISSING;
case BOARD_THEME_NOT_FOUND -> ResponseCode.BOARD_THEME_NOT_FOUND;
case BOARD_CREATE_FAILED -> ResponseCode.BOARD_CREATE_FAILED;
case BOARD_NOT_FOUND -> ResponseCode.BOARD_NOT_FOUND;
case BOARD_UPDATE_FORBIDDEN -> ResponseCode.BOARD_UPDATE_FORBIDDEN;
case BOARD_STICKER_NOT_FOUND -> ResponseCode.BOARD_STICKER_NOT_FOUND;
case BOARD_UPDATE_INTERNAL_ERROR -> ResponseCode.BOARD_UPDATE_INTERNAL_ERROR;
case BOARD_DELETE_FORBIDDEN -> ResponseCode.BOARD_DELETE_FORBIDDEN;
case BOARD_STICKER_MAP_NOT_FOUND -> ResponseCode.BOARD_STICKER_MAP_NOT_FOUND;
case BOARD_ACCESS_FORBIDDEN -> ResponseCode.BOARD_ACCESS_FORBIDDEN;
// ์•Œ๋ฆผ
case ALARM_NOT_AGREED -> ResponseCode.ALARM_NOT_AGREED;
default -> ResponseCode.SERVER_ERROR;
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.security.SecurityRequirements;
Expand All @@ -14,6 +13,8 @@
import org.springframework.web.bind.annotation.RestController;
import zim.tave.memory.config.swagger.ApiErrorCodeExamples;
import zim.tave.memory.domain.Emotion;
import zim.tave.memory.global.common.ApiResponseDto;
import zim.tave.memory.global.common.ResponseCode;
import zim.tave.memory.global.common.exception.ErrorCode;
import zim.tave.memory.service.EmotionService;

Expand All @@ -35,8 +36,8 @@ public class EmotionController {
content = @Content)
})
@GetMapping("")
public ResponseEntity<List<Emotion>> getAllEmotions() {
public ResponseEntity<ApiResponseDto<List<Emotion>>> getAllEmotions() {
List<Emotion> emotions = emotionService.getAllEmotions();
return ResponseEntity.ok(emotions);
return ResponseEntity.ok(ApiResponseDto.success(ResponseCode.SUCCESS, emotions));
}
}
7 changes: 3 additions & 4 deletions src/main/java/zim/tave/memory/controller/JoinController.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
Expand Down Expand Up @@ -45,17 +44,17 @@ public class JoinController {
@ApiResponse(responseCode = "400", description = """
์ž˜๋ชป๋œ ์š”์ฒญ์ž…๋‹ˆ๋‹ค. ๋‹ค์Œ ์—๋Ÿฌ ์ฝ”๋“œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:
- KAKAO_LOGIN_REQUIRED: ์นด์นด์˜ค ๋กœ๊ทธ์ธ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.
"""),
""", content = @Content),
@ApiResponse(responseCode = "401", description = """
์ธ์ฆ ์‹คํŒจ์ž…๋‹ˆ๋‹ค. ๋‹ค์Œ ์—๋Ÿฌ ์ฝ”๋“œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:
- AUTHENTICATION_FAILED
- INVALID_TOKEN
- UNAUTHORIZED_USER
"""),
""", content = @Content),
@ApiResponse(responseCode = "409", description = """
์ค‘๋ณต ๊ฐ€์ž… ์˜ค๋ฅ˜์ž…๋‹ˆ๋‹ค:
- ALREADY_JOINED: ์ด๋ฏธ ๊ฐ€์ž…๋œ ์‚ฌ์šฉ์ž์ž…๋‹ˆ๋‹ค.
""")
""", content = @Content)
})
@PostMapping("/join")
public ResponseEntity<ApiResponseDto<UserResponseDto>> join(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
Expand All @@ -19,7 +18,6 @@
import zim.tave.memory.global.common.ApiResponseDto;
import zim.tave.memory.global.common.ResponseCode;
import zim.tave.memory.global.common.exception.ErrorCode;
import zim.tave.memory.security.CustomUserDetails;
import zim.tave.memory.service.MyPageService;
import zim.tave.memory.service.VisitedCountryService;

Expand Down Expand Up @@ -93,7 +91,8 @@ public ResponseEntity<ApiResponseDto<MyPageResponseDto>> getMyPage(
- USER_NOT_FOUND: ์‚ฌ์šฉ์ž๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.
- VISITED_COUNTRY_NOT_FOUND: ๋ฐฉ๋ฌธํ•œ ๊ตญ๊ฐ€ ์ •๋ณด๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.
""", content = @Content),
@ApiResponse(responseCode = "500", description = "์„œ๋ฒ„ ์˜ค๋ฅ˜๋กœ ์ธํ•ด ๋ฐฉ๋ฌธ ๊ตญ๊ฐ€ ์กฐํšŒ์— ์‹คํŒจํ•˜์˜€์Šต๋‹ˆ๋‹ค.")
@ApiResponse(responseCode = "500", description = "์„œ๋ฒ„ ์˜ค๋ฅ˜๋กœ ์ธํ•ด ๋ฐฉ๋ฌธ ๊ตญ๊ฐ€ ์กฐํšŒ์— ์‹คํŒจํ•˜์˜€์Šต๋‹ˆ๋‹ค.",
content = @Content)
})
@GetMapping("/mypage-visited-countries")
public ResponseEntity<ApiResponseDto<VisitedCountryListResponseDto>> getVisitedCountries(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@ public ResponseEntity<ApiResponseDto<Void>> deleteAccount(
๋ฆฌ์†Œ์Šค๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค:
- USER_NOT_FOUND: ์‚ฌ์šฉ์ž๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.
""", content = @Content),
@ApiResponse(responseCode = "500", description = "์„œ๋ฒ„ ์˜ค๋ฅ˜๋กœ ์ธํ•ด ํšŒ์› ์ •๋ณด ์ˆ˜์ • ์‹คํŒจ")
@ApiResponse(responseCode = "500", description = "์„œ๋ฒ„ ์˜ค๋ฅ˜๋กœ ์ธํ•ด ํšŒ์› ์ •๋ณด ์ˆ˜์ • ์‹คํŒจ",
content = @Content)
})
@PatchMapping("/users/me")
public ResponseEntity<ApiResponseDto<UserResponseDto>> updateUserInfo(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
import zim.tave.memory.global.common.ApiResponseDto;
import zim.tave.memory.global.common.ResponseCode;
import zim.tave.memory.global.common.exception.ErrorCode;
import zim.tave.memory.security.CustomUserDetails;
import zim.tave.memory.service.StorageService;

import java.util.List;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
import zim.tave.memory.config.swagger.ApiErrorCodeExamples;
import zim.tave.memory.domain.TripTheme;
import zim.tave.memory.dto.response.TripThemeResponseDto;
import zim.tave.memory.global.common.ApiResponseDto;
import zim.tave.memory.global.common.ResponseCode;
import zim.tave.memory.global.common.exception.ErrorCode;
import zim.tave.memory.repository.TripThemeRepository;

Expand All @@ -37,11 +39,11 @@ public class TripThemeController {
@ApiResponse(responseCode = "500", description = "์„œ๋ฒ„ ๋‚ด๋ถ€ ์˜ค๋ฅ˜์ž…๋‹ˆ๋‹ค.",
content = @Content)
})
public ResponseEntity<List<TripThemeResponseDto>> getAllThemes() {
public ResponseEntity<ApiResponseDto<List<TripThemeResponseDto>>> getAllThemes() {
List<TripTheme> themes = tripThemeRepository.findAllByOrderById();
List<TripThemeResponseDto> response = themes.stream()
.map(TripThemeResponseDto::from)
.collect(Collectors.toList());
return ResponseEntity.ok(response);
return ResponseEntity.ok(ApiResponseDto.success(ResponseCode.SUCCESS, response));
}
}
11 changes: 7 additions & 4 deletions src/main/java/zim/tave/memory/controller/WeatherController.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
import org.springframework.web.bind.annotation.*;
import zim.tave.memory.config.swagger.ApiErrorCodeExamples;
import zim.tave.memory.dto.response.WeatherResponseDto;
import zim.tave.memory.global.common.ApiResponseDto;
import zim.tave.memory.global.common.ResponseCode;
import zim.tave.memory.global.common.exception.ErrorCode;
import zim.tave.memory.service.WeatherService;

Expand All @@ -34,9 +36,9 @@ public class WeatherController {
@ApiResponse(responseCode = "500", description = "์„œ๋ฒ„ ์˜ค๋ฅ˜์ž…๋‹ˆ๋‹ค.",
content = @Content)
})
public ResponseEntity<List<WeatherResponseDto>> getAllWeathers() {
public ResponseEntity<ApiResponseDto<List<WeatherResponseDto>>> getAllWeathers() {
List<WeatherResponseDto> weathers = weatherService.getAllWeathers();
return ResponseEntity.ok(weathers);
return ResponseEntity.ok(ApiResponseDto.success(ResponseCode.SUCCESS, weathers));
}

@GetMapping("/{weatherId}")
Expand All @@ -47,9 +49,10 @@ public ResponseEntity<List<WeatherResponseDto>> getAllWeathers() {
@ApiResponse(responseCode = "404", description = "๋‚ ์”จ๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.\n- WEATHER_NOT_FOUND: ๋‚ ์”จ๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.",
content = @Content)
})
public WeatherResponseDto getWeatherById(
public ResponseEntity<ApiResponseDto<WeatherResponseDto>> getWeatherById(
@Parameter(description = "๋‚ ์”จ ID", required = true, example = "1")
@PathVariable Long weatherId) {
return weatherService.getWeatherById(weatherId);
WeatherResponseDto weather = weatherService.getWeatherById(weatherId);
return ResponseEntity.ok(ApiResponseDto.success(ResponseCode.SUCCESS, weather));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,16 @@
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Schema(name = "ApiResponseDto", description = "๊ณตํ†ต ์‘๋‹ต ํ˜•์‹")
@Schema(description = "๊ณตํ†ต ์‘๋‹ต ํ˜•์‹")
public class ApiResponseDto<T> {

@Schema(description = "์‘๋‹ต ์ฝ”๋“œ")
private int code;

@Schema(description = "์‘๋‹ต ๋ฉ”์‹œ์ง€")
private String message;

@Schema(description = "๋ฐ์ดํ„ฐ")
private T data;

// ์„ฑ๊ณต ์‘๋‹ต
Expand Down
Loading