코딩학습을 하는 여러 방법이 있을 수 있겠다.

  1. 책 하나를 두고 학습한다.
  2. 강의를 하나 두고 학습힌다.

이러한 것이 내가 기존에 C/C++/Java/Python을 학습했던 것들이다. LLM이 생겨나면서 새로운 방법도 생겼다고도 할 수 있겠다.

  • 기존에 해봤던 것 혹은 풀어야 하는 문제를 새로운 언어를 가지고 LLM으로 해본다.
  • 그 때 나왔던 코드를 이해하면서, 언어의 새로운 부분을 배워 본다.

이렇게 접근하는 방법이 효과적인 이유는 내가 알고 있던 분야에 대한 코드가 어떻가 다르게 나오는지 알고, 또한 실재 활용하는 부분에 사용되기 때문에 더 와 닫는다. 위에 이야기한 기존의 방법은 바로 활용하지 못하는 부분도 많기 때문에 살펴 봤다고 해도 시간이 지나면 흐릿해 지기 쉽상이다.

 

다음은 이런 예로서, kotlin의 사용에 대해서 내가 새로운 것들을 정리해 본다. 막상 정리하면서, 슬쩍 다시 봤을 때, Android 관련 부분도 섞여 있다.

companion object

You said:

kotlin에서 companion object에 대해서 설명해줘

ChatGPT said:

Kotlin에서 companion object란?

companion object클래스 내부에 정의되는 싱글턴 객체로, 해당 클래스의 정적 멤버처럼 동작하는 개념입니다. Kotlin에는 Java의 static 키워드가 없기 때문에 이를 대체하는 방식으로 companion object를 사용합니다.

1. 기본 사용법

class MyClass {
    companion object {
        val message = "Hello, Companion Object!"

        fun sayHello() {
            println(message)
        }
    }
}

fun main() {
    // 인스턴스 생성 없이 호출 가능
    println(MyClass.message)
    MyClass.sayHello()
}

실행 결과:

Hello, Companion Object!
  • companion object 내부의 멤버(messagesayHello())는 클래스 이름을 통해 직접 접근 가능합니다.
  • Java에서 static 변수/메서드를 호출하는 것과 같은 방식으로 사용할 수 있습니다.

2. Companion Object에 이름 지정

companion object는 기본적으로 이름 없이 선언되지만, 필요하면 이름을 지정할 수도 있습니다.

class MyClass {
    companion object Factory {
        fun create(): MyClass {
            return MyClass()
        }
    }
}

fun main() {
    val instance = MyClass.create()
    println(instance)
}
  • 이름을 지정해도 companion object는 여전히 클래스 이름을 통해 접근할 수 있습니다.

3. Companion Object의 특징

(1) 클래스 당 하나만 존재

class A {
    companion object { }
}

class B {
    companion object { }
}
  • 한 클래스에는 하나의 companion object 만 가질 수 있습니다.

(2) 인터페이스 구현 가능

interface Printer {
    fun printMessage()
}

class MyClass {
    companion object : Printer {
        override fun printMessage() {
            println("Companion Object is implementing an interface!")
        }
    }
}

fun main() {
    MyClass.printMessage()
}
  • companion object는 일반적인 객체처럼 인터페이스를 구현할 수 있음.

(3) 실제 객체 (Singleton)

class MyClass {
    companion object {
        init {
            println("Companion Object Initialized!")
        }
    }
}

fun main() {
    println("Before accessing companion object")
    MyClass // 접근 시점에서 Companion Object 초기화됨
}
  • companion object클래스가 로드될 때 즉시 생성되지 않으며, 최초 접근 시점에 초기화됨.

4. Java와 연동

Java에서 companion object의 멤버를 사용하려면 정적 메서드나 필드처럼 접근할 수 있도록 변환해야 합니다.

(1) 기본 접근 방식

class MyClass {
    companion object {
        @JvmStatic
        fun staticMethod() {
            println("Called from Java!")
        }
    }
}

Java에서 호출:

public class Main {
    public static void main(String[] args) {
        MyClass.Companion.staticMethod();  // 기본 접근 방식
    }
}

(2) @JvmStatic을 사용한 정적 메서드 변환

class MyClass {
    companion object {
        @JvmStatic
        fun staticMethod() {
            println("Called from Java with @JvmStatic!")
        }
    }
}

Java에서 호출:

public class Main {
    public static void main(String[] args) {
        MyClass.staticMethod();  // @JvmStatic 덕분에 바로 접근 가능!
    }
}

public class Main { public static void main(String[] args) { MyClass.staticMethod(); // @JvmStatic 덕분에 바로 접근 가능! } }

  • @JvmStatic을 붙이면 Kotlin의 Companion Object 내부 메서드가 Java의 정적 메서드처럼 사용 가능.

5. Companion Object vs Object (싱글턴과의 차이)

object 키워드를 사용하면 싱글턴 패턴을 쉽게 만들 수 있습니다.

object Singleton {
    val message = "I am a Singleton"
}

fun main() {
    println(Singleton.message) // 인스턴스 없이 바로 사용 가능
}
  • companion object는 특정 클래스에 속하지만,
  • object 키워드는 독립적인 싱글턴 객체를 생성하는 차이점이 있습니다.

6. Companion Object의 활용 사례

(1) Factory Pattern 구현

class Car(val model: String) {
    companion object {
        fun create(model: String): Car {
            return Car(model)
        }
    }
}

fun main() {
    val car = Car.create("Tesla Model S")
    println(car.model)
}

(2) 상수 값 정의 (const val 불가)

Kotlin의 const val은 최상위 또는 object에서만 사용 가능하므로, companion object에서는 val로 선언.

class Constants {
    companion object {
        val API_URL = "https://api.example.com"
    }
}

fun main() {
    println(Constants.API_URL)
}

class Constants { companion object { val API_URL = "https://api.example.com" } } fun main() { println(Constants.API_URL) }


결론

  • companion object클래스 내부에서 정적 멤버처럼 동작하는 객체.
  • 한 클래스당 하나만 존재 가능.
  • 인터페이스를 구현할 수 있으며, 최초 접근 시 초기화됨.
  • Java와 연동할 때 @JvmStatic을 사용하면 더 자연스럽게 호출 가능.
  • 싱글턴 패턴, Factory Pattern, 상수 관리 등에 활용할 수 있음.

companion object는 Kotlin에서 static을 대체하는 강력한 기능이므로, 잘 활용하면 효율적인 코드 작성이 가능합니다!

mutableMapOf 키워드

You said:

mutableMapOf 키워드에 대해서 설명해줘.

ChatGPT said:

mutableMapOf 키워드란?

mutableMapOfKotlin에서 변경 가능한(Mutable) Map을 생성하는 함수입니다.
Map은 키(key)와 값(value)의 쌍으로 이루어진 컬렉션이며, mutableMapOf를 사용하면 실행 중에 데이터를 추가, 수정, 삭제할 수 있습니다.


1. 기본 사용법

val myMap = mutableMapOf("A" to 1, "B" to 2, "C" to 3)

println(myMap) // {A=1, B=2, C=3}
  • "A" to 1 같은 표현을 사용하여 키-값 쌍을 정의할 수 있습니다.
  • mutableMapOf()를 사용하면, 이후에 데이터를 수정할 수 있음.

2. 값 추가 (put[] 연산자)

val myMap = mutableMapOf("A" to 1, "B" to 2)

// 값 추가
myMap["C"] = 3   // 방법 1: 대괄호 사용
myMap.put("D", 4) // 방법 2: put() 사용

println(myMap) // {A=1, B=2, C=3, D=4}
  • myMap["C"] = 3 또는 myMap.put("D", 4)을 사용하여 새로운 키-값 추가 가능.

3. 값 수정

val myMap = mutableMapOf("A" to 1, "B" to 2)

myMap["A"] = 10   // 방법 1: 대괄호 사용
myMap.put("B", 20) // 방법 2: put() 사용

println(myMap) // {A=10, B=20}
  • 기존 키에 대해 myMap["A"] = 10을 사용하면 값이 업데이트됨.

4. 값 삭제 (remove)

val myMap = mutableMapOf("A" to 1, "B" to 2, "C" to 3)

// 특정 키 삭제
myMap.remove("A") 

println(myMap) // {B=2, C=3}

val myMap = mutableMapOf("A" to 1, "B" to 2, "C" to 3) // 특정 키 삭제 myMap.remove("A") println(myMap) // {B=2, C=3}

  • remove(key)를 사용하여 특정 키를 삭제 가능.

5. 특정 키 또는 값이 존재하는지 확인 (containsKey, containsValue, in 연산자)

val myMap = mutableMapOf("A" to 1, "B" to 2)

println(myMap.containsKey("A")) // true
println(myMap.containsValue(2)) // true
println("C" in myMap) // false
  • containsKey(key), containsValue(value), "key" in map을 사용하여 존재 여부 확인 가능.

6. 전체 반복 (forEach, for 루프)

val myMap = mutableMapOf("A" to 1, "B" to 2, "C" to 3)

// 방법 1: forEach 사용
myMap.forEach { (key, value) ->
    println("$key -> $value")
}

// 방법 2: for 루프 사용
for ((key, value) in myMap) {
    println("$key -> $value")
}

출력:

A -> 1
B -> 2
C -> 3

7. 크기 확인 (size)

val myMap = mutableMapOf("A" to 1, "B" to 2)

println(myMap.size) // 2

val myMap = mutableMapOf("A" to 1, "B" to 2) println(myMap.size) // 2

  • size 속성을 사용하면 Map에 저장된 키-값 개수를 확인 가능.

8. 모든 키(keys), 모든 값(values) 가져오기

val myMap = mutableMapOf("A" to 1, "B" to 2, "C" to 3)

println(myMap.keys)   // [A, B, C]
println(myMap.values) // [1, 2, 3]
  • keys: 모든 키를 반환 (Set<String>)
  • values: 모든 값을 반환 (Collection<Int>)

9. 초기화 없이 빈 Map 만들기

val emptyMap = mutableMapOf<String, Int>() // 빈 MutableMap 생성

println(emptyMap) // {}
  • mutableMapOf<String, Int>()초기 데이터 없이 빈 Map을 생성.

10. 불변 Map (mapOf) vs. 변경 가능 Map (mutableMapOf)

메서드 변경 가능 여부 예제
mapOf ❌ (불변) val map = mapOf("A" to 1)
mutableMapOf ✅ (변경 가능) val map = mutableMapOf("A" to 1)
val immutableMap = mapOf("A" to 1)
// immutableMap["B"] = 2 // 오류 발생!

val mutableMap = mutableMapOf("A" to 1)
mutableMap["B"] = 2 // 정상 동작

11. 정리

  • mutableMapOf()변경 가능한 Map 객체를 생성하는 함수.
  • put(), []= 연산자로 값을 추가/수정 가능.
  • remove(key)로 값 삭제 가능.
  • forEach 또는 for ((key, value) in map) 형태로 반복 가능.
  • mapOf()는 불변 Map을 생성하지만, mutableMapOf()는 값 변경 가능.

실제 프로젝트에서 동적으로 데이터를 저장, 수정, 삭제하는 경우 mutableMapOf()를 적극 활용할 수 있습니다!

MutableLiveData

You said:

MutableLiveData에 대해서도 설명해줘

ChatGPT said:

MutableLiveData란?

MutableLiveDataAndroid의 LiveData를 상속한 변경 가능한(mutable) 데이터 홀더입니다.
LiveData는 관찰 가능한 데이터 컨테이너로, 데이터 변경 시 자동으로 UI에 반영되는 기능을 제공합니다.
특히 MutableLiveData는 LiveData와 다르게 값을 변경(set/update)할 수 있는 기능을 추가로 제공합니다.

1. MutableLiveData 기본 사용법

val liveData = MutableLiveData<String>() // MutableLiveData 생성

// 값 설정
liveData.value = "Hello, LiveData!"
  • MutableLiveData<String>(): 문자열 값을 저장할 수 있는 변경 가능한 LiveData 생성.
  • liveData.value = "Hello": LiveData의 값을 변경.

2. MutableLiveData vs LiveData

LiveData MutableLiveData
읽기 전용 (value만 제공) 값 변경 가능 (value, postValue 사용 가능)
UI 업데이트에 사용 ViewModel 내부에서 값 변경 가능
LiveData<T> 형태로 선언 MutableLiveData<T> 형태로 선언
// ViewModel 내부에서
private val _text = MutableLiveData<String>() // 변경 가능
val text: LiveData<String> = _text // 외부에서는 변경 불가능

fun updateText(newText: String) {
    _text.value = newText // 내부에서만 변경 가능
}
  • _textMutableLiveData로 변경 가능하지만,
    textLiveData로 감싸서 외부에서는 읽기만 가능하게 함.

3. MutableLiveData 값 변경 방법

(1) value로 직접 변경 (Main Thread)

liveData.value = "New Value"
  • value 속성에 값을 직접 할당하면 즉시 UI에 반영됨.
  • Main Thread에서만 사용 가능.

(2) postValue()로 비동기 변경 (Background Thread)

liveData.postValue("New Value")
  • 백그라운드 스레드에서 값 변경 가능.
  • UI 업데이트는 자동으로 메인 스레드에서 처리됨.

postValue()는 여러 번 호출되면 마지막 값만 반영될 수 있음.

4. MutableLiveData를 활용한 ViewModel 예제

(1) ViewModel에서 데이터 관리

class MyViewModel : ViewModel() {
    val liveData = MutableLiveData<String>()

    fun updateText() {
        liveData.value = "Updated Text!"
    }
}

(2) Activity에서 관찰 (Observer)

class MainActivity : AppCompatActivity() {
    private lateinit var viewModel: MyViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        viewModel = ViewModelProvider(this).get(MyViewModel::class.java)

        // LiveData 관찰
        viewModel.liveData.observe(this, Observer { newValue ->
            textView.text = newValue
        })

        // 버튼 클릭 시 값 변경
        button.setOnClickListener {
            viewModel.updateText()
        }
    }
}
  • observe(this, Observer { ... })를 사용하면 값이 변경될 때 자동으로 UI 업데이트됨.
  • 버튼 클릭 시 updateText()가 호출되어 LiveData 값이 변경됨.

5. MutableLiveData의 활용 사례

(1) API 호출 후 데이터 업데이트

class MyViewModel : ViewModel() {
    val userData = MutableLiveData<User>()

    fun fetchUser() {
        viewModelScope.launch {
            val user = api.getUserData() // API 호출
            userData.postValue(user) // 비동기 업데이트
        }
    }
}
  • API 응답 데이터를 LiveData로 저장하고 UI에 반영.

(2) RecyclerView 데이터 관리

class MyViewModel : ViewModel() {
    val items = MutableLiveData<List<Item>>(mutableListOf())

    fun addItem(newItem: Item) {
        val updatedList = items.value?.toMutableList() ?: mutableListOf()
        updatedList.add(newItem)
        items.value = updatedList
    }
}
  • MutableLiveData<List<Item>>를 사용하여 RecyclerView의 데이터 변경을 자동으로 감지.

6. MutableLiveData와 Flow/StateFlow 비교

Feature MutableLiveData StateFlow
라이프사이클 인식 O (LifecycleOwner 필요) X
초기값 필요 X O
Main Thread 값 변경 value 사용 value 사용
Background Thread 값 변경 postValue() 사용 value 사용
데이터 캐싱 O (마지막 값 유지) O
콜드 스트림(구독 시 최신 값만 받음) O X (구독 시 최신 값과 변동 값 받음)

StateFlowLiveData보다 비동기 흐름을 더 효과적으로 처리할 수 있지만, UI 수명 주기와 직접 연동되지 않음.

7. MutableLiveData 사용 시 주의할 점

  1. UI에서 직접 값 변경하지 말 것
viewModel.text.value = "New Value" // ViewModel 외부에서 값 변경 ❌
- ViewModel 내부에서만 값 변경하도록 설계해야 함.
  1. 비동기 작업에서는 postValue() 사용
viewModelScope.launch {
    delay(1000)
    liveData.postValue("New Value")
}
- **백그라운드 스레드에서 값 변경할 때 `postValue()` 필수**.    

8. 결론

  • MutableLiveData변경 가능한 LiveData로, UI와 데이터를 쉽게 연결하는 기능을 제공.
  • valueMain Thread에서 즉시 변경, postValue()비동기 변경 가능.
  • ViewModel에서 데이터를 관리하고, Activity/Fragment에서 observe()를 통해 변경을 감지.
  • API 응답 처리, RecyclerView 업데이트, 상태 관리 등에 유용하게 사용됨.

✅ 언제 사용할까?

  • UI와 데이터의 자동 동기화가 필요할 때
  • 비동기 데이터 변경을 관리해야 할 때
  • LiveData 기반 MVVM 아키텍처를 사용할 때

mapValues

You said:

변경가능한 map 객체에서 mapValues에 대해서 설명해줘.

ChatGPT said:

mapValues란?

mapValuesKotlin의 Map에서 값을 변환할 때 사용하는 함수입니다.
이 함수는 기존 Map의 키(key)는 유지하면서 값(value)만 변경한 새로운 Map을 반환합니다.

⚠️ mapValues불변 Map을 반환하므로, 원본 MutableMap을 변경하지 않습니다.

1. mapValues 기본 사용법

val myMap = mutableMapOf("A" to 1, "B" to 2, "C" to 3)

// mapValues를 사용하여 값(value)만 10배 증가
val newMap = myMap.mapValues { it.value * 10 }

println(newMap) // {A=10, B=20, C=30}
println(myMap)  // {A=1, B=2, C=3} (원본 변경 X)
  • mapValues { it.value * 10 }: 각 값에 연산을 적용하여 새로운 Map을 생성.
  • 원본 myMap은 변경되지 않으며, 새로운 Map이 생성됨.

2. MutableMap에서 mapValues 사용

mutableMapOf()로 만든 MutableMap에서도 mapValues를 사용할 수 있지만,
mapValues는 새로운 불변 Map을 반환하므로, 기존 MutableMap에 반영되지 않습니다.

val mutableMap = mutableMapOf("A" to 1, "B" to 2, "C" to 3)

// mapValues 적용 후 새로운 Map을 mutableMap에 덮어씌우기
mutableMap = mutableMap.mapValues { it.value * 2 }.toMutableMap()

println(mutableMap) // {A=2, B=4, C=6}
  • toMutableMap()을 사용하면 다시 MutableMap으로 변환하여 수정 가능.

3. mapValuesmap의 차이

🔹 mapValues

  • 키(key)는 유지하고 값(value)만 변경.
  • 결과는 새로운 Map이지만, 원본 구조는 유지됨.
val original = mapOf("A" to 1, "B" to 2, "C" to 3)
val result = original.mapValues { it.value * 10 }

println(result) // {A=10, B=20, C=30}

val original = mapOf("A" to 1, "B" to 2, "C" to 3) val result = original.mapValues { it.value * 10 } println(result) // {A=10, B=20, C=30}

🔹 map

  • map을 사용하면 List<Pair<K, V>> 형태가 반환됨.
  • Map이 아닌 List 형태가 되므로, 다시 toMap()을 사용해야 함.
val original = mapOf("A" to 1, "B" to 2, "C" to 3)
val result = original.map { it.key to it.value * 10 }.toMap()

println(result) // {A=10, B=20, C=30}

mapValues는 원래의 Map 구조를 유지하지만, mapList<Pair<K, V>>를 반환하기 때문에 변환 과정이 필요함.

4. mapValues의 고급 사용법

(1) 값이 특정 조건을 만족할 때만 변경

val scores = mutableMapOf("Alice" to 50, "Bob" to 90, "Charlie" to 80)

// 점수가 60 미만이면 "Fail"로 변경
val result = scores.mapValues { if (it.value < 60) "Fail" else "Pass" }

println(result) // {Alice=Fail, Bob=Pass, Charlie=Pass}

(2) 원본 MutableMap을 변경하려면 toMutableMap() 사용

val scores = mutableMapOf("Alice" to 50, "Bob" to 90, "Charlie" to 80)

scores = scores.mapValues { it.value + 10 }.toMutableMap()

println(scores) // {Alice=60, Bob=100, Charlie=90}

5. mapValues 사용 시 주의할 점

  • mapValues새로운 Map을 반환하며, 원본을 직접 변경하지 않음.
  • MutableMap에서 mapValues를 사용하면, 원본을 유지한 채 새로운 불변 Map이 생성됨.
  • 원본을 변경하려면 toMutableMap()을 사용하여 다시 MutableMap으로 변환해야 함.

6. 결론

  • mapValues키는 그대로 유지하고, 값만 변환하여 새로운 Map을 생성.
  • MutableMap에서도 사용할 수 있지만, 결과는 불변 Map이므로 주의.
  • 원본을 유지하면서 값을 변경하려면 mapValues 사용, 원본을 변경하려면 toMutableMap() 활용.
  • mapValues원본 Map을 유지하면서 가공된 데이터가 필요할 때 유용.

mapValues를 사용할 때:

값만 변경해야 할 때
Map의 구조를 유지하면서 변형해야 할 때
원본을 변경하지 않고 새로운 Map을 만들고 싶을 때

? Elvis 연산자자

You said:

kotlin에서 ?: 연산자에 대해서 알려줘

ChatGPT said:

?: (Elvis 연산자)란?

Kotlin에서 ?: 연산자는 Elvis 연산자(Elvis Operator) 라고 하며,
nullable 변수의 값이 null일 경우 기본값을 제공하는 연산자입니다.

1. 기본 사용법

val name: String? = null

val result = name ?: "Default Name"

println(result) // "Default Name"
  • namenull이면 "Default Name"이 할당됨.
  • namenull이 아니면 name의 원래 값이 할당됨.

2. Elvis 연산자의 동작 방식

val value: Int? = null

// value가 null이면 10을 사용
val result = value ?: 10 

println(result) // 10
  • 왼쪽 값이 null이면 오른쪽 값(10)이 사용됨.
  • 왼쪽 값이 null이 아니면 그대로 사용.

3. ?:와 함수 활용

fun getLength(str: String?): Int {
    return str?.length ?: 0
}

println(getLength("Kotlin")) // 6
println(getLength(null)) // 0
  • str?.length에서 null이면 0을 반환.

4. ?:throw 또는 return 함께 사용

?:를 활용하여 null일 경우 예외를 던지거나, 즉시 return할 수도 있음.

(1) throw와 함께 사용 (예외 발생)

val name: String? = null

val result = name ?: throw IllegalArgumentException("Name cannot be null")

println(result) // 예외 발생: IllegalArgumentException: Name cannot be null
  • namenull이면 즉시 예외 발생.

(2) return과 함께 사용 (조기 반환)

fun getUserName(name: String?): String {
    return name ?: return "Unknown"
}

println(getUserName("Alice")) // Alice
println(getUserName(null)) // Unknown
  • namenull이면 "Unknown"을 즉시 반환.

5. ?:let을 조합하여 안전한 처리

val text: String? = null

text?.let {
    println("Text: $it")
} ?: println("Text is null")
  • let 내부는 null이 아닐 때만 실행됨
  • null일 경우 ?: 연산자의 우측 코드가 실행됨.

출력 결과:

Text is null

6. ?:를 활용한 기본값 설정

fun getUserInput(input: String?): String {
    return input ?: "No input provided"
}

println(getUserInput("Hello")) // Hello
println(getUserInput(null)) // No input provided
  • 사용자 입력이 null이면 "No input provided"를 기본값으로 사용.

7. ?: vs if-else

?: 연산자는 if-else를 간결하게 줄일 수 있음.

// if-else 방식
val result = if (name != null) name else "Default"

// ?: 연산자 사용
val result = name ?: "Default"
  • if-else보다 간결하고 가독성이 좋음.

8. 결론

  • ?: (Elvis 연산자)는 null인 경우 기본값을 제공하는 간단하고 강력한 연산자.
  • ?:를 사용하면 if-else 없이 null 처리 가능.
  • throw, return, let과 조합하여 안전한 null 처리가 가능.
  • Kotlin에서 null을 다룰 때 매우 유용한 연산자!

nullable 타입

You said:

val value: Int? = null 이 문장에 대해서 설명해줘 ?가 있는 경우와 없는 경우에 차이에 대해서 설명해줘

ChatGPT said:

val value: Int? = null 문장 설명

이 문장에서 ?nullable 타입을 의미합니다.
즉, value 변수는 Int 값을 가질 수도 있고, null을 가질 수도 있습니다.

val value: Int? = null
  • value의 타입은 Int?, 즉 nullable Int.
  • valuenull을 가질 수 있음.

?가 있는 경우 (Int?)

val value: Int? = null  // null 허용
println(value) // 출력: null

val value2: Int? = 10  // 정상적인 Int 값 가능
println(value2) // 출력: 10
  • valuenull을 가질 수 있음 (null 가능).
  • value를 사용할 때 ?.(safe call)이나 ?:(Elvis 연산자) 등을 활용하여 null을 처리해야 함.

?. (safe call) 사용 예제

val value: Int? = null
println(value?.plus(10)) // null (null인 경우 연산 수행 X)
  • ?.plus(10): valuenull이면 아무 동작 없이 null 반환.

?가 없는 경우 (Int)

val value: Int = null // 오류 발생
  • Int는 기본적으로 nullable이 아님.
  • null을 넣으면 컴파일 오류 발생.
val value: Int = 10 // 정상 작동
println(value) // 10
  • Int 변수에는 null을 할당할 수 없음.
  • 항상 값이 존재해야 함.

차이점 요약

선언 방식 null 허용 여부 사용 방식
val value: Int? ✅ (허용) ?., ?: 사용 필요
val value: Int ❌ (불가) 항상 값이 존재해야 함

언제 Int?를 사용할까?

  • 데이터가 null일 가능성이 있는 경우
    (예: API 응답, 데이터베이스 조회 결과, 사용자가 입력하지 않은 값)
  • null을 기본값으로 처리하고 싶을 때
fun getAge(): Int? {
    return null // 나이를 모를 수도 있음
}

val age = getAge() ?: 0 // null이면 0을 기본값으로 설정
println(age) // 0

반면, Int는 null이 허용되지 않아 항상 값이 있어야 하는 경우 사용합니다.

lifecycleScope

You said:

android에서 lifecycleScope에 대해서 설명해줘.

ChatGPT said:

lifecycleScope란?

lifecycleScopeAndroid에서 LifecycleOwner(예: Activity, Fragment)의 생명주기에 맞춰 실행되는 코루틴 스코프입니다.
이를 사용하면 Activity 또는 Fragment가 종료될 때 자동으로 코루틴이 취소되므로, 메모리 누수를 방지할 수 있습니다.

1. lifecycleScope 기본 사용법

lifecycleScopeActivity나 Fragment 내부에서 사용할 수 있는 CoroutineScope입니다.

import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.launch

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // lifecycleScope 사용
        lifecycleScope.launch {
            // 백그라운드 작업 수행
            val result = fetchData()
            println(result) // 데이터 출력
        }
    }

    suspend fun fetchData(): String {
        // 네트워크 요청 또는 데이터베이스 작업 (예제)
        return "Hello, World!"
    }
}

lifecycleScope.launch {} 의 의미

  • 코루틴이 onDestroy()에서 자동 취소됨메모리 누수 방지.
  • launch 내부에서 비동기 작업 실행 가능.
  • fetchData() 같은 suspend 함수 호출 가능.

2. lifecycleScope의 동작 방식

lifecycleScope의 코루틴은 LifecycleOwner(Activity, Fragment)의 생명주기에 따라 실행됩니다.

🔹 lifecycleScope의 생명주기

Lifecycle 상태 코루틴 실행 가능 여부 자동 취소 시점
onCreate ~ onDestroy ✅ 가능 onDestroy()에서 취소
onStart ~ onStop ✅ 가능 onStop()에서 취소 (when using launchWhenStarted)
onResume ~ onPause ✅ 가능 onPause()에서 취소 (when using launchWhenResumed)

3. launchWhenStartedlaunchWhenResumed

🔹 launchWhenStarted

  • onStart() 이후에 실행되며, onStop()에서 일시 중단됨.
lifecycleScope.launchWhenStarted {
    println("Started 상태에서 실행")
}
  • onStop()에서 일시 정지되며, 다시 onStart()가 호출되면 재개됨.
  • UI 업데이트가 필요한 네트워크 요청 등에 적합.

🔹 launchWhenResumed

  • onResume() 이후에 실행되며, onPause()에서 일시 중단됨.
lifecycleScope.launchWhenResumed {
    println("Resumed 상태에서 실행")
}
  • onPause()에서 일시 정지되며, 다시 onResume()이 호출되면 재개됨.
  • 화면이 보일 때만 필요한 작업(예: 애니메이션, 사용자 입력 감지)에 적합.

4. lifecycleScope vs GlobalScope vs viewModelScope

스코프 생명주기 사용 위치 특징
lifecycleScope onDestroy()에서 취소 Activity, Fragment 메모리 누수 방지, UI 관련 작업
GlobalScope 앱이 종료될 때까지 유지 어디서나 사용 가능 수동으로 취소 필요, 메모리 누수 위험
viewModelScope ViewModel이 삭제될 때 취소 ViewModel 내부 ViewModel에서 데이터 처리 시 사용

lifecycleScope는 UI와 관련된 작업을 안전하게 실행할 때 가장 적합합니다.

5. lifecycleScope 활용 예제

(1) 비동기 API 호출

lifecycleScope.launch {
    val data = fetchDataFromApi()
    textView.text = data // UI 업데이트
}
  • API 요청 후 데이터를 받아와서 UI 업데이트.

(2) 주기적인 작업 실행 (delay())

lifecycleScope.launch {
    while (true) {
        updateUI()
        delay(5000) // 5초마다 UI 업데이트
    }
}
  • Activity가 종료되면 자동 취소됨 → 메모리 누수 방지.

(3) launchWhenStarted로 UI 업데이트

lifecycleScope.launchWhenStarted {
    val userData = fetchUserData()
    textView.text = userData // UI 업데이트
}
  • onStart() 이후 실행되고, onStop()에서 일시 정지됨.

(4) 데이터베이스 작업

lifecycleScope.launch {
    val users = database.getAllUsers()
    updateUI(users)
}
  • Room 같은 데이터베이스에서 데이터를 가져와 UI 업데이트.

6. 결론

  • lifecycleScopeActivity 및 Fragment의 생명주기에 맞춰 코루틴을 실행하고 자동 취소함.
  • launchWhenStarted, launchWhenResumed를 사용하면 특정 생명주기에서만 실행 가능.
  • 메모리 누수를 방지하고, UI 관련 비동기 작업을 안전하게 실행할 수 있음.

lifecycleScope를 활용하면 Android에서 안전하고 효율적인 비동기 처리가 가능!

lifecycleScope와 LifecycleService 관계

You said:

lifecycleScope와 LifecycleService와의 관계에 대해서 설명해줘.

ChatGPT said:

lifecycleScopeLifecycleService의 관계

1. lifecycleScope란?

  • Activity, Fragment 등의 LifecycleOwner에서 사용할 수 있는 코루틴 스코프.
  • 생명주기(onDestroy())에 맞춰 자동으로 코루틴을 취소.
  • UI 관련 비동기 작업을 안전하게 수행할 때 사용.

2. LifecycleService란?

  • 일반적인 Service에는 생명주기(LifecycleOwner) 개념이 없음.
  • LifecycleServiceServiceLifecycleOwner로 만들어, 생명주기를 관리할 수 있도록 확장한 클래스.
  • 따라서 Activity처럼 lifecycleScope을 사용할 수 있음.

📌 LifecycleService에서 lifecycleScope 사용하기

🔹 일반 Service에서는 lifecycleScope 사용 불가능

기본 ServiceLifecycleOwner가 아니므로, lifecycleScope을 사용할 수 없음.

class MyService : Service() {
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        lifecycleScope.launch {  // ❌ 컴파일 오류 발생
            doWork()
        }
        return START_STICKY
    }
}
  • 일반 Service에서는 lifecycleScope을 사용할 수 없음.
  • 대신 GlobalScope 또는 CoroutineScope를 사용해야 하지만, 이는 메모리 누수 위험이 있음.

🔹 LifecycleService를 사용하면 lifecycleScope 사용 가능!

import android.app.Service
import android.content.Intent
import android.os.IBinder
import androidx.lifecycle.LifecycleService
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch

class MyLifecycleService : LifecycleService() {
    override fun onCreate() {
        super.onCreate()

        lifecycleScope.launch {
            while (true) {
                println("Service is running...")
                delay(5000) // 5초마다 실행
            }
        }
    }
}
  • LifecycleService를 사용하면 Activity처럼 lifecycleScope 사용 가능.
  • onDestroy()가 호출되면 코루틴이 자동으로 취소됨메모리 누수 방지.

📌 lifecycleScopeLifecycleService의 관계 정리

특징 일반 Service LifecycleService
lifecycleScope 사용 가능 여부 ❌ (불가능) ✅ (가능)
LifecycleOwner ❌ (아님) ✅ (맞음)
코루틴 자동 취소 ❌ (직접 관리해야 함) ✅ (자동 취소됨)
비동기 작업 수행 방식 GlobalScope 사용 (메모리 누수 위험) lifecycleScope 사용 (안전함)

즉, LifecycleService를 사용하면 lifecycleScope을 활용하여 안전하게 비동기 작업을 수행할 수 있음!

kotlin typeof 사용법법

You said:

kotlin에서 typeof 사용법

ChatGPT said:

Kotlin에서 typeof 사용법

Kotlin에는 typeof 연산자가 존재하지 않으며, 대신 ::class 또는 javaClass를 사용하여 타입을 확인할 수 있습니다.

✅ 1. ::class 사용 (KClass)

val number = 10
println(number::class) // 출력: class kotlin.Int
  • ::class를 사용하면 Kotlin의 KClass 객체를 얻을 수 있음.

✅ 2. javaClass 사용 (Class)

val text = "Hello"
println(text.javaClass) // 출력: class java.lang.String
  • javaClassJava의 Class 객체를 반환.

✅ 3. is 연산자 (instanceof 대체)

Kotlin에서 객체의 타입을 확인할 때 is 연산자를 사용합니다.

val data: Any = 42

if (data is Int) {
    println("data는 Int 타입입니다.") // 출력: data는 Int 타입입니다.
}
  • is 연산자는 런타임에서 타입을 체크하는 역할.

✅ 4. whenis 조합

fun checkType(value: Any) {
    when (value) {
        is Int -> println("정수입니다.")
        is String -> println("문자열입니다.")
        is Boolean -> println("Boolean 타입입니다.")
        else -> println("알 수 없는 타입입니다.")
    }
}

checkType(10)        // 정수입니다.
checkType("Kotlin")  // 문자열입니다.
checkType(true)      // Boolean 타입입니다.
  • whenis를 조합하면 타입별 로직을 쉽게 처리 가능.

✅ 5. 제네릭에서 타입 확인 (reified 키워드 사용)

Kotlin의 제네릭(Generic)은 타입 소거(type erasure)로 인해 런타임에서 타입을 확인할 수 없음.
그러나 inlinereified를 사용하면 제네릭 타입 정보를 유지할 수 있음.

inline fun <reified T> checkType(value: Any) {
    if (value is T) {
        println("값이 ${T::class.simpleName} 타입입니다.")
    } else {
        println("타입이 다릅니다.")
    }
}

checkType<Int>(42)       // 값이 Int 타입입니다.
checkType<String>(42)    // 타입이 다릅니다.
  • reified가 없으면 컴파일 시 T 타입 정보가 사라짐 → 런타임에서 타입 체크 불가능.
  • reified를 사용하면 T::class제네릭 타입을 유지한 채 타입 체크 가능.

✅ 6. KClassClass 차이

방식 반환 타입 특징
::class KClass<T> Kotlin 타입 시스템
javaClass Class<T> Java 클래스 객체
val number = 100

println(number::class)       // class kotlin.Int
println(number.javaClass)    // class java.lang.Integer

✅ 결론

  • is → 런타임에서 타입 체크 (is Int, is String)
  • ::classKClass 타입 확인 (value::class)
  • javaClassClass 타입 확인 (value.javaClass)
  • when + is → 여러 타입 분기 처리
  • 제네릭에서는 reified 필요T::class 사용 가능

Kotlin에서 typeof는 없지만, ::class, is, reified 등을 활용하면 동일한 기능을 구현할 수 있음!

collection 확인

You said:

변수가 collection인지 다른 것인지 kotlin에서 확인하는 방법 알려줘

ChatGPT said:

✅ Kotlin에서 변수가 Collection인지 확인하는 방법

Kotlin에서 변수가 Collection(리스트, 맵, 셋 등)인지 확인하려면 is 연산자 또는 isInstance()를 사용할 수 있습니다.

1. is 연산자로 확인

Kotlin에서는 is 키워드를 사용하여 특정 타입인지 확인할 수 있습니다.

fun checkCollection(obj: Any) {
    when (obj) {
        is Collection<*> -> println("이 변수는 Collection 입니다.")
        else -> println("이 변수는 Collection이 아닙니다.")
    }
}

val list = listOf(1, 2, 3)
val map = mapOf("A" to 1, "B" to 2)
val set = setOf(1, 2, 3)
val text = "Hello"

checkCollection(list)  // 출력: 이 변수는 Collection 입니다.
checkCollection(map)   // 출력: 이 변수는 Collection이 아닙니다.
checkCollection(set)   // 출력: 이 변수는 Collection 입니다.
checkCollection(text)  // 출력: 이 변수는 Collection이 아닙니다.

🔹 is Collection<*>List, Set모든 Collection 타입을 포함하지만, Map은 포함되지 않음.


2. Map인지도 확인하기

MapCollection의 하위 타입이 아니므로, 따로 검사해야 합니다.

fun checkType(obj: Any) {
    when (obj) {
        is List<*> -> println("이 변수는 List 입니다.")
        is Set<*> -> println("이 변수는 Set 입니다.")
        is Map<*, *> -> println("이 변수는 Map 입니다.")
        is Collection<*> -> println("이 변수는 Collection 입니다.")
        else -> println("이 변수는 Collection이 아닙니다.")
    }
}

val list = listOf(1, 2, 3)
val set = setOf(1, 2, 3)
val map = mapOf("A" to 1, "B" to 2)
val text = "Hello"

checkType(list)  // 출력: 이 변수는 List 입니다.
checkType(set)   // 출력: 이 변수는 Set 입니다.
checkType(map)   // 출력: 이 변수는 Map 입니다.
checkType(text)  // 출력: 이 변수는 Collection이 아닙니다.

🔹 MapCollection이 아니므로, 별도로 is Map<*, *>으로 체크해야 함.

3. isInstance() 사용

import kotlin.reflect.full.isInstance

fun main() {
    val list = listOf(1, 2, 3)

    if (Collection::class.isInstance(list)) {
        println("list는 Collection 입니다.") // 출력
    }
}

🔹 Collection::class.isInstance(obj)리플렉션을 사용하여 타입 체크.

🔥 결론

방법 Collection 확인 Map 확인
is Collection<*> ✅ 가능 ❌ 불가능
is List<*>, is Set<*> ✅ 가능 ❌ 불가능
is Map<*, *> ❌ 불가능 ✅ 가능
Collection::class.isInstance(obj) ✅ 가능 ❌ 불가능

is Collection<*>is Map<*, *>을 조합해서 사용하면 안전하게 확인 가능!

type 출력 방법법

You said:

일반적으로 변수의 타입이나 특징을 확인하고 print로 출력하는 코드를 알려줘

ChatGPT said:

✅ 변수의 타입이나 특징을 확인하고 print로 출력하는 코드

Kotlin에서는 ::class, javaClass, is 연산자 등을 사용하여 변수의 타입을 확인할 수 있습니다.


1️⃣ 기본적인 타입 확인 및 출력

fun printType(value: Any) {
    println("값: $value, 타입: ${value::class.simpleName}")
}

fun main() {
    printType(42)         // 값: 42, 타입: Int
    printType(3.14)       // 값: 3.14, 타입: Double
    printType("Hello")    // 값: Hello, 타입: String
    printType(true)       // 값: true, 타입: Boolean
}
  • value::class.simpleNameKotlin 타입 출력

2️⃣ 자세한 타입 정보 (javaClass)

fun printDetailedType(value: Any) {
    println("값: $value, Kotlin 타입: ${value::class}, Java 타입: ${value.javaClass}")
}

fun main() {
    printDetailedType(42)          // Kotlin 타입: class kotlin.Int, Java 타입: class java.lang.Integer
    printDetailedType("Hello")     // Kotlin 타입: class kotlin.String, Java 타입: class java.lang.String
    printDetailedType(listOf(1,2)) // Kotlin 타입: class kotlin.collections.ArrayList, Java 타입: class java.util.ArrayList
}
  • value.javaClassJava의 Class 정보 출력
  • value::classKotlin의 KClass 정보 출력

3️⃣ 변수가 Collection인지 확인

fun printCollectionType(value: Any) {
    when (value) {
        is List<*> -> println("List 타입: ${value::class.simpleName}, 크기: ${value.size}")
        is Set<*> -> println("Set 타입: ${value::class.simpleName}, 크기: ${value.size}")
        is Map<*, *> -> println("Map 타입: ${value::class.simpleName}, 크기: ${value.size}")
        is Collection<*> -> println("Collection 타입: ${value::class.simpleName}, 크기: ${value.size}")
        else -> println("이 변수는 Collection이 아닙니다.")
    }
}

fun main() {
    printCollectionType(listOf(1, 2, 3))  // List 타입: ArrayList, 크기: 3
    printCollectionType(setOf("A", "B"))  // Set 타입: LinkedHashSet, 크기: 2
    printCollectionType(mapOf(1 to "One", 2 to "Two"))  // Map 타입: LinkedHashMap, 크기: 2
    printCollectionType("Hello")  // 이 변수는 Collection이 아닙니다.
}
  • is List<*>, is Set<*>, is Map<*, *>각 컬렉션 타입을 구분
  • is Collection<*>모든 컬렉션(List, Set) 확인 가능 (Map 제외)

4️⃣ 변수가 null인지, 기본 타입인지 확인

fun printVariableInfo(value: Any?) {
    when {
        value == null -> println("이 변수는 null 입니다.")
        value is Number -> println("숫자 타입: ${value::class.simpleName}, 값: $value")
        value is String -> println("문자열 타입: ${value::class.simpleName}, 길이: ${value.length}")
        else -> println("기타 타입: ${value::class.simpleName}")
    }
}

fun main() {
    printVariableInfo(null)       // 이 변수는 null 입니다.
    printVariableInfo(123)        // 숫자 타입: Int, 값: 123
    printVariableInfo(3.14)       // 숫자 타입: Double, 값: 3.14
    printVariableInfo("Kotlin")   // 문자열 타입: String, 길이: 6
    printVariableInfo(true)       // 기타 타입: Boolean
}
  • value == null변수가 null인지 확인
  • is NumberInt, Double, Float 등을 포함한 숫자 타입 확인
  • is String문자열 타입 확인 및 길이 출력

5️⃣ 제네릭에서 reified를 사용하여 타입 확인

Kotlin에서는 일반적인 제네릭 함수에서는 T::class를 사용할 수 없지만, inline + reified를 사용하면 가능.

inline fun <reified T> printGenericType(value: T) {
    println("값: $value, 타입: ${T::class.simpleName}")
}

fun main() {
    printGenericType(42)       // 값: 42, 타입: Int
    printGenericType("Hello")  // 값: Hello, 타입: String
}
  • reified를 사용하면 제네릭 타입 정보를 유지하여 런타임에서 T::class.simpleName 사용 가능.

✅ 결론

기능 사용 방법 예제
기본 타입 확인 value::class.simpleName "Int"
Java 타입 확인 value.javaClass "java.lang.Integer"
컬렉션인지 확인 is Collection<*> true or false
null 체크 포함 value == null "null"
제네릭 타입 확인 inline fun <reified T> T::class.simpleName

is, ::class, javaClass 등을 조합하여 변수가 어떤 타입인지 쉽게 확인 가능!

람다식 기반 스코프 함수 호출

You said:

kotlin에서 다음 코드의 문법에 대해서 상세히 설명해줘.

distanceInKm.value.let { distance ->
}
ChatGPT said:

해당 코드의 문법을 상세히 설명해 줄게요.

1. distanceInKm.value

이 표현식은 distanceInKm라는 변수가 객체 (예: LiveData, MutableState, StateFlow 등) 이고, 해당 객체가 value라는 프로퍼티를 가지고 있다고 가정합니다.

예를 들어 MutableLiveData<Int> 타입이라면:

val distanceInKm = MutableLiveData<Int>()

여기서 distanceInKm.value를 호출하면 현재 저장된 값을 가져옵니다.

2. let 함수

letKotlin의 표준 확장 함수 (scope function) 중 하나입니다.
주어진 람다 함수 안에서 it 또는 원하는 이름의 변수로 현재 값을 사용할 수 있습니다.

let의 주요 특징:

  • null이 아닌 값에 대해서만 실행 가능 (?.let을 사용하면 안전하게 호출 가능)
  • 람다 블록 내부에서 전달된 값을 it 또는 지정한 이름으로 참조 가능
  • 블록 내부에서 특정 연산을 수행한 후, 마지막 표현식의 값을 반환 (하지만 let { } 안에 반환값이 없으면 Unit 반환)

예제:

val distanceInKm = 10
distanceInKm.let { distance ->
    println("Distance: $distance km")
}

출력:

Distance: 10 km

여기서 let 내부의 distancedistanceInKm의 값을 가리킵니다.

3. distanceInKm.value.let { distance -> ... } 의 동작

만약 distanceInKmLiveData<Int>라고 가정하면:

val distanceInKm = MutableLiveData<Int>(10)
distanceInKm.value?.let { distance ->
    println("Distance: $distance km")
}

이 코드의 동작을 설명하면:

  1. distanceInKm.value 값을 가져온다.
  2. 값이 null이 아니라면, let 블록을 실행한다.
  3. distance라는 이름으로 값을 람다 내부에서 사용한다.
  4. 현재 println("Distance: $distance km")을 실행한다.

만약 valuenull이면 let 블록은 실행되지 않습니다.

4. let을 활용한 실용적인 사용 예제

(1) null 안전한 호출 (?.let)

val distanceInKm: Int? = null
distanceInKm?.let { distance ->
    println("Distance: $distance km") // 실행되지 않음
}
  • distanceInKmnull이면 let 내부 코드가 실행되지 않음.

(2) 데이터 변환 후 처리

val distanceInKm = 10
val distanceInMiles = distanceInKm.let { it * 0.621371 }
println("Distance in miles: $distanceInMiles")

출력:

Distance in miles: 6.21371
  • let을 활용하여 distanceInKm 값을 마일 단위로 변환 후 저장.

(3) 여러 프로퍼티를 조합하여 사용

data class Distance(val km: Int)
val distance = Distance(15)

distance.km.let { km ->
    println("Distance: $km km")
}

출력:

Distance: 15 km

5. 결론

distanceInKm.value.let { distance -> ... } 코드의 역할:

  • distanceInKm.valuenull이 아니라면 let 블록 실행
  • let 내부에서 distance 변수로 value 값을 안전하게 사용 가능
  • valuenull일 경우 블록이 실행되지 않음 (이를 이용해 null 체크 없이 안전한 코드 작성 가능)

let을 사용하면 좋은 경우:

  • null 체크 없이 안전한 호출을 원할 때 (?.let)
  • 값 변환 후 처리하고 싶을 때
  • 객체를 전달받아 임시 변수를 만들어 가독성을 높일 때

이해가 안 가는 부분이 있으면 추가로 설명해줄게!

최근에, AC2에서 디버거 활용하여 디버깅을 하신 세션을 공유한 세션을 듣고 나의 생각이 있어 적어 봅니다저는 개발자이고 싶은 관리자(^.^)이기 때문에 사례의 경우 동료들의 같이 하는 고민으로 이해 해주시면 됩니다. Android Phone Framework System Service 주로 개발하는 저와 동료들의 경우도, 요사이 디버깅은 로그 베이스일 밖에 없다는 생각이 들었습니다. 디버깅을 10번한다면, 9 이상이지요. 이유는 번개 때에 지나가듯 이야기 해주시는 부분에 있다고 생각했습니다. 그래서, 빠르게 생각을 정리해 보았습니다.

 

제가 생각하기에 디버거가 예전 같이 많이 사용되지 않는 것은 리눅스와 같은 우선 멀티프로세스(Multi-process) 상황이기 때문이란 생각이 들었습니다. 주로, 디버거로 확인 가능한 것은 하나의 프로세스에서 관련된 심볼(High level language machine language 연결 시켜주는 정보) 읽어와서 보여주며 실행하여야 하기 때문이라고 생각됩니다. 우리가 담당하지 않는 다른 프로세스로 넘어가게 되면 확인이 어려워 집니다. 상황에서 타이밍 이슈가 있는 부분이라면, 로그와 같은 디버깅 코드를 추가하면서 문제가 사라지는 경우도 발생하게 됩니다. 것은 번개에서 언급되었던 것으로 기억합니다. 실재로 동료들과 이야기 나눠 봐도 디버거를 아예 모르는 분도 계시고 경력이 많은 분도 실재 기기에서는  Watchdog 같은 제약도 많아 로그 기반으로 접근한다고 하네요.

 

디버거로 가능한 것들은 어떤 것들일까요? 우선 디버거가 있으면 1. 원하는 코드 부분에서 멈추게 있습니다. 그것만 가능한 것이 아니고 2. 멈춘 후에, 상황에서의 변수들의 값을 확인할 있습니다. 그리고, 3. 함수를 불렸을 , 어떤 함수에서 호출한 것인지(call stack) 있습니다. 그리고 4. Call stack 따라 호출한 시점으로 변수의 (물론 호출하여 관련 변수들이 변경되었다면 최신 상태) 확인해 있습니다. 세션에서도 설명하셨지만, 5. 원하는 코드가 특정 변수의 상태일 멈추게하는 것과 같은 고급 스킬도 가능합니다. 세션에서도 시연을 시도하셨죠.

 

다시 돌아와 디버거를 언제 써야 할까요? 10번에 1번이 될까 말까 하는 기술을 알아 두는 것은 좋을까요? 세션에서 공유해주신 사례에 적절하다고 생각됩니다. 저도 이쪽 도메인을 몰라서 이상한 이야기일 있습니다. 제생각에 방법을 모르셨다면, 사용하시는 Framework 뒤지고 로그를 넣어가며 확인하셔야 했을 가능성이 있어 보입니다. , 우리가 사용하는 모듈의 복잡성이 높은 부분에서 필요한 부분을 적응적으로 찾아 봐야할 때가 디버거를 사용을 고민해야 하는 아닌가 조심스럽게 추측해 봅니다. 저의 요즘 화두인 탐색이 필요하겠다는 생각을 잠시 했습니다. 망치를 쓰는 법을 알았으니, 망치를 쓰는 것이 좋겠다 싶은 곳을 탐색도 하고 연습도 해야죠. 그리고, 여기 저기 망치질을 하고 다니는 아닌가 고민해야 하겠네요.

들어가며...

Rust언어를 들어 보기는 했지만, 그나마 최근에 Linux 6.1에서 채용하기 시작한다는 기사[1]를 보고 다시 보기 시작했다. 최근에는 새롭게 시작한 프로젝트와 관련한 Open Source가 Rust를 채용하고 있어서 더 관심을 가지게 되었다. 최근에 살펴본 TIOBE Index[2]에서 보면 20위에 진입했다. 올해 말, 내년까지는 조금 씩은 보게 될 것이라 생각되니 익숙해 져보자는 생각에 이 것 저 것 뒤져 보다가, 바이블이라고 하는 The book[3]도 찾아 보고 읽어 보았다. 총 20장까지 있는데 현재는 10장 정도까지 어렵게 읽은 상태이다. 부족하지만, 기억나는 것을 정리하고 우선 멈추려고 한다. 이 후에, 정말 필요할 때 더 읽어 보려고 한다.

 

변수, 스코프 그리고 소유 

Hello World 프로그램을 보고 몇 가지 예제를 보고 가장 먼저 눈에 띄는 것은 mutable, immutable 개념이다. Rust에서는 변수를 만들면 값의 변경이 불가능한 immutable이 된다. 상수(constant)와는 다르다. 다시 같은 이름으로 선언할 수 있기 때문이다. 변경하기 위해서는 let mut x = 1;과 같이 mutable로 만들어야 한다. 쉐도우잉이라는 개념도 있는데 이는 Python등과 유사한 것 같다. 이는 같은 이름의 변수를 다시 선언해서 사용하는 것이다. 즉, let x = 1; let x = x +1;과 같이 사용한다는 것이다.


그 다음에 새로웠던 것이 소유권(Ownership)이다. Rust가 메모리 관리에 우수하다고 해서 Java 처럼 가비지 컬렉터가 있는가 했다. 하지만, 다른 방법으로 이 부분을 강화 했는데 그 중 하나가 소유권이라는 것이다. 이 것이 C나 다른 언어에서 알고 있는 스코프(Scope)과 관련이 있다. 즉, 변수의 Visibility와 관련된 것으로 같은 레벨이나 그 하위에서 보이는 것이라 알고 있을 것이다. 그런데, Rust에서는 Heap에 관리하는 변수의 경우 얕은 복사(Shallow Copy)의 경우는 같은 스코프라고 하더라도 유효하지 않을 수 있다.

 

또한 함수의 인자로 전달된 변수의 인자의 소유권이 넘어가 다시 복귀하게 되더라도 유효하지 않을 수 있다. 좀 더 상세히 이야기 하면, 메소드가 인자를 단순히 읽기만 하여 함수 처리 후 복귀했을 때 유효하기도 하다 (&self). 혹은 함수 내부에서 변형시키기도 하다(&mut self). 여전히 복귀 후에 사용이 가능하다. 아니면 소유권을 호출하는 함수에 넘겨 함수가 소비하고 함수 호출 이후에는 유효하지 않게 하기도 (self)한다. 즉, 인자를 어떻게 사용할지 결정론적으로 정하게 되는 것이다. 

 

Android와 Rust

Google도 Rust를 지원하는 큰 기업 중에 하나이고, Android에도 적용한다고 한다[4]. 이 부분도 관심과 시간을 가지고 살펴 볼 부분이다. Android에 Sandboxing이 도입되고 있음에도 메모리 관련 관리에 어려움이 있는 것[5]으로 보인다. 2019년도 부터 채용되고 있는 것으로 보아 조금이라도 적용되는 부분이 증가하지 않을까 생각된다. 

 

정리하며...

여전히 성능적 측면의 우려와 학습 곡선이 가파르다는 비판도 있는 것으로 보이지만, 2022년도에 Linux 커널에 도입되는 것을 보았을 때에는 추이를 계속 지켜봐야할 부분으로 보인다. 한국에서도 채용이 되고 있고 사용자 모임[5]이 있다. Discord도 상당히 활발해 보인다.

 

참고 내용

[1] Linus Torvalds: Rust will go into Linux 6.1, https://www.zdnet.com/article/linus-torvalds-rust-will-go-into-linux-6-1/?taid=6329bce71c8e3300010028ae&utm_campaign=trueAnthem%3A%20Trending%20Content&utm_medium=trueAnthem&utm_source=twitter

[2] TIOBE Index for November 2022, https://www.tiobe.com/tiobe-index/

[3] 러스트 공식 안내서 "The Rust Programming Language", https://rinthel.github.io/rust-lang-book-ko/ch00-00-introduction.html

[4] Android Rust 소개, https://source.android.com/docs/setup/build/rust/building-rust-modules/overview?hl=ko 

[5] Rust in Android Platform, https://security.googleblog.com/2021/04/rust-in-android-platform.html

[6] 한국 러스트 사용자 그룹, https://rust-kr.org/

 

내가 사용하고 있는 컴퓨터 언어에는 C/C++, Java, Python이 예전에 사용하던 Perl등도 포함할 수 있겠지만 너무 사용하지 않고 있다. 최근에 관심이 가는 언어는 Go Lang이다. Google의 개발자인 로버트 그리즈머, 롭 파이크, 켄 톰슨 세사람이 개발한 언어이다. [1]에서 설명하는 특징은 다음과 같다.

 

  • 정적 타입, 강 타입 ...
  • 컴파일 언어 ...
  • 가비지 컬렉션 ...
  • 병행성(Concurrency) ...
  • 멀티코어 환경 지원 ...
  • 모듈화 및 패키지 시스템npm, pip, gem 이나 Maven과 같은 모듈 의존성에 따른 패키지 관리를 언어 차원에서 지원. ...
  • 빠른 컴파일 속도

이 언어를 개발한 이유가 C/C++이 사용하기 어려워서 개발하기 시작했다고 한다. 그래서, 서버, 브라우져, 데이터베이스등 큰 응용을 개발하는데 유용하다고 한다. 하지만, 시스템 레벨 프로그래밍에는 적절하지 않다고 한다. 

 

대략적인 문법이나, 특징은 책에서도 볼 수 있지만 인터넷에 자료가 많고 특히 [2]에서 보게 되었다. 

 

내가 이 언어를 보게 된 가장 큰 이유는 Android의 Build Systemdp Ninja[3]로 바뀌고 Blueprint(.bp)[4]로 변경되기 시작하고 이다. 이 bp파일들이 Go lang으로 쓰여 지고 있다.

 

참고 문헌

[1] GO 언어란? - 주요 특징,  https://goodjoon.tistory.com/241

[2] A Tour of Go, https://tour.golang.org/welcome/1

[3] Ninja, a small build system, https://ninja-build.org/

[4] Soong 빌드 시스템, https://source.android.com/setup/build

C언가 다시 떠오른다고 한다[1]. 물론 1위는 아니지만, 여전히 2위이고 성장세가 3위인 Python을 앞선다는설래발이다. 이유는 사물인터넷(IoT)과 최신 스마트 기기에 적합하기 때문이라고 이야기 한다.

 

알려진데로[3], C언어는 저수준 디바이스 드라이버, 커널 등에 대한 작업에 필요하다. 최근의 Android와 같은 곳에 사용되는 Linux는 이 C를 기반으로 한다. 오래되기도한 이 언어는 많은 시스템에서 사용이 가능하고, 포인터를 통한 메모리 접근이 간편하다. 하지만, 이러한 특징들이 단점으로 다가오고, 오래되었기 때문에 최신 언어들이 가지는 연구 성과들이 포함되지 않아 약점이 있다. 앞의 기사[1]에서 이야기 하는 부분은 위의 장점들을 살리는 특징 때문이라고 볼수도 있다.

 

하지만, 여전히 단점은 있다. 그렇기 때문에 MS는 다른 언어[3]를 개발하여 공개한다고 하고, Google은 Go라는 언어를 릴리즈한 것이 아닌가하는생각이 든다.

 

개인적인 생각으로는 초보자라면 한가지 언어를 결정하고 배우고 활용한다. 처음 추천하는 언어는 Java이다. Object Oriented Programming의 개념을 배울 수 있는 언어이며, 서버쪽이나 안드로이드쪽에서 활용이 가능하다. 이후에 다른 언어를 배우기도 좋다. 예전에 비전공자로 다른 업무를 하면서 프로그래밍을 배우겠다면 Python을 추천한 적도 있다.

 

그리고, 다른 환경에서 필요한 언어가 있다면 추가적으로 배우는 것이 좋겠다. 우선 현재 자신이 필요한 환경에 필요하면 배우면 된다. 이 때, C를 배우는 것도 좋다.

 

나는 C언어를 주로 썼다. 가장 오랫동안 썼고, 마치 내 Native Langauge와 같은 언어이다. 그래서, 알고리즘 연습을 하거나 간단한 프로그램을 구현하는 경우는 이를 기반으로 한다. 현재 업무가 안드로이드 개발이므로 Java도 많이 보게 된다. 그리고, 최근에는 업무 자동화 등으로 Python을 배워서 활용하고 있다. Android의 빌드 시스템이 Go언어 기반으로 바뀌어서 관심을 가지고 있다. 그리고는, 최근에는 Node.js로 팀원이 작업한 내용이 있어 Javascript도 최근에는 관심을 가지고 조금씩 살펴 보고있다. 

 

이렇 듯, 주력 언어가 하나 있고 필요에 따로 다른 언어를 익히고 있고 있다. 

 

참고 문헌

[1] C언어, 2019 올해의 프로그래밍 언어...상승세 파이썬 제쳐, 

http://m.zdnet.co.kr/news_view.asp?article_id=20200110021453&re=zdk#imadnews

[2] C 언어를 배워야 할 것 같아요?!, https://technical-leader.tistory.com/43 

[3] MS, C언어 대체할 신규 프로그래밍 언어 연구, http://www.zdnet.co.kr/view/?no=20200119172404

 

아들과 아들 친구에게 방학을 이용해서 Python을 가르쳤다. 벌써 한두해 전이라 컴퓨터는 게임이나 그림 그리는데 관심이 있는 아들은 거의 다 잊었던 것 같다. 지나가 듯, 자신이 배우면 C 언어를 배워야 할 것 같다는 이야기를 한다. 요인 즉은 컴퓨터를 잘아는 친구는 C를 알고 있고, 경시 대회 등의 알고리즘 대회에서는 C여야 한다는 것이 주장의 논지이다.

 

그러다가, 최근에 아래 기사를 보았다[1]. 주장에 틀린 바는 없으나, 내 생각과 다르니 정리해 보고 나의 입장을 한번 정리해 두는 것도 필요해 보인다.

 

주장의 요지는?

[1]이 그리 길지 않으나, 읽어 봐도 좋겠다. 요지는 새로운 언어들이 장점은 있지만, 여전히 C를 써야 하는 이유가 있다는 것이다.

 

우선 C는 C++대비해서 C는 미니멀리즘을 가지고 있다는 것이다. 결국 C++은 새로운 언어 특징을 가지도록 성장하고 변화하고 있어서 복잡하다는 것이다. 물론 나도 Simple is best라는 생각을 가지고 있는 사람이기는 하다. 하지만, C는 예전에 읽기 복잡할 정도로 짧게 간략하게 쓰는 것을 특징으로 가지기도 했다는 측면에서 이 미니멀리즘이 읽기 쉽고 이해하기 쉽게 코딩할 수 있게 한다는 것을 의미하지 않는다는 것을 명심하자. 하지만, C가 가지는 간략함은 C++이나 Java로 계승되고 있지 않나 생각한다.

 

Java나 C#등은 성능 혹은 메모리 관련 비판을 많이 받는다. 이러한 비판이 있다는 것은 시스템 이식성 혹은 메모리 관리의 간편성등의 장점이 있다는 뜻이기도 하다. 이렇다면, C가 가지는 반대편은 바로 메모리 관리 및 성능일 것이다.

 

Go는 Google에서 발표 하고 최근에 관심을 많이 받는 언어 중에 하나이다. 컴파일 언어로서 웹서비스나 Commandline Utility, Network Service등을 만드는데 채용되고 있다. 하지만, 글에서는 저수준 디바이스 드라이버, 커널 등에 대한 작업에는 C가 필요하다고 주장하고 있다. 사실이다. 하지만, 이런 작업을 하는 시장의 인력 요구는 얼마나 될까? 

그렇다면, 결론은???

나는 여전히 같은 결론이다. 컴퓨터 언어를 알고 그것을 어디에 어떻게 사용할 것인가를 따져 봐야 한다. 자신이 시스템이나 성능에 민감한 시스템을 만드는 사람이라면 C를 알아야 한다. 하지만, 그렇지 않고 자신의 분야가 따로 있고, 그 분야가 Software를 전문으로 하는 분야가 아니라고 해보자. 하지만, 원하는 작업을 위한 존재하는 솔루션이 제품으로 제공되지 않는다고 해보자. 그럼, 그것을 만들어야 할 것이다. 이 때, 이 무엇인가 만들어야 하는 초보자라면 Python을 고려하는 것이 좋을 것이다.

 

위에서 이야기 한 것 처럼 C를 배우기 마음 먹고 기본 사항을 익힌다고 하자. 그렇다면 거기서 멈출 것인가? 새로운 언어를 배우면서 자신을 확장해야 한다. 그렇지 않는다면, 위와 같은 비판을 한다고 하더라도 공허한 주장일 수 있지 않을까?

참고 문헌

[1] C 프로그래밍 언어가 여전히 최고인 이유 : 개발언어 일대일 비교, http://www.itworld.co.kr/news/124767?page=0,1

Python을 배우면서 2.x대와 3.x의 버전이 있다는 것을 들었고 더 이상 .2.x대의 업그레이드는 없다고하여 많은 고민 없이 3.x대를 쓰기 시작했다. 하지만, 실재로는 여러 이슈가 있다. Android는 여전히 2.x대를 쓰고 있다. 그래서 상황에 따라서는, 2.x 과 3.x를 병행해서 쓰게 한다거나 개발 환경을 Windows와 Linux로 나누어 PC를 2대로 운용한다던지 해야 하는 상황이 있다. 그럼에도 지금까지는 2.x는 그래도 강건너 불구경이었다.


도움 요청

네트워크 성능 최적화가 주요 업무인 후배는 network에서 실재로 전송하고 받는 packet을 캡춰해 놓은 pcacp 데이터를 많이 분석한다. 관련해서 이야기도 많이 하고 나에게 조언을 구하기도 한다. 한번은 python 이야기를 하길레 내 경험을 이야기 하고 python 강습을 듣도록 권유했다. 그래서, 나름 작업을 시작했던 것 같다. 관련된 조언을 몇번씩 구해서 같이 고민하고 간단히 답을 구하기도 하고 혹은 못 도와주기도 했다. 하지만, 이번에는 좀 더 심각해 보였다. 우리 집까지 찾아 오겠다는 것이었다.


내용은 python의 pcap 분석 툴인 captcp[1]의 포팅을 도와 달라는 것이었다. 이 툴은 dpkt[2] 모듈을 이용해서 TCP/IP 패킷을 분석하는 툴이었다. 예전에 몇번 질문한 것도 결국은 이 것과 관련 된 것이었다. 추가적인 문제점은 운용 환경이 Windows였다는 것에서 시작해서, 결국은 Python 3로 포팅해야 한다는 것이었다.


간단한 변환

새롭게 알게 된 것은 python 2 스크립트를 3로 변환하는 툴이 있었다. 기본적으로 python 3를 설치하면 [python 3]\Tools\scripts에 설치 되어 있는 툴이다. 이를 이용하여 기본 변환을 하면 된다.



아래와 간단히 명령어를 입력하면 변환이 일어난다.



미궁을 해매기

위와 같이 변환해도 Logical하게 문제가 발생하는 부분을 모두 처리해주지 못하기 때문에 바로 동작하지는 않는다. 결국에는 이 코드를 가지고 작동 시켜보면서 디버깅을 해야 하는 상황이었다. 이미 여러 이슈를 해결했지만, 최종 결과에는 이슈가 있었다. 테스트로 사용했던 동작은 statistic 옵션 이었다.



하지만, 지속적으로 error가 발생하면서 이슈를 파악하기 어려웠다 이 부분을 같이 살펴 보면서 Logical한 이슈가 무엇인지 분석을 하였다. 


결과 부터 이야기 하면, python 3로 오면서 Object의 연산자 overloading에 대한 구현 부분이 바뀐 것이다. Object의 비교 연산자가 Override 구현이 정확하게 동작하지 않고 단순 주소 비교로 되면서 처리가 완료되지 못하고 오류가 발생하는 건이었다.


근본 원인이 밝혀 지니 수정 방향도 수립이 되었다. __cmp__가 적절하지 못하는 것이 예상되었고, googling해 보니 이미 관련된 설명[3]이 있다. 이를 기반으로 __eq__로 대체 구현하였더니 동작함을 확인할 수 있었다.



돌아보며

결국은 Python2의 결과물을 Python 3로 변환하는 것은 쉬운일이 아니다. 또한, Open Source의 특성 상 지원이 있을 수도 있고 없을 수도 있기 때문이다. 하지만 또 하나의 장점은 내가 직접 변환할 수도 있다는 것이다.


최종 결과물은 내 github repsitory[4]에 저장되어 있다. 원본 Project가 2016년 부터 멈추어 있고, python 3과 연계가 어떻게 될지 불분명해 보인다. 그래서, 원본으로 pull request를 보내지는 않을 예정이다. 대신, 내 repository에 유지해 보는 것이 여러 방향으로 괜찮지 않을까 하여 유지해 보려 한다.


사족이지만 방금 찾은 것인데, python 2.7은 2020년에 퇴역(EOL)한다[5].


[1] captcp, https://github.com/hgn/captcp

[2] dpkt, https://github.com/kbandla/dpkt

[3] cmp method in python 3, https://stackoverflow.com/questions/8276983/why-cant-i-use-the-method-cmp-in-python-3-as-for-python-2

[4] captcp for python 3, https://github.com/blcktgr73/captcp

[5] Python 2.7 will retire in..., https://pythonclock.org/





나의 기억 속의 Java

Java는 C/C++만 알고 있던 나에게는 새로운 언어라고 보다는 개념을 도입하는 언어였다. 이는 Java의 언어 형태가 C/C++과 매우 흡사하기 때문이다. 코드 일부만 보면, 어떤 언어인지 판단하기가 쉽지 않을 수도 있다. 처음 배울 때가 Java가 Public하게 처음 소개 될 때였다. 그 당시에는 Sun Microsystem에서 릴리즈 하였다. 대학원 당시 랩에서 Sun Spark 워크스테이션을 처음 접해 본 터라 컴퓨터를 만드는 회사에서 도입한 컴퓨터 언어라는 것이 신기하고 Object-Oriented 개념으로 만든 언어라는 것이 신기했다.


그리고는 , Java 1.2가 Java 2로 소개 되고, J2SE, J2ME와 같이 Profile을 가지고 소개되었다. 단말을 하였었기 때문에, 옆 부서에서 J2ME의 여러 모듈들을 포팅하는 것을 기억하고 있다. 이 당시는 J2ME라도 상당히 무겁고 응용도 많지 않은 상태여서 Mobile에는 적용이 어렵다는 생각을 많이 하였고, 심지어는 모바일에는 Java는 절대로 보편화 되지 못할 것이라는 나름대로의 판단이 있었ㄴ다.


다시 안드로이드를 시작하면서, Java로 어플리케이션을 작성하는 부분에 대해서 놀랐다. 그리고, 이야기 듣던 JNI로 C++단이나 커널과 연결되어 Platform을 구성하는 것에 대해서 많은 놀라울 수 밖에 없었다. 그리고도, 시간이 흘러 보니 내가 알고 있던 Java와는 많은 차이가 있었고 처음 부터 다시 다져봐야 할 이유가 충분하였다.


또 다시 시작.

Android를 하기 때문에, Java를 다시 봐야 하는 것은 자연 스러웠다. Python도 강좌를 통해서 익힌 터라 강좌를 찾았다[1]. 2가지 과정으로 되어 있었지만 첫번째 강좌만 들었다. 이후는 응용인데, Android에는 큰 도움이 되어 보이지 않아서였다. 전반적으로 IDE로 Eclipse를 쓰고 있어서 툴을 배우는데는 유용했다. 다른 언어와 유사한 데이터 타입, 연산자, 입출력, 루프 그리고 함수까지는 평이했고 C++을 했기 때무에 객체 지향관련 Class와 다형성까지 크게 문제가 없었다. Interface와 C++과 다른 부분들은도 예전과 크게 다르지 않았다고 느껴서 어렵지않았다. 하지만, 이 것을 마무리 하고나서는 많이 부족한 생각이 들었다.





책으로 보는 Java

결국은 책을 찾게 되었고, C++ 책의 같은 저자가 만든 책이 있는 것을 알고 그 책을 찾아 보았다. 이책은 Java 8기반으로 작성되어 있어서 Android를 기반으로 하고 있는 나도 더 도움이 되겠다 싶어 보게 되었다. 다 보고 난 후 총평은 새로운 개념들을 설명하면서는 정리가 좀 덜 된 듯 한 느낌을 들기는 하였다. 요리를 해야 하는 재로는 다 들어갔는데 잘 어울어지지 않는다는 느낌이었다. 물론, 내가 개념들을 깊이 이해하지 못해서이기도 하지만, 설명을 잘 하는 책이라면 충분히 그 느낌을 덜어주지는 않았을까 한다.


나의 관점에서 새로운 개념들은 Chapter 21부터 설명되는 Generic그리고 이를 활용한 Collection Framework였다. 이 후, Lambda와 Method 참조, 그리고 스트림까지이다. 특히, Android에서도 적용되는 Java 8의 신규 기능이라 할 수 있는 것은 Lambda와 Stream이다. 새로운 Android 코드들에는 이를 활용한 코드가 적용[3]되고 있으니, 반복해서 확인하고 몸에 익혀야 할 내용들이다.


Android와 Java

Android는 Java와 때려고 해도 때기 어렵다. Google이 Android 작성하는 언어으로 Kotlin을 발표하였다. 또한, Java의 현재 주인인 Oracle과 Android의 주인인 Google의 전쟁의 승자가 Oracle로 결정이 되어 더욱 암울해 보인다[4]. 하지만, 나는 여전히 처음 시작하는 언어로서 Java를 추천한다. 아직도 Java가 Android에서 계속 사용되어야만할 이유가 충분해 보이기 때문이다.

우선 Android 개발자 입장에서는 Java를 버릴 이유가 없다. 왜냐하면, 이미 Java를 기반으로 한 Android Phone들이 많이 팔렸고, 여기에 App들을 Java로 만들어서 배포한다. 극단적인 가정으로, Google이 Java를 포기하고 새롭게 Android2를 만든다고 하더라도 새로운 폰들이 바뀌는데는 상당한 시간이 걸린다. 아직까지 5년전 발표된 Kitkat까지 해야 94%휴대폰 커버(2018년도 1월 18일 기준)되니까. 비슷한 논리로 내년에 완전 Java를 사용하지 않는 플랫폼 만들어서 배포해서 유사한 시간이 걸린다고 하더라도 5년은 걸릴 것이다. 추가 참고 자료로 [5]를 보면, 시간 상으로 어떤 Version의 안드로이드 플랫폼들이 변화 하는지 볼 수 있다. 또한, 사람들의 스마트폰 교체 주기도 점점 길어 지고 있다. 이렇게 보면, 지금 Android가 가지고 있는 Market과 사용자들이 그렇게 쉽게 Java에서 다른 언어로 넘어갈까? 이 부분에 판단은 글을 읽으시는 분께 맏긴다.

공부라는 측면에서의 의견도 나는 Java를 계속 권한다. 위에서도 설명했듯이 Object Oriented Programming Paradigm을 근간으로 하고 있는 언어로서 이를 배우기 위한 최적의 언어이다. 그렇기 때문에 Java가 가지고 있는 여러 툴이나 도구이 아주 많다. 뿐만 아니라, 언어를 배우시고 배워야 할 Architecture 관련된 분야들이 Java를 기반으로 시작된 것들이 상당히 많다. Refactoring, Design Pattern 그리고 Unit Testing 등이 그 예이다. 그렇기 때문에, 이를 가장 잘 설명하는 책 및 Practice들이 Java를 기준으로 한 것이 차고 넘친다. 그리고, 사족으로 컴퓨터 언어 한두가지 정도는 더 하야 한다. 그렇기 때문에, Java로 시작해서 Java로 끝난다고 생각해서는 안되고, 여기서 필요에 따라 여기서 발전된 언어를 적어도 한 두개는 더 한다고 생각해야 한다.

그럼, Java는 없어질 언어인가요?

개인적으로는 C를 주로 사용했고, 거기서 C++, Java, Python 등의 다른 언어로 가지치기를 하고 있다. Java는 현재 정말 Popular하게 전개 되어 있는 프로그래밍 패러다임인 Object-Orient를 기반으로 만들어진 언어이다. 그리고, Android의 기반이 되고 있는 언어이다. 위에서도 이야기 했지만, 이를 바꾼다고 해도 시간이 소요된다. 그런 의미에서 Java는 위의 여러 사실을 바탕으로 Programmer의 First Language로 적극 추천할만한 언어이다. 물론 이것은 현재의 내 판단이고, 이는 또 바뀔 수 있다. 하지만, 적어도 5년 길면 10년까까지도 이 의견은 유효하리라고 판단한다.


[1] Java 배우기 Part 1(기본), http://www.credu.com/main/credu/user/course/zu_course_detail_R.jsp?p_subj=C69401

[2] 윤성우, "윤성우의 열헐 Java 프로그래밍", 2017년 7월

[3] Android Java 8 Support, https://developer.android.com/studio/write/java8-support

[4] 8년 전장 오크랄 웃고, 구글 울었다. http://www.zdnet.co.kr/news/news_view.asp?artice_id=20180328141031

[5] Android version history, https://en.wikipedia.org/wiki/Android_version_history



Python을 처음 들었을 때에는 Linux Shell Script 혹은 Windows에서 Bat 파일과 함께 Perl을 사용해서 업무 자동화를 주로 할 때였다. 이 때의 자동화는 Software 빌드를 자동으로 하는 업무가 주여서 주어진 내용으로 충분했고, Perl의 경우도 필요한 내용만 찾아서 사용하는 정도의 수준이었다. Python도 이와 유사항 언어였고 필요성을 느끼지 모해서 배우지 않았던 것이 사실이다. 


최근에 컴퓨터 언어의 Ranking들을 살펴 보다 보니, 순위가 엄청나게 높아져 있었다. 용처도 많고, 옆의 동료가 온라인 과정을 통해서 배우고 있고 이를 통해서 자신의 업무 자동화에 활용하고 있는 것을 보고 자극이 되어서 사내 온라인 강좌를 통해서 기본적인 내용을 듣게 되었다. 목표도 어렴풋이 사내 웹싸이트에 있는 정보를 모아서 주기적으로 보고하는 것을 주기적으로 해야 하는데, Web Page의 테이블에 있는 내용들을 지속적으로 손으로 정리해서 보고 하게 되면 과제 진행하는 2~3개월 동안 보고 하게 되면 업무 효율이 나지 않았다. 그래서, 반드시 자동화를 해야하기는 했는데 어떻게 해야할지 막막했다.



온라인 강좌로 시작하다.

내가 들었던 강좌는 멀티캠퍼스의 2개의 강좌[1-2] 였다. 첫번째 수강한 강좌는 활용 보다는 전체 내용을 처음 부터 하나씩 설명하는 구조였기 때문에 지루하였다. 하지만, 책을 보고 공부하는 것도 습관이 붙어 있지 않은 상태여서 매일 조금씩 강좌를 보는 것은 꾸준히 진도를 나가는 도움이 되었다. 컴퓨터 언어를 전혀 하지 않았다면 첫번째 구조와 같은 강좌가 도움이 될 것 같고, 이미 다른 언어를 알고 있다면 [2] 강좌가 응용 예가 더 많이 소개 되기 때문에 도움이 될 것이다. 두 개의 강좌에서 살펴 보면, 크게는 다음 항목들을 다루고 있다.


0. 설치, 환경 설정 및 IDE

어느 언어든 기본적인 환경 설정이 필요하다. Python도 다운로드 받아서 설치를 해야 한다. 또한, 개발을 편리하게 해주는 Integrated Development Environment (IDE)가 있다. 모든 강좌에서는 기본적인 Python 설치를 다룬다. [2] 강좌에서는 Python 개발자들이 상당히 많이 사용하는 PyCharm[3] 을 설정하는 법을 설명한다.


1. Basic Language

어떤 언어를 배워도 다루게 되는 기본 자료형(Default Data Type, 연산자(Operator), 변수(Variable), 표준 입출력, 제어문, 함수가 우선 이 부분에 해당되었다. [1]에서는 2~6장이 여기에 해당되고, [2]에서는 2~8장까지가 여기에 해당된다고 볼 수 있다.


3. OOP, Module 과 예외 처리

Python은 OOP를 지원한다. 하지만, 간략화 된 클래스, 상속과 다형성을 포함한다. [1]에서는 7장, 8장이 여기에 속하고 [2[에서는 16장에서 설명한다. 또한, 기능들을 분리하여 관리하기 위해 모듈 기능을 제공한다. 추가적으로 예외 처리 관련 항목도 설명한다.


4. 확장 기능

기본적인 개념을 바탕으로 파일 처리, XML, 네트워크, 데이터 베이스 뿐 아니라 그래픽 관련 모듈 설명도 한다. 특히, Tkinter는 Python이 설치되면 기본적으로 제공되는 기능으로 UI를 쉽게 구성할 수 있다. Python에서는 Regular Expression, 정규식도 많이 사용된다. 2개의 강의 모두 Regular Expression을 다루지만, [2[에서는 2개의 챕터를 할애해서 설명한다.


5. 응용

[2]의 강좌가 좀 더 응용에 집중하고 있다. 엑셀, JSON을 포함한 Open API 활용, Web Scrapping, 날짜 및 자동화 기능, 메일 발송/수신까지 광범위하게 다룬다. 위에서 설명한 특정 웹 페이지에 접속해서 정보를 받아 정리하는 기능이 Web Scrapping으로 볼 수 있는데, 내가 필요로 했던 자동화 기능도 바로 이를 활용하면 가능하였다.


아들에게 가르치기: Python for Kids

일주일에 한번씩 아이들이게 가르치는 기회가 생겼다. 방학에 중학교 1학년인 아들과 아들 친구과 관심을 보였고, 어떻개 진행할까 고민하다가 책[4]을 발견하였다. 원래는 영문판을 먼저 발견했다가 번역을 하려 하다가 번역본이 있다는 것을 알게 되어 이 책을 선정하였다. 강의 계획은 아래와 같았다.


12/09: Python 소개, 기본 설정. Ch1 책소개 Ch2

12/16: 복습, Ch3, 4: Data types & Turtle graphics

12/23: 복습, Ch5, 6 (if/loop)

12/30: 복습, Ch7, 8 (function, class)

01/06: 복습, Ch9, 10 (Built-ins and useful modules)

01/13: 복습, Ch11, 12 (Graphics), Project 논의

01/20: 복습, Ch13, 14 (Bounse Ball), Project 계획

01/27: 복습, Ch15, 16 (Game2)

02/03: 복습, Ch17, 18 (Game2)





한번에 2시간 혹은 2시간 30분 정도 소요 되었다. 중간에 쉬는 시간을 가지면서 했다. 복습을 시키면서 새로운 단원을 나갔고 실습 위주로 진행하였다. 이해하지 못하는 것은 이론 설명을 충분히 하였지만, 책에 포함된 코드를 이용하여 실습에 더 많은 시간을 할해했다. 그래서 인지, 두 아이는 큰 무리 없이 따라 왔었다. 이책이 좋았던 부분은 설명 자체도 실습이 가능한 구조여서 입력하여 결과를 볼 수 있었고, 마지막에 Game을 다루기 때문에 흥미를 이끌어 내기 쉬웠다.


추가적인 부분으로, PyCharm을 처음 부터 설치하게 해서 인터프리터 기능 보다는 코딩 개념으로 설명하였다. 강의, 실습도 2명이 하기 때문에 더 진도가 잘 나갔던 것 같다. 원래 계획으로는 위의 강습 후에 Project 같은 것을 할 계획이었지만, 시간 부족으로 진행하지는 못했다. 나중에 또 기회가 있다면, 프로젝트를 하면서 여러 명이 함께 기획하고 구현하고 발표하는 시간도 3주 정도 가져보면 더 발전이 있을 것으로 예상한다. 그렇다면, 총 12주 정도의 시간이 필요했다.


업무 자동화에 활용: Web Scrapping

Python은 C++/Java와는 다르게 인터프리터를 사용하는 언어이다. 내가 가장 먼저 사용해본 인터프리터 언어는 Basic이었다. 그 당시는 마치 지금의 Windows 명령어 프람프트(Cmd.exe) 와 유사한 인터페이스였다. 다른 것은 컴퓨터를 켜면 항상 그 화면이 떴고, 수행할 내용을 입력하고 얻는 방식, 즉 인터프리터 방식이 기본이었다. 옛날 방식의 기술이 항상 나쁜 것은 아니고 상황에 따른 선택이라는 생각이 든다. 즉, 이렇게 하게 되면 컴퓨터의 동작 방법을 몇가지는 알지 않더라도 컴퓨터 언어에 접근하게 해주는 것이 장점이 되고, 그래서 Python을 비전공자에게 권하게 된다고 생각된다. 고급 사용자의 경우는, 실행할 내용을 text파일로 저장해 놓는 Script도 사용할 수 있으니 이 또한 유연한 접근 방법이다. Android에서는 2.x를 사용하지만, 나는 우선 자동화와 추후를 위해서 3.x대를 선택하여 사용하였다.


위에서 설명하였듯이 Web 페이지에 접속해서 필요한 내용을 추출해 해는 것이라고 간단히 말할 수 있겠다. 내가 필요했던 업무도 사내 홈페이지들에 흩어 져 있는 우리 부서의 정보를 원하는 형태로 취합 정리하는 것이었다. 인트라넷 홈페이지는 로그인 처리가 필요했고 개별 링크에 관련 정보들이 흩어져 있었고, Java Script를 이용한 Dynamic Web이었다. 강좌에서 소개한 Library는 Beautiful Soup이였다. 이 라이브러리는 디테일한 컨트롤이 어려웠고, 사내 홈페이지에 접속하는 것이 매우 어려웠다. 결국 Googling을 많이 하다 보니, Selenium이라는 모듈을 알게 되었고, [5]에 포함된 내용과 Googling을 통해서 원하는 기능을 구현할 수 있었다.




돌아 보며.

새셈 느낀 것이지만 필요한 목적이 있으면 배움이 더 유용하다. 그래서, 업무와 연계 시키거나 혹은 다른 목적을 찾으면 좋다. 특히, 가르치는 것은 더 많은 배움을 동반한다. 현재는 이러한 목적을 넘어선 다른 것 또한 찾고 있다. 그것이 더 좋은 공부라는 것을 알기 때문이다.


[1] 파이썬으로 배우는 실전 프로그래밍, http://www.credu.com/main/credu/user/course/zu_course_detail_R.jsp?p_subj=C90113

[2] 파이썬을 이용한 자동화 스크립트, http://www.credu.com/main/credu/user/course/zu_course_detail_R.jsp?p_subj=CB6392

[3] JetBrains PyCharm, https://www.jetbrains.com/pycharm/

[4] 제이슨 R. 브리그스 et. al, "누구나 쉽게 배우는 파이썬 프로그래밍 (Python for Kids)", BJPUBLIC. 2013

[5] Ryan Mitchel, "Web Scrapping with Python", O'Reilly 2015


우선 C++에 대해서 보수 학습을 고민하게 된 것은 처음에도 이야기 했던데로 내가 C를 학습했었고 Object Oriented Paradigm/Programming(OOP)을 접하게 되면서 C++이 C를 기반으로 OOP를 접목한 언어로 소개 받았기 때문이다. 1998년 경으로 기억한다. 이 당시에는 어렴풋한 기억이지만 이 Template 개념이 없었다. 이후 Template이 적용된 C++ 코드를 보았었고, 이 후에 C++에서도 여러 버전이 만들어 지고 있다는 사실을 알게 되었다.


마지막으로 C++에 대한 보수 교육을 하기로 마음 먹게 된 이유는 내 업무에 이 부분이 아직도 많이 활용되기 때문이다. 내 업무는 Android 어플리케이션에서 동영상과 음악 파일을 재생하게 하는 Multimedia Framework이다. Android의 경우, Application이 Java로 이루어 있다. 하지만, Linux Kernel위에 여러 Service와 Framework를 이루는 기본적인 언어는 C++이다. 이 부분이 예전 부터 사용되어 오고 있는 Open Source 레거시 코드(Legacy Code)들이 있기 때문이기도 하다. 또 다른 이유는 하드웨어 (Hardware, HW)와 연동하는 코드들의 경우, 성능을 이유로 C/C++로 작성되는 경우가 많다.


이와 같은 이유로 C++에 대해서 공부하기로 했고, 우선 학습 방법은 기존 부터 가지고 있던 윤성우 저자의 "열혈 C++ 프로그래밍 개정판"이었다. 이 책은 내가 보기에는 C++03 기준으로 설명하고 있는 것으로 보여진다. 책의 서문이나 내용 중에서 이에 대해서 명확하게 하고 있지는 않다. 발행일이 2011년도인 것으로 보아도 C++11의 내용을 담기에는 어려움이 있어 보인다. 그렇더라도, 아직 내가 보는 코드들에서 C++11까지 이해해야 할 정도가 아니기 때문에 이책으로도 충분히 필요한 내용은 포함된다고 생각했다. 이 부분도 일독을 마친 후에 나중에 알고 되고 추가 공부할 내용을 알게 된 점에 대해서도 분명히 한다.



학습 내용

책을 일독하고, 2번 정도 Sample Code를 살펴 보면서 알게 된 것은 사람들이 많이 이야기 하고 있는 Standard Template Library(STL)과 Lambda등에 대한 내용은 책에서 다루고 있지 않다는 부분이었다. 그래서, 인터넷등을 통해 알게 된 다른 자료까지 학습한 내용을 포함하여 정리해 보면 다음과 같다.


1. Procedural Programming Paradigm 

이 부분은 컴퓨터 언어의 개요서를 보게 되면 일반적으로 가장 먼저 다루게 되는 부분이다. 하지만, 이 책[1]에서는 C를 안다는 가정으로 시작한다. 나는 오랫 동안 사용한 언어가 C여서 크게 문제 될 부분은 없었다. 혹시라도 C를 잘 모르는 거나, 컴퓨터 언어를 처음 배우는 사람은 다른 C++책을 보는 것이 좋겠다. 


2. Objected Oriented Paradigm/Programming (OOP)

이 책에서는 Encapsulation에서 시작하여 OOP를 설명하면서, 상속(Inheritance), 다형성(Polymorphism)까지 설명하고 있다. C++이 다른 객체 지향 언어와 다르게 혼동을 주고 있는 virtual keyword에 대해서도 Chapter를 하나 할해하여 설명하고 있다. Chapter 11까지의 내용이 여기에 포함된다.


3. Template

구지 분리할 필요는 없지만, OOP와 Template를 분리하는 것은 내가 이 부분에 대한 이해가 낮기 때문이다. 책에서는 2개의 Chapter를 할당하여 개념 설명, Class Template, Class Template Specialization, Template Parameter (템플릿 인자)등을 설명하고 있다. Chapter 13, 14 이다.


4. Standard Template Library (STL)

책에서는 이 부분을 설명하지 않고 있다. 하지만, 알고리즘 등을 설명하는 등의 여러 웹싸이트 등에서 설명할 때, STL을 가정하고 코딩을 하는 경우가 종종 있기 때문에 이 부분에 대해서는 알아 둘 필요가 있다. 좋은 자료로 처음 찾은 것은 Topcoder의 Tutorial이었다. 현재 내가 봤던 Tutorial은 찾기가 어려워 현재 검색되는 Tutorial[2-3] 최신 관련 링크를 추가해 둔다.


5. Lambda와 C++11 관련 내용

특히 여기서는 Lambda에 관한 내용을 [4-6]으로 학습했다. 전반적인 C++11 내용은 [7]에도 잘 요약되어 있다. 이 부분은 특별히 필요한지 공감이 되지는 않아서 우선 다 살펴 보지는 않았다.


기존에 이해한 내용에 대해서 상기하는 부분과 이해가 부족한 부분에 대한 학습 그리고 STL, Lambda에 대한 학습은 쭉 살펴 본 상태이다. 하지만, 계속 쓰지 않으면 잊게 되어 코드를 시간 날 때 타이핑해보거나 다시 살펴 볼 생각이다. 여라가지 항목에 관련해서 하나씩 정리도 가능하다. C의 레거시 항목은 포인터도 다른 언어에서는 c++에서 Reference라는 개념이 도입되면서, 대부분의 언어에서는 사용하지 않지만, Object Pointer만이 아닌 Function의 Pointer등도 Lambda등과 같이 고민해 보면 흥미로울 듯 하다. 다른 항목 중, 추가로 작성 해보고 싶은 내용은 나에게는 신규 항목인 Template 부터 Lambda까지는 따로 글을 써서 정리해 보는 것도 재미 있을 것 같다. 특히, C++, Java, Python에서 공통되는 부분도 있어 글을 써보면 매우 흥미로울 것 같다.


참고자료

[1] 윤성우, "열혈 C++ 프로그래밍", Orange Media 2011

[2] "Power up C++ with the Standard Template Library: Part 1", https://www.topcoder.com/community/data-science/data-science-tutorials/power-up-c-with-the-standard-template-library-part-1/

[3] "Power up C++ with the Standard Template Library: Part 2", https://www.topcoder.com/community/data-science/data-science-tutorials/power-up-c-with-the-standard-template-library-part-2/

[4] "씹어먹는 C++ 토막글 ② - 람다(lambda) 함수", http://itguru.tistory.com/196

[5] http://ciere.com/cppnow12/lambda.pdf (링크 깨진 듯 합니다.)

[6] http://pages.cs.wisc.edu/~gerald/cs368/resources/Lambda.pdf

[7] "C++ 11 특징 및 사용 예제 정리", https://duragon.gitbooks.io/c-11/content/

+ Recent posts