swagger ์˜ค๋ฅ˜

์ง€ํ˜œยท2025๋…„ 2์›” 20์ผ
0

๐Ÿ’ก https://github.com/H5-dev-project/newsfeed

1. swagger ์ ์šฉ ์ด์œ 

์ด๋ฒˆ ํŒ€ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ๋กœ๊ทธ์ธ ์‹œ ๋ฐœ๊ธ‰๋ฐ›์€ ํ† ํฐ์„ HTTP header์— ํฌํ•จ์‹œ์ผœ ๋กœ๊ทธ์ธ ์—ฌ๋ถ€๋ฅผ ํŒ๋‹จํ–ˆ๋‹ค.
์ฒ˜์Œ swagger๋ฅผ ์ ์šฉํ•˜๊ธฐ ์ด์ „์—๋Š” postman์„ ์ด์šฉํ•˜์—ฌ ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ–ˆ์œผ๋‚˜, ์ด ๊ฒฝ์šฐ์—๋Š” ๋ฐœ๊ธ‰ ๋ฐ›์€ ํ† ํฐ์„ ํ…Œ์ŠคํŠธํ•  API์˜ header์— ๋งค๋ฒˆ ๊ฐ’์„ ๋ณ€๊ฒฝํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค๋Š” ๋ถˆํŽธํ•จ์ด ์กด์žฌํ–ˆ๋‹ค.
๋”ฐ๋ผ์„œ ๋ณด๋‹ค ํŽธ๋ฆฌํ•œ ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•ด swagger๋ฅผ ๋„์ž…ํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ๋‹ค.

2. swagger ์˜ค๋ฅ˜

๐Ÿ“ 500 ์—๋Ÿฌ

swagger๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ์ง„ํ–‰ํ–ˆ์œผ๋‚˜ ์œ„์˜ ๋‚ด์šฉ ์ฒ˜๋Ÿผ 500 ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค.
์›์ธ์„ ์ฐพ์•„๋ณด๋‹ˆ ์šฐ๋ฆฌ๊ฐ€ ํ”„๋กœ์ ํŠธ์—์„œ ์ ์šฉํ•œ spring security์—์„œ filterChain์— ๊ฑธ๋ ธ๊ธฐ ๋•Œ๋ฌธ์ด์—ˆ๋‹ค.
๊ทธ๋ž˜์„œ filter์—์„œ swagger ๊ฒฝ๋กœ๋ฅผ requestMatchers๋กœ ์ง€์ • ํ›„ permitAll()์„ ํ†ตํ•ด ์˜ˆ์™ธ ์ฒ˜๋ฆฌ๋ฅผ ํ†ตํ•ด filter์—์„œ ๋ง‰ํžˆ์ง€ ์•Š๋„๋ก ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์ฃผ์—ˆ๋‹ค.

๐Ÿ“Œ ์ฝ”๋“œ ์ˆ˜์ •

@Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        return http
                .csrf(AbstractHttpConfigurer::disable)
                .sessionManagement(session -> session
                        .sessionCreationPolicy(SessionCreationPolicy.STATELESS) // SessionManagementFilter, SecurityContextPersistenceFilter
                )
                .addFilterBefore(jwtSecurityFilter, SecurityContextHolderAwareRequestFilter.class)
                .formLogin(AbstractHttpConfigurer::disable) // UsernamePasswordAuthenticationFilter, DefaultLoginPageGeneratingFilter ๋น„ํ™œ์„ฑํ™”
                .anonymous(AbstractHttpConfigurer::disable) // AnonymousAuthenticationFilter ๋น„ํ™œ์„ฑํ™”
                .httpBasic(AbstractHttpConfigurer::disable) // BasicAuthenticationFilter ๋น„ํ™œ์„ฑํ™”
                .logout(AbstractHttpConfigurer::disable) // LogoutFilter ๋น„ํ™œ์„ฑํ™”
                .exceptionHandling(exception -> exception
                        .authenticationEntryPoint(jwtAuthEntryPoint)  // EntryPoint ๋“ฑ๋ก
                )
                .authorizeHttpRequests(auth -> auth
                // โœ… ์ฝ”๋“œ ์ถ”๊ฐ€
                        .requestMatchers("/swagger",
                                "/swagger-ui.html",
                                "/swagger-ui/**",
                                "/api-docs",
                                "/api-docs/**",
                                "/v3/api-docs/**")
                        .permitAll()                
                        .requestMatchers(
                                "/api/users/register",
                                "/api/users/login",
                                "/api/auth/refresh"
                        )
                        .permitAll()
                        .anyRequest().authenticated()
                )
                .build();
    }

๐Ÿ“ ์ฒจ๋ถ€ํŒŒ์ผ ์—…๋กœ๋“œ ์˜ค๋ฅ˜

์ฒจ๋ถ€ํŒŒ์ผ๊ณผ ๊ฒŒ์‹œ๊ธ€ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ™์ด ์ €์žฅํ•ด์•ผํ•˜๋Š” ๋ถ€๋ถ„์ด ์กด์žฌํ–ˆ๋Š”๋ฐ, swagger์—์„œ json๊ฐ’์„ ์ธ์‹ํ•˜์ง€ ๋ชปํ•ด์„œ ์˜ค๋ฅ˜๊ฐ€ ๋‚ฌ๋‹ค.
ํ™•์ธํ•ด๋ณธ ๊ฒฐ๊ณผ swagger UI๋Š” @RequestPart ํ•„๋“œ์˜ JSON๊ฐ’์„ ์ œ๋Œ€๋กœ ์ฒ˜๋ฆฌํ•˜์ง€ ๋ชปํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ์กด์žฌํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ์•˜๋‹ค.
๊ทธ๋ž˜์„œ ๊ธฐ์กด requestDto ๊ฐ’์„ ๊ทธ๋Œ€๋กœ ๋ฐ›์•˜๋˜ ๊ฒƒ์„ String ํ˜•ํƒœ๋กœ ๋จผ์ € ๋ฐ›์€ ํ›„ ์ˆ˜๋™์œผ๋กœ requestDto๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ํ•ด๊ฒฐํ–ˆ๋‹ค.

๐Ÿ“Œ ๊ธฐ์กด ์ฝ”๋“œ)

@PostMapping("/api/boards")
    public ResponseEntity<ResponseDto<BoardResponseDto>> save(
            @UserSession AuthUsers authUsers, @RequestPart("board") BoardSaveRequestDto dto,
            @RequestPart(value = "file") MultipartFile file
            ) {
        return ResponseEntity.ok(boardService.save(authUsers, dto, file));
    }

๐Ÿ“Œ ์ˆ˜์ • ์ฝ”๋“œ)

// MULTIPART_FORM_DATA ํ˜•์‹์œผ๋กœ ๋ฐ›๊ธฐ
@PostMapping(value = "/api/boards", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public ResponseEntity<ResponseDto<BoardResponseDto>> save(
            @Parameter(hidden = true) @UserSession AuthUsers authUsers,
            @RequestPart("board") @io.swagger.v3.oas.annotations.media.Schema(type = "string" , example = "{\"title\" : \"์ œ๋ชฉ\" , \"content\" : \"๋‚ด์šฉ\" , \"visibilityType\" : 1}")
            String boardJson, // String์œผ๋กœ ๋ฐ›๊ธฐ
            @RequestPart(value = "file", required = false) MultipartFile file
    ) {
        try {
            // JSON์„ BoardSaveRequestDto๋กœ ๋ณ€ํ™˜
            BoardSaveRequestDto dto = objectMapper.readValue(boardJson, BoardSaveRequestDto.class);
            return ResponseEntity.ok(boardService.save(authUsers, dto, file));
        } catch (Exception e) {
            return ResponseEntity.badRequest().build();
        }
    }

3.๊ทธ ์™ธ ์‚ฌ์šฉ๋œ ์–ด๋…ธํ…Œ์ด์…˜

  • @Parameter(hidden = true) : @UserSession ์–ด๋…ธํ…Œ์ด์…˜์„ requestBody๋กœ ์ธ์‹ํ•˜์—ฌ swagger์—์„œ ๋ณด์ด์ง€ ์•Š๋„๋ก ๋ณ€๊ฒฝ
์‚ฌ์šฉ ์ „์‚ฌ์šฉ ํ›„



  • @Schema(description = "์—ญํ• , ์‚ฌ์šฉ๋ฒ•์„ ์„ค๋ช…", example = "๋ฐ์ดํ„ฐ์˜ ์˜ˆ์‹œ ๊ฐ’ ์ œ์‹œ")
์‚ฌ์šฉ ์ „์‚ฌ์šฉ ํ›„
์„ค๋ช…์„ค๋ช…

0๊ฐœ์˜ ๋Œ“๊ธ€

๊ด€๋ จ ์ฑ„์šฉ ์ •๋ณด