2024. 10. 20. 23:36ใ๐ ๋ฐฑ์๋ ๊ฐ๋ฐ/SPRING
[์ธํ๋ฐ ์๋ฐ์ ํด๋ฝ 2๊ธฐ - BE] 3์ฃผ์ฐจ ๋ฐ์๊ตญ
์ด ๋ธ๋ก๊ทธ๋ ์ ๋ณด๊ทผ๋์ ์ ๋ฌธ์๋ฅผ ์ํ Spring Boot with Kotlin - ๋๋ง์ ํฌํธํด๋ฆฌ์ค ์ฌ์ดํธ ๋ง๋ค๊ธฐ ๊ฐ์ ๊ธฐ๋ฐ์ผ๋ก ์ฝ๋์์ฑ๊ณผ ์ฝ๋์ค๋ช ์ ์ ์์ต๋๋ค
1. controller test ์ฝ๋ ๋ถ์
์ ๋ํ ์ด์ ์กฐ์ฌ
- @AutoConfigureMockMvc
- MockMvc๋ฅผ ์๋์ผ๋ก ์ค์ ํด ์ฃผ๋ ์ ๋ํ ์ด์ . ์ด ์ ๋ํ ์ด์ ์ ํตํด HTTP ์์ฒญ์ ์ํํ๊ณ ์๋ต์ ํ์ธํ ์ ์๋ค.
- MockMvc ๋ ?
- ์ค์ ๋ก ์๋ฒ๋ฅผ ๋์ฐ์ง ์๊ณ ์ปจํธ๋กค๋ฌ๋ฅผ ํ ์คํธํ ์ ์๋ ๋๊ตฌ
- @DisplayName("Test")
- ํ ์คํธ ํด๋์ค๋ฅผ ์์ํ ๋ ํ ์คํธ ํด๋์ค์ ์ด๋ฆ์ ์ง์ ํด์ ํ ์คํธ ๋ฆฌํฌํธ์ ํ์๋๋ค.
- ์ ์ฝ๋๋ฅผ ์คํ์ํค๋ฉด ๋ฆฌํฌํธ์ TEST ๋ผ๋ ์ด๋ฆ์ผ๋ก ํ ์คํธ ์ฑ๊ณต์ ๋ฌด๊ฐ ํ์๋จ.
- @Configuration : Spring์์ Bean์ ์๋์ผ๋ก ๋ฑ๋กํ๊ธฐ ์ํด์ ์ฌ์ฉ
๋ฉ์๋ ๋ถ์
@Test
@DisplayName("Introductions ์กฐํ")
fun testGetIntroductions() {
val uri = "/api/v1/introductions"
val mvcResult = performGet(uri)
val contentAsString = mvcResult.response.getContentAsString(StandardCharsets.UTF_8)
val jsonArray = JSONArray(contentAsString)
assertThat(jsonArray.length()).isPositive()
}
- api/vi/introduction์ uri ๋ณ์์ ๋ฃ์.
- HTTP GET ์์ฒญ์ ๋ณด๋ธํ, ์๋ต์ ๋ฌธ์์ด๋ก ๋ณํํ ํ์ JSONArray๋ก ๋ณํ
- JSONArray์ ๊ธธ์ด๊ฐ 0๋ณด๋ค ํฐ์ง ๊ฒ์ฆ, ์๋ต ๋ฐ์ดํฐ๊ฐ ๋น์ด ์์ง ์์์ ํ์ธ
@Test
@DisplayName("Link ์กฐํ")
fun testGetLinks() {
val uri = "/api/v1/links"
val mvcResult = performGet(uri)
val contentAsString = mvcResult.response.getContentAsString(StandardCharsets.UTF_8)
val jsonArray = JSONArray(contentAsString)
assertThat(jsonArray.length()).isPositive()
}
- api/vi/links์ uri ๋ณ์์ ๋ฃ์
- HTTP GET ์์ฒญ์ ๋ณด๋ธํ, ์๋ต์ JSONArray๋ก ๋ณํ
- ๋ฐฐ์ด์ ํฌ๊ธฐ์, ๊ทธ ๋ฐ์ดํฐ๊ฐ ์กด์ฌํ๋์ง ๊ฒ์ฆ
@Test
@DisplayName("Resume ์กฐํ")
fun testGetResume() {
val uri = "/api/v1/resume"
val mvcResult = performGet(uri)
val contentAsString = mvcResult.response.getContentAsString(StandardCharsets.UTF_8)
val jsonObject = JSONObject(contentAsString)
assertThat(jsonObject.optJSONArray("experiences").length()).isPositive()
assertThat(jsonObject.optJSONArray("achievements").length()).isPositive()
assertThat(jsonObject.optJSONArray("skills").length()).isPositive()
}
- /api/vi/resume๋ฅผ uri๋ณ์์ ๋ฃ์
- GET์์ฒญ์ ๋ณด๋ธํ, ์๋ต์ JSONObject๋ก ๋ณํ
- experiences, achievements,skills๋ฅผ JSONArray๋ก ๋ณํ ํ ๊ทธ ๊ฐ์ด ์์์ธ์ง์ ๋ฐ์ดํฐ๊ฐ ์กด์ฌํ๋์ง ๊ฒ์ฆ
@Test
@DisplayName("Projects ์กฐํ")
fun testProjects() {
val uri = "/api/v1/projects"
val mvcResult = performGet(uri)
val contentAsString = mvcResult.response.getContentAsString(StandardCharsets.UTF_8)
val jsonArray = JSONArray(contentAsString)
assertThat(jsonArray.length()).isPositive()
}
- /api/vi/projects๋ฅผ uri ๋ณ์์ ๋ฃ์
- GET์์ฒญ์ ๋ณด๋ธํ ์๋ต ๋ณธ๋ฌธ์ JSONArray๋ก ๋ณํํ๊ณ , ๋ฐฐ์ด์ ๊ธธ์ด๊ฐ ์์์ธ์ง์ ๋ฐ์ดํฐ๊ฐ ์กด์ฌํ๋์ง ๊ฒ์ฆ
private fun performGet(uri: String): MvcResult {
return mockMvc
.perform(MockMvcRequestBuilders.get(uri))
.andDo(MockMvcResultHandlers.print())
.andReturn()
}
- perform(MockMvcRequestBuilders.get(uri)) : ํน์ uri์ GET ์์ฒญ์ ๋ณด๋ด๋ ์ฝ๋. ์๋ฒ๋ฅผ ๋์ฐ์ง ์๊ณ API ์๋ํฌ์ธํธ์ ๋์์ ํ์ธํ ์ ์์
- andDo(MockMvcResultHandlers.print()) : ์์ฒญ๊ณผ ์๋ต์ ์ฝ์์ ์ถ๋ ฅ
- andReturn() : MvcResult๋ฅผ ๋ฐํ
2. ๋ถ๋ถ ์ฝ๋ ๋ถ์
- Thymeleaf ํ ํ๋ฆฟ ์์ง์์ ๋ ์ด์์์ ์ ์ํ๊ณ ํด๋น ๋ ์ด์์์ ์ฌ์ฌ์ฉํ๋ ๋ฐฉ์, ํ ํ๋ฆฟ ์ฝ๋์ ์ค๋ณต์ ์ค์ด๊ณ , HTML ํ์ผ ๊ฐ์ ๊ณตํต ์์๋ฅผ ์ฌ์ฌ์ฉํ ์ ์๊ฒ ํ๋ ๊ฒ.
th:replace="~{presentation/layouts/layout-main :: layout(~{::#content})}"
- th:replace : ๋ค๋ฅธ ํ์ผ์ ๊ฐ์ ธ์ ํ์ฌ ์์น์ ์ฝ์ ํ๋ ๊ธฐ๋ฅ ์ํ
- ~{presentation/layouts/layout-main :: layout(~{::#content})} :
- ~{presentation/layouts/layout-main} : layout-main.html ํ์ผ ์ฐธ์กฐ
- :: layout : layout ์ด๋ผ๋ ์ด๋ฆ์ fragment๋ฅผ ์ฌ์ฉํ๊ฒ ๋ค๋ ์๋ฏธ, ์ ์ฝ๋์์๋ layout fragment๋ฅผ ๊ฐ์ ธ์ค๊ฒ ๋ค๋ ๋ป
- (~{::#content}) : id="content" ๋ก ์ง์ ๋ ๋ถ๋ถ์ layout fragment์ ํน์ ์์น์ ๋์ฒดํ ๊ฒ์ด๋ผ๋ ์๋ฏธ
3. interceptor ์ฝ๋ ๋ถ์
@Component
class PresentationInterceptor(
private val httpInterfaceRepository: HttpInterfaceRepository
) : HandlerInterceptor {
override fun afterCompletion(request: HttpServletRequest, response: HttpServletResponse, handler: Any, ex: Exception?) {
val httpInterface = HttpInterface(request)
httpInterfaceRepository.save(httpInterface)
}
}
- private val httpInterfaceRepository: HttpInterfaceRepository
- ์์กด์ฑ ์ฃผ์ ์ ํตํด์ Repository๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
- override fun afterCompletion(request: HttpServletRequest, response: HttpServletResponse, handler: Any, ex: Exception?) {
- afterCompletion : HTTP ์์ฒญ ์ฒ๋ฆฌ ํ์ ํธ์ถ๋๋ ๋ฉ์๋
- val httpInterface = HttpInterface(request)
- request ์ ๋ณด๋ฅผ ๋ด๊ณ ์๋ httpInterface ๊ฐ์ฒด๋ฅผ ์์ฑ ํ, httpInterfaceRepository์ ์ ์ฅ.
- httpInterfaceRepository.save(httpInterface)
@Configuration
class PresentationInterceptorConfiguration(
private val presentationInterceptor: PresentationInterceptor
) : WebMvcConfigurer {
override fun addInterceptors(registry: InterceptorRegistry) {
registry.addInterceptor(presentationInterceptor)
.addPathPatterns("/**")
.excludePathPatterns("/assets/**", "/css/**", "/js/**", "/admin/**", "h2**",
"/favicon.ico", "/error")
}
}
- addInterceptors
- ์ธํฐ์ ํฐ๋ฅผ ๋ฑ๋กํ๋ ๋ฉ์๋
- registry.addInterceptor(presentationInterceptor)
- addInterceptor ๋ฉ์๋๋ฅผ ํตํด presentationInterceptor๋ฅผ ๋ฑ๋ก
- addPathPatterns("/**")
- ๋ชจ๋ ์์ฒญ ๊ฒฝ๋ก์ ๋ํด ์ธํฐ์ ํฐ ์ ์ฉ
- excludePathPatterns(...) : ์ธํฐ์
ํฐ๊ฐ ๋์ํ์ง ์๋๋ก ์ค์
- /assets/**: ์ ์ ์์(์: ์ด๋ฏธ์ง, ํฐํธ ๋ฑ).
- /css/**: CSS ํ์ผ.
- /js/**: JavaScript ํ์ผ.
- /admin/**: ๊ด๋ฆฌ ํ์ด์ง.
- h2**: H2 ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฝ์.
- /favicon.ico: ์ฌ์ดํธ ์์ด์ฝ.
- /error: ์ค๋ฅ ์ฒ๋ฆฌ ๊ฒฝ๋ก.
[๋ฏธ์ 4] ์กฐํ REST API ๋ง๋ค๊ธฐ-ํ๊ณ
์ด๋ฒ ๋ฏธ์ ์ ํ๋ฉด์ @GetMapping ์ด๋ ธํ ์ด์ ์ ๋ํด์๋ ์ด๋์ ๋ ์ดํด๋ฅผ ํ ์ ์์๋ค. ์ ๋ฒ ๋ฐ์๊ตญ์์ @Id, @GeneratedValue ์ด๋ ธํ ์ด์ ์ ๋ํด์ ์ ์์ง๋ง ์์ง ๋ถ์กฑํ๋ค๋ ๊ฑธ ์ ์ ์์๊ณ , @ManyToOne ์ด๋ ธํ ์ด์ ์ ๋ํด ๋ ๊น์ด ๊ณต๋ถํด์ผ๊ฒ ๋ค๋๊ฑธ ๊นจ๋ฌ์๋ค. ์์ง Spring์ ๋ณธ์ง์ ์ธ๊ฑธ ์ดํด๋ฅผ ๋ชปํ๊ฑธ์๋ ์๋๊ฑฐ ๊ฐ๋ค. ์ฝ๋๋ฅผ ์์ฑํ๋ฉด ํ ์๋ก ๋ ๊น๊ฒ ๊ณต๋ถ๋ฅผ ํด์ผํ๊ณ , JAVA ๋ฌธ๋ฒ๋ ๋ค์ ํด์ผ๊ฒ ๋ค๋๊ฑธ ๋ผ์ ๋ฆฌ๊ฒ ๋๋ผ๋ฉด์ ํ๊ฒ ๊ฐ๋ค...
3์ฃผ์ฐจ ํ๊ณ
Spring์ ํ๋ฉด ํ ์๋ก ์ฌ๋ฐ๋๊ฑด ๋ง๋ค. Test ์ฝ๋๋ฅผ ์์ฑํ ๋๋ ์ด๋์ ์ด์ฝ๋๊ฐ ์๋์ด ๋๋๊ฒ๋ ์ ์ ์๊ณ , ๋ถ๋ถ์ ์ธ ๊ฑธ ๋ฐ๋ก ๋ชจ๋ํ? ํด์ ๋ง๋๋๊ฒ๋ ์ฌ๋ฐ๋ค. ์ด๋ฒ ์๋ฐ์ ํด๋ฝ๋ ๊ณง ์ข ๋ฃ๊ฐ ๋๋๋ฐ java์ ์ค์์ฑ๋ ๊นจ๋ฌ์์ java๊ณต๋ถ๋ ์ด์ฌํ ํ๊ณ , Spring์ ๋ํด์ ๋ ๊น๊ฒ ๊ณต๋ถํ ๊ฑฐ๋ค!
์ฐธ๊ณ ๋ก ์ค๊ฐ์ ๊ฒ ๋ ์ ๋ณด๊ทผ๋๊ป์ ๋ด๊ฐ ์์ฑํ ์ง๋ฌธ์ ๋ํด ๋ต๋ณ์ ํด์ฃผ์ ๊ฑฐ ๊ฐ๋ค. ๋์ ์ง๋ฌธ์ ์ด๋ฒ ์๋ฐ์ ํด๋ฝ์ด ์ข ๋ฃ๊ฐ ๋๋ฉด java์ ์ ์, spring๊ณต๋ถ๋ฅผ ํ ๊ฑด๋ฐ ์ถ์ฒํด์ค ๊ฐ์๊ฐ ์๋ ๋ผ๋ ์ง๋ฌธ์ด์๋ค. ๋ต๋ณ์ ์ญ์ ๊น์ํ๋์ ๊ฐ์์๊ณ ๋ค๋ฅธ ๊ฐ์๋ ์ถ์ฒํด์ฃผ์ จ๋๋ฐ ์ด๊ฑฐ๋ ํ์ธํด๋ณด๊ณ ๊ธ์ ์์ ํด์ผ๊ฒ ๋ค...! ๋ฌดํผ, spring์ ์ ํํ๊ฑด ์ํ ์ผ ๊ฐ๋ค...!
'๐ ๋ฐฑ์๋ ๊ฐ๋ฐ > SPRING' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
@PutMapping, @PatchMapping, @GetMapping, @PostMapping ์ ๊ดํ์ฌ... (0) | 2025.02.15 |
---|---|
DTO๋ฅผ Request, Response๋ก ๋๋๋ ์ด์ (0) | 2025.02.15 |
[์ธํ๋ฐ ์๋ฐ์ ํด๋ฝ 2๊ธฐ - BE] 2์ฃผ์ฐจ ๋ฐ์๊ตญ (0) | 2024.10.13 |
[์ธํ๋ฐ ์๋ฐ์ ํด๋ฝ 2๊ธฐ - BE] 1์ฃผ์ฐจ ๋ฐ์๊ตญ (10) | 2024.10.06 |
Spring ์๋ ์ฌ์์ (0) | 2024.07.25 |