๐Ÿ“• Spring Framework/Spring Project

ใ€Œํ…Œ์ŠคํŠธ ์ฝ”๋“œ & Spring REST Docsใ€

GroovyArea 2022. 6. 20. 17:42
๊ฐœ์ธ ํ”„๋กœ์ ํŠธ๋Š” ์™„์„ฑ์ด ๋˜์—ˆ๋‹ค. ์ด์ œ๋Š” ์„ธ๋ถ€์ ์ธ ๋””ํ…Œ์ผ์— ์‹ ๊ฒฝ์„ ์“ฐ๋ฉฐ ๋ฆฌํŒฉํ„ฐ๋ง๊ณผ ๊ทธ์— ํ•„์š”ํ•œ ๊ฐœ๋… ์ •๋ฆฌ๋ฅผ ํ•˜๋ฉฐ ํ”„๋กœ์ ํŠธ์— ์ ์šฉ์‹œํ‚ค๊ณ  ์žˆ๋‹ค.
REST API๋ฅผ ์ฒ˜์Œ ์„ค๊ณ„ํ•ด ๋ณด์•˜๋Š”๋ฐ, ๋ถ€์กฑํ•œ ์ ์ด ๋งŽ์•˜์ง€๋งŒ ์‹ ์„ ํ•œ ๊ฒฝํ—˜์ด๊ณ  ์งง์€ ๊ธฐ๊ฐ„ ๋‚ด์— ๋ฐฐ์šด ๊ฒƒ์ด ๋งŽ์€ ์„ค๊ณ„ ๊ณผ์ •์ด์—ˆ๋‹ค. 
API๋ฅผ ์„ค๊ณ„ํ•˜๋ฉด ๊ทธ์— ๋งž๋Š” ๋ช…์„ธ๊ฐ€ ํ•„์š”ํ•œ๋ฐ, ๋ณดํ†ต Swagger๋‚˜ Spring REST Docs ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์ด์šฉํ•œ๋‹ค๊ณ  ํ•œ๋‹ค.
Swagger๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์• ๋…ธํ…Œ์ด์…˜์„ ์ด์šฉํ•ด ํŽธํ•˜๊ฒŒ ๋ฌธ์„œ๋ฅผ ์ž‘์„ฑํ•  ์ˆœ ์žˆ์ง€๋งŒ, ํ”„๋กœ์ ํŠธ์˜ ํ™•์‹ค์„ฑ๊ณผ ์ •ํ™•์„ฑ์„ ๋’ท๋ฐ›์นจํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑ์ด ํ•„์ˆ˜๋ผ๊ณ  ์ƒ๊ฐํ•˜๊ธฐ ๋•Œ๋ฌธ์— Spring REST Docs๋ฅผ ์ด์šฉํ•˜๊ธฐ๋กœ ์ƒ๊ฐํ•˜๋ฉฐ ์ ์šฉํ•ด๋ณด์•˜๋‹ค. 

ํ…Œ์ŠคํŠธ ์ฝ”๋“œ

ํฌ๊ฒŒ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ์™€ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ๊ฐ€ ์žˆ๋‹ค. 

 

ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ

๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋ณด๋‹ค ๋” ํฐ ๋™์ž‘์„ ๋‹ฌ์„ฑํ•˜๊ธฐ ์œ„ํ•ด ์—ฌ๋Ÿฌ ๋ชจ๋“ˆ๋“ค์„ ๋ชจ์•„ ์˜๋„๋Œ€๋กœ ํ˜‘๋ ตํ•˜๋Š”์ง€ ํ™•์ธํ•˜๋Š” ํ…Œ์ŠคํŠธ

์ฃผ๋กœ @SpringBootTest ์• ๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜์—ฌ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์„ ์ƒ์„ฑํ•œ๋‹ค.

 

๋‹จ์œ„ ํ…Œ์ŠคํŠธ

๊ฐ€๋Šฅํ•œ ๊ฐ€์žฅ ์ž‘์€ ์†Œํ”„ํŠธ ์›จ์–ด๋ฅผ ์‹คํ–‰ํ•˜์—ฌ ์˜ˆ์ƒ๋Œ€๋กœ ๋™์ž‘ํ•˜๋Š”์ง€ ํ™•์ธํ•˜๋Š” ํ…Œ์ŠคํŠธ

=> TDD ์ง„ํ–‰ ์‹œ ์œ ์šฉํ•˜๋‹ค

์ž๋ฐ”๋Š” ์ฃผ๋กœ Junit์„ ์‚ฌ์šฉํ•˜์—ฌ ํ…Œ์ŠคํŠธํ•œ๋‹ค. 

 

๋‚˜์˜ ๊ฒฝ์šฐ๋Š” ์Šคํ”„๋ง ํ”„๋กœ์ ํŠธ์ด๋ฏ€๋กœ ์˜์กด์„ฑ์— ํ…Œ์ŠคํŠธ ๋ชจ๋“ˆ์ด ์ž๋™ ์ถ”๊ฐ€ ๋˜์—ˆ์œผ๋ฏ€๋กœ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

testImplementation 'org.springframework.boot:spring-boot-starter-test'

 

์—ฌ๊ธฐ์„œ Mock Object๋ฅผ ์ด์šฉํ•œ Mockito Framework๋ฅผ ์ด์šฉํ•˜์—ฌ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ๋‹ค. 

 

๊ทธ์ค‘ ์ปจํŠธ๋กค๋Ÿฌ์˜ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๊ธฐ๋กœ ํ–ˆ๋‹ค. 

์‚ฌ์šฉํ•˜๋Š” ์• ๋…ธํ…Œ์ด์…˜์€ @WebMVCTest๊ณผ @AutoConfigureRestDocs๋ฅผ ์ด์šฉํ•˜์—ฌ Spring REST Docs๋ฅผ ๋™์‹œ์— ์ง„ํ–‰ํ–ˆ๋‹ค.

 

User API ํ…Œ์ŠคํŠธ

@BeforeEach
public void setUp(RestDocumentationContextProvider restDocumentationContextProvider) {
    mockMvc =
            MockMvcBuilders.standaloneSetup(new UserController(userService))
                    .apply(documentationConfiguration(restDocumentationContextProvider))
                    .addFilters(new CharacterEncodingFilter("utf-8", true))
                    .alwaysDo(document("{method-name}", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint())))
                    .build();
}

@Test
@DisplayName("ํšŒ์› ๋””ํ…Œ์ผ ์กฐํšŒ ํ…Œ์ŠคํŠธ")
void detailActionTest() throws Exception {
    Mockito.when(userService.findById(userDTO.getUserId())).thenReturn(userDTO);
    this.mockMvc
            .perform(get("/api/users/{userId}", userDTO.getUserId()).accept(MediaType.APPLICATION_JSON)
                    .header("Authorization", "Bearer ${AUTH_TOKEN}"))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.data.userId", is(userDTO.getUserId())))
            .andExpect(jsonPath("$.data.userMainAddress", is(userDTO.getUserMainAddress())))
            .andExpect(jsonPath("$.message", is("๊ถŒํ•œ : BASIC_USER")))
            .andDo(document("detailUser", preprocessRequest(prettyPrint()),
                    preprocessResponse(prettyPrint())))
    ;
}

 

@ExtendWith({RestDocumentationExtension.class, SpringExtension.class})
@WebMvcTest(UserController.class)
@Import(value = {AuthorizationExtractor.class, JwtTokenProvider.class})
@AutoConfigureRestDocs
class UserControllerTest {

ํ…Œ์ŠคํŠธ ํด๋ž˜์Šค์— ๋ถ™์ธ ์• ๋…ธํ…Œ์ด์…˜๋“ค

 

ํ…Œ์ŠคํŠธ๊ฐ€ ์™„๋ฃŒ ๋˜๋ฉด snipets ํŒŒ์ผ๋“ค์ด ์ƒ์„ฑ๋œ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๋ฌธ์„œํ™”ํ•  ์ˆ˜ ์žˆ๋‹ค.

์ด๋ ‡๊ฒŒ ์ƒ์„ฑ์ด ๋จ.

 

.adoc ํŒŒ์ผ์„ ํ†ตํ•ด ๋ฌธ์„œ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋•Œ ์•„๊นŒ ์ƒ์„ฑ๋œ snippets ํŒŒ์ผ๋“ค์„ ์‚ฝ์ž…ํ•ด ์ž‘์„ฑ ๊ฐ€๋Šฅํ•˜๋‹ค.

 

๊ฐ„๋‹จํžˆ ์ดˆ๊ธฐ ๋ฒ„์ „์„ ์ž‘์„ฑํ–ˆ๋‹ค.

์ด์ œ ์žฅ๋ฐ”๊ตฌ๋‹ˆ, ์ฃผ๋ฌธ, ๊ด€๋ฆฌ์ž API๋ฅผ ์ถ”๊ฐ€ํ•ด์•ผ๊ฒ ๋‹ค. 

 

 

๋ฐ˜์‘ํ˜•