From 2faecb2bcee6f596ea134f8a3b30e6783486814c Mon Sep 17 00:00:00 2001 From: kkw01234 Date: Sun, 23 Aug 2020 10:30:17 +0900 Subject: [PATCH 1/5] =?UTF-8?q?board=20crud=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .DS_Store | Bin 0 -> 6148 bytes build.gradle.kts | 1 + .../example/kotlinweb/KotlinwebApplication.kt | 2 + .../example/kotlinweb/aspect/BoardAspect.kt | 27 ++++++++++++ .../example/kotlinweb/config/BoardConfig.kt | 16 +++++++ .../kotlinweb/controller/BoardController.kt | 39 ++++++++++++++++++ .../kotlinweb/controller/HealthController.kt | 2 +- .../kotlinweb/exception/ControllerAdvice.kt | 31 ++++++++++++++ .../com/example/kotlinweb/model/Board.kt | 7 ++++ .../com/example/kotlinweb/model/Response.kt | 9 ++++ .../example/kotlinweb/service/BoardService.kt | 33 +++++++++++++++ 11 files changed, 166 insertions(+), 1 deletion(-) create mode 100644 .DS_Store create mode 100644 src/main/kotlin/com/example/kotlinweb/aspect/BoardAspect.kt create mode 100644 src/main/kotlin/com/example/kotlinweb/config/BoardConfig.kt create mode 100644 src/main/kotlin/com/example/kotlinweb/controller/BoardController.kt create mode 100644 src/main/kotlin/com/example/kotlinweb/exception/ControllerAdvice.kt create mode 100644 src/main/kotlin/com/example/kotlinweb/model/Board.kt create mode 100644 src/main/kotlin/com/example/kotlinweb/model/Response.kt create mode 100644 src/main/kotlin/com/example/kotlinweb/service/BoardService.kt diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..f6f678ceccc558b99bbef9b318fbfdd419634a05 GIT binary patch literal 6148 zcmeHKO>fgc5PjQ(rfoPx>Y+XQ$cd=uUQCn-DiwrU!5N%H2oi7<+l_kjh5yjM(BIb| zK;O)+z;=x2302jMH2e1B&F=VZ?9BpjqhfXf3;^uW1-l0{pw`?c$JSRGxV|KPo zr^zx+wz$?|3YY?qPXT#%pCLhkuUMk`{_>OHjyU?_mzt(N_`}3Ye8C!@F=O1Etp##q zmG@shDYx{SO8jm$rq113t5@Y>(_sH>WY({v1#@I1$btQ?e#rc zX$qJEroe9n=CFNBwlL&?^A3%WgH+<=~f?C}140^vDsK^Pxl^>cSPn`Ed3} zffolXJ^FCCaQSdyWfyKJPFLsrk<#H}k5-xjra)bRj&Ba+{Ga^&{$CHWmMLHgJShcS zcYHY>@sYyWy7h2!)<*PCbTQ$T9yNqTIF7X +){ + + @Before(value = "execution(* com.example.kotlinweb.service.BoardService.*(..)) && args(id))") + fun interceptBoard(joinPoint: JoinPoint, id:Int){ + if(id <= 0) throw RuntimeException("1이상의 숫자를 입력해주세요") + if(id > boardList.size ) throw RuntimeException("게시글 개수보다 많습니다.") + } + + + + +} diff --git a/src/main/kotlin/com/example/kotlinweb/config/BoardConfig.kt b/src/main/kotlin/com/example/kotlinweb/config/BoardConfig.kt new file mode 100644 index 0000000..eb03d94 --- /dev/null +++ b/src/main/kotlin/com/example/kotlinweb/config/BoardConfig.kt @@ -0,0 +1,16 @@ +package com.example.kotlinweb.config + +import com.example.kotlinweb.model.Board +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration + +@Configuration +class BoardConfig{ + + + @Bean + fun boardList():MutableList{ + return mutableListOf(); + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/example/kotlinweb/controller/BoardController.kt b/src/main/kotlin/com/example/kotlinweb/controller/BoardController.kt new file mode 100644 index 0000000..be99148 --- /dev/null +++ b/src/main/kotlin/com/example/kotlinweb/controller/BoardController.kt @@ -0,0 +1,39 @@ +package com.example.kotlinweb.controller + +import com.example.kotlinweb.model.Board +import com.example.kotlinweb.model.Response +import com.example.kotlinweb.service.BoardService +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.http.HttpStatus +import org.springframework.web.bind.annotation.* + +@RestController +class BoardController( + private val boardService: BoardService +){ + + @PostMapping("/board") + fun postBoard(@RequestBody board:Board):Response{ + return Response(HttpStatus.CREATED, "create",boardService.createBoard(board)); + } + + @GetMapping("/board") + fun findAllBoard():Response>{ + return Response(HttpStatus.OK, "findAll",boardService.findAllBoard()) + } + + @GetMapping("/board/{id}") + fun findBoard(@PathVariable id:Int):Response{ + return Response(HttpStatus.OK, "find",boardService.findBoard(id)) + } + + @DeleteMapping("/board/{id}") + fun deleteBoard(@PathVariable id:Int):Response{ + return Response(HttpStatus.OK,"delete",boardService.deleteBoard(id)) + } + + @PutMapping("/board/{id}") + fun update(@PathVariable id:Int, @RequestBody board:Board):Response{ + return Response(HttpStatus.OK, "update", boardService.updateBoard(id, board)) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/example/kotlinweb/controller/HealthController.kt b/src/main/kotlin/com/example/kotlinweb/controller/HealthController.kt index fed3266..d0bdd04 100644 --- a/src/main/kotlin/com/example/kotlinweb/controller/HealthController.kt +++ b/src/main/kotlin/com/example/kotlinweb/controller/HealthController.kt @@ -13,5 +13,5 @@ class HealthController{ @get:GetMapping(value = ["/health_check.html"]) val ratingStatus: ResponseEntity get() = ResponseEntity("Health", HttpStatus.OK) - + } diff --git a/src/main/kotlin/com/example/kotlinweb/exception/ControllerAdvice.kt b/src/main/kotlin/com/example/kotlinweb/exception/ControllerAdvice.kt new file mode 100644 index 0000000..f98b4ce --- /dev/null +++ b/src/main/kotlin/com/example/kotlinweb/exception/ControllerAdvice.kt @@ -0,0 +1,31 @@ +package com.example.kotlinweb.exception + +import com.example.kotlinweb.model.Response +import org.springframework.http.HttpStatus +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.ControllerAdvice +import org.springframework.web.bind.annotation.ExceptionHandler +import org.springframework.web.client.HttpClientErrorException + +@ControllerAdvice +class ControllerAdvice(){ + + @ExceptionHandler(Exception::class) + private fun handleUnexpectedHandler(exception: Exception): ResponseEntity> { + return makeResponseException(exception) + } + + private fun makeResponseException(exception: Exception): ResponseEntity>{ + return when(exception){ + is HttpClientErrorException.BadRequest ->{ + makeResponse(exception.message, HttpStatus.BAD_REQUEST) + } + else -> { + makeResponse(exception.message,HttpStatus.INTERNAL_SERVER_ERROR) + } + } + } + private fun makeResponse(message:String?, status:HttpStatus, data:Any? = null):ResponseEntity>{ + return ResponseEntity(Response(code = status,message = message?.let { message } ?: "", data = data), status) + } +} diff --git a/src/main/kotlin/com/example/kotlinweb/model/Board.kt b/src/main/kotlin/com/example/kotlinweb/model/Board.kt new file mode 100644 index 0000000..0758afb --- /dev/null +++ b/src/main/kotlin/com/example/kotlinweb/model/Board.kt @@ -0,0 +1,7 @@ +package com.example.kotlinweb.model + +class Board( + val title:String, + val name:String, + val content:String +) \ No newline at end of file diff --git a/src/main/kotlin/com/example/kotlinweb/model/Response.kt b/src/main/kotlin/com/example/kotlinweb/model/Response.kt new file mode 100644 index 0000000..9d98431 --- /dev/null +++ b/src/main/kotlin/com/example/kotlinweb/model/Response.kt @@ -0,0 +1,9 @@ +package com.example.kotlinweb.model + +import org.springframework.http.HttpStatus + +class Response( + val code:HttpStatus, + val message:String, + val data: T +) \ No newline at end of file diff --git a/src/main/kotlin/com/example/kotlinweb/service/BoardService.kt b/src/main/kotlin/com/example/kotlinweb/service/BoardService.kt new file mode 100644 index 0000000..37e6a6c --- /dev/null +++ b/src/main/kotlin/com/example/kotlinweb/service/BoardService.kt @@ -0,0 +1,33 @@ +package com.example.kotlinweb.service + +import com.example.kotlinweb.model.Board +import org.springframework.stereotype.Service + +@Service +class BoardService( + private val boardList:MutableList +){ + + fun createBoard(board:Board):Board{ + boardList.add(board) + return board + } + + fun findBoard(id:Int):Board{ + return boardList[id-1] + } + fun findAllBoard():List{ + return boardList + } + + fun deleteBoard(id:Int):Boolean{ + val board = boardList[id-1] + return boardList.remove(board) + } + + fun updateBoard(id:Int, board: Board):Board{ + boardList[id-1] = board; + return board; + } + +} \ No newline at end of file From a8e61f7d0e7163081092f293820f4aa82b3cc889 Mon Sep 17 00:00:00 2001 From: kkw01234 Date: Sun, 23 Aug 2020 10:55:11 +0900 Subject: [PATCH 2/5] =?UTF-8?q?WebConfig=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/example/kotlinweb/config/WebConfig.kt | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 src/main/kotlin/com/example/kotlinweb/config/WebConfig.kt diff --git a/src/main/kotlin/com/example/kotlinweb/config/WebConfig.kt b/src/main/kotlin/com/example/kotlinweb/config/WebConfig.kt new file mode 100644 index 0000000..12fe73f --- /dev/null +++ b/src/main/kotlin/com/example/kotlinweb/config/WebConfig.kt @@ -0,0 +1,27 @@ +package com.example.kotlinweb.config + +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.web.filter.CharacterEncodingFilter +import org.springframework.web.servlet.config.annotation.CorsRegistry +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer + +@Configuration +class WebConfig: WebMvcConfigurer { + + override fun addCorsMappings(registry: CorsRegistry) { + registry.addMapping("/**") + .allowedMethods("GET", "PUT", "DELETE", "POST", "OPTIONS") + .allowedOrigins("*") + .allowedHeaders("*") + .allowCredentials(true) + } + + @Bean + fun characterEncodingFilter(): CharacterEncodingFilter { + val characterEncodingFilter: CharacterEncodingFilter = CharacterEncodingFilter() + characterEncodingFilter.encoding = "UTF-8" + characterEncodingFilter.setForceEncoding(true) + return characterEncodingFilter + } +} \ No newline at end of file From 537c1f7c5ceebbfa3e862b3893c49d7e01c2bea3 Mon Sep 17 00:00:00 2001 From: kkw01234 Date: Sun, 23 Aug 2020 10:55:34 +0900 Subject: [PATCH 3/5] =?UTF-8?q?Controller=20Test=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/BoardControllerTest.kt | 103 ++++++++++++++++++ .../kotlinweb/service/BoardServiceTest.kt | 10 ++ 2 files changed, 113 insertions(+) create mode 100644 src/test/kotlin/com/example/kotlinweb/controller/BoardControllerTest.kt create mode 100644 src/test/kotlin/com/example/kotlinweb/service/BoardServiceTest.kt diff --git a/src/test/kotlin/com/example/kotlinweb/controller/BoardControllerTest.kt b/src/test/kotlin/com/example/kotlinweb/controller/BoardControllerTest.kt new file mode 100644 index 0000000..3229100 --- /dev/null +++ b/src/test/kotlin/com/example/kotlinweb/controller/BoardControllerTest.kt @@ -0,0 +1,103 @@ +package com.example.kotlinweb.controller + +import com.example.kotlinweb.model.Board +import com.example.kotlinweb.model.Response +import com.fasterxml.jackson.core.type.TypeReference +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import org.junit.jupiter.api.* +import org.junit.jupiter.api.Assertions.assertEquals +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.http.MediaType +import org.springframework.test.context.ActiveProfiles +import org.springframework.test.web.servlet.MockMvc +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders +import org.springframework.test.web.servlet.result.MockMvcResultMatchers + + +@SpringBootTest +@AutoConfigureMockMvc +@ActiveProfiles(value = ["test"]) +@TestMethodOrder(MethodOrderer.OrderAnnotation::class) +class BoardControllerTest { + + + @Autowired + private lateinit var mockMvc:MockMvc + + + + companion object{ + private val objectMapper:ObjectMapper = jacksonObjectMapper() + } + + + + @Test + @Order(1) + fun createBoard(){ + val board = Board("abcd","abcd","안녕하세요") + val result = mockMvc.perform(MockMvcRequestBuilders.post("/board") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(board))) + .andExpect(MockMvcResultMatchers.status().isOk) + .andReturn() + val resultPayload = objectMapper.readValue(result.response.contentAsString, object : TypeReference>(){}) + assertEquals(resultPayload.data.name, board.name) + assertEquals(resultPayload.data.title, board.title) + assertEquals(resultPayload.data.content, board.content) + } + @Test + @Order(2) + fun findBoard(){ + val board = Board("abcd","abcd","안녕하세요") + val result = mockMvc.perform(MockMvcRequestBuilders.get("/board/1") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(MockMvcResultMatchers.status().isOk) + .andReturn() + val resultPayload = objectMapper.readValue(result.response.contentAsString, object : TypeReference>(){}) + assertEquals(resultPayload.data.name, board.name) + assertEquals(resultPayload.data.title, board.title) + assertEquals(resultPayload.data.content, board.content) + } + + @Test + @Order(3) + fun findAllBoard(){ + val result = mockMvc.perform(MockMvcRequestBuilders.get("/board") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(MockMvcResultMatchers.status().isOk) + .andReturn() + val resultPayload = objectMapper.readValue(result.response.contentAsString, object : TypeReference>>(){}) + assertEquals(resultPayload.data.size, 1) + } + + @Test + @Order(4) + fun updateBoard(){ + val board = Board("abcd","abcd","안녕하세요2") + val result = mockMvc.perform(MockMvcRequestBuilders.put("/board/1") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(board))) + .andExpect(MockMvcResultMatchers.status().isOk) + .andReturn() + val resultPayload = objectMapper.readValue(result.response.contentAsString, object : TypeReference>(){}) + assertEquals(resultPayload.data.name, board.name) + assertEquals(resultPayload.data.title, board.title) + assertEquals(resultPayload.data.content, board.content) + } + + @Test + @Order(5) + fun deleteBoard(){ + val result = mockMvc.perform(MockMvcRequestBuilders.delete("/board/1") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(MockMvcResultMatchers.status().isOk) + .andReturn() + val resultPayload = objectMapper.readValue(result.response.contentAsString, object : TypeReference>(){}) + assertEquals(resultPayload.data, true) + } + +} \ No newline at end of file diff --git a/src/test/kotlin/com/example/kotlinweb/service/BoardServiceTest.kt b/src/test/kotlin/com/example/kotlinweb/service/BoardServiceTest.kt new file mode 100644 index 0000000..54bf5b0 --- /dev/null +++ b/src/test/kotlin/com/example/kotlinweb/service/BoardServiceTest.kt @@ -0,0 +1,10 @@ +package com.example.kotlinweb.service + +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.test.context.ActiveProfiles + +@SpringBootTest +@ActiveProfiles(value = ["test"]) +class BoardServiceTest{ + // TODO: 만들어야함 +} \ No newline at end of file From 53328c2d1d4332cc25d72403c45a2f5e09c8b6fb Mon Sep 17 00:00:00 2001 From: kkw01234 Date: Sun, 23 Aug 2020 11:20:46 +0900 Subject: [PATCH 4/5] =?UTF-8?q?webhook=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4f1386b..fc25173 100644 --- a/README.md +++ b/README.md @@ -3,4 +3,4 @@ ### 필요한건 Notion에 저장해놓을게요 양해부탁드려요.. https://www.notion.so/kkw01234/dbaf7c5999e04a39b8241682854ddfae -### 권한 요청 필요합니다 +### 권한 요청 필요합니다 \ No newline at end of file From e2b6d4dbaaf2fddccca1b6010fc1a6d66ce91209 Mon Sep 17 00:00:00 2001 From: kakao Date: Mon, 21 Sep 2020 01:06:06 +0900 Subject: [PATCH 5/5] =?UTF-8?q?db=20=EC=84=A4=EC=A0=95=20=ED=9B=84=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .DS_Store | Bin 6148 -> 6148 bytes build.gradle.kts | 4 ++ .../example/kotlinweb/KotlinwebApplication.kt | 2 +- .../com/example/kotlinweb/config/WebConfig.kt | 10 ++++- .../kotlinweb/controller/BoardController.kt | 24 +++++------ .../kotlinweb/controller/UserController.kt | 7 +++ .../kotlinweb/exception/ControllerAdvice.kt | 5 ++- .../kotlinweb/interceptor/TokenInterceptor.kt | 34 +++++++++++++++ .../com/example/kotlinweb/model/Board.kt | 14 ++++-- .../com/example/kotlinweb/model/User.kt | 15 +++++++ .../example/kotlinweb/model/UserContext.kt | 20 +++++++++ .../kotlinweb/model/request/BoardRequest.kt | 6 +++ .../model/{ => response}/Response.kt | 2 +- .../kotlinweb/repository/BoardRepository.kt | 6 +++ .../kotlinweb/repository/UserRepository.kt | 8 ++++ .../example/kotlinweb/service/BoardService.kt | 36 ++++++++++------ .../example/kotlinweb/service/UserService.kt | 40 ++++++++++++++++++ src/main/resources/application.properties | 9 +++- .../controller/BoardControllerTest.kt | 38 +++++++++++------ src/test/resources/application.properties | 12 ++++++ 20 files changed, 245 insertions(+), 47 deletions(-) create mode 100644 src/main/kotlin/com/example/kotlinweb/controller/UserController.kt create mode 100644 src/main/kotlin/com/example/kotlinweb/interceptor/TokenInterceptor.kt create mode 100644 src/main/kotlin/com/example/kotlinweb/model/User.kt create mode 100644 src/main/kotlin/com/example/kotlinweb/model/UserContext.kt create mode 100644 src/main/kotlin/com/example/kotlinweb/model/request/BoardRequest.kt rename src/main/kotlin/com/example/kotlinweb/model/{ => response}/Response.kt (75%) create mode 100644 src/main/kotlin/com/example/kotlinweb/repository/BoardRepository.kt create mode 100644 src/main/kotlin/com/example/kotlinweb/repository/UserRepository.kt create mode 100644 src/main/kotlin/com/example/kotlinweb/service/UserService.kt create mode 100644 src/test/resources/application.properties diff --git a/.DS_Store b/.DS_Store index f6f678ceccc558b99bbef9b318fbfdd419634a05..5c9085750f8543d240388f24468646c8d72e0d75 100644 GIT binary patch delta 40 wcmZoMXfc@J&&ahgU^g=(*JK`+zmqdrg*SV#<}+@-&!)jRu_0tLJI7ys0171yHvj+t delta 97 zcmZoMXfc@J&&abeU^g=(&tx8!zsl?k9t^GwJ`4d2dJMS?DV{m`$w@i+Nem1O0zhmH m#4{!vu*z+gVa;b0XJ#m8NM%SuRmAvta}Aq0<7Re_zx)7ZzZU}l diff --git a/build.gradle.kts b/build.gradle.kts index d102698..ae78695 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -5,6 +5,7 @@ plugins { id("io.spring.dependency-management") version "1.0.9.RELEASE" kotlin("jvm") version "1.3.72" kotlin("plugin.spring") version "1.3.72" + kotlin("plugin.jpa") version "1.3.72" } group = "com.example" @@ -18,9 +19,12 @@ repositories { dependencies { implementation("org.springframework.boot:spring-boot-starter-web") implementation ("org.springframework.boot:spring-boot-starter-aop") + implementation("org.springframework.boot:spring-boot-starter-data-jpa") implementation("com.fasterxml.jackson.module:jackson-module-kotlin") implementation("org.jetbrains.kotlin:kotlin-reflect") implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") + runtimeOnly("mysql:mysql-connector-java") + testImplementation("com.h2database:h2") testImplementation("org.springframework.boot:spring-boot-starter-test") { exclude(group = "org.junit.vintage", module = "junit-vintage-engine") } diff --git a/src/main/kotlin/com/example/kotlinweb/KotlinwebApplication.kt b/src/main/kotlin/com/example/kotlinweb/KotlinwebApplication.kt index 92e3006..7785c0d 100644 --- a/src/main/kotlin/com/example/kotlinweb/KotlinwebApplication.kt +++ b/src/main/kotlin/com/example/kotlinweb/KotlinwebApplication.kt @@ -10,4 +10,4 @@ class KotlinwebApplication fun main(args: Array) { runApplication(*args) -} +} \ No newline at end of file diff --git a/src/main/kotlin/com/example/kotlinweb/config/WebConfig.kt b/src/main/kotlin/com/example/kotlinweb/config/WebConfig.kt index 12fe73f..81202f8 100644 --- a/src/main/kotlin/com/example/kotlinweb/config/WebConfig.kt +++ b/src/main/kotlin/com/example/kotlinweb/config/WebConfig.kt @@ -1,13 +1,17 @@ package com.example.kotlinweb.config +import com.example.kotlinweb.interceptor.TokenInterceptor import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.web.filter.CharacterEncodingFilter import org.springframework.web.servlet.config.annotation.CorsRegistry +import org.springframework.web.servlet.config.annotation.InterceptorRegistry import org.springframework.web.servlet.config.annotation.WebMvcConfigurer @Configuration -class WebConfig: WebMvcConfigurer { +class WebConfig( + private val tokenInterceptor: TokenInterceptor +): WebMvcConfigurer { override fun addCorsMappings(registry: CorsRegistry) { registry.addMapping("/**") @@ -24,4 +28,8 @@ class WebConfig: WebMvcConfigurer { characterEncodingFilter.setForceEncoding(true) return characterEncodingFilter } + + override fun addInterceptors(registry: InterceptorRegistry) { + registry.addInterceptor(tokenInterceptor).addPathPatterns("/**") + } } \ No newline at end of file diff --git a/src/main/kotlin/com/example/kotlinweb/controller/BoardController.kt b/src/main/kotlin/com/example/kotlinweb/controller/BoardController.kt index be99148..b90da00 100644 --- a/src/main/kotlin/com/example/kotlinweb/controller/BoardController.kt +++ b/src/main/kotlin/com/example/kotlinweb/controller/BoardController.kt @@ -1,9 +1,9 @@ package com.example.kotlinweb.controller import com.example.kotlinweb.model.Board -import com.example.kotlinweb.model.Response +import com.example.kotlinweb.model.request.BoardRequest +import com.example.kotlinweb.model.response.Response import com.example.kotlinweb.service.BoardService -import org.springframework.beans.factory.annotation.Autowired import org.springframework.http.HttpStatus import org.springframework.web.bind.annotation.* @@ -13,27 +13,27 @@ class BoardController( ){ @PostMapping("/board") - fun postBoard(@RequestBody board:Board):Response{ - return Response(HttpStatus.CREATED, "create",boardService.createBoard(board)); + fun postBoard(@RequestBody boardRequest:BoardRequest): Response { + return Response(HttpStatus.CREATED, "create", boardService.createBoard(boardRequest)); } @GetMapping("/board") - fun findAllBoard():Response>{ - return Response(HttpStatus.OK, "findAll",boardService.findAllBoard()) + fun findAllBoard(): Response> { + return Response(HttpStatus.OK, "findAll", boardService.findAllBoard()) } @GetMapping("/board/{id}") - fun findBoard(@PathVariable id:Int):Response{ - return Response(HttpStatus.OK, "find",boardService.findBoard(id)) + fun findBoard(@PathVariable id:Long): Response { + return Response(HttpStatus.OK, "find", boardService.findBoard(id)) } @DeleteMapping("/board/{id}") - fun deleteBoard(@PathVariable id:Int):Response{ - return Response(HttpStatus.OK,"delete",boardService.deleteBoard(id)) + fun deleteBoard(@PathVariable id:Long): Response { + return Response(HttpStatus.OK, "delete", boardService.deleteBoard(id)) } @PutMapping("/board/{id}") - fun update(@PathVariable id:Int, @RequestBody board:Board):Response{ - return Response(HttpStatus.OK, "update", boardService.updateBoard(id, board)) + fun update(@PathVariable id:Long, @RequestBody boardRequest:BoardRequest): Response { + return Response(HttpStatus.OK, "update", boardService.updateBoard(id, boardRequest)) } } \ No newline at end of file diff --git a/src/main/kotlin/com/example/kotlinweb/controller/UserController.kt b/src/main/kotlin/com/example/kotlinweb/controller/UserController.kt new file mode 100644 index 0000000..64cc0a9 --- /dev/null +++ b/src/main/kotlin/com/example/kotlinweb/controller/UserController.kt @@ -0,0 +1,7 @@ +package com.example.kotlinweb.controller + +class UserController( + +){ + +} \ No newline at end of file diff --git a/src/main/kotlin/com/example/kotlinweb/exception/ControllerAdvice.kt b/src/main/kotlin/com/example/kotlinweb/exception/ControllerAdvice.kt index f98b4ce..2e697bd 100644 --- a/src/main/kotlin/com/example/kotlinweb/exception/ControllerAdvice.kt +++ b/src/main/kotlin/com/example/kotlinweb/exception/ControllerAdvice.kt @@ -1,6 +1,6 @@ package com.example.kotlinweb.exception -import com.example.kotlinweb.model.Response +import com.example.kotlinweb.model.response.Response import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.ControllerAdvice @@ -26,6 +26,7 @@ class ControllerAdvice(){ } } private fun makeResponse(message:String?, status:HttpStatus, data:Any? = null):ResponseEntity>{ - return ResponseEntity(Response(code = status,message = message?.let { message } ?: "", data = data), status) + return ResponseEntity(Response(code = status, message = message?.let { message } + ?: "", data = data), status) } } diff --git a/src/main/kotlin/com/example/kotlinweb/interceptor/TokenInterceptor.kt b/src/main/kotlin/com/example/kotlinweb/interceptor/TokenInterceptor.kt new file mode 100644 index 0000000..552b412 --- /dev/null +++ b/src/main/kotlin/com/example/kotlinweb/interceptor/TokenInterceptor.kt @@ -0,0 +1,34 @@ +package com.example.kotlinweb.interceptor + +import com.example.kotlinweb.model.UserContext +import com.example.kotlinweb.service.UserService +import org.springframework.stereotype.Component +import org.springframework.web.servlet.ModelAndView +import org.springframework.web.servlet.handler.HandlerInterceptorAdapter +import java.lang.RuntimeException +import javax.servlet.http.HttpServletRequest +import javax.servlet.http.HttpServletResponse + +@Component +class TokenInterceptor( + private val userService: UserService +): HandlerInterceptorAdapter(){ + @Throws(Exception::class) + override fun preHandle(request: HttpServletRequest, response: HttpServletResponse, handler: Any): Boolean { + val headerMap: MutableMap = HashMap() + request.headerNames.toList().map { headerMap[it.toLowerCase()] = request.getHeader(it) } + + val token = headerMap["authorization"] ?: throw RuntimeException("not found authorization token") + + val user = userService.getUserByToken(token) + UserContext.setUser(user) + + return super.preHandle(request, response, handler) + } + + @Throws(Exception::class) + override fun postHandle(request: HttpServletRequest, response: HttpServletResponse, handler: Any, modelAndView: ModelAndView?) { + UserContext.removeAll() + super.postHandle(request, response, handler, modelAndView) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/example/kotlinweb/model/Board.kt b/src/main/kotlin/com/example/kotlinweb/model/Board.kt index 0758afb..9c7a489 100644 --- a/src/main/kotlin/com/example/kotlinweb/model/Board.kt +++ b/src/main/kotlin/com/example/kotlinweb/model/Board.kt @@ -1,7 +1,15 @@ package com.example.kotlinweb.model +import javax.persistence.* + +@Entity class Board( - val title:String, - val name:String, - val content:String + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + val id:Long? = null, + var title:String, + var content:String, + @ManyToOne + @JoinColumn(name = "userId") + val user:User ) \ No newline at end of file diff --git a/src/main/kotlin/com/example/kotlinweb/model/User.kt b/src/main/kotlin/com/example/kotlinweb/model/User.kt new file mode 100644 index 0000000..984b7fa --- /dev/null +++ b/src/main/kotlin/com/example/kotlinweb/model/User.kt @@ -0,0 +1,15 @@ +package com.example.kotlinweb.model + +import javax.persistence.Entity +import javax.persistence.GeneratedValue +import javax.persistence.GenerationType +import javax.persistence.Id + +@Entity +class User( + @Id + val id: String, + val password: String, + val name: String, + var token: String? = null +) \ No newline at end of file diff --git a/src/main/kotlin/com/example/kotlinweb/model/UserContext.kt b/src/main/kotlin/com/example/kotlinweb/model/UserContext.kt new file mode 100644 index 0000000..16f5784 --- /dev/null +++ b/src/main/kotlin/com/example/kotlinweb/model/UserContext.kt @@ -0,0 +1,20 @@ +package com.example.kotlinweb.model + +object UserContext{ + + private val exception = ThreadLocal() + private val user = ThreadLocal() + + fun setUser(user:User) = this.user.set(user) + + fun getUser() = this.user.get() + + fun removeUser() = user.remove() + + fun removeException() = exception.remove() + + fun removeAll() { + removeUser() + removeException() + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/example/kotlinweb/model/request/BoardRequest.kt b/src/main/kotlin/com/example/kotlinweb/model/request/BoardRequest.kt new file mode 100644 index 0000000..98f7ef1 --- /dev/null +++ b/src/main/kotlin/com/example/kotlinweb/model/request/BoardRequest.kt @@ -0,0 +1,6 @@ +package com.example.kotlinweb.model.request + +class BoardRequest( + val title:String, + val content:String +) \ No newline at end of file diff --git a/src/main/kotlin/com/example/kotlinweb/model/Response.kt b/src/main/kotlin/com/example/kotlinweb/model/response/Response.kt similarity index 75% rename from src/main/kotlin/com/example/kotlinweb/model/Response.kt rename to src/main/kotlin/com/example/kotlinweb/model/response/Response.kt index 9d98431..4bec0b4 100644 --- a/src/main/kotlin/com/example/kotlinweb/model/Response.kt +++ b/src/main/kotlin/com/example/kotlinweb/model/response/Response.kt @@ -1,4 +1,4 @@ -package com.example.kotlinweb.model +package com.example.kotlinweb.model.response import org.springframework.http.HttpStatus diff --git a/src/main/kotlin/com/example/kotlinweb/repository/BoardRepository.kt b/src/main/kotlin/com/example/kotlinweb/repository/BoardRepository.kt new file mode 100644 index 0000000..8418cfa --- /dev/null +++ b/src/main/kotlin/com/example/kotlinweb/repository/BoardRepository.kt @@ -0,0 +1,6 @@ +package com.example.kotlinweb.repository + +import com.example.kotlinweb.model.Board +import org.springframework.data.jpa.repository.JpaRepository + +interface BoardRepository: JpaRepository{} \ No newline at end of file diff --git a/src/main/kotlin/com/example/kotlinweb/repository/UserRepository.kt b/src/main/kotlin/com/example/kotlinweb/repository/UserRepository.kt new file mode 100644 index 0000000..9db3ed4 --- /dev/null +++ b/src/main/kotlin/com/example/kotlinweb/repository/UserRepository.kt @@ -0,0 +1,8 @@ +package com.example.kotlinweb.repository + +import com.example.kotlinweb.model.User +import org.springframework.data.jpa.repository.JpaRepository + +interface UserRepository: JpaRepository{ + fun findByToken(token:String):User? +} \ No newline at end of file diff --git a/src/main/kotlin/com/example/kotlinweb/service/BoardService.kt b/src/main/kotlin/com/example/kotlinweb/service/BoardService.kt index 37e6a6c..a03df18 100644 --- a/src/main/kotlin/com/example/kotlinweb/service/BoardService.kt +++ b/src/main/kotlin/com/example/kotlinweb/service/BoardService.kt @@ -1,33 +1,43 @@ package com.example.kotlinweb.service import com.example.kotlinweb.model.Board +import com.example.kotlinweb.model.UserContext +import com.example.kotlinweb.model.request.BoardRequest +import com.example.kotlinweb.repository.BoardRepository +import com.example.kotlinweb.repository.UserRepository +import org.springframework.data.repository.findByIdOrNull import org.springframework.stereotype.Service +import java.lang.RuntimeException +import javax.transaction.Transactional @Service class BoardService( - private val boardList:MutableList + private val boardRepository: BoardRepository ){ - fun createBoard(board:Board):Board{ - boardList.add(board) - return board + fun createBoard(boardRequest:BoardRequest):Board{ + val board = Board(null, boardRequest.title, boardRequest.content, UserContext.getUser()) + return boardRepository.save(board) } - fun findBoard(id:Int):Board{ - return boardList[id-1] + fun findBoard(id:Long):Board{ + return boardRepository.findByIdOrNull(id) ?: throw RuntimeException("not found board") } fun findAllBoard():List{ - return boardList + return boardRepository.findAll() } - fun deleteBoard(id:Int):Boolean{ - val board = boardList[id-1] - return boardList.remove(board) + fun deleteBoard(id:Long):Boolean{ + boardRepository.deleteById(id) + return true } - fun updateBoard(id:Int, board: Board):Board{ - boardList[id-1] = board; - return board; + @Transactional + fun updateBoard(id:Long, boardRequest: BoardRequest):Board{ + val board = boardRepository.findByIdOrNull(id) ?: throw RuntimeException("not found board") + board.title = boardRequest.title + board.content = boardRequest.content + return board } } \ No newline at end of file diff --git a/src/main/kotlin/com/example/kotlinweb/service/UserService.kt b/src/main/kotlin/com/example/kotlinweb/service/UserService.kt new file mode 100644 index 0000000..4c9b018 --- /dev/null +++ b/src/main/kotlin/com/example/kotlinweb/service/UserService.kt @@ -0,0 +1,40 @@ +package com.example.kotlinweb.service + +import com.example.kotlinweb.model.User +import com.example.kotlinweb.repository.UserRepository +import org.springframework.data.repository.findByIdOrNull +import org.springframework.stereotype.Service +import java.lang.RuntimeException +import java.time.ZonedDateTime +import kotlin.random.Random + +@Service +class UserService( + private val userRepository: UserRepository +){ + + fun login(id: String, password: String):User{ + val user = findUser(id) + if(user.password != password) throw RuntimeException("not found user") + user.token = Random(ZonedDateTime.now().toInstant().toEpochMilli()).nextBits(12).toString() + return user + } + + fun logout(id: String):User{ + val user =findUser(id) + user.token = null + return user + } + + fun register(user: User){ + userRepository.save(user) + } + + fun getUserByToken(token:String):User{ + return userRepository.findByToken(token) ?: throw RuntimeException("not found user"); + } + + private fun findUser(id:String):User{ + return userRepository.findByIdOrNull(id) ?: throw RuntimeException("not found user") + } +} \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 8b13789..1a263cf 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1 +1,8 @@ - +spring.datasource.url=jdbc:mysql://localhost:3307/board-study?useSSL=false&characterEncoding=utf-8 +spring.datasource.username=root +spring.datasource.password=1234 +spring.datasource.driverClassName=com.mysql.jdbc.Driver +spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect +spring.jpa.hibernate.ddl-auto=create-drop +spring.jpa.show-sql=true +spring.jpa.database=mysql \ No newline at end of file diff --git a/src/test/kotlin/com/example/kotlinweb/controller/BoardControllerTest.kt b/src/test/kotlin/com/example/kotlinweb/controller/BoardControllerTest.kt index 3229100..ce11eb5 100644 --- a/src/test/kotlin/com/example/kotlinweb/controller/BoardControllerTest.kt +++ b/src/test/kotlin/com/example/kotlinweb/controller/BoardControllerTest.kt @@ -1,7 +1,9 @@ package com.example.kotlinweb.controller import com.example.kotlinweb.model.Board -import com.example.kotlinweb.model.Response +import com.example.kotlinweb.model.User +import com.example.kotlinweb.model.response.Response +import com.example.kotlinweb.repository.UserRepository import com.fasterxml.jackson.core.type.TypeReference import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper @@ -21,44 +23,52 @@ import org.springframework.test.web.servlet.result.MockMvcResultMatchers @AutoConfigureMockMvc @ActiveProfiles(value = ["test"]) @TestMethodOrder(MethodOrderer.OrderAnnotation::class) +@TestInstance(TestInstance.Lifecycle.PER_CLASS) class BoardControllerTest { @Autowired private lateinit var mockMvc:MockMvc - + @Autowired + private lateinit var userRepository: UserRepository companion object{ + private val objectMapper:ObjectMapper = jacksonObjectMapper() + private val user = User("123","123","123","123") } + @BeforeAll + fun init(){ + userRepository.save(user) + } @Test @Order(1) fun createBoard(){ - val board = Board("abcd","abcd","안녕하세요") + val board = Board(null,"abcd","abcd",user) val result = mockMvc.perform(MockMvcRequestBuilders.post("/board") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(board))) + .content(objectMapper.writeValueAsString(board)) + .header("Authorization","123")) .andExpect(MockMvcResultMatchers.status().isOk) .andReturn() val resultPayload = objectMapper.readValue(result.response.contentAsString, object : TypeReference>(){}) - assertEquals(resultPayload.data.name, board.name) assertEquals(resultPayload.data.title, board.title) assertEquals(resultPayload.data.content, board.content) } @Test @Order(2) fun findBoard(){ - val board = Board("abcd","abcd","안녕하세요") + val board = Board(null,"abcd","abcd",user) val result = mockMvc.perform(MockMvcRequestBuilders.get("/board/1") - .contentType(MediaType.APPLICATION_JSON)) + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization","123")) .andExpect(MockMvcResultMatchers.status().isOk) .andReturn() val resultPayload = objectMapper.readValue(result.response.contentAsString, object : TypeReference>(){}) - assertEquals(resultPayload.data.name, board.name) assertEquals(resultPayload.data.title, board.title) assertEquals(resultPayload.data.content, board.content) } @@ -67,7 +77,8 @@ class BoardControllerTest { @Order(3) fun findAllBoard(){ val result = mockMvc.perform(MockMvcRequestBuilders.get("/board") - .contentType(MediaType.APPLICATION_JSON)) + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization","123")) .andExpect(MockMvcResultMatchers.status().isOk) .andReturn() val resultPayload = objectMapper.readValue(result.response.contentAsString, object : TypeReference>>(){}) @@ -77,14 +88,14 @@ class BoardControllerTest { @Test @Order(4) fun updateBoard(){ - val board = Board("abcd","abcd","안녕하세요2") + val board = Board(null,"abcd","abcd",user) val result = mockMvc.perform(MockMvcRequestBuilders.put("/board/1") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(board))) + .content(objectMapper.writeValueAsString(board)) + .header("Authorization","123")) .andExpect(MockMvcResultMatchers.status().isOk) .andReturn() val resultPayload = objectMapper.readValue(result.response.contentAsString, object : TypeReference>(){}) - assertEquals(resultPayload.data.name, board.name) assertEquals(resultPayload.data.title, board.title) assertEquals(resultPayload.data.content, board.content) } @@ -93,7 +104,8 @@ class BoardControllerTest { @Order(5) fun deleteBoard(){ val result = mockMvc.perform(MockMvcRequestBuilders.delete("/board/1") - .contentType(MediaType.APPLICATION_JSON)) + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization","123")) .andExpect(MockMvcResultMatchers.status().isOk) .andReturn() val resultPayload = objectMapper.readValue(result.response.contentAsString, object : TypeReference>(){}) diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties new file mode 100644 index 0000000..6e44c54 --- /dev/null +++ b/src/test/resources/application.properties @@ -0,0 +1,12 @@ +spring.profiles.active=test +spring.datasource.url=jdbc:h2:mem:default;mode=mysql +spring.datasource.driverClassName=org.h2.Driver + +spring.datasource.username=sa +spring.datasource.password= + +spring.jpa.database=h2 +spring.jpa.hibernate.ddl-auto=create-drop +spring.jpa.show-sql=true +#spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl +spring.jpa.generate-ddl=true \ No newline at end of file