Note:이 연재는 'Do It! Kotlin Programming'에서 일부 내용을 연재하고 있습니다. 완전한 내용은 책을 참고해 주세요!
코틀린에서 프로젝트(Project)는 모듈(Module), 패키지(Package), 파일(File)로 구성되어 있습니다. 코틀린 프로젝트를 여행에 비유하면 모듈은 목적지, 패키지는 여행용 가방, 파일은 패키지 속에 넣은 짐이라고 할 수 있습니다. 당장 변수와 자료형에 대해 알고 싶겠지만 실습을 진행하며 헷갈리지 않도록 코틀린 프로젝트를 이해하는 것이 더 중요합니다. 그러면 지금부터 프로젝트를 구성하는 모듈, 패키지, 파일에 대해 알아보겠습니다.
코틀린 프로젝트에는 모듈이 있고 모듈은 다시 패키지로 구성되어 있습니다. 그리고 패키지는 파일(클래스)로 구성되어 있죠. 다음은 프로젝트, 모듈, 패키지, 파일의 관계를 나타낸 그림입니다. 먼저 프로젝트와 모듈의 관계에 대해 살펴보겠습니다.
보통 대규모 프로젝트를 진행할 때는 기능을 모듈로 분리하여 관리합니다. 즉, 이 프로그램은 2개의 기능을 가지고 있는 것이죠. 위 그림의 실제 모습은 다음과 같습니다.
HelloKotlin 프로젝트내에 OtherModule을 생성했고 뷰의 모습을 Packages로 바꿔보면 프로젝트 이름의 모듈명과 생성된 OtherModule이 구분되는 것을 보여줍니다. src 폴더를 살펴볼까요? HelloKotlin 모듈에는 com, example, edu 폴더로 구성된 com.example.edu 패키지가 있습니다. default 패키지는 어디에 있을까요? default 패키지는 src 폴더에 특별히 패키지명이 지정되지 않은 파일이 됩니다. 즉, src 폴더에 저장된 HelloKotlin.kt 파일은 default 패키지에 포함된 것입니다.
코틀린 파일은 .kt 확장자를 가지며 맨 위에는 이 파일이 어떤 패키지에 포함된 것인지 코틀린 컴파일러가 알 수 있도록 패키지 이름을 선언해야 합니다. 만약 패키지 이름을 선언하지 않으면 그 파일은 자동으로 default 패키지에 포함됩니다.
▶ 파일이 패키지 안에 들어있어도 패키지 이름을 선언하지 않으면 default 패키지에 포함된 것으로 인식합니다.
만일 파일에 클래스가 한 개 정의되어 있다면 프로젝트 창 화면에서 kt확장자가 빠진 클래스명만 보이게 됩니다. 파일에 클래스가 여러 개 정의되어 있다면 파일은 단순히 클래스를 묶는 역할을 하고 kt 확장자가 붙게 됩니다. 따라서 코틀린에서는 파일명과 클래스의 선언 개수에 큰 의미를 두지는 않습니다. 단 같은 파일에 있는 여러 개의 클래스는 모두 그 파일에서 지정한 패키지로 인식합니다.
▶ 코틀린은 자바처럼 클래스명과 파일명이 동일해야하고 public 클래스는 하나만 사용 해야하는 규칙은 없습니다.
그러면 패키지는 왜 만들어야 할까요? 만약 두 명의 프로그래머가 프로젝트를 진행하다 우연히 같은 이름의 파일(클래스)을 만들었다고 가정해 봅시다. 그러면 당연히 오류가 발생하겠죠? 하지만 패키지가 다르면 오류가 발생하지 않습니다. 예를 들어 HelloKotlin 모듈에서 default 패키지에 Person이라는 파일(클래스)을 만들고 com.example.edu 패키지에도 같은 이름(Person)의 파일(클래스)를 만들어도 패키지는 다르기 때문에 프로그램이 정상적으로 실행될 수 있습니다.
이제 프로젝트와 모듈, 모듈과 패키지, 패키지와 파일의 관계를 이해했을 것입니다. 공부한 내용을 실습을 통해 확인해 볼까요? 새 프로젝트를 생성한 다음 패키지를 만들어 보겠습니다.
패키지 이름은 파일 맨 위에 적으면 됩니다. 이때 패키지 이름 앞에 package라는 키워드를 함께 입력해야 패키지 이름으로 인식합니다. 단, 패키지의 이름은 특수 문자나 숫자로 시작하면 안 됩니다. 만일 여러 단계의 분류가 필요하면 점(.)을 붙여 이름을 지으면 됩니다.
패키지 이름을 지을 때는 다른 제품과 중복되지 않도록 유일해야 하므로 보통 웹 사이트 도메인 이름을 짓는 방식을 많이 사용합니다. 예를 들어 여러분이 다니는 회사의 사이트 이름이 acaroom.com이라면 com.acaroom이라고 패키지 이름을 지으면 됩니다.
▶ acaroom.com이 아니라 com.acaroom이라고 지은 이유는 실제 웹사이트와 구분되어야 하므로 일반적으로 반대로 기재하게 됩니다.
package com.acaroom // 패키지 이름의 예
만약 패키지에 네트워크 기능을 구분하기 위해 net을 지정하고 이 안에 네트워크 업로드 기능 구현하기 위해 net.upload라는 이름으로 묶어 관리하면 소스관리가 좀 더 쉬워 집니다.
package com.acaroom.net.upload // 네트워크 업로드 기능을 가진 파일에 적은 패키지 이름
이제 본격적으로 새 프로젝트에 패키지를 만들어 보겠습니다.
1. IntelliJ IDEA를 실행한 다음 [File > New > Project] 메뉴를 선택합니다. 그런 다음 [Kotlin]의 [Kotlin/JVM]을 선택하고 [Next] 버튼을 누르세요.
2. 원하는 이름으로 프로젝트를 생성하세요. 여기서는 KotlinProgramming이라고 짓겠습니다. 여러분이 생성할 프로젝트의 위치를 기억하고 넘어가세요. [Finish] 버튼을 누릅니다.
3. KotlinProgramming이라는 새 프로젝트가 생성되었습니다. src 폴더에서 마우스 오른쪽 버튼을 누르고 [New > Package] 메뉴를 선택합니다. 새로운 패키지 이름으로 com.example.edu을 입력하고 [OK] 버튼을 누르세요.
▶ example.com의 edu의 속성을 가진 파일을 패키지로 만들겠다는 뜻입니다.
4. com.example.edu라는 새로운 패키지가 만들어졌습니다. 패키지가 실제로 어떻게 구성되어 있는지 살펴볼까요? 윈도우 탐색기에서 프로젝트가 생성된 위치로 이동하면 패키지 이름 사이에 있는 점(.)을 기준으로 하위 폴더가 생성되어 있는 것을 확인할 수 있습니다.
5. 이제 파일을 만들 차례입니다. 패키지 이름(com.example.edu)에서 마우스 오른쪽 버튼을 누르고 [New>Kotlin File/Class]를 선택하세요. 그러면 코틀린 파일 생성 화면이 나타납니다. 파일의 이름을 Person으로 입력하고 [OK] 버튼을 눌러 파일을 생성하세요.
6. 그러면 Person.kt라는 파일이 생성되며 생성된 파일이 오른쪽 편집기 화면에 열립니다. 파일의 맨 위에 입력된 코드를 보면 패키지 이름이 자동으로 입력되어 있음을 확인할 수 있습니다. 이 파일에 Person
이라는 클래스를 만들어 보겠습니다. 다음과 같이 입력해 보세요.
패키지에 Person 클래스 추가하기 | Person.kt |
package com.example.edu
class Person(val name: String, val age: Int)
7. 패키지만 다르면 같은 이름의 클래스를 만들어도 된다고 설명했던 것이 기억나나요? 이번에는 src 폴더에서 과정 5와 같은 방식으로 새로운 파일을 생성해 이름을 File1라고 짓고 그 안에서 Person
이라는 이름의 클래스를 만들어 보겠습니다.
default 패키지에 Person 클래스 추가 | File1.kt |
class Person(val name: String, val age: Int)
8. 과정 7을 진행해도 IntelliJ IDEA에서 클래스의 이름이 같다는 등의 경고를 하지 않죠? 과정 5에서 만든 Person.kt 파일에서 패키지 이름을 지워보세요. 그러면 default 패키지에 같은 클래스가 두 개인 것으로 인식되면서 오류가 발생합니다.
9. 파일의 이름과 클래스의 이름이 같으면 프로젝트 탐색기에서 파일의 확장자를 표시하지 않습니다. Person.kt는 파일의 이름과 클래스의 이름이 Person으로 같기 때문에 프로젝트 탐색기에 .kt가 생략되었습니다. 하지만 File1.kt는 파일의 이름은 File1이고 클래스 이름이 Person이기 때문에 .kt가 생략되지 않았습니다.
이렇게 클래스명은 동일하지만 패키지가 서로 다른 클래스를 선언해 사용할 수 있습니다.
기본 패키지란 코틀린으로 프로그램을 만들 때 자주 사용하는 클래스와 함수 등을 미리 만들어 놓은 것입니다. 패키지는 import
키워드로 선언해야 사용할 수 있는데 코틀린에서 제공하는 기본 패키지는 이름에 걸맞게 import
키워드로 선언하지 않아도 바로 사용할 수 있습니다. 다음은 코틀린의 기본 패키지를 포함한 라이브러리(kotlin-stdlib-sources.jar)를 표로 정리한 것입니다.
패키지 이름 | 설명 |
---|---|
kotlin.* | Any, Int, Double 등 코어 함수와 자료형 |
kotlin.text.* | 문자와 관련된 API |
kotlin.sequences.* | 컬렉션 자료형의 하나로 반복이 허용되는 개체를 열거 |
kotlin.ranges.* | If문이나 for문에서 사용할 범위 관련 요소 |
kotlin.io.* | 입출력 관련 API |
kotlin.collections.* | List, Set, Map 등의 컬렉션 |
kotlin.annotation.* | 애노테이션 관련 API |
표에 소개한 패키지들 중 자료형과 깊은 관련이 있는 kotlin.* 패키지를 실습에 사용해 보겠습니다. 더불어 정말로 임포트하지 않아도 사용할 수 있는지 실습을 통해 알아 보겠습니다.
▶ 기본 패키지 이름 뒤에 붙은 별표(*)는 해당 패키지 안에 포함된 모든 요소를 의미합니다.
1. 앞에서 만든 KotlinProgramming 프로젝트에서 src 폴더 안에 패키지(chap02.section1)를 새로 만들고 그 안에 새 파일(defaultPackage.kt)을 만드세요. 그런 다음 아래의 ‘안녕하세요’라는 문자열과 20이라는 숫자를 출력하는 코드를 완성하고 실행해 보세요.
기본 패키지 kotlin.* 사용하기 | defaultPackage.kt |
package chap02.section1
fun main() {
val intro: String = "안녕하세요!"
val num: Int = 20
println("intro: $intro, num: $num")
}
Result |
intro: 안녕하세요!, num: 20
2. 프로그램이 잘 실행되었죠? 그러면 기본 패키지는 어떻게 생겼는지 직접 확인해 보겠습니다. 코드에서 문자열이라는 자료형을 의미하는 String이라는 키워드를 클릭하고 Ctrl + B를 눌러 보세요. 그러면 String.kt 파일이 열립니다. 이 파일이 들어있는 위치는 상단 파일 경로 바에 나타납니다.
kotlin-stdlib-sources.jar는 jar로 압축된 표준 라이브러리 파일을, kotlin은 패키지의 이름을, String.kt는 파일의 이름을 의미합니다. 이렇게 String, Int와 같은 기본 자료형은 모두 기본 패키지에 선언되어 있습니다. 즉, 여러분이 String 자료형으로 변수를 선언하면 코틀린은 기본 패키지 안에서 String 클래스를 찾아 변수를 선언해 줍니다.
3. 다시 defaultPackage.kt 파일로 돌아옵니다. 이번에는 기본 패키지가 아닌 다른 패키지를 사용해 보겠습니다. 만약 기본 패키지가 아닌 다른 패키지를 불러오려면 반드시 package라는 키워드를 이용하여 임포트해야 합니다. 일단 파이(PI)와 -12.6의 절댓값을 출력하는 코드를 추가로 입력해 보세요.
수학 라이브러리 추가하기 | defaultPackage.kt |
package chap02.section1
fun main() {
val intro: String = "안녕하세요!"
val num: Int = 20
println(PI)
println(abs(-12.6)) // 절댓값을 위해 사용하는 abs 함수
println("intro: $intro, num: $num")
}
4. 위에서 입력한 PI
와 abs
는 기본 패키지에 포함되지 않은 상수와 함수입니다. 프로그램이 제대로 실행되려면 컴파일러가 이 상수와 함수를 해석할 수 있어야 합니다. 그런데 과정 3에서는 이 상수와 함수를 사용하는 패키지를 불러오지 않았습니다. 다행히 IntelliJ IDEA의 힌트 기능이 동작하며 해당 상수와 함수를 사용하려면 말풍선에 나타난 패키지를 불러와야 한다고 알려줍니다.
println(PI)를 입력하면 kotlin.math.PI를 임포트하라고 안내하는 말풍선이 나타납니다. 말풍선에 나타난 것처럼 Alt + Enter를 누르세요. println(abs(-12.6))
을 입력할 때도 같은 방식으로 kotlin.math.abs를 선택합니다.
5. 과정 4를 제대로 진행했다면 코드 윗부분에 kotlin.math 패키지의 PI와 abs라는 두 요소를 임포트하는 코드가 각각 추가됩니다.
package chap02.section1
import kotlin.math.PI
import kotlin.math.abs
fun main() {
...
}
6. 만약 패키지의 여러 요소를 한꺼번에 임포트하려면 다음과 같이 패키지 이름 뒤에 별표(*)를 붙여 표현할 수 있습니다. 하지만 여러분이 프로젝트에 사용할 패키지의 요소가 많지 않다면 별표(*) 표기법보다 과정 5와 같이 사용할 요소만 임포트할 것을 권장합니다. 별 표기법은 모든 요소를 프로젝트에 포함시키기 때문에 사이즈가 늘어나 코드 최적화에 문제가 될 수 있습니다.
import kotlin.math.*
이번에는 여러분이 직접 만든 사용자 클래스를 또 다른 패키지에서 사용해 보겠습니다. 이 경우에도 패키지의 이름(com.example.edu)과 함께 패키지의 요소를 import 키워드와 함께 적어주면 됩니다. 다음은 com.example.edu 패키지에 포함된 Person 클래스를 chap02.section1 패키지에서 가져온 것입니다.
▶chap02.section1에 새파일 UserClassImport.kt를 만들고 아래 내용을 입력하세요.
사용자 클래스(Person) 가져오기 | UserClassImport.kt |
package chap02.section1
import com.example.edu.Person
fun main() {
val user1 = Person("Kildong", 30)
println(user1.name)
println(user1.age)
}
Result |
Kildong 30
프로그램을 실행하면 잘 동작합니다. 그렇다면 chap02.section1에도 같은 이름의 클래스(Person)가 있는 경우에는 어떻게 할까요? 그런 경우에는 as
라는 키워드를 사용해 이름을 바꾸어 별명을 사용하면 됩니다. as
키워드는 여러분이 가져온 클래스에 별명을 붙여줍니다. 다음은 com.example.edu에 포함된 클래스(Person)에 User라는 별명을 다시 붙여 수정한 것입니다.
사용자 클래스에 별명 붙이기 | UserClassImport.kt |
package chap02.section1
import com.example.edu.Person as User
fun main() {
val user1 = User("Kildong", 30) // com.example.edu의 Person이 User로 대체
val user2 = Person("A123", "Kildong") // 이 파일 내에 있는 Person 클래스의 객체 생성
println(user1.name)
println(user1.age)
println(user2.id)
println(user2.name)
}
class Person(val id: String, val name: String)
Result |
Kildong 30 A123 Kildong
UserClassImport.kt 파일에 일부러 Person
이라는 이름의 클래스를 추가했습니다. 그러면 com.example.edu 패키지의 Person
클래스와 충돌할 수도 있겠죠. 그렇게 되지 않도록 as
키워드로 충돌을 피했습니다. 이렇게 하면 chap02.section1의 Person과 com.example.edu의 클래스를 구분하여 사용할 수 있습니다.
이렇게 기본 패키지에 대한 이야기를 마칩니다. 다음은 변수와 자료형을 알아보겠습니다.
"어떤 것을 완전히 알려거든 그것을 다른 이에게 가르쳐라."
- Tryon Edwards -