챗gpt 유료버전을 결제했다! 완전 똑똑이야


Listof와 MutableListOf

Kotlin에서 컬렉션 타입(collection type)은 여러 개의 데이터를 표현하는 방법이며 Array, List, Set, Map이 있다. List, Set, Map은 Collection 인터페이스를 구현한 클래스이며, 이를 통틀어 컬렉션 타입 클래스라고 한다. 그중 List순서가 있는 데이터 집합으로 데이터의 중복을 허용한다.

Collection 타입의 클래스는 가변 클래스불변 클래스로 나뉜다. Kotlin에서는 불변 타입인 List, 가변 타입인 MutableList를 제공한다. 불변 타입은 요소 추가, 삭제가 불가능하며, 가변 타입은  .add(), .remove() 등의 메서드를 통해 값을 자유롭게 수정할 수 있다. 

listOf()함수로 List 객체를, mutableListOf()함수를 통해 MutableList를 생성할 수 있다. 또한 mutableListOf() 함수는 타입 추론이 가능하기 때문에 명시적인 타입 선언 없이도 앞서 사용한 타입을 그대로 사용할 수 있다.

fun main() {

    // Immutable list
    // val shoppingList = listOf("Processor", "RAM", "Graphics Card", "SSD")

    // mutable list
    val shoppingList = mutableListOf("Processor", "RAM", "Graphics Card RTX 3060", "SSD")

    // adding items to lists
    shoppingList.add("Cooling System")
    shoppingList.remove("Graphics Card RTX 3060")
    shoppingList.add("Graphics Card RTX 4090")

    println(shoppingList) // [Processor, RAM, SSD, Cooling System, Graphics Card RTX 4090]
}

 

  •  Array와 List의 차이점

Array는 고정 크기의 배열, List는 요소 개수 제한 없는 컬렉션이다.

 

  • mutableListOf()를 val로 선언하는 이유

val은 참조(reference)를 바꿀 수 없다는 뜻으로, 그 안에 있는 내용(data)는 바꿀 수 있다.

val list = mutableListOf("a", "b", "c")
list.add("d")  // ✅ 가능
list.remove("b")  // ✅ 가능
// list = mutableListOf("x", "y")  // ❌ 안 됨! -> 참조 자체를 바꾸려 하니까

val list로 선언한 것은 이 변수가 특정 리스트를 가리키는 참조(주소)를 바꾸지 않겠다는 의미다. 이 리스트의 주소는 고정되지만, 리스트 내부의 요소는 변경할 수 있다. 반면 var로 선언하면, 이 변수는 나중에 다른 리스트를 가리키는 것으로 변경될 수 있다. 즉, 리스트 전체를 새로 덮어쓸 수 있다는 의미다.
따라서 일반적으로는 리스트 안의 요소만 바꾸고 참조는 유지하는 경우가 많기 때문에, 리스트 전체를 다른 것으로 바꾸는 실수를 막기 위해 val 선언을 권장한다.

 

리스트에서의 set 메서드

set() 메서드는 리스트에서 특정 인덱스의 항목을 새로운 값으로 교체사용된다. 개의 매개변수를 받으며, 번째는 바꾸고 싶은 요소의 인덱스, 번째는 새로 넣을 이다.

 

Contains 메서드 - 항목이 리스트에 있는지 확인

contains() 메서드는 리스트 안에 특정 항목이 포함되어 있는지 여부를 확인할 때 사용한다. 찾고 싶은 값을 인자로 전달하면 되고, 결과는 Boolean 타입으로 반환된다.

 

Kotlin에서 리스트 조작 연습

package com.example.kotlinbasics

fun main() {
    val fruitsList = mutableListOf("apple", "peach", "strawberry", "cherry", "banana")
    println(fruitsList)
    fruitsList.add("melon")
    println(fruitsList)
    fruitsList.remove("apple")
    println(fruitsList)
    if (fruitsList.contains("watermelon")) {
        println("Has watermelon")
    }
    else {
        println("No watermelon")
    }
}

 

 

리스트와 함께 사용하는 for루프

  for(item in shoppingList) {
        println(item)
        if (item == "RAM"){
            shoppingList.removeLast()
            break;
        }
    }

    println(shoppingList)

 

for 루프에서 인덱스 가져오기

  • for (i in 1..10) { ... } : 1부터 10까지 1씩 증가
  • for (i in 1 until 10) { ... } : 1부터 9까지 1씩 증가(10 미포함)
  • for (i in 2...10 step 2) { ... } : 2부터 10까지 2씩 증가
  • for (i in 10 downTo 1) { ... } : 10부터 1까지 1씩 감소

 

Amy와 그녀의 커피 함수

커피 없이는 코드 한 줄도 못짜는 Amy(와 본인)

코틀린에서 함수를 선언하려면 fun이라는 키워드를 이용한다. 

  • 함수 선언 형식 : fun 함수명 (매개변수명: 타입) : 반환 타입 {...}
fun makeCoffee(name : String, sugarCount : Int) {
    if (sugarCount == 0) {
        println("Coffee with no sugar for $name")
    }
    else if (sugarCount == 1) {
        println("Coffee with $sugarCount spoon of sugar for $name")
    }
    else {
        println("Coffee with $sugarCount spoons of sugar for $name")
    }
}

 

함수의 매개변수에는 기본값을 선언할 수 있다. 만약 어떤 매개변수에 기본값을 선언했다면 호출할 때 인자를 전달하지 않아도 되며 이때 선언문에 명시한 기본값이 적용된다. 

fun read(
    b: ByteArray,
    off: Int = 0,
    len: Int = b.size,
) { /*...*/ }

 

사용자 입력과 함께 커피는 누구를 위한 것인가

package com.example.kotlinbasics

fun main() {
    println("Who is this coffee for?")
    val name = readln()
    println("How many pieces of sugar do you want?")
    val sugarCount = readln().toInt()

    makeCoffee(name, sugarCount)
}

// Define Function
fun makeCoffee(name : String, sugarCount : Int) {
    if (sugarCount == 0) {
        println("Coffee with no sugar for $name")
    }
    else if (sugarCount == 1) {
        println("Coffee with $sugarCount spoon of sugar for $name")
    }
    else {
        println("Coffee with $sugarCount spoons of sugar for $name")
    }
}

 

반환 유형에 대해 더 알아보기

fun divide(num1 : Int, num2 : Int):Double {
    val result = num1 / num2
    return result
}

해당 함수는 에러가 발생한다. Int와 Int를 나누면 결과가 Int일 것이라고 예상하지만, 반환 타입을 Double로 명시해두었기 때문에 오류가 발생하는 것이다.
이 문제를 해결하려면 num1과 num2 모두를 Double로 변환하거나, 둘 중 하나만 Double로 변환해도 된다.

 

클래스와 객체 생성 그리고 생성

코틀린에서 클래스는 class 키워드로 선언한다. 이때

클래스 이름은 무조건 단수로 짓는다.

 

클래스의 멤버는 생성자, 변수, 함수, 클래스로 구성된다. 이 중 생성자는 constructor 키워드로 선언하는 함수이다. 그리고 클래스 안에 다른 클래스를 선언할 수도 있다. 이때 생성자는 주 생성자보조 생성자로 구분되는데 주 생성자는 클래스 선언부에 constructor 키워드를 사용해 정의하며 반드시 선언해야 하는 것은 아니고 한 클래스에 하나만 가질 수 있다. 만약 어노테이션이나 public, private과 같은 접근 제한자가 없다면, constructor 키워드는 생략할 수 있다.

 

init 키워드로 지정한 영역은 클래스 객체를 생성하는 순간 자동으로 실행된다. 

class User(name: String, count: Int) {
	init {
    	println("I am init...")
    }
}
fun main() {
	val user = User("yeonee", 10)
}

// i am init...

 

생성자의 매개변수는 기본적으로 생성자에서만 사용할 수 있는 지역 변수이다. 즉 init 영역에서만 사용가능하다.

class User(name: String, count: Int) {
	init {
    	println("name : $name, count : $count")	// 성공
    }
    
   fun someFun() {
   	println("name : $name, count : $count")	// 오류
   }
]

 

코틀린은 프로퍼티를 선언하고, 그것을 주 생성자에서 초기화하는 간결한 문법을 제공한다. 주 생성자 안에서 val이나 var 키워드로 선언하면 클래스의 멤버 변수가 된다. 

멤버 변수, 즉 프로퍼티는 실제 객체의 일부라는 뜻이고 매개변수는 그저 객체에 정보를 전달한 것이다. 변수나 클래스의 프로퍼티를 String으로 다루고 싶다면 중괄호를 추가해서 어디서부터 어디까지가 영역인지 구분한다. 또한 프로퍼티에도 기본값을 줄 수 있다.

package com.example.kotlinbasics

class Book (val title : String = "Unknown",
            val author : String = "Anonymous", 
            val yearPublished : Int = 2025) {
}
package com.example.kotlinbasics

fun main() {
    val myBook = Book()
    println("title : ${myBook.title}, " +
            "author : ${myBook.author}, " +
            "yearPublished : ${myBook.yearPublished}")
}

 

데이터 클래스와 커피

코틀린의 데이터 클래스는 주로 데이터를 저장하는 데 사용된다. class대신에 data class 키워드를 사용하여 선언하다.data class를 사용하면 toString(), equals(), hashCode() 같은 유용한 함수들이 자동으로 만들어진다. 데이터 클래스에서는 별도의 함수 정의가 불가능하다.

 

데이터 클래스의 주요 특징은 다음과 같다.

  • Immutability : 불변성
  • Standard Methods : 표준 메서드
  • Destructuring Declarations : 구조 분해 선언

우선, 데이터 클래스는 주로 val을 사용해 불변 속성을 권장하므로, 단순하고 변경되지 않는 데이터를 표현하기에 적합하다. 또한, toString(), equals(), hashCode() 같은 기본 메서드들을 코틀린이 자동으로 생성해준다. 마지막으로, 구조 분해 선언을 지원해 객체의 속성들을 개별 변수로 간편하게 꺼내 쓸 수 있다.

package com.example.kotlinbasics


data class CoffeeDetails(
    val sugarCount: Int,
    val name: String,
    val size: String,
    val creamAmount: Int)

fun main() {
    val coffeeForYeonee = CoffeeDetails(0, "yeonee", "L", 0)
    makeCoffee(coffeeForYeonee)
}

// Define Function
fun makeCoffee(coffeeDetails: CoffeeDetails) {
    if (coffeeDetails.sugarCount == 0) {
        println("Coffee with no sugar for ${coffeeDetails.name}")
    }
    else if (coffeeDetails.sugarCount == 1) {
        println("Coffee with ${coffeeDetails.sugarCount} " +
                "spoon of sugar for ${coffeeDetails.name}")
    }
    else {
        println("Coffee with ${coffeeDetails.sugarCount} spoons of sugar for ${coffeeDetails.name}")
    }
}

 

요약

https://tutorials.eu/basic-kotlin-syntax-functions-objects-and-classes-in-kotlin-day-3-android-14-masterclass/

2일차! 부족한 개념은 Do it! 책을 활용했습니당. 코틀린 공식 문서가 굉장히 잘 되어있네요... 그치만 영어는 시러

몇 가지 부분은 건너뛰었습니당


Int 데이터 유형

Type Size (bits) Min value Max value
Byte 8 -128 127
Short 16 -32768 32767
Int 32 -2,147,483,648 (-231) 2,147,483,647 (231 - 1)
Long 64 -9,223,372,036,854,775,808 (-263) 9,223,372,036,854,775,807 (263 - 1)

 

Kotlin에서는 변수를 선언하는 두 가지 방법으로 val과 var이 있다.

  • val : value의 줄임말로, 불변하는 값. 초깃값을 할당하면 재할당 할 수 없다.
  • var : variable의 줄임말로 변하는 값. 초깃값을 할당하고 나서도 재할당 할 수 있다.

변수명 뒤에는 콜론(:)을 추가해 타입을 명시할 수 있으며, 타입을 명시하지 않아도 자동으로 Int로 저장된다. 이를 타입 추론이라고 한다.  만약 Int 범위가 넘어간다면 자동으로 Long으로 저장된다.

 

큰 숫자를 사용할 예정으로 Long을 쓰고 싶다면 L을 표기한다. 만약 Byte 또는 Short를 사용하고 싶다면 콜론 기호(:)를 통해 명시적으로 선언한다. 

val one = 1 // Int
val threeBillion = 3000000000 // Long
val oneLong = 1L // Long
val oneByte: Byte = 1

 

Hello World 실행

온라인에서 코틀린을 실행시킬 수 있는 사이트

https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMi4xLjIwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiLyoqXG4gKiBZb3UgY2FuIGVkaXQsIHJ1biwgYW5kIHNoYXJlIHRoaXMgY29kZS5cbiAqIHBsYXkua290bGlubGFuZy5vcmdcbiAqL1xuZnVuIG1haW4oKSB7XG4gICAgcHJpbnRsbihcIkhlbGxvLCB3b3JsZCEhIVwiKVxufSJ9

 

부호 없는 정수

Type Size (bits) Min value Max value
UByte 8 0 255
UShort 16 0 65,535
UInt 32 0 4,294,967,295 (232 - 1)
ULong 64 0 18,446,744,073,709,551,615 (264 - 1)

 

-128 ~ 127 대신 0~255를 사용한다. 1이 아닌 0부터 시작하고 양수 방향으로만 범위가 커진다.  

 

대입하는 값 끝에 u또는 U를 붙여줘야 한다. 대문자, 소문자는 관계없다. 

val b: UByte = 1u  // UByte, expected type provided
val s: UShort = 1u // UShort, expected type provided
val l: ULong = 1u  // ULong, expected type provided

val a1 = 42u // UInt: no expected type provided, constant fits in UInt
val a2 = 0xFFFF_FFFF_FFFFu // ULong: no expected type provided, constant doesn't fit in UInt
val a = 1UL // ULong, even though no expected type provided and the constant fits into UInt

정수의 전체 bit범위를 양수로 사용하고 싶을 때 부호없는 정수를 사용한다.

 

Booleans true, false, 부정

오직 두가지 값 true 또는 false를 가진다. Boolean?은 true, false, null 세 가지 값 중 하나를 가질 수 있다.

또한 세 개의 내장된 연산자를 가진다. 

  • ||
  • &&
  • !

 

Char, 유니코드 및 백슬래시 이스케이프 문자

하나의 문자만 저장 가능하며 작은 따옴표(')로 감싸서 표현한다. 

 

유니코드를 표현하려면, \u를 붙이고 뒤에 유니코드 숫자만 붙인다.

println('\uFF00')

 

문자열

일련의 문자, 즉 단어나 문단들 같은 긴 문자 같은 것들을 저장할 수 있다. 큰따옴표(")로 감싸서 표현한다. 문자열(String)의 요소들은 문자(Char)들이며, s[i]와 같은 인덱싱 연산을 통해 접근할 수 있다. 또한, for (c in str) 같은 for문을 사용해 이 문자들을 순회(iterate)할 수 있다.

한 번 문자열을 초기화하면, 그 값을 바꾸거나 새 값을 할당할 수 없다. 문자열을 변형하는 모든 연산은 새로운 String 객체로 결과를 반환하며, 원래 문자열은 그대로 유지된다.

val str = "abcd"

// Creates and prints a new String object
println(str.uppercase())
// ABCD

// The original string remains the same
println(str) 
// abcd

 

ReadIn 및 toInt를 사용하여 문자열을 정수 변수로 변환

readln() 함수를 사용하여 표준 입력으로부터 데이터를 읽는다. 이때 전체 줄을 문자열로 읽는다. 만약 다른 자료형으로 변환하고 싶으면 .toInt(), .toLong(), .toDouble(), .toFloat(), 또는 .toBoolean()와 같은 함수를 이용한다. 즉 .toInt()를 사용해서 Integer을 만들 수 있다. 이는 String을 파싱하여 Integer 숫자로 결과를 반환한다.

val age = readln().toInt()

 

.toInt(), .toDouble() 같은 형 변환 함수는, 입력된 문자열이 해당 자료형에 맞는 올바른 값일 거라고 가정하고 동작한다. 따라서서 "hello"처럼 숫자가 아닌 문자열을 변환하려고 하면 에러(예외)가 발생한다.

 

Else if 및 in 키워드

if (age in 18..39){...}

if (age >= 18 && age < 40){...}

 

in은 범위 지정 연산자로, 두 조건식은 같은 의미이다.

 

가위 바위 보

컴퓨터의 선택 받기

package com.example.rockpaperscissors

fun main(){
var computerChoice = ""
var playerChoice = ""
println("Rock, Paper or Scissors? Enter your choice!")
playerChoice = readln()

val randomNumber = (1..3).random()
if (randomNumber == 1) computerChoice = "Rock"
else if (randomNumber == 2) computerChoice = "Paper"
else computerChoice = "Scissors"

println(computerChoice)
}

1부터 3까지의 숫자 중 .random() 함수를 이용하여 숫자 하나를 무작위로 선정하여 변수 randomNumber에 저장한다. 

 

승자 찾기

when문을 이용한다. when 키워드 다음의 소괄호()에 넣은 데이터가 조건이 되고 이 값에 따라 -> 오른쪽에 있는 구문을 실행한다. 또는 데이터를 명시하지 않고 조건만 명시할 수도 있다. 표현식으로도 사용할 수 있는데, 즉 when 문의 실행 결과를 반환할 수 있다. when문을 표현식으로 사용할 때는 else문을 생략할 수 없다. 

val winner = when {
playerChoice == computerChoice -> "Tie"
playerChoice == "Rock" && computerChoice == "Scissors" -> "Player"
playerChoice == "Paper" && computerChoice == "Rock" -> "Player"
playerChoice == "Scissors" && computerChoice == "Paper" -> "Player"
else -> "Computer"
}

승자 표시

if (winner == "Tie") {
println("It's a tie")
}
else {
println(winner + " won!")
}
if (winner == "Tie") {
println("It's a tie")
}
else {
println("$winner won!")
}

String 타입의 데이터에 변숫값이나 어떤 연산식의 결괏값을 포함해야 할 때는 $ 기호를 이용한다. 이를 문자열 템플릿이라고 한다. 

 

카운터와 함께 While 루프

fun main() {
var count = 0
while (count < 3) {
println("Count is $count")
count++
}
}

사용자 입력과 함께 While 루프

fun main() {
var userInput = readln()
while (userInput == "1") {
println("While loop executed")
userInput = readln()
}
println("Loop is done!")
}

퀴즈7: 코딩 연습 - 가위바위보 게임에서 플레이어 입력 유효성 검사

package com.example.rockpaperscissors

fun main(){
var computerChoice = ""
var playerChoice = ""
println("Rock, Paper or Scissors? Enter your choice!")
playerChoice = readln().lowercase()

while(playerChoice != "rock" && playerChoice != "paper" && playerChoice != "scissors"){
println("Rock, Paper or Scissors? Enter your choice!")
playerChoice = readln().lowercase()
}
val randomNumber = (1..3).random()

when (randomNumber) {
1 -> {
computerChoice = "Rock"
}
2 -> {
computerChoice = "Paper"
}
3 -> {
computerChoice = "Scissors"
}
}

println(computerChoice)
computerChoice = computerChoice.lowercase()

val winner = when {
playerChoice == computerChoice -> "Tie"
playerChoice == "rock" && computerChoice == "scissors" -> "Player"
playerChoice == "paper" && computerChoice == "rock" -> "Player"
playerChoice == "scissors" && computerChoice == "paper" -> "Player"
else -> "Computer"
}

if (winner == "Tie") {
println("It's a tie")
}
else {
println("$winner won!")
}
}
  • 플레이어가 가위, 바위, 보만 입력하도록 강제
  • 대소문자 구분을 없앰 -> rock, Rock, ROCK, rOCk 모두 바위로 처리
println(computerChoice) // Rock

computerChoice.lowercase()
println(computerChoice) // Rock

computerChoice = computerChoice.lowercase()
println(computerChoice) // rock

 

요약

https://tutorials.eu/exploring-basic-kotlin-syntax-and-structure-day-2-android-14-masterclass/

 

Exploring Basic Kotlin Syntax - Day 2 Android 14 Masterclass

Welcome to Day 2 of our Android 14 Masterclass, where we dive into the Basic Kotlin Syntax and Structure. Embark on this coding journey!

tutorials.eu

 

https://www.udemy.com/course/best-android-12-kotlin/?couponCode=KEEPLEARNING

이번에 할인하길래 2만원에 샀다.

17000원대에 살 수 있었는데 몇시간 뒤에 샀더니 그새 가격이 올라버림...

 

+ Do it! 책을 통해서 개념 보충


이 강의에서 사용하는 버전

이 강의를 수강하려면 다음 버전이 설치되어 있는지 확인하세요:

  • Android Studio: Hedgehog (다음 강의에 다운로드 링크 확인 가능)
  • Android SDK version: Android 14 SDK
  • Kotlin: 1.9.10

행복한 코딩하세요!


  • Empty Views Activity : 를 이용해서 프로그램 작성
  • Empty Activity : 컴포즈를 이용해서 프로그램 작성

뷰를 이용하는 방법은 안드로이드 초창기부터 오랫동안 이용되던 방식인 반면,

컴포즈를 이용하는 방식은 제트팩에서 제공하는 방식


  • SDK : Software Development Kit

개발자를 위한 플랫폼별 구축 도구 세트. SDK는 소프트웨어를 개발하고 실행하는 데 필요한 모든 것을 한 곳에서 제공한다. 

SDK는 일반적으로 라이브러리, API, IDE, 문서로 구성된다. 

SDK와 API의 차이를 알고싶다면 (참고 2) 열람


 

  • Gradle : 안드로이드 앱 빌드 도구

  • @Preview

이 컴포저블이 이 파일의 디자인 뷰에 표시되어야 한다고 Android 스튜디오에 알린다. 수정하는 동안 컴포저블 미리보기의 실시간 업데이트를 확인할 수 있다. 즉 AVD나 폰에서 애플리케이션을 재실행할 필요가 없다.


  • MainActivity : 프로그램의 실행 시작점
  • ComponentActivity : Android에서 Jetpack의 Activity 구조 중 하나이다. Activity의 기본 구현체로, Jetpack 생명주기(Lifecycle), ViewModel, SavedState 같은 최신 아키텍처 컴포넌트를 지원하는 기반 클래스

  • Activity : 화면을 구성하는 컴포넌트

super.onCreate(savedInstanceSta3te)

onCreate의 기본적인 역할은 해당 액티비티를 먼저 실행하도록 하는 것

 

Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background)

Surface 라는 container가 theme의 배경 색상을 사용한다. 애플리케이션의 배경을 위해 사용하는 것

fillMaxSize : 화면 전체를 채워야한다. 


  • Composable : 단순히 화면에 보이는 요소

 

퀴즈

1. 안드로이드에서 activity란 사용자 인터페이스를 가진 단일 화면을 나타내빈다. 버튼, 텍스트뷰 등과 같은 다양한 UI 요소가 배치됩니다.

2. Composable은 UI 요소를 나타내며 다른 Composabe 내에 중첩될 수 있다

3. Jetpack Compose 의 preview 함수를 사용하면 앱을 실행하지 않고도 개발자들이 composable의 시각적 표현을 볼 수 잇다

+ Recent posts