스프링

 

 

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/braverokmc79/sprig_boot_2.7.0_mybatis_board_project/commit/b405eb1d2d474a25c9834c9370eed6119ce34b03

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/braverokmc79/sprig_boot_2.7.0_mybatis_board_project/commit/35a4cc46dbdeeabf4bde8a40cef4ec2f4a8f0805

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/braverokmc79/sprig_boot_2.7.0_mybatis_board_project/commit/49b7d6c422dca2bb015f5ca3b626579cbc1084b6

https://github.com/stylehosting/example-spring

 

 

 

 

 

 

spring

 

about author

PHRASE

Level 60  라이트

글 못한 놈 붓 고른다 , 학식이나 기술이 모자라는 사람일수록 공연한 트집을 잘 잡는다는 말.

댓글 ( 4)

댓글 남기기

작성