[kotlin] 2. Kotlin + Spring Boot (일명 코프링)으로 CRUD 게시판 API 구현

2023. 12. 7. 17:09cs및 소프트스킬/kotlin

728x90
반응형
SMALL

안녕하세요.

코틀린 입문 개발을 진행을 하면서 지금까지 진행한 프로젝트를 이어서 Java 기반으로 만든 Spring Boot 개발을 Kotlin 기반으로 한 Spring Boot로 CRUD 작성하는 시간을 가져보겠습니다.

이글은 Java 개발자가 Kotlin을 익히는 과정으로 작성을 하였기 때문에 처음부터 작성을 하지 않았으며 어느정도 개발 숙련도를 올린 상태에서 보시는 것을 추천 드립니다.

 

Github (ver02 참조) : https://github.com/BerkleyLim/basic_kotlin

 

GitHub - BerkleyLim/basic_kotlin: 코틀린 연습 프로젝트 (입문용) + ktor 및 spring boot 등 활용하기

코틀린 연습 프로젝트 (입문용) + ktor 및 spring boot 등 활용하기. Contribute to BerkleyLim/basic_kotlin development by creating an account on GitHub.

github.com

 

 

 

0. 사전 준비

- Java 17 버전 이상, Kotlin 19버전 환경 설정 셋팅, Intelli J

- 게시판 프로젝트 기반으로 설정 된 Mybatis, My-SQL (타 DBMS 무관)

- Kotlin 문법 어느정도 숙지

- Kotlin Spring Boot 미리 환경 설정하고 초기 프로젝트 생성 (2023년 12월 7일 기준 Spring Boot 3.x.x와 JDK 17 이상만 지원)

 

 

1. 프로젝트 구성 요소

 

 

 

2. Gradle 환경 설정

import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
	id("org.springframework.boot") version "3.2.0"
	id("io.spring.dependency-management") version "1.1.4"
	kotlin("jvm") version "1.9.20"
	kotlin("plugin.spring") version "1.9.20"
}

group = "com.example"
version = "0.0.1-SNAPSHOT"

java {
	sourceCompatibility = JavaVersion.VERSION_17
}

repositories {
	mavenCentral()
}

dependencies {
	implementation("org.springframework.boot:spring-boot-starter-data-jdbc")
	implementation("org.springframework.boot:spring-boot-starter-jdbc")
	implementation("org.springframework.boot:spring-boot-starter-web")
	implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
	implementation("org.jetbrains.kotlin:kotlin-reflect")
	implementation("org.mybatis:mybatis:3.5.14")
	implementation("org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.3")
	developmentOnly("org.springframework.boot:spring-boot-devtools")
	runtimeOnly("com.mysql:mysql-connector-j")
	testImplementation("org.springframework.boot:spring-boot-starter-test")
	testImplementation("org.mybatis.spring.boot:mybatis-spring-boot-starter-test:3.0.3")

}

tasks.withType<KotlinCompile> {
	kotlinOptions {
		freeCompilerArgs += "-Xjsr305=strict"
		jvmTarget = "17"
	}
}

tasks.withType<Test> {
	useJUnitPlatform()
}

 

 

 

3. CORS 해결

- 프론트엔드 서버 (CSR 기반)으로 개발 한 경우 CORS 문제를 해결해 줘야합니다.

- 이것을 Kotlin 코드로 작성하여 설정해줍니다.

 

<src/main/kotlin/com/example/ver02/configuration/WebConfig.kt>

@Configuration
class WebConfig : WebMvcConfigurer {
    override fun addCorsMappings(registry: CorsRegistry) {
        registry
            .addMapping("/api/**")
            .allowedOrigins("http://localhost:3000")
    }
}

 

 

 

4. property 설정

<src/main/application.yaml>

server:
  shutdown: graceful

spring:
  h2:
    console:
      enabled: true

  datasource:
    url: jdbc:mysql://localhost:3306/table?useSSL=false&serverTimezone=UTC&zeroDateTimeBehavior=convertToNull&allowPublicKeyRetrieval=true&autoReconnect=true
    driverClassName: com.mysql.cj.jdbc.Driver
    password: 1 # 비밀번호 수정
    username: table

  devtools:
    livereload:
      enabled: true

  sql:
    init:
      encoding: UTF-8

mybatis:
  type-aliases-package: com.example.ver02.entity
  configuration:
    cache-enabled: true
    use-column-label: true
    use-generated-keys: false
    map-underscore-to-camel-case: true
    default-statement-timeout: 25000
    jdbc-type-for-null: NULL
  mapper-locations: classpath:/mapper/*Mapper.xml

 

 

5. API 개발 (CRUD)

<BoardMapper.xml>

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "//mybatis.org/DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.example.ver02.mapper.BoardMapper">

    <!-- id : Mapper 인터페이스에 사용할 메소드명 -->
    <!-- resultType : Response Parameter에 반환할 변수 값의 대한 내용 -->
    <!-- parameterType : Request Parameter에 들어갈 변수 값을 저장된 내용 -->

    <!-- 전체 파라미터 조회 관련 게시글 -->
    <select id="selectList" resultType="Board">
        select * from board
    </select>


    <!-- mybatis에서 param1, param2 를 이용하여 삽입이 가능합니다. -->
    <!-- 이는 mapper에서 첫번째 파라미터가 title이고, 두번째 파라미터는 contents입니다. -->
    <!-- 생성 sql문  -->
    <insert id="insertBoard">
        <!-- insert into scroll_board (title, contents) value (#{title}, #{contents}) -->
        insert into board (title, contents) value (#{param1}, #{param2})
    </insert>

    <!-- 현재 내용 수정 sql문 -->
    <update id="updateBoard" parameterType="Board">
        update board set
        title=#{title} ,
        contents=#{contents}
        where bno = #{bno}
    </update>

    <!-- 현재 내용을 삭제하는 sql문 -->
    <delete id="deleteBoard">
        delete from board where bno=#{bno}
    </delete>

</mapper>

 

 

<Board.kt>

package com.example.ver02.entity

import com.fasterxml.jackson.annotation.JsonInclude

@JsonInclude(JsonInclude.Include.NON_NULL)
class Board {
    var bno: Long? = null
    var title: String? = null
    var contents: String? = null

    override fun toString(): String {
        return "Board(bno=$bno, title=$title, contents=$contents)"
    }

    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (javaClass != other?.javaClass) return false

        other as Board

        if (bno != other.bno) return false
        if (title != other.title) return false
        return contents == other.contents
    }

    override fun hashCode(): Int {
        var result = bno?.hashCode() ?: 0
        result = 31 * result + (title?.hashCode() ?: 0)
        result = 31 * result + (contents?.hashCode() ?: 0)
        return result
    }
}

 

 

 

<BoardMapper.kt>

package com.example.ver02.mapper

import com.example.ver02.entity.Board
import org.apache.ibatis.annotations.Mapper
import org.springframework.stereotype.Repository


@Repository
@Mapper
interface BoardMapper {
    /**
     * 각 메소드 이름은 BoardMapper.xml에서 지정한 메소드 별로 반드시 지어줘야 합니다.
     */
    // 전체 파라미터 조회 관련 게시글
    fun selectList(): List<Board?>?

    // 생성 sql문
    fun insertBoard(title: String?, contents: String?): Int?

    // 현재 내용 수정 sql문
    fun updateBoard(board: Board?): Int?

    // 현재 내용을 삭제하는 sql문
    fun deleteBoard(bno: Long?): Int?
}

 

 

<BoardService.kt>

package com.example.ver02.service

import com.example.ver02.entity.Board
import com.example.ver02.mapper.BoardMapper
import org.springframework.stereotype.Service

@Service
class BoardService(private val boardMapper : BoardMapper) {
    override fun equals(other: Any?): Boolean {
        return super.equals(other)
    }

    /**
     * 각 Mapper별의 응답값을 불려오고, 앞으로의 로직을 짤 때는 Controller단이 아닌
     * Service 단에서 별도의 로직과 알고리즘을 구현 하는 형태로 진행합니다.
     */
    // 전체 조회 로직
    fun selectList(): List<Board?>? {
        return boardMapper.selectList()
    }



    // 게시물 삽입 로직
    fun insertBoard(title: String?, contents: String?): Int? {
        return boardMapper.insertBoard(title, contents)
    }

    // 게시물 수정 로직
    fun updateBoard(board: Board?): Int? {
        return boardMapper.updateBoard(board)
    }

    // 게시물 삭제 로직
    fun deleteBoard(bno: Long?): Int? {
        return boardMapper.deleteBoard(bno)
    }
}

 

 

<BoardController.kt>

package com.example.ver02.controller

import com.example.ver02.entity.Board
import com.example.ver02.service.BoardService
import org.springframework.web.bind.annotation.*


// 컨트롤러 단
// 현재는 Default API는
// "/api/board"
@RestController
@RequestMapping("/api/board")
class BoardController(private var boardService: BoardService) {
    // API : /api/board/select/list
    // 전체 조회를 이용한 API 기능
    @GetMapping("/select/list")
    fun selectList(): List<Board?>? {
        return boardService!!.selectList()
    }

    // API : /api/board/insert/board
    // 게시판 삽입을 이용한 API 기능
    @PostMapping("/insert/board")
    fun insertBoard(@RequestBody board: Board): Int? {
        println(board.toString())
        return boardService!!.insertBoard(board.title, board.contents)
    }

    // API : /api/board/update/board
    // 게시판 수정을 이용한 API 기능
    @PostMapping("/update/board")
    fun updateBoard(@RequestBody board: Board): Int? {
        println("수정")
        println(board.toString())
        return boardService!!.updateBoard(board)
    }

    // API : /api/board/delete/board
    // 게시판 삭제를 이용한 API 기능
    @PostMapping("/delete/board")
    fun deleteBoard(@RequestBody board: Board): Int? {
        return boardService!!.deleteBoard(board.bno)
    }
}

 

 

 

6. 실제 테스트

- 기존 Java spring Boot + React로 만든 게시판 프로젝트를 Kotlin Spring Boot + React로 진행하여 결과를 알아보겠습니다.

- 게시판 리파지토리 위치 : https://github.com/BerkleyLim/project_board 

 

GitHub - BerkleyLim/project_board: Spring Boot + React CRUD형 게시판 만들기 프로젝트 안내서

Spring Boot + React CRUD형 게시판 만들기 프로젝트 안내서. Contribute to BerkleyLim/project_board development by creating an account on GitHub.

github.com

 

<조회>

조회

 

 

 

<추가>

추가할 폼

 

추가 이후 조회

 

 

<수정>

수정 폼
수정 이후 조회

 

 

<삭제>

 

 

 

이렇게 하여 Kotlin으로 이용한 Spring Boot API 개발을 마쳤습니다.

 

 

마치며

이번에 Kotlin을 학습하면서 환경 설정에 대해 맨땅의 헤딩으로 실시해보았지만, Spring Boot로 이용하여 적용하면서 Kotlin의 대해 어느정도 감각을 익힐 수 있게 되었다.

하지만, 아직은 더 공부해야겠다고 판단하고, 차후 VS Code 환경에서도 Kotlin 프로젝트를 진행 할 수 있게 연습을 해보아야 겠지만 Intelli J 기반으로 이용하여 프로젝트 진행에 용이한 것 같다.

 

 

 

 

※ 참조 Github : React + Java 기반 Spring boot 리파지토리

https://github.com/BerkleyLim/project_board

 

GitHub - BerkleyLim/project_board: Spring Boot + React CRUD형 게시판 만들기 프로젝트 안내서

Spring Boot + React CRUD형 게시판 만들기 프로젝트 안내서. Contribute to BerkleyLim/project_board development by creating an account on GitHub.

github.com

 

 

 

728x90
반응형
LIST

'cs및 소프트스킬 > kotlin' 카테고리의 다른 글

[Kotlin] Kotlin의 기본 용어 - 1탄  (0) 2024.03.10
[Kotlin] 1. 코틀린 - 입문하기  (0) 2023.12.06