카테고리 없음

✍️코틀린 & 컴포즈 문법

minnote29 2025. 4. 16. 15:00

🍎 코틀린

코틀린(kotlin)은 IntelliJ, PyCharm 등 다양한 IDE를 선보인 것으로 유명한 JetBrains 사에서 오픈소스 그룹을 만들어 개발한 프로그래밍 언어이다.

기존에는 안드로이드 앱을 자바로 개발했지만, 코틀린의 등장으로 점점 대체 되어 가는 추세다.

  • 2017년 구글에서 안드로이드 공식 언어로 지정되었고,
  • 2021년 신규 앱 개발시 코틀린으로 개발할 것을 권장되고 있다.

코틀린은 어떻게 Java를 대체할 수 있었을까? 바로 JVM 기반 언어라는 특성 때문이다. JVM 기반 언어를 다르게 얘기해보자면, 어떠한 언어도 자바 바이트 코드(.class)로 컴파일할 수 있다면 동작할 수 있는 것이다.

 

jvm - 자바 프로그램을 실행하기 위한 실행 환경을 만들어 주는 소프트웨어다.
이러한 JVM은 흔히 자바를 설치할 때 설치하는 JDK (Java Development Kit)의 JRE(Java Runtime Environment)에 포함되어 있다.
jvm 기반 언어 - 사실 jvm 기반 언어라는 특성은 호환성이 매우 좋아 특별하기 때문에 코틀린 외에도 굉장히 많다. 대표적인 언어로, 스칼라(scala), 그루비(Groovy), Jython(Python)가 있다.

 

🍎 코틀린 언어의 특징 

  1. 표현력과 간결함(expressive and concise): 매우 간결한 문법을 제공한다.
  2. 안전한 코드(safer code): 코틀린에서는 변수 선언시 널 허용과 불허용을 구분해서 선언할 수 있다.
  3. 상호 운용성(interoperable): Java와 100% 호환된다.
  4. 구조화 동시성(structed concurrency): 코루틴(coroutines) 기법을 이용하면 비동기 프로그래밍을 간소화할 수 있다.

 

🍎 기본 문법

📌 변수

변수는 val과 var 키워드로 선언한다. 

  • val (value): 불변성 변수
  • var (variable): 가변성 변수
val data1 = 10
var data2 = 10

fun main(){
	data1 = 20 // 오류!
	data2 = 20 // 성공!
}

 

코틀린은 일반적으로 정적 타이핑 언어이지만, 타입 추론이 가능한 경우에는 위와 같이 생략할 수 있다. ⇒ 동적 타이핑 기능(타입 추론) 제공.

또한 명시적으로 타입을 선언할 수 있다.

val data1:Int = 10
  • lateinit - 변수를 선언하면 변수의 초기화를 미룰 수 있다. (var 키워드로 선언한 변수에만 사용 가능. Java에서 Primitive 타입으로 분류되는 Int, Long, Short, Double, Float, Boolean, Byte에는 사용할 수 없음.)
  • lazy 키워드는 변수 선언문 뒤에 by lazy {}형식으로 선언하며, 변수가 최초로 이용되는 순간 중괄호로 묶은 부분이 자동으로 실행되어 그 결과 값이 변수의 초기 값으로 할당된다.

 

타입 - 코틀린의 모든 변수는 객체이다. 자바에서 제공하는 원시 타입 개념 존재하지 않으며, 코틀린에서는 해당 타입들은 모두 객체로 제공된다.

  1. 기초 타입 객체: Int, Short, Long, Double, Float, Byte, Boolean
  2. 문자와 문자열: Char, String
  3. 모든 타입: Any (Java로 치면 Object)
  4. 반환문이 없는 함수: Unit
  5. null이나 예외를 반환하는 함수: Nothing
    ⇒ Noting으로 선언한 변수는 null만 대입할 수 있다.
    • 컬렉션 타입(collection type) - 여러 개의 데이터를 표현하는 방법이며 Array, List, Set, Map 이 있다.
    • Array - 배열을 표현하는 클래스로, 배열에 접근할 때는 대괄호([])를 이용하거나, set(), get() 함수를 이용한다. -> 배열의 타입은 제네릭(generic), 배열의 타입이 기초 타입이라면 Array를 이용하지 않고 각 기초 타입의 배열을 나타내는 클래스를 이용할 수 있다. arrayOf() 함수를 이용하면 선언과 동시에 할당할 수 있다.
    • List, Set, Map - Collection 인터페이스를 타입으로 표현한 클래스이며, 컬렉션 타입 클래스라고 한다. 각 클래스의 특징은 다음과 같다.
      • List - 순서가 있는 데이터 집합. 데이터의 중복 허용
      • Set - 순서가 없으며 데이터의 중복을 허용하지 않음
      • Map - 키와 값으로 이루어진 데이터 집합으로 순서가 없으며, 키의 중복을 허용하지 않음
  • 널 허용과 불허용코틀린에서는 변수를 선언할 때 null을 대입할 수 있는 변수인지 아닌지 명확하게 구분해서 선언해야 한다.
  • 널 허용으로 선언하려면 타입 뒤에 물음표(?)를 추가해야 한다.
  • 코틀린의 모든 변수는 객체이기 때문에 null 값이 들어갈 수 있습니다. null 은 값이 할당되지 않은 상황을 의미한다.

 

📌 함수

함수를 선언할 때는 fun 키워드를 사용한다. 아래는 함수 선언 형식이다.

fun 함수명(매개변수명: 타입): 반환 타입 { ... }
  • 함수의 매개변수에는 val이나 var 키워드를 사용할 수 없으며, val로 자동 적용된다. (즉, 불변 변수, 대입 불가)
  • 함수의 매개변수에는 기본 값을 선언할 수 있다. 기본 값을 선언했다면, 호출시 인자를 전달하지 않아도 된다.
  • 함수 호출 시 매개 변수 명을 지정하면 매개변수 값의 순서를 바꿀 수 있다. (명명된 매개변수)

Kotlin에서는 if~else는 표현식으로도 사용할 수 있다. 그리고 when 조건문, for 반복문, while 반복문 등을 사용할 수 있다.

 

📌 클래스, 생성자

클래스

 

  • 객체(Object)를 만들기 위한 설계도라고 보면 되겠다.
  • 필드(속성) + 메서드(기능)를 함께 가진다.

생성자

  • 객체를 생성할 때 실행되는 함수, 즉 "문 열고 들어가는 방식"이다.
  • 코틀린에서는 크게 2가지 생성자가 있다:

 

주 생성자 클래스 헤더에 정의됨 (val name: String)
보조 생성자 클래스 본문 내부에서 여러 개 정의 가능 constructor(...)
init 블록 객체 생성 직후 초기화 작업 실행 init
기본값 생성자 매개변수에 기본값 부여 = 값

 

📌 람다식과 고차 함수

람다(Lambda)

  • 익명 함수로, 이름 없이 함수처럼 사용 가능하다.
  • 간결하게 함수형 프로그래밍 가능하다.
val add = { a: Int, b: Int -> a + b }
println(add(3, 4)) // 출력: 7

 

고차 함수

  • 함수를 매개변수로 받거나, 반환하는 함수
 
fun operate(x: Int, y: Int, op: (Int, Int) -> Int): Int {
    return op(x, y)
}

val result = operate(10, 5) { a, b -> a * b }
println(result) // 출력: 50

 

📌 데이터 클래스 (data class)

특징

  • 단순히 데이터를 저장하기 위한 클래스
  • 코틀린에서 data 키워드를 붙이면 toString, equals, copy 같은 함수들이 자동 생성된다.
  • 자동으로 만들어지는 함수들:
    • toString(): 문자열 표현
    • equals(): 객체 비교
    • hashCode(): 해시 값
    • copy(): 복사본 생성
  • 코루틴 (Coroutine)
    • 비동기 작업을 간단하게 처리할 수 있는 코틀린의 핵심 기능
    • Thread보다 가볍고 효율적
    • suspend, launch, async, delay 등이 주요 키워드
    • 주요 키워드
      • runBlocking: 코루틴 실행을 기다림 (main에서 주로 사용)
      • launch: 새 코루틴 시작 (비동기 실행)
      • delay: 비동기 대기
      • async: 값을 반환하는 비동기 작업

 

 

 

🍎 Compose

Jetpack Compose는 안드로이드 UI를 훨씬 쉽고, 선언형으로, 간결하게 만들어주는 혁신적인 도구로, 클래식 XML UI보다 가독성도 좋고, 생산성도 훨씬 높아졌다.

개념 설명
선언형 UI '무엇을 보여줄 것인지'만 선언하면 된다.
Composable 함수 UI를 구성하는 빌딩 블록
상태(State) 관리 UI 상태 변화에 따라 자동으로 리렌더링
재구성(Recomposition) 상태가 바뀌면 필요한 부분만 다시 그린다.
Slot API 구성 요소를 유연하게 끼워넣는 방식
Modifier UI 스타일링 및 동작을 부여하는 체이닝 객체

 

  • Composable 함수 - 모든 UI 구성 요소는 @Composable로 시작하는 함수 안에서 정의된다.
  • 상태 (State) 관리 - UI가 반응형이 되려면 상태 변화에 따라 자동으로 UI가 업데이트되어야 한다.
  • Modifier -  UI 요소의 크기, 정렬, 배경색, 클릭 이벤트 등을 설정할 때 사용한다.
    • Modifier는 체이닝 방식으로 연결 가능
    • 매우 유연하고 선언형 스타일과 잘 맞음
Text(
    text = "Hello!",
    modifier = Modifier
        .padding(16.dp)
        .background(Color.Yellow)
        .clickable { println("클릭됨!") }
)

 

📌 레이아웃 기본 요소

컴포넌트 설명
Column 위에서 아래로 정렬
Row 왼쪽에서 오른쪽으로 정렬
Box 겹치게 배치
LazyColumn RecyclerView 대체 (스크롤 가능한 리스트)
@Composable
fun MyLayout() {
    Column(
        modifier = Modifier.padding(16.dp)
    ) {
        Text("제목", fontSize = 20.sp)
        Row {
            Text("왼쪽")
            Spacer(modifier = Modifier.width(10.dp))
            Text("오른쪽")
        }
    }
}

 

  • Slot API (슬롯을 전달하는 방식) - 컴포즈에서는 UI 조각들을 함수에 인자로 넘길 수 있다. 이것이 Slot API.
@Composable
fun MyCard(content: @Composable () -> Unit) {
    Card(
        modifier = Modifier.padding(8.dp),
        elevation = 4.dp
    ) {
        content()
    }
}

@Composable
fun Usage() {
    MyCard {
        Text("카드 안에 들어간 텍스트")
    }
}

 

 

  • Scaffold – 전체 구조를 잡는 컴포넌트 -> Scaffold는 앱의 구조(상단 바, FAB 등)를 쉽게 구성할 수 있게 해준다.
  • 왜 Scaffold를 사용할까? 아래 요소들을 각자 따로 배치하려면 복잡하고 번거롭지만, Scaffold를 사용하면 딱 정해진 틀에 쉽게 끼워 넣기만 하면 된다.
요소 예시
상단 앱 바 TopAppBar
하단 네비게이션 BottomNavigation
플로팅 버튼 FloatingActionButton
바디(내용 영역) Scroll 가능한 내용 등
스낵바 사용자 알림 (Snackbar)
@Composable
fun MyScreen() {
    Scaffold(
        topBar = {
            TopAppBar(title = { Text("내 앱") })
        },
        floatingActionButton = {
            FloatingActionButton(onClick = {}) {
                Icon(Icons.Default.Add, contentDescription = "추가")
            }
        }
    ) {
        // 본문 내용
        Text("내용입니다")
    }
}

 

  • 각 요소가 자리와 역할이 명확히 분리되어 있어서 유지보수가 쉽고,
  • 다양한 화면에도 동일한 레이아웃 틀을 재사용할 수 있다.

Scaffold 없이 구성한다면

Column {
    TopAppBar(title = { Text("내 앱") })
    // FAB는 어디 두지... 레이아웃 신경 써야 함
    // Snackbar도 별도 상태관리 필요
}
  • 각 컴포넌트를 직접 배치해야 하고, FAB나 Snackbar 위치, 동작 관리가 까다롭다.