Kotlin

[Kotlin] 코틀린 기초문법 요약 (Kotlin Summary)

JongHyun99 2022. 5. 7. 11:33
728x90

미루던 코틀린 공부를 시작하였습니다. 사실 자바에 익숙한 사람이면 스킵해도 되는 부분이 많아보이지만, 기초를 다시 한번 복습하는 의미에서 포스팅합니다.

 

목차

  1. 구문 (Syntax)
  2. 출력 (Output)
  3. 주석 (Comments)
  4. 변수 (Variables)
  5. 데이터 타입 (Data Types)
  6. 연산자 (Operators)
  7. 조건문 (If, Else)
  8. While Loop
  9. Break/Continue
  10. 배열 (Arrays)
  11. 반복문 (For Loop)
  12. Ranges
  13. 함수 (Functions)
  14. 클래스/객체 (Classes/Objects)
  15. 생성자 (Constrctors)
  16. 상속 (Inheritance)
  17. NullSafe

1. 구문 (Syntax)

“Hello World”를 출력

fun main() {
  println("Hello World")
}

fun 키워드는 함수를 정의할 때 사용합니다. 함수는 특정 작업을 수행하기 위해 설계된 코드블럭입니다.

main() 함수는 코틀린을 실행시키기 위한 함수이며, 함수 안 중괄호로 이루어진 내용이 실행됩니다.

위 예시에서 println() 함수는 main() 함수 안에 있으므로 println() 함수가 실행됩니다.

 

코틀린은 코드 끝에 세미콜론 ( ; )를 붙일 필요가 없습니다. (요즘 언어 특징)

main() 함수의 파라미터
코틀린 1.3 버전 이전에서는 main() 함수 안에 꼭 파라미터를 함께 써주어야 했습니다. e.g. fun main(args : Array<String>) (현 시점 사용중인 버전은 1.6) 따라서 지금은 파라미터 넣는것이 필요 없어졌으나, 사용해서 문제될 것은 없습니다.
※main() 함수의 파라미터는 프로그램이 실행될 때 임의로 사용할 추가 변수를 받기 위함 입니다.

 

2. 출력 (Output)

println() 함수

println() 함수는 변수나 텍스트를 출력하기 위해 사용됩니다.

fun main() {
  println("Hello World!")
  println("I am learning Kotlin.")
  println("It is awesome!")
}
// 여러줄 출력 예시

fun main() {
  println(3 + 3)
}
// 숫자 계산 예시

print() 함수

fun main() {
  print("Hello World! ")
  print("I am learning Kotlin. ")
  print("It is awesome!")
}

위에서 다룬 println() 함수는 출력 후 개행(줄바꿈)을 해주지만 print() 함수는 개행을 해주지 않고 한 줄로 출력해줍니다.

 

3. 코틀린 주석 (Comments)

한줄 주석

한 라인의 주석이 필요한 경우 주석하고자 하는 문장 맨 앞에 ( // ) 를 사용합니다.

// 주석입니다.
println("Hello World")

println("Hello World") // 이 또한 주석 입니다.

여러 줄 주석

여러줄의 주석은 /* */ 입니다.

/*와 */ 사이의 글자들은 전부 무시됩니다.

/*
여러줄
주석
입니다
*/
println("Hello World")

 

4. 변수 (Variables)

변수는 데이터를 저장하기 위한 그릇 입니다.

var 또는 val 키워드를 사용하여 변수를 선언하고, ( = ) 를 이용해 데이터를 할당합니다.

var variableName = value
val variableName = value

 

예시

var name = "종현"
val birthyear = 1999

println(name)          // 종현
println(birthyear)     // 1999
  • var은 데이터 수정이 가능!
  • val은 데이터 수정이 불가능! (final과 같으며 상수를 선언할 때 주로 사용)
 

변수 타입 (Variable Type)

코틀린에서는 변수 타입 선언이 필요 없습니다. (자바에서의 String이나 int 등)

하지만 한번 선언한 변수 타입에서 다른 타입의 변수를 대입할 수 없습니다.

var name = "종현"
name = 1234 // 런타임에러 발생!

원한다면 타입을 함께 선언할 수 있습니다.

var name: String = "종현" //String
val birthYear: Int = 1975 // Int

변수 네이밍 규칙(Display Variables)

  • 변수 이름은 문자, 숫자, 밑줄, 달러 등을 포함할 수 있습니다
  • 이름은 문자로 시작해야 합니다.
  • 이름은 $ 및 _로 시작할 수 있습니다.
  • 대소문자를 구분합니다.
  • 보통 소문자로 시작하며 공백을 포함할 수 없습니다. (카멜케이스 권장)
  • 예약어 (var, String) 등을 사용할 수 없습니다.

 

5. 데이터 타입 (Data Types)

val number = 5 // Int
val doubleNumber = 5.99 // Double
val letter = 'D' // Char
val myBoolean = true // Boolean
val myText = "Hello" // String

// 변수 타입을 선언하고 싶다면?
val myNum: Int = 5                // Int
val myDoubleNum: Double = 5.99    // Double
val myLetter: Char = 'D'          // Char
val myBoolean: Boolean = true     // Boolean
val myText: String = "Hello"      // String

숫자형 (Numbers)

숫자 형은 정수형(Integer)과 실수형(Floating)으로 나눌 수 있습니다. 정수형 타입으로는 Byte, Short, Int, Long이 있습니다. 실수형 타입으로는 Float와 Double이 있습니다.

타입을 선언하지 않을 시 정수에는 Int, 실수에는 Double이 자동으로 대입됩니다.

 

정수형 (Integer Types)

Byte (8비트 = 1바이트)

Byte 타입은 숫자 -128 부터 127까지 저장할 수 있습니다. 변수에 작은 숫자가 대입될 것이라고 확신할 때 사용합니다.

Short (16비트 = 2바이트)

Short 타입은 -32768 부터 32767 까지 저장할 수 있습니다.

Int (32비트= 4바이트)

Int 타입은 -2147483648 to 2147483647 까지 저장할 수 있습니다.

Long (64비트 = 8바이트)

Long 타입은 -9223372036854775807 to 9223372036854775807 까지 저장할 수 있으며 선택적으로 값을 L로 끝낼 수 있습니다.

val myNum1 = 2147483647  // Int
val myNum2 = 2147483648  // Long

int 타입의 최대 값 2147483647이 초과되면 자동으로 Long 타입으로 정의됩니다.

실수형 (Floating Point Types)

실수형은 소수점 아래 숫자를 저장할 때 사용됩니다. (9.99 , 3.14.. 등)

//Float Example
val myFloat: Float = 5.75F

//Double Example
val myNum: Double = 19.99
val myNum1: Float = 35E3F  // 35000 표기
val myNum2: Double = 12E4  // 120000 표기

실수형은 e 또는 E를 이용해 지수표기법을 사용할 수 있습니다.

Float와 Double 무엇을 사용할까요?

코틀린을 비롯한 대부분의 프로그램 언어의 실수자료형에는 부동소수점 방식을 사용하고 있어 실>수를 이진수로 표현할 때 순환소수가 발생하여 정확한 실수 표현을 할 수 없습니다.

Float 타입은 이 오차를 소수점 아래 7자리 까지 표현하지만 Double 타입은 15자리 까지 표현하여, 더 정>확한 실수를 계산하고자 하면 Double형을 사용하는 것을 권장합니다.

논리자료형 (Booleans)

Boolean 데이터 타입은 참과 거짓 값을 저장할 수 있습니다.

val isKotlinFun: Boolean = true
val isFishTasty: Boolean = false
println(isKotlinFun)   // Outputs true
println(isFishTasty)   // Outputs fales

문자형 (Characters)

Char 데이터 타입은 문자 하나를 저장하며 따옴표( ‘ )를 사용해 저장합니다.

val myGrade: Char = 'B'

자바에서는 Char 타입에 ASCII 코드를 기반으로 숫자를 대입하여 문자를 출력할 수 있지만 코틀린에서는 작동하지 않습니다.

val myLetter: Char = 66 // 런타임 에러 발생!

문자열 (String)

String 타입은 문자 시퀀스를 저장하는데 사용되며 큰 따옴표( “ )를 사용해 저장합니다.

val myText: String = "Hello World"

배열 (Arrays)

배열은 여러 값을 하나의 변수로 저장할 때 사용할 수 있습니다. (나중 챕터에서 다시 등장합니다.)

타입변환 (Type Conversion)

타입변환은 데이터의 타입을 다른 타입으로 바꿀 때 사용합니다.

as 연산자를 이용하여 캐스팅할 수 있습니다.

var x : Number = 5
var y : Int x as Int

 

숫자형 변환시 주의점!

val x: Int = 5
val y: Long = x // Error: Type mismatch

특정 숫자 데이터 타입을 다른 숫자 데이터 타입으로 변환할 때는 다음과 같은 함수를 사용하여야 합니다.

toByte(), toShort(), toInt(), toLong(), toFloat(), toDouble(), toChar()

val x: Int = 5
val y: Long = x.toLong() // OK!

 

6. 연산자 (Operators)

연산자는 변수와 값들을 다룹니다.

값을 operand(피연산자)라고 하며 이를 계산하는 것을 operator(연산자) 라고 합니다.

아래는 연산자 사용 예제입니다.

var x = 100 + 50

var sum1 = 100 + 50       // 150 (100 + 50)
var sum2 = sum1 + 250     // 400 (150 + 250)
var sum3 = sum2 + sum2    // 800 (400 + 400)

코틀린의 연산자는 다음 그룹으로 나눌 수 있습니다.

  • 산술연산자(Arithmetic operators)
  • 대입연산자(Assignment operators)
  • 비교연산자(Comparison operators)
  • 논리연산자(Logical operators)

산술연산자 (Arithmetic Operators)

  • + : 덧셈 x + y
  • - : 뺄셈 x - y
  • * : 곱셈 x * y
  • / : 나눗셈 x / y
  • % : 나머지 x % y
  • ++ : 증감 ++x
  • — : 가감 —x

증감, 가감연산자는 표기 위치에 따라 선연산 후연산처리를 정할 수 있습니다.

a++: 실행 후 증감, ++a: 증감 후 실행

대입연산자(Assignment operators)

  • =
  • +=
  • -=
  • *=
  • /=
  • %=

변수에 값을 할당(대입)할 때 사용하는 연산자입니다.

아래 예제와 같이 사용합니다.

var x = 10

변수에 값을 연산하면서 대입할 때는 += 와 같이 사용해줍니다.

var x = 10
x += 5 // x = 15

비교 연산자

값을 비교하여 참과 거짓을 반환시킬 때 사용합니다.

  • == : 같을 때
  • ≠ : 같지 않을 때
  • `> : 클 때
  • < : 작을 때
  • ≥ : 크거나 같을 때
  • ≤ : 작거나 같을 때

논리연산자

  • && : AND 조건
  • || : OR 조건
  • ! : Not 조건

유용한 다른 연산자들에 대해서는 나중 목차에 준비되어 있습니다.

 

7. 조건문 (If ... Else)

조건에 따라 다른 동작을 실행시킬 수 있습니다.

if문

if (20 > 18) {
  println("20 is greater than 18")
}

조건이 true일 경우 해당하는 코드블럭이 실행됩니다.

else문

val time = 20
if (time < 18) {
  println("Good day.")
} else {
  println("Good evening.") 
}
// Good evening. 출력됨

else: 조건이 false일 경우 코드블럭이 실행됩니다, (time의 값이 18미만이라면 Good day가 실행됩니다.)

else if

val time = 22
if (time < 10) {
  println("Good morning.")
} else if (time < 20) {
  println("Good day.")
} else {
  println("Good evening.")
}

else if: 새로운 조건문을 작성하나, 앞 서 있는 조건문이 false일 때 실행됩니다.

val time = 20
val greeting = if (time < 18) {
  "Good day."
} else {
  "Good evening."
}
println(greeting) // Good evening

코틀린에서의 if문은 자바와 다르게 변수에 값을 할당하는 용도로 사용할 수 있습니다.

When

하나의 값에 여러 조건이 필요할 때 if ... else 표현식대신, when 을 사용하면 더 읽기 쉽습니다.

val day = 4

val result = when (day) {
  1 -> "Monday"
  2 -> "Tuesday"
  3 -> "Wednesday"
  4 -> "Thursday"
  5 -> "Friday"
  6 -> "Saturday"
  7 -> "Sunday"
  else -> "Invalid day."
}
println(result) // Thursday

when 표현식은 자바의 switch case 표현식과 비슷합니다.

break문과 default문이 들어가는 자바보다 더 가독성이 좋습니다.

 

8. While 반복문 (While Loop)

코드블럭 내 실행문을 조건에 따라 여러차례 실행시킬 수 있습니다.

var i = 0
while (i < 5) {
  println(i)
  i++
}

조건 값을 증감시켜주는 걸 까먹으면 무한루프에 빠질 수 있으니 주의하세요.

Do..While 반복문

do..while 반복문은 조건이 거짓이더라도 반드시 한번은 실행시킵니다.

var i = 0
do {
	  println(i)
	  i++
  }
while (i < 5)

 

9. 반복 제어문 (Break, Continue)

Break

break는 반복문을 벗어납니다.

아래는 조건이 4가 되면 반복문을 벗어나는 예제입니다.

var i = 0
while (i < 10) {
  println(i)
  i++
  if (i == 4) {
    break
  }
}

Continue

continue 는 조건에 따라 반복문을 한 차례 건너뜁니다.

아래 예제는 i가 4인 경우에만 반복문을 스킵합니다.

var i = 0
while (i < 10) {
  if (i == 4) {
    i++
    continue
  }
  println(i) // 0, 1,2,3,5,6,7,8,9
  i++
}

 

10. 배열 (Arrays)

배열은 하나의 변수에 여러 값을 넣을 때 사용합니다.

배열은 arrayOf() 함수를 이용하여 생성합니다.

val cars = arrayOf("Volvo", "BMW", "Ford", "Mazda")

배열에 접근하기

대괄호 기호를 이용해 배열의 요소에 접근할 수 있습니다.

val cars = arrayOf("Volvo", "BMW", "Ford", "Mazda")
println(cars[0])
// Volvo 출력

다음과 같이 특정 배열 인덱스의 값을 바꿀 수 있습니다.

cars[0] = "Opel"

배열의 크기

size 속성을 이용해 배열의 크기를 구할 수 있습니다.

val cars = arrayOf("Volvo", "BMW", "Ford", "Mazda")
println(cars.size)
// 4 출력

배열 요소 존재 확인

in 연산자를 이용해 배열의 요소 존재 여부를 구할 수 있습니다.

val cars = arrayOf("Volvo", "BMW", "Ford", "Mazda")
if ("Volvo" in cars) {
  println("It exists!")
} else {
  println("It does not exist.")
}

배열 반복문 사용

for 반복문을 이용해 배열의 요소에 반복문으로 접근할 수 있습니다.

val cars = arrayOf("Volvo", "BMW", "Ford", "Mazda")
for (x in cars) {
  println(x)
}

 

11. For 반복문 (For Loop)

코틀린의 for문은 자바의 for문과 형태가 다소 다릅니다.

자바처럼 전통적인 형태의 for문이 없으며, 배열이나 범위 등 값만을 반복할 수 있습니다.

val cars = arrayOf("Volvo", "BMW", "Ford", "Mazda")
for (x in cars) {
  println(x)
}

val nums = arrayOf(1, 5, 10, 15, 20)
for (x in nums) {
  println(x)
}

 

12. 범위 반복문 (Ranges)

for loop 와 함께 사용하며 .. 기호와 함께 범위를 생성합니다.

문자의 범위를 지정할 수 있다.

for (chars in 'a'..'x') {
	print(chars)
}

당연히 숫자의 범위도 지정할 수 있다. (주로 사용)

for (nums in 5..15) {
  println(nums) // 5부터 15까지 출력
}
for (nums in 5..15) {
  if (nums == 10) {
    break
  }
  println(nums)
}

for (nums in 5..15) {
  if (nums == 10) {
    continue
  }
  println(nums)
}

break와 continue 또한 사용할 수 있습니다.

 

13. 함수 (Functions)

함수는 호출될 때만 실행되며, 메소드라고도 부릅니다.

정의된 함수

println()과 같이 미리 정의되어 있는 함수들

fun main() {
	println("Hello World")
}

직접 함수 만들고 호출하기

myFunction() 이라는 함수를 만들었습니다.

fun myFunction() {
  println("I just got executed!")
}

아래와 같이 호출할 수 있습니다.

fun main() {
  myFunction()
  myFunction()
  myFunction()
}

// I just got executed!
// I just got executed!
// I just got executed

함수 파라미터

코틀린에서는 함수의 파라미터에 타입을 지정해주어야 합니다.

fun myFunction(fname: String) {
  println("$fname Doe")
}

fun main() {
  myFunction("짱구")
  myFunction("철수")
  myFunction("훈이")
}

여러 파라미터를 사용할 땐 쉼표를 이용해 표기해줍니다.

fun myFunction(fname: String, age: Int) {
  println(fname + " is " + age)
}

fun main() {
  myFunction("John", 35)
  myFunction("Jane", 32)
  myFunction("George", 15)
}

반환 (Return)

함수의 실행결과를 반환해주기 위해 return 키워드를 사용할 수 있습니다. 자바의 함수 구조와 달리 코틀린에서는 반환타입을 함수선언부 가장 뒤에 적어줍니다.

fun MyFunction():String{
	return "ReturnWord'
}

var strVar = myFunction() // ReturnWord 값 할당

다음과 같이 두개 이상의 인자를 사용하여 반환할 수 있습니다.

fun myFunction(x: Int, y: Int): Int {
  return (x + y)
}

fun main() {
  var result = myFunction(3, 5)
  println(result)
}

축약형 구조

= 연산자를 이용해 return 키워드를 생략하고 표현할 수 있습니다.

fun myFunction(x: Int, y: Int) = x + y

fun main() {
  var result = myFunction(3, 5)
  println(result)
}

// 8 (3 + 5)

 

14. 클래스와 객체 (Classes/Objects)

클래스는 하나의 객체로 이루어진 함수(기능)과 변수(특성)의 모음입니다. 예를들면 자동차 클래스는 색, 모델, 크기 등의 특성을 가질 수 있고, 주차, 주행, 브레이크 등의 기능을 가질 수 있습니다.

class Car {
  var brand = ""
  var model = ""
  var year = 0

	fun driving(){
		println("Driving...")
	}

	fun parknig(){
		println("parking...")
	}

	fun breaking(){
		println("breaking...")
	}
}
// c1 변수에 Car 클래스를 대입함으로서 c1은 하나의 객체로 생성됩니다.
val c1 = Car()

// Access the properties and add some values to it
c1.brand = "Ford"
c1.model = "Mustang"
c1.year = 1969

println(c1.brand)   // Outputs Ford
println(c1.model)   // Outputs Mustang
println(c1.year)    // Outputs 1969

참고) 코틀린에서의 최상위 부모객체는 Any입니다 (자바의 Object)

 

15. 생성자 (Constructors)

생성자는 클래스를 이용해 객체를 생성할 때, 초기 값을 지정해주는 역할을 합니다.

class Car(brand: String, var model: String, var year: Int)

fun main() {
  val c1 = Car("Ford", "Mustang", 1969)
}

자바에서와 달리 클래스의 선언과 동시에 파라미터에 변수를 대입함으로서 생성자를 생성할 수 있습니다.

 

파라미터의 기본 값을 주어 생성할 수 있습니다.

class Car(brand: String, var model: String="현대", var year: Int=2010)

위와 같이 작성하면 brand는 필수로, model과 year은 선택적으로 주입하여 생성할 수 있습니다.

init 키워드

클래스 생성자는 코드블럭을 가질 수 없는데, init 키워드를 이용해 클래스가 생성될 때 원하는 동작을 시행할 수 있습니다.

private class Car (var brand: String, var model: String, var year: Int){
    init {
        println("NewCar!")
    }
}

fun main() {
    val c1 = Car("Ford", "Mustang", 1969)
    // main을 실행시키면 NewCar! 가 출력됩니다.
}

이를 이용해 유효성 검증과 같은 객체 생성 시 초기화 동작을 지정할 수 있습니다.

constructor 키워드

생성자에는 constrctor 키워드가 생략되어 있습니다. 싱글톤 패턴을 사용할 때 constructors 키워드를 붙여주면 더 가시성 있게 사용할 수 있습니다.

private class Car constrctor(...)

constructor는 부생성자를 선언할 때 사용합니다. 부 생성자는 클래스 내부에서 선언하는 생성자입니다.

private class Car (var brand: String, var model: String, var year: Int){
    var color:String ="red"
    constructor(color:String,brand: String,model: String,year: Int) : this(brand, model, year){
        this.color=color
    }
}

위 예제와 같이 기본 생성자와 별도로 color 필드가 추가된 객체를 생성할 때 별도로 생성자를 만들어 사용할 수 있습니다. 이 때, 주 생성자를 기반으로 생성이 되기 때문에 this 키워드를 사용하여 생성을 위임하고, color 생성자를 별도로 추가해주는 것이라 할 수 있습니다.

또한, 부 생성자는 코드블럭을 가질 수 있어 별도 로직을 추가할 수 있습니다.

다른 사람들의 기술 블로그에는 anotation이나 private 접근제어자에서 생성자를 선언할 때 꼭 constructor 키워드를 붙여주어야 한다는 글이 많던데, 직접 테스트해봤을 때는constructor를 붙여주지 않아도 잘 사용이 되더라고요, 아마 버전이 지나면서 수정이 된 부분이 아닌가 싶은데, 언제 수정된건지는 찾아보기 어렵네요.

 

16. 상속 (Inheritance)

코틀린에서는 상속 관계를 자식클래스와 부모클래스 (subclass 와 superclass ) 라고 명칭합니다.

// Superclass 부모클래스
open class MyParentClass {
  val x = 5
}

// Subclass 자식클래스
class MyChildClass: MyParentClass() {
  fun myFunction() {
    println(x) // x는 부모 클래스의 5에 접근할 수 있습니다.
  }
}

// Create an object of MyChildClass and call myFunction
fun main() {
  val myObj = MyChildClass()
  myObj.myFunction()
}

자바와 다르게 부모 클래스에 open키워드를 붙여주어 상속가능한 클래스임을 표기합니다. 자식클래스 선언부에 : MyParentClass() 로 상속시켜줍니다. (자바의 extends 와 비슷합니다.) 인터페이스 또한 클래스 상속과 같이 콜론을 이용해 구현해줍니다.

 

17. NullSafe

코틀린이 자바와의 다른 점으로 코틀린은 Null을 허용하지 않는다는 점 입니다. 그렇기에 Null이 들어갈 수 있는 변수를 사용하려면 타입 뒤에 특정 키워드를 선언해주어야 합니다.

? 연산자

? 키워드는 타입 뒤에 선언해주어 변수에 null이 들어갈 수 있음을 알립니다.

var var1:String = null // 런타임에러 발생!
var var2:String? = null // var2에 null 대입됨

?. (안전한 호출 연산자)

?. 키워드는 메서드를 호출할 때 널체크를 함께 해줍니다.

var car1:Car? = Car()
car1.drive() // 런타임 에러 발생

var car2:Car? = Car()
car2?.drive() // 정상

car1 객체가 nullable로 선언되었기 때문에 car1.drive()를 호출할 때 null이 발생할 수 있다고 런타임에러가 발생합니다. 여기서 ?. 키워드를 사용하면 car1 객체가 null일 경우 null을 반환해줍니다.: 키워드

?: (엘비스 연산자)

?: 키워드는 값이 Null일 경우 기본 값을 지정해줄 수 있습니다.

var var1:String? = null
var var2 = var1?:"isNull!"
print(var2) // isNull 출력

as? (안전한 캐스팅)

코틀린에서는 타입 변환에 as를 사용합니다. 이 때 지정한 타입으로 바꿀 수 없으면 ClassCastException이 발생하는데, as?연산자를 통해 변환 가능한 타입인지 검사할 수 있습니다.

foo as? Type

변환 가능 시 Type으로 변환, 불가능 시 null 반환

val otherPerson = o as? Person ?: return false

엘비스 연산자와 함께 사용할 수 있습니다.

!! 연산자

변수가 null이 아님을 확신할 때 사용합니다.

var test:String ?= null
test = "value"
println(test!!)

위와 같이 사용하며 만약 !!를 선언해주고 값을 할당해주지 않으면 NullPointException이 발생합니다.

강력한 NullCheck

코틀린에서는 String 타입의 null을 체크할 수 있는 메소드가 있습니다.

isNullOrEmpty : 공백 문자로 취급함

isNullOrBlank : 공백 무시함

var test:String = " "
println(test.isNullOrEmpty()) // false
println(test.isNullOrBlank()) // true

위 함수를 재정의하여 사용하여 깔끔한 코드를 작성할 수 있을 듯 합니다.

 

 

 

학습 및 참고 사이트

https://www.w3schools.com/kotlin/index.php

 


 

 

인터넷에 올라와 있는 튜토리얼 위주로 기초적이고 간단한 내용 위주로 정리하였습니다.

 

노션에 작성한 글을 Tistory 블로그에 옮겨적을 때 글 간격이나 특수 부호들이 다 깨지는게 너무 불편함..