스프링

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);
	}
}