스프링
Swagger/CORS
코딩 화이팅
2023. 4. 26. 14:10
- 프로젝트 개발 시 일반적으로 FrongEnd 개발자와 BackEnd 개발자가 분리
- FrontEnd 개발자의 경우 화면에 집중하고 BackEnd 개발자가 만든 API를 보며 데이터 처리를 하게 된다.
- 이때 개발 상황의 변화에 따른 API의 추가 또는 변경할 때 마다 문서에 적용하는 불편함 발생
- 이 문제를 해결하기 위해 Swagger사용
Swagger란?
- 기존 문서로 사용하던 문제를 해결하기 위해 Swagger 사용
- 간단한 설정으로 프로젝트의 API 목록을 웹에서 확인 및 테스트 할 수 있게 해주는 Library
- Swagger를 사용하면 Controller에 정의되어 있는 모든 URL을 바로 확인 가능
- API 목록 뿐만 아니라 API의 명세 및 설명도 볼 수 있으며, 또한 API를 직접 테스트해볼 수도 있다.
Swagger 호출 URL
http://localhost:8080/context-path/swagger-ui/index.html
예 : http://localhost:8080/mvc/swagger-ui/index.html
Swagger 설정
Pom.xml
mvnrepository-> SpringFox Swagger 3버전 복붙->pox.xml->SpringFox Swagger2 3버전 복붙->pom.xml
pom.xml
<!-- 스웨거사용 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>3.0.0</version>
</dependency>
Servlet-context.xml

Servlet-context.xml
<resources location="classpath:/META-INF/resources/webjars/springfox-swagger-ui/" mapping="/swagger-ui/**"></resources>
com.ssafy.board.config 클래스
package com.ssafy.board.config;
import org.springframework.context.annotation.Bean;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("com.ssafy.board.controller"))
.paths(PathSelectors.ant("/*/api/**"))
.build();
}
}
===============================================================
Servlet-context.xml
<beans:bean id="swagger2Config" class="com.ssafy.board.config.SwaggerConfig"/>

3가지 설정을 마쳐주면 마침내 들어가진다.
Swagger 수정
package com.ssafy.board.config;
import org.springframework.context.annotation.Bean;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("com.ssafy.board.controller"))
.paths(PathSelectors.ant("/*/api/**"))
.build()
.apiInfo(apiInfo());
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("SSAFY 9기 BOARD REST API")
.description("엄청나게 대단한 게시판을 위한 레스트풀한 서버 입니다.")
.version("0.1")
.build();
}
}

package com.ssafy.board.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
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.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.ssafy.board.model.dto.Board;
import com.ssafy.board.model.dto.SearchCondition;
import com.ssafy.board.model.service.BoardService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import springfox.documentation.annotations.ApiIgnore;
@RestController
@RequestMapping("/api")
@Api(tags = "게시판 컨트롤러")
public class BoardRestController {
@Autowired
private BoardService boardService;
//1. 목록을 가져와보자~
@ApiOperation(value="게시글 조회", notes = "검색 조건도 넣으면 같이 가져옴")
@GetMapping("/board")
public ResponseEntity<?> list(SearchCondition condition){
// List<Board> list = boardService.getBoardList(); //단순히 전제 조회
List<Board> list = boardService.search(condition); //검색 조건이 있으면 그걸로 조회
if(list == null || list.size() == 0)
return new ResponseEntity<Void>(HttpStatus.NO_CONTENT);
return new ResponseEntity<List<Board>>(list, HttpStatus.OK);
}
//2. 상세보기
@GetMapping("/board/{id}")
public ResponseEntity<Board> detail(@PathVariable int id){
Board board = boardService.readBoard(id);
//만약 사용자가 잘못 보냈다... 처리를 해라 board라고 하는게 null이면 무엇인가 처리를 해봐라잉
return new ResponseEntity<Board>(board, HttpStatus.OK);
}
//3. 등록
@PostMapping("/board")
public ResponseEntity<Board> write(Board board){
boardService.writeBoard(board);
//지금 우리의 게시글은 키가 절대로 중복이 되지 않는다. 그래서 무조건 등록은 될꺼임...
//가끔가다가 혹여나 여기말고 다른곳에서 문제가 발생해서 글이 등록되지 않았다...
//DB에 댕겨올때 테이블을 변경하는 작업이라면 무엇인가를 하나 돌려줌... 무엇? 테이블을 건드린 행의 개수가 반환이된다.
//만약에 0이라면 이거 등록 안된거니까 등록 안됬어요 ㅠㅠ 하고 프론트에게 돌려주어야 겠다.
//그게 아니라면 잘 등록이 된거니까... OK 보내도 가넝
return new ResponseEntity<Board>(board, HttpStatus.CREATED);
}
//4. 삭제
@DeleteMapping("/board/{id}")
public ResponseEntity<Void> delete(@PathVariable int id){
boardService.removeBoard(id);
//이것도 마찬가지로 반환값이 1이상이어야 실제로 무엇인가 지워진거지 ... 그게 아니면 지워진건 아니다
//이상한 값을 넣어도 그냥 동작을 해버린다.. 그러니 쿼리문을 날리지 못하게 사전에 커팅 해야함.
//그리고 권한이 있어야만 지우는 거지 지금과 같은 방식은 남의글도 마구잡이로 지울수 있다.
return new ResponseEntity<Void>(HttpStatus.OK);
}
//5. 수정
@ApiIgnore
@PutMapping("/board") //JSON형태의 데이터로 요청을 보내보자
public ResponseEntity<Void> update(@RequestBody Board board){
boardService.modifyBoard(board);
//얘도 마찬가지 이죠 옥헤이?
return new ResponseEntity<Void>(HttpStatus.OK);
}
}


PUT이 사라져있다.
package com.ssafy.board.model.dto;
import io.swagger.annotations.ApiModel;
@ApiModel(value="게시판 바구니", description = "게시글 정보")
public class Board {
private int id;
private String title;
private String writer;
private String content;
private String regDate;
private int viewCnt;
public Board() {
}
public Board(String title, String writer, String content) {
super();
this.title = title;
this.writer = writer;
this.content = content;
}
public Board(int id, String title, String writer, String content, String regDate, int viewCnt) {
this.id = id;
this.title = title;
this.writer = writer;
this.content = content;
this.regDate = regDate;
this.viewCnt = viewCnt;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getWriter() {
return writer;
}
public void setWriter(String writer) {
this.writer = writer;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getRegDate() {
return regDate;
}
public void setRegDate(String regDate) {
this.regDate = regDate;
}
public int getViewCnt() {
return viewCnt;
}
public void setViewCnt(int viewCnt) {
this.viewCnt = viewCnt;
}
@Override
public String toString() {
return "Board [id=" + id + ", title=" + title + ", writer=" + writer + ", content=" + content + ", regDate="
+ regDate + ", viewCnt=" + viewCnt + "]";
}
}

CORS

html코드
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<title>연습용</title>
</head>
<body>
<button id="btn">게시글 가져와</button>
<script>
document.querySelector("#btn").addEventListener("click", function () {
fetch("http://localhost:8080/mvc/api/board")
.then((response) => response.text())
.then((text) => console.log(text));
});
</script>
</body>
</html>
package com.ssafy.board.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
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.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.ssafy.board.model.dto.Board;
import com.ssafy.board.model.dto.SearchCondition;
import com.ssafy.board.model.service.BoardService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import springfox.documentation.annotations.ApiIgnore;
@RestController
@RequestMapping("/api")
@Api(tags = "게시판 컨트롤러")
이걸 추가!!!!!!!!!!!!!!!
@CrossOrigin("*")
public class BoardRestController {
@Autowired
private BoardService boardService;
//1. 목록을 가져와보자~
@ApiOperation(value="게시글 조회", notes = "검색 조건도 넣으면 같이 가져옴")
@GetMapping("/board")
public ResponseEntity<?> list(SearchCondition condition){
// List<Board> list = boardService.getBoardList(); //단순히 전제 조회
List<Board> list = boardService.search(condition); //검색 조건이 있으면 그걸로 조회
if(list == null || list.size() == 0)
return new ResponseEntity<Void>(HttpStatus.NO_CONTENT);
return new ResponseEntity<List<Board>>(list, HttpStatus.OK);
}
//2. 상세보기
@GetMapping("/board/{id}")
public ResponseEntity<Board> detail(@PathVariable int id){
Board board = boardService.readBoard(id);
//만약 사용자가 잘못 보냈다... 처리를 해라 board라고 하는게 null이면 무엇인가 처리를 해봐라잉
return new ResponseEntity<Board>(board, HttpStatus.OK);
}
//3. 등록
@PostMapping("/board")
public ResponseEntity<Board> write(Board board){
boardService.writeBoard(board);
//지금 우리의 게시글은 키가 절대로 중복이 되지 않는다. 그래서 무조건 등록은 될꺼임...
//가끔가다가 혹여나 여기말고 다른곳에서 문제가 발생해서 글이 등록되지 않았다...
//DB에 댕겨올때 테이블을 변경하는 작업이라면 무엇인가를 하나 돌려줌... 무엇? 테이블을 건드린 행의 개수가 반환이된다.
//만약에 0이라면 이거 등록 안된거니까 등록 안됬어요 ㅠㅠ 하고 프론트에게 돌려주어야 겠다.
//그게 아니라면 잘 등록이 된거니까... OK 보내도 가넝
return new ResponseEntity<Board>(board, HttpStatus.CREATED);
}
//4. 삭제
@DeleteMapping("/board/{id}")
public ResponseEntity<Void> delete(@PathVariable int id){
boardService.removeBoard(id);
//이것도 마찬가지로 반환값이 1이상이어야 실제로 무엇인가 지워진거지 ... 그게 아니면 지워진건 아니다
//이상한 값을 넣어도 그냥 동작을 해버린다.. 그러니 쿼리문을 날리지 못하게 사전에 커팅 해야함.
//그리고 권한이 있어야만 지우는 거지 지금과 같은 방식은 남의글도 마구잡이로 지울수 있다.
return new ResponseEntity<Void>(HttpStatus.OK);
}
//5. 수정
@ApiIgnore
@PutMapping("/board") //JSON형태의 데이터로 요청을 보내보자
public ResponseEntity<Void> update(@RequestBody Board board){
boardService.modifyBoard(board);
//얘도 마찬가지 이죠 옥헤이?
return new ResponseEntity<Void>(HttpStatus.OK);
}
}
