[ Java ] swagger(springdoc) 사용하기 (+예제 실습)

2024. 5. 12. 15:34· LANGUAGE/└ Java

환경 : Spring Tool Suite4

 

 

1) 의존성 추가

   - pom.xml

<!-- springdoc : 프론트엔드가 필요로 하는 데이터를 공급해주기 위함 
                 API를 설계, 빌드, 문서화하는 데 도움이 됩니다
-->
<!-- spring boot 2.x -->
<!--<dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-ui</artifactId>
    <version>1.8.0</version>
</dependency>-->
<!-- spring boot 3.x -->
<dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
    <version>2.5.0</version>
</dependency>

 

 

2) 설정 추가

   - application.properties

# springdoc setting : supply data what front-end needs 
springdoc.packages-to-scan=restcontroller패키지명
springdoc.swagger-ui.path=/swagger-ui

 

 

3) 프로젝트 Maven 후 재실행

 

 

4) 코드를 작성하여 사용법을 알아보자

 

먼저, 내가 사용한 emp 데이터베이스 정보이다

    - emp database

drop table emp;
create table emp(
emp_no number primary key,--사원번호, 시퀀스로 자동 부여
emp_name varchar2(21) not null,--사원의 한글 이름
emp_dept varchar2(30) not null,--사원의 소속 부서
emp_date char(10) not null,--사원의 입사일
emp_sal number default 0 not null check(emp_sal >= 0)--사원의 급여(원)
);

 

 

    - emp-mapper.xml

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE mapper
    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
    
<mapper namespace="emp">

	<!-- Create -->
	<select id="sequence" resultType="int">
		select emp_seq.nextval from dual
	</select>
	<insert id="insert">
		insert into emp(
			emp_no, emp_name, emp_dept, emp_date, emp_sal
		)
		values(
			#{empNo}, #{empName}, #{empDept}, #{empDate}, #{empSal}
		)
	</insert>
	
	<!-- Read -->
	<select id="list" resultType="empDto">
		select * from emp order by emp_no asc
	</select>
	<select id="find" resultType="empDto">
		select * from emp where emp_no = #{empNo}
	</select>
	
	<!-- Update -->
	<!-- 합쳐 만든 코드 -->
	<!-- <update id="update">
		update emp
		<set>
			<if test="empName != null">
				emp_name = #{empName},
			</if>
			<if test="empDept != null">
				emp_dept = #{empDept},
			</if>
			<if test="empDate != null">
				emp_date = #{empDate},
			</if>
			<if test="empSal != null">
				emp_sal = #{empSal},
			</if>
		</set>
		where emp_no = #{empNo}
	</update> -->
	<!-- 따로 만든 코드(더 정확해짐) -->
	<update id="editAll">
		update emp
		set 
			emp_name=#{empName},
			emp_dept=#{empDept},
			emp_date=#{empDate},
			emp_sal=#{empSal}
		where emp_no=#{empNo}
	</update>
	<update id="editUnit">
		update emp
		<set>
			<if test="empName != null">
				emp_name = #{empName},
			</if>
			<if test="empDept != null">
				emp_dept = #{empDept},
			</if>
			<if test="empDate != null">
				emp_date = #{empDate},
			</if>
			<if test="empSal > 0">
				emp_sal = #{empSal},
			</if>
		</set>
		where emp_no=#{empNo}
	</update>
	
	<!-- Delete -->
	<delete id="delete">
		delete emp where emp_no = #{empNo}
	</delete>
	
</mapper>

     (+) 합쳐만든 코드 주석 부분은 단일조회, 전체조회를 나누지 않고 한 번에 작성한 예시이다. 

          더 정확하게 사용하고자 한다면 따로 작성하는 것이 좋다. 

     (+) <set>, <if> 태그를 사용하려고 한다면 sql문 다음에 ,(콤마) 작성을 명심해야 한다!!

 

 

   - EmpDto.java

@Data @Builder @NoArgsConstructor @AllArgsConstructor
public class EmpDto {
	private int empNo;
	private String empName;
	private String empDept;
	private String empDate;
	private int empSal;
}

 

   - EmpDao.java

@Repository
public class EmpDao {

	@Autowired
	private SqlSession sqlSession;

	public List<EmpDto> selectList() {
		return sqlSession.selectList("emp.list");
	}

	public EmpDto selectOne(int empNo) {
		return sqlSession.selectOne("emp.find", empNo);
	}

	public int sequence() {
		return sqlSession.selectOne("emp.sequence");
	}
	
	public void insert(EmpDto empDto) {
		sqlSession.insert("emp.insert", empDto);
	}

	public boolean editAll(EmpDto empDto) {
		return sqlSession.update("emp.editAll", empDto) > 0;
	}
	public boolean editUnit(EmpDto empDto) {
		return sqlSession.update("emp.editUnit", empDto) > 0;
	}
	
	public boolean delete(int empNo) {
		return sqlSession.delete("emp.delete", empNo) > 0;
	}

}

 

    - EmpRestController.java

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
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.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.kh.spring19.dao.EmpDao;
import com.kh.spring19.dto.EmpDto;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.ExampleObject;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;

//문서용 설정 추가
@Tag(name = "사원정보 관리도구", description="emp 테이블 CRUD 처리")

@CrossOrigin
@RestController
@RequestMapping("/emp")
public class EmpRestController {
	
	@Autowired
	private EmpDao empDao;
	
	//문서용 설정 추가
	@Operation(
		description = "사원 목록 조회",
		responses = {
			@ApiResponse(
				responseCode = "200",
				description = "조회 성공",
				content = {
					@Content(
						mediaType = "application/json",
						array = @ArraySchema( //empDto가 담긴 배열이야.. 라는 뜻
							schema = @Schema(implementation = EmpDto.class)
						)
					)
				}
			),
			@ApiResponse(
				responseCode = "500",
				description ="서버 오류",
				content = {
					@Content(
						mediaType = "text/plain",
						schema = @Schema(implementation = String.class),
						examples = @ExampleObject("server error")
					)
				}
			)
		}
	)
	
	@GetMapping("/")
	public List<EmpDto> list(){
		return empDao.selectList();
	}
	
	
	@Operation(
			description = "사원 상세 조회",
			responses = {
				@ApiResponse(
					responseCode = "200",
					description = "조회 성공",
					content = {
						@Content(
							mediaType = "application/json",
							array = @ArraySchema( //empDto가 담긴 배열이야.. 라는 뜻
								schema = @Schema(implementation = EmpDto.class)
							)
						)
					}
				),
				@ApiResponse(
					responseCode = "404",
					description ="해당 사번의 데이터가 없음",
					content = {
						@Content(
							mediaType = "text/plain",
							schema = @Schema(implementation = String.class),
							examples = @ExampleObject("not found")
						)
					}
				),
				@ApiResponse(
					responseCode = "500",
					description ="서버 오류",
					content = {
						@Content(
							mediaType = "text/plain",
							schema = @Schema(implementation = String.class),
							examples = @ExampleObject("server error")
						)
					}
				)
			}
		)
	//empNo 값이 null인 경우 에러 발생
//	@GetMapping("/{empNo}")
//	public EmpDto find(@PathVariable int empNo) {
//		EmpDto empDto = empDao.selectOne(empNo);
//		return empDto;
//	}
	
	//조회되지 않는 경우(null인 경우)는 404번으로 처리하고 싶다면
	//아래와 같은 처리가 없는 경우 null일 때도 성공으로 나오게 됨
	@GetMapping("/{empNo}")
	public ResponseEntity<EmpDto> find(@PathVariable int empNo){ //사용자에게 보여지는 화면(응답)을 아예 세팅(내가 개조)하여 반환한다는 의미
		EmpDto empDto = empDao.selectOne(empNo);
		if(empDto == null) {
			//사용자에게 404를 보낸다
//			return ResponseEntity.notFound().build();
			return ResponseEntity.status(404).build();
		}
//		return ResponseEntity.ok(empDto);//성공했을 경우 반환 값
		return ResponseEntity.status(200).body(empDto);
	}
	
	@PostMapping("/")
	public EmpDto insert(@RequestBody EmpDto empDto) {
		int sequence = empDao.sequence();//번호생성
		empDto.setEmpNo(sequence);//번호 설정
		empDao.insert(empDto);//등록
		
		//return empDto;
		return empDao.selectOne(sequence);//등록된 결과를 조회하여 반환
	}
	
	// 조회되지 않는 사원(존재하지 않는 사원)은 404번으로 반환
	@PutMapping("/")
	public ResponseEntity<?> editAll(@RequestBody EmpDto empDto) {
		boolean result = empDao.editAll(empDto);
		if(result == false) {
			//return ResponseEntity.notFound().build();
			return ResponseEntity.status(404).build();
		}
		return ResponseEntity.ok().build();
	}
	
	@PatchMapping("/")
	public ResponseEntity<?> editUnit(@RequestBody EmpDto empDto) {
		boolean result = empDao.editUnit(empDto);
		if(result == false) {
			//return ResponseEntity.notFound().build();
			return ResponseEntity.status(404).build();
		}
		return ResponseEntity.ok().build();
	}
	
	//@Operation(작업에 대한 설명)
	@DeleteMapping("/{empNo}")
	public ResponseEntity<?> delete(@PathVariable int empNo) { //? 대신 Void 써도 되는데 애매함
		boolean result = empDao.delete(empNo);
		if(result == false) {
			return ResponseEntity.notFound().build();
		}
		return ResponseEntity.ok().build();
	}
	
}

      (+) import 되는 부분도 중요하니 참고정도 하길 바람

      (+) 문서용 설정 (@Operation)을 하여 출력 정보에 대한 값 처리를 해주면 좋다 (이후 다른 부서와의 소통에 있어서도 좋음..)

 

 


 화면에 나온 정보를 토대로 기능을 사용해보자 (http://localhost:8080/swagger-ui/index.html#/)

 

 

   - 전체 조회

 

   - 회원 등록

 

   - 한 회원의 일부 정보만 수정

 

   - 회원번호로 조회하여 회원 정보 수정

있는 회원의 정보 수정

   - 회원 번호로 조회

 

   - 회원 정보 삭제

 

 

 

 

개인 공부 기록용입니다:)

728x90