2025. 12. 2. 13:40ㆍJava/SpringBoot
오늘은 Spring Boot로 만든 학생 관리 REST API에 Swagger(OpenAPI 3.0)와 Postman을 붙여서, “백엔드 API → 문서 → 테스트 도구”까지 한 번에 연결하는 실습을 했다.
단순히 코드만 작성하는 수준이 아니라, HTTP 응답 설계(ResponseEntity), API 문서화(@Tag, @Operation), OpenAPI JSON, Postman 연동까지 현업에서 실제로 사용하는 흐름과 거의 동일한 구조였다.
❶ REST API 기반 Student CRUD 구조 복습
1) 레이어드 아키텍처 구조
이번 실습의 기본 골격은 이미 만들어 둔 Student CRUD 프로젝트다. 레이어드 아키텍처 구조는 다음과 같다.
- Controller : HTTP 요청(URI, 메서드) → Java 메서드 매핑, DTO 입출력 담당
- Service : 비즈니스 로직, Entity ↔ DTO 변환, 예외 처리
- Repository : Spring Data JPA, DB CRUD 담당
- Entity : DB 테이블과 매핑되는 도메인 객체
- DTO : 클라이언트와 주고받는 데이터 모델
// Entity
@Entity
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
private String email;
private String phone;
}
// DTO
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class StudentDto {
private int id;
private String name;
private String email;
private String phone;
}
Service 계층에서는 Entity ↔ DTO 변환을 직접 해 주어 JPA 내부 구현이 외부(API 응답)에 새어 나가지 않도록 막는다. 이는 실제 서비스에서도 많이 쓰는 패턴이다.
❷ ResponseEntity로 HTTP 응답 제어하기
기존 컨트롤러는 단순히 StudentResultDto만 리턴해서, 스프링이 항상 200 OK로 응답하도록 두었다. 오늘은 여기서 한 단계 더 나가서 HTTP 응답 코드와 바디를 직접 제어하는 방법을 실습했다. 그 핵심이 ResponseEntity<T> 이다.
1) 기본 사용 예시
@RestController
@RequestMapping("/api")
@RequiredArgsConstructor
public class StudentControllerCrudResponseEntity {
private final StudentServiceCrud studentServiceCrud;
// 목록
@GetMapping("/students")
public ResponseEntity<StudentResultDto> listStudent() {
StudentResultDto resultDto = studentServiceCrud.listStudent();
// 성공
return new ResponseEntity<>(resultDto, HttpStatus.OK);
// 혹은 축약형
// return ResponseEntity.ok(resultDto);
}
}
2) 다른 HTTP 상태 코드 사용
단순 성공(200) 뿐 아니라, 상황에 따라 다른 상태 코드를 줄 수 있다.
// 200 OK - 바디 포함
return ResponseEntity
.status(HttpStatus.OK)
.body(studentResultDto);
// 404 Not Found - 바디 없음
return ResponseEntity
.status(HttpStatus.NOT_FOUND)
.build();
// 축약형 404
return ResponseEntity.notFound().build();
이렇게 하면 StudentResultDto 안에 result="success/fail" 같은 문자열만 두는 것보다, HTTP가 원래 가진 표현력(200, 404, 500 등)을 활용할 수 있어서 REST API스럽고, 클라이언트(프론트) 입장에서도 해석이 더 명확해진다.
❸ Swagger(springdoc-openapi)로 REST API 문서화
1) Gradle 의존성 추가
Swagger 2가 아니라, springdoc-openapi 기반으로 OpenAPI 3.0을 사용했다.
dependencies {
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.5.0'
}
서버 재시작 후 아래 주소로 접속하면 Swagger UI가 자동으로 뜬다.
- Swagger UI :
http://localhost:8080/swagger-ui/index.html - OpenAPI JSON :
http://localhost:8080/v3/api-docs - OpenAPI YAML :
http://localhost:8080/v3/api-docs.yaml
2) @Tag – 컨트롤러(그룹) 단위 설명
Swagger UI에서 API를 그룹으로 묶기 위해 @Tag를 사용했다.
import io.swagger.v3.oas.annotations.tags.Tag;
@Tag(
name = "기본 Student CRUD REST API",
description = "Student의 등록, 수정, 삭제, 목록 조회, 상세 조회 기능을 REST API 로 제공합니다."
)
@RestController
@RequestMapping("/api")
@RequiredArgsConstructor
public class StudentControllerCrud {
...
}
이 태그 정보가 Swagger UI 왼쪽의 섹션 이름으로 표시된다.
3) @Operation – 메서드 단위 설명
각 API 엔드포인트(메서드)에는 @Operation을 붙여 요약(summary)과 상세 설명(description)을 작성했다.
import io.swagger.v3.oas.annotations.Operation;
@Operation(
summary = "학생 목록 조회",
description = "전체 학생 목록을 조회합니다."
)
@GetMapping("/students")
public StudentResultDto listStudent() {
return studentServiceCrud.listStudent();
}
@Operation(
summary = "학생 상세 조회",
description = "ID 값을 이용해 개별 학생 1명의 상세 정보를 조회합니다.",
deprecated = true // Swagger UI 에 해당 API가 deprecated 표시
)
@GetMapping("/students/{id}")
public StudentResultDto detailStudent(@PathVariable("id") Integer id) {
return studentServiceCrud.detailStudent(id);
}
deprecated = true 로 표시하면 Swagger UI에서 이 API가 “더 이상 권장되지 않는 API”라는 표시가 뜬다. 실무에서는 구버전 API를 남겨 두되, 새 API로 갈아타게 안내할 때 사용한다.
❹ Swagger 문서를 인터페이스로 분리하는 패턴
오늘 배운 것 중에 특히 인상 깊었던 부분은, Swagger 어노테이션을 인터페이스에 모으고, 컨트롤러 클래스는 그 인터페이스를 구현만 하는 구조였다.
1) Swagger 전용 인터페이스
@Tag(
name = "JSON Student CRUD REST API",
description = "JSON 요청을 통해 Student의 등록, 수정, 삭제, 조회를 수행하는 REST API"
)
public interface StudentControllerCrudJsonRequestSwagger {
@Operation(
summary = "학생 목록",
description = "전체 학생 목록을 조회합니다."
)
@GetMapping("/students")
StudentResultDto listStudent();
@Operation(
summary = "학생 상세",
description = "개별 학생 1명을 조회합니다."
)
@GetMapping("/students/{id}")
StudentResultDto detailStudent(@PathVariable("id") Integer id);
@Operation(
summary = "학생 등록",
description = "JSON 요청을 통해 신규 학생 1명을 등록합니다."
)
@PostMapping("/students")
StudentResultDto insertStudent(StudentDto studentDto);
}
2) 실제 컨트롤러 구현
@RestController
@RequiredArgsConstructor
public class StudentControllerCrudJsonRequest
implements StudentControllerCrudJsonRequestSwagger {
private final StudentServiceCrud studentServiceCrud;
@Override
public StudentResultDto listStudent() {
return studentServiceCrud.listStudent();
}
@Override
public StudentResultDto detailStudent(Integer id) {
return studentServiceCrud.detailStudent(id);
}
@Override
public StudentResultDto insertStudent(StudentDto studentDto) {
return studentServiceCrud.insertStudent(studentDto);
}
}
이 패턴의 장점은 다음과 같다.
- 컨트롤러 구현 코드가 Swagger 어노테이션으로 지저분해지지 않는다.
- 인터페이스가 API 스펙 역할을 하기 때문에, 구현체가 반드시 이 스펙을 따르도록 강제할 수 있다.
- 버전이 여러 개(v1, v2...)일 때 인터페이스를 나눠서 관리하기 좋다.
실무에서도 “API 스펙 인터페이스 + 구현 컨트롤러” 패턴을 쓰는 팀이 꽤 있다.
❺ /v3/api-docs JSON 확인 + JSON 포매터 + Postman Import
1) /v3/api-docs JSON 확인
Swagger UI는 사실 /v3/api-docs 에서 제공되는 OpenAPI JSON 문서를 렌더링한 것일 뿐이다.
- 브라우저에서
http://localhost:8080/v3/api-docs접속 - 엄청 긴 JSON 텍스트가 나온다 → 이것이 API 스펙의 원본
- 내용 복사 후, JSON 포매터 사이트에 붙여서 구조를 보기 좋게 정리
JSON 포매터를 사용하면 paths, components.schemas, tags 구조를 한눈에 볼 수 있어서 “내 API가 OpenAPI 기준으로 어떻게 정의되어 있는지”를 정확히 확인할 수 있다.
2) Postman에 OpenAPI 스펙 Import
다음으로, 이 OpenAPI JSON을 Postman에 가져와서 API 테스트 컬렉션으로 만드는 작업을 실습했다.
- Postman 실행 후 상단의 Import 버튼 클릭
- Raw text 탭 선택
/v3/api-docs에서 복사한 JSON 전체를 붙여넣기- Postman이 포맷을 자동으로 OpenAPI 3.0 으로 인식
- Import as: API Collection 으로 표시되는 것 확인 후 Import 클릭
이렇게 하면 Swagger에서 정의한 모든 엔드포인트가 Postman Collection으로 자동 생성된다. 각 요청에는 이미 HTTP 메서드, URL, PathVariable 구조, RequestBody 형식이 들어있어서, 별도로 등록할 필요 없이 바로 Send 버튼만 눌러 테스트할 수 있다.
실무에서는 이 과정을 통해 “백엔드 개발자가 만든 Swagger 스펙 → 프론트/QA 팀이 Postman으로 가져와 테스트” 라는 협업 흐름이 자연스럽게 만들어진다.

❻ 오늘 실습에서 정리된 핵심 개념 요약
- 레이어드 아키텍처 Controller - Service - Repository - Entity/DTO를 분리해서 유지보수성과 테스트성을 높이는 구조.
- ResponseEntity<T> HTTP 응답 코드, 헤더, 바디를 직접 제어할 수 있는 스프링의 응답 래퍼.
ok(),status(),notFound()등으로 표현. - springdoc-openapi Spring MVC 기반 REST API를 OpenAPI 3.0 문서로 자동 변환해 주는 라이브러리. Swagger UI도 함께 제공.
- @Tag / @Operation Swagger에서 컨트롤러 그룹과 개별 API 메서드를 설명하기 위한 어노테이션.
- /v3/api-docs Swagger UI가 참조하는 OpenAPI JSON 원본 문서. 이 파일이 실제 “API 계약서”.
- Postman Import (OpenAPI) /v3/api-docs JSON을 Postman에 가져오면 Swagger 스펙을 기반으로 한 API Collection이 자동 생성되어 테스트가 매우 편해진다.
❼ 오늘 실습에서 얻은 교훈
단순히 “엔드포인트를 만들었다”에서 끝나는 것이 아니라, HTTP 상태 코드, 문서화, 테스트 도구까지 연결하는 것이 요즘 백엔드 개발자의 기본 역량이라는 걸 몸으로 느낀 날이었다.
- 코드를 짜는 것뿐 아니라, API를 설명하고 공유할 수 있는 상태까지 만들어야 한다.
- Swagger(OpenAPI)와 Postman은 이를 도와주는 표준 도구 세트다.
- 앞으로 개인 프로젝트에서도 REST API를 만들 때, 오늘 실습한 흐름을 그대로 적용하면 포트폴리오의 완성도가 크게 올라갈 것이다.
'Java > SpringBoot' 카테고리의 다른 글
| [LG U+ 유레카 3기] Redis Queue + Scheduler로 선착순 쿠폰(100장) 동시성 해결 실습 (0) | 2026.01.06 |
|---|---|
| 🌐 Spring Boot 프로젝트에 Swagger(OpenAPI) 적용하는 방법 (0) | 2025.06.02 |
| 🚀 Spring Boot로 회원가입 & 유저 조회 API 만들기 (feat. Postman) (0) | 2025.05.29 |
| 🛠️IntelliJ로 스프링부트 백엔드 개발환경 구축하기 (JDK 17 기반) (2) | 2025.05.29 |