🍿 강의수강/카프카활용

[카프카활용] 챕터 2. 프로젝트 준비

케로⸝⸝◜࿀◝ ⸝⸝ 2024. 5. 21. 23:14

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 설치

스프링, 자바, 코틀린 등 버전을 관리해 주는 툴

https://sdkman.io/install

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

 

반응형