[카프카활용] 챕터 2. 프로젝트 준비
1. 프로젝트 기술 스택 설명
Gradle vs Maven
- Gradle이 Maven보다 최신 기술이고, Maven의 단점을 보완하기 위해 나온 빌드 툴
- 퍼포먼스와 사용성에서 Gradle이 더 좋더라
Single-Module vs Multi-Module
- 중복되는 코드 제거
- 공통 모듈을 하나 두고, 암호/다른 서버와 통신하는 웹 클라이언트/DB 관련된 프로파일 작성 등 한 곳에 모아둘 수 있음
- 관리 리소스 줄어듦
- 서비스를 진행하다가 데이터베이스가 바뀔 일이 있다고 가정해 보자. 엔티티가 여러 군데에 적용이 되어있다면 영향도 파악이 굉장히 어렵다. 하지만 한 곳에 모아서 엔티티를 관리한다면, 서비스를 운영하거나 유지보수하는데 유리
- 각 모듈별로 jar 파일 실행 가능
Kotlin
- 편의성
- fianl
- data class
- nullable
- 멀티플랫폼 언어
- 코틀린 라이브러리만 사용한다면 여러 플랫폼에서 사용 가능
- 서버 개발, 안드로이드 개발, ...
- 어떤 부분에서는 Java가 앞서고, 어떤 부분에서는 Kotlin이 앞서고 비등비등하더라
- https://www.baeldung.com/kotlin/kotlin-java-performance
2. 환경 세팅
sdkman 설치
스프링, 자바, 코틀린 등 버전을 관리해 주는 툴
curl -s "https://get.sdkman.io" | bash
source "$HOME/.sdkman/bin/sdkman-init.sh"
sdk version
sdkman을 사용하여 Java 11 설치
sdk list java
sdk install java 11.0.17-zulu
java --version
sdk use java 11.0.17-zulu
sdkman을 사용하여 Gradle 설치
sdk list grade
sdk install gradle 7.3.1
gradle --version
sdk use gradle 7.3.1
Docker 설치
Install Docker Engine
Learn how to choose the best method for you to install Docker Engine. This client-server application is available on Linux, Mac, Windows, and as a static binary.
docs.docker.com
redis 설치
brew install redis
3. Multi-Module Gradle, 데이터베이스 세팅
루트 프로젝트 수정
공통적으로 사용하는 의존성 - 루트 프로젝트의 build.gradle에 설정
각 모듈별로 사용하는 의존성 - 모듈별 build.gradle에 설정
루트 프로젝트에서는 따로 소스 코드를 관리하지 않는다.
따라서, src 디렉터리를 삭제한다.
build.gradle.kts
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
kotlin("jvm") version "1.6.0"
kotlin("plugin.spring") version "1.6.10" apply false
kotlin("plugin.jpa") version "1.6.0" apply false
id("org.springframework.boot") version "2.6.3"
id("io.spring.dependency-management") version "1.0.11.RELEASE" apply false
}
repositories {
mavenCentral()
}
allprojects {
group = "happyprogfrog"
repositories {
mavenCentral()
}
}
subprojects {
apply {
plugin("org.jetbrains.kotlin.jvm")
plugin("org.jetbrains.kotlin.plugin.spring")
plugin("org.springframework.boot")
plugin("io.spring.dependency-management")
}
tasks.withType<KotlinCompile> {
kotlinOptions {
freeCompilerArgs = listOf("-Xjsr305=strict")
jvmTarget = "11"
}
}
tasks.withType<Test> {
useJUnitPlatform()
}
dependencies {
implementation("org.springframework.boot:spring-boot-starter")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
testImplementation("org.springframework.boot:spring-boot-starter-test")
}
}
api, consumer, gateway, css*, domain* 모듈 추가
모듈을 추가해 준다.
build.gradle.kts
plugins {}
version = "0.0.1"
dependencies {}
* css: 신용평가모델링
신용평가모델링 개념
1. CB사, Credit Bureau 먼저 CSS(신용평가모델링) 모델에 대해 이해하기 위해서는 금융 신용 process에 대해 알 필요가 있다. 그에 대해 첫번째로 확인해볼 것이 CB라는 개념이다. CB는 Credit Bereau의 줄임
signing.tistory.com
*domain: DB는 하나를 봄, 엔티티를 관리하는 모듈
로컬에서 Docker를 통해 MySQL과 redis 띄우기
매번 커맨드 라인으로 작성하기 귀찮기 때문에, 쉘 스크립트 작성
docker 디렉터리 생성 후, fintech-mysql.sh 파일 만들기
docker run -d \
--name fintech-mysql \
-e MYSQL_ROOT_PASSWORD="fintech" \
-e MYSQL_USER="fintech" \
-e MYSQL_PASSWORD="fintech" \
-e MYSQL_DATABASE="fintech" \
-p 3306:3306 \
mysql:latest
fintect-redis.sh 파일 만들기
docker network create redis-net
docker run --name fintech-redis \
-p 6379:6379 \
--network redis-net \
-d redis:latest
IntelliJ에서 테이블 확인하기
[IntelliJ] Community 버전에서 Database Navigator가 보이지 않을 때
너무 충격적이게도...Community Edition엔 기본 Database Navigator가 없다... 그래서 오른쪽 툴바를 보면 Gradle만 있다ㅠ__ㅠ 플러그인으로 설치하면 사용할 수 있다고 해서 설치해보기로 했다. Intellij IDEA -
progfrog.tistory.com
redis 접속하기
redis-cli -h localhost
4. 개발 환경 점검
domain 모듈 작업
build.gradle.kts
plugins {
kotlin("plugin.jpa")
}
version = "0.0.1"
dependencies {
dependencies {
implementation("org.springframework.boot:spring-boot-starter-data-jpa:2.7.6")
implementation("mysql:mysql-connector-java")
implementation("org.springframework.boot:spring-boot-starter-data-redis")
}
}
- jpa 플러그인이 있으면 엔티티에 굳이 기본 생성자를 만들어 주지 않아도 된다.
- 의존성 추가해 주기!
application-domain.yml
spring:
jpa:
generate-ddl: false
show-sql: true
hibernate:
ddl-auto: validate
naming:
physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/fintech?characterEncoding=UTF-8&serverTimezone=Asia/Seoul
username: fintech
password: fintech
- physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
- 데이터들의 대소문자를 구분하기 때문에, 넣는 것을 추천!
- username, password
- 민감한 데이터이기 때문에 실제로는 암호화를 하거나 별도의 서버에서 따로 관리
domain 패키지 생성
package happyprogfrog.domain.domain
import javax.persistence.*
@Entity
@Table(name = "USR_INFO")
class UserInfo(
@Column(name = "usr_key")
val userKey: String,
@Column(name = "usr_reg_num")
val userRegistrationNumber: String,
@Column(name = "usr_nm")
val userName: String,
@Column(name = "usr_icm_amt")
val userIncomeAmount: Long
) {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long? = null
}
package happyprogfrog.domain.domain
import javax.persistence.*
@Entity
@Table(name = "LOAN_REVIEW")
class LoanReview (
@Column(name = "usr_key")
val userKey: String,
@Column(name = "loan_lmt_amt")
val loanLimitedAmount: Long,
@Column(name = "loan_intrt")
val loanInterest: Double
){
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long? = null
}
repository 패키지 생성
package happyprogfrog.domain.repository
import happyprogfrog.domain.domain.UserInfo
import org.springframework.data.jpa.repository.JpaRepository
interface UserInfoRepository : JpaRepository<UserInfo, Long> {
fun findByUserKey(userKey: String): UserInfo
}
package happyprogfrog.domain.repository
import happyprogfrog.domain.domain.LoanReview
import org.springframework.data.jpa.repository.JpaRepository
interface LoanReviewRepository : JpaRepository<LoanReview, Long> {
fun findByUserKey(userKey: String): LoanReview?
}
테이블 생성
CREATE TABLE USR_INFO(
id bigint not null auto_increment primary key,
usr_key varchar(32) not null unique,
usr_reg_num varchar(50) not null,
usr_nm varchar(20) not null,
usr_icm_amt bigint default 0 not null
);
CREATE TABLE LOAN_REVIEW(
id bigint not null auto_increment primary key,
usr_key varchar(32) not null unique,
loan_lmt_amt bigint default 0 not null,
loan_intrt double default 0.0 not null
);
- 주민 번호가 저장되는 usr_reg_num 테이블의 경우 민감한 정보라 암호화가 되어야 하기 때문에 넉넉하게 VARCHAR(50)으로 잡은 것
api 모듈 작업
build.gradle.kts
plugins {}
version = "0.0.1"
dependencies {
// spring-boot-starter-web
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-data-jpa:2.7.6")
// 다른 모듈이지만 그대로 사용할 수 있다
implementation(project(":domain"))
}
간단한 테스트용 엔트리포인트를 만들어보자.
application.yml
spring:
profiles:
include:
- domain
TestController
package happyprogfrog.api.test
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
@RestController
@RequestMapping("/fintech/api/v1")
class TestController(
private val testService: TestService
){
@GetMapping("/test/get/{userKey}")
fun test(
@PathVariable("userKey") userKey: String
): TestDto.UserInfoDto = testService.testGetService(userKey)
}
TestDto
package happyprogfrog.api.test
class TestDto {
data class UserInfoDto(
val userKey: String,
val userRegistrationNumber: String,
val userName: String,
val userIncomeAmount: Long
)
}
TestService
package happyprogfrog.api.test
import happyprogfrog.domain.domain.UserInfo
import happyprogfrog.domain.repository.UserInfoRepository
import org.springframework.stereotype.Service
@Service
class TestService(
private val userInfoRepository: UserInfoRepository
) {
fun testGetService(userKey: String) = userInfoRepository.findByUserKey(userKey).toDto()
fun UserInfo.toDto() = TestDto.UserInfoDto(userKey, userRegistrationNumber, userName, userIncomeAmount)
}
실행
curl -X GET http://localhost:8080/fintect/api/v1/test/get/12345