티스토리 뷰
개요
Kotlin
언어를 접해본 사람은 다른 언어로 돌아가기 어려워한다. 언어의 저변을 떠나 굉장한 생산성과 편의성을 제공하기 때문이다. 이런 가정을 해보자. 만약, 백엔드에 추가로 프론트엔드까지 한 프로젝트를 전부 Kotlin 언어로 제작할 수 있다고 생각해보자. Kotlin 숙련자라면 굉장히 빠른 속도로 프로젝트를 제작할 수 있을 것이다. 이번 글에서 소개할KVision
은 바로 그런 의도로 탄생하였다.KVision
은 Kotlin 언어가 가진 강력한 DSL 기능을 이용하여 개발된 오픈 소스 풀스택 Kotlin 웹 프레임워크이다. 하나의 프로젝트 내에서 백엔드, 프론트엔드 모든 영역을 Type-Safe가 보장되는 Kotlin 언어로 작성할 수 있다는 장점이 있다.- KVision의 UI 디자인은
Bootstrap
을 기반으로 설계되어 관련 기능이 모두 Kotlin DSL로 제공된다. (추가적으로PatternFly
를 적용할 수도 있다.) - KVision의 백엔드 영역은 다양한 프레임워크에 대응할 수 있도록 설계되어 있다. 이번 글에서는 대부분 개발자들에게 익숙한
Spring Boot
를 선정하여 프로젝트 제작 방법을 소개하고 자한다. - KVision + Spring Boot 프로젝트를 처음부터 작성하려면 손이 무척 많이 간다. 아래와 같이 제작자가 제공하는 템플릿 프로젝트를 내려 받아 시작하는 방법을 추천한다.
프로젝트 생성
# 샘플 프로젝트 복제
$ git clone https://github.com/rjaros/kvision-examples.git
# Spring Boot 샘플 프로젝트로 이동
$ cd kvision-examples/template-fullstack-spring-boot
애플리케이션 실행
- 풀스택 프로젝트는 아래와 같이 애플리케이션을 실행할 수 있다.
# 8080 포트로 백엔드 실행
$ gradlew backendRun
# 백엔드 변경점을 런타임에서 반영하기 위한 모니터링 실행
$ gradlew -t compileKotlinBackend
# 3000 포트로 프론트엔드 실행
$ gradlew frontendRun
애플리케이션 배포
- 풀스택 프로젝트는 아래와 같이 백엔드와 프론트엔드가 모두 통합된 .jar 파일을 빌드할 수 있다.
$ gradlew clean jar
- Docker 이미지를 생성하고자 할 경우 아래와 같이 Spring Boot의 기본 방법을 그대로 따르면 된다. (보다 자세한 내용은 본 블로그의 이 글을 참고한다.)
# Docker 이미지 빌드
$ gradlew bootBuildImage --imageName=kvision/example
# 빌드된 Docker 이미지 실행
$ docker run -d -p 8080:8080 kvision/example
- Docker 컨테이너 실행시 8080 포트를 맵핑해야 작동한다. 실제 운영 환경에서 로드 밸런서 연동시 참고한다.
PatternFly 적용
PatternFly
는 일관된 모던 디자인을 제공하기 위해 RedHat에서 오픈 소스로 개발한 인터페이스 디자인 시스템이다. KVision에서도 지원하기 때문에 손쉽게 적용할 수 있다. 프로젝트 내/build.gradle.kts
파일에 아래 내용을 추가한다.
kotlin {
...
sourceSets {
...
val frontendMain by getting {
...
dependencies {
...
implementation(npm("@patternfly/patternfly", "4.87.3"))
implementation(npm("@patternfly/react-core", "4.97.2"))
}
...
}
}
}
@patternfly/react-core
패키지를 추가하면 PatternFly의 React 컴포넌트를 이용할 수 있어 매우 편리하다. (React 컴포넌트 연동 예는 아래 작성하였다.)- 이제 프론트엔드에 스타일을 적용할 차례이다. 프로젝트 내
/src/frontendMain/kotlin/App.kt
에 아래 내용을 추가하면 적용이 완료된다.
import io.kvision.require
class App : Application() {
init {
require("@patternfly/patternfly/patternfly.min.css")
require("@patternfly/patternfly/patternfly-addons.css")
...
}
...
}
비지니스 로직 작성 순서
- KVision의 비지니스 로직 작성은 아래 순서를 따른다.
1. /src/commonMain/kotlin 폴더에 @Serializable이 명시된 Model 클래스 작성
2. /src/commonMain/kotlin 폴더에 @KVService이 명시된 Service 인터페이스 작성
3. /src/backendMain/kotlin 폴더에 @Service, @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)이 명시된 Service 구현체 클래스 작성
4. /src/frontEndMain/kotlin 폴더에 작성된 App 클래스에서 해당 구현체의 인스턴스를 싱글턴으로 사용
- 개발자는 위 순서대로 풀스택 영역에 걸쳐 전부 Kotlin 언어로로 코드를 작성하고 호출하기만 하면 된다. 빌드 과정에서 프레임워크에 의해 내부적으로 서비스 호출부가 AJAX 통신의 형태로 자동 변환된다.
비지니스 로직 작성 예
- 가장 먼저 백엔드와 프론트엔드에서 공통으로 사용 가능한 모델 클래스를 작성할 차례이다. /src/commonMain/kotlin 경로에 작성한다.
@Serializable
data class Book(
val title: String,
val author: String,
val year: Int,
val rating: Int
)
- 아래는
@KVService
서비스 인터페이스의 작성 예이다. 일반적인 Spring Boot에서의 구현 방법과 약간의 차이가 있을 뿐 쉽게 적응할 수 있는 구조이다. /src/commonMain/kotlin 경로에 작성한다. - 유의할 점은 인터페이스 이름을 지을 때 반드시 IXXXService 형태의 네이밍 컨벤션을 따라야 한다.
@KVService
interface IBookService {
suspend fun getBooks(): List<Book>
}
- 아래는 앞의 인터페이스를 기반으로
@Service
서비스 구현체를 작성한 예이다. /src/backendMain/kotlin 경로에 작성한다.
@Service
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
actual class BookService: IBookService {
override suspend fun getBooks(): List<Book> {
return listOf(
Book("넷플릭스의 시대", "라몬 로바토", 2020, 5),
Book("THIS IS TOYOTA 도요타 이야기", "노지 츠네요시", 2019, 4)
)
}
}
- 작성된 서비스 클래스가 프론트엔드에서 사용 가능한 형태가 되기 위해서는 백엔드의 매니저 목록에 추가되어야 한다. 백엔드 빌드 후 /src/backendMain/kotlin/Main.kt 파일에 아래 자동 생성된 매니저 클래스를 추가하면 이제 프론트엔드에서 서비스 클래스를 사용할 준비가 완료된다.
class KVApplication {
@Bean
fun getManagers() = listOf(BookServiceManager, ...)
}
- 마지막으로 프론트엔드에서 앞서 작성한 서비스를 이용할 차례이다. /src/frontEndMain/kotlin/App.kt 파일에 아래 내용을 추가한다.
class App : Application() {
private val bookService = BookService()
private lateinit var aText: Div
override fun start() {
aText = div {
fontSize = 32.px
content = bookService.getBooks().first().title
}
}
}
테이블 작성 예
- 다음은 프론트엔드에서 테이블을 작성하는 방법이다. 먼저 프론트엔드에 모델 오브젝트를 작성한다.
ObservableList<T>
를 이용하여 테이블에 연동할 데이터의 상태를 저장하는 역할을 수행한다.
object BookModel {
private val bookService: BookService = BookService()
val books: ObservableList<Book> = observableListOf()
suspend fun getBooks() {
GlobalScope.launch {
books.syncWithList(bookService.getBooks())
}
}
}
- 다음으로 실제 테이블 렌더링을 담당할 패널 오브젝트를 작성할 차례이다. 사전 작성된 모델을 이용하여 직접적으로 DOM을 제어하지 않고, 데이터의 변경점이 자동으로 테이블에 반영되도록 설계했다.
object BookListPanel : SimplePanel() {
init {
// 페이지 로드시 Model을 백엔드로부터 미리 획득
GlobalScope.launch { BookModel.getBooks() }
// table 생성 및 Model과 상태 연동
val bookTable = table(
Model.books,
types = setOf(TableType.STRIPED, TableType.HOVER),
responsiveType = ResponsiveType.RESPONSIVE
) { books ->
headerCell("Title")
headerCell("Author")
headerCell("Year")
headerCell("Rating")
books.forEach {
row {
cell(it.title)
cell(it.author)
cell(it.year.toString())
cell(it.rating.toString())
}
}
}
// button 생성 및 Model에 데이터 추가 연동, table에도 변경된 Model의 데이터가 반영
button("책 추가하기", style = ButtonStyle.PRIMARY).onClick {
GlobalScope.launch {
BookModel.books.add(Book("커리어 스킬", "존 손메즈", 2016, 10))
}
}
}
}
- 마지막으로 아래와 같이 작성한 패널을 추가하면 테이블 작성이 완료된다.
class App : Application() {
override fun start() {
root("kvapp") {
add(BookListPanel())
}
}
PatternFly + React: DatePicker 작성 예
- 아래는 PatternFly의 React 컴포넌트를 이용하여 DatePicker 컴포넌트를 작성한 예이다.
// Type-Safe의 props 인터페이스를 설계
external interface PFDatePickerProps : RProps {
var value: String
var placeholder: String
var onChange: (String) -> Unit
}
// KVision에서 작동 가능한 PFDatePicker 컴포넌트를 생성
val PFDatePicker: RClass<PFDatePickerProps> = require("@patternfly/react-core").DatePicker
// state 초기 값을 부여하여 DatePicker 렌더링
val datePicker = react(state = "2021-02-19") { getState, changeStatus ->
PFDatePicker {
attrs.value = getState()
attrs.placeholder = "yyyy-MM-dd"
attrs.onChange = { value -> changeStatus { value } }
}
}
// 클릭시 state 값을 변경하는 Button 렌더링
button("Change Date", style = ButtonStyle.PRIMARY)
.onClick {
datePicker.state = "2021-01-01"
}
참고 글
댓글
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
링크
TAG
- JavaScript
- Tomcat
- kotlin
- jstl
- JHipster
- 구동계
- spring
- Spring MVC 3
- node.js
- Eclipse
- 로드 바이크
- Kendo UI Web Grid
- 알뜰폰
- jsp
- Docker
- DynamoDB
- jpa
- MySQL
- 로드바이크
- 태그를 입력해 주세요.
- maven
- chrome
- 자전거
- CentOS
- java
- Kendo UI
- 평속
- graylog
- bootstrap
- Spring Boot
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | |
7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 29 | 30 |
글 보관함