반응형

1. FDS란?

 

  • 금융, 이커머스, 결제 서비스 등에서 사기성 거래(부정거래)를 탐지하고 차단하기 위한 시스템
    신용카드 결제, 계좌 이체, 로그인 시도 등에서 정상적인 사용 패턴과 다르게 이상 징후가 있을 경우 이를 탐지

 

2. 데모 프로젝트의 FDS 아키텍처

FDS에 대해서 처리 프로세스에 대해서 고민해보았다

FDS의 기본 기능을 기준을 벗어난느 데이터를 검출하고 이에 대해 알리거나 별도의 처리를 하는 시스템이다

다수의 클라이언트로 부터 발생하는 데이터를 평가하고 기준을 벗어나는 데이터에 대해 검출하여 그 결과를 저장하고 알림을 발생하는 기능에 대해 아키텍처를 구성했다

  • 높은 처리량을 위해 Kafka를 활용하여 비동기적으로 event를 처리할 수 있도록 하였다
  • 이벤트 엔진별로 토픽을 consume 할 수 있도록 분리(엔진 별 독립성 확보)

 

반응형
반응형

1. JVM Annotation

(1) @JvmOverloads

  • 코틀린과 자바의 상호 운용을 위해 오버로드된 메서드를 자동으로 생성(자바의 경우 기본 인자를 지원하지 않음)
  • 생성자, 함수에 사용 가능
class Hello {
    @JvmOverloads
    fun sayHello(name: String = "Guest", age: Int = 0) {
        println("Hello, $name. You are $age years old.")
    }
}

 

(2) @JvmField

  • 코틀린의 프로러티는 getter, setter를 통해 접근을 할 수 있는데, 이를 직접 필드 접근이 가능하도록 할 때 사용
class Person {
    @JvmField
    val name: String = "홍길동"
}

 

(3) @JvmName

  • 함수, 프로퍼티, 파일 이름 등을 지정된 이름이 아닌 다른 이름으로 java에서 호출이 가능하도록 함
@file:JvmName("MathUtils")
fun add(a: Int, b: Int): Int = a + b

 

(4) @JvmStatic

  • 코틀린의 경우 companion object를 통해 static과 비슷한 효과를 내게 되는데 실제 자바에서 companion object를 접근할 때 static이 아니기 때문에 이를 해결하기 위한 어노테이션
  • 해당 어노테이션을 붙이면 java에서 static으로 사용 가능
class Hello {
    companion object {
        @JvmStatic
        fun sayHi() {
            println("Hi from Kotlin")
        }

        fun sayBye() {
            println("Bye from Kotlin")
        }
    }
}
  • Java에서 호출 시
Utils.sayHi();  // @JvmStatic의 경우
Utils.Companion.sayBye();
반응형
반응형

1. 범위 지정 함수

(1)
val result1 = list.apply {
  this.add(1)
  add(2) // this 생략 가능
}

(2)
val result2 = list.also {
	print("리스트: ${it}") // {} 생략 가능
}

(3)
val result3 = "kotlin"
	.let{
		it.uppercase()
	}
	.let{
		it.reversed()
	}

(4)
val s = "kotlin"

val result4 = with(s) {
	println(this)
	println(s == this)
	uppercase() // this 생략 가능 this.uppercase() or s.uppercase() 
}

(5)
val result5 = run {
    val a = 10
    val b = 20
    a + b
} // result5 == 30

 

(1) apply ( public inline fun <T> T.apply(block: T.() -> Unit): T )

  • apply는 객체를 만들고 속성을 설정할 때(초기화) 사용하는 범위 지정 함수로 리시버로 this를 사용하며 리턴 값은 객체 자체이다
inline fun <T> T.apply(block: T.() -> Unit): T {
    block()  // this.block()
    return this
}

 

(2) also ( public inline fun <T> T.also(block: (T) -> Unit): T )

  • also는 객체를 만들고 객체에 대한 부가 작업(로깅, validation 등)을 수행하는 데 사용하는 범위 지정 함수로 리시버로 it을 사용하며 리턴 값은 객체 자체 이다
inline fun <T> T.also(block: (T) -> Unit): T {
    block(this)
    return this
}

 

(3) let( public inline fun <T, R> T.let(block: (T) -> R): R )

  • let은 주로 null체크 후 안전한 실행, 임시 스코프 생성, 체이닝, 값 가공 후 반환 등에 사용할 수 있으며, 리시버로 it을 사용하고 리턴은 마지막 표현식 이다
inline fun <T, R> T.let(block: (T) -> R): R {
    return block(this)
}

 

(4) with ( public inline fun <T, R> with(receiver: T, block: T.() -> R): R )

  • with는 확장 함수가 아닌 일반 함수로 객체의 여러 멤버에 접근하거나 조작할 때 사용하며 리시버를 인자로 받고 this를 통해서도 사용할 수 있다
inline fun <T, R> with(receiver: T, block: T.() -> R): R {
    return receiver.block()
}

 

(5) run ( public inline fun <T, R> T.run(block: T.() -> R): R )

  • run은 객체를 초기화하거나 계산후 결과를 반환할 때 사용되며, 리시버로 this를 사용하고 마지막 표현식을 반환한다
inline fun <T, R> T.run(block: T.() -> R): R {
    return block()  // this.block()
}

 

apply, also, let, run 비교

구현에 있어서는 각각에 대해 차이가 있을 수 있다 실제로 사용해보면 별 차이가 없는 것 처럼 느껴질 수 있다 하지만 이는 함수의 각 역할에 따라 구분을 해둔 것이라고 이해하면 좋을 것 같다

apply는 객체에 대한 속성을 설정하는 역할, also는 생성된 객체에 대한 validation이나 logging등 부가적인 작업, let은 안전한 null처리나 임시 스코프 생성 등의 역할, run은 초기화 및 계산후 결과 반환 등의 역할을 수행한다

다만, 그렇다면 가령 (2)에 대해서는 변경을 제한하는 등의 작업이 있으면 좋을 텐데 그렇지 않은 것은 실제 사용하는 환경에서는 이러한 역할을 딱딱 맞게 나누기 어렵기도 하기 때문에 역할 구분해두고 이렇게 쓰기를 권장하는 방향으로 기능이 존재한다

반응형
반응형

1. 예외처리

  1. 기본 구조는 try-catch-finally이다
  2. 코틀린은 Checked Exception을 지원하지 않는다.(코틀린에서의 예외는 모두 UnChecked Exception)
  3. 코틀린에서는 throws를 통해 예외 선언을 할 필요가 없다
  4. 코틀린의 try-catch블록은 값을 반환할 수 있다
val result: Int = try {
    10 / 2
} catch (e: Exception) {
    -1
}

 

Custom Exception

Custom Exception을 정의 할 때는 것은 Exception이나 RuntimeException 클래스를 상속하면 된다

코틀린에서는 Checked Exception이 없으므로 의미상의 차이로 구분해서 상속을 하면 된다

(비즈니스 로직 내 발생하는 에러는 RuntimeException으로 하는게 의미상 적절해 보인다)

/*
Throwable (클래스)
 ├── Exception (클래스)
 │    ├── RuntimeException
 │    └── IOException, etc.
 └── Error (클래스)
*/

class CustomException(message: String) : Exception(message)

 

runCatching

runCatching을 활용하여 예외를 안전하게 처리할 수 있다

runCatching에 의해 생성된 결과에 따라 분기로 로직을 실행할 수 있다

val result = runCatching { // 코틀린 내장 함수로 성공 또는 예외를 Result<T>로 감싸주는 인라인 함수
    val x = 11 / 0
    x
}

result
    .onSuccess { println("success: $it") }
    .onFailure { println("fail: ${it.message}") }

 

인라인 함수란?

- 함수 호출을 없애고 본문을 직접 복사해서 넣는 고차 함수 최적화

- fun 앞에 inline 키워드를 통해 정의

- 객체 생성이 없고, 호출 오버헤드 없음

반응형
반응형

1. 컬렉션

코틀린에서 컬렉션은 읽기 전용(Immutable)변경 가능(mutable)한 컬렉션으로 나뉜다

 

읽기 전용 컬렉션(Immutable)

Collection<T> (인터페이스)

  • 읽기 전용 컬렉션의 최상위 계층으로 Iterable<T>를 상속 받는다 (Iterable은 순차적으로 반복이 가능한지 정의하는 인터페이스)
// List<T>
val abc = listOf("a", "b", "c")

println("${abc.size}")
println("${abc.get(0)}")
println("${abc[1]}")

// Set<T>
val setA = setOf("a", "b", "c")
val setB = setOf("a", "b", "c")
println("equals: ${setA==setB}") // true

// Map<K,V>
val mapA = mapOf("a" to 1, "b" to 2, "c" to 3)
val mapB = mapOf("a" to 1, "b" to 2, "c" to 3)

println(mapA.get("a"))
println("equals: ${mapA == mapB}") // true

 

변경 가능한 컬렉션(Mutable)

MutableCollection<T> (인터페이스)

  • 변경 가능한 컬렉션의 최상위 계층으로 Iterable<T>를 상속 받는다
// MutableList<T>
val alpabets = mutableListOf("a", "b", "c")
// mutableListOf<String>("a", "b", "c")에서 형식 추론으로 String 생략
alpabets.add("d")
alpabets.add("e")
alpabets.removeAt(1)

println(alpabets)

// MutableSet<T>
val setA = mutableSetOf("a", "b", "c")
val setB = mutableSetOf("a", "b", "c")

println("equals: ${setA==setB}") // true

setA.add("a")
println("equals: ${setA==setB}") // true

setB.add("d")
println("equals: ${setA==setB}") // false

// MutableMap<K,V>
val mapA = mutableMapOf("a" to 1, "b" to 2)
val mapB = mutableMapOf("a" to 1, "b" to 2)

println(mapA.keys)
println(mapB.values)

println("equals: ${mapA == mapB}") // true

mapA["a"] = 9
println("equals: ${mapA == mapB}") // false

mapB.remove("a")
println("equals: ${mapA == mapB}") // false
반응형
반응형

1. Nullable, Non Nullable

코틀린의 변수는 기본적으로 Non Nullable하다

 

하지만 필요에 따라 null을 허용할 수 있다

프로퍼티 선언시 타입의 끝에 ?를 붙여주면 null을 허용할 수 있다

(1)
var name: String? = null
var length: Int? = name?.length

(2)
var name2: String? = "kim"
var length2: Int = name2!!.length

(3)
var name3: String? = null
var length3: Int = name3?.length ?: 0
  1. name이 null을 허용하고 있다 ?(Safe Call Operator)를 통해 name이 null인 경우 null을 반환하고, null이 아닌 경우 name의 length를 반환한다
  2. name2는 null을 허용하지만 “kim”이라는 값이 할당되어 있다
    이때 name2는 non null을 보장하지 않으므로 ??:를 활용해서 사용해야하지만, 값이 있음을 보장할 수 있다면
    !!(Non-null Assertion Operator)를 통해 개발자가 null이 아님을 보장하고 사용할 수 있다
    단, 이 경우 null이면 NullPointerException이 발생한다

  3. ?:(Elvis Operator)를 통해 null인 경우 반환할 값을 지정할 수 있다
반응형
반응형

1. enum

enum class DevelopCategory(val code: String) {
	DEVELOPING("dev") {
		override fun apply() {
			println("develop")
		}
	},
	DEPLOY("dpy") {
		override fun apply() {
			println("deploy")
		}
	},
	PRODUCTION("prd") {
		override fun apply() {
			println("production")
		}
	};
	
	abstract fun apply()
    
    companion object {
        fun fromCode(code: String): DevelopCategory? =
            values().find { it.code == code }
    }
}

상수 값을 저장할 공간을 별도로 선언하지 않아도 되며, 추상 함수가 존재하는 경우 이에 대한 구현을 강제한다

 

enum, when 예제

fun test(category: DevelopCategory) {
	
	when (category) {
		DevelopCategory.DEVELOPING -> println("develop")
		DevelopCategory.DEPLOY -> println("deploy")
		DevelopCategory.PRODUCTION -> println("production")
	}
	
	category.apply()
	
	println("${category.code}")
	println(DevelopCategory.fromCode(category.code))
}

 

반응형
반응형

1. Type Checking, Casting

코틀린은 강타입 언어로 변수의 타입을 정확하게 지정해야 한다

이를 위해 타입 안전성을 보장하기 위한 타입 체크와 타입 캐스팅의 다양한 방법이 존재한다

is

  • 객체가 특정 타입인지 확인하는데 사용
  • is를 통한 검사에서 타입이 맞다면, 자동으로 그 타입으로 간주(smart casting)

as

  • 명시적 캐스팅으로 타입이 맞지 않으면 예외를 던진다

as?

  • 안전한 캐스팅으로 타입이 맞지 않으면 null을 반환한다

타임 체크 및 캐스팅에 대한 예제

fun test(obj: Any) {
    
    if (obj is String) {
	    print("String 임!")
    } else {
    
	    val t1 = obj as? Int
	    if (t1 != null) {
		    print("Int 임!")
	    }
	    
	    when (obj) {
		    is Person -> println("Person 임!"
			  else -> println("else 임!")
	    }
	    
	    val t2 = obj as Double // 캐스팅 실패시 exception 던짐
    }
}

 

반응형

+ Recent posts