안녕하세요, 오늘은 프로젝트에서 Rest API 방식을 설정하였으며, 본 포스팅에서는 API 테스트 및 문서화하는 방법을 기록했습니다.
https://www.postman.com/
https://chromewebstore.google.com/detail/talend-api-tester-free-ed/aejoelaoggembcahagimdiliamlcdmfm?pli=1
https://insomnia.rest/download






스프링 부트 프로젝트를 사용해 API 문서 생성을 자동화
// https://mvnrepository.com/artifact/org.springdoc/springdoc-openapi-starter-webmvc-ui
implementation group: 'org.springdoc', name: 'springdoc-openapi-starter-webmvc-ui', version: '2.8.4'
http://localhost:8080/swagger-ui/index.html ⇐ Swagger UI
http://localhost:8080/v3/api-docs ⇐ OpenAPI JSON Format
http://localhost:8080/v3/api-docs.yaml ⇐ YAML Format 다운로드
springdoc.swagger-ui.path=/swagger-ui.html
springdoc.api-docs.path=/api-docs
springdoc.packages-to-scan=board.controller, board.test
springdoc.paths-to-match=/v1, /api/**
https://springdoc.org/#properties 를 참고하여 작성
package board.configuration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;
@Configuration
public class SpringDocConfiguration {
@Bean
public OpenAPI openAPI() {
return new OpenAPI().info(
new Info()
.title("SpringBoot Board REST API")
.description("스프링 부트 기반의 게시판 REST API")
.version("v1.0")
.license(new License().name("Apache 2.0").url("http://myapiserver.com"))
);
}
}
package board.controller;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.util.ObjectUtils;
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.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import com.fasterxml.jackson.databind.ObjectMapper;
import board.dto.BoardDto;
import board.dto.BoardFileDto;
import board.service.BoardService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import jakarta.servlet.http.HttpServletResponse;
@RestController
@RequestMapping("/api")
public class RestApiBoardController {
@Autowired
private BoardService boardService;
// 목록 조회
@Operation(summary = "게시판 목록 조회", description = "등록된 게시물 목록을 조회해서 반환합니다.")
@GetMapping("/board")
public List<BoardDto> openBoardList() throws Exception {
return boardService.selectBoardList();
}
// 저장 처리
@PostMapping(value = "/board", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public void insertBoard(@RequestParam("board") String boardData, MultipartHttpServletRequest request) throws Exception {
ObjectMapper objectMapper = new ObjectMapper();
BoardDto boardDto = objectMapper.readValue(boardData, BoardDto.class);
boardService.insertBoard(boardDto, request);
}
// 상세 조회
@Operation(summary = "게시판 상세 조회", description = "게시물 아이디와 일치하는 게시물의 상세 정보를 조회해서 반환합니다.")
@Parameter(name = "boardIdx", description = "게시물 아이디", required = true)
@GetMapping("/board/{boardIdx}")
public BoardDto openBoardDetail(@PathVariable("boardIdx") int boardIdx) throws Exception {
return boardService.selectBoardDetail(boardIdx);
}
// 수정 처리
@PutMapping("/board/{boardIdx}")
public void updateBoard(@PathVariable("boardIdx") int boardIdx, @RequestBody BoardDto boardDto) throws Exception {
boardDto.setBoardIdx(boardIdx);
boardService.updateBoard(boardDto);
}
// 삭제 처리
@DeleteMapping("/board/{boardIdx}")
public void deleteBoard(@PathVariable("boardIdx") int boardIdx) throws Exception {
boardService.deleteBoard(boardIdx);
}
// 첨부파일 다운로드
@GetMapping("/board/file")
public void downloadBoardFile(@RequestParam("idx") int idx, @RequestParam("boardIdx") int boardIdx, HttpServletResponse response) throws Exception {
BoardFileDto boardFileDto = boardService.selectBoardFileInfo(idx, boardIdx);
if (ObjectUtils.isEmpty(boardFileDto)) {
return;
}
Path path = Paths.get(boardFileDto.getStoredFilePath());
byte[] file = Files.readAllBytes(path);
response.setContentType("application/octet-stream");
response.setContentLength(file.length);
response.setHeader("Content-Disposition", "attachment; fileName=\"" + URLEncoder.encode(boardFileDto.getOriginalFileName(), "UTF-8") + "\";");
response.setHeader("Content-Transfer-Encoding", "binary");
response.getOutputStream().write(file);
response.getOutputStream().flush();
response.getOutputStream().close();
}
}
package board;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.filter.HiddenHttpMethodFilter;
@SpringBootApplication
public class BoardApplication {
public static void main(String[] args) {
SpringApplication.run(BoardApplication.class, args);
}
@Bean
public HiddenHttpMethodFilter hiddenHttpMethodFilter() {
return new HiddenHttpMethodFilter();
}
}
// 상세 조회
@Operation(summary = "게시판 상세 조회", description = "게시물 아이디와 일치하는 게시물의 상세 정보를 조회해서 반환합니다.")
@Parameter(name = "boardIdx", description = "게시물 아이디", required = true)
@GetMapping("/board/{boardIdx}")
public ResponseEntity<Object> openBoardDetail(@PathVariable("boardIdx") int boardIdx) throws Exception {
BoardDto boardDto = boardService.selectBoardDetail(boardIdx);
if (boardDto == null) {
Map<String, Object> result = new HashMap<>();
result.put("code", HttpStatus.NOT_FOUND.value());
result.put("name", HttpStatus.NOT_FOUND.name());
result.put("message", "게시판 번호 " + boardIdx + "와 일치하는 게시물이 존재하지 않습니다.");
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(result);
} else {
return ResponseEntity.status(HttpStatus.OK).body(boardDto);
}
}
@Override
public BoardDto selectBoardDetail(int boardIdx) {
boardMapper.updateHitCnt(boardIdx);
// int i = 10 / 0;
BoardDto boardDto = boardMapper.selectBoardDetail(boardIdx);
if (boardDto != null) {
List<BoardFileDto> boardFileInfoList = boardMapper.selectBoardFileList(boardIdx);
boardDto.setFileInfoList(boardFileInfoList);
}
return boardDto;
}
C:\Users\myanj> curl -X GET http://localhost:8080/api/board/8 -v

C:\Users\myanj> curl -X GET http://localhost:8080/api/board/88 -v

