2025-02-22T01:33:42

This commit is contained in:
2025-02-22 01:33:42 +09:00
parent b5f6bbb1e0
commit ee63c56f2b
9 changed files with 1451 additions and 0 deletions

12
app.sh Executable file
View File

@@ -0,0 +1,12 @@
#! /usr/bin/env bash
case $1 in
gitup)
echo "Pushing to git"
git add -A
git commit -m $(date "+%Y-%m-%dT%H:%M:%S")
git push origin
;;
*)
code .
esac

118
docs/02_기본 문법.md Normal file
View File

@@ -0,0 +1,118 @@
# **코틀린 vs 자바: 기본 문법 비교**
코틀린은 자바 개발자가 배우기 쉬운 언어이지만, 몇 가지 중요한 문법적 차이가 있다. 이 글에서는 **변수 선언**, **null 안정성**, **타입 추론**을 중심으로 코틀린과 자바의 차이점을 비교해보겠다.
## **1. 변수 선언: `val`과 `var`**
자바에서 변수를 선언할 때는 반드시 데이터 타입을 명시해야 한다.
### **자바의 변수 선언**
```java
int x = 10; // 변경 가능한 변수
final int y = 20; // 변경 불가능한 변수 (상수)
```
코틀린에서는 `var``val` 키워드를 사용하여 변수를 선언한다.
- `var`**변경 가능**(mutable) 변수
- `val`**변경 불가능**(immutable) 변수 (자바의 `final`과 유사)
### **코틀린의 변수 선언**
```kotlin
var x = 10 // 변경 가능
val y = 20 // 변경 불가능 (한 번만 할당 가능)
```
> **차이점 요약**
> - 코틀린에서는 **데이터 타입을 생략**할 수 있다. (타입 추론 덕분)
> - `val`은 `final`과 유사하지만, **런타임 상수는 아님**. (즉, `val` 변수는 런타임에서 초기화 가능)
---
## **2. 널 안정성 (Null Safety)**
자바에서는 `null` 값이 있을 경우 `NullPointerException(NPE)`이 발생할 수 있다.
### **자바의 null 처리**
```java
String name = null; // 컴파일러가 오류를 발생시키지 않음
System.out.println(name.length()); // NullPointerException 발생 가능
```
코틀린에서는 **기본적으로 모든 변수는 null을 허용하지 않는다.** 만약 null을 허용하려면 `?`를 붙여야 한다.
### **코틀린의 null 처리**
```kotlin
var name: String = "Kotlin" // null 허용 X
// name = null // 컴파일 오류 발생
var nullableName: String? = "Kotlin" // null 허용 O
nullableName = null // 가능
```
#### **안전한 null 처리 방법**
1. **안전 호출 연산자 (`?.`)**
```kotlin
println(nullableName?.length) // null이면 null 반환, 아니면 length 값 반환
```
2. **엘비스 연산자 (`?:`)**
```kotlin
val length = nullableName?.length ?: 0 // null이면 기본값(0) 반환
```
3. **강제 호출 연산자 (`!!`)**
```kotlin
println(nullableName!!.length) // null이면 강제 NPE 발생
```
> **차이점 요약**
> - 코틀린은 기본적으로 모든 변수에 대해 null을 허용하지 않음.
> - `?`를 붙이면 null 허용 변수로 선언 가능.
> - 안전한 호출 (`?.`), 기본값 제공 (`?:`), 강제 호출 (`!!`) 등의 문법을 제공.
---
## **3. 타입 추론 (Type Inference)**
자바에서는 변수 선언 시 반드시 타입을 명시해야 한다.
### **자바의 타입 선언**
```java
String message = "Hello, Java!";
int number = 42;
```
코틀린에서는 타입을 **컴파일러가 자동으로 추론**하므로, 명시적으로 쓸 필요가 없다.
### **코틀린의 타입 추론**
```kotlin
val message = "Hello, Kotlin!" // String으로 추론
var number = 42 // Int로 추론
```
하지만 타입을 명확하게 하고 싶다면 명시적으로 적을 수도 있다.
```kotlin
val message: String = "Hello, Kotlin!"
var number: Int = 42
```
> **차이점 요약**
> - 코틀린은 **타입을 생략 가능**(컴파일러가 추론).
> - 하지만 필요하면 타입을 명시할 수도 있음.
---
## **정리: 자바 vs 코틀린 비교**
| 기능 | 자바 | 코틀린 |
|------|------|--------|
| **변수 선언** | `int x = 10;`<br>`final int y = 20;` | `var x = 10`<br>`val y = 20` |
| **null 안정성** | `String name = null;` (가능하지만 NPE 위험) | `var name: String? = null` (null 허용 여부를 명확히 지정) |
| **안전한 null 처리** | `if (name != null) { name.length(); }` | `name?.length ?: 0` |
| **타입 추론** | `String message = "Hello";` | `val message = "Hello"` (타입 생략 가능) |
---
코틀린은 **더 간결하고 안전한 코드**를 작성할 수 있도록 설계된 언어다. 자바 개발자가 코틀린을 배우면 **불필요한 코드가 줄어들고**, **NPE 걱정 없이 더 안전한 프로그래밍**이 가능해진다.
다음에는 어떤 코틀린 개념을 비교해볼까?

View File

@@ -0,0 +1,193 @@
# **코틀린 vs 자바: 함수형 프로그래밍 차이점**
코틀린은 **객체지향 프로그래밍(OOP)과 함수형 프로그래밍(FP)을 모두 지원**하는 하이브리드 언어다. 반면, 자바는 원래 객체지향 언어였지만, 자바 8 이후로 람다 표현식과 스트림 API를 도입하면서 함수형 스타일을 부분적으로 지원하게 되었다.
이 글에서는 **함수형 프로그래밍의 핵심 개념**인 **고차 함수, 람다 표현식, 확장 함수, 불변성** 등을 자바와 코틀린을 비교하며 설명하겠다.
---
## **1. 함수형 프로그래밍이란?**
함수형 프로그래밍(FP, Functional Programming)은 **순수 함수**와 **불변성(immutability)**을 중심으로 하는 프로그래밍 패러다임이다. 핵심 원칙은 다음과 같다.
- **순수 함수 (Pure Function):** 같은 입력에 대해 항상 같은 출력을 반환하며, 외부 상태를 변경하지 않는다.
- **불변성 (Immutability):** 데이터를 변경하지 않고 새로운 값을 반환하는 방식으로 동작한다.
- **고차 함수 (Higher-Order Function):** 함수를 인자로 받거나 반환하는 함수.
- **람다 표현식 (Lambda Expression):** 간결한 익명 함수 표현.
---
## **2. 함수 선언 방식 차이**
### **자바의 함수 선언 (객체지향 스타일)**
```java
public class Calculator {
public static int add(int a, int b) {
return a + b;
}
}
```
```java
int result = Calculator.add(3, 5);
```
### **코틀린의 함수 선언 (함수형 스타일 지원)**
```kotlin
fun add(a: Int, b: Int): Int {
return a + b
}
```
```kotlin
val result = add(3, 5)
```
> **차이점 요약:**
> - 코틀린에서는 클래스 없이 **함수를 직접 정의 가능**(파일 수준 함수).
> - `fun` 키워드를 사용하며 **반환 타입을 `:`로 지정**.
> - 한 줄짜리 함수는 `{}` 없이 `=`로 간결하게 표현 가능.
```kotlin
fun add(a: Int, b: Int) = a + b
```
---
## **3. 람다 표현식 (Lambda Expression)**
람다 표현식은 **익명 함수(Anonymous Function)**의 한 형태로, 간결한 함수형 코드를 작성하는 데 사용된다.
### **자바의 람다 표현식 (Java 8 이상)**
```java
// 두 수를 더하는 람다 함수
BiFunction<Integer, Integer, Integer> sum = (a, b) -> a + b;
System.out.println(sum.apply(5, 3)); // 출력: 8
```
> 자바에서는 `BiFunction<T, U, R>` 같은 **함수형 인터페이스**를 사용해야 람다를 활용할 수 있다.
### **코틀린의 람다 표현식**
```kotlin
// 두 수를 더하는 람다 함수
val sum: (Int, Int) -> Int = { a, b -> a + b }
println(sum(5, 3)) // 출력: 8
```
> 코틀린은 별도의 함수형 인터페이스 없이 **람다 표현식을 바로 변수에 할당 가능**.
#### **더 간결한 표현**
코틀린에서는 **타입 추론**이 가능하므로, 타입을 생략할 수도 있다.
```kotlin
val sum = { a: Int, b: Int -> a + b }
```
> **차이점 요약:**
> - 자바는 람다를 사용하려면 `BiFunction<>` 같은 **함수형 인터페이스**가 필요.
> - 코틀린은 **별도의 인터페이스 없이 람다를 변수에 직접 할당 가능**.
> - `{ 파라미터 -> 함수 본문 }` 형태로 사용.
---
## **4. 고차 함수 (Higher-Order Function)**
고차 함수는 **다른 함수를 인자로 받거나 반환하는 함수**를 의미한다.
### **자바에서 고차 함수 구현 (익명 클래스 사용)**
자바에서는 **람다 도입 전**에는 익명 클래스를 활용해야 했다.
```java
public interface MathOperation {
int operate(int a, int b);
}
// 고차 함수
public static int executeOperation(int x, int y, MathOperation operation) {
return operation.operate(x, y);
}
// 사용 예시
int result = executeOperation(5, 3, new MathOperation() {
@Override
public int operate(int a, int b) {
return a + b;
}
});
```
자바 8 이후에는 람다 표현식 덕분에 훨씬 간결해졌다.
```java
int result = executeOperation(5, 3, (a, b) -> a + b);
```
### **코틀린에서 고차 함수 구현**
코틀린에서는 **함수를 직접 타입으로 선언 가능**.
```kotlin
fun executeOperation(a: Int, b: Int, operation: (Int, Int) -> Int): Int {
return operation(a, b)
}
// 사용 예시
val result = executeOperation(5, 3) { x, y -> x + y }
```
> **차이점 요약:**
> - 자바에서는 **인터페이스를 먼저 정의해야 하지만**, 코틀린에서는 **함수를 직접 타입으로 선언 가능**.
> - 코틀린의 람다는 **더 간결한 문법 제공**.
---
## **5. 확장 함수 (Extension Function)**
자바에서는 기존 클래스를 확장하려면 **상속(Inheritance)이나 유틸리티 클래스**를 사용해야 한다.
### **자바의 유틸리티 메서드 방식**
```java
public class StringUtils {
public static String greet(String name) {
return "Hello, " + name + "!";
}
}
System.out.println(StringUtils.greet("Java"));
```
### **코틀린의 확장 함수**
코틀린에서는 **기존 클래스에 새로운 메서드를 추가하는 것처럼 확장 가능**.
```kotlin
fun String.greet(): String {
return "Hello, $this!"
}
println("Kotlin".greet()) // 출력: Hello, Kotlin!
```
> **차이점 요약:**
> - 자바에서는 **유틸리티 클래스의 정적 메서드**를 사용해야 함.
> - 코틀린에서는 **클래스를 수정하지 않고도 확장 함수로 기능 추가 가능**.
---
## **6. 불변성과 컬렉션 처리**
### **자바의 불변 컬렉션**
```java
List<String> names = List.of("Alice", "Bob"); // 변경 불가능한 리스트 (Java 9+)
```
### **코틀린의 불변 컬렉션**
```kotlin
val names = listOf("Alice", "Bob") // 변경 불가능한 리스트
```
> **차이점 요약:**
> - 코틀린은 `listOf()`, `setOf()` 등을 통해 불변 컬렉션을 쉽게 생성 가능.
> - 자바 8까지는 `Collections.unmodifiableList()`를 사용해야 했음.
---
## **정리: 자바 vs 코틀린 함수형 프로그래밍**
| 기능 | 자바 | 코틀린 |
|------|------|--------|
| 람다 표현식 | `(a, b) -> a + b` (`BiFunction` 필요) | `{ a, b -> a + b }` (함수형 타입 지원) |
| 고차 함수 | 함수형 인터페이스 필요 | 함수 타입 `(Int, Int) -> Int` 직접 사용 가능 |
| 확장 함수 | 유틸리티 메서드 필요 | `fun String.greet() = "Hello, $this"` |
| 불변 컬렉션 | `List.of()` (Java 9+) | `listOf()` |
코틀린은 자바보다 함수형 프로그래밍을 **더 자연스럽고 간결하게 지원**한다.
다음에는 어떤 개념을 비교해볼까?

231
docs/04_OOP.md Normal file
View File

@@ -0,0 +1,231 @@
# **코틀린 vs 자바: 객체지향 프로그래밍(OOP) 비교**
코틀린과 자바는 둘 다 객체지향 프로그래밍(OOP)을 지원하는 언어지만, 코틀린은 더 간결하고 유연한 문법을 제공한다.
이번 글에서는 **클래스 선언, 생성자, 상속, 접근 제어자, 데이터 클래스, 객체 선언** 등을 중심으로 자바와 코틀린의 차이점을 비교하겠다.
---
## **1. 클래스 선언 방식 차이**
자바에서는 클래스를 선언할 때 `class` 키워드를 사용하고, **필드와 생성자를 분리**해서 작성하는 것이 일반적이다.
### **자바의 클래스 선언**
```java
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() { return name; }
public int getAge() { return age; }
}
```
### **코틀린의 클래스 선언**
코틀린에서는 **주 생성자(Primary Constructor)**를 **클래스 헤더에 직접 정의**할 수 있다.
```kotlin
class Person(val name: String, val age: Int)
```
> **차이점 요약:**
> - **자바:** 필드, 생성자, 게터를 따로 작성해야 함.
> - **코틀린:** `val` 또는 `var`을 사용하면 **자동으로 필드와 게터 생성**.
---
## **2. 생성자 (Constructors)**
자바에서는 **기본 생성자**와 **매개변수가 있는 생성자**를 따로 정의해야 한다.
### **자바의 생성자 오버로딩**
```java
public class Car {
private String model;
public Car() {
this.model = "Unknown";
}
public Car(String model) {
this.model = model;
}
}
```
코틀린에서는 **기본값을 지정**할 수 있어 생성자 오버로딩을 줄일 수 있다.
### **코틀린의 기본값 제공 방식**
```kotlin
class Car(val model: String = "Unknown")
```
> **차이점 요약:**
> - 자바는 **생성자 오버로딩**이 필요하지만,
> - 코틀린은 **기본값을 제공하면 생성자 하나로 해결 가능**.
---
## **3. 상속 (Inheritance)과 `open` 키워드**
자바에서 클래스를 상속하려면 `extends` 키워드를 사용하고, **메서드 오버라이딩 시 `@Override`를 명시**해야 한다.
### **자바의 상속**
```java
public class Animal {
public void makeSound() {
System.out.println("Some sound");
}
}
public class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("Bark!");
}
}
```
코틀린에서는 클래스가 **기본적으로 `final`이므로 상속을 허용하려면 `open` 키워드를 붙여야 한다**.
### **코틀린의 상속**
```kotlin
open class Animal {
open fun makeSound() {
println("Some sound")
}
}
class Dog : Animal() {
override fun makeSound() {
println("Bark!")
}
}
```
> **차이점 요약:**
> - **자바:** 모든 클래스가 기본적으로 **상속 가능**(`final`이 아님).
> - **코틀린:** 모든 클래스가 기본적으로 **`final`이며, 상속하려면 `open`을 붙여야 함**.
> - **오버라이딩 시 자바는 `@Override` 사용**, 코틀린은 **`override` 키워드 필수**.
---
## **4. 접근 제어자 (Access Modifiers)**
자바와 코틀린 모두 **`public`, `protected`, `private`** 접근 제어자를 제공하지만,
코틀린에는 **추가로 `internal`이 있다**.
| 접근 제어자 | 자바 | 코틀린 |
|------------|------|--------|
| `public` | 모든 곳에서 접근 가능 | 동일 |
| `protected` | 같은 패키지 + 하위 클래스에서 접근 가능 | 하위 클래스에서만 접근 가능 (패키지 무관) |
| `private` | 같은 클래스 내에서만 접근 가능 | 동일 |
| `default` (생략 시) | 같은 패키지 내에서 접근 가능 | `internal` (같은 모듈 내에서 접근 가능) |
### **코틀린의 `internal` 키워드**
```kotlin
internal class InternalClass {
fun greet() = "Hello"
}
```
> - `internal`은 **같은 모듈 내에서만 접근 가능**(자바에는 해당 없음).
---
## **5. 데이터 클래스 (Data Class)**
자바에서는 **`equals()`, `hashCode()`, `toString()` 메서드를 직접 구현**해야 한다.
### **자바의 데이터 클래스를 수동 구현**
```java
public class User {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
User user = (User) obj;
return age == user.age && Objects.equals(name, user.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public String toString() {
return "User{name='" + name + "', age=" + age + "}";
}
}
```
코틀린에서는 **`data` 키워드를 추가하는 것만으로 자동 생성**된다.
### **코틀린의 데이터 클래스**
```kotlin
data class User(val name: String, val age: Int)
```
> **차이점 요약:**
> - 자바에서는 `equals()`, `hashCode()`, `toString()`을 직접 작성해야 함.
> - 코틀린에서는 `data class`만 선언하면 자동 생성됨.
---
## **6. 싱글턴 (Singleton) 객체 선언**
자바에서 싱글턴 패턴을 구현하려면 **`static` 키워드를 사용하거나, `enum`을 활용**해야 한다.
### **자바의 싱글턴 패턴**
```java
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return INSTANCE;
}
}
```
코틀린에서는 **`object` 키워드를 사용하면 자동으로 싱글턴이 된다**.
### **코틀린의 싱글턴**
```kotlin
object Singleton {
fun greet() = "Hello"
}
println(Singleton.greet()) // Hello
```
> **차이점 요약:**
> - 자바는 **싱글턴을 만들기 위해 보일러플레이트 코드 필요**.
> - 코틀린은 `object`를 사용하면 자동으로 싱글턴 객체 생성됨.
---
## **정리: 자바 vs 코틀린 OOP 차이점**
| 기능 | 자바 | 코틀린 |
|------|------|--------|
| **클래스 선언** | 필드 + 생성자 + 게터 필요 | `class Person(val name: String)` |
| **생성자 오버로딩** | 여러 개의 생성자 정의 필요 | 기본값 사용 가능 |
| **상속** | 기본적으로 가능 (`final`을 붙여야 제한) | 기본적으로 `final`, `open`을 붙여야 상속 가능 |
| **데이터 클래스** | `equals()`, `hashCode()`, `toString()` 직접 구현 | `data class`로 자동 생성 |
| **싱글턴 패턴** | `static` 필드 사용 | `object` 키워드 사용 |
| **`protected` 접근 제어자** | 같은 패키지 + 하위 클래스 접근 가능 | 오직 하위 클래스만 접근 가능 |
코틀린은 **객체지향 프로그래밍을 더 간결하고 효율적으로 작성할 수 있도록 개선**된 언어다.
다음에는 어떤 개념을 비교해볼까?

183
docs/05_컬렉션.md Normal file
View File

@@ -0,0 +1,183 @@
# **코틀린 컬렉션과 스트림 API vs 자바 스트림 API**
코틀린은 자바와 마찬가지로 **리스트(List), 세트(Set), 맵(Map)** 등의 컬렉션을 제공한다. 하지만 자바와는 다르게, **컬렉션을 더욱 간결하게 다룰 수 있는 함수형 API**를 기본 제공하며, 대부분의 연산을 **람다식과 메서드 체이닝**을 통해 쉽게 수행할 수 있다.
이번 글에서는 **코틀린의 컬렉션 사용법과 자바 스트림 API와의 차이점**을 비교해보겠다.
---
## **1. 코틀린 컬렉션 기본 개념**
### **1.1. 불변(Immutable) vs 가변(Mutable) 컬렉션**
코틀린의 컬렉션은 **불변형(`listOf`, `setOf`, `mapOf`)**과 **가변형(`mutableListOf`, `mutableSetOf`, `mutableMapOf`)**으로 나뉜다.
자바에서는 `Collections.unmodifiableList()` 등을 사용해야 하지만, 코틀린에서는 기본적으로 **불변 컬렉션을 사용하도록 유도**한다.
#### **자바의 리스트 선언**
```java
List<String> immutableList = List.of("A", "B", "C"); // Java 9+
List<String> mutableList = new ArrayList<>();
mutableList.add("A");
mutableList.add("B");
```
#### **코틀린의 리스트 선언**
```kotlin
val immutableList = listOf("A", "B", "C") // 불변 리스트
val mutableList = mutableListOf("A", "B") // 가변 리스트
mutableList.add("C")
```
> **차이점 요약:**
> - **자바**: `List.of()`(불변) vs `ArrayList<>()`(가변)
> - **코틀린**: `listOf()`(불변) vs `mutableListOf()`(가변)
---
## **2. 코틀린 컬렉션 사용 예제**
### **2.1. 리스트(List)**
```kotlin
val numbers = listOf(1, 2, 3, 4, 5)
println(numbers[0]) // 1
println(numbers.size) // 5
```
가변 리스트:
```kotlin
val mutableNumbers = mutableListOf(1, 2, 3)
mutableNumbers.add(4)
println(mutableNumbers) // [1, 2, 3, 4]
```
---
### **2.2. 집합(Set)**
```kotlin
val names = setOf("Alice", "Bob", "Charlie")
println(names.contains("Alice")) // true
```
가변 집합:
```kotlin
val mutableNames = mutableSetOf("Alice", "Bob")
mutableNames.add("Charlie")
```
---
### **2.3. 맵(Map)**
```kotlin
val userMap = mapOf("Alice" to 25, "Bob" to 30)
println(userMap["Alice"]) // 25
```
가변 맵:
```kotlin
val mutableUserMap = mutableMapOf("Alice" to 25)
mutableUserMap["Bob"] = 30
```
---
## **3. 코틀린의 스트림 API vs 자바의 스트림 API**
자바에서는 컬렉션을 처리할 때 **스트림 API(`Stream`)**를 사용해야 하지만,
코틀린은 **컬렉션 자체가 스트림 API처럼 동작**한다.
### **3.1. 자바 스트림 예제**
```java
List<String> names = List.of("Alice", "Bob", "Charlie");
List<String> filteredNames = names.stream()
.filter(name -> name.startsWith("A"))
.map(String::toUpperCase)
.collect(Collectors.toList());
System.out.println(filteredNames); // [ALICE]
```
> **자바의 특징:**
> - `.stream()`을 호출해야 함
> - `.collect(Collectors.toList())`를 사용해야 리스트 반환
---
### **3.2. 코틀린 컬렉션 API 예제**
```kotlin
val names = listOf("Alice", "Bob", "Charlie")
val filteredNames = names
.filter { it.startsWith("A") }
.map { it.uppercase() }
println(filteredNames) // [ALICE]
```
> **코틀린의 특징:**
> - `stream()` 없이 컬렉션 자체에서 메서드 체이닝 가능
> - `collect()` 없이 리스트 반환
---
### **3.3. 숫자 리스트 필터링 & 변환**
#### **자바 (Stream API 사용)**
```java
List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6);
List<Integer> evenSquares = numbers.stream()
.filter(n -> n % 2 == 0)
.map(n -> n * n)
.collect(Collectors.toList());
System.out.println(evenSquares); // [4, 16, 36]
```
#### **코틀린 (컬렉션 API 사용)**
```kotlin
val numbers = listOf(1, 2, 3, 4, 5, 6)
val evenSquares = numbers
.filter { it % 2 == 0 }
.map { it * it }
println(evenSquares) // [4, 16, 36]
```
> **차이점 요약:**
> - **자바**: `.stream()` 호출 → `filter()`, `map()` → `.collect(Collectors.toList())` 필요
> - **코틀린**: 바로 `filter()`, `map()` 사용 가능
---
## **4. 추가적인 컬렉션 함수들**
### **4.1. `forEach`**
```kotlin
val numbers = listOf(1, 2, 3)
numbers.forEach { println(it) }
```
### **4.2. `groupBy`**
```kotlin
val words = listOf("apple", "banana", "avocado", "blueberry")
val grouped = words.groupBy { it.first() }
println(grouped)
// {a=[apple, avocado], b=[banana, blueberry]}
```
### **4.3. `associateBy` (키 매핑)**
```kotlin
data class Person(val name: String, val age: Int)
val people = listOf(Person("Alice", 25), Person("Bob", 30))
val personMap = people.associateBy { it.name }
println(personMap["Alice"]) // Person(name=Alice, age=25)
```
---
## **5. 정리: 코틀린 vs 자바 스트림 API 차이점**
| 기능 | 자바 (Stream API) | 코틀린 (컬렉션 API) |
|------|----------------|----------------|
| 스트림 사용 | `.stream()` 필요 | 컬렉션 자체에서 사용 가능 |
| 리스트 반환 | `.collect(Collectors.toList())` 필요 | 자동 반환 |
| `filter()` 사용 | `filter(n -> 조건)` | `filter { it 조건 }` |
| `map()` 사용 | `map(n -> 변환)` | `map { it 변환 }` |
| 그룹핑 | `.collect(Collectors.groupingBy())` | `groupBy { it 기준 }` |
| 가변 리스트 | `new ArrayList<>()` | `mutableListOf()` |
코틀린은 **컬렉션을 더욱 직관적이고 간결하게 다룰 수 있도록 설계**되었다.
자바에서 스트림 API를 써야 하는 많은 경우를 **코틀린에서는 기본 컬렉션 함수만으로 해결**할 수 있다.
> **코틀린으로 스트림 API를 더 쉽게 다룰 수 있다는 점을 기억하자!**

View File

@@ -0,0 +1,216 @@
# **코틀린 vs 자바: 예외 처리와 흐름 제어 차이점**
코틀린과 자바는 기본적으로 예외 처리(Exception Handling)와 흐름 제어(Control Flow)를 비슷하게 제공하지만,
코틀린은 **더 간결한 문법과 강력한 기능**을 제공하여, 예외를 보다 효과적으로 처리하고 흐름 제어를 쉽게 구현할 수 있다.
이 글에서는 **예외 처리와 흐름 제어**에서 **코틀린과 자바의 차이점**을 비교해 보겠다.
---
## **1. 예외 처리 (Exception Handling)**
자바와 코틀린 모두 **`try-catch-finally`** 블록을 지원하지만,
코틀린에서는 **예외가 표현식(Expression)으로 사용 가능**하고, **Checked Exception이 없다**는 차이가 있다.
### **1.1. 자바의 예외 처리**
```java
public class JavaExceptionExample {
public static void main(String[] args) {
try {
int result = divide(10, 0);
System.out.println(result);
} catch (ArithmeticException e) {
System.out.println("예외 발생: " + e.getMessage());
} finally {
System.out.println("예외 여부와 관계없이 실행됨");
}
}
static int divide(int a, int b) throws ArithmeticException {
return a / b; // 0으로 나누면 예외 발생
}
}
```
**출력 결과:**
```
예외 발생: / by zero
예외 여부와 관계없이 실행됨
```
> **자바의 특징:**
> - `throws` 키워드를 사용하여 Checked Exception을 선언해야 할 수 있음
> - `try-catch-finally` 블록 필요
---
### **1.2. 코틀린의 예외 처리**
```kotlin
fun main() {
try {
val result = divide(10, 0)
println(result)
} catch (e: ArithmeticException) {
println("예외 발생: ${e.message}")
} finally {
println("예외 여부와 관계없이 실행됨")
}
}
fun divide(a: Int, b: Int): Int {
return a / b // 0으로 나누면 예외 발생
}
```
**출력 결과:**
```
예외 발생: / by zero
예외 여부와 관계없이 실행됨
```
> **코틀린의 특징:**
> - `throws` 선언이 필요 없음 (Checked Exception이 없음)
> - 예외 처리를 **표현식(Expression)으로 사용할 수 있음**
---
### **1.3. 예외를 반환값으로 사용하는 경우 (코틀린만 가능)**
자바에서는 `try-catch` 블록을 표현식으로 사용할 수 없지만,
코틀린에서는 **`try` 자체가 값(value)을 반환할 수 있다**.
```kotlin
val result: Int = try {
divide(10, 2)
} catch (e: ArithmeticException) {
-1 // 예외 발생 시 기본값 반환
}
println(result) // 5
```
> **코틀린의 특징:**
> - `try-catch` 블록을 값처럼 사용 가능
> - 예외 발생 시 기본값을 쉽게 설정 가능
---
## **2. 흐름 제어 (Control Flow) 차이점**
자바와 코틀린은 기본적인 흐름 제어 구조(`if`, `when`, `for`, `while`)가 유사하지만,
코틀린은 **표현식(Expression) 기반으로 설계**되어 보다 간결하게 코드를 작성할 수 있다.
### **2.1. `if` 문법 차이**
#### **자바의 `if-else` (Statement 기반)**
```java
int number = 10;
String result;
if (number % 2 == 0) {
result = "짝수";
} else {
result = "홀수";
}
System.out.println(result);
```
#### **코틀린의 `if-else` (Expression 기반)**
```kotlin
val number = 10
val result = if (number % 2 == 0) "짝수" else "홀수"
println(result)
```
> **차이점 요약:**
> - 자바는 `if-else`가 **Statement(문장)**
> - 코틀린은 `if-else`가 **Expression(값을 반환할 수 있음)**
---
### **2.2. `switch-case` vs `when`**
자바에서는 `switch-case` 문을 사용하지만, 코틀린에서는 더 강력한 **`when`**을 제공한다.
#### **자바의 `switch-case`**
```java
int number = 2;
String result;
switch (number) {
case 1:
result = "One";
break;
case 2:
result = "Two";
break;
default:
result = "Unknown";
}
System.out.println(result);
```
#### **코틀린의 `when` (더 강력한 표현 가능)**
```kotlin
val number = 2
val result = when (number) {
1 -> "One"
2 -> "Two"
else -> "Unknown"
}
println(result)
```
> **코틀린의 특징:**
> - `when`은 **Expression**으로 값을 반환 가능
> - `break`가 필요 없음
> - 복합 조건 사용 가능 (`in`, `is` 활용 가능)
```kotlin
val result = when (number) {
in 1..5 -> "1~5 범위"
is Int -> "정수"
else -> "알 수 없음"
}
```
---
### **2.3. 반복문 차이점**
#### **자바의 `for` 반복문**
```java
List<String> names = List.of("Alice", "Bob", "Charlie");
for (String name : names) {
System.out.println(name);
}
```
#### **코틀린의 `for` 반복문**
```kotlin
val names = listOf("Alice", "Bob", "Charlie")
for (name in names) {
println(name)
}
```
> **차이점 요약:**
> - 코틀린은 **`for (element in collection)`** 형식을 사용
> - **인덱스가 필요하면 `withIndex()` 활용 가능**
```kotlin
for ((index, name) in names.withIndex()) {
println("$index: $name")
}
```
---
## **3. 결론: 코틀린이 더 간결하고 유연하다**
| 기능 | 자바 | 코틀린 |
|------|------|------|
| 예외 처리 | Checked Exception 있음 | Checked Exception 없음 |
| `try-catch` | Statement (값 반환 불가) | Expression (값 반환 가능) |
| `if-else` | Statement | Expression |
| `switch-case` | `break` 필요 | `when`으로 더 강력하게 가능 |
| `for` 반복문 | `for (item : collection)` | `for (item in collection)` |
| `for` 인덱스 | `for (int i = 0; i < list.size(); i++)` | `for ((index, item) in list.withIndex())` |
코틀린은 **표현식 기반**으로 설계되어 불필요한 문법을 줄이고 **더 간결한 코드**를 작성할 수 있다.
특히 예외 처리에서 Checked Exception이 없고, `when``try-catch`를 표현식으로 사용할 수 있어
**보다 유연하고 직관적인 코드 작성이 가능**하다. 🚀

223
docs/07_코루틴.md Normal file
View File

@@ -0,0 +1,223 @@
# **코틀린의 코루틴과 비동기 프로그래밍**
코틀린은 **코루틴(Coroutines)** 을 통해 **비동기(Asynchronous) 프로그래밍**을 쉽고 직관적으로 구현할 수 있다.
코루틴은 **스레드보다 가볍고 효율적**이며, **콜백 지옥을 피할 수 있는 강력한 기능**을 제공한다.
이 글에서는 **코루틴의 개념과 주요 기능**을 설명하고,
**비동기 프로그래밍을 구현하는 예제**를 함께 살펴보겠다. 🚀
---
## **1. 비동기 프로그래밍이란?**
비동기 프로그래밍은 **작업이 완료될 때까지 기다리지 않고 다음 코드를 실행하는 방식**이다.
즉, **시간이 오래 걸리는 작업(예: 네트워크 요청, 파일 I/O 등)** 도 프로그램이 멈추지 않고 실행된다.
### **1.1. 전통적인 방식 (콜백 지옥)**
자바에서는 비동기 작업을 콜백(callback) 방식으로 처리해야 한다.
콜백이 중첩되면 **콜백 지옥(Callback Hell)** 이 발생할 수 있다.
#### **자바의 콜백 예제**
```java
void fetchData(Callback callback) {
new Thread(() -> {
try {
Thread.sleep(2000); // 2초 대기 (네트워크 요청 가정)
callback.onSuccess("데이터를 가져왔습니다!");
} catch (InterruptedException e) {
callback.onError(e);
}
}).start();
}
interface Callback {
void onSuccess(String data);
void onError(Exception e);
}
public static void main(String[] args) {
fetchData(new Callback() {
@Override
public void onSuccess(String data) {
System.out.println(data);
}
@Override
public void onError(Exception e) {
System.out.println("에러 발생: " + e.getMessage());
}
});
}
```
> - `fetchData()` 가 비동기적으로 데이터를 가져오지만, **콜백을 계속 중첩해서 작성해야 한다.**
> - 코드가 복잡해지고 가독성이 나빠지는 문제가 있다.
---
## **2. 코루틴이란?**
**코루틴(Coroutines)** 은 **비동기 프로그래밍을 간결하고 가독성 높게** 구현할 수 있도록 도와준다.
코루틴을 사용하면 **콜백 없이도 순차적인 코드 스타일**로 **비동기 작업**을 작성할 수 있다.
> **코루틴의 특징:**
> - **스레드보다 가벼움** (필요할 때만 실행되고, 자동으로 일시 중단됨)
> - **순차적 스타일로 작성 가능** (콜백 없이 비동기 코드 작성 가능)
> - **비동기 코드가 직관적이고 읽기 쉬움**
---
## **3. 코틀린에서 코루틴 사용하기**
### **3.1. 기본적인 코루틴 사용**
```kotlin
import kotlinx.coroutines.*
fun main() = runBlocking { // 코루틴 블록 시작
launch {
delay(1000L) // 1초 대기
println("코루틴 실행!")
}
println("메인 함수 실행")
}
```
**출력 결과:**
```
메인 함수 실행
코루틴 실행!
```
> - `runBlocking {}`: 메인 함수에서 코루틴을 실행하는 블록
> - `launch {}`: 새로운 코루틴을 실행
> - `delay(1000L)`: 1초 동안 비동기 대기 (스레드 차단 없이 실행 가능)
---
## **4. 코루틴 빌더 (`launch` vs `async`)**
코루틴을 실행할 때는 **`launch`** 와 **`async`** 를 사용할 수 있다.
| 빌더 | 반환값 | 특징 |
|------|------|------|
| `launch` | 없음 (`Job` 반환) | 단순히 코루틴을 실행 |
| `async` | `Deferred<T>` 반환 | 결과 값을 반환하는 비동기 작업 |
### **4.1. `launch` 사용 예시 (결과 값 없음)**
```kotlin
import kotlinx.coroutines.*
fun main() = runBlocking {
launch {
delay(1000L)
println("launch: 비동기 작업 완료!")
}
}
```
> - `launch {}` 는 단순히 비동기 작업을 실행하고, 결과를 반환하지 않음.
> - `Job` 객체를 반환하지만, 보통 `join()`을 호출하지 않는 이상 결과를 기다리지 않음.
---
### **4.2. `async` 사용 예시 (결과 값 반환)**
```kotlin
import kotlinx.coroutines.*
fun main() = runBlocking {
val result = async {
delay(1000L)
"async: 비동기 작업 완료!"
}
println(result.await()) // 결과 값을 기다림
}
```
> - `async {}` 는 **결과 값을 반환하는 비동기 작업**을 실행.
> - `await()` 을 호출해야 실제 값을 가져올 수 있음.
---
## **5. 여러 개의 비동기 작업 실행 (`async` 활용)**
비동기 작업을 병렬로 실행하려면 **`async {}` 를 여러 개 실행**하면 된다.
```kotlin
import kotlinx.coroutines.*
fun main() = runBlocking {
val time = measureTimeMillis {
val job1 = async { fetchData(1) }
val job2 = async { fetchData(2) }
println(job1.await())
println(job2.await())
}
println("총 실행 시간: $time ms")
}
suspend fun fetchData(id: Int): String {
delay(1000L) // 네트워크 요청 시뮬레이션
return "데이터 $id 가져옴"
}
```
**출력 결과:**
```
데이터 1 가져옴
데이터 2 가져옴
총 실행 시간: 1003 ms
```
> - `async {}` 로 실행된 두 개의 코루틴이 **병렬로 실행됨**.
> - `await()` 를 호출하면 해당 코루틴이 완료될 때까지 기다림.
> - 실행 시간은 1초 내외 (동시 실행되었기 때문).
---
## **6. `suspend` 함수 사용하기**
코루틴에서 비동기 함수를 만들려면 **`suspend` 키워드**를 사용해야 한다.
```kotlin
suspend fun fetchData(): String {
delay(1000L) // 1초 대기
return "데이터 가져옴"
}
fun main() = runBlocking {
val data = fetchData()
println(data)
}
```
> - `suspend` 함수는 **코루틴 내에서만 실행 가능**
> - `delay()` 같은 비동기 함수 호출 가능
---
## **7. 예외 처리 (`try-catch` 활용)**
코루틴에서도 **예외 처리를 `try-catch` 로 할 수 있음**.
```kotlin
import kotlinx.coroutines.*
fun main() = runBlocking {
try {
val result = async { errorTask() }.await()
println(result)
} catch (e: Exception) {
println("예외 발생: ${e.message}")
}
}
suspend fun errorTask(): String {
delay(500L)
throw RuntimeException("오류 발생!")
}
```
**출력 결과:**
```
예외 발생: 오류 발생!
```
> - `try-catch` 를 사용하면 **비동기 코드에서도 예외를 안전하게 처리 가능**.
---
## **8. 정리**
| 기능 | 자바 | 코틀린 |
|------|------|------|
| 비동기 실행 | 콜백 기반 | 코루틴 기반 (`launch`, `async`) |
| 가독성 | 콜백 중첩 발생 | 순차적 코드 스타일 |
| 예외 처리 | 예외 전달 어려움 | `try-catch` 사용 가능 |
| 실행 효율 | 스레드 사용 | 가벼운 코루틴 사용 |
> **코루틴을 사용하면:**
> ✅ **비동기 코드가 더 간결하고 직관적**
> ✅ **콜백 지옥 없이 순차적 스타일로 작성 가능**
> ✅ **스레드보다 가볍고 효율적**
코틀린에서 **비동기 프로그래밍을 쉽게 구현하려면 코루틴을 적극 활용하자!** 🚀

220
docs/08_특수 기능.md Normal file
View File

@@ -0,0 +1,220 @@
# **코틀린의 특수 기능과 주요 키워드**
코틀린은 **개발 생산성을 높이는 다양한 특수 기능**을 제공한다.
이 글에서는 **코틀린의 고급 기능**을 **사용 예시와 함께** 설명하겠다. 🚀
### ✨ **소개할 내용**
1. **`inline` 함수**
2. **`reified` 키워드**
3. **제너릭(Generic)**
4. **위임(Delegation) 패턴과 `by` 키워드**
---
## **1. `inline` 함수: 함수 호출 비용 줄이기**
코틀린의 `inline` 함수는 **함수 호출 오버헤드를 줄이기 위해 사용**된다.
### **1.1. 기본적인 `inline` 함수 예제**
```kotlin
inline fun execute(block: () -> Unit) {
println("실행 시작")
block()
println("실행 종료")
}
fun main() {
execute {
println("실제 로직 실행")
}
}
```
**출력 결과:**
```
실행 시작
실제 로직 실행
실행 종료
```
> - **함수 호출이 사라지고 코드가 그대로 복사됨 (인라이닝)**
> - **람다를 인자로 받을 때 유용**
> - 단점: **코드 크기가 증가할 수 있음 (작은 함수에만 사용)**
---
## **2. `reified` 키워드: 실행 시점에서 타입 유지**
코틀린에서는 **제너릭 타입 정보가 실행 시점에 사라지는 문제(type erasure)가 있음**.
하지만 `reified` 키워드를 사용하면 **실행 시점에서도 타입을 유지할 수 있음**.
### **2.1. `reified` 없이 제너릭 타입 확인 불가**
```kotlin
fun <T> getClassName(): String {
return T::class.simpleName // 오류 발생!
}
fun main() {
println(getClassName<String>())
}
```
> - 오류 발생! **제너릭 타입은 실행 시점에서 사라지기 때문**.
### **2.2. `reified` 키워드 사용하여 해결**
```kotlin
inline fun <reified T> getClassName(): String {
return T::class.simpleName ?: "알 수 없음"
}
fun main() {
println(getClassName<String>()) // "String"
println(getClassName<Int>()) // "Int"
}
```
> - **`reified`를 사용하면 실행 시점에서도 제너릭 타입을 알 수 있음**
> - 주의: **`inline` 함수에서만 `reified` 사용 가능**
---
## **3. 제너릭(Generic): 타입을 유연하게 만들기**
제너릭을 사용하면 **코드의 재사용성을 높이고, 타입 안정성을 유지할 수 있음**.
### **3.1. 기본적인 제너릭 사용**
```kotlin
class Box<T>(val value: T) {
fun get(): T = value
}
fun main() {
val intBox = Box(123)
val strBox = Box("Hello")
println(intBox.get()) // 123
println(strBox.get()) // Hello
}
```
> - `T`를 사용하여 **어떤 타입이든 저장할 수 있는 클래스**를 정의
### **3.2. 제너릭 함수 사용**
```kotlin
fun <T> printItem(item: T) {
println("아이템: $item")
}
fun main() {
printItem(42) // 아이템: 42
printItem("코틀린") // 아이템: 코틀린
}
```
> - **함수에도 제너릭 적용 가능**
---
## **4. 위임(Delegation) 패턴과 `by` 키워드**
코틀린의 `by` 키워드는 **객체의 기능을 다른 객체에 위임할 때 사용**된다.
이를 통해 **불필요한 코드 중복을 방지**할 수 있다.
### **4.1. 인터페이스를 이용한 기본 위임 패턴**
```kotlin
interface Printer {
fun printMessage()
}
class ConsolePrinter : Printer {
override fun printMessage() {
println("콘솔에 출력")
}
}
class SmartPrinter(private val printer: Printer) : Printer {
override fun printMessage() {
println("스마트 프린터 작동 중...")
printer.printMessage()
}
}
fun main() {
val printer = SmartPrinter(ConsolePrinter())
printer.printMessage()
}
```
**출력 결과:**
```
스마트 프린터 작동 중...
콘솔에 출력
```
> - `SmartPrinter`가 `ConsolePrinter`의 기능을 **위임(Delegation)**
### **4.2. `by` 키워드를 사용한 간결한 위임 패턴**
```kotlin
class SmartPrinter2(private val printer: Printer) : Printer by printer
fun main() {
val printer = SmartPrinter2(ConsolePrinter())
printer.printMessage() // "콘솔에 출력"
}
```
> - `by printer` 를 사용하면 **위임을 자동으로 처리**
> - 코드가 훨씬 간결해짐
---
## **5. `by lazy`를 활용한 지연 초기화**
`by lazy`를 사용하면 **객체를 필요할 때만 초기화**할 수 있다.
```kotlin
class Example {
val lazyValue: String by lazy {
println("초기화 중...")
"Hello, Kotlin!"
}
}
fun main() {
val example = Example()
println("객체 생성 완료")
println(example.lazyValue) // 여기서 초기화 발생
println(example.lazyValue) // 이미 초기화됨, 출력만 함
}
```
**출력 결과:**
```
객체 생성 완료
초기화 중...
Hello, Kotlin!
Hello, Kotlin!
```
> - `by lazy`는 **최초 접근 시점에만 실행됨**
> - 이후에는 **이미 생성된 값을 사용**
---
## **6. `object` 키워드: 싱글톤 객체**
코틀린에서 싱글톤 객체를 만들 때 **`object` 키워드**를 사용한다.
```kotlin
object Singleton {
fun showMessage() {
println("싱글톤 객체 실행!")
}
}
fun main() {
Singleton.showMessage()
}
```
**출력 결과:**
```
싱글톤 객체 실행!
```
> - **별도의 인스턴스 생성 없이** `Singleton.showMessage()` 사용 가능
> - **앱 설정, 네트워크 요청 관리 등에 유용**
---
## **🔹 정리: 코틀린의 특수 기능 요약**
| 기능 | 설명 | 사용 예시 |
|------|------|------|
| `inline` 함수 | 함수 호출을 줄이고 성능 향상 | `inline fun execute(block: () -> Unit) {}` |
| `reified` | 실행 시점에서도 제너릭 타입 유지 | `inline fun <reified T> getType() = T::class.simpleName` |
| 제너릭 | 코드의 재사용성과 타입 안정성 유지 | `class Box<T>(val value: T)` |
| Delegation | 클래스 기능을 다른 객체에 위임 | `class PrinterImpl : Printer by ConsolePrinter()` |
| `by lazy` | 지연 초기화 | `val name: String by lazy { "코틀린" }` |
| `object` | 싱글톤 객체 | `object Singleton {}` |
코틀린의 강력한 기능들을 활용하면 **코드를 더 간결하고 효율적으로 작성**할 수 있다.
이제 **직접 사용해보면서** 익숙해지자! 🚀

55
docs/README.md Normal file
View File

@@ -0,0 +1,55 @@
### **코틀린 원포인트 레슨: 자바 개발자를 위한 빠른 학습서**
#### **목차**
#### **1. 코틀린 시작하기**
1.1 코틀린의 특징과 철학
1.2 자바 코드와의 상호운용성
1.3 코틀린 개발 환경 설정 (IntelliJ, Gradle, Maven)
#### **2. 기본 문법과 차이점**
2.1 변수 선언: `val``var`
2.2 null 안전성: `?` 연산자와 `!!` 연산자
2.3 타입 추론과 `is` 연산자
#### **3. 함수형 프로그래밍 요소**
3.1 함수 선언 방식 (`fun` 키워드)
3.2 고차 함수와 람다 표현식
3.3 확장 함수와 확장 프로퍼티
#### **4. 객체지향 프로그래밍 차이점**
4.1 클래스와 생성자 (기본, 보조 생성자)
4.2 데이터 클래스와 자바의 `record` 비교
4.3 객체 선언 (`object`)과 싱글턴 패턴
4.4 `sealed class``enum class`
#### **5. 코틀린 컬렉션과 스트림 API**
5.1 리스트, 맵, 셋 사용법
5.2 `filter`, `map`, `reduce` 활용법
5.3 자바 스트림 API와 비교
#### **6. 예외 처리와 흐름 제어**
6.1 `try-catch``check` 키워드
6.2 `when` 표현식과 자바의 `switch` 비교
#### **7. 코루틴과 비동기 프로그래밍**
7.1 `suspend` 함수와 `async/await`
7.2 `launch`, `runBlocking`, `withContext` 개념
#### **8. 코틀린의 특수 기능**
8.1 `inline` 함수와 `crossinline`, `noinline`
8.2 `reified` 키워드와 제네릭
8.3 `delegation` 패턴과 `by` 키워드
#### **9. 코틀린을 활용한 실제 개발**
9.1 Spring Boot에서 코틀린 활용하기
9.2 Android 개발에서 코틀린 적용하기
9.3 Kotlin DSL과 Gradle 설정
#### **10. 자바에서 코틀린으로 마이그레이션**
10.1 코틀린으로 코드 변환 (IntelliJ 기능 활용)
10.2 자바-코틀린 혼용 프로젝트 관리
10.3 코틀린 도입 시 고려할 점
---
이 책의 핵심은 자바 개발자가 코틀린을 빠르게 이해하고 실무에서 바로 활용할 수 있도록 짧고 명확한 레슨으로 구성하는 것입니다. 추가하고 싶은 내용이 있으면 말해줘!