티스토리 뷰
개요
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
- graylog
- Spring Boot
- JavaScript
- 알뜰폰
- Kendo UI Web Grid
- kotlin
- 로드바이크
- Kendo UI
- DynamoDB
- node.js
- JHipster
- spring
- jsp
- Eclipse
- 평속
- jstl
- 자전거
- maven
- jpa
- 태그를 입력해 주세요.
- 로드 바이크
- Docker
- java
- chrome
- Tomcat
- Spring MVC 3
- CentOS
- MySQL
- bootstrap
- 구동계
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 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 |
글 보관함
