
λ°±μλ APIλ₯Ό κ°λ°νλ€ λ³΄λ©΄ νλ‘ νΈμλ/QA νκ³Ό API λͺ μΈλ₯Ό λ¬Έμλ‘ κ³΅μ νλ μΌμ΄ νμμ μ λλ€.
κ³Όκ±°μλ Excelμ΄λ Wikiμ μλμΌλ‘ μμ±νμ§λ§, μ΄μ λ Swagger(OpenAPI) λ₯Ό ν΅ν΄ API λ¬Έμλ₯Ό μλμΌλ‘ μμ±νκ³ μ μ§λ³΄μν μ μμ΅λλ€.
μ΄λ² κΈμμλ Spring Boot 3.4 + Java 21 + Gradle νκ²½μ κΈ°μ€μΌλ‘, Swaggerλ₯Ό μ μ©νκ³ μ΄μν λμ Best Practiceλ₯Ό μκ°ν©λλ€.
build.gradleμ OpenAPI κ΄λ ¨ μμ‘΄μ±μ μΆκ°ν©λλ€.
Spring Boot 3μμλ springdoc-openapi λΌμ΄λΈλ¬λ¦¬κ° κ°μ₯ λ§μ΄ μ¬μ©λ©λλ€.
dependencies {
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.0'
}
μμ‘΄μ±λ§ μΆκ°νλ©΄ μλμΌλ‘ Swagger UI κ° νμ±νλ©λλ€.
κΈ°λ³Έ κ²½λ‘: http://{{νΈμ€νΈ}}:{{ν¬νΈ}}/swagger-ui.html
OpenAPI λ¬Έμ(JSON): http://{{νΈμ€νΈ}}:{{ν¬νΈ}}/v1/api-docs
νμ¬ λ΄ λ³΄μ μ μ± μ λ°λΌ μ κ·Ό κΆνμ μ μ΄νκ±°λ, νλ‘νλ³λ‘ μ€μ μ λΆλ¦¬νλ κ²μ΄ μ€μν©λλ€.
/**
* Swagger UI μλ¨μ νμλλ API μ λ³΄κ° μ΄ λΆλΆμμ μ€μ
*/
@Configuration
@OpenAPIDefinition(
info = @Info(
title = "ν
μ€νΈ μ μ© API",
description = "κ°μΈμ μΌλ‘ ν
μ€νΈνκΈ° μν API λ¬Έμμ
λλ€",
version = "v1"
)
)
public class SwaggerConfiguration {
}
/**
* Swagger νλ©΄μμ μ¬μ©μ νΈμμ±μ λμ΄λ μ΅μ
* Swagger UIμμ μ¬μ©μ/κ΄λ¦¬μ APIλ₯Ό νμΌλ‘ ꡬλΆ
*/
springdoc:
default-consumes-media-type: application/json
default-produces-media-type: application/json
api-docs:
path: /v1/api-docs
swagger-ui:
path: /swagger-ui.html
operations-sorter: alpha
tags-sorter: alpha
display-query-params-without-oauth2: true
disable-swagger-default-url: true
doc-expansion: list
defaultModelsExpandDepth: 1
defaultModelExpandDepth: 2
defaultModelRendering: model
show-extensions: true
show-common-extensions: true
display-request-duration: true
try-it-out-enabled: true
group-configs:
- group: μ¬μ©μ
paths-to-match:
- /v1/member/**
- /v1/board/**
- group: κ΄λ¦¬μ
paths-to-match:
- /v1/admin/**
/**
* μ¬μ©μ μμ± API
*/
@Operation(
summary = "μ¬μ©μ μμ±",
requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(
required = true,
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = CreateMemberRequest.class)
)
),
responses = {
@ApiResponse(
responseCode = "201",
description = "μ¬μ©μ μμ± μλ£",
content = {
@Content(
mediaType = "application/json",
schema = @Schema(implementation = CommonResponse.class)
),
}
),
}
)
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public CommonResponse createMember(@Valid @RequestBody CreateMemberRequest createMemberRequest) {
Member member = memberWebMapper.toDomain(createMemberRequest);
Member resultMember = createMemberUseCase.createMember(member);
CreateMemberResponse createMemberResponse = memberWebMapper.toCreateMemberResponse(resultMember);
CommonResponse commonResponse = new CommonResponse<>("μ¬μ©μ μμ±μ΄ μ μμ μΌλ‘ μ²λ¦¬λμ΅λλ€", createMemberResponse);
return commonResponse;
}
- λ°±μλ(Spring Boot) β /v1/api-docs μ 곡
- νλ‘ νΈμλ β OpenAPI Generator, orval, swagger-typescript-api λ±μ νμ©νμ¬
- νμ μ μ
- API νΈμΆ μ½λ
- React Query/SWR ν μλμΌλ‘ μμ± κ°λ₯

Swagger(OpenAPI)λ API κ°λ°/μ΄μμμ νμ ν¨μ¨μ κ·Ήλννλ λꡬμ λλ€.
Spring Bootμμλspringdoc-openapiλ₯Ό ν΅ν΄ μ½κ² μ μ©ν μ μκ³ ,
보μ/νλ‘ν λΆλ¦¬/λ¬Έμ κ΄λ¦¬κΉμ§ μ κ²½μ΄λ€λ©΄ μ€λ¬΄μ λ°λ‘ μ μ© κ°λ₯ν λ² μ€νΈ νλν°μ€κ° λ©λλ€.