• 리소스 활용하기

안드로이드 프로그래밍은 리소스를 많이 이용한다는 특징이 있습니다.

layout, drawable 이외에도 많은 리소스를 등록하고 활용합니다.

단순히 res 디렉토리에 리소스 파일을 만들고 R 파일을 이용해 코드에서 참조하는 것 이외에 플랫폼 리소스도 있으며, 어떤 리소스를 특정 환경에 적용할지 명시하는 방법도 있습니다.

활용 조건 명시와 기기 크기와 호환하는 앱 개발을 위해 신경써야 하는 부분에 대해 알아봅니다.


09-1 리소스의 종류와 특징

리소스(Resource)란 정적인 자원을 뜻합니다.

앱이 동작하면서 변경되지 않는 콘텐츠는 리소스로 분리해서 외부 파일로 만들어 이용할 수 있습니다.

앱에서 이요하는 리소스는 앱 리소스플랫폼 리소스로 구분됩니다.

앱 리소스 사용하기

개발자가 직접 추가한 리소스를 의미합니다.

res 디렉토리와 하위 drawble, layout, mipmap, values 4개 디렉토리에 개발자가 리소스 파일을 만듭니다.

이 외에도 리소스 파일의 종류는 더 많습니다.

안드로이드 앱에서 이용되는 리소스의 종류는 아래와 같습니다.

디렉토리명 리소스 종류
animator 속성 애니메이션 XML
anim 트윈 애니메이션 XML
color 색상 상태 목록 정의 XML
drawable 이미지 리소스
mipmap 앱 실행 아이콘 리소스
layout 레이아웃 XML
menu 메뉴 구성 XML
raw 원시 형태로 이용되는 리소스 파일
values 단순 값으로 이용되는 리소스
xml 특정 디렉토리가 정의되지 않은 나머지 XML 파일
font 글꼴 리소스

디렑토리와 파일은 이름을 지을 때 규칙이 있습니다.

위 표의 리소스 디렉토리명은 고정이며 임의로 이름을 붙인 디렉토리를 만들 수 없고 하위 디렉토리 역시 추가할 수 없습니다.

파일명은 values에 추가하는 파일들을 제외하면 자바의 이름 작성 규칙을 지켜야 하며 알파벳 대문자를 사용할 수 없습니다.

R 파일에 식별자로 등록해서 이용하기 때문입니다.

 

레이아웃 리소스 - layout 디렉토리

화면을 구성하는 레이아웃 XML파일을 저장하는 디렉토리입니다.

 

이미지 리소스 - drawable 디렉토리

이미지 리소스를 저장하는 디렉토리 입니다. png, jpg, gif, webp 파일이 저장됩니다.

XML로 작성한 이미지도 저장할 수 있습니다.

XML 이미지를 만들 때 사용되는 태그는 아래와 같습니다.

태그 설명
<shape> 도형을 의미하며 android:shape="rectangle"처럼 속성을 이용해 도형의 타입을 지정.
rectangle, oval, line, ring 중 선택 가능
<corners> 둥근 모서리를 그리는 데 사용, shape값이 rectangle 일 때만 적용
<gradient> 그라디에션 색상 지정
<size> 도형의 크기 지정
<solid> 도형의 색상 지정
<stroke> 도형의 윤곽선 지정

 

실행 아이콘 리소스 - mipmap 디렉토리

앱의 실행 아이콘의 이미지 리소스가 저장되는 디렉토리입니다.

 

값 리소스 - values 디렉토리

문자열, 색상, 크기, 스타일, 배열 등을 XML로 저장할 수 있습니다.

layout 디렉토리에 activity_main.xml 파일은 R.layout.activity_main으로 이용합니다.

그런데 values 디렉토리에 있는 R 파일에 식별자로 등록되지 않고 파일에 값을 지정한 태그의 name 속성값이 등록됩니다.

따라서 values 디렉토리에 생성되는 color.xml, strings.xml등의 파일명은 권장할 뿐 자유롭게 정할 수 있으며 알파벳 대문자를 이용할 수도 있습니다.

<resources>
	<string name="app_name">AppName</string>
    <string name="txt_data1">Hello</string>
    <string name="txt_data2">World</string>
</resources>

strings.xml파일에 위처럼 작성했따면 name 속성의 값이 R파일에 식별자로 기록되어 레이아웃 XML과 코드에서 다음처럼 이용할 수 있습니다.

<TextView
	andorid:id="+id/textView"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/txt_data1/>
binding.textView.text=getString(R.string.txt_data2)

이 외에도 색상, 크기, 스타일 등이 있는데 색상 리소스는 color 태그로 등록, 크기 리소스는 dimen 태그로 등록하고 아래와 같ㅇ ㅣ사용합니다.

<Textview
	...
    android:textColor="@color/txtcolor"
    android:textSize="@dimen/text_size"/>
binding.textView.text=getString(R.string.txt_data2)
binding.textView.setTextColor=(ResourcesCompat.getColor(resources, R.color.txt_color, null))
binding.textView.textSize = resources.getDimension(R.dimen.txt_size)

 

스타일 리소는 style 태그로 등록합니다. 여러 속성을 스타일로 등록해 한꺼번에 적용하거나 중복되는 속성을 스타일로 정의해 재사용하는 용도로 쓰입니다.

<resources>
	<style name="MyTextStyle">
   	<item name="android:textSize">@dimen/txt_size</item>
        <item name="anroid:textColor">@color/txt_color</item>
    </style>
    <style name="MyTextStyleSub" parent="MyTextStyle">
    	<item name="android:textColor">#0000FF</item>
        <item name="android:background">@color/txt_bg_color</item>
    </style>
</resources>

MyTextStyle을 적용하면 스타일 두개 적용, MyTextStyleSub를 적용하면 parent와 맞물려 속성 세개가 적용됩니다.

 

<Textview
	android:layout_width="match_parent"
    android:layout_height="wrap_content"
    style="@style/MyTextStyleSub"
    android:text="Hello World"/>

위와 같이 사용해 적용할 수 있습니다.

 

색상 리소스 - color 디렉토리

color 디렉토리에는 색상 리소스를 등록합니다.

색상 리소스는 values 디렉토리에 <color>태그로 등록할 수 있는데 왜 별도의 디렉토리가 있을까요 ?

color 디렉토리의 리소스는 특정 뷰의 상태를 표현하고, 그 상태에 적용되는 색상을 등록할 때 사용합니다.

예를 들어, 어떤 버튼을 눌렀을 때와 누르지 않았을 때의 색상을 리소스로 등록하고자 할 때, 버튼의 상태에 다른 색상을 하나의 XML에 등록해 적용하면 더 편리합니다. 이 때 color 디렉토리를 사용합니다.

<selector xmls...
	<item
    	android:state_pressed="true"
    	android:color="#ffff0000"/>
 	<item
    	android:state_focused="true"
        android:color="#ff0000ff"/>
    <item android:color="#ff000000"/>
</selector>

뷰를 눌렀을때와 뷰에 포커스가 주어졌을 때를 의미하는 색상을 지정하고, 각 상태의 기본 색상을 지정했습니다.

뷰에 등록해 사용할 때는 다음과 같습니다.

<Button
	android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="Click Me !!"
    android:textColor="@color/button_text"/>

 

글꼴 리소스 - font 디렉토리

font 디렉토리는 글꼴 리소스를 저장합니다. 이곳에 TTF나 OTF 파일을 저장한 후 글꼴을 적용할 뷰에서 이용합니다.

<TextView
	...
    android:fontFamily="@font/pacifico"/>

 

플랫폼 리소스 사용하기

앱 리소스 외에 안드로이드 플랫폼이 제공하는 리소스가 있습니다.

스튜디오 프로젝트 탐색 창에서 보기 옵션을 [Packages]로 설정 후 [Libraries]항목을 살펴보면 확인할 수 있습니다.

플랫폼 리소스도 R 파일의 식별자로 이용할 수 있습니다.

그러나 앱 리소스가 아니라서 R 파일이 아니라서 android.R 이라는 플랫폼 라이브러리의 R 파일로 등록되어있습니다.

binding.imageView.setImageDrawable(ResourcesCompay.getDrawable(resources,
	android.R.drawable.alert_dark_frame, null))
binding.textView.text=getString(android.R.string.emptyPhoneNumber)

 

XML에서 앱 리소스는 @기호로 R파일의 리소스를 이용했습니다.

플랫폼의 리소스를 XML 파일에서 이용할 때는 @android:패턴으로 사용합니다.

@android:drawable/save 와 같은 형식으로 사용합니다.

 

09-2 리소스 조건 설정

리소스를 특정 환경에서 적용되도록 설정하는 것을 말합니다.

만약 기기마다 화면 크기가 달라지면 특정 이미지가 선명하게 나오지 않는 문제가 발생합니다.

기기별 실행 아이콘 크기는 다음과 같습니다.

화면 밀도 크기
XXXHDPI 192 * 192
XXHDPI 144 * 144
XHDPI 96 * 96
HDPI 72 * 72
MDPI 48 * 48

따라서 보통 앱을 개발할 때에는 아이콘을 5개씩 준비해야 합니다.

그런데 모두 같은 이름으로 저장할 수 없어 디렉토리 이름 뒤에 붙임표(-)와 조건을 추가합니다.

  • mipmap-mdpi/ic_launcher.png
  • mipmap-hdpi/ic_launcher.png
  • mipmap-xhdpi/ic_luancher.png
  • ...

 

리소스 디렉토리명에 지정할 수있는 조건은 다음과 같습니다.

조건 예시 설명
MCC 및 MNC mcc310
mcc310-mnc004
이동통신 국가 코드(MCC)와 선택적으로 이동통신 네트워크 코드(MNC) 추가 가능. mcc310(미국), mcc310-mnc004(버라이즌)
언어 및 지역 en
ko-rKR
ISO639-1 언어 코드이며 선택적으로 뒤에 나올 소문자 r을 추가해 ISO3166-1-alpha-2의 지역 코드가 나올 수 있음
레이아웃 방향 ldrtl
ldltr
히브리어처럼 우→좌 로 쓰는 언어의 레이아웃 이용
ldrtl은 우에서 좌, ldltr은 좌에서 우 방향 레이아웃
더 작은 쪽 sw320dp 화면 방향과 상관없이 화면의 높이와 너비 중 작은 쪽에 대한 조건을 의미 sw320dp 이면 너비든 높이든 상관없이 작은 쪽의 치수가 320dp 인경우를 의미
이용 가능한 너비 w720dp 화면 너비에 대한 조건. w720dp이면 너비가 720 인 기기
이 외의 다양한 조건을 아래 문서에서 확인 가능
https://developer.android.com/guide/topics/resources/providing-resources?hl=ko#AlternativeResources

이런 다양한 조건을 어느 리소스 디렉토리에 적용할 지는 개발자의 선택에 달렸습니다.

예를 들어 언어 조건은 values디렉토리에 적용해 values-ko-rKR로, drawable 디렉토리에 적용해 drawable-ko-rKR로 사용할 수 있습니다.

이어 붙여 여러 조건을 지정해도 됩니다.

그러나 위 표의 순서대로 조건을 지켜야 합니다.

 

화면 회전에 대응하기

화면 회전에 대응하려면 가로와 세로 방향일 때 출력할 레리아웃 XML 파일을 각각 준비해야 합니다.

그리고 어느 방향에서 어떤 XML을 출력할지 지정해야 하빈다.

이를 리소스 조건으로 설정해 처리하면 편리합니다.

 

가로, 세로 방향에 적용할 레이아웃 XML 파일을 만들고 각 리소스 디렉토리명에 조건을 명시해 저장합니다.

세로방향 xml파일은 layout 디렉토리에, 가로방향 xml 파일은 layout-land 디렉토리에 저장합니다.

 

국제 언어 제공하기

이 부분도 리소스 조건 설정으로 처리합니다.

strings.xml 파일에 <string> 태그로 문자열 리소스를 작성하는데, 파일을 여러개 만들어서 각 언어에 맞는 리소스 문자열을 담고 어느 XML 파일을 적용해야 하는지를 리소스 디렉토리명으로 지정하면 됩니다.

<resources>
	<string name="app_name">TextEnglish</string>
    <string name="intro">Hello</string>
</resources>
<resources>
	<string name="app_name">테스트</string>
    <string name="intro">안녕하세요</string>
</resources>

문자열 리소스는 values 디렉토리의 리소스이므로 식별자는 파일명이 아니라 태그의 name 값입니다.

따라서 각 파일에 문자열을 등록할 때 name값은 같아야 합니다.

 

09-3 폰 크기의 호환성

폰 크기의 호환성은 안드로이드 시스템에서 도와주는 부분이 있고 개발자가 직접 해결해야 하는 부분이 있습니다.

논리적인 단위 알아보기

크기 설명
ldpi 저밀도 화면 ~120dpi
mdpi 중밀도 화면 ~160dpi
hdpi 고밀도 화면 ~240dpi
xhdpi 초고밀도 화면 ~320dpi
xxhdpi 초초고밀도 화면 ~480dpi
xxxhdpi 초초초고밀도 화면 ~640dpi

 

위 표처럼 기기의 크기를 구분해 콘텐츠의 크기를 늘리거나 줄이도록 안드로이드 시스템이 돕습니다.

하지만 이 도움을 받으려면 개발자가 콘텐츠의 크기를 지정할 때 논리적인 단위를 사용해야 합니다.

앱 개발시 크기 지정에 사용할 수 있는 단위는 다음과 같습니다.

  • dp(dpi: density-independent pixels): 스크린의 물리적 밀도에 기반을 둔 단위
  • sp(sip: scale-independent pixels): dp와 유사하며 글꼴 크기에 적용
  • pt(points): 스크린 크기의 1/72를 1pt로 함
  • px: 픽셀
  • mm: 밀리미터
  • in: 인치

화면 정보 가져오기

안드로이드 시스템은 기본으로 크기 호환성을 지원하지만 때로는 개발자가 직접 코드에서 조정할 수도 있습니다.

이때 기기의 크기 정보를 가져오기위해 API를 사용합니다.

30 이전 버전에서는 DisplayMetrics로 크기 정보를 가져왔지만, 이 후부터는 WindowMetrics를 이용해야 합니다.

아래는 버전에 따라 다르게 적용하는 예시입니다.

if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.R){
	val windowMetrics: WindowMetrics = windowManager.currentWindowMetrics
    binding.textView.text = "width: ${windowMetrics.bounds.width()}, hieght:
    ${windowMetrics.bounds.height()}"
}else{
	val display = windowManager.defaultDisplay
    val displayMetrics = DisplayMetrics()
    display?.getRealMetrics(displayMetrics)
    binding.textView.text =
    	"width: ${displayMetrics.widthPixels}, height: ${displayMetrics.heightPixels}"
}

Build.VERSION.SDK_INT는 앱이 실행되는 기기의 버전값이며 Build.VERSION_CODES.R은 안드로이드 11버전, API 레벨30을 의미합니다.

 

09-4 메신저 앱의 인트로 화면 만들기

(실습)

'Java > Kotlin' 카테고리의 다른 글

Kotlin Programming Study - 08  (0) 2025.12.17
Kotlin Programming Study - 07  (0) 2025.12.16
Kotlin Programming Study - 06  (0) 2025.12.15
Kotlin Programming Study - 04  (0) 2025.12.10
Kotlin Programming Study - 03  (0) 2025.12.09
  • 사용자 이벤트 처리하기

화면에 출력되는 뷰에서 발생하는 이벤트를 처리하거나, 뒤로가기 버튼 같은 안드로이드 시스템의 키 이벤트를 처리하는 방법


08-1 터치와 키 이벤트

터치 이벤트

앱의 화면에서 발생하는 사용자 이벤트는 터치(Touch)입니다.

사용자의 터치 이벤트를  처리하고 싶다면 액티비티 클레스에 터치 이벤트의 콜백 함수인 onTouchEvenet()를 선언하면 됩니다.

(콜백 함수란 어떤 이벤트가 발생하거나 시점에 도달했을 때 시스템에서 자동으로 호출하는 함수를 말합니다.)

class MainActivity: AppComaptActivity(){
	...
    override fun onTouchEvent(event: MotionEvent?): boolean{
    	return super.onTouchEvent(event)
    }
}

onTouchEvent()함수를 재정의 해서 선언만 해놓으면 액티비티 화면을 터치하는 순간 onTouchEvent()함수가 호출됩니다.

매개변수 MotionEvent객체는 터치의 종류와 발생지점(좌푯값)이 담깁니다.

 

터치이벤트의 종류

  • ACTION_DOWN: 화면을 손가락으로 누른 순간의 이벤트
  • ACTION_UP: 화면에서 손가락을 떼는 순간의 이벤트
  • ACTION_MOVE: 화면을 손가락으로 누른 채로 이동하는 순간의 이벤트

화면을 손가락으로 눌렀다 때면 onTouchEvent()함수는 2번 호출됩니다. DOWN → UP

손가락을 이동후 떼었다면 3번 호출됩니다. DOWN → MOVE → UP

override fun onTouchEvent(event: MotionEvent?): Boolean{
	when(event?.action){
    	MotionEvent.ACTION_DOWN ->{
        	Log.d("down", "TOUCH DOWN EVENT")
        }
        MotionEvent.ACTION_UP -> {
        	Log.d("up", "TOUCH UP EVENT")
        }
    }
    return super.OnTouchEvent(event)
}

 

터치 이벤트 발생 좌표 얻기

이벤특가 발생한 지점을 알아야 하는 경우 onTouchEvent()함수의 매개변수인 MotionEvent객체로 얻습니다.

  • x: 이벤트가 발생한 뷰의 X좌표
  • y: 이벤트가 발생한 뷰의 Y좌표
  • rawX: 화면의 X좌표
  • rawY: 화면의 Y좌표
override fun onTouchEvent(event: MotionEvent?): Boolean{
	when (event.action){
    	MotionEvent.ACTION_DOWN->{
        	Log.d("down",
            	"Touch down event x: ${event.x}, rawX: ${event.rawX}")
        }
    }
    return super.onTouchEvent(event)
}

 

키 이벤트

키 이벤트는 사용자가 폰의 키를 누르는 순간에 발생합니다.

키 이벤트를 처리하려면 다음과 같이 콜백 함수를 재정의해야 합니다. 그러면 키 이벤트가 발생할 때 해당 함수가 자동으로 호출됩니다.

  • onKeyDown: 키를 누른 순간의 이벤트
  • onKeyUp: 키를 떼는 순간의 이벤트
  • onKeyLongPress: 키를 오래 누르는 순간의 이벤트
class MainActivity2: AppCompatActivity(){
	...
    override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
    	Log.d("down", "onKeydown")
        return super.onKeyDown(keyCode, event)
    }  
}

 

키 이벤트 함수의 첫번째 매개변수는 키의 코드이며 이 값으로 사용자가 어떤 키를 눌렀는지 식별할 수 있습니다.

override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
	when(keyCode){
    	KeyEvent.KEYCODE_0 -> Log.d("kkang", "0키 누름")
    }
    return super.onKeyDown(keyCode,event)
}

그런데 키 이벤트가 발생하는 키는 폰에서 제공하는 소프트 키보드의 키를 의미하지 않습니다.

하드웨어 키보드를 갖는 안드로이드 기기나 시스템 버튼을 키로 취급하므롤 이 버튼의 이벤트를 처리하는 데도 사용됩니다.

(전원 버튼, 볼륨 버튼, 뒤로가기, 홈, 오버뷰-네비게이션 바 등)

 

이 중 뒤로가기, 볼륨 조절 버튼은 '키'로 취급해 이벤트로 처리할 수 있으나, 전원, 홈, 오버뷰 버튼은 액티비티에 onKeyDown()함수를 선언해 놓아도 함수가 호출되지 않는, 앱에서 이벤트를 처리할 수 없는 버튼입니다.

 

(뒤로가기 버튼은 onBackPressed()를 사용했으나 deprecated되어 현재는 androidx.activity.OnBackPressedCallback()함수 이용을 권장함)

 

08-2 뷰 이벤트

액티비티 화면은 TextView, EditText, ImageView, Button등의 뷰로 화면을구성하고 구현합니다.

이런 뷰를 사용자가 터치했을 때 이벤트 처리는 앞에서 살펴본 터치 이벤트를 이용하지 않습니다. 각 뷰에서 이벤트를 별도로 제공합니다.

뷰 이벤트의 처리 구조

뷰 이벤트에서는 이벤트 콜백 함수만 선언해서는 처리할 수 없습니다.

뷰 이벤트 처리는 이벤트 소스와 이벤트 핸들러를 리스너로 연결해야 합니다.

  • 이벤트 소스: 이벤트가 발생한 객체
  • 이벤트 핸들러: 이벤트 발생 시 실행할 로직이 구현된 객체
  • 리스너: 이벤트 소스와 이벤트 핸들러를 연결해 주는 함수

이벤트 소스에 리스너로 이벤트 핸들러를 등록해 놓으면 이벤트가 발생할 때 실행되는 구조입니다.

binding.checkbox.setOnCheckedChangeListener(object: CompoundButton.OnCheckedChangeListener{
	override fun onCheckedChanged(p0: CompoundButton?, p1: Boolean){
    	Log.d("check", "체크박스 클릭")
    }
}

checkbox객체가 이벤트가 발생하는 이벤트 소스, 처리 내용이 담긴 이벤트 핸들러는 OnCheckedChangeListener 인터페이스를 구현한 객체입니다.

대부분 이벤트 핸들러는 이름 형식이 OnXXXListener인 인터페이스를 구현해서 만듭니다.

인터페이스를 구현한 object클래스를 이벤트 핸들러로 만들었지만, 액티비티 자체에서 인터페이스를 구현할 수도 있습니다.

혹은 이벤트 핸들러를 별도의 클래스로 만들어 처리할 수도 있으며 코틀린의 SAM(Single Abstract Method)기법을 이용할 수도 있습니다.

 

액티비티 자체에서 인터페이스 구현하기

class MainActivity: AppCompatActivity(), CompoundButton. OnCheckedChangeListener{
	override fun onCreate(savedInstancesState: Bundle?){
    	super.onCreate(savedInstanceState)
        val binding = ActivityMain3Binding.inflate(layoutInflater)
        steContentView(binding.root)
        binding.checkbox.setOnCheckedListener(this)
    }
    override fun onCheckedChanged(p0: CompoundButton?, p1: Boolean){
    	Log.d("click", "체크박스 클릭")
    }
}

 

 

이벤트 핸들러를 별도의 클래스로 만들기

class MyEventHandler: CompoundButton.OncheckedListener{
	override fun onCheckedChanged(p0: CompoundButton?, p1: Boolean){
    	Log.d("click", "체크박스 클릭")
    }
}

class MainActivity3: AppCompatActivity(){
	override fun onCreate(savedInstanceState: Bundle?){
    	super.onCreate(savedInstanceState)
        val binding = ActivityMain3Binding.inflate(layoutInflater)
        setContentView(binding.root)
        
        binding.checkbox.setOnCheckedChangeListener(MyEventHandler())
    }
}

 

 

SAM기법으로 구현하기

class MainActivity3: AppCompatActivity(){
	override fun onCreate(savedInstanceState: Bundle?){
    	super.onCreate(savedInstanceState)
        
        val binding = ActivityMain3Binding.infalte(layoutInflater)
        setContentView(binding.root)
        
        binding.checkbox.setOnCheckedChangelistener{
        	compoundButton, b ->
            Log.d("click", "체크박스 클릭")
        }
    }
}

 

클릭과 롱클릭 이벤트 처리

안드로이드는 앱을 위한 다양한 뷰를 제공하며 자체 이벤트도 존재합니다.

그러나 뷰가 아무리 많아도 이벤트처리 구조는 같아 이벤트 소스와 이벤트 핸들러를 리스너로 연결하는 구조만 이해한다면 어떤 뷰 이벤트라도 처리할 수 있습니다.

 

대표적으로 뷰를 짧게 클릭할때 발생하는 ClickEvent와 길게 클릭할 때 발생하는 LongClickEvent에 대해 알아봅니다.

ClickEvent, LongClickEvent는 뷰의 최상위 클래스인 View에 정의된 이벤트입니다.

즉 가장 기초이면서 많이 이용하는 이벤트입니다. 핸들러는 다음과 같습니다.

  • open fun setOnClickListener(l: View.OnClickListener?): Unit
  • open fun setOnLongClickListener(l: View.OnLongClickListener?): Unit

각 이벤트는 해당 리스너를 구현한 객체를 이벤트 핸들러로 등록해야 합니다.

binding.button.setOnClickListener{
	Log.d("click", "클릭 이벤트")
}

binding.button.setOnLongClickListener{
	Log.d("click", "롱클릭 이벤트")
    true
}

위 코드를 보면 setOnClickListener() 함수를 이벤트 핸들러로 등록하고 OnClickListner이벤트를 구현한 객체를 지정해야 하는데, 람다 함수를 매개변수로 지정한 것처럼 보입니다.

 

자바 코드로 작성하면 다음과 같습니다.

binding.btn.setOnClickListener(new View.OnClickListener(){
	@Override
    public void onClick(View view){
    }
}

이 코드가 코틀린 코드에서는 아래와 같습니다.

binding.btn.setOnClickListener(object: View.OnclickListener{
	override fun onClick(p0: View?){
    }
}

 

이를 SAM기법을 활용하면 더 간단하게 작성할 수 있습니다.

SAM은 자바 API를 코틀린에서 활용할 때 람다 표현식으로 쉽게 이용할 수 있게 해주는 기법입니다.

 

다음처럼 선언된 자바 인터페이스가있다고 가정합니다.

public interface JavaInterface1{
	void callback();
}

이 객체를 등록하는 함수도 자바에 선언되어있다고 가정합니다.

public class SAMTest{
	JavaInterface1 callback;
    public void setInterface(JavaInterface1 callback){
    	this.callback = callback;
    }
}

위처럼 setInterfact를 코틀린에서 이용하려면 인터페이스를 구현한 객체를 매개변수로 지정해야하므로 다음과같이 작성할 수 있습니다.

obj.setInterface(object: JavaInterface1{
	override fun callback(){
    	println("hello kotlin")
    }
})

이를 SAM 기법을 이용하면 아래와같이 작성됩니다.

obj.setInterface{println("hello SAM")}

 

모든 자바 인터페이스를 구현한 객체를 SAM으로 활용할 수 있는것은 아니지만, 대부분의 이벤트 핸들러 등록 코드를 다음처럼 작성할 수 있습니다.

binding.btn.setOnClickListener{
	...
}

08-3 시계 앱의 스톱워치 기능 만들기

(실습)

'Java > Kotlin' 카테고리의 다른 글

Kotlin Programming Study - 09  (0) 2025.12.19
Kotlin Programming Study - 07  (0) 2025.12.16
Kotlin Programming Study - 06  (0) 2025.12.15
Kotlin Programming Study - 04  (0) 2025.12.10
Kotlin Programming Study - 03  (0) 2025.12.09
  • 뷰를 배치하는 레이아웃

뷰를 배치하는 클래스를 레이아웃 클래스라고 합니다. 안드로이드는 여러  배치아웃 클래스를 제공하는데, 저마다 배치 규칙이 있습니다.


07-1 선형으로 배치 - LinearLayout

LinearLayout 배치 규칙

뷰를 가로나 세로 방향으로 나열하는 레이아웃 클래스입니다.

orientation 속성에 horizontal(가로)이나 vertical(세로)값으로 방향을 지정합니다.

화면에서 벗어나도 줄을 자동으로 바꾸지 않습니다.

이미지뷰 텍스트뷰 텍스트뷰
텍스트뷰

위의 표 처럼 레이아웃을 나열하려면 LinearLayout의 중첩으로 가능합니다.

레이아웃 클래스도 뷰 이므로 다른 레이아웃 클래스에 포함할 수 있습니다.

 

여백을 채우는 layout_weight 속성

화면에 뷰를 배치하다보면 가로나 세로 방향으로 새이는 여백을 뷰로 채울 수 있습니다.

 

뷰 1개로 전체 여백 채우기

layout_weight속성을 이용하면 계산하지 않아도 각 뷰에 설정한 가중치로 여백을 채울 수 있습니다.

해당 요소에 'android:layout_weight="1"'로 설정하면 해당 요소로 가로방향의 모든 여백을 채울 수 있습니다.

 

뷰 여러개로 여백을 나누어 채우기

여러 요소에 layout_weight속성을 지정하면 해당 숫자를 가중치로 계산해 여백을 채웁니다.

버튼 2개를 가로로 배치하며 1과 3으로 속성을 설정하면 가로여백을 1/4만큼, 3/4만큼 나누어 차지합니다.

 

중첩된 레이아웃에서 여백 채우기

중첩된 레이아웃에서의 layout_weight속성은 해당 요소가 속한 레이아웃 여백을 대상으로 적용됩니다.

 

여백 채우기로 뷰의 크기 설정하기

뷰의 크기를 0으로 하고 layout_weight값만 설정하기도 합니다.

layout_height값을 모두 0으로 설정해 화면에 아무것도 나오지 않게 화면 전체를 여백으로 만들고, 각 요소의 layout_weight를 1로 설정해 세로 여백을 3등분해 요소를 배치할 수 있습니다.

 

뷰를 정렬하는 gravity, layout_gravity 속성

뷰에 gravity와 layout_gravity속성 적용하기

뷰를 정렬할 때는 gravity와 layout_gravity속성을 사용합니다.

이 속성을 사용하지 않으면 기본값은 left/top입니다(왼쪽 위를 기준으로 정렬).

gravity속성을 right/bottom으로, layout_gravity속성을 center로 정렬하면, 요소의 정렬은 가운데로, 콘텐츠의 정렬은 우/하단으로 지정됩니다.

 

레이아웃에 gravity속성 적용하기

그런데 LinearLayout의 orientation이 vertical로 설정된 채로 layer_gravity속성을 center_vertical값으로 지정하면 정렬이 적용되지 않습니다. LinearLayout자체가 방향으로 뷰를 배치하는 레이아웃이므로 같은 방향으로는 속성이 적용되지 않기 때문입니다.

뷰를 LinearLayout영역의 가운데로 정렬하려면 layout_gravity가 아닌 gravity속성을 center로 설정해야 합니다.

 

07-2 상대 위치로 배치 - RelativeLayout

RalativeLayout 배치 규칙

RelativeLayout은 상대 뷰의 위치를 기준으로 정렬하는 레이아웃 클래스입니다.

화면에 이미 출력된 특정 뷰를 기준으로 방향을 지정해 배치합니다. 아래 속성을 이용하며 각 속성에 입력하는 값은 기준이 되는 뷰의 id입니다.

android:layout_above:기준 뷰의 위쪽에 배치

android:layout_below:기준 뷰의 아래쪽에 배치

android:layout_toLeftOf: 기준 뷰의 왼쪽에 배치

android:layout_toRightOf: 기준 뷰의 오른쪽에 배치

 

맞춤 정렬하는 align 속성

상대 뷰의 어느쪽에 맞춰 정렬할지를 정하는 속성은 다음과 같습니다. 입력하는 값 역시 기준이 되는 뷰의 id 입니다.

android:layout_alignTop: 기준 뷰와 위쪽을 맞춤

android:layout_alignBottom: 기준 뷰와 아래쪽을 맞춤

android:layout_alignLeft: 기준 뷰와 왼쪽을 맞춤

android:layout_alignRight: 기준 뷰와 오른쪽을 맞춤

android:layout_alignBaseLine: 기준 뷰와 텍스트 기준선을 맞춤

 

상위 레이아웃을 기준으로 정렬하는 속성도 있습니다.

android:layout_alignParentTop: 부모의 위쪽에 맞춤(alignParentBottom, alignParentLeft, alignParentRight)

android:layout_centerHorizontal: 부모의 가로 방향 중앙에 맞춤(centerVertical, InParent(가로, 세로, 중앙에 맞춤)

 

07-3 겹쳐서 배치 - FrameLayout

FrameLayout은 뷰를 겹쳐서 출력하는 레이아웃 클래스입니다.

카드를 쌓듯 뷰를 추가한 순서대로 위에 겹쳐서 게속 출력하는 레이아웃입니다.

똑같은 위치에 여러 뷰를 겹처서 놓고, 어떤 순간에 하나의 뷰만 출력할 때 사용합니다. 따라서 대부분 뷰의 표시 여부를 결정하는 visibility 속성을 함께 사용합니다.

이를 액티비티 코드에서 아래와 같이 사용합니다.

class MainActivity: AppCompatActivity(){
	override fun onCreate(savedInstanceState: Bundle?){
    	super.onCreate(savedInstanceState)
        
        val binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        
        binding.button.setOnClickListener{
        	//	버튼 숨기고 이미지 보이기
        	binding.button.visibility = View.INVISIBLE
            binding.imageView.visibility = View.VISIBLE
        }
        binding.button.setOnClickListener{
        	//	버튼 보이고 이미지 숨기기
        	binding.button.visibility = View.VISIBLE
            binding.imageView.Visibility = View.INVISIBLE
        }
    }
}

 

07-4 표 형태로 배치 - GridLayout

GridLayout 배치 규칙

GridLayout은 행과 열로 구성된 테이블 화면을 만드는 레이아웃 클래스입니다. LinearLayout처럼 orientation속성으로 가로나 세로 방향으로 뷰를 나열하는데 줄바꿈을 자동으로 해줍니다.

orintation: 방향 설정

rowCount: 세로로 나열할 뷰 개수

columnCount: 가로로 나열할 뷰 개수

 

GridLayout 속성

특정 뷰의 위치 조정하기

GridLayout으로 뷰를 배치하면 추가한 순서대로 배치되는데, layout_row, layout_column 속성을 이용하면 특정 뷰의 위치를 조정할 수 있습니다.

layout_row: 뷰가 위치하는 세로 방향 인덱스

layout_column: 뷰가 위치하는 가로 방향 인덱스

 

특정 뷰의 크기 확장하기

특정 뷰의 크기때문에 다른 뷰의 크기가 확장되거나 여백이 생기는 경우 layout_gravity="fill_horizontal"과 같은 속성값을 이용합니다.

 

열이나 행 병합하기

특정 뷰가 테이블에서 여러 칸을 차지하게 설정할 수 있습니다.

layout_columnSpan: 가로로 열 병합

layout_rowSpan: 세로로 행 병합

 

(TableLayout이라는 레이아웃도 있습니다.)

 

07-5 계층 구조로 배치 - ConstraintLayout

androidx에서 제공하는 라이브러리 입니다.

build.gradle.kts파일의 dependencies에 다음처럼 implementation을 선언해야 합니다.

(안드로이드 프로젝트를 만들면 자동으로 선언 됩니다.)

'implementation(libs.androidx.constraintlayout)'

 

레이아웃 편집기에서 레이아웃 구성하기

ConstraintLayout은 RelativeLayout과 비슷하지만 더 많은 속성을 제공합니다.

그런데 XML파일에 직접 작성하기는 부담스러우므로 안드로이드 스튜디오에서 제공하는 레이아웃 편집기를 이용합니다.

 

07-6 전화 앱의 키패드 화면 만들기

(실습)

'Java > Kotlin' 카테고리의 다른 글

Kotlin Programming Study - 09  (0) 2025.12.19
Kotlin Programming Study - 08  (0) 2025.12.17
Kotlin Programming Study - 06  (0) 2025.12.15
Kotlin Programming Study - 04  (0) 2025.12.10
Kotlin Programming Study - 03  (0) 2025.12.09
  • 앱의 기본 기능 구현하기

화면을 구성하고 사용자 이벤트를 처리하는 방법, 안드로이드 리소스를 활용하는 방법, 알림을 띄우는 방법 등을 다룹니다.


06-1 화면을 구성하는 방법

액티비티-뷰 구조

안드로이드 앱은 액티비티, 서비스, 브로드캐스트 리시버, 콘텐츠 프로바이더 같은 컴포넌트를 조합해 만들지만, 화면을 출력하는 컴포넌트는 액티비티 뿐입니다.

(화면을 제공하지 않는 앱이라면 액티비티가 없어도 됩니다.)

(화면이 10개여도 액티비티 1개로 만들 수 있습니다. 프래그먼트(Fragment)나 컴포즈(Compose)를 이용하면 됩니다.)

 

액티비티는 화면을 출력하는 컴포넌트일 뿐 그 자체가 화면은 아닙니다.

액티비티만을 실행하면 텅 빈 흰색 화면만 보이므로 내용을 표시하려면 뷰(View)클래스를 사용해 구성합니다.

예를들어, 문자열을 출력하려면 TextView클래스를, 이미지는 ImageView 클래스를 이용합니다.

이런 클래스를 뷰 클래스라고 합니다.

 

액티비티 코드로 화면 구성하기

뷰로 화면을 구성하는 방법은 2가지 입니다.

액티비티 코드로 작성하는 방법과 레이아웃 XML파일로 작성하는 방법입니다.

액티비티 코드로 작성하는 방법은 뷰 클래스를 액티비티 코드에서 직접 생성합니다.

class MainActivity : AppCompatActivity(){
	override fun onCreate(savedInstanceState: Bundle?){
    	super.onCreate(savedInstanceState)
        //	이름 문자열 출력 TextView 생성
        val name = TextView(this).apply{
        	typeface = Typeface.DEFAULT_BOLD
            text = "Lake Louise"
        }
        
        //	이미지 출력 ImageView 생성
        val image = ImageView(this).also{
        	it.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.lake_1))
        }
        
        //	주소 문자열 출력 TextView 생성
        val address = TextView(this).apply{
        	typeface = Typeface.DEFAULT_BOLD
            text = "Lake Louise, AB, 캐나다"
        }
    
    	val layout = LinearLayout(this).apply{
        	orientation = Linear?Layout.VERTICAL
            gravity = Gravity.CENTER
            //	LinearLayout 객체에 TextView, ImageView, TextView 객체 추가
            addView(name, WRAP_CONTENT, WRAP_CONTENT)
            addView(image, WRAP_CONTENT, WRAP_CONTENT)
            addView(address, WRAP_CONTENT, WRAP_CONTENT)
        }	
        
        //	LinearLayout 객체를 화면에 출력
        setContentView(layout)
    }
}

필요한 뷰 객체 코드를 직접 생성해 크기, 출력 데이터 등도 일일이 객체에 대입했습니다.

그리고 TextView 2개와 ImageView 1개를 추가한 LinearLayout객체를 setContentView() 액티비티 컴포넌트 함수로 전달해 화면을 출력했습니다.

 

apply{}: 함수를 초훌하는 구문, 함수 호출문운 apply()처럼 소괄호를 써야하나 호출하려는 함수가 고차함수(함수를 값처럼 취급해 매개변수로 반환값으로 주고받는 함수)이고 마지막 전달 인자가 람다 함수이면 소괄호를 생략합니다.

 

레이아웃 XML로 화면 구성하기

같은 화면 구성을 레이아웃XML 파일로 구현해 봅니다.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
	android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:textStyle="bold"
    android:text="Lake Louise"/>
    <TextView
    	android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textStyle="bold"
        anroid:text="Lake Louise/>
    <ImageView
    	android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/lake_1"/>
    <TextView
    	android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textStyle="bold"
        android:text="Lake Louise, AB, 캐나다"/>
</LinearLayout>

XML을 이용하면 액티비티 코드에 내용을 작성하지 않아도 되지만, 코드에서 화면을 구현한 XML을 명시해 출력하도록 해야합니다.

class MainActivity: AppComaptActivity(){
	override fun onCreate(savedInstanceState: Bundle?){
    	super.onCreate(savedInstanceState)
        //	화면 출력 XML 명시
        setContentView(R.layout.activity_main)
    }
}

 

06-2 뷰 클래스

뷰 클래스의 기본 구조

안드로이드는 TextView, ImageView, EditText, Button, Spinner, ListView등 많은 뷰 클래스를 제공합니다.

뷰의 구조를 이해하면 여러 가지 뷰를 사용할 때 많은 도움이 됩니다.

 

뷰 객체의 계층 구조

액티비티 화면으 구성할 때 사용하는 클래스는 모두 View의 하위 클래스 입니다.

View: 모든 뷰 클래스의 최상위 클래스입니다. 액티비티는 View의 서브 클래스만 화면에 출력합니다.

ViewGroup: View의 하위 클래스지만 자체 UI는 없어서 화면에 출력해도 아무것도 나오지 않고, 다른 뷰 여러개를 묶어서 제어할 목적으로 사용합니다. 컨테이너 기능을 담당한다고 이야기하고 실제로는 ViewGroup의 서브 클래스의 레이아웃 클래스 등을 사용합니다.

TextView: 특정 UI를 출력할 목적으로 사용하는 클래스, 문자열을 출력하는 뷰 입니다.이 외에도 ImageView등 다양한 클래스가 있습니다.

 

레이아웃 중첩

뷰의 계층 구조는 레이아웃 객체를 중첩해서 복잡하게 구성할 수도 있습니다.

<LinearLayout xmlns:android="..."
	...
    <Button
    	android:layout_wdth"wrap_content"
        .../>
    <Button
    	android:layout_wdth"wrap_content"
        .../>
    <LinearLayout
    	...
        <Button
            android:layout_wdth"wrap_content"
            .../>
        <Button
            android:layout_wdth"wrap_content"
            .../>
    </LinearLayout>
</LinearLayout>

이처럼 객체를 계층 구조로 만들어 이용하는 패턴을 컴포지트 패턴(Composite Pattern)또는 문서 객체 모델(Document Object Model)이라고 합니다.

 

레이아웃 XML의 뷰를 코드에서 사용하기

화면 구성을 XML 파일에 작성하고 액티비티에서 setContentView() 함수로 XML파일을 지정하면 화면을 출력합니다.

그런데 XML에 선언한 객체를 코드에서 사용할 때가 있습니다.

문제는 직접 생성한 객체가 아니므로 이름이 없어서 지칭할 수가 없다는 것입니다. 이때 id속성을 부여해 사용합니다.

<TextView
	android:id="@+id/text1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="hello"/>

이처럼 id 속성은 android:id="@+id/text1"형태로 추가합니다. text1이 id 값이고 식별자로 이용할 것이므로 앱에서 유일해야 합니다.

id속성을 추가하면 자동으로 R.java파일에 상수 변수로 추가됩니다.

XML속성값이 @로 시작하면 R.java파일을 의미하므로 R.java파일에 text1이라는 상수 변수를 추가하라는 의미입니다.

 

이제 코드에서 R.java파일의 상수변수로 객체를 얻을 수 있습니다. findViewById()함수를 이용합니다.

//	XML 화면 출력
setContentView(R.layout.activity_main)	//	액티비티 화면 출력(뷰 객체 생성)
//	id값으로 뷰 객체 획득
val textView1: TextView = findViewById(R.id.text1)

findViewById()함수로 얻은 뷰 객체의 타입을 제네릭으로 명시해도 됩니다.

'findViewById<TextView>(R.id.text1)'

 

뷰의 크기를 지정하는 방법

뷰를 레이아웃 XML에 등록해 화면을 구성할 때 생략할 수 없는 속성이 크기입니다.

뷰가 화면에 나올 때 어떤 크기로 보여야 하는지는 필수 정보이며 이 크기를 설정하는 속성은 layout_width, layout_height입니다.

그리고 이 속성값은 다음 3가지 중 하나를 선언합니다.

1. 구체적 수치: 100px처럼 수치로 지정할 수 있습니다. px, dp등의 단위를 사용합니다.

2. match_parent: 부모의 크기 전체를 뜻합니다. 뷰 객체는 계층 구조를 이용하므로 자신보다 상위 계층의 크기를 의미합니다.

3. wrap_content: 화면에 출력할 수 있는 적절한 크기를 의미합니다. 문자열의 경우 길이나 글꼴 크기에 따라 문자열이 출력될 정도의 크기로 설정됩니다.

 

뷰의 간격 설정

뷰의 간격은 margin과 padding으로 설정합니다. margin은 뷰와 뷰 사이의 간격, padding은 뷰의 콘텐츠와 테두리 사이 간격입니다.

paddingLeft, paddingRight... layout_marginLeft, layout_marginRight ... 등을 이용할 수 있습니다.

 

뷰의 표시 여부 설정

visibility 속성은 뷰가 화면에 출력되어야 하는지를 설정합니다.

값을 visible, invisible, gone으로 설정합니다.

이때 invisible은 뷰가 화면에 보이지 않지만 자리는 차지하고, gone은 자리조차 차지하지 않습니다.

(invisible이나 gone을 사용하는 이유는 처음에는 화면에 보이지 않다가 어떤 순간이 되면 보이게 처리하기 위함입니다.)

 

코드에서 visibility 속성을 조정하려면 뷰의 visibility속성값을 View.VISIBLE이나 View.INVISIBLE로 설정하면 됩니다.

visibleBtn.setOnClickListener(){
	targetView.visibility = View.VISIBLE
}
visibleBtn.setOnClickListener(){
	targetView.visibility = View.INVISIBLE
}

(자바에서는 속성값을 변경하기 위해 setVisibility(View.Visible)로 사용하나, 코틀린은 자바와 다르게 필드가 아니라 프로퍼티 임으로 세터와 게터가 내장되어있습니다.)

 

06-3 기본적인 뷰 살펴보기

텍스트 뷰

TextView는 문자열을 화면에 출력하는 뷰 입니다.

 

android:text속성

TextView에 출력할 문자열을 지정합니다. android:text="helloworld"처럼 문자열을 대입해도 되고 android:text="@string/hello"처럼 문자열 리소르를 지정해도 됩니다.

 

android:textColor속성

문자열의 색상을 지정합니다. android:textColor="#FF0000"처럼 16진수 RGB형식을 사용합니다.

 

android:textSize속성

문자열의 크기를 지정합니다. android:textSize="20sp"처럼 값은 숫자를 사용하며 단위 생략은 불가능합니다.(px, dp, sp 등)

 

android:textStyle속성

문자열의 스타일을 지정합니다. android:textStyle="bold"처럼 이용합니다. (bold, italic, normal)

 

android:autoLink속성

TextView에 출력할 문자열을 분석해 특정 형태의 문자열에 자동 링크를 추가합니다.

android:autoLink"web"으로 설정하면 문자열에 웹 주소가 포함됐을 때 해당 문자열을 링크모양으로 표시합니다.

(web, phone, email 등 사용가능, 여러개를 함께 설정하려면 | 기호로 연결)

 

android:maxLines속성

긴 문자열의 자동 줄바꿈을 하거나 특정 줄까지만 나오도록 합니다.

android:maxLines="3"으로 설정하면3행 까지만 출력됩니다.

 

android:ellipsize속성

maxLines 속성을 이용할 때 출력되지 않은 문자열이 더 있다는 것을 표시하기위해 줄임표(...)를 넣습니다.

end, middle, start 등의 값이 있습니다. start와 middle은 앞이나 중간에 추가되는데 signleLine="ture" 속성으로 문자열을 한 줄로 출력했을 때만 적용됩니다.

 

이미지 뷰

ImageView는 이미지를 화면에 출력합니다.

 

android:src속성

ImageView에 출력할 이미지를 설정합니다. 리소스 이미지, 파일 이미지, 네트워크 이미지 등을 출력할 수 있습니다.

src 속성으로 android:src="@drawable/image3"처럼 설정합니다.

 

android:maxWidth, maxHeight, adjustViewBounds속성

이미지의 최대 크기를 지정합니다. 뷰의 크기는 layout_width, height 속성으로 설정하지만 이 크기는 고정되어있어서 뷰에 넣을 이미지 크기가 다양하다면 이미지 뷰와 뷰의 크기가 맞지 않는 상황이 발생할 수 있습니다.

maxWidth, maxHeight 속성은 adjustViewBounds속성과 함게 사용해야 하며, 이 속성을 true로 설정하면 이미지의 가로 세로 길이와 비례해 뷰의 크기를 맞춥니다.

 

버튼, 체크박스, 라디오 버튼

Button은 사용자 이벤트를 처리하고 CheckBox는 다중 선택을, RadioButton은 단일 선택을 제공하는 뷰입니다.

체크박스는 다중 선택을 제공하나 라디오 버튼은 하나만 선택할 수 있는 단일 선택이므로 묶어서 처리해야합니다.

RadioGgroup과 함께 사용하며 그룹으로 묶은 라디오 버튼 중 하나만 선택할 수 있습니다.

 

에디트 텍스트

EditText는 사용자가 글을 입력할 수 있는 뷰입니다.

 

adnroid:lines, maxLines속성

EditText는 한줄 크기로 출력 되었다가 사용자가 키보드에서 엔터를 누르면 아래로 늘어나서 여러 줄 입력 크기가 됩니다.

처음부터 여러줄 입력 크기로 나오게 하는것이 android:lines입니다. android:lines="3"과 같이 사용합니다.

maxLines="3"으로 지정하면 처음에는 한줄 크기로 입력이 되고, 사용자의 엔터입력에 따라 3줄까지만 늘어납니다.

 

android:inputType속성

글을 이볅할 때 올라오는 키보드를 지정하는 속성입니다. 예를들어 키보드로 한줄 입력을 강제하거나, 전화번호 입력 모드로 지정하고 싶을 때 사용합니다. android:inputType="phone"과 같이 사용합니다.

 

자주 사용하는 inptuType의 속성값은 아래와 같습니다.

속성값 설명
none 지정X, 모든 문자 입력 가능하며 줄바꿈 가능
text 문자열 한 줄 입력
textCapCharacters 대문자 입력 모드
textCapWords 각 단어의 첫 글자 입력 시 키보드가 자동으로 대문자 입력 모드
textCapSentences 각 문단의 첫 글자 입력 시 키보드가 자동으로 대문자 입력 모드
textMultiLine 여러 줄 입력 가능
textNoSuggestions 단어 입력 시 키보드의 추천 단어를 보여 주지 않음
textUri URL 입력 모드
textEmailAddress 이메일 주소 입력 모드
textPassword 비밀번호 입력 모드로 입력한 문자를 점으로 표시. 키보드는 영문자, 숫자, 특수 키만 표시
textVisiblePassword textPassword와 같으며 입력한 문자 표시
number 숫자 입력 모드
numberSigned number와 같으며 부호 키인 마이너스(-)입력 가능
numberDecimal number와 같으며 소숫점 입력 가능
numberPassword 숫자 키만 입력 가능, 입력한 문자는 점으로 표시
phone 전화번호 입력 모드

(inputType속성을 지원하지 않는 기기가 있을수도 있음)

 

06-4 뷰 바인딩

뷰 바인딩은 레이아웃 XML파일에 선언한 뷰 객체를 코드에서 쉽게 이용하는 방법입니다.

findViewById()함수로 얻어 사용하는 방법 대신 레이아웃 XML파일에 등록된 뷰 객체를 쉽게 사용하는 방법입니다.

<LinearLayout ...
	...>
  	<Button
    	android:id="@+id/visibleBtn"
        .../>
    <TextView
    	android:id="@+id/targetView"
        .../>
    <Button
    	android:id="@+id/invisibleButton"
        .../>
</LinearLayout>

이처럼 작성한 레이아웃 XML파일이 있다고 가정하겠습니다.

이곳에서 선언한 뷰 3개를 코드에서 id값을 얻어서 사용할 수도 있으나, 뷰 바인딩 기법을 이용해 코드에서 더 간편하게 이용할 수 있습니다.

 

뷰 바인딩을 사용하려면 그래들 파일에 다음처럼 선언해야 합니다.

android{
	(...생략...)
    viewBinding.isEnabled=true
}

build.gradle 파일을 열고 android 영역에 buildFeatures를 선언합니다.

그 안에 뷰 바인딩을 적용하라는 의미로 viewBinding=true를 설정합니다.

이러면 레이아웃 XML 파일에 등록된 뷰 객체를 포함하는 클래스가 자동으로 만들어집니다.

자동으로 만들어지는 클래스의 이름은 레이아웃 XML파일명을 따릅니다.

첫 글자를 대문자로 하고 밑줄(_)은 빼고 뒤에 오는 단어를 대문자로 만든 후 Binding을 추가합니다.

activity_main.xml → ActivityMainBinding

 

자동으로 만들어진 클래스의 inflate()함수를 호출하면 바인딩 객체를 얻을 수 있습니다.

이때 인자로 layoutInflater를 전달합니다. 그리고 바인딩 객체의 root 프로퍼티에는 XML의 루트 태그 객체가 자동으로 등록되므로 액티비티 화면 출력은 setCOntentView()함수에 binding.root를 전달하면 됩니다.

class MainActivity: AppCompatActivity(){
	override fun onCreate(savedInstanceState: Bundle?){
    	super.onCreate(savedInstanceState)
        
        //	바인딩 객체 등록
        val binding = ActivityMainBinding.inflate(layoutInflater)
        //	액티비티 화면 출력
        setContentView(binding.root)
        
        //	뷰 객체 이용
        binding.visibleBtn.setOnClickListener{
        	binding.targetView.visibility = View.VISIBLE
        }
        binding.invisibleBtn.setOnClickListenere{
        	binding.targetView.visibility = View.INVISIBLE
        }
    }
}

바인딩 객체에 등록된 뷰 객체명은 XML파일에 등록된 id 값을 따릅니다.

뷰 바인딩 이용을 build.gradle에 선언하면 레이아웃 XML하나당 바인딩 클래스가 자동으로 만들어지는데, 어떤 것들은 제이외하고 싶을 수도 있습니다. 이때는 해당 XML파일의 루트 태그에 tools:viewBindingIgnore="ture"라는 속성을 추가합니다.

'Java > Kotlin' 카테고리의 다른 글

Kotlin Programming Study - 08  (0) 2025.12.17
Kotlin Programming Study - 07  (0) 2025.12.16
Kotlin Programming Study - 04  (0) 2025.12.10
Kotlin Programming Study - 03  (0) 2025.12.09
Kotlin Programming Study - 02  (0) 2025.12.09
  • 코틀린 객체지향 프로그래밍

안드로이드 앱은 SDK가 제공하는 클래스를 상속받아 개발하므로 개발자는 주로 클래스를 다룹니다.

코틀린은 객체지향 프로그래밍을 제공하며 다른 언어와 차이가 있는 코틀린만의 기법이 있으므로 잘 기억해둡니다.

코틀린은 생성자를 주 생성자와 보조 생성자로 구분하는데 각각의 정의 방법과 어떻게 활용해야 하는지 알아야 합니다.


04-1 클래스와 생성자

클래스 선언

클래스는 class로 선언합니다. 클래스의 본문에 입력하는 내용이 없다면 {}를 생략할 수 있습니다.

(코틀린에서는 클래스의 생성자를 본문이 아닌 선언부에 작성할 수 있어서 본문이 없는 클래스도 의미가 있습니다.)

class User(val name: String, val age: Int)

(선언부에 생성자와 멤버변수를 함께 적고 내용을 생략한 예시)

 

클래스의 멤버는 생성자, 변수, 함수, 클래스로 구성됩니다. 생성자는 constructor라고 키워드로 선언하는 함수입니다.

class User{
	var name = "kkang"
    constructor(name: String){
    this.name = name
    }
    fun someFun(){
    	println("name: $name")
    }
    class SomeClass{}
}

 

클래스는 객체를 생성해 사용하며 객체로 클래스의 멤버에 접근합니다. 그런데 코틀린에서는 객체를 생성할 때 new 키워드를 사용하지 않습니다.

객체를 생성할 때 생성자가 자동으로 호출되므로 소괄호 안에 전달한 인자는 클래스에 선언된 생성자의 매개변수와 들어맞아야 합니다.

 

주 생성자

코틀린 클래스의 생성자는 주 생성자와 보조 생성자로 구분됩니다. 어느 하나만 선언할 수도 있고 둘다 선언할 수도 있습니다.

주 생성자는 constructor키워드로 클래스 선언부에 선언합니다. 필수는 아니지만 한 클래스에 하나만 가능합니다.

(constructor키워드는 생략될 수 있습니다.)

주 생성자를 선언하지 않으면 컴파일러가 매개변수 없는 주 생성자를 자동으로 추가합니다.

 

주 생성자의 본문 - init 영역

주 생성자를 이용해 객체를 생성할 때 특정한 로직을 수행할 수 있습니다. 그런데 주 생성자의 실행 역역인 본문을 다음처럼 추가하면 오류가 발생합니다.

class User(name: String, age: Int){
	//	주 생성자 본문
} {	//	오류 !
	//	클래스 본문
}

보통 클래스나 함수의 본문은 중괄호({})로 감싸지만 주 생성자에는 {}를 추가할 수 없습니다. 주 생성자는 클래스 선언부에 있기 때문입니다. 이럴 때 init 키워드를 이용해 주 생성자의 본문을 구현할 수 있습니다.

init 키워드로 지정한 영역: 객체를 생성할 때 자동으로 실행됩니다. 꼭 선언할 필요는 없으므로 주 생성자의 본문을 구현하고 싶을 때 사용합니다.

class User(name: String, age: Int){
	init{
    	println("I am Init")
    }
}

fun main(){
	val user = User("kkang", 10)
}

//	실행결과: I am Init 출력

 

생성자의 매개변수를 클래스의 멤버 변수로 선언하는 방법

생성자의 매개변수는 기본적으로 생성자에서만 사용할 수 있는 지역 변수입니다.

그런데 이 변수를 init영역이나 특정 함수에서 실행하고 싶은데, 생성자를 호출할 때 init영역이 실행되므로 이곳에서 생성자의 매개변수에 접근할 수 있습니다.

그러나 이는 지역변수이므로 다른 함수에 사용할 수는 없습니다. 만약 다른 함수에서 사용한다면 다음처럼 작성해야 합니다.

class User(name: String, age: Int){
	//	클래스 멤버 변수 선언
	var name: String
    var age: Int
    init{
    	//	클래스 멤버 변수에 생성자 매개변숫값 대입
    	this.name = name
        this.age = age
    }
    
    fun someFun(){
    	println("name: $name, count: $count")
    }
}

fun main(){
	var user = User("kkang", 10)
    user.someFun
}

그런데 이런 방법 말고 생성자의 매개변수를 클래스의 멤버 변수로 선언하는 방법이 있습니다.

 

class User(val name: String, val count: Int){
	fun someFun(){
    	println("name: $name, count: $count")
    }
}
fun main(){
	val user = User("kkang", 10)
    user.someFun()
}

매개변수를 선언할 때 var이나 val 키워드를 추가할 수 없으나 주 생성자에서 유일하게 이를 사용해 멤버변수로 동시에 선언할 수 있습니다.

 

보조 생성자

보조 생성자는 클래스 본문에 constructor 키워드로 선언하는 함수입니다. 클래스 본문에 선언하므로 여러개를 추가할 수 있습니다.

보조 생성자도 생성자이므로 객체를 생성할 때 자동으로 호출됩니다. 그리고 클래스 본문에 선언되므로 생성자 본문을 중괄호로 묶어서 객체 생성과 동시에 실행할 영역을 지정할 수 있습니다.

class User{
	constructor(name: String){
    	println("constructor(name: String) call...")
    }
    constructor(name: String, count: Int){
    	println("constructor(name: String, count: Int) call...")
    }
}

fun main(){
	val user1 = User("kkang")	//	실행결과: constructor(name: String) call...
    val user2 = User("kkang", 10)	//	실행결과: constructor(name: String, count: Int) call...
}

 

보조 생성자에 주 생성자 연결

둘 중 하나만 선언하면 문제가 없지만, 모두 선언한다면 반드시 생성자끼리 연결해주어야 합니다.

보조 생성자는 객체를 생성할 때 호출되며, 이때 클래스 내에 주 생성자가 있다면 this()구문으로 주 생성자를 호출해야 합니다.

class User(name: String){
	constructor(name: String, count: Int) :this(name){
    	...
    }
}

fun main(){
	val user = User("kkang", 10)
}

이렇게 하면 보조 생성자로 객체를 생성할 때, 주 생성자가 함께 호출됩니다.

만약 주 생성자가 있는 상태에서 보조생성자를 여러 개 선언한다면 보조 생성자에서 this()로 다른 보조 생성자를 호출할 수도 있습니다.

그런데 이때애도 보조 생성자로 객체를 생성한다면 어떤 식으로든 주 생성자가 호출되게 해야 합니다.

 

class User(name: String){
	constructor(name: String, count: Int): this(name){
    	
    }
    constructor(name: String, count: Int, email:String): this(name, count){
    
    }
}
fun main(){
	val user = User("kkang", 10, "a@a.com")
}

 

주 생성자와 보조 생성자 구분 이유는 객체를 여러가지 형태로 생성할 수 있도록 하기 위함입니다.

 

04-2 클래스를 재사용하는 상속

상속과 생성자

클래스를 선언할 때 다른 클래스를 참조해서 선언하는 것을 상속이라고 합니다. 코틀린에서는 선언부에 콜론(:)과 함께 상속받을 클래스 이름을 입력합니다.

open class Super{

}
class Sub: Super(){

}

상속 대상이 되는 클래스는 상위클래스, 상속받는 클래스를 하위 클래스라고 합니다.

코틀린의 클래스는 기본적으로 다른 클래스가 상속할 수 없습니다. class Super{}처럼 클래스를 선언하면 다른 클래스에서 Super클래스를 상속할 수 없습니다. 따라서 상속이 가능하게 open 키워드를 사용해 클래스의 상속을 허용합니다.

상위 클래스를 상속받은 하위 클래스의 생성자에서는 상위 클래스의 생성자를 호출해야 합니다.

: Supser() 구문의 괄호 안에서 매개변수가 없는 생성자를 호출합니다. 만약 매개변수가 있는 상위 클래스의 생성자를 호출할 때는 매개변수 구성에 맞게 인자를 전달해야 합니다.

 

오버라이딩-재정의

상위 클래스에 정의된 멤버(변수, 함수)를 하위클래서에서 자신의 멤버처럼 사용할 수 있습니다.

그런데 이를 재정의 해야할 경우 같은 이름으로 하위 클래스에 다시 선언하는것을 오버라이딩이라고 합니다.

(주로 함수를 재정의 하기 위해 사용됩니다.)

open class Super{
	open var someData = 10
    open fun someFun(){
    	println("i am super class function: $someData")
    }
}
class Sub: Super(){
	override var someData = 20
    override fun someFun(){
    	println("i am sub class function: $someData")
	}
}
fun main(){
	val obj = Sub()
    obj.someFun()	//	실행결과: i am sub class function: 20
}

오버라이딩 규칙은 허용할 변수나 함수 선언 앞에 open키워드를 추가하는 것입니다.

또, 하위 클래스에서 재정의 할 때는 반드시 선언문 앞에 override키워드를 추가해야 합니다.

 

접근 제한자

클래스의 멤버를 외부의 어느 범위까지 이용하게 할 것인지를 결정하는 키워드입니다.

접근 제한자 최상위에서 이용 클래스 멤버에서 이용
public 모든 파일에서 가능 모든 클래스에서 가능
internal 같은 모듈 내에서 가능 같은 모듈 내에서 가능
protected 사용 불가 상속 관계의 하위 클래스에서만 가능
private 파일 내부에서만 이용 클래스 내부에서만 이용

(같은 모듈: 그래들이나 메이븐같은 빌드 도구에서 프로젝트 단위 또는 같은 세트 단위를 가리킵니다.)

 

04-3 코틀린 클래스의 종류

데이터 클래스

data 키워드로 선언하며 자주 사용하는 데이터를 객체로 묶어 줍니다. VO(Value-Object)클래스를 편리하게 이용할 수 있게 해줍니다.

equals()함수로 두 객체를 비교하는 경우 data키워드로 선언한 클래스는 객체의 데이터를 비교하게 됩니다.

따라서 데이터 클래스의 경우 주 생성자에 val, var 키워드로 매개변수를 선언해 클래스의 멤버 변수로 활용하는 것이 일반적입니다.

data class DataClass(val name: String, val email: String, val age: Int){
	lateinit var address: String
    constructor(name: String, email: String, age: Int, address: String)
    : this(name, email, age){
    	this.address = address
    }
}
fun main(){
	val obj1 = DataClass("kkang", "a@a.com", 10, "seoul")
    val obj2 = DataClass("kkang", "a@a.com", 10, "busan")
    println("obj1.equals(obj2): ${obj.equals(obj2)}")
    //	실행결과: obj1.equals(obj2): true
}

주 생성자의 멤버변수만 비교하므로 위 코드의 실행결과는 true가 출력됩니다.

 

객체의 데이터를 반환하는 toString()함수

객체가 가지는 값을 확인하기 위해 사용하는 toString()함수는 데이터 클래스와 일반 클래스일때의 반환값이 다릅니다.

데이터 클래스의 toString()함수만 객체가 포함하는 멤버 변수의 데이터들을 출력합니다.

 

오브젝트 클래스

오브젝트 클래스는 익명 클래스를 만들 목적으로 사용합니다.

익명 클래스는 말 그대로 이름이 없는 클래스로 선언과 동시에 객체를 생성해야 합니다.

object키워드를 사용합니다.

val obj = object {
	var data = 10
    fun some(){
    	println("data: $data")
    }
}
fun main(){
	obj.data = 20
    obj.some()
    //	실행결과: 둘 다 오류 !
}

object키워드로 선언했지만 타입을 명시하지 않았으므로 이 객체는 코틀린의 최상위 타입인 Any로 취급합니다.

그런데 Any타입 객체는 data, some()이라는 멤버가 없어서 오류가 발생합니다.

따라서 익명 클래스를 선언할때는 보통 타입까지 뒤에 입력해서 선언합니다.

뒤에 콜론(:)을 입력하고 그 뒤에 클래스의 상위 또는 인터페이스를 입력합니다.

open class Super{
	open var data = 10
    open fun some(){
    	println("i am super some(): $data")
    }
}

val obj = object: Super(){
	override var data = 20
    override fun som(){
    	println("i am object some(): $data")
    }
}
fun main(){
	obj.data = 30
    obj.some()
    //	실행결과: i am object some: 30
}

 

컴패니언 클래스

컴패니언 클래스는 멤버 변수나 함수를 클래스 이름으로 접근하고자 할 때 사용합니다.

일반적으로 클래스의 멤버는 객체를 생성해서 접근해야 합니다. 그런데 컴패니언 클래스는 객체를 생성하지 않고서도 클래스 이름으로 특정 멤버를 이용할 수 있습니다.

class MyClass{
	companion object{
        var data = 10
        fun some(){
            println(data)
        }
    }
}

fun main(){
    MyClass.data = 20		//	성공
    MyClass.some()		//	성공
}

클래스 내부에 companion object {}형태로 선언하면 이 클래스를 감싸는 클래스 이름으로 멤버에 접근할 수 있습니다.

'Java > Kotlin' 카테고리의 다른 글

Kotlin Programming Study - 07  (0) 2025.12.16
Kotlin Programming Study - 06  (0) 2025.12.15
Kotlin Programming Study - 03  (0) 2025.12.09
Kotlin Programming Study - 02  (0) 2025.12.09
Kotlin Programming Study - 01  (0) 2025.12.05
  • 코틀린 시작하기

03-1 코틀린 언어 소개

코틀린은 젯프레인스에서 오픈소스 그룹을 만들어 개발한 프로그래밍 언어입니다.

자바를 대체할 목적으로 만들어져 JVM에서 실행할 수 있으나 둘은 다른 언어입니다.

자바는 확장자로 .java를 사용하지만 코틀린은 .kt를 사용합니다.

하지만 코틀린 컴파일러(kotlinc)가 .kt 파일을 컴파일하면 자바 바이트 코드가 만들어집니다. 그리고 이를 JVM이 실행합니다.

 

코틀린 개발이 자바 개발에 비해 갖는 이점

표현력과 간결함: 훨씬 간결한 구문으로 프로그램 작성이 가능합니다.

안전한 코드: 코틀린은 널 안정성을 지원하는 언어로 변수를 널허용(nullable)과 널불허용(not null)로 구분해 선언합니다.

상호 운용성: 자바와 100% 호환합니다. 자바 클래스나 라이브러리를 얼마든지 활용할 수 있습니다. 자바로 개발한 앱을 유지보수하기위해 추가하는 코드만 코틀린으로 작성할 수도 있습니다.

구조화 동시성: 코루틴이라는 기법을 이용하면 비동기 프로그래밍을 간소화 할 수 있습니다. 네트워크 연동이나 DB 갱신과 같은 작업을 할때 이용하면 프로그램을 조금 더 간단하고 효율적으로 작성할 수 있습니다.

 

코틀린 파일 구성

var data = 10

fun formatDate(date: Date) String {
	val sdformat = SimpleDateFormat("yyyy-mm-dd")
    return sdformat.format(date)
}

class User {
	var name = "hello"
    
    fun sayHello(){
    	println("name : $name")
    }
}

package 구문은 컴파일했을때 만들어지는 클래스파일의 위치를 나타내며 맨 처음 한 줄로 선언합니다.

그런데 package구문은 kt 파일의 위치와 상관없는 별도의 이름으로도 선언할 수 있습니다.

User.kt 파일이 com/example/test3에 있더라도 package ch3으로 선언하면 ch3폴더에 컴파일된 클래스파일을 생성합니다.

 

import구문은 여러 줄 작성할 수 있으며 변수, 함수, 클래스를 선언할 수 있습니다. 변수와 함수는 클래스 안, 밖에도 선언할 수 있습니다.

그리고 어떤 파일에 선언한 멤버(변수, 함수, 클래스)를 다른 코틀린 파일에서 참조할 때 두 파일을 같은 package로 선언했다면 import없이 사용할 수 있습니다.

 

자바와 다르게 코틀린 파일명과 그 파일에 선언한 클래스명은 아무런 상관이 없습니다.

코틀린의 변수 함수를 클래스로 묶지않고 선언한다면 변수, 함수, 클래스에 각각 접근하면 됩니다.

(자바는 모든 것을 클래스로 묶어야 합니다.)

 

03-2 변수와 함수

변수 선언하기

val, var 키워드로 선언합니다.

val: value의 줄임말로 초깃값이 할당되면 바꿀 수 없는 변수를 선언할 때 사용합니다.

var: variable의 줄임말로 초깃값이 할당된 후에도 값을 바꿀 수 있는 변수를 선언할 때 사용합니다.

 

타입 지정과 타입 추론

변수명 뒤에는 콜론(:)을 추가해 타입을 명시할 수 있으며, 대입하는 값에 따라 타입을 유추(타입 추론)할 수 있을 때는 생략할 수 있습니다.

val data1: Int = 10
val data2 = 10

data2는 대입한 값이 10이므로 타입을 명시하지 않아도 Int 타입이 됩니다.

 

초깃값 할당

최상위에 선언한 변수나 클래스의 멤버 변수는 선언과 동시에 초깃값을 할당해야 하며, 함수 내부에 선언하는 경우는 할당하지 않아도 됩니다.

val data1: Int
val data2 = 10

fun someFun(){
	val data3: Int
    println("data3 : $data3")	//	오류
    data3 = 10
    println("Data3 : $data3")	//	성공
}

class User{
	val data4: Int				//	오류
    val data5: Int = 10			//	성공
}

 

초기화 미루기

변수를 선언할 때 초깃값을 할당할 수 없는 경우가 있습니다.

이때는 값을 이후에 할당할 것이라고 컴파일러에게 알려주어야 합니다.

lateinit나 lazy키워드를 이용합니다.

 

lateinit: 이후에 초깃값을 할당할 것임을 명시적으로 선언합니다.

하지만 모든 유형의 변수 선언에 사용할 수는 없으며 다음 2가지 규칙을 따라야 합니다.

1. var 키워드로 선언한 변수에만 사용할 수 있습니다.

2. Int, Long, Short, Double, Float, Boolean, Byte 타입에는 사용할 수 없습니다.

 

lazy: 변수 뒤에 by lzay{}형식으로 선언하며, 소스에서 변수가 최초로 이용되는 순간 중괄호로 묶은 부분이 자동으로 실행되어 그 결괏값이 변수의 초깃값으로 할당됩니다.

val data4: Int by lazy{
	println("in lazy.....")
    10
}

fun main(){
	println("in main.....")
    println(data4 + 10)
    println(data4 + 10)
}

위 코드를 실행하면

in main.....

in lazy.....

20

20

이 출력됩니다.

 

데이터 타입

코틀린의 모든 변수는 객체 입니다. 정수를 다루는 타입이 Int인데 이것은 기초 데이터 타입(primitive type:int)가 아니라 클래스입니다.

(따라서 선언 및 초기화시 null 대입 가능, 객체의 메서드도 호출 가능)

val a1: Byte = 0b00001011
val a2: Int = 123
val a3: Short = 123
val a4: Long = 10L
val a5: Double = 10.0
val a6: Float = 10.0f
val a7: Boolean = true

기초 데이터를 객체로 표현하는 타입들 입니다.

 

Char, String - 문자와 문자열

Char는 문자를 표현하는 타입입니다. 작은 따옴표(')로 감싸서 사용하며 Number타입으로 표현할 수 없습니다.

String은 문자열을 표현하는 타입입니다. 큰따옴표(")나 삼중 따옴표(""")로 감싸서 표현합니다.

줄바꿈이나 들여쓰기 등을 그대로 유지하려면 역슬래시(\)를 사용합니다.

(안드로이드 스튜디오에서 삼중 따옴표를 사용하면 닫는 따옴표 뒤에 .trimIndent()함수가 자동으로 추가됩니다. 문자열 앞에 공백을 없에줍니다.)

 

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

fun main(){
    fun sum(no: Int): Int{
        var sum = 0
        for(i in 1..no){
            sum += i
        }
        return sum
    }

    val name: String = "kkang"
    println("name: $name, sum: ${sum(10)}, plus: ${10+20}")
}

 

Any - 모든 타입 가능

코틀린에서 최상위 클래스 입니다. 모든 코틀린의 클래스는 Any의 하위 클래스라 모든 타입의 데이터를 할당할 수 있습니다.

 

Unit - 반환문이 없는 함수

데이터의 형식이 아닌 특수한 상황을 표현하려는 목적으로 사용합니다.

Unit타입으로 선언한 변수에는 Unit 객체만 대입할 수 있습니다.

주로 함수의 반환 타입으로 사용됩니다. 함수에서 반환문이 없음을 명시적으로 나타낼 때 Unit타입을 사용합니다.

 

Noting - null이나 예외를 반환하는 함수

Unit과 마찬가지로 의미 있는 데이터가 아니라 특수한 상황을 표현합니다. Nothing으로 선언한 변수는 null만 대입할 수있습니다.

주로 함수의 반환 타입에 사용합니다. 어떤 함수의 반환 타입이 Nothing이면 반환은 있지만 의미 있는 값은 아니라는 의미 입니다.

항상 null만 반환하는 함수나 예외를 던지는 함수의 반환타입을 Nothing으로 선언합니다.

 

널 허용과 불허용

코틀린의 모든타입은 객체이므로 변수에 null을 대입할 수 있습니다.

null은 값이 할당되지 않은 상황을 의미합니다. 변수를 선언할 때 널 허용인지 널 불혀용인지 명확하게 구분해서 선언해야 합니다.

이런 구분은 물음표(?)로 표시합니다. 타입 뒤에 물음표를 추가하면 널 허용으로 선언 합니다.

 

함수 선언

fun이라는 키워드를 이용합니다.

fun 함수명(매개변수명: 타입) 반환타입 {...}

반환타입을 생략하면 자동으로 Unit타입이 적용됩니다.

 

함수의 매개변수에는 var이나 val 키워드를 사용할 수 없습니다. val이 자동으로 적용되며 함수 안에서 매개변숫값을 변경할 수 없습니다.

또, 함수의 매개변수에는 기본값을 선언할 수 있습니다. 이렇게하면 호출할 때 인자를 전달하지 않아도 되며 선언문에 명시한 기본값이 적용됩니다.

fun main(){
	fun some(data1: Int, data2: Int = 10): Int{
    	return data1 * data2
    }
    
    println(some(10))		//	실행결과 100
    println(some(10, 20))	//	실행결과 200
}

 

함수의 매개변수가 여러개면 호출할 때 전달한 인자를 순서대로 할당합니다. 그런데 매개변수명을 지정하면 매개변숫값의 순서를 바꿔도 됩니다.

fun some(data1: Int, data2: Int): Int{
	return data1 * data2
}
println(some(10, 20))

some(data2 = 20, data1 = 10)

매개변수명을 지정해 호출하는 것을 명명된 매개변수라고하며, 이렇게 하면 순서에 맞춰 호출하지 않아도 됩니다.

 

컬렉션 타입

여러 개의 데이터를 표현하는 방법이며 Array, List, Set, Map이 있습니다.

 

Array-배열표현

생성자의 첫 번째 매개변수는 배열의 크기이며, 두번째 매개변수는 초깃값을 지정하는 함수입니다. 배열의 타입은 제네릭(선언하는 곳이 아니라 이용하는 곳에서 타입을 지정하는 기법)으로 표현합니다.

<init>(size: Int, init: (Int) -> T)		//	Array클래스의 생성자
val data1: Array<Int> = Array(3, {0})

0으로 초기화한 데이터를 3개 나열한 정수형 배열을 선언 한 코드입니다.

배열의 데이터에 접근할 때는 대괄호([])를 이용해도 되고 set()이나 get()함수를 이용할 수도 있습니다.

 

기초타입의 배열

배열의 데이터가 기초 타입이라면 Array를 사용하지 않고 각 기초타입의 배열을 나타내는 클래스를 이용할 수도 있습니다.

(BooleanArray, ByteArray, CharArray .. 등)

val data1: IntArray = IntArray(3, {0})
val data2: BooleanArray = BooleanArray(3, {false})

또한 arrayOf()함수를 이용하면 배열을 선언할 때 값을 할당할 수도 있습니다.

val data1 = arrayOf<Int>(10, 20, 30)

arrayOf()함수도 기초타입을 대상으로 하는 booleanArrayOf(), byteArrayOf() ... 함수를 제공합니다.

 

List, Set, Map

Collection 인터페이스를 타입으로 표현한 클래스에며, 통틀어 컬렉션 타입 클래스라고 합니다.

List: 순서가 있는 데이터 집합으로 데이터의 중복을 허용

Set: 순서가 없으며 데이터의 중복을 불허용

Map: 키와 값으로 이루어진 데이터 집합으로 순서가 없으며 키의 중복을 불허용

 

초기에 데이터를 대입하면 더 이상 변경할 수 없는 불변 클래스와 이후에도 데이터를 추가하거나 변경할 수 있는 가변 클래스로 나뉩니다.

구분 타입 함수 특징
List List listOf() 불변
MutableList mutableListOf() 가변
Set Set setOf() 불변
MutableSet mutableSetOf() 가변
Map Map mapOf() 불변
MutableMap mutableMapOf() 가변
fun main(){
	var list = listOf<Int>(10, 20, 30)
    println(
    	"""
        list size : ${list.size}
        list data : ${list[0]), $(list.get(1)}, ${list.get(2)}
        """
    )
}
fun main(){
	var mutableList = mutableListOf<Int>(10, 20, 30)
    mutableList.add(3, 40)
    mutableList.set(0, 50)
    println(
    	"""
        list size : ${mutableList.size}
        list data : ${mutableList[0]}, ${mutableList.get(1)},
        			${mutableList.get(2)}, ${mutableList.get(3)}
        """
    )
}

 

Map객체는 키와 값으로 이루어진 데이터의 집합으로 키와 값은 Pair객체를 이용할 수도 있고 '키 to 값'형태로 이용할 수도 있습니다.

fun main(){
	var map = mapOf<String, String>(Pair("one", "hello"), "two" to "world")
    prnitln(
    	"""
        map size : ${map.size}
        map data : ${map.get("one")}, ${map.get("two")}
        """
    )
}

Pair("one", "hello")처럼 키값을 Pair 객채로 표현해 대입하거나 "two" to "world"처럼 대입한 코드입니다.

 

03-3 조건문과 반복문

조건문 if~else와 표현식

if문에 명시한 조건을 만족하면 if 부분을 실행하고, 그렇지 않으면 else부분을 실행합니다.

else if를 이용해 조건을 여러개로 나열할 수도 있습니다.

대부분의 프로그래밍 언어에서 제공하는 조건문과 차이가 없으나, 코틀린에서 if~else는 표현식으로도 사용할 수 있습니다.

표현식이란 결괏값을 반환하는 계산식을 말합니다. 즉 if~else문을 단순히 조건에 맞는 영역을 실행하는 용도로 사용해도 되지만, 결괏값을 반환하는 표현식으로도 사용할 수 있습니다.

fun main(){
	var data = 10
    val result = if(data>0){
    	println("data > 0")
        true
    }else{
        println("data <= 0")
        false
    }
    println(result)
}

단 if~else문을 표현식으로 사용하려면 항상 else구문이 있어야 하며 if~else혹은 if~else if~else 형태로 사용해야 합니다.

반환하는 결괏값은 각 영역의 마지막 줄에 해당합니다.

 

조건문 when

fun main(){
	var data = 10
    when (data) {
        10 -> println("data is 10")
        20 -> println("data is 20")
        else -> {
            println("data is not valid data")
        }
    }
}

소괄호 안에 넣은 데이터가 조건이 되고 이 값이 때라 화살표 오른쪽의 구문을 실행합니다.

정수가 아닌 다른 타입의 데이터를 지정할 수도 있습니다. ("hello", "hello" -> println(""))

fun main(){
	var data: Any = 10
    when (data){
    	is String -> println("data is String")	//	data가 문자열 타입이면
        20, 30 -> println("data is 20 or 30")	//	data가 20 또는 30이면
        in 1..10 -> println("data is 1..10")	//	data가 1~10의 값이면
        else -> println("data is not valid")
    }
}

(in: 범위 지정 연산자)

 

when문을 이용하면서 데이터를 명시하지 않고 조건만 명시할 수도 있습니다.

fun main(){
	var data = 10
    when{
    	data <= 0 -> println("data is <= 0")
        data > 100 -> println("data is > 100")
        else -> println("data is valid")
    }
}

 

if 문처럼 표현식으로도 사용할 수있습니다. else역시 생략할 수 없습니다.

fun main(){
	var data = 10
    var result = when{
    	data <= 0 -> data is <= 0
        data > 100 -> data is > 100
        else -> data is valid
    }
    println(result)
}

 

반복문 for 와 while

for문은 제어 변숫값을 증감하면서 특정 조건이 참일 때까지 구문을 반복해서 실행합니다. 주로 범위연산자인 in을 사용합니다.

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씩 감소

 

증감 조건을 숫자로 명시하지 않고 컬렉션 타입의 데이터 개수만큼 반복하게 할 수도 있습니다.

fun main(){
	var data = arrayOf<Int>(10, 20, 30)
    for(i in data.indices){
    	print(data[i])
        if(i !== data.size - 1) print(",")
    }
    //	실행결과: 10, 20, 30
}

indicies는 컬렉션 타입의 인덱스값을 의미하므로 i 값이 0, 1, 2를 대입합니다.

인덱스와 함께 실제 데이터를 가져오려면 withIndex()함수를 이용합니다.

 

fun mian(){
	var data = arrayOf<Int>(10, 20, 30)
    for ((index, value) in data.withIndex()){
    	print(value)
        if(index !== data.size - 1) print(",")
    }
    //	실행결과 : 10, 20, 30
}

 

 

'Java > Kotlin' 카테고리의 다른 글

Kotlin Programming Study - 07  (0) 2025.12.16
Kotlin Programming Study - 06  (0) 2025.12.15
Kotlin Programming Study - 04  (0) 2025.12.10
Kotlin Programming Study - 02  (0) 2025.12.09
Kotlin Programming Study - 01  (0) 2025.12.05
  • 안드로이드 소개
  • 안드로이드 앱 개발의 특징
  • 앱 구성 파일 분석

02-1. 안드로이드 소개

안드로이드 운영체제의 구조

1. 리눅스 커널(Linux Kernel) - 리눅스에 기반을 둔 오픈소스 소프트웨어 스택입니다.

2. 하드웨어 추상화 레이어(Hardware Abstraction Layer, HAL) - 상위의 자바 API 프레임워크에서 하드웨어 기능을 이용할 수 있게 표준 인터페이스를 제공합니다.

예를들어, 앱에서 카메라나 위치정보가 필요한 경우 자바 API를 이용해 각 제조사(삼성, 화웨이, 샤오미 등)별로 다른 HAL에 맞춰 처리할 수 있습니다.

3. 안드로이드 런타임(Android Runtime) - ART라고하며 앱을 실행하는 역할을 합니다. 안드로이드 앱은 DEX파일로 빌드되는데, 이 DEX파일을 해석해서 실행하는 주체가 ART 입니다.

자바는 컴파일 이후 클래스파일이 만들어지고 이것을 자바 가상 머신(JVM)에서 실행하는데, 안드로이드는 런타임 때 그대로 실행하지않고 DEX파일로 컴파일하고 이를 ART에서 해석, 실행합니다.

4. 네거티브 C/C++ 라이브러리 - 안드로이드 앱은 대부분 자바 프레임워크로 개발하지만 네티이브 C/C++ 라이브러리를 이용할 수도 있는데 이를 안드로이드 NDK(Native Development Kit)이라고 합니다.

5. 자바 API 프레임워크 - 앱을 개발할 때 사용하는 자바 API 입니다.

 

안드로이드 버전

새로운 안드로이드 버전이 나오면 개발자는 변경사항을 잘 파악해 앱에 적용하는것이 중요합니다.

안드로이드 버전은 13.0, 14.0 처럼 운영체제 버전을 가리키지만, 앱을 개발할 때 사용하는 버전은 API레벨(SDK 버전) 입니다.

운영체제 버전별로 API레벨이 지정되어 있기 때문에 소스코드에서는 대부분 API레벨을 이용합니다.

따라서, 개발자는 운영체제 버전과 API레벨을 함께 알고 있어야 합니다.

 

02-1. 안드로이드 앱 개발의 특징

컴포넌트 기반의 개발

컴포넌트는 애플리케이션의 구성요소라고 할 수 있습니다.

애플리케이션이 아니라(X), 애플리케이션을 구성하는 단위로 하나의 애플리케이션은 여러 컴포넌트로 구성됩니다.

컴포넌트의 형태는 상황에 따라 달라지는데 JAR이나 DLL로도 개발합니다.

안드로이드에서는 클래스로 컴포넌트를 개발합니다. 하나의 클래스가 하나의 컴포넌트가 되는 것입니다.

 

하지만 모든 클래스가 컴포넌트인 것은 아닙니다. 컴포넌트 클래스와 일반 컴포넌트로 구분됩니다.

생명주기 관리를 개발자 코드에서 한다면: 일반클래스(객체를 생성하거나 참조하며 코드에서 관리되는 경우)

생명주기를 안드로이드 시스템에서 관리한다면: 컴포넌트 클래스 (메모리 부족 등의 이유로 시스템이 생명주기를 관리하는 경우 예: 백그라운드 음악재생 등)

 

안드로이드 컴포넌트는 크게 4종류가 있습니다.

1. 액티비티: 화면을 구성하는 컴포넌트

2. 서비스: 백그라운드 작업을 하는 컴포넌트

3. 컨텐츠 프로바이더: 앱의 데이터를 공유하는 컴포넌트(앱간 데이터를 공유하는 경우 예: 카톡에서 프로필 변경을 위해 갤러리 앱 접근 등)

4. 브로드캐스트 리시버: 시스템 이벤트가 발생할 때 실행되는 컴포넌트(부팅 완료, 배터리 방전 등)

 

컴포넌트의 구분과 구성

컴포넌트는 앱이 실행될 때 안드로이드 시스템에서 생명주기를 관리하지만 개발자가 만들어야 하는 클래스입니다.

이를 만들기 이위해 지정된 클래스를 상속받는데 Activity 클래스를 상속받으면 액티비티 컴포넌트, 같은 방식으로 Service, ContentProvider, BroadcastReceiver를 상속받는 경우 해당 컴포넌트 클래스로 구분 지을 수 있습니다.

 

구성은 개발자가 만들고자 하는 앱의 기능과 화면 등을 고려해 필요한 만큼 구성하면 됩니다.

설계에 따라 달라지며 정해진 규칙은 없습니다.

(화면을 제공하지 않는 앱도 개발할 수 있습니다.)

 

컴포넌트는 앱 안에서 독립된 실행 단위이다.

카카오톡의 목록 화면과 채팅을 예로들겠습니다.

채팅방 목록 화면은 ListActivity라는 클래스명으로, 채팅 화면은 ChatActivity라는 클래스로 작성해 화면 전환을 한다고 가정합ㄴ디ㅏ.

ListActivity에서 ChatActivity객체를 생성해 실행하는 것은 안드로이드에서 불가능 합니다.

따라서 안드로이드 시스템에 의뢰해 두 클래스가 서로 종속되지 않고 독립해서 실행되게 해야 합니다.

 

따라서 다양한 앱 실행 시점에 대처가 가능하다.

카카오톡을 켜고 채팅창을 여는 방법이 있지만, 카카오톡 알림을 눌러 채팅창을 바로 열기도 합니다.

컴포넌트각 독립되어 실행되기 때문에 가능합니다.

 

애플리케이션 라이브러리를 사용할 수 있다.

카카오톡에서 사진을 즉석에서 찍어 공유하기 위해 카카오톡 → 카메라 → 카카오톡 순으로 카메라 앱을 라이브러리처럼 이용할 수 있습니다.

 

리소스를 활용한 개발이 가능하다.

리소스란 코드에서 정적인 값을 분리한 것입니다. 사용자 이벤트에 따라 동적인 값이 아니라 항상 같은 값이라면 굳이 코드에 담지 않고 분리해서 개발할 수 있습니다.

화면에 애국가 가사를 출력한다면, 애국가 가사를 저장해두고 리소스처럼 등록해 언제든 간편하게 사용할 수 있습니다.

이 외에 색상, 크기, 레이아웃, 이미지, 메뉴 등 많은 요소를 리소스로 활용할 수 있습니다. 이미지 등 몇몇을 제외하면 대부분 리소스는 XML로 작성해 활용합니다.

 

02-3. 앱 구성 파일 분석

프로젝트의 폴더 구성

프로젝트를 만들면 app이라는 모듈이 자동으로 생성되며 모듈 하나가 앱 하나(원한다면 하나의 앱을 여러 모듈로 나눠 개발 가능)이며 프로젝트는 여러 모듈을 묶어서 관리하는 개념입니다.

 

모듈의 폴더 구성

build.gradle.kts: 빌드 설정 파일

AndroidManifest.xml: 앱의 메인 환경 파일

res: 리소스 폴더

activity_main.xml: 레이아웃 xml 파일

MainActivity.kt: 메인 액티비티 파일

 

그래들 빌드 설정 파일

안드로이드 앱의 빌드 도구 입니다. [Gradle Scripts]에서 찾을 수 있습니다.

파일이 두개가 있는데 하나는 프로젝트 수준의 build.gradle.kts이고, 다른 하나는 모듈 수준의 build.gradle.kts입니다.

모듈은 앱을 의미하므로 대부분의 빌드 설정은 모듈 수준의 그래들 파일에 작성합니다.

 

리소스 폴더

앱의 리소스를 등록하는 목적으로 사용됩니다.

drawble: 이미지 리소스

layout: UI 구성에 필요한 XML 리소스

mipmap: 앱 아이콘 이미지

values: 문자열 등의 값으로 이용되는 리소스

 

res폴더 아래에 리소스를 만들면 자동으로 R.java 파일에 상수 변수로 리소스가 등록되며, 코드에서는 이 상수 변수로 리소스를 이용합니다.

res/drawble폴더에 person1.png파일을 등록했다면 이를 식별하기위한 int형 변수가 R.java에 등록되고 R.drawble.person1과 같은 형식으로 식별됩니다.

이는 다음과 같은 규칙을 갖습니다.

1. res 하위의 폴더명은 지정된 폴더명을 사용해야 합니다.

2. 각 리소스 폴더에 다시 하위폴더를 정의할 수는 없습니다.

3. 리소스 파일명은 자바의 이름 규칙을 위배할 수 없습니다. (숫자로 시작할 수 없음 등)

4. 리소스 파일명에는 알파벳 대문자를 이용할 수 없습니다. (addUser.xml 대신 add_user.xml으로 정의하는것이 일반적)

 

레이아웃 XML 파일

res/layout폴더 아래에 기본으로 만들어지는 activity_main.xml파일은 화면을 구성하는 레이아웃 XML파일입니다.

화면을 구성하는 요소로만 알아두고 화면을 프로그래밍 하는 방법에 대해 알아볼때 다룹니다.

 

메인 액티비티 파일

package com.example.androidlab

import android.os.Bundle
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat

//  메인 액티비티 파일
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContentView(R.layout.activity_main)
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
            val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
            insets
        }
    }
}

AppCompatActivity를 상속받아 정의되어있습니다. AppCompatActivity는 Activity의 하위 클래스입니다. 따라서 MainActivity는 액티비티 컴포넌트 클래스입니다. 화면 출력을 목적으로 하는 액티비티 클래스라는 의미입니다.

 

MainActivity클래스 실행 → onCreate()함수 호출

이 때 setContentView() 함수는 매개변수에 지정한 내용을 액티비티 화면에 출력합니다. R.layout.activity_main이 지정되어 res/layout/activity_main.xml 파일에 구성한 내용을 화면에 출력합니다.

enableEdgeToEdge()함수는 상단의 배터리표시가 있는 영역(Status Bar)와 하단 버튼 영역(Navigation Bar)까지 나오게 하는 설정입니다.

setOnApplyWindowInsetsListener()함수는 출력되는 내용이 NavigationBar등과 겹치지 않게 하기 위한 설정입니다.

이 두 함수는 자동으로 작성되어있지만 필요없다면 제거해도 됩니다.

 

 

'Java > Kotlin' 카테고리의 다른 글

Kotlin Programming Study - 07  (0) 2025.12.16
Kotlin Programming Study - 06  (0) 2025.12.15
Kotlin Programming Study - 04  (0) 2025.12.10
Kotlin Programming Study - 03  (0) 2025.12.09
Kotlin Programming Study - 01  (0) 2025.12.05

- 2025.12.05

프로젝트 생성


Package name: 앱의 식별값, 패키지명이 같은 앱은 스토어에 등록할 수 없을 뿐 아니라 기기에 설치할 수도 없으므로 고유한 이름으로 지어야 합니다.

Save Location: 프로젝트의 파일들이 저장되는 루트 디렉터리입니다.

Language: 안드로이드 앱을 어떤 언어로 개발할 것인지를 나타냅니다.(Java와 Kotlin 상호 이용 가능)

Minimum SDK: 앱이 설치되는 최소 SDK버전입니다.


안드로이드 SDK

안드로이드 앱을 개발하기 위한 툴킷. SDK는 개발 환경에 중요한 부분이므로 설치된 SDK를 잘 확인해야합니다.

  • SDK Platforms: 안드로이드 SDK 목록을 보여줍니다. 여기서 체크박스에 표시하거나 해제하는 방법으로 SDK를 설치하거나 업데이트 또는 제거할 수 있습니다.

 

  • SDK Tools: 개발자 도구들이 표시됩니다. 각 도구를 업데이트하거나 에뮬레이터 추가 도구를 설치할 수 있ㅅ습니다.
    • Android SDK Build-Tools: 앱을 빌드하는 데 필요한 도구
    • Android Emulator: 앱을 실행하는 데 필요한 도구
    • Android SDK Platform-Tools: 안드로이드 플랫폼과 연동되는 adb, fastboot, systrace와 같은 도구 모음

[Tip.01]

세팅에서 위 두 옵션을 체크하면 소스 코드에서 해당 클래스명만 입력해도 해당 패키지를 포함하는 import 구문을 자동으로 작성해 줍니다.


앱을 출시하는 방법

(간단하게 알아보기)

1. 배포파일

: 안드로이드의 배포파일은 APK(Android Application Package)와 AAB(Android App Bundle)이 있습니다.

APK는 안드로이드의 전통적인 배포 파일로 컴파일된 코드와 리소스를 묶어 코드로 서명한 것입니다.

AAB는 2018년 구글 IO에서 발표한 새로운 안드로이드 앱 배포 파일입니다.

(Play 스토어에 올리면 사용자 기기에 맞게 최적화된 APK를 대신 만들어 줍니다. 이때 앱을 내려받는 기기에 맞는 파일만 포함하므로 앱의 크기가 줄어드는 효과가 있습니다.)

 

2. 앱 서명

: 안드로이드 배포 파일을 만들려면 키를 만들어 앱에 서명해야 합니다.

두가지 방법이 있는데, 하나는 서명 키를 개발자가 직접 만들어 관리하는 방법이고, 다른 하나는 구글 Play에서 관리하는 방법입니다.

전자의 방법은 직접 만든 키를 Play스토어에 등록해 사용자에게 전달하는 방식이나, 개발자가 키를 분실하거나 도용될 때 대처할 수 없습니다.

이 문제점을 해결하고자 Play 앱 서명 이라는 서비스가 생겼습니다.

개발자가 만들고 관리하는 업로드 키와 구글 Play가 만드는 앱 서명 키로 이것이 사용자에게 전달됩니다.

 

3. 구글 Play 스토어에 게시

: 배포를 위해 준비해야 할 파일은 다음과 같습니다.

  • AAB 파일
  • 앱 아이콘 이미지
  • 그래픽 이미지
  • 휴대전화 스크린샷
  • 7-10인치 태블릿 스크린샷

자세한 배포는 배포에 대해 공부할 때 정리해 올리겠습니다.

'Java > Kotlin' 카테고리의 다른 글

Kotlin Programming Study - 07  (0) 2025.12.16
Kotlin Programming Study - 06  (0) 2025.12.15
Kotlin Programming Study - 04  (0) 2025.12.10
Kotlin Programming Study - 03  (0) 2025.12.09
Kotlin Programming Study - 02  (0) 2025.12.09

코딩 테스트에서 시간 초과를 자주 마주치게 됩니다.

최적화를 위해 입출력 방식부터 최적화가 가능하지 점검해보는 것이 좋은데 일반적으로 많이 사용하는 Scanner와 System.out.print 대신 BufferedReader와 BufferedWriter를 활용하면 처리 속도를 크게 향상할 수 있습니다.

(물론, 이전에 풀이 로직의 시간복잡도를 먼저 확인하고 최적화 하는것이 중요하겠습니다.)

 

입출력 데이터의 양이 많지 않다면 두 방법이 큰 성능 차이를 보이지 않지만, 입출력 데이터의 양이 많아질 수록 성능차이가 발생할 수 있다고 합니다.

 

아래는 1,000,000개의 정수를 입력받고 출력하는 데 걸리는 시간을 비교한 표입니다.

방식 입력 속도(초) 출력 속도(초) 총 소요 시간(초)
Scanner/System.out.print 1.8 1.2 3
BufferedReader/BufferedWriter 0.3 0.3 0.6

 

 

성능차이가 발생하는 이유

Scanner는 입력할 때마다 필요한 자료형으로 변환하는 과정을 거치므로 처리 속도가 느려질 수 있습니다.

이후 사용하는 System.out.print는 출력이 발생할 때마다 버퍼를 비우는 작업이 이뤄지므로 성능이 저하될 수 있습니다.

 

반면 BufferedReader는 입력을 버퍼에 저장한 후 데이터를 한번에 읽어 오는 방식으로 I/O 작업 횟수를 줄여 성능을 향상합니다.

BufferedWriter도 마찬가지로 출력할 데이터를 버퍼에 저장한 후 한 번에 출력하는 방식으로 성능을 개선합니다.

write()함수로 데이터를 버퍼에 추가하고, flush()함수로 버퍼의 내용을 한꺼번에 출력함으로써 효율을 높입니다.

 

public class Main(){
	public static void main(String[] args) throws IOException{
    	Scanner sc = new Scanner(SYstem.in);
        int a = sc.nextInt();
        Systemout.println(a);
        
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        int b = Integer.parseInt(br.readLine());
        bw.write(String.valueOf(b));	// 정수를 String형으로 변환하여 출력
        bw.flush();
    }
}

 

테스트코드를 작성하고 인풋데이터를 넣을 때, 예를들어 코딩테스트를 위해 작성한 코드에 예제 입력값을 넣어야 할 때, 매번 프로그램을 실행하고 예제 입력줄을 한 줄씩 넣는 방법을 사용하는데, 이 예제 입력창을 인텔리제이 환경에 띄우고 해당 입력창에 예제 입력줄을 전체 복사 붙여넣기해 즉시 사용하는 방법에 대해 알아봅니다.


1. 임의의 input.txt 파일 생성하기

예제입력을 작성할 빈 텍스트파일을 프로젝트 아무곳에 생성합니다.

 

2. 입력 리디렉션 설정하기

실행환경설정에서 편집을 클릭해 환경설정 창을 엽니다.

 

Modify optons를 선택하고, Redirect input을 체크해 'Redirect input from:' 옵션을 활성화합니다.

 

활성화 된 'Redirect input from:' 옵션에 1단계에서 만든 텍스트파일 경로를 입력하고 OK를 눌러 적용합니다.

 

3. 테스트 해보기

다음과 같이 'Hello World'를 입력텍스트 파일에 붙여넣고, 실행해보면 아래와같이 잘 출력되는것을 확인할 수 있습니다.

 

 

이후 여러줄의 예제입력이 추가로 필요한 경우에는 자신의 코드를 예제입력에 알맞게 작성해 분리해 사용하면 되겠습니다.

'Java > Basic' 카테고리의 다른 글

Scanner VS Buffered 의 차이  (1) 2025.08.07
Jar 파일과 War 파일 알아보기  (1) 2025.01.20
Garbage Collection 알아보기(작성 중)  (3) 2025.01.08
JVM(Java Virtual Machine)  (3) 2025.01.06

+ Recent posts