spring/게시판 api

Spring boot 게시판 API 서버 제작 (12) - 로그인 - api 문서 만들기

얼킴 2023. 2. 23. 19:12

이번에는 저번 시간까지 만들었던 로그인 API에 대한 문서를 만들어 보겠습니다. Swagger를 활용해서 API문서를 만들어볼건데요, Swagger에 대한 내용은 다음 포스팅을 읽어봐주세요.

https://coding-kim.tistory.com/37

 

[Spring] Swagger에 대해서 알아보자

Swagger는 RESTful API 문서화를 위한 도구로, API의 사용 방법과 API의 매개변수, 반환값 등을 문서로 정리해주는 역할을 합니다. 이번 글에서는 Spring Framework에서 Swagger를 사용하는 방법을 소개하겠습

coding-kim.tistory.com

 

Swagger 추가

Swagger를 사용하기 위해 build.gradle에 의존성을 추가하겠습니다.

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
	implementation 'org.springframework.boot:spring-boot-starter-security'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	implementation 'org.springframework.boot:spring-boot-starter-validation'
	implementation 'io.springfox:springfox-boot-starter:3.0.0' //<- 추가
	compileOnly 'org.projectlombok:lombok'
	runtimeOnly 'com.h2database:h2'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	testImplementation 'org.springframework.security:spring-security-test'
	implementation 'io.jsonwebtoken:jjwt:0.9.1'
}

 

Swagger 설정

SwaggerConfig.java

@Import(BeanValidatorPluginsConfiguration.class) //Bean Validation 라이브러리를 사용하여 데이터 유요성 검사 수행하기 위한 구성 제공
@Configuration
public class SwaggerConfig {

    @Bean
    public Docket api() {
        return new Docket(DocumentationType.OAS_30)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("farm.board.controller")) //package에 있는 모든 클래스를 문서화 대상으로 설정
                .paths(PathSelectors.any()) // 모든 api 엔드포인트를 문서화 대상으로 포함
                .build()
                .securitySchemes(List.of(apiKey())) //api 보안 스키마 설정
                .securityContexts(List.of(securityContext())); //
    }

    private ApiInfo apiInfo() { //API 문서의 메타데이터 설정
        return new ApiInfoBuilder()
                .title("Spring Kim Board")
                .description("Spring board REST API Documentation")
                .license("jaeyeon423@gmail.com")
                .licenseUrl("https://github.com/jaeyeon423")
                .version("1.0")
                .build();
    }

    private static ApiKey apiKey() {
        return new ApiKey("Authorization", "Bearer Token", "header");
    }

    private SecurityContext securityContext() {
        return SecurityContext.builder().securityReferences(defaultAuth())
                .operationSelector(oc -> oc.requestMappingPattern().startsWith("/api/")).build();
    }

    private List<SecurityReference> defaultAuth() {
        AuthorizationScope authorizationScope = new AuthorizationScope("global", "global access");
        return List.of(new SecurityReference("Authorization", new AuthorizationScope[] {authorizationScope}));
    }
}

위 코드에 대해서 설명 드리겠습니다.

  • @Import(BeanValidatorPluginsConfiguration.class): Bean Validation 라이브러리를 사용하여 데이터 유효성 검사 수행하기 위한 구성을 제공하는 클래스를 import합니다.
  • new Docket(DocumentationType.OAS_30): OpenAPI 3.0 버전을 사용하여 API 문서를 생성합니다.
  • apiInfo(apiInfo()): API 문서의 메타데이터를 설정하기 위해 apiInfo() 메소드를 호출합니다.
  • select(): API 문서 생성 대상을 설정합니다.
  • apis(RequestHandlerSelectors.basePackage("farm.board.controller")): farm.board.controller 패키지에 있는 모든 클래스를 문서화 대상으로 설정합니다.
  • paths(PathSelectors.any()): 모든 API 엔드포인트를 문서화 대상으로 포함합니다.
  • .securitySchemes(List.of(apiKey())): API 보안 스키마를 설정합니다.
  • .securityContexts(List.of(securityContext())): 보안 컨텍스트를 설정합니다.

WebConfig.java

특별한 기능이 없는 클래스이지만 Swagger를 사용하기 위해서 @EnableWebMvc를 추가해줘야합니다. 

@EnableWebMvc
@Configuration
public class WebConfig {
}

 

 

SecurityConfig.java

Swagger가 생성한 API 문서를 사용하기 위해서 URL 접근 설정을 SecurityConfig에서 해줍니다.

@Override
    public void configure(WebSecurity web) throws Exception {
        //web.ignoring().mvcMatchers("/exception/**"); // /exception/** 패턴의 요청은 보안 검사를 거치지 않는다
        web.ignoring().mvcMatchers("/exception/**","/swagger-ui/**", "/swagger-resources/**", "/v3/api-docs/**"); // /exception/** 패턴의 요청은 보안 검사를 거치지 않는다
    }

 

API 문서 작성

이제 API 문서를 작성해 보겠습니다.

SignController

@Api(value = "Sign Controller", tags = "Sign") // SignController API를 대표하는 value와 tage 정보 설정
@RestController // @Controller + @ResponseBody
@RequiredArgsConstructor // 클래스에서 선언된 final 필드를 가지고 생성자를 만들어줌
public class SignController {
    private final SignService signService;

    @ApiOperation(value = "회원가입", notes = "회원가입을 한다.") //API 문서에서 aPI의 이름(value)와 설명(notes)를 설정
    @PostMapping("/api/sign-up") //HTTP POST 요청을 처리하는 핸들러 메소드를 지정
    @ResponseStatus(HttpStatus.CREATED) //메소드의 실행 결과에 대한 HTTP 응답 코드를 HpptStatus.CREATED로 설정
    public Response signUp(@Valid @RequestBody SignUpRequest req){ //@Valid : SignUpRequest 객체에 대한 유효성 검사, @RequestBody : 청 바디에서 데이터를 추출하여, 매개변수로 전달된 객체에 매핑
        signService.signUp(req); // 회원 가입 처리
        return success();
    }

    @ApiOperation(value = "로그인", notes = "로그인을 한다.")
    @PostMapping("/api/sign-in")
    @ResponseStatus(HttpStatus.OK)//메소드의 실행 결과에 대한 HTTP 응답 코드를 HpptStatus.OK로 설정
    public Response signIn(@Valid @RequestBody SignInRequest req) {
        return success(signService.signIn(req)); // 로그인 처리
    }

    @ApiOperation(value = "토큰 재발급", notes = "리프레시 토큰으로 새로운 액세스 토큰을 발급 받는다.")
    @PostMapping("/api/refresh-token")
    @ResponseStatus(HttpStatus.OK)
    public Response refreshToken(@RequestHeader(value = "Authorization") String refreshToken) {
        return success(signService.refreshToken(refreshToken));
    }
}
  • @Api(value = "Sign Controller", tags = "Sign"): Swagger API 문서에서 Sign Controller API를 대표하는 value와 tag 정보를 설정합니다. value는 API의 분류를 나타내며, tags는 API의 그룹화를 나타냅니다.
  • @ApiOperation(value = "회원가입", notes = "회원가입을 한다."): Swagger API 문서에서 API의 value와 notes 정보를 설정합니다. value는 API의 이름을 나타내며, notes는 API의 설명을 나타냅니다.

SignUpRequest.java

@ApiModel(value = "회원가입 요청")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class SignUpRequest {
    @ApiModelProperty(value = "이메일", notes = "이메일을 입력해주세요", required = true, example = "member@email.com")
    @Email(message = "이메일 형식을 맞춰주세요.")
    @NotBlank(message = "이메일을 입력해주세요.")
    private String email; // 1

    @ApiModelProperty(value = "비밀번호", notes = "사용자의 비밀번호를 입력해주세요.", required = true, example = "123456a!")
    @NotBlank(message = "비밀번호를 입력해주세요.")
    @Pattern(regexp = "^(?=.*[A-Za-z])(?=.*\\d)(?=.*[@$!%*#?&])[A-Za-z\\d@$!%*#?&]{8,}$",
            message = "비밀번호는 최소 8자리이면서 1개 이상의 알파벳, 숫자, 특수문자를 포함해야합니다.")
    private String password; // 2

    @NotBlank(message = "사용자 이름을 입력해주세요.")
    @Size(min=2, message = "사용자 이름이 너무 짧습니다.")
    @Pattern(regexp = "^[A-Za-z가-힣]+$", message = "사용자 이름은 한글 또는 알파벳만 입력해주세요.")
    private String username; // 3

    @NotBlank(message = "닉네임을 입력해주세요.")
    @Size(min=2, message = "닉네임이 너무 짧습니다.")
    @Pattern(regexp = "^[A-Za-z가-힣]+$", message = "닉네임은 한글 또는 알파벳만 입력해주세요.")
    private String nickname; // 4

    public static Member toEntity(SignUpRequest req, Role role, PasswordEncoder encoder){
        return new Member(req.email, encoder.encode(req.password), req.username, req.nickname, List.of(role));
    }
}
  • @ApiModel(value = "회원가입 요청"): \API 문서에서 회원가입 요청에 대한 정보를 제공하는 모델 클래스임을 나타냅니다.
  • @ApiModelProperty(value = "이메일", notes = "이메일을 입력해주세요", required = true, example = "member@email.com"): API 문서에서 모델 클래스의 필드 정보를 설정합니다. value는 필드의 이름을 나타내며, notes는 필드의 설명을 나타냅니다. required는 필드의 필수 여부를 나타내며, example은 필드의 예시 값을 나타냅니다.

SignInRequest.java

@ApiModel(value = "로그인 요청")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class SignInRequest {
    @ApiModelProperty(value = "이메일", notes = "사용자의 이메일을 입력해주세요", required = true, example = "member@email.com")
    @Email(message = "이메일 형식을 맞춰주세요.")
    @NotBlank(message = "이메일을 입력해주세요.")
    private String email; // 1

    @ApiModelProperty(value = "비밀번호", notes = "사용자의 비밀번호를 입력해주세요.", required = true, example = "123456a!")
    @NotBlank(message = "비밀번호를 입력해주세요.")
    private String password; // 2
}

 

MemberController.java

@Api(value = "Member Controller", tags = "Member")
@RestController
@RequiredArgsConstructor
@Slf4j
public class MemberController {
    private final MemberService memberService;

    @ApiOperation(value = "사용자 정보 조회", notes = "사용자 정보를 조회한다.")
    @GetMapping("/api/members/{id}")
    @ResponseStatus(HttpStatus.OK)
    public Response read(@PathVariable Long id){
        return Response.success(memberService.read(id)); //회원 조회 후 테이터와 함께 성공 반환
    }

    @ApiOperation(value = "사용자 정보 삭제", notes = "사용자 정보를 삭제한다.")
    @DeleteMapping("/api/members/{id}")
    @ResponseStatus(HttpStatus.OK)
    public Response delete(@PathVariable Long id) {
        memberService.delete(id); //회원 삭제
        return Response.success(); //성공 반환
    }
}

 

API 문서 확인

먼저 문서를 확인하기 위해 API 문서 url로 이동 하겠습니다. url : http://localhost:8080/swagger-ui/index.html

접속하면 다음과 같은 화면이 보입니다.

코드에서 설정했던 정보들을 문서에서 확인할 수 있습니다.

 

회원가입

회원가입을 살펴보면 다음과 같이 코드에서 설정해놓은 제약 조건과 예시 데이터를 확인할 수 있습니다.

실제로 테스트를 진행해 보고 싶으면 우측에 Try it out 버튼을 누르면 예시데이터가 입력되어있고 Excute를 눌러 실행해볼 수 있습니다.

결과

로그인

결과

토큰 재발급

로그인 했을 때 받은 refresh token 값을 입력하면

회원 조회

회원 조회까지 모두 정상적으로 결과가 나오는 것을 확인할 수 있었습니다.

 


궁금한신점이나 잘못된 부분이 있으면 자유롭게 댓글 달아주세요.

github : https://github.com/jaeyeon423/spring_board_api