(Kotlin/코틀린) use로 리소스 자동 닫기
개요
파일, 스트림, DB 커서, 네트워크 응답 등 닫아야 하는 리소스(Closeable) 를 다룰 때 가장 중요한 원칙은 하나입니다.
무슨 일이 있어도 반드시 닫아라. (정상 종료든 예외든)
코틀린에서는 이를 가장 깔끔하게 해결하는 표준 도구가 use 입니다.
- 요약
- use { }는 블록이 끝나면 자동으로 close() 호출
- 예외가 발생해도 닫힘(try-finally 대체)
- Closeable(또는 AutoCloseable)에 적용 가능
- useLines는 파일을 라인 단위로 안전하게 처리하는 전용 유틸
1. use가 필요한 이유 (누수 방지)
❌ 잘못된 예: close 누락 가능
val input = FileInputStream(file)
val text = input.readBytes().toString(Charsets.UTF_8)
// 예외가 발생하면 close() 못 하고 누수
input.close()
읽기 중 예외가 나면 close()까지 도달하지 못해 리소스가 새는 문제가 생깁니다.
2. use 기본 형태
FileInputStream(file).use { input ->
val text = input.readBytes().toString(Charsets.UTF_8)
}
- use 블록이 끝나면 input.close()가 자동 호출됩니다.
- 예외가 발생해도 close()는 호출됩니다.
// 내부적으로 다음과 유사합니다.
val input = FileInputStream(file)
try {
// 작업
} finally {
input.close()
}
3. 어떤 리소스에 use를 적용할 수 있나?
- 기본적으로 java.io.Closeable 계열에 적용됩니다.
- InputStream, OutputStream
- Reader, Writer
- BufferedReader
- FileInputStream, FileOutputStream
- Socket
- (Android) Cursor도 Closeable이라 use 가능
일부 타입은 AutoCloseable만 구현하기도 하는데, 코틀린/JDK 버전 및 환경에 따라 확장 함수가 제공됩니다. 실무에서는 대부분 Closeable 리소스에 use를 적용합니다.
4. 실무 예제 1: 파일 읽기/쓰기
// 파일 읽기
val text = FileInputStream(file).use { input ->
input.readBytes().toString(Charsets.UTF_8)
}
// 파일 쓰기
FileOutputStream(file).use { out ->
out.write("hello".toByteArray(Charsets.UTF_8))
}
5. 실무 예제 2: BufferedReader + 라인 처리 (useLines)
파일을 라인 단위로 처리할 때는 useLines가 가장 깔끔합니다.
val count = file.bufferedReader().useLines { lines ->
lines.count()
}
- 내부적으로 자동 close
- 라인 스트림을 안전하게 순회
6. 실무 예제 3: Android Cursor도 use로 닫기
ContentResolver 쿼리 결과 Cursor는 반드시 close 해야 합니다.
val names = mutableListOf<String>()
context.contentResolver.query(uri, null, null, null, null)
?.use { cursor ->
val idx = cursor.getColumnIndexOrThrow("name")
while (cursor.moveToNext()) {
names.add(cursor.getString(idx))
}
}
- query()가 null일 수 있으므로 ?.use { } 패턴을 많이 씁니다.