1.Swagger 설치 및 게시판 API 문서화
스프링부트 버전 : 2.7.0
1) 라이브러리 등록
https://mvnrepository.com/search?q=springfox
pom.xml
<properties> <java.version>11</java.version> <swagger-version>2.9.2</swagger-version> </properties> <dependencies> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>${swagger-version}</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>${swagger-version}</version> </dependency> </dependencies>
2) Configuration 설정
SwaggerConfiguration
mport org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.ApiSelectorBuilder; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger2.annotations.EnableSwagger2; @Configuration @EnableSwagger2 public class SwaggerConfiguration { @Bean public Docket docker() { ApiInfoBuilder apiInfo=new ApiInfoBuilder(); apiInfo.title(null); apiInfo.description("API 서버 문서 입니다."); Docket docket=new Docket(DocumentationType.SWAGGER_2); docket.apiInfo(apiInfo.build()); ApiSelectorBuilder apis=docket.select().apis(RequestHandlerSelectors.basePackage("kr.so.songjava.mvc.controller")); apis.paths(PathSelectors.ant("/**")); return apis.build(); } }
3) 컨트롤에서 설정
BoardController
import java.util.List; import org.springframework.beans.factory.annotation.Autowired; 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.PutMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import io.swagger.annotations.Api; import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParams; import io.swagger.annotations.ApiOperation; import kr.so.songjava.mvc.domain.dto.BoardDto; import kr.so.songjava.mvc.domain.entity.Board; import kr.so.songjava.mvc.service.BoardSevice; @RestController @RequestMapping("/board") @Api(tags="게시판 API") public class BoardController { @Autowired private BoardSevice boardSevice; /** 게시판 목록리턴 */ @GetMapping("/") @ApiOperation(value="목록조회", notes="게시물 번호에 해당하는 목록정보를 조회할수 있습니다.") public List<Board> getList(){ return boardSevice.getList(); } /** 게시판 상세보기 */ @GetMapping("/{boardSeq}") @ApiOperation(value="상세조회", notes="게시물 번호에 해당하는 상세정보를 조회할수 있습니다.") @ApiImplicitParams({ @ApiImplicitParam(name="boardSeq", value="게시물 번호", example = "1") }) public Board get(@PathVariable int boardSeq) { return boardSevice.get(boardSeq); } /** 게시판 등록/수정처리 */ @PutMapping("/save") @ApiOperation(value="등록/수정처리", notes="신규 게시물 저장 및 기존 게시물 업데이트가 가능합니다.") @ApiImplicitParams({ @ApiImplicitParam(name="boardSeq", value="게시물 번호", example = "1"), @ApiImplicitParam(name="title", value="제목", example = "spring"), @ApiImplicitParam(name="contents", value="내용", example="spring 강좌"), }) public Integer save(BoardDto boardDto) { return boardSevice.save(boardDto); } /** 게시판 삭제처리 */ @DeleteMapping("/{boardSeq}") @ApiOperation(value="게시판 삭제처리", notes="게시물 번호에 해당하는 정보를 삭제합니다.") @ApiImplicitParams({ @ApiImplicitParam(name="boardSeq", value="게시물 번호", example = "1") }) public int delete(@PathVariable int boardSeq) { return boardSevice.delete(boardSeq); } }
application.properties 에 추가
spring.mvc.pathmatch.matching-strategy= ant-path-matcher
4) /swagger-ui.html 실행
http://localhost:8080/swagger-ui.html
소스
https://github.com/stylehosting/example-spring
2.API 공통 Response class, enum 사용하기
BaseResponseCode
public enum BaseResponseCode { SUCCESS(200), // 성공 ERROR(500), // 실패 ; private int status; BaseResponseCode(int status) { this.status = status; } public int status() { return status; } }
BaseResponse
import lombok.Data; @Data public class BaseResponse<T> { private BaseResponseCode code; private String message; private T data; public BaseResponse(T data) { this.code=BaseResponseCode.SUCCESS; this.data=data; } public BaseResponse(String status, T data) { if(status.equals("bad"))this.code=BaseResponseCode.ERROR; else this.code=BaseResponseCode.SUCCESS; this.data=data; } }
BoardController
import java.util.List; import org.springframework.beans.factory.annotation.Autowired; 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.PutMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import io.swagger.annotations.Api; import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParams; import io.swagger.annotations.ApiOperation; import kr.so.songjava.configuration.http.BaseResponse; import kr.so.songjava.mvc.domain.dto.BoardDto; import kr.so.songjava.mvc.domain.entity.Board; import kr.so.songjava.mvc.service.BoardSevice; @RestController @RequestMapping("/board") @Api(tags="게시판 API") public class BoardController { @Autowired private BoardSevice boardService; /** 게시판 목록리턴 */ @GetMapping("/") @ApiOperation(value="목록조회", notes="게시물 번호에 해당하는 목록정보를 조회할수 있습니다.") public BaseResponse<List<Board>> getList(){ return new BaseResponse<List<Board>>(boardService.getList()); } /** 게시판 상세보기 */ @GetMapping("/{boardSeq}") @ApiOperation(value="상세조회", notes="게시물 번호에 해당하는 상세정보를 조회할수 있습니다.") @ApiImplicitParams({ @ApiImplicitParam(name="boardSeq", value="게시물 번호", example = "1") }) public BaseResponse<Board> get(@PathVariable int boardSeq) { return new BaseResponse<Board>(boardService.get(boardSeq)); } /** 게시판 등록/수정처리 */ @PutMapping("/save") @ApiOperation(value="등록/수정처리", notes="신규 게시물 저장 및 기존 게시물 업데이트가 가능합니다.") @ApiImplicitParams({ @ApiImplicitParam(name="boardSeq", value="게시물 번호", example = "1"), @ApiImplicitParam(name="title", value="제목", example = "spring"), @ApiImplicitParam(name="contents", value="내용", example="spring 강좌"), }) public BaseResponse<Integer> save(BoardDto boardDto) { boardService.save(boardDto); return new BaseResponse<Integer>(boardDto.getBoardSeq()); } /** 게시판 삭제처리 */ @DeleteMapping("/{boardSeq}") @ApiOperation(value="게시판 삭제처리", notes="게시물 번호에 해당하는 정보를 삭제합니다.") @ApiImplicitParams({ @ApiImplicitParam(name="boardSeq", value="게시물 번호", example = "1") }) public BaseResponse<Boolean> delete(@PathVariable int boardSeq) { return new BaseResponse<Boolean>(boardService.delete(boardSeq)); } }
소스
https://github.com/stylehosting/example-spring
3. ControllerAdvice 사용과 예외처리 다국어 활용
다국어 참조
https://velog.io/@haerong22/Springboot-쿠키를-이용한-다국어-처리-i18n
1) 프러퍼티 설정
messages_en.properties
language = English welcome = Hello, {0} testName= My name is {0}. I live in {1}. login.info.error= Login Error user.password.hasLength= You must enter a password. user.password.isTrue= Password does not match
messages_ko.properties
language = 한글 welcome = 안녕? {0} testName= 제 이름은 {0} 입니다. 사는 곳은 {1} 입니다. login.info.error= 로그인 오류 user.password.hasLength= 비밀번호를 필수로 입력해주셔야 합니다. user.password.isTrue= 비밀번호가 일치하지 않습니다.
2) I18nConfig
import java.util.Locale; import org.springframework.context.MessageSource; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.support.ReloadableResourceBundleMessageSource; import org.springframework.web.servlet.LocaleResolver; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.i18n.CookieLocaleResolver; import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; @Configuration public class I18nConfig implements WebMvcConfigurer { /** 스프링이 클라이언트의 언어,국가 정보를 인식하게 하는 메소드 여기서는 쿠키의 값을 저장하여 사용 * * AcceptHeaderLocaleResolver : Http 헤더의 Accept-Language의 값을 사용한다. (기본값) CookieLocaleResolver : 쿠키의 값을 저장하여 사용한다. SessionLocaleResolver : 세션에 값을 저장하여 사용한다. FixedLocaleResolver : 요청과 관계없이 default locale 사용한다. * */ @Bean public LocaleResolver localeResolver() { CookieLocaleResolver resolver = new CookieLocaleResolver(); resolver.setDefaultLocale(Locale.getDefault()); resolver.setCookieName("lang"); return resolver; } /** * Locale 값이 변경되면 인터셉터가 동작한다. url의 query parameter에 지정한 값이 들어올 때 동작한다. ex) http://localhost:8080?lang=ko * @return */ @Bean public LocaleChangeInterceptor localeChangeInterceptor() { LocaleChangeInterceptor interceptor = new LocaleChangeInterceptor(); interceptor.setParamName("lang"); return interceptor; } /** 스프링이 작성한 언어 리소스들을 사용할 수 있게 등록,설정 */ @Bean public MessageSource messageSource() { // 지정한 시간마다 다시 리로드 하도록 한다. ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource(); // 언어 리소스들이 있는 경로를 지정한다. messageSource.setBasename("classpath:/i18n/messages"); messageSource.setDefaultEncoding("UTF-8"); // 인코딩 messageSource.setCacheSeconds(1); //개발시 //messageSource.setCacheSeconds(10 * 60); // 리로드 시간 messageSource.setDefaultLocale(Locale.KOREA); messageSource.setUseCodeAsDefaultMessage(true); return messageSource; } /** * 인터셉터 등록 LocaleChangeInterceptor 를 스프링 컨테이너에 등록한다. WebMvcConfigurer 를 상속받고 addInterceptors를 오버라이딩 한다. */ @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(localeChangeInterceptor()); } }
3) GlobalControllerAdvice 설정
import org.springframework.context.MessageSource; import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import kr.so.songjava.configuration.http.BaseResponse; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; /** * 예외 처리 * @RestControllerAdvice는 @ControllerAdvice+@ResponseBody의 역할을 한다. * **/ @RestControllerAdvice @RequiredArgsConstructor @Slf4j public class GlobalControllerAdvice { private final MessageSource messageSource; @ExceptionHandler(IllegalArgumentException.class) public BaseResponse<?> handleIllegalArgumentException(IllegalArgumentException e){ String code=e.getMessage(); String message=messageSource.getMessage(code, null, LocaleContextHolder.getLocale()); log.info("code : {} ", code); log.info("message : {} ", message); log.info("e :{} ", e); return new BaseResponse<Object>("error", null, message); } }
4)사용예 1
import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.util.Assert; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import kr.so.songjava.mvc.domain.dto.UserDTO; @RestController @RequestMapping("/api/user") public class UserApiController { //비밀번호 예시 성공 값 private static final String DATABASE_PASSWORD ="test1234" ; //@PostMapping("/confirm") @GetMapping("/confirm") //get 테스트용 public ResponseEntity<UserDTO> confirm(@RequestParam String password){ Assert.hasLength(password, "user.password.hasLength"); Assert.isTrue(password.equals(DATABASE_PASSWORD) ,"user.password.isTrue"); UserDTO userDTO=new UserDTO(); userDTO.setUsername("개발자"); userDTO.setEmail("test@gmail.com"); return ResponseEntity.status(HttpStatus.OK).body(userDTO); } }
사용예2
import java.util.Locale; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.MessageSource; import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import io.swagger.annotations.Api; import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParams; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; @Controller @Api(tags="인덱스 API") @Slf4j public class IndexController { @Autowired private MessageSource messageSource; @GetMapping({"","/"}) @ResponseBody @ApiOperation(value="인덱스화면", notes="인덱스 홈 화면을 조회할수 있습니다.") public String index(){ return "index"; } @GetMapping("/i18n") @ResponseBody @ApiOperation(value="다국어테스트", notes="다국어테스트 입니다.") @ApiImplicitParams({ @ApiImplicitParam(name="name", value="이름", example = "홍길동"), @ApiImplicitParam(name="age", value="나이", example = "24"), }) public String 다국어테스트(String name, String age){ log.info(messageSource.getMessage("welcome", new String[]{"kim"}, Locale.ENGLISH)); log.info(messageSource.getMessage("welcome", new String[]{"kim"}, Locale.KOREAN)); String code="login.info.error"; String message =messageSource.getMessage(code, null, LocaleContextHolder.getLocale()); log.info("message : {} " , message); String message2=messageSource.getMessage("testName", new String[]{name ,age}, LocaleContextHolder.getLocale()); return message2; } }
소스
https://github.com/stylehosting/example-spring
댓글 ( 4)
댓글 남기기