Restful, Rest Api 하면 따라다니는 용어가 있는데,
OAS => Open API Specfication
=> 그저 Rest 관련 표준을 정하고 있구나 생각하면 된당.
=> 요것땜시 open-api 란 글자들도 자주 누네띤당.
HATEOAS => Hypermedia As The Engine Of Application State
=> 자원(Resource)에 접근하면, 해당 자원 관련 처리를
할 수 있는 추가 API 링크를 받을 수 있는 형식!
억지로 무쟈게 쉽게 얘기하면
get으로 /api/idol/3 아이디 3번인 idol 정보를 요청했는데
추가적으로 idol3번을 지우는 link와 수정할 수 있는
link 정보도 같이 오는 것이당. (와아 넘 쉽당!~~ ㅋㅋ)
상식이닝 그냥 머리에 담아서 누가 얘기하면 아는 척 있어보이장!
Restful로 만들면 그 사용법을 시각적으로., 테스트도 거저 쉽게 하고프당
이럴 때 사용하는 것이 Swagger당 말은 타고 달려봐야 진정 느낄 수 있다.
말로 말을 설명하려는 자가 있다면 그 사람 말은 가치폭락이당.
느낌오면 의욕이 생기고,확장해서 내껄 만들고픈 욕심도 생기닝, 일단 달리장.
먼저 스프링 부트 프로젝트를 1개 아래처럼 새로 맹글장.
STS5(난 최신을 마구 좋아함)에서 Spring Starter Project
( Gradle 안 써본 그 분들을 위해 Maven아니공 괘닝 Gradle롱)

Dependencies 선택은 Spring Web, Lombok
그리고 그냥 매번 넣는 Spring Boot DevTools 와 Spring Boot Actuator을 하고서
Swagger를 쓰기 위해 최신 springdoc-openapi-starter-webmvc-ui:3.0.0 를
build.gradle 파일에 직접 넣으면 아래와 같을지어당. (그렇당)
build.gradle
plugins {
id 'java'
id 'org.springframework.boot' version '4.0.1'
id 'io.spring.dependency-management' version '1.1.7'
}
group = 'com.e7e'
version = '0.0.1-SNAPSHOT'
description = 'Demo project for Spring Boot'
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'org.springframework.boot:spring-boot-starter-webmvc'
implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:3.0.0")
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-actuator-test'
testImplementation 'org.springframework.boot:spring-boot-starter-webmvc-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
tasks.named('test') {
useJUnitPlatform()
}
build.gradle 파일을 수정하면 마우스 오른쪽 눌렁, 아래와 같은 Refresh를 잊지 말장.

참~ 미리 1개만 준비하장. 시작페이지가 없어서 안된다고 오해할 그들을 위한 준비당
src/main/resources 아래 static 폴더에 index.html을 아래처럼 미리 맹글장.
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<style>
#wrapper {
margin: 20px auto;
padding: 10px;
width:60%;
border: 10px groove gold;
text-align:center;
}
a {
font-size:2em;
display: block;
width:100%;
}
</style>
</head>
<body>
<div id="wrapper" >
<h1>E7E 노파심에 여기에 링크를....</h1>
<a href="/swagger-ui/index.html">Swagger-UI 이동</a>
</div>
</body>
</html>
application.properties 파일을 아래 처럼 맹글장.
눈으로 대략 천천히 훓으면 뜻 그대로 느끼미가 찾아온당.
springdoc.swagger-ui.enabled=true 요줄이 있어야 화면에 swagger가 나온당.
application.properties
spring.application.name=swagger3
logging.level.com.e7e.swagger3=debug
# 아래 2개는 그냥 내가 보고 싶어서 넣음
logging.level.org.springframework.web=debug
logging.level.org.springframework.webmvc=debug
# Swagger Spring UI 세팅
springdoc.packages-to-scan=com.e7e.swagger3
springdoc.default-consumes-media-type=application/json
springdoc.default-produces-media-type=application/json
springdoc.cache.disabled=true
# 아래 default값은 enabled
# springdoc.api-docs.enabled=false
# 아래 default값은 /v3/api-docs/
# springdoc.api-docs.path=/api-docs/json
springdoc.api-docs.groups.enabled=true
springdoc.swagger-ui.enabled=true
#defaut /swagger-ui/index.html
#springdoc.swagger-ui.path=/e7e-ui.html
springdoc.swagger-ui.tags-sorter=alpha
# alpha, default, method
springdoc.swagger-ui.operations-sorter=method
사용하려면 아래와 같은 설정이 그냥 필요하당. 그렇당.
눈이 흐르면 느끼미도 따라 흐른당.
SwaggerConfig.java
package com.e7e.swagger3.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
@Configuration
public class SwaggerConfig {
@Bean
OpenAPI openAPI() {
Info info = new Info()
.title("Swagger로 해보는API Document 연습")
.version("v1.0.0")
.description("Swagger 연습용 API 명세");
// OpenAPI 생성
return new OpenAPI()
.components(new Components())
.info(info);
}
}
스키마로 쓸 VO 1개 만들장. (나중에 결과 실행화면과 매칭시켜서 봐야한당)
BdVO.java
package com.e7e.swagger3.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
public class BdVO {
@Schema(description = "bd 고유키", example = "272")
private int seq;
@Schema( description = "bd 제목", example = "제목 쓰삼")
private String bdTitle;
@Schema( description = "bd 내용", example = "Swagger 재밌넹")
private String bdContent;
@Schema( description = "bd 작성자명", example = "경미니")
private String userName;
}
초간단 Restful 컨트롤러 1개 만들장
(get 방식 조회 2개, post , put, delete 각 1개)
BdController.java
package com.e7e.swagger3.controller;
import java.util.ArrayList;
import java.util.List;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.e7e.swagger3.vo.BdVO;
import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
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.tags.Tag;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Tag(name = "boards", description = "Board CRUD API EndPoint")
@RestController
public class BdController {
// DB 대신 그냥 static 으로 연습
private static List<BdVO> bdLists = new ArrayList<>();
// 생성 후 초기화
@PostConstruct
void init() {
log.debug("그냥 실행 여부 눈으로 확인 - 디버깅용");
List<String> names = List.of("경미닝",
"수미닝",
"지워닝",
"선주닝",
"미서닝");
List<String> titles = List.of("넘 자랑하고팡",
"난 이미 아이돌",
"Support 매니아",
"당당한 갑옷이요?",
"웃음 에너지");
List<String> conts = List.of("최고 인사고과 평점 S용",
"왜 절 캐스팅하지 않아용?",
"이름 자체가 Support",
"건강하면 그렇게 예뻐보여용",
"인생은 웃어야 맛이죵 그죵?");
BdVO bdVO = null;
for (int i = 0; i < names.size(); i++) {
bdVO = new BdVO();
bdVO.setSeq(i);
bdVO.setUserName(names.get(i));
bdVO.setBdTitle(titles.get(i));
bdVO.setBdContent(conts.get(i));
bdLists.add(bdVO);
}
}
/* Restful API GET 메서드 */
@GetMapping("/boards")
@Operation(summary = "전체 조회", description = "board 전체 조회")
@ApiResponse(responseCode = "200", description = "성공", content = @Content(schema = @Schema(implementation = BdVO.class)))
public ResponseEntity<List<BdVO>> boardList() {
return ResponseEntity.ok(bdLists);
}
/* Restful API GET 메서드 */
@GetMapping("/boards/{seq}")
//감추고 싶다면 @Hidden
@Operation(summary = "seq로 조회", description = "board by seq")
@ApiResponse(responseCode = "200", description = "성공", content = @Content(schema = @Schema(implementation = BdVO.class)))
public ResponseEntity<BdVO> boardByUserId(
@Parameter(name = "seq", description = "seq지용") @PathVariable(value = "seq") int seq) {
log.debug("체킁: {}", seq);
List<BdVO> schList = bdLists.stream().filter(bd -> bd.getSeq() == seq).toList();
BdVO bdVO = schList.size() > 0 ? schList.get(0) : null;
return ResponseEntity.ok(bdVO);
}
/* Restful API POST 메서드 */
@PostMapping("/boards")
@Operation(summary = "게시글 등록", description = "게시글을 등록합니다.")
@ApiResponse(responseCode = "200", description = "등록 성공")
public ResponseEntity<BdVO> createBoard(@RequestBody @Schema(implementation = BdVO.class) BdVO bdVO) {
int maxSeq = 0;
for (BdVO bdVO2 : bdLists) {
if (bdVO2.getSeq() > maxSeq) {
maxSeq = bdVO2.getSeq();
}
}
bdVO.setSeq(maxSeq + 1);
bdLists.add(bdVO);
return ResponseEntity.ok(bdVO);
}
/* Restful API PUT 메서드 */
@PutMapping("/boards/{seq}")
@Operation(summary = "Board Update", description = "Board Update By seq", responses = {
@ApiResponse(responseCode = "200", description = "떵공"),
@ApiResponse(responseCode = "400", description = "잘못된 요청"),
@ApiResponse(responseCode = "404", description = "못찾겠다"),
@ApiResponse(responseCode = "500", description = "서버가 미안") }, parameters = {
@Parameter(name = "seq", description = "Board seq", required = true, example = "1") })
public ResponseEntity<BdVO> updateBoard(@PathVariable(value = "seq") int seq,
@RequestBody @Schema(implementation = BdVO.class) BdVO bdVO) {
for (BdVO bdVO1 : bdLists) {
if (bdVO1.getSeq() == seq) {
bdVO1.setBdContent(bdVO.getBdContent());
bdVO1.setBdTitle(bdVO.getBdTitle());
bdVO1.setUserName(bdVO.getUserName());
}
}
bdVO.setSeq(seq);
return ResponseEntity.ok(bdVO);
}
/* Restful API DELETE 메서드 */
@DeleteMapping("/boards/{seq}")
@Operation(summary = "Board Delete", description = "Delete Board By seq")
public ResponseEntity<BdVO> deleteBoard(@Parameter(name = "seq") @PathVariable(value = "seq") int seq) {
log.debug("delete {}", seq);
List<BdVO> schList = bdLists.stream().filter(bd -> bd.getSeq() == seq).toList();
// iterator 대신에
bdLists.removeIf(bd -> bd.getSeq() == seq);
BdVO bdVO = schList.size() > 0 ? schList.get(0) : null;
return ResponseEntity.ok(bdVO);
}
}
http://localhost:8080/swagger-ui/index.html 를 확인해 보면 당근 아래와 같당.
각각의 메소드를 눌러보면 Try it out ㅓ버튼이 있으니, 눌러서 실제 실행도 해보장.


실행 결과와 컨트롤러/VO 소스를 맵핑시켜 보면
@Tag / @Operation / @ApiResponse / @Hidden / @Content / @Parameter / @Schema
어노테이션들의 의미가 느끼미와 붙어서 순간 뇌에 아하! 하공~ 박히공 만당.
억지로 외우지 말고, 그저 필요한 부분만 복사/붙여넣기 식으로 사용하면
저절로 급하지 않게 충분한 속도로 익숙해 질 것이당.
http://localhost:8080/v3/api-docs 도 눈으로 확인해 보길!~ 꼬옥
오타왕들을 위한 전체소스
STS에서 이상한 현상을 1개 발견하였다.~ 눈치 못챌뻔~~
webmvc-ui 라이브러리를 넣고
gradle에서 Refresh Gradle Project를 하면
resources 아래 templates 폴더가 패키지 형태로 바뀌어 버린당.
다시 폴더형태로 바꾸면 index.html이 실행이 안된당.
먼가 있구낭 하고, 스프링 web과 webmvc 패키지에 debug 로깅레벨을
걸어보았지만 암것도 나오지 않았당.~~ㅠㅠ
추론 가능성이 너무 많아서 기다려보장! 으로 맘을 정했당.
아래는 난 Swagger에 중독되었어 생각이 든다면 가보길 추천한당.(그닥 재미는 없을거당)
Swagger UI (https://swagger.io/swagger-ui/)
Swagger Editor (https://editor.swagger.io/)
Swagger Codegen (https://github.com/swagger-api/swagger-codegen/tree/3.0.0)
주의및 관심 2025-12-25 기준 issues 가 3.1K나 됨.. 기다릴 줄도 알아야...
언제였던가? 기억이 있긴 한건가? 스스로 만든 환영인가?
친구집에 갔다가 반갑게 "하이" 하는 그 아들에게 손가락 총으로
"빵!" 하니 물끄러미 쳐다봐서, 멋쩍은 나머지 다시 "빵!" 했다.
그 아인 쓰러지진 않고 부엌으로 달리길래
난 도망치는 줄 알고 웃고 말았당. 이런 그건 정답이 아니었당.
동심을 품고 사는 건 나였당.
얼만큼 뒤 다시 쪼르륵 미끄러지듯 나타난 그 아이 손에는
뜨거운 듯 조심스럽게 티슈에 감싼 호빵이 있었당.
"아더씨 호빵!" ~~~
태생적 아재개그 유전자의 원형을 보게 된 난
그 은총에 빵맞은 것처럼 쓰러지고 말았당!.
분명 아주 오래전 옛날엔 총에 빵을 넣어 쐈을거당.
빵 맞은 것도~ 빵 먹은 것도~ 빵빵 거린 것도~~
떨어진 부스러기 추억을 모아서 몰빵
https://www.youtube.com/watch?v=m6ftHZi9qTI
| Annotation(어노테이션)을 이용한 AOP (0) | 2025.12.19 |
|---|---|
| 스프링 부트 war 패키징 (4) | 2025.02.10 |
| spring boot3 security jwt 적용 1 (B/E) (11) | 2025.01.08 |
| Spring boot WebSocket(스프링 부트 웹소켓 사용) (8) | 2024.11.11 |
| sts4 spring boot 세번쨍 (0) | 2023.07.10 |