<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>J.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Tue, 28 Mar 2023 01:04:35 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>J.log</title>
            <url>https://velog.velcdn.com/images/true_je0n/profile/79541072-e580-44e9-b33d-a7c91a070555/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. J.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/true_je0n" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[안드로이드] Opacity(투명도) 설정 방법]]></title>
            <link>https://velog.io/@true_je0n/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-Opacity%ED%88%AC%EB%AA%85%EB%8F%84-%EC%84%A4%EC%A0%95-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@true_je0n/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-Opacity%ED%88%AC%EB%AA%85%EB%8F%84-%EC%84%A4%EC%A0%95-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Tue, 28 Mar 2023 01:04:35 GMT</pubDate>
            <description><![CDATA[<h3 id="opacity-컬러코드-투명도-hex-값">[Opacity 컬러코드 투명도 Hex 값]</h3>
<p>100% — FF
95% — F2
90% — E6
85% — D9
80% — CC
75% — BF
70% — B3
65% — A6
60% — 99
55% — 8C
50% — 80
45% — 73
40% — 66
35% — 59
30% — 4D
25% — 40
20% — 33
15% — 26
10% — 1A
5% — 0D
0% — 00</p>
<h3 id="안드로이드에서-view에-투명도-설정하는-방법">[안드로이드에서 View에 투명도 설정하는 방법]</h3>
<ol>
<li>각 Opacity 퍼센트에 따라 16진수로 2자리 숫자가 정해져있다.</li>
<li>해당 숫자 + 000000 를 붙여서 각 퍼센트에 맞는 투명도(Opacity)를 지정해줄 수 있다.</li>
<li>res-drawable 폴더에 xml 파일을 생성하고, selector 태그 안에 투명도를 설정해준다.<pre><code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;selector xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;&gt;
 &lt;item android:state_selected=&quot;true&quot;&gt;
     &lt;color android:color=&quot;#33000000&quot;/&gt;
 &lt;/item&gt;
&lt;/selector&gt;</code></pre></li>
<li>해당 xml을 투명도를 지정하고자 하는 view에 background로 지정해준다.<pre><code>android:background=&quot;@drawable/test_opacity&quot;</code></pre></li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[기술] Linux에서 Build 돌리기 전 필수로 확인할 사항]]></title>
            <link>https://velog.io/@true_je0n/%EA%B8%B0%EC%88%A0-Linux%EC%97%90%EC%84%9C-Build-%EB%8F%8C%EB%A6%AC%EA%B8%B0-%EC%A0%84-%ED%95%84%EC%88%98%EB%A1%9C-%ED%99%95%EC%9D%B8%ED%95%A0-%EC%82%AC%ED%95%AD</link>
            <guid>https://velog.io/@true_je0n/%EA%B8%B0%EC%88%A0-Linux%EC%97%90%EC%84%9C-Build-%EB%8F%8C%EB%A6%AC%EA%B8%B0-%EC%A0%84-%ED%95%84%EC%88%98%EB%A1%9C-%ED%99%95%EC%9D%B8%ED%95%A0-%EC%82%AC%ED%95%AD</guid>
            <pubDate>Mon, 20 Mar 2023 08:03:35 GMT</pubDate>
            <description><![CDATA[<h3 id="1-gitignore-파일-보고-필요-없는-파일-지우기">1. .gitignore 파일 보고 필요 없는 파일 지우기</h3>
<ul>
<li>.gradle</li>
<li>.idea</li>
<li>local.properties</li>
<li>/build<h3 id="2-unit-test-진행한-것이-아니면-test-관련-파일-및-코드-지우기">2. unit test 진행한 것이 아니면 test 관련 파일 및 코드 지우기</h3>
예시)</li>
<li>/test , /androidTest 등</li>
<li>build.gradle(Module) 에서 아래 코드 <pre><code>testInstrumentationRunner &quot;androidx.test.runner.AndroidJUnitRunner&quot;</code></pre><h3 id="3-buildgradlemodule">3. build.gradle(Module)</h3>
</li>
<li>signingConfigs 관련 코드 지우기</li>
<li>library 종속성 추가 코드 지우기</li>
<li>맨 하단에 아래 코드 추가<pre><code>if (System.getenv(&quot;ANDROID_BUILD_TOP&quot;) != null) {
  String path = System.getenv(&quot;ANDROID_BUILD_TOP&quot;)
  /* System build */
  String[] libraries = System.getenv(&quot;LOCAL_JAVA_LIBRARIES&quot;).split(&quot; &quot;)
  for (def library : libraries) {
      dependencies.add(&quot;compileOnly&quot;, files(path + &quot;/&quot; + library))
  }
  libraries = System.getenv(&quot;LOCAL_STATIC_JAVA_LIBRARIES&quot;)
  for (def library : libraries) {
      dependencies.add(&quot;implementation&quot;, files(path + &quot;/&quot; + library)).split(&quot; &quot;)
  }
}</code></pre></li>
</ul>
<h3 id="4-androidmk-파일-확인하기">4. Android.mk 파일 확인하기</h3>
<ul>
<li>LOCAL_JAVA_LBRARIES 에 앱에서 사용하는 lib 잘 들어갔는지 확인하기.</li>
<li>모듈이 여러 개인 앱이면 PRODUCT_CONSTRUCTOR 분기 치기</li>
<li>danger permission 사용하는 앱이면 permission 코드 추가 되어있는지 확인하기.</li>
</ul>
<h3 id="5-danger-permission-사용하는-앱이면-permissionxml-넣었는지-확인하기">5. danger permission 사용하는 앱이면 permission.xml 넣었는지 확인하기.</h3>
<h3 id="6-모듈이-여러-개인-앱이라면-settingsgradle에-빌드-하고자-하는-모듈이-들어가있는지-확인하기">6. 모듈이 여러 개인 앱이라면 settings.gradle에 빌드 하고자 하는 모듈이 들어가있는지 확인하기.</h3>
]]></description>
        </item>
        <item>
            <title><![CDATA[[기술] 월패드에서 apk파일 (또는 폴더) 지우기]]></title>
            <link>https://velog.io/@true_je0n/%EA%B8%B0%EC%88%A0-%EC%9B%94%ED%8C%A8%EB%93%9C%EC%97%90%EC%84%9C-apk%ED%8C%8C%EC%9D%BC-%EB%98%90%EB%8A%94-%ED%8F%B4%EB%8D%94-%EC%A7%80%EC%9A%B0%EA%B8%B0</link>
            <guid>https://velog.io/@true_je0n/%EA%B8%B0%EC%88%A0-%EC%9B%94%ED%8C%A8%EB%93%9C%EC%97%90%EC%84%9C-apk%ED%8C%8C%EC%9D%BC-%EB%98%90%EB%8A%94-%ED%8F%B4%EB%8D%94-%EC%A7%80%EC%9A%B0%EA%B8%B0</guid>
            <pubDate>Mon, 20 Mar 2023 07:59:07 GMT</pubDate>
            <description><![CDATA[<ol>
<li>adb remount &lt;= 필수*</li>
<li>adb shell</li>
<li>rm -rf [폴더명 또는 파일명]</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[기술] adb 명령어로 pkg 파일 push하]]></title>
            <link>https://velog.io/@true_je0n/%EA%B8%B0%EC%88%A0-adb-%EB%AA%85%EB%A0%B9%EC%96%B4%EB%A1%9C-pkg-%ED%8C%8C%EC%9D%BC-push%ED%95%98</link>
            <guid>https://velog.io/@true_je0n/%EA%B8%B0%EC%88%A0-adb-%EB%AA%85%EB%A0%B9%EC%96%B4%EB%A1%9C-pkg-%ED%8C%8C%EC%9D%BC-push%ED%95%98</guid>
            <pubDate>Mon, 20 Mar 2023 07:58:15 GMT</pubDate>
            <description><![CDATA[<ol>
<li>adb 명령어 강제실행 : adb root; adb remount</li>
<li>adb shell 실행 : adb shell</li>
<li>adb push 명령어 : adb push .\FacePass.apk system/app/FacePass</li>
<li>adb 리부팅 : adb reboot</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[안드로이드] RecyclerView 샘플코드]]></title>
            <link>https://velog.io/@true_je0n/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-RecyclerView-%EC%83%98%ED%94%8C%EC%BD%94%EB%93%9C</link>
            <guid>https://velog.io/@true_je0n/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-RecyclerView-%EC%83%98%ED%94%8C%EC%BD%94%EB%93%9C</guid>
            <pubDate>Mon, 20 Mar 2023 07:34:06 GMT</pubDate>
            <description><![CDATA[<h3 id="mainactivityjava">MainActivity.java</h3>
<pre><code>package com.example.myapplication;

import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import android.os.Bundle;

public class MainActivity extends AppCompatActivity {
    RecyclerView recyclerView;
    Adapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
        recyclerView.setLayoutManager(new LinearLayoutManager(this, RecyclerView.VERTICAL, false));

        adapter = new Adapter();
        for (int i = 0; i &lt; 100; i++) {
            String str = i + &quot;번째 아이템&quot;;
            adapter.setArrayData(str);
        }

        recyclerView.setAdapter(adapter);
    }
}</code></pre><h3 id="viewholderjava">ViewHolder.java</h3>
<pre><code>package com.example.myapplication;

import android.content.Context;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import androidx.recyclerview.widget.RecyclerView;

public class ViewHolder extends RecyclerView.ViewHolder {
    public TextView textView;
    public Button button;

    ViewHolder(Context context, View itemView) {
        super(itemView);

        textView = itemView.findViewById(R.id.textView);
        button = itemView.findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String strText = textView.getText().toString();
                Toast.makeText(context, strText, Toast.LENGTH_SHORT).show();
            }
        });
    }
}</code></pre><h3 id="adapterjava">Adapter.java</h3>
<pre><code>package com.example.myapplication;

import android.annotation.SuppressLint;
import android.content.Context;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import java.util.ArrayList;

public class Adapter extends RecyclerView.Adapter&lt;ViewHolder&gt; {
    private ArrayList&lt;String&gt; arrayList;

    public Adapter() {
        arrayList = new ArrayList&lt;&gt;();
    }

/*
- onCreateViewHolder() : RecyclerView는 ViewHolder를 새로 만들어야 할 때마다 이 메서드를 호출합니다.
이 메서드는 ViewHolder와 그에 연결된 View를 생성하고 초기화하지만 뷰의 콘텐츠를 채우지는 않습니다.
ViewHolder가 아직 특정 데이터에 바인딩된 상택 아니기 때문입니다.
*/
    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        Context context = parent.getContext();
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View view = inflater.inflate(R.layout.item_list, parent, false);

        ViewHolder viewholder = new ViewHolder(context, view);

        return viewholder;
    }

/*
- onBindViewHolder() : RecyclerView는 ViewHolder를 데이터와 연결할 때 이 메서드를 호출합니다.
이 메서드는 적절한 데이터를 가져와서 그 데이터를 사용하여 뷰 홀더의 레이아웃을 채웁니다.
예를 들어 RecyclerView가 이름 목록을 표시하는 경우 메서드는 목록에서 적절한 이름을 찾아 뷰 홀더의 TextView 위젯을 채울 수 있습니다.
*/
    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        String text = arrayList.get(position);
        holder.textView.setText(text);
    }

/*
- getItemCount() : RecyclerView는 데이터 세트 크기를 가져올 때 이 메서드를 호출합니다.
예를 들어 주소록 앱에서는 총 주소 개수가 여기에 해당할 수 있습니다.
RecyclerView는 이 메서드를 사용하여, 항목을 추가로 표시할 수 없는 상황을 확인합니다.
*/
    @Override
    public int getItemCount() {
        return arrayList.size();
    }

    //데이터를 입력
    public void setArrayData(String strData) {
        arrayList.add(strData);
    }
}</code></pre><h3 id="activity_mainxml">activity_main.xml</h3>
<pre><code>
&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;RelativeLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    xmlns:app=&quot;http://schemas.android.com/apk/res-auto&quot;
    xmlns:tools=&quot;http://schemas.android.com/tools&quot;
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;match_parent&quot;
    tools:context=&quot;.MainActivity&quot;&gt;

    &lt;androidx.recyclerview.widget.RecyclerView
        android:id=&quot;@+id/recycler_view&quot;
        android:layout_width=&quot;match_parent&quot;
        android:layout_height=&quot;match_parent&quot;/&gt;

&lt;/RelativeLayout&gt;</code></pre><h3 id="item_listxml">item_list.xml</h3>
<pre><code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;RelativeLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;50dp&quot;&gt;

    &lt;TextView
        android:layout_width=&quot;match_parent&quot;
        android:layout_height=&quot;match_parent&quot;
        android:layout_alignParentLeft=&quot;true&quot;
        android:text=&quot;text text text text&quot;
        android:textSize=&quot;20dp&quot;
        android:gravity=&quot;center_vertical&quot;
        android:id=&quot;@+id/textView&quot;/&gt;

    &lt;Button
        android:layout_width=&quot;wrap_content&quot;
        android:layout_height=&quot;match_parent&quot;
        android:layout_alignParentRight=&quot;true&quot;
        android:text=&quot;button&quot;
        android:id=&quot;@+id/button&quot;/&gt;


&lt;/RelativeLayout&gt;</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[안드로이드] View가 그려지는 과정]]></title>
            <link>https://velog.io/@true_je0n/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-View%EA%B0%80-%EA%B7%B8%EB%A0%A4%EC%A7%80%EB%8A%94-%EA%B3%BC</link>
            <guid>https://velog.io/@true_je0n/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-View%EA%B0%80-%EA%B7%B8%EB%A0%A4%EC%A7%80%EB%8A%94-%EA%B3%BC</guid>
            <pubDate>Mon, 20 Mar 2023 07:25:17 GMT</pubDate>
            <description><![CDATA[<h3 id="1-constructor-">1. Constructor :</h3>
<p>모든 뷰는 생성자에서 출발하게 됩니다.
생성자에서 초기화되고, default 값을 설정합니다.
뷰는 초기설정을 쉽게 세팅하기 위해서 AttributeSet 이라는 인터페이스를 지원합니다.
attrs.xml 파일(res/values/attrs.xml)을 만들어 이것을 부름으로서 뷰의 설정값을 쉽게 설정할 수 있습니다.</p>
<h3 id="2-onattachedtowindow-">2. onAttachedToWindow() :</h3>
<p>부모 뷰가 addView(childView)를 호출한 뒤 자식뷰는 윈도우에 붙게 됩니다.
이때 뷰의 id를 통해 접근할 수 있습니다.</p>
<h3 id="3-onmeasure-">3. onMeasure() :</h3>
<p>뷰의 크기를 측정하는 단계로 중요합니다.
레이아웃에 맞게 특정 크기를 가져가야 합니다.</p>
<p>여기서는 세 가지의 단계가 있는데</p>
<ol>
<li>뷰가 원하는 사이즈를 계산합니다.</li>
<li>MeasureSpec에 따라 mode를 가져옵니다.</li>
<li>MeasureSpec의 mode를 체크하여 뷰의 크기를 적용합니다.</li>
</ol>
<h3 id="4-onlayout-">4. onLayout() :</h3>
<p>뷰의 위치와 크기를 할당합니다.
onMeasuer를 통해 사이즈가 결정된 후에 onLayout이 불립니다.
부모뷰일때 주로 쓰이며, child 뷰를 붙일 때 위치를 정해주는데 사용합니다.
넘어오는 파라미터는 어플리케이션 전체를 기준으로 위치가 넘어옵니다. (주의)</p>
<h3 id="5-ondraw-">5. onDraw() :</h3>
<p>뷰를 실제로 그리는 단계입니다.
Canvas와 Paint를 이용하여 필요한 내용을 그립니다.</p>
<p>여기서 주의할 점은 onDraw 함수를 호출시 많은 시간이 소요됩니다.
Scroll 또는 Swipe 등을 할 경우 뷰는 다시 onDraw와 onLayout을 다시 호출하게 됩니다.
따라서 함수 내에서 객체할당을 피하고 한 번 할당한 객체를 재사용할 것을 권장합니다.</p>
<p>퍼센티지를 보여주는 숫자 0~100과 그에 맞는 퍼센티지를 원으로 보여주는 뷰를 그립니다.
invalidate를 통하여 1초마다 갱신하여 다시 그립니다.</p>
<h3 id="viewupdate">ViewUpdate</h3>
<ul>
<li>invalidate() : 단순히 뷰를 다시 그릴 때 사용됩니다. 예를 들어 뷰의 text 또는 color가 변경되거나, touch interactivity가 발생할 때 onDraw() 함수를 재호출하면서 뷰를 업데이트 합니다.</li>
<li>requestLayout() : onMeasure()부터 다시 뷰를 그린다. 뷰의 사이즈가 변경될 때 그것을 다시 재측정해야하기 lifecycle을 onMeasure() 부터 순회하면서 뷰를 그립니다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[안드로이드] MVVM 패턴]]></title>
            <link>https://velog.io/@true_je0n/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-MVVM-%ED%8C%A8%ED%84%B4</link>
            <guid>https://velog.io/@true_je0n/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-MVVM-%ED%8C%A8%ED%84%B4</guid>
            <pubDate>Mon, 20 Mar 2023 07:22:00 GMT</pubDate>
            <description><![CDATA[<h3 id="1-mvvm-패턴-구조">1. MVVM 패턴 구조</h3>
<ul>
<li>View : UI 요소를 표시하며 사용자가 발생한 이벤트를 받는 역할을 합니다.</li>
<li>ViewModel : UI 요소에 들어갈 데이터를 관리합니다. 그리고 Model과 View 사이의 다리 역할을 합니다.</li>
<li>Model : 데이터 전반적인 것을 처리하는 역할을 합니다.</li>
</ul>
<h3 id="2-mvvm-패턴의-장점">2. MVVM 패턴의 장점</h3>
<p>여러 화면이 있더라도 비슷한 데이터를 가지고 있는 애라면 같은 ViewModel을 공유할 수 있다.
(MVP 처럼 1:1이 아니기 때문)
ViewModel이 직접적으로 요소를 그리라고 View에게 요청하지 않기 때문.</p>
<h3 id="3-mvvm-패턴의-단점">3. MVVM 패턴의 단점</h3>
<p>간단한 프로젝트에 사용하기에는 과하다.
비교적 구현 구조가 복잡하고 설계가 수비지 않다.</p>
<h3 id="4-mvvm-패턴-구현">4. MVVM 패턴 구현</h3>
<p>이번 예제의 프로젝트 구조입니다.</p>
<h4 id="a-buildgradle-모듈-앱">a. build.gradle (모듈 앱)</h4>
<pre><code>dependencies {
    implementation &#39;androidx.appcompat:appcompat:1.5.1&#39;
    implementation &#39;com.google.android.material:material:1.7.0&#39;
    implementation &#39;androidx.constraintlayout:constraintlayout:2.1.4&#39;
    implementation &#39;com.google.android.material:material:1.0.0&#39;
    implementation &#39;androidx.drawerlayout:drawerlayout:1.1.1&#39;
    implementation &#39;com.orhanobut:logger:2.2.0&#39;
    testImplementation &#39;junit:junit:4.13.2&#39;
    androidTestImplementation &#39;androidx.test.ext:junit:1.1.4&#39;
    androidTestImplementation &#39;androidx.test.espresso:espresso-core:3.5.0&#39;
}</code></pre><h4 id="b-activity_mainxml">b. activity_main.xml</h4>
<pre><code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;layout xmlns:tools=&quot;http://schemas.android.com/tools&quot;
    xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    xmlns:app=&quot;http://schemas.android.com/apk/res-auto&quot;&gt;
    &lt;data&gt;
        &lt;variable
            name=&quot;viewModel&quot;
            type=&quot;com.example.project1.viewmodel.ViewModel&quot; /&gt;
    &lt;/data&gt;

    &lt;androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width=&quot;match_parent&quot;
        android:layout_height=&quot;match_parent&quot;
        tools:context=&quot;.view.MainActivity&quot;&gt;

        &lt;TextView
            android:layout_width=&quot;wrap_content&quot;
            android:layout_height=&quot;wrap_content&quot;
            android:text=&quot;오늘의 간식 당번&quot;
            android:textSize=&quot;40sp&quot;
            app:layout_constraintBottom_toBottomOf=&quot;parent&quot;
            app:layout_constraintHorizontal_bias=&quot;0.5&quot;
            app:layout_constraintLeft_toLeftOf=&quot;parent&quot;
            app:layout_constraintRight_toRightOf=&quot;parent&quot;
            app:layout_constraintTop_toTopOf=&quot;parent&quot;
            app:layout_constraintVertical_bias=&quot;0.25&quot; /&gt;

        &lt;TextView
            android:id=&quot;@+id/user_textview&quot;
            android:layout_width=&quot;wrap_content&quot;
            android:layout_height=&quot;wrap_content&quot;
            android:text=&quot;@{viewModel.winner}&quot;
            android:textSize=&quot;30sp&quot;
            app:layout_constraintBottom_toBottomOf=&quot;parent&quot;
            app:layout_constraintLeft_toLeftOf=&quot;parent&quot;
            app:layout_constraintRight_toRightOf=&quot;parent&quot;
            app:layout_constraintTop_toTopOf=&quot;parent&quot;
            app:layout_constraintVertical_bias=&quot;0.4&quot; /&gt;

        &lt;Button
            android:id=&quot;@+id/ok_btnview&quot;
            android:layout_width=&quot;114dp&quot;
            android:layout_height=&quot;68dp&quot;
            android:text=&quot;돌리기&quot;
            android:textSize=&quot;20sp&quot;
            android:textStyle=&quot;bold&quot;
            app:layout_constraintBottom_toBottomOf=&quot;parent&quot;
            app:layout_constraintLeft_toLeftOf=&quot;parent&quot;
            app:layout_constraintRight_toRightOf=&quot;parent&quot;
            app:layout_constraintTop_toTopOf=&quot;parent&quot;
            app:layout_constraintVertical_bias=&quot;1.0&quot; /&gt;

    &lt;/androidx.constraintlayout.widget.ConstraintLayout&gt;
&lt;/layout&gt;</code></pre><h4 id="c-mainactivityjava">c. MainActivity.java</h4>
<pre><code>package com.example.project1.view;

import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;

import android.os.Bundle;
import android.view.View;

import com.example.project1.R;
import com.example.project1.databinding.ActivityMainBinding;
import com.example.project1.model.Database;
import com.example.project1.viewmodel.ViewModel;
import com.orhanobut.logger.Logger;

public class MainActivity extends AppCompatActivity {

    ActivityMainBinding binding; //상속 ViewDataBinding
    ViewModel viewModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        Logger.d(&quot;Main_onCreate() 실행&quot;);
        super.onCreate(savedInstanceState);
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main);

        viewModel = new ViewModel(Database.getInstance());
        binding.setViewModel(viewModel);

        binding.okBtnview.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Logger.d(&quot;버튼 클릭&quot;);
                viewModel.getUser();
            }
        });

    }
}</code></pre><h4 id="d-viewmodeljava">d. ViewModel.java</h4>
<pre><code>package com.example.project1.viewmodel;

import androidx.databinding.BaseObservable;

import com.example.project1.model.Database;
import com.example.project1.model.Person;
import com.orhanobut.logger.Logger;

import java.util.ArrayList;
import java.util.List;

public class ViewModel extends BaseObservable {
    private Database database;
    private List&lt;Person&gt; items = new ArrayList&lt;&gt;();
    private String winner;

    public ViewModel(Database database){
        Logger.d(&quot;ViewModel 생성자 실행 | DB(Model) 참조&quot;);
        this.database = database;

        this.database.setOnDatabaseListener(new Database.DatabaseListener() {
           @Override
           public void onChanged() {
               Logger.d(&quot;리스너 실행&quot;);
               winner = null;
               winner = database.getWinner();
               notifyChange();
           }
        });
    }

    public void getUser() {
        Logger.d(&quot;db에게 user(winner)를 달라고 요청&quot;);
        database.getUser();
    }

    public String getWinner() {
        Logger.d(&quot;Winner 변환 (%s)&quot;, winner);
        return winner;
    }
}</code></pre><h4 id="e-database">e. Database</h4>
<pre><code>package com.example.project1.model;

import com.orhanobut.logger.Logger;

import java.util.ArrayList;

public class Database {
    private static Database instance;
    private ArrayList&lt;Person&gt; personList = new ArrayList&lt;&gt;();
    private String winner;
    private DatabaseListener databaseListener;

    private Database(){
        Logger.d(&quot;Model인 Database 생성&quot;);
        personList.add(new Person(0, &quot;최00&quot;));
        personList.add(new Person(1, &quot;김00&quot;));
        personList.add(new Person(2, &quot;고00&quot;));
        personList.add(new Person(3, &quot;문00&quot;));
        personList.add(new Person(4, &quot;윤00&quot;));
    }

    public static Database getInstance() {
        Logger.d(&quot;Model에 접근 할 수 있도록 DB 인스턴스 값 요청&quot;);
        if(instance == null) {
            instance = new Database();
        }
        return instance;
    }

    public void getUser() {
        Logger.d(&quot;당첨자 획득&quot;);
        winner = personList.get((int)(Math.random()*5)).getName();
        notifyChange();
    }
    private void notifyChange() {
        if(databaseListener != null) {
            Logger.d(&quot;Model | Data 변경 되어 notify 하라고 알림&quot;);
            databaseListener.onChanged();
        }
    }

    public void setOnDatabaseListener(DatabaseListener databaseListener) {
        Logger.d(&quot;DatabaseListener 구현 객체 참조 변수 세팅 (arg1 : %s)&quot;, databaseListener.getClass().getSimpleName());
        this.databaseListener = databaseListener;
    }

    public String getWinner() {
        return winner;
    }

    public interface DatabaseListener {
        void onChanged();
    }
}</code></pre><h4 id="f-person">f. Person</h4>
<pre><code>package com.example.project1.model;

public class Person {
    private long id;
    private String name;

    public Person(long id, String name) {
        this.id = id;
        this.name = name;
    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[안드로이드] MVP 패턴 적용해보기 - View와 Presenter]]></title>
            <link>https://velog.io/@true_je0n/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-MVP-%ED%8C%A8%ED%84%B4-%EC%A0%81%EC%9A%A9%ED%95%B4%EB%B3%B4%EA%B8%B0-View%EC%99%80-Presenter</link>
            <guid>https://velog.io/@true_je0n/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-MVP-%ED%8C%A8%ED%84%B4-%EC%A0%81%EC%9A%A9%ED%95%B4%EB%B3%B4%EA%B8%B0-View%EC%99%80-Presenter</guid>
            <pubDate>Mon, 20 Mar 2023 07:13:34 GMT</pubDate>
            <description><![CDATA[<p>MVP는 Model과 View, Presenter로 나누는 패턴이다.
MVC와 비교하여 View와 Model이 서로 존재를 몰라 의존성이 줄어든다.
이말은 쉽게 View와 Model 클래스를 변경, 수정할 수 있고 MVC에 비해 Model 클래스의 유닛 테스트를 쉽게 시도할 수 있다.</p>
<ul>
<li>Model</li>
<li>Local DB, Remote DB, sharedPreference .. 등 데이터를 수정, 관리하는 Class</li>
<li>Presenter</li>
<li>사용자의 액션을 받아 로직을 처리하고, Model에게 Data 변경을 요청하거나 UI 업데이트하는 로직을 처리하는 Class</li>
<li>ViewContract : View and Presenter의 설계</li>
<li>사용자 액션을 Presetner에 떠넘기고, UI 업데이트하는 코드만 있는 클래스</li>
</ul>
<pre><code>interface AccountContract {
   interface View{
      public void showText();
   }

   interface Presenter {
     public void clickButton();
   }
}</code></pre><p>Google Architecture에 따르면 Contract는 View와 Presenter 서로 소통하는 인터페이스를 정의합니다.</p>
<p>위 AccoutContact로 View와 Presenter 예제를 봅시다.</p>
<h3 id="view-정의하기">View 정의하기</h3>
<pre><code>class AccountActivity implements AccountCountact, View.OnClickListener {

private AccountPresenter presenter;

@Override
  protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_account);
      presenter = new AccountPresenter(this);
      initView();
}

private void initView() {
   Button refreshBtn = view.findViewById(R.id.refresh_btn);
   refreshBtn.setOnClickListener(this);
}

//Presenter에게 onClick 위임
@Override
public void onClick(View view) {
    presenter.clickButton();
}

@Override
public void showText() {
          Toast.makeText(this, getString(R.string.account_message), Toast.LENGTH_SHORT).show();
    } 
}</code></pre><p>Contract에서 정의한 View interface를 구현하는 AccountActivity를 생성합니다.
View는 화면에 UI를 그리는 기능만 수행하고 사용자 행동으로 인한 액션은 Contract를 통해 Presenter로 넘깁니다.
그러기 위해 멤버 변수로 Presenter 객체를 가집니다.</p>
<h3 id="presenter-정의하기">Presenter 정의하기</h3>
<p>Contract에서 정의한 Presenter interface를 구현합니다.
Presenter는 사용자 액션을 View로 부터 넘겨받아 Model Data 변경을 요청하거나, UI 업데이트 로직을 수행하는 class입니다. 그러기 위해 멤버 변수로 View 객체와 Model 객체를 가집니다.</p>
<pre><code>    class AccountPresenter implements AccountContract.Presenter {

    private AccountContact.View accountView;
    private AccountModel model;

    AccountPresenter(AccountContract.View accountView, AccountModel model) {
       this.accountView = accountView;
       this.model = model;
    }

     @Override
      public void clickButton() {
          model.update();
          accountView.showText();
          }
      }</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[안드로이드] LiveData]]></title>
            <link>https://velog.io/@true_je0n/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-LiveData</link>
            <guid>https://velog.io/@true_je0n/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-LiveData</guid>
            <pubDate>Mon, 20 Mar 2023 07:04:16 GMT</pubDate>
            <description><![CDATA[<p>Databinding과 함께 쓰면 찰떡궁합의 조합을 맛볼 수 있는, Android Jetpack 라이브러리의 기능 중 하나이다.</p>
<h3 id="live-data란">Live Data란?</h3>
<ul>
<li>Data의 변경을 관찰할 수 있는 Data Holder 클래스</li>
<li>일반적인 Observable과는 달리 안드로이드의 생명주기(Life Cycle)를 알고 있다.
<em>==&gt; 그래서 웬만하면 Observable은 잘 쓰지 않는거였군...!!!!!</em></li>
<li>그래서 Live Data는 활성상태(active)일때만 데이터를 업데이트(Update)한다.
활성상태란 STARTED 또는 RESUMED를 의미한다.</li>
<li>LiveData 객체는 Observer와 함께 사용된다. LiveData가 가지고 있는 데이터에 어떠한 변화가 일어날 경우, LiveData는 등록된 Observer 객체에 변화를 알려주고, Observer의 onChanged() 메소드가 실행된다.</li>
</ul>
<h3 id="live-data가-어떻게-생명주기를-알지">Live Data가 어떻게 생명주기를 알지..?</h3>
<ul>
<li>바로 LifeCycleOwner라는 녀석이 안드로이드 생명주기(Android LifeCycle)를 알고있는 클래스라고 할 수 있다.</li>
<li>메서드가 오직 getLifeCycle() 밖에 없는 단일 메소드 인터페이스 클래스이며, Activity나 Fragment에서 이를 상속하고 있다.</li>
<li>한 마디로 LiveData의 Observer 메소드의 LifeCycleOwner를 Activity나 Fragment를 변수로써 사용한다면 각 화면 별 생명주기에 따라 LiveData는 자신의 임무를 수행합니다,</li>
</ul>
<h3 id="장점">장점</h3>
<ul>
<li>Data와 UI간 동기화 : LiveData는 Observer 패턴을 따른다. 그에 따라서 LiveData는 안드로이드 생명주기에 데이터 변경이 일어날 때마다 Observer 객체에 알려준다. 그리고 이 Observer 객체를 사용하면 데이터의 변화가 일어나는 곳마다 매번 UI를 업데이트 하는 코드를 작성할 필요가 없이 통합적이고 확실하게 데이터의 상태와 UI를 일치시킬 수 있다.</li>
<li>메모리 누수(Memory Leak)가 없다 : Observer 객체는 안드로이드 생명주기 객체와 결합되어 있기 때문에 컴포넌트가 Destroy 될 경우 메모리에서 스스로 해체한다.</li>
<li>Stop 상태의 액티비티와 Crash가 발생하지 않는다 : 액티비티가 Back Stack에 있는 것처럼 Observer의 생명주기가 inactive(비활성화) 일 경우, Observer는 Live Data의 어떠한 이벤트도 수신하지 않는다.</li>
<li>생명주기에 대한 어떠한 handling도 하지 않아도 된다 : LiveData가 안드로이드 생명주기에 따른 Observing을 자동으로 관리해주기 때문에 UI 컴포넌트는 그저 관련있는 데이터를 &quot;관찰&quot;하기만 하면 된다.</li>
<li>항상 최신 데이터를 유지한다 : 화면 구성이 변경되어도 데이터를 유지한다. 예를 들어서, 디바이스를 회전하여 세로에서 가로로 화면이 변경될 경우에도 LiveData는 회전하기 전의 최신상태를 즉시 받아온다.</li>
<li>자원(Resource)를 공유할 수 있다 : LiveData를 상속하여 자신만의 LiveData 클래스를 구현할 수 있고, 싱글톤 패턴을 이용하여 시스템 서비스를 둘러싸면(Wrap) 앱 어디에서나 자원을 공유할 수 있다.</li>
</ul>
<h3 id="live-data-사용-시-주의할-점">Live Data 사용 시 주의할 점</h3>
<ul>
<li>Generic을 사용해 관찰하고자 하는 데이터의 타입을 갖는 Live Data 인스턴스를 생성한다.
(보통 Live Data 객체는 안드로이드 아키텍처 패턴의 ViewModel 클래스 내에서 함께 사용된다.)</li>
<li>LiveData 클래스의 Observe() 메소드를 사용해 Observer 객체를 LiveData 객체에 &quot;결합&quot;한다. 이때 observer() 메소드는 LifecycleOwner 객체를 필요로 하며 보통은 Activity를 전달한다. LiveData에 저장된 데이터에 어떠한 변화가 일어난 경우 결합된 LifecyclerOwner에 의해서 상태가 active(활성)인 한 모든 데이터에 대해 Trigger가 발생한다.</li>
<li>Observer 객체를 생성한다 : 생성시 LiveData가 들고있는 데이터가 변화가 일어났을 때 수행해야 할 로직이 들어있는 onChanged() 메서드를 정의해야 한다. 보통은 액티비티나 프래그먼트같은 UI Controller 내에서 해당 메서드를 생성한다.</li>
<li>observeForever(Observer)를 통해 LifeCycleOwner 없이 Observer를 생성하여 등록할 순 있지만, 이 경우에는 Observer는 항상 active(활성) 상태이므로 데이터 변화를 전달받는다. 단, removeObserver(Observer) 메소드를 통해 Observer를 제거할 수 있다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[안드로이드] DiffUtil]]></title>
            <link>https://velog.io/@true_je0n/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-DiffUtil</link>
            <guid>https://velog.io/@true_je0n/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-DiffUtil</guid>
            <pubDate>Mon, 20 Mar 2023 06:57:36 GMT</pubDate>
            <description><![CDATA[<h3 id="1-개요">1. 개요</h3>
<p>DiffUtil은 안드로이드 어댑터에서 현재 데이터 리스트와 교체될 데이터 리스트를 비교하여 무엇이 바뀌었는지 알아내는 클래스입니다.</p>
<p>이를 통해 기존 데이터 리스트에서 아이템에 수정이 생겼을 때 전체 리스트를 갱신하는 게 아니라 바뀐 아이템에 대해서만 데이터를 바꿔주고, 이를 통해 빠르고 효율적으로 데이터 갱신을 할 수 있습니다.</p>
<h3 id="2-만드는-법">2. 만드는 법</h3>
<p>DiffUtil을 사용하기 위해선 DiffUtil.ItemCallback과 ListAdapter가 필요합니다.</p>
<p>이전에는 DiffUtil.Callback, AsyncListDiffer등이 필요했지만</p>
<p>이제는 ListAdapter라는 최강의 무기가 나왔기 때문에 위 두 개만 사용하면 됩니다.</p>
<h3 id="3-diffutilitemcallback">3. DiffUtil.ItemCallback</h3>
<p>non-null 인 데이터 리스트를 비교해 &#39;차이&#39;를 계산한 후 이를 콜백 해주는 클래스입니다.</p>
<p>여기서는 두 개의 추상 메서드를 구현해주어야 합니다.</p>
<h4 id="a-areitemsthesame">a. areItemsTheSame</h4>
<p>두 객체가 같은 아이템인지 비교합니다. 두 객체를 구별할 수 있는 값을 사용하여 비교합니다.</p>
<h4 id="b-arecontentsthesame">b. areContentsTheSame</h4>
<p>위 메서드가 구별하는 것이 객체 자체라면, 이 메서드는 두 아이템이 같은 콘텐츠(내용물)를 갖고 있는지 비교합니다.</p>
<p>만약 두 객체의 ID나 객체를 식별할 수 있는 값이 같은데 안의 데이터가 다르다면 이 메서드에서 false를 반환합니다.</p>
<p>이 메서드는 areItemsTheSame 메서드가 true를 반환할 때에만 호출됩니다.</p>
<p>만일 false를 반환했다면 이 메서드가 호출되지 ㅇ낳고 그냥 바로 데이터를 갱신하면 되기 때문입니다.</p>
<h3 id="4-listadapter">4. ListAdapter</h3>
<p>RecyclerView.Adapter를 상속받는 어댑터입니다.</p>
<p>특히 ListAdapter는 내부적으로 AsyncListDiffer를 갖고 있어 더 효율적으로 코드를 작성할 수 있습니다.</p>
<p>여기서 AsyncListDiffer란,
백그라운드 스레드에서 기존 데이터 리스트와 새로운 데이터 리스트의 &#39;차이&#39;를 비교하고 리스트를 갱신하는 역할을 하는 클래스입니다.</p>
<p>이전에는 이것도 개별적으로 생성해주어야 했지만, ListAdapter는 내부적으로 이를 갖고 있기에 이제는 생성해주지 않아도 됩니다.</p>
<p>이 클래스에는 다음과 같은 메서드가 있습니다.</p>
<h4 id="a-getcurrentlist">a. getCurrentList()</h4>
<p>어댑터가 갖고 잇는 리스트를 가져올 때 사용합니다.</p>
<h4 id="b-submitlist">b. submitList()</h4>
<p>리스트 항목을 변경하고 싶을 때 사용합니다. 여기에 데이터를 변경할 리스트를 넣으면 알아서 리스트를 갱신해줍니다.</p>
<h3 id="5-사용법">5. 사용법</h3>
<h4 id="1-데이터-클래스-생성">1) 데이터 클래스 생성</h4>
<p>Person 클래스라는 데이터 클래스를 생성해줍니다.</p>
<pre><code>data class Person (var name: String, var age: Int, var num: Int)</code></pre><h4 id="2-diffutilitemcallback-클래스-생성">2) DiffUtil.ItemCallback 클래스 생성</h4>
<pre><code>class PersonDiffItemCallback : DiffUtil.ItemCallback&lt;Person&gt;() {
    override fun areItemsTheSame(oldItem: Person, newItem: Person): Boolean {
    return oldItem == newItem
    }
    override fun areContentsTheSame(oldItem: Person, newItem: Person): Boolean {
    return oldItem == newItem
    }
}</code></pre><p>이때 두 메서드에서는 원래 다르게 비교를 해야 합니다. areItemsTheSame은 객체 자체를 비교하고,
areContentsTheSame은 객체의 내용물을 비교하기 때문입니다.
그러나 저는 현재 그 둘의 차이를 둘 필요가 없기 때문에 두지 않았습니다.</p>
<p>만일 데이터베이스를 다룬다면 areItemsTheSame() 메서드에는 ID값을 비교하고,
areContentsTheSame() 메서드에서는 데이터를 비교하면 되겠습니다.</p>
<h4 id="3-listadapter-클래스-생성">3) ListAdapter 클래스 생성</h4>
<pre><code>class PersonListAdapter : ListAdapter&lt;Person, PersonListAdapter.Holder&gt; (PersonDiffItemCallback()) {
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
        val binding = PersonItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
        return Holder(binding)
    }
    override fun onBindViewHolder(holder: Holder, position: Int) {
        holder.setPerson(currentList[position])
    }
    fun add() {
        val newPersons = mutableListOf&lt;Person&gt;()
        newPersons.addAll(currentList)
        newPersons.add(Person(&quot;Java&quot;, 20, PersonAdapter.index++)) submitList(newPersons)
    }
    fun delete() {
    val newPersons = mutableListOf&lt;Person&gt;()
    newPersons.addAll(currentList)
    val index = (Random.nextDouble() * currentList.size).toInt()
    newPersons.removeAt(index) submitList(newPersons)
    }
    inner class Holder(val binding: PersonItemBinding) : RecyclerView.ViewHolder(binding.root) {
    fun setPerson(item: Person) {
        binding.name.text = item.name binding.num.text = &quot;${item.num}번째&quot;
        }
    }
}</code></pre><p>먼저 일반적인 리싸이클러뷰의 어댑터처럼 ViewHolder 클래스 및 필요한 메서드를 오버라이드해서 구현해줍니다.</p>
<p>그 이외의 메서드는 제가 임의로 작성한 코드이며, 내용은 다음과 같습니다.</p>
<h4 id="aadd">a.add</h4>
<p>아이템을 추가하는 메서드입니다.</p>
<p>이때 add() 메서드에서는 새로운 리스트를 생성한 다음, 기존 리스트를 복사한 후 데이터를 추가한 것을 볼 수 있습니다.</p>
<p>그리고 submitList() 메서드를 호출하는데, 이렇게 하는 이유는 다음과 같습니다.</p>
<p>DiffUtil은 기존 데이터 리스트와 새로운 데이터 리스트의 &#39;차이&#39;를 분석해서 해당 부분만 갱신을 해줍니다.</p>
<p>따라서 두개의 리ㅏ스트가 필요하며, 리스트를 갱신할 때에도 새로운 리스트를 전달해야 합니다.</p>
<p>그렇기에 새로운 리스트를 만든 다음 submitList() 메서드에 파라미터로 새로운 리스트를 전달하는 것입니다.</p>
<h4 id="b-delete">b. delete()</h4>
<p>아이템을 제거하는 메서드입니다.</p>
<h4 id="c-deleterandom">c. deleteRandom()</h4>
<p>임의 인덱스에 위치한 아이템을 제거하는 메서드입니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[안드로이드] DataBinding]]></title>
            <link>https://velog.io/@true_je0n/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-DataBinding</link>
            <guid>https://velog.io/@true_je0n/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-DataBinding</guid>
            <pubDate>Mon, 20 Mar 2023 06:42:50 GMT</pubDate>
            <description><![CDATA[<p>xml에 데이터를 바인딩하여 불필요한 코드를 줄이는 방법으로, 보통 MVVM 패턴을 구현할 때 사용된다.</p>
<h2 id="사용방법">사용방법</h2>
<h3 id="1-buildgradle-파일에-databinding-요소를-추가">1. build.gradle 파일에 dataBinding 요소를 추가</h3>
<pre><code>dataBinding {
    enabled = true
}</code></pre><h3 id="2-xml-수정하기--레이아웃-파일의-루트태그에-layout로-추가">2. xml 수정하기 : 레이아웃 파일의 루트태그에 &#39;layout&#39;로 추가</h3>
<pre><code>&lt;layout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;&gt;

    &lt;data&gt;
        &lt;variable
            name=&quot;user&quot;
            type=&quot;com.example.databinding_sample.data.User&quot; /&gt;
    &lt;/data&gt;

&lt;LinearLayout
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;match_parent&quot;
    android:orientation=&quot;vertical&quot;&gt;

&lt;TextView
    android:id=&quot;@+id/firstname_textView&quot;
    android:layout_width=&quot;wrap_content&quot;
    android:layout_height=&quot;wrap_content&quot;
    android:text=&quot;@{user.firstName, default=defaults}&quot; /&gt;

&lt;TextView
    android:id=&quot;@+id/lastname_textView&quot;
    android:layout_width=&quot;wrap_content&quot;
    android:layout_height=&quot;wrap_content&quot;
    android:text=&quot;@{user.lastName, default=defaults}&quot; /&gt;

&lt;/layout&gt;</code></pre><p>data 내에 있는 user 변수는 이 레이아웃 내에서 사용할 수 있는 속성에 대한 설명이다.</p>
<p>레이아웃 내에 있는 식은 “@{}” 구문을 사용하여 특성 속성에 기록된다. 여기서 TextView의 텍스트는 사용자의 firstName 속성으로 설정된다.</p>
<h3 id="3-xml에-binding할-데이터-클래스-생성">3. xml에 binding할 데이터 클래스 생성</h3>
<pre><code>public class User {
    private final String firstName;
    private final String lastName;

    public User(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    public String getFirstName() {
        return firstName;
    }

    public String getLastName() {
        return lastName;
    }
}</code></pre><p>TextView의 android:text 특성에 사용되는 식 @{user.firstName}은 전자의 클래스에 있는 firstName 필드와 후자의 클래스에 있는 getFirstName() 메서드에 액세스한다.</p>
<p>또는 firstName() 메서드가 존재할 경우에는 이 메서드로 해석하기도 한다.</p>
<h3 id="4-데이터-바인딩-하기">4. 데이터 바인딩 하기</h3>
<pre><code>public class MainActivity extends AppCompatActivity{
    ActivityMainBinding binding;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        User user = new User(&quot;JungHoon&quot;, &quot;Park&quot;);
        binding.setUser(user);
}</code></pre><p>위의 레이아웃 파일은 main_activity.xml이고, 따라서 생성된 클래스는 MainActivityBinding이었습니다.</p>
<p>이 클래스는 레이아웃 속성(예: user 변수)에서 레이아웃의 View까지 모든 바인딩을 유지하고 바인딩 식에 대해 값을 할당하는 방법을 알고 있습니다. 바인딩을 가장 쉽게 생성하는 방법은 다음과 같이 확장하는 동안 바인딩을 생성하는 것입니다.</p>
<pre><code>MainActivityBinding binding = MainActivityBinding.inflate(getLayoutInflater());</code></pre><h3 id="5-참고---이벤트-처리하기">5. &lt;참고&gt; - 이벤트 처리하기</h3>
<p>버튼 클릭 시 아래의 함수를 실행시키고 싶다면</p>
<pre><code>public void OnButtonClick(View view){
    Toast.makeText(this, &quot;Button Click&quot;, Toast.LENGTH_SHORT).show();
}</code></pre><p>xml에서 함수를 onClick에 지정해준다.</p>
<pre><code>&lt;Button
    android:id=&quot;@+id/btnSample&quot;
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;wrap_content&quot;
    android:onClick=&quot;@{activity::onButtonClick}&quot;
    android:text=&quot;button&quot;/&gt;</code></pre><h3 id="6-xml에서-activity-변수-지정">6. xml에서 activity 변수 지정</h3>
<pre><code>&lt;data&gt;
    &lt;variable
        name=&quot;activity&quot;
        type=&quot;com.example.databinding_sample.MainActivity&quot; /&gt;
&lt;/data&gt;</code></pre><h3 id="7-mainactivity-에서-사용하기">7. MainActivity 에서 사용하기</h3>
<pre><code>DatabindingActivityBinding binding;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
    binding.setActivity(this);</code></pre><p>setActivity(this)는 우리가 위에서 지정한 activity라는 변수를 이 Class로 지정하겠다는 의미이다.</p>
<h3 id="8-recyclerview-adapter에서-사용하기">8. RecyclerView Adapter에서 사용하기</h3>
<pre><code>@Override
public void onBindViewHolder(@NonNull BindingViewHolder holder, int position) {
    holder.binding.setUser(mList.get(position));
}


public class BindingViewHolder extends RecyclerView.ViewHolder {
    public RecyclerItemLayoutBinding binding;

    public BindingViewHolder(@NonNull View itemView) {
        super(itemView);
        binding = DataBindingUtil.bind(itemView);
    }
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[안드로이드] Room]]></title>
            <link>https://velog.io/@true_je0n/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-Room</link>
            <guid>https://velog.io/@true_je0n/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-Room</guid>
            <pubDate>Mon, 20 Mar 2023 06:25:48 GMT</pubDate>
            <description><![CDATA[<h2 id="1-room">1. Room</h2>
<h3 id="1-1-room이란">1-1. Room이란..?</h3>
<p>Room은 스마트폰 내장 DB에 데이터를 저장하기 위해 사용하는 라이브러리이다.
과거에는 SQLite라는 데이터베이스 엔진을 사용해 데이터를 저장했으나, 현재는 사용하기 어려워 직접적으로 쓰지는 않는다.
Room은 SQLite의 문제점을 자동으로 처리할 수 있도록 도와주는 역할을 하는데,
완전히 새로운 개념이 아니라 SQLite를 활용해서 객체 매핑을 하는 역할을 한다.</p>
<p>이러한 이유들로 인해서 구글에서는 Room사용을 권장하고 있다.</p>
<h3 id="1-2-room의-구조">1-2. Room의 구조</h3>
<p><img src="https://velog.velcdn.com/images/true_je0n/post/57812286-813d-4cc6-bfe8-245323dbaa8f/image.png" alt="">
위 사진에서 Room Database, Data Access Objects, Entities 이렇게 3개가 Room의 구성요소이고,
Rest of The App은 앱의 나머지 부분을 뜻한다.</p>
<h3 id="1-3-tmi">1-3. TMI</h3>
<p>정말 정말 간단한 정보를 저장할 경우를 생각해보자.
예를 들어서 자동 로그인 여부를 저장하고 싶은데, 고작 이 true/false 값을 저장하려고 Room을 사용하는 것은
닭을 잡는데 소를 잡는 칼을 쓰는 격이다.
별 것도 아닌 것에 큰 노력을 들여야 한다는 것이다.
이럴 때는, Room이 아니라 SharedPreferences 라는 것을 쓰면 된다.</p>
<p>반대로, 대량의 데이터를 처리하게 될 경우는 Room보다 Realm을 사용하면 좋다.
속도도 빠르고 안정적이고 비동기 지원이 된다는 장점이 있으나, 앱 용량이 커진다는 단점이 있어서 상황에 맞게 사용하면 된다.</p>
<h2 id="2-사용법">2. 사용법</h2>
<h3 id="2-1-gradle">2-1. gradle</h3>
<p>gradle을 추가하는 가장 쉬운 방법은 액티비티에 Room이라고 쓰고, Alt+Enter해서 추가하는 방법이다.</p>
<pre><code>implementation &#39;androidx.room:room-runtime:2.2.6&#39;
annotationProcessor &#39;androidx.room:room-compiler:2.2.6&#39;</code></pre><p>그러면 이렇게 gradle에 2개의 문장이 추가된 것을 확인할 수 있다.
<em>(하지만, 코틀린 사용시 여기에서 몇 가지 설정을 더 해줘야 한다고 함. 하지만 난 아직 코틀린으로 작업하지 않으므로 Pass<del>~</del>)</em></p>
<h3 id="2-2-entity">2-2. Entity</h3>
<p>Entity의 한국어 뜻은 &#39;개체&#39;로, 관련이 있는 속성끼리 모여서 하나의 &#39;정보&#39;를 이룬 것을 말한다.
예를 들어서, 사람의 이름, 나이, 전화번호 라는 속성이 모여서 하나의 정보 단위를 이루면 그것을 &#39;Entity&#39;라고 한다.</p>
<p>(Entity(개체)와 Object(객체)는 비슷해보이지만 다른 의미를 가지고 있다.
객체는 개체를 포함한 더 큰 개념이다.
대상에 대한 정보 뿐만 아니라, 동작, 기능, 절차 등을 포함하는 것이 바로 객체이다.)</p>
<p>아무튼 그래서 Entity를 생성해야 한다.
(DB 테이블을 만든다고 생각하면 쉽다~~)</p>
<pre><code>@Entity
data class User (
    var name: String,
    var age: String,
    var phone: String
){
    @PrimaryKey(autoGenerate = true) var id: Int = 0
}</code></pre><p>data class에 @Entity 어노테이션을 붙여주고 저장하고 싶은 속성의 변수 이름과 타입을 정해준다.
primaryKey는 키 값이기 때문에 유니크(Unique) 해야 한다.
직접 지정해도 되지만, autoGenerate를 true로 주면 자동으로 값을 생성한다.</p>
<h3 id="2-3-dao">2-3. DAO</h3>
<p>Data Access Object의 줄임말이다.
데이터에 접근할 수 있는 메서드를 정의해놓은 인터페이스이다.</p>
<pre><code>@Dao
interface UserDao {
    @Insert
    fun insert(user: User)

    @Update
    fun update(user: User)

    @Delete
    fun delete(user: User)
}</code></pre><p>Dao는 이렇게 생성하면 된다.
(우선 class가 아니라 interface임에 유의하자.)
맨 위에 @Dao 어노테이션을 붙이고 그 안에 메서드를 정의하게 되는데,</p>
<ul>
<li>@Insert를 붙이면 테이블에 데이터 삽입,</li>
<li>@Update를 붙이면 테이블의 데이터 수정,</li>
<li>@Delete를 붙이면 테이블의 데이터 삭제
이다.</li>
</ul>
<p>그렇다면, 만약에 삽입, 수정, 삭제외에 다른 기능을 하는 메서드를 만들고 싶다면 어떻게 할까?
테이블에 있는 값을 전부 불러온다던지, 특정 이름을 가진 사람만 데이터 삭제를 할 수도 있지 않을까?</p>
<pre><code>@Dao
interface UserDao {
    @Query(&quot;SELECT * FROM User&quot;) // 테이블의 모든 값을 가져와라
    fun getAll(): List&lt;User&gt;

    @Query(&quot;DELETE FROM User WHERE name = :name&quot;) // &#39;name&#39;에 해당하는 유저를 삭제해라
    fun deleteUserByName(name: String)
}</code></pre><p>그런 경우는 @Query 어노테이션을 붙이고 그 안에 어떤 동작을 할지 sql문법으로 작성해주면 된다.</p>
<h3 id="2-4-room-database">2-4. Room Database</h3>
<pre><code>@Database(entities = [User::class], version = 1)
abstract class UserDatabase: RoomDatabase() {
    abstract fun userDao(): UserDao
}</code></pre><p>이번에는 데이터베이스를 생성하고 관리하는 데이터베이스 객체를 만들기 위해서 위와 같은 추상 클래스를 만들어줘야 한다.
우선 RoomDatabase 클래스를 상속하고, @Database 어노테이션으로 데이터베이스임을 표시한다.</p>
<p>어노테이션 괄호 안을 보면 entities가 있는데, 여기에 2-2번에서 만들어준 Entity를 집어넣으면 된다.
version은 앱을 업데이트하다가 entity의 구조를 변경해야 하는 일이 생겼을 때 이전 구조와 현재 구조를 구분해주는 역할을 한다.
만약에 구조가 바꼈는데 버전이 같다면 에러가 뜨며 디버깅이 되지 않는다.
처음 데이터베이스를 생성하는 상황이라면 그냥 1을 넣어주면 된다.</p>
<pre><code>@Database(entities = arrayOf(User::class, Student::class), version = 1)
abstract class UserDatabase: RoomDatabase() {
    abstract fun userDao(): UserDao
}</code></pre><p>만약에 하나의 데이터베이스가 여러 개의 entity를 가져야 한다면, arrayOf() 안에 콤마로 구분해서 entity를 넣어주면 된다.
(공식문서에서는 데이터베이스 객체를 인스턴스 할 때 싱글톤으로 구현하기를 권장하고 있다.
일단 여러 인스턴스에 액세스를 꼭 해야하는 일이 거의 없고, 객체 생성에 비용이 많이 들기 때문이다.)</p>
<pre><code>@Database(entities = [User::class], version = 1)
abstract class UserDatabase: RoomDatabase() {
    abstract fun userDao(): UserDao

    companion object {
        private var instance: UserDatabase? = null

        @Synchronized
        fun getInstance(context: Context): UserDatabase? {
            if (instance == null) {
                synchronized(UserDatabase::class){
                    instance = Room.databaseBuilder(
                        context.applicationContext,
                        UserDatabase::class.java,
                        &quot;user-database&quot;
                    ).build()
                }
            }
            return instance
        }
    }
}</code></pre><p>그래서 위와 같이 companion object로 객체를 선언해서 사용하면 된다.
싱글톤으로 구현하지 않을 거라면 저 코드 부분을 호출할 부분에서 사용하면 된다.</p>
<p>객체를 생성할 때 databaseBuilder라는 static 메서드를 사용하는데,
context와, database 클래스 그리고 데이터베이스를 저장할 때 사용할 데이터베이스의 이름을 정해서 넘겨주면 된다.
(다른 데이터베이스와 이름이 겹치면 꼬이니 주의하자!!)</p>
<h3 id="2-5-데이터베이스-사용">2-5. 데이터베이스 사용</h3>
<p>var newUser = User(&quot;김똥깨&quot;, &quot;20&quot;, &quot;010-1111-5555&quot;)</p>
<pre><code>// 싱글톤 패턴을 사용하지 않은 경우
val db = Room.databaseBuilder(
         applicationContext,
         AppDatabase::class.java,
         &quot;user-database&quot;
         ).build()
db.UserDao().insert(newUser)

// 싱글톤 패턴을 사용한 경우
val db = UserDatabase.getInstance(applicationContext)
db!!.userDao().insert(newUser)</code></pre><p>2-4에서 싱글턴 패턴 사용 유무에 따라 위와 같이 사용해주면 된다.
이렇게 하면 2-3에서 insert를 새로운 데이터를 삽입하는 메서드로 정의했었기 때문에 newUser가 데이터베이스에 추가된다.</p>
<p>여기서 끝이 났으면 좋겠지만 사실 끝이 아니다.</p>
<p>저대로 실행하면 &quot;Cannot access database on the main thread since it may potentially lock the UI for a long period of time&quot; 에러가 뜬다.</p>
<p>쉽게 말하자면 &quot;오래 걸리는 작업이니까 다른 애한테 시켜. 나 바쁨.&quot; 정도로 해석할 수 있다.</p>
<pre><code>var newUser = User(&quot;김똥깨&quot;, &quot;20&quot;, &quot;010-1111-5555&quot;)

// 싱글톤 패턴을 사용하지 않은 경우
val db = Room.databaseBuilder(
         applicationContext,
         AppDatabase::class.java,
         &quot;user-database&quot;
).allowMainThreadQueries() // 그냥 강제로 실행
 .build()
db.UserDao().insert(newUser)

// 싱글톤 패턴을 사용한 경우
val db = UserDatabase.getInstance(applicationContext)
CoroutineScope(Dispatchers.IO).launch { // 다른애 한테 일 시키기
     db!!.userDao().insert(newUser)
}</code></pre><p>이제 선택권이 2가지가 있다.</p>
<ol>
<li><p>allowMainThreadQueries()를 사용해 강제로 실행시킨다.
=&gt; 이 경우, 나중에 문제가 생길 수 있다. Room을 한번 사용해보는 학습단계에서는 써도 무방하다.</p>
</li>
<li><p>비동기 실행을 시킨다.
=&gt; 비동기 실행에는 여러 가지 방법이 있다. 대표적으로 많이 사용하는 비동기에는 &#39;코루틴&#39;이 있다.</p>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[안드로이드] border bottom 만들기]]></title>
            <link>https://velog.io/@true_je0n/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-border-bottom-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@true_je0n/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-border-bottom-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Mon, 20 Mar 2023 06:15:24 GMT</pubDate>
            <description><![CDATA[<p>안드로이드에서는 테두리를 지정해주는 &quot;border&quot; 속성이 없다... (ㅠㅠ)
그래서 밑이나 옆 구분선과 같은 테두리를 지정해줄 때 layer-list 를 이용해서 .xml을 만들어줘야 한다.</p>
<p>귀찮지만 어쩔 수 없지...</p>
<p>border.xml은 drawable 폴더 안에 만들어주면 된다.</p>
<p>border.xml</p>
<pre><code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;layer-list xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;&gt;
    &lt;item&gt;
        &lt;shape android:shape=&quot;rectangle&quot;&gt;
            &lt;solid android:color=&quot;#d7d7d7&quot;/&gt;
        &lt;/shape&gt;
    &lt;/item&gt;
    &lt;item android:top=&quot;1dp&quot; android:bottom=&quot;1dp&quot;&gt;
        &lt;shape android:shape=&quot;rectangle&quot;&gt;
            &lt;solid android:color=&quot;#ffffff&quot;/&gt;
        &lt;/shape&gt;
    &lt;/item&gt;
&lt;/layer-list&gt;</code></pre><p>이렇게 만든 border를 사용하려면
background=&quot;@drawable/border&quot; 이런식으로 지정해서 사용하면 된다..!</p>
<p>끝!!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[안드로이드] 싱글톤(Singleton) 패턴이란?]]></title>
            <link>https://velog.io/@true_je0n/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-%EC%8B%B1%EA%B8%80%ED%86%A4Singleton-%ED%8C%A8%ED%84%B4%EC%9D%B4%EB%9E%80</link>
            <guid>https://velog.io/@true_je0n/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-%EC%8B%B1%EA%B8%80%ED%86%A4Singleton-%ED%8C%A8%ED%84%B4%EC%9D%B4%EB%9E%80</guid>
            <pubDate>Mon, 20 Mar 2023 06:07:51 GMT</pubDate>
            <description><![CDATA[<p>싱글톤(Singleton) 패턴이란, 객체의 인스턴스가 오직 1개만 생성되는 패턴을 의미한다.</p>
<p>싱글톤 패턴을 사용하는 이유는, _<strong>메모리 낭비를 방지</strong>_할 수 있기 때문인데,
최초 한 번의 new 연산자를 통해서 고정된 메모리 영역을 사용하기 때문에 추후 해당 객체에 접근할 때 메모리 낭비를 방지할 수 있다.
뿐만 아니라 이미 생성된 인스턴스를 활용하니 속도 측면에서도 이점이 있다고 볼 수 있다.</p>
<p>또 다른 이유는 클래스 간의 <em><strong>데이터 공유가 쉽기 때문</strong>_인데,
싱글톤 인스턴스가 전역으로 사용되는 인스턴스이기 때문에 <strong>다른 클래스의 인스턴스들이 접근하여 사용할 수 있다.</strong>
하지만 _여러 클래스의 인스턴스에서 싱글톤 인스턴스의 데이터에 동시에 접근하게 되면 동시성 문제가 발생할 수 있으니</em> 이점을 유의해서 설계하는 것이 좋다.</p>
<p>이 외에도 도메인 관점에서 인스턴스가 한 개만 존재하는 것을 보증하고 싶은 경우, 싱글톤 패턴을 사용하기도 한다.</p>
<h4 id="싱글톤-패턴의-문제점">싱글톤 패턴의 문제점</h4>
<ol>
<li>싱글톤 패턴을 구현하는 코드 자체가 많이 필요하다.</li>
<li>테스트 하기 어렵다.</li>
<li>의존 관계상 클라이언트가 구체 클래스에 의존하게 된다.</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[코틀린] 코틀린에서 예외를 다루는 ]]></title>
            <link>https://velog.io/@true_je0n/%EC%BD%94%ED%8B%80%EB%A6%B0-%EC%BD%94%ED%8B%80%EB%A6%B0%EC%97%90%EC%84%9C-%EC%98%88%EC%99%B8%EB%A5%BC-%EB%8B%A4%EB%A3%A8%EB%8A%94</link>
            <guid>https://velog.io/@true_je0n/%EC%BD%94%ED%8B%80%EB%A6%B0-%EC%BD%94%ED%8B%80%EB%A6%B0%EC%97%90%EC%84%9C-%EC%98%88%EC%99%B8%EB%A5%BC-%EB%8B%A4%EB%A3%A8%EB%8A%94</guid>
            <pubDate>Mon, 20 Mar 2023 06:04:15 GMT</pubDate>
            <description><![CDATA[<h2 id="1-try-catch-finally-구문">1. try catch finally 구문</h2>
<h3 id="주어진-문자열을-정수로-변경하는-예제">주어진 문자열을 정수로 변경하는 예제</h3>
<p>&lt;자바코드&gt;</p>
<pre><code>private int parseIntOrThrow(@NotNull String str) {
  try {
    return Integer.parseInt(str);
  } catch {
    throw new IllegalArgumentException(String.format(&quot;주어진 %s는 숫자가 아닙니다.&quot;, str));
  }
}
</code></pre><p>&lt;코틀린코드&gt;</p>
<pre><code>fun parseIntOrThrow(str: String): Int {
  try {
    return str.toInt()
  } catch (e: NumberFormatException) {
    throw IllegalArgumentException(&quot;주어진 ${str}은 숫자가 아닙니다.&quot;)
  }
}</code></pre><h3 id="자바와-코틀린의-예외-처리의-차이점">자바와 코틀린의 예외 처리의 차이점</h3>
<p>a. 기본타입간의 형변환은 toType()을 사용.
b. 타입이 뒤에 위치함.
c. new를 사용하지 않음.
d. 포맷팅이 간결함.
e. 그 외의 try~catch 구문은 같음.
b. 주어진 문자열을 정수로 변경하는 예제, 실패하면 null 반환</p>
<p>&lt;자바코드&gt;</p>
<pre><code>private Integer parseIntOrThrowV2(String str) {
  try {
    return Integer.parseInt(str);
  } catch (NumberFormatException e) {
    return null;
  }
}</code></pre><p>&lt;코틀린코드&gt;</p>
<pre><code>fun parseIntOrThrowV2(str: String): Int? {
  return try {
    str.toInt()
  } catch (e: NumberFormatException) {
    null
  }
}</code></pre><p>=&gt; 코틀린에서는 try<del>catch 문도 하나의 Expression으로 간주된다. 그래서 try</del>catch문에서 나온 최종 결과물은 한번만 return 해도 코드가 동작한다.
=&gt; try ~ catch ~ finally 문도 자바와 코틀린의 문법이 동일하다.</p>
<h2 id="2-checked-exception과-unchecked-exception">2. Checked Exception과 Unchecked Exception</h2>
<h3 id="a-프로젝트-내-파일의-내용물을-읽어오는-예제">a. 프로젝트 내 파일의 내용물을 읽어오는 예제</h3>
<p>&lt;자바코드&gt;</p>
<pre><code>public void readFile() throws IOException { //close, read line, FileReader 등의 구문을 생성할 경우에는 반드시 체크 예외가 날 수 있다는 의미로 [throws IOException] 을 표시해줘야 한다.
  File currentFile = new File(&quot;.&quot;);
  File file = new File(currentFile.getAbsolutePath() + &quot;/a.txt&quot;);
  BufferedReader reader = new BufferdReader(new FileReader(file));
  System.out.println(reader.readLine());
  reader.close();
}</code></pre><p>&lt;코틀린코드&gt;</p>
<pre><code>fun readFile() {
  val currentFile = File(&quot;.&quot;)
  val file = File(currentFile.absolutePath + &quot;/a.txt&quot;)
  val reader = BufferedReader(FileReader(file))
  println(reader.readLine())
  reader.close()
}</code></pre><p>&lt;자바코드와 코틀린코드의 차이점&gt;
a. 자바에서는 close(), readLine(), FileReader() 등의 구문들 생성할 경우에는 반드시 체크 예외가 날 수 있다는 의미로 throws IOException을 표시해줘야 한다.
b. 하지만, 코틀린에서는 Checked Exception 과 Unchecked Exception을 구분하지 않는다. 모두 Unchecked Exception이다.</p>
<h2 id="3-try-with-resources">3. try with resources</h2>
<h3 id="a-프로젝트-내-파일의-내용물을-읽어오는-예제-1">a. 프로젝트 내 파일의 내용물을 읽어오는 예제</h3>
<p>&lt;자바코드&gt;</p>
<pre><code>public void readFile(String path) throws IOException {
  try (BufferedReader reader = new BufferedReader(new FileReader(path))) {
    System.out.println(reader.readLine());
  }
}</code></pre><p>&lt;코틀린 코드(코틀린에는 try with resouces 구문이 없다.)&gt;</p>
<pre><code>fun readFile(path: String) {
  BufferedReader(FileReader(path)).use { reader -&gt;
    println(reader.readeLine())
  }
}</code></pre><p>&lt;코틀린에서는 try with resouces 구문이 없어지고, 대신 use라는 inline 확장 함수를 사용해야 한다.&gt;</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[코틀린] 코틀린에서 반복문을 다루는 방법]]></title>
            <link>https://velog.io/@true_je0n/%EC%BD%94%ED%8B%80%EB%A6%B0-%EC%BD%94%ED%8B%80%EB%A6%B0%EC%97%90%EC%84%9C-%EB%B0%98%EB%B3%B5%EB%AC%B8%EC%9D%84-%EB%8B%A4%EB%A3%A8%EB%8A%94-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@true_je0n/%EC%BD%94%ED%8B%80%EB%A6%B0-%EC%BD%94%ED%8B%80%EB%A6%B0%EC%97%90%EC%84%9C-%EB%B0%98%EB%B3%B5%EB%AC%B8%EC%9D%84-%EB%8B%A4%EB%A3%A8%EB%8A%94-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Mon, 20 Mar 2023 05:56:34 GMT</pubDate>
            <description><![CDATA[<h2 id="1-for-each-문">1. for-each 문</h2>
<h3 id="숫자가-들어-있는-리스트를-하나씩-출력하는-예제">숫자가 들어 있는 리스트를 하나씩 출력하는 예제</h3>
<p>&lt;자바 코드&gt;</p>
<pre><code>List&lt;Long&gt; numbers = Arrays.asList(1L, 2L, 3L);
for (long number : numbers) {
  System.out.println(number);
}</code></pre><p>&lt;코틀린 코드&gt;</p>
<pre><code>fun main() {
  val numbers = listOf(1L, 2L, 3L) //컬렉션을 만드는 법이 다름.
  for(number in numbers) { // : 대신 in을 사용함. Java와 동일하게 Iterable이 구현된 타입이라면 모두 들어갈 수 있다.
    println(number)
  }
}</code></pre><h2 id="2-전통적인-for문">2. 전통적인 for문</h2>
<h3 id="1부터-3까지-출력하는-예제">1부터 3까지 출력하는 예제</h3>
<p>&lt;자바 코드&gt;</p>
<pre><code>for (int i = 1; i &lt;= 3; i++) {
  System.out.println(i);
}</code></pre><p>&lt;코틀린 코드&gt;</p>
<pre><code>for (i in 1..3) { //1..3은 1부터 3까지라는 의미이다.
  println(i)
}</code></pre><h3 id="3부터-1까지-내려가는-경우-출력하는-예제">3부터 1까지 (내려가는 경우) 출력하는 예제</h3>
<p>&lt;자바 코드&gt;</p>
<pre><code>for (int i = 3; i &gt;= 1; i--) {
  System.out.println(i);
}</code></pre><p>&lt;코틀린 코드&gt;</p>
<pre><code>for (i in 3 downTo 1) {
  println(i)
}</code></pre><h3 id="2칸씩-올라가는-경우는">2칸씩 올라가는 경우는?!</h3>
<p>&lt;자바 코드&gt;</p>
<pre><code>for (int i = 1; i &lt;= 5; i+=2) {
  System.out.println(i);
}</code></pre><p>&lt;코틀린 코드&gt;</p>
<pre><code>for (i in 1..5 step 2) {
  System.out.println(i)
}</code></pre><h2 id="3-progression과-range">3. Progression과 Range</h2>
<h3 id="연산자--범위를-만들어-내는-연산자">..연산자 : 범위를 만들어 내는 연산자</h3>
<ul>
<li><p>1..3 : 1부터 3의 범위</p>
</li>
<li><p>IntRange → IngProgression</p>
</li>
<li><p>Progression = 등차수열 :</p>
</li>
<li><p>시작 값</p>
</li>
<li><p>끝 값</p>
</li>
<li><p>공차 (몇 칸씩 뛸지)
=&gt; 사실은 등차수열을 만들어주고 있던 것!!</p>
</li>
<li><p>3 downTo 1 : 시작값3, 끝값1, 공차가 -1일 등차수열</p>
</li>
<li><p>1..5 step 2 : 시작값1, 끝값5, 공차가 2인 등차수열</p>
</li>
<li><p>downTo, step 도 함수이다! (중위 호출 함수)</p>
</li>
<li><p>변수.함수이름(argument) 대신 변수 함수이름 argument</p>
</li>
<li><p>1..5 step 2 : 1부터 5까지 공차가 1인 등차수열 생성, 1~5, 공차 1 등차수열 step2</p>
</li>
</ul>
<h2 id="4-while문">4. While문</h2>
<h3 id="1부터-3을-출력하는-예제-자바">1부터 3을 출력하는 예제 (자바)</h3>
<pre><code>int i = 1;
while (i &lt;= 3) {
  System.out.println(i);
  i++;
}</code></pre><h3 id="1부터-3을-출력하는-예제-코틀린">1부터 3을 출력하는 예제 (코틀린)</h3>
<pre><code>var i = 1
while (i &lt;= 3) {
  println(i)
  i++
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[코틀린] 코틀린에서 제어문을 다루는 법]]></title>
            <link>https://velog.io/@true_je0n/%EC%BD%94%ED%8B%80%EB%A6%B0-%EC%BD%94%ED%8B%80%EB%A6%B0%EC%97%90%EC%84%9C-%EC%A0%9C%EC%96%B4%EB%AC%B8%EC%9D%84-%EB%8B%A4%EB%A3%A8%EB%8A%94-%EB%B2%95</link>
            <guid>https://velog.io/@true_je0n/%EC%BD%94%ED%8B%80%EB%A6%B0-%EC%BD%94%ED%8B%80%EB%A6%B0%EC%97%90%EC%84%9C-%EC%A0%9C%EC%96%B4%EB%AC%B8%EC%9D%84-%EB%8B%A4%EB%A3%A8%EB%8A%94-%EB%B2%95</guid>
            <pubDate>Mon, 20 Mar 2023 05:38:43 GMT</pubDate>
            <description><![CDATA[<h2 id="if문">if문</h2>
<h3 id="a-자바-코드">a. 자바 코드</h3>
<pre><code>private void validateScoreIsNotNegative(int score) {
  if(score &lt; 0) {
    throw new IllegalArgumentException(String.format(&quot;%s는 0보다 작을 수 없습니다.&quot;, score));
  }
}</code></pre><h3 id="b-코틀린-코드">b. 코틀린 코드</h3>
<pre><code>fun validateScoreIsNotNegative(score: Int) {
  if(score &lt; 0) {
    throw IllegalArgumentException(&quot;${score}는 0보다 작을 수 없습니다.&quot;) //new를 사용하지 않고 예외를 throw 하는 것 외엔 자바와 다를 게 없음.
  }
}</code></pre><h3 id="c-문법">c. 문법</h3>
<pre><code>if (조건) {

}</code></pre><h3 id="자바-코드-2">자바 코드 2</h3>
<pre><code>private String getPassOrFail(int score) {
  if(score &gt;= 50) {
    return &quot;P&quot;;
  } else {
    return &quot;F&quot;;
  }
}</code></pre><h3 id="코틀린-코드-2">코틀린 코드 2</h3>
<pre><code>fun getPassOrFail(score: Int): String{
  if (score &gt;= 50) {
    return &quot;P&quot;
  } else {
    return &quot;F&quot;
  }
}</code></pre><h2 id="2-expression--statement">2. Expression &amp; Statement</h2>
<p>Java에서 if-else는 Statement 이지만, Kotlin에서는 Expression 입니다.</p>
<ul>
<li>Statement : 프로그램의 문장, 하나의 값으로 도출되지 않는다.</li>
<li>Expression : 하나의 값으로 도출되는 문장.</li>
</ul>
<pre><code>int score = 30 + 40;</code></pre><p>30 + 40 은 70이라는 하나의 결과가 나온다. Expression 이면서 Statement라고 할 수 있다.</p>
<p>자바에서는 if문을 하나의 값으로 취급하지 않는다.
따라서 다음과 같은 코드에서는 에러가 날 수 밖에 없다.</p>
<pre><code>String grade = if (score &gt;= 50) { // 여기서부터 문법 오류가 남.
  &quot;P&quot;;
} else {
  &quot;F&quot;;
}</code></pre><p>따라서, 이 문장은 &#39;=&#39;을 통해서 바로 값을 대입할 수 없으므로 Statement라고 할 수 있다.</p>
<pre><code>String grade = score &gt;= 50 ? &quot;P&quot; : &quot;F&quot;</code></pre><p>=&gt; 3항 연산자는 하나의 값으로 취급하므로 에러가 없다. Expression 이면서 Statement 이다.</p>
<p>코틀린에서는 if~else 구문을 다음과 같이 사용 가능하다.</p>
<pre><code>fun getPassOrFail(socre: Int): String {
  if (score &gt;= 50) {
    return &quot;P&quot;
  } else {
    return &quot;F&quot;
  }
}</code></pre><p>또는</p>
<pre><code>fun getPassOrFail(score: Int): String {
  return if (score &gt;= 50) {
    &quot;P&quot;
  } else {
    &quot;F&quot;
  }
}</code></pre><p>=&gt; 두번째 경우처럼 사용 가능한 이유는 코틀린은 Expression 이기 때문.
자바의 3항 연산자를 통해서 바로 값을 계산해서 return 하는 것 처럼, 코틀린에서는 if<del>else 를 바로 계산해서 return 할 수 있다.
그리고 코틀린은 if</del>else를 expression으로 사용할 수 있기 때문에 3항 연산자가 없다.</p>
<p>if~ else if ~ else 문법도 동일하다.</p>
<p>Java 코드</p>
<pre><code>private String getGrade (int score) {
  if(score &gt;= 90) {
    return &quot;A&quot;;
  } else if (score &gt;= 80) {
    return &quot;B&quot;;
  } else if (score &gt;= 70) {
    return &quot;C&quot;;
  } else {
    return &quot;D&quot;;
  }
}
</code></pre><p>Kotlin 코드</p>
<pre><code>fun getGrade(score: Int): String {
  return if(score &gt;= 90) {
    &quot;A&quot;
  } else if (score &gt;= 80) {
    &quot;B&quot;
  } else if (score &gt;= 70) {
    &quot;C&quot;
  } else {
    &quot;D&quot;
  }
}</code></pre><h3 id="간단한-tip">간단한 TIP</h3>
<p>어떠한 값이 특정 범위에 포함되어 있는지, 포함되어 있지 않은지</p>
<pre><code>if (0 &lt;= score &amp;&amp; score &lt;= 100) {...}

=

if (score in 0..100) {...}</code></pre><p>예제</p>
<pre><code>fun validateScoreIsNotNegative (score: Int) {
  if(score !in 0..100) {
    throw IllegalArgumentException(&quot;score의 범위는 0부터 100입니다&quot;)
  }
}</code></pre><pre><code>fun validateScore(score: Int) {
  if(score in 0..100) {
    var score2 = &quot;dddddddd&quot;
  }
}</code></pre><h2 id="3-switch와-when-1">3. switch와 when (1)</h2>
<p>Java 코드</p>
<pre><code>private String getGradeWithSwitch (int score) {
  switch (score / 10) {
    case 9:
      return &quot;A&quot;;
    case 8:
      return &quot;B&quot;;
    case 7:
      return &quot;C&quot;;
    default:
      return &quot;D&quot;;
  }
}</code></pre><p>Kotlin 코드 ver.1</p>
<pre><code>fun getGradeWithSwitch(score: Int): String {
  return when (score / 10) {
    9 -&gt; &quot;A&quot;
    8 -&gt; &quot;B&quot;
    7 -&gt; &quot;C&quot;
    else -&gt; &quot;D&quot;
  }
}</code></pre><p>Kotlin 코드 ver.2</p>
<pre><code>fun getGradeWithSwitch(score: Int): String {
  return when (score) {
    in 90..99 -&gt; &quot;A&quot;
    in 80..89 -&gt; &quot;B&quot;
    in 70..79 -&gt; &quot;C&quot;
    else -&gt; &quot;D&quot;
  }
}</code></pre><h2 id="3-switch-와-when-2">3. switch 와 when (2)</h2>
<p>when (값) {</p>
<p>조건부 → 어떠한 구문</p>
<p>조건부 → 어떠한 구문</p>
<p>else → 어떠한 구문</p>
<p>}</p>
<ul>
<li>조건부 : 어떠한 expression이라도 들어갈 수 있다. (ex. is Type), 여러 개의 조건을 동시에 검사할 수 있다 (, 로 구분)</li>
</ul>
<p>Java 코드</p>
<pre><code>private void judgeNumber(int number) {
  if(number == 1 || number == 0 || number == -1) {
    System.out.println(&quot;어디서 많이 본 숫자입니다&quot;);
  } else {
    System.out.println(&quot;1, 0, -1이 아닙니다&quot;);
  }
}</code></pre><p>Kotlin 코드</p>
<pre><code>fun judgeNumber(number: Int) {
  when (number) {
    1, 0, -1 -&gt; println(&quot;어디서 많이 본 숫자입니다&quot;)
    else -&gt; println(&quot;1, 0, -1이 아닙니다&quot;)
  }
}</code></pre><p>Java 코드 2</p>
<pre><code>private void judgeNumber2(int number) {
  if (number == 0) {
    System.out.println(&quot;주어진 숫자는 0입니다&quot;);
    return;
  }

  if (number % 2 == 0) {
    System.out.println(&quot;주어진 숫자는 짝수입니다&quot;);
    return;
  }

  System.out.println(&quot;주어지는 숫자는 홀수입니다&quot;);
}</code></pre><p>Kotlin 코드 2</p>
<pre><code>fun judgeNumber2(number: Int) {
  when {
    number == 0 -&gt; println(&quot;주어진 숫자는 0입니다&quot;)
    number % 2 == 0 -&gt; println(&quot;주어진 숫자는 짝수입니다&quot;)
    else -&gt; println(&quot;주어지눈 숫자는 홀수입니다&quot;)
  }
}</code></pre><p>when은 Enum Class 혹은 Sealed Class와 함께 사용할 경우, 더욱더 진가를 발휘한다.</p>
<h3 id="-코틀린에서-조건문을-다루는-방법-">※ 코틀린에서 조건문을 다루는 방법 ※</h3>
<ul>
<li>if / if - else / if - else if - else 모두 Java와 문법이 동일하다.</li>
<li>단 Kotlin에서는 Expression 으로 취급된다.</li>
<li>때문에 Kotlin 에서는 삼항 연산자가 없다.</li>
<li>Java의 switch는 Kotlin에서 when으로 대체되었고, when은 더 강력한 기능을 갖는다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[코틀린] 코틀린에서 연산자를 다루는 방법]]></title>
            <link>https://velog.io/@true_je0n/%EC%BD%94%ED%8B%80%EB%A6%B0-%EC%BD%94%ED%8B%80%EB%A6%B0%EC%97%90%EC%84%9C-%EC%97%B0%EC%82%B0%EC%9E%90%EB%A5%BC-%EB%8B%A4%EB%A3%A8%EB%8A%94-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@true_je0n/%EC%BD%94%ED%8B%80%EB%A6%B0-%EC%BD%94%ED%8B%80%EB%A6%B0%EC%97%90%EC%84%9C-%EC%97%B0%EC%82%B0%EC%9E%90%EB%A5%BC-%EB%8B%A4%EB%A3%A8%EB%8A%94-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Mon, 20 Mar 2023 05:30:00 GMT</pubDate>
            <description><![CDATA[<h2 id="1-단항-연산자--산술-연산자">1. 단항 연산자 / 산술 연산자</h2>
<ul>
<li>단항 연산자 : ++, --</li>
<li>산술 연산자 : +, -, *, /, %</li>
<li>산술대입 연산자 : +=, -=, *=, /=, %=</li>
<li>비교 연산자 : &gt;, &lt;, &gt;=, &lt;=</li>
</ul>
<p>단, Java와 다르게 객체를 비교할 때 비교 연산자를 사용하면 자동으로 compareTo를 호출해준다.</p>
<pre><code>public class JavaMoney implements Comparable&lt;JavaMoney&gt; {
  private final long amount;

  public JavaMoney(long amount) {
    this.amount = amount;
  }

  @Override
  public int compareTo(@NotNull JavaMoney o) {
    return Long.compare(this.amount, o.amount);
  }
}

fun main() {
  val money1 = JavaMoney(2_000L)
  val money2 = JavaMoney(1_000L)

  if(money1 &gt; money2) {
    println(&quot;Money1이 Money2보다 금액이 큽니다&quot;)
  }
}</code></pre><h2 id="2-비교-연산자와-동등성-동일성">2. 비교 연산자와 동등성, 동일성</h2>
<ul>
<li>동등성(Equality) : 두 객체의 값이 같은가?! (자바의 equals  →  코틀린의 ==)  → 자바스크립트 역시 ==</li>
<li>동일성(Identity) : 완전히 동일한 객체인가?! 즉 주소가 같은가?! (자바의 ==  →  코틀린의 ===)  → 자바스크립트 역시 ===</li>
</ul>
<p>&lt;자바 코드&gt;</p>
<pre><code>public class Lec04Main {
  public static void main(String[] args) {
    JavaMoney money1 = new JavaMoney(1_000L);
    JavaMoney money2 = money1;
    JavaMoney money3 = new JavaMoney(1_000L);

    System.out.println(money1 == money2); // true
    System.out.println(money1 == money3); // false
    System.out.println(money1.equals(money3)); // false
  }
}</code></pre><p>&lt;코틀린 코드&gt;</p>
<pre><code>fun main() {
  val money1 = JavaMoney(1_000L)
  val money2 = money1
  val money3 = JavaMoney(1_000L)

  println(money1 === money2) // true
  println(money1 === money3) // false
  println(money1 == money3) // true
}</code></pre><h2 id="3-논리-연산자와-코틀린에-있는-특이한-연산자">3. 논리 연산자와 코틀린에 있는 특이한 연산자</h2>
<ul>
<li>&amp;&amp;</li>
<li>||</li>
<li>!</li>
</ul>
<h3 id="논리연산자">논리연산자</h3>
<p>=&gt; Java와 완전히 동일합니다.
=&gt; Java 처럼 Lazy 연산을 수행합니다.</p>
<h3 id="lazy-연산이란--코틀린-코드">Lazy 연산이란? : 코틀린 코드</h3>
<pre><code>fun main() {
  if (fun1() || fun2()) {
    println(&quot;본문&quot;) // ||(또는) 이므로 값은 true이므로 실행됨.
  }

  if (fun1() &amp;&amp; fun2()) {
    println(&quot;본문&quot;) // &amp;&amp;(그리고) 이므로 값은 false 이므로 실행되지 않음.
  }
}

fun fun1(): Boolean {
  println(&quot;fun 1&quot;)
  return true
}

fun fun2(): Boolean {
  println(&quot;fun 2&quot;)
  return false
}
</code></pre><h3 id="in--in">in / !in</h3>
<p>컬렉션이나 범위에 포함되어 있다, 포함되어 있지 않다
println(1 in numbers)</p>
<h3 id="ab">a..b</h3>
<p>a 부터 b 까지의 범위 객체를 생성한다
반복문에서 자주 쓰임.</p>
<h3 id="ai">a[i]</h3>
<p>a에서 특정 Index i 로 값을 가져온다</p>
<pre><code>val str = &quot;ABC&quot;
println(str[2]) //C

a[i] = b</code></pre><p>a의 특정 index i에 b를 넣는다</p>
<h2 id="4-연산자-오버로딩">4. 연산자 오버로딩</h2>
<p>Kotlin에서는 객체마다 연산자를 직접 정의할 수 있다.</p>
<pre><code>val money1 = Money(1_000L)
val money2 = Money(2_000L)
println(money1 + money2) // Money(amount=3000)</code></pre><p>&lt;자바코드&gt;
JavaMoney.java</p>
<pre><code>public class JavaMoney implements Comparable&lt;JavaMoney&gt; {
  private final long amount;
  public JavaMoney(long amount) {
    this.amount = amount;
  }

  public JavaMoney plus(JavaMoney other) {
    return new JavaMoney(this.amount + other.amount);
  }

  ...

  @Override
  pulic String toString() {
    return &quot;JavaMoney{&quot;  +
          &quot;amount=&quot; + amount +
          &#39;}&#39;;
  }
}

Lec04Main.java

public class Lec04Main {
  public static void main(String[] args) {
    JavaMoney money1 = new JavaMoney(1_000L);
    JavaMoney money2 = new JavaMoney(2_000L);
    System.out.println(money1.plus(money2));
  }
}</code></pre><p>&lt;코틀린 코드&gt;
Money.kt</p>
<pre><code>data class Money(val amount: Long) {
  operator fun plus(other: Money): Money {
    return Money(this.amount + other.amount)
  }
}</code></pre><p>Lec04Main.kt</p>
<pre><code>fun main() {
  val money1 = Money(1_000L) // 코틀린 class의 Money
  val money2 = Money(2_000L)

  println(money1 + money2) //  toString이 구현되어 있어서 바로 덧셈 후 println 가능함. 물론 자바처럼 println(money1.plus(money2)) 처럼 구현도 가능함.
}</code></pre><p>=&gt; 진짜 진짜 진~~짜 JavaScript와 동일함…………… 찐이다…!!!!</p>
<h3 id="-코틀린에서-연산자를-다루는-방법정리-">※ 코틀린에서 연산자를 다루는 방법(정리) ※</h3>
<ul>
<li>단항연산자, 산술연산자, 산술대입연산자 Java와 똑같다</li>
<li>비교연산자 사용법도 Java와 똑같다</li>
<li>단, 객체끼리도 자동 호출되는 compareTo를 이용해 비교연산자를 사용할 수 있다.</li>
<li>in, !in / a..b / a[i] / a[i] = b 와 같이 코틀린에서 새로 생긴 연산자도 있다.</li>
<li>객체끼리의 연산자를 직접 정의할 수 있다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[코틀린] 코틀린에서 Type을 다루는 방법]]></title>
            <link>https://velog.io/@true_je0n/%EC%BD%94%ED%8B%80%EB%A6%B0-%EC%BD%94%ED%8B%80%EB%A6%B0%EC%97%90%EC%84%9C-Type%EC%9D%84-%EB%8B%A4%EB%A3%A8%EB%8A%94-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@true_je0n/%EC%BD%94%ED%8B%80%EB%A6%B0-%EC%BD%94%ED%8B%80%EB%A6%B0%EC%97%90%EC%84%9C-Type%EC%9D%84-%EB%8B%A4%EB%A3%A8%EB%8A%94-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Mon, 20 Mar 2023 05:10:12 GMT</pubDate>
            <description><![CDATA[<h2 id="1-기본-타입">1. 기본 타입</h2>
<ul>
<li>Byte</li>
<li>Short</li>
<li>Int</li>
<li>Long</li>
<li>Float</li>
<li>Double</li>
<li>부호 없는 정수들</li>
</ul>
<p>코틀린에서는 선언된 기본 값을 보고 타입을 추론한다.</p>
<p>&lt;Java와 다른 내용&gt;</p>
<ul>
<li>Java : 기본 타입간의 변환은 암시적으로 이루어질 수 있다.</li>
<li>Kotlin : 기본 타입간의 변환은 명시적으로 이루어져야 한다.</li>
</ul>
<p>&lt;Java 버전&gt;</p>
<pre><code>int number1 = 4;
long number2 = number1; //둘이 타입이 다른데도 불구하고 int는 4바이트, long은 8바이트로 long이 더 크므로 들어간 다음에 계산이 된다.
//int 타입의 값이 long 타입으로 암시적으로 변경되었다. Java에서 더 큰 타입으로는 암시적 변경이 됨.
System.out.println(number1 + number2);</code></pre><p>&lt;Kotlin 버전&gt;</p>
<pre><code>val number1 = 4
val number2: Long = number1 // Type mismatch
//Kotlin에서는 암시적 타입 변경이 불가능함 -&gt; 어떻게 해야할까?!
println(number1 + number2)</code></pre><p>Kotlin에서는 암시적 타입 변경이 불가능하다.</p>
<p>다만, 명시적으로는 가능한데, to변환타입() 을 사용하면 가능하다.</p>
<pre><code>val number1 = 4
val number2: Long = number1.toLong()

println(number1 + number2)

val number1 = 3
val number2 = 5
val result = number1 / number2.toDouble()

println(result)

val number1: Int = 4
val number2: Long = number1.toLong()

println(number1 + number2)</code></pre><p>=&gt; 결론적으로 코틀린에서 타입 변환을 하기 위해서는 to변환타입()을 사용해야 한다.</p>
<p>변수가 nullable이라면 적절한 처리가 필요하다.</p>
<h2 id="2-타입-캐스팅">2. 타입 캐스팅</h2>
<p>기본 타입이 아닌 일반 타입은 어떨까?</p>
<p>&lt;Java 코드&gt;</p>
<pre><code>public static void printAgeIfPerson(Object obj) {
  if (obj instanceof Person) {
    Person person = (Person) obj;
    System.out.println(person.getAge());
  }
}</code></pre><ul>
<li>instanceof : 변수가 주어진 타입이면 true, 그렇지 않으면 false = 코틀린의 is</li>
<li>(타입) : 주어진 변수를 해당 타입으로 변경한다 = 코틀린의 as 타입 (생략 가능함 → 생략하는 기능을 스마트 캐스트라고 함.)</li>
</ul>
<p>&lt;Kotlin 코드&gt;</p>
<pre><code>fun printAgeIfPerson(obj: Any) {
  if(obj is Person) {
    val person = obj as Person
    println(person.age)
  }
}</code></pre><p>&lt;Java 코드 2&gt;</p>
<pre><code>public static void printAgeIfPerson(Object obj) {
  if(obj instanceof Person) {
    System.out.println(((Person) obj).getAge());
  }
}</code></pre><p>&lt;Kotlin 코드 2&gt;</p>
<pre><code>fun printAgeIfPerson(obj: Any) {
  if(obj is Person) {
    println(obj.age) //스마트 캐스트
  }
}</code></pre><ul>
<li>스마트 캐스트 : 코틀린 컴파일러가 똑똑하게 자동으로 변환해주는 기능. 스마트 캐스트는 두 가지의 경우에 자동으로 수행되는데, 첫 번째로 변수의 값이 null인지 확인할 때, 두 번째로 is, !is 연산자로 변수 타입을 확인할 때 자동으로 수행된다.</li>
</ul>
<h3 id="instanceof의-반대도-존재할까">Instanceof의 반대도 존재할까?!</h3>
<p>&lt;Java 코드 3&gt;</p>
<pre><code>public static void printAgeIfPerson(Obejct obj) {
  if(!(obj instanceof Person) {
    ...
  }
}</code></pre><p>&lt;Kotlin 코드 3&gt;</p>
<pre><code>fun printAgeIfPerson(obj: Any) {
  if(obj !is Person) {
    ...
  }
}</code></pre><h3 id="만약-obj에-null이-들어올-수-있다면">만약 obj에 null이 들어올 수 있다면?!</h3>
<p>&lt;Kotlin 코드&gt;</p>
<pre><code>fun main() {
  printAgeIfPerson(null)
}

fun printAgeIfPerson(obj: Any?) {
  val person = obj as? Person
  println(person?.age)
}</code></pre><h3 id="value-is-type">value is Type</h3>
<ul>
<li>value가 Type이면? true</li>
<li>value가 Type이 아니면? false</li>
</ul>
<h3 id="value-is-type-1">value !is Type</h3>
<ul>
<li>value가 Type이면? false</li>
<li>value가 Type이 아니면? true</li>
</ul>
<h3 id="value-as-type">value as Type</h3>
<ul>
<li>value가 Type이면? Type으로 타입 캐스팅</li>
<li>value가 Type이 아니면? 예외 발생</li>
</ul>
<h3 id="value-as-type-1">value as? Type</h3>
<ul>
<li>value가 Type이면? Type으로 타입 캐스팅</li>
<li>value가 null이면? null</li>
<li>value가 Type이 아니면? null</li>
</ul>
<h2 id="3-kotlin의-특이한-타입-3가지">3. Kotlin의 특이한 타입 3가지</h2>
<p>a. Any
b. Unit
c. Nothing</p>
<h3 id="a-any">a. Any</h3>
<p>Java의 Object 역할. (모든 객체의 최상위 타입)
모든 Primitive Type의 최상의 타입도 Any이다.
Any 자체로는 null을 포함할 수 없어 null을 포함하고 싶다면, Any?로 표현.
Any에 equals / hashCode / toString 존재.</p>
<h3 id="b-unit">b. Unit</h3>
<p>Unit은 Java의 void와 동일한 역할.
(살짝 어려운 내용) void와 다르게 Unit은 그 자체로 타입 인자로 사용 가능하다.
함수형 프로그래밍에서 Unit은 단 하나의 인스턴스만 갖는 타입을 의미. 즉, 코틀린의 Unit은 실제 존재하는 타입이라는 것을 표현.</p>
<h3 id="c-nothing">c. Nothing</h3>
<p>Nothing은 함수가 정상적으로 끝나지 않았다는 사실을 표현하는 역할.
무조건 예외를 반환하는 함수 / 무한 루프 함수 등</p>
<pre><code>fun fail(message: String): Nothing {
  throw IllegalArgumentException(message)
}</code></pre><h2 id="4-string-interpolation--string-indexing">4. String interpolation / String indexing</h2>
<p>&lt;Java 코드&gt;</p>
<pre><code>Person person = new Person(&quot;전하윤&quot;, 100);
String log = String.format(&quot;사람의 이름은 %s이고 나이는 %s세 입니다.&quot;, person.getName(), person.getAge());

StringBuilder builder = new StringBuilder();
builder.append(&quot;사람의 이름은&quot;);
builder.append(person.getName());
builder.appdend(&quot;이고 나이는&quot;);
builder.append(person.getAge());
builder.append(&quot;세 입니다.&quot;);</code></pre><p>&lt;Kotlin 코드&gt;</p>
<pre><code>val person = Person(&quot;전하윤&quot;, 100) // Person(name, age) 임.
val log = &quot;사람의 이름은 ${person.name}이고 나이는 ${person.age}세 입니다&quot;</code></pre><p>=&gt; ${변수} 를 사용하면 값이 들어간다.
=&gt; 이것도 javascript와 유사하다. 오… </p>
<h3 id="예제">예제</h3>
<pre><code>fun main() {
  val person = Person(&quot;최태현&quot;, 100) // Person(name, age) 임.
  System.out.println(String.format(&quot;이름 : %s&quot;, person.name))

  println(&quot;이름 : ${person.name}&quot;)
}</code></pre><h3 id="kotlin-코드-2">Kotlin 코드 2</h3>
<pre><code>val name = &quot;최태현&quot;
val age = 100
val log = &quot;사람의 이름: $name 나이: $age&quot;</code></pre><p>=&gt; $변수 를 사용할 수도 있다.
=&gt; javascript와 유사 22…</p>
<h3 id="예제-2">예제 2</h3>
<pre><code>fun main() {
  val name = &quot;최태현&quot;

  println(&quot;이름 : $name&quot;)
}</code></pre><h3 id="tip">TIP</h3>
<p>변수 이름만 사용하더라도 ${변수}를 사용하는 것이</p>
<p>01) 가독성
02) 일괄 변환
03) 정규식 활용</p>
<p>측면에서 좋다.</p>
<p>코틀린에서는 여러 줄의 문자열을 작성 시, 큰 따옴표(“) 3개를 작성하면 더 편하게 작성 가능하다.</p>
<pre><code>fun main() {
  val name = &quot;최태현&quot;
  var str = &quot;&quot;&quot;
    ABC
    EFG
    ${name}
  &quot;&quot;&quot;.trimIndent()
  println(str)
}
</code></pre><p>&lt;자바 코드&gt;</p>
<pre><code>String str = &quot;ABCDE&quot;;
char ch = str.charAt(1);</code></pre><p>=&gt; Java에서 문자열의 특정 문자 가져오기</p>
<p>&lt;코틀린 코드&gt;</p>
<pre><code>val str = &quot;ABCDE&quot;
val ch = str[1]</code></pre><p>=&gt; Kotlin에서 문자열의 특정 문자 가져오기</p>
<h3 id="-코틀린에서-type을-다루는-방법-정리-">※ 코틀린에서 Type을 다루는 방법 (정리) ※</h3>
<ul>
<li>코틀린의 변수는 초기값을 보고 타입을 추론하며, 기본 타입들 간의 변환은 명시적으로 이루어진다.</li>
<li>코틀린에서는 is, !is, as, as? 를 이용해 타입을 확인하고 캐스팅한다.</li>
<li>코틀린의 Any는 Java의 Object와 같은 최상위 타입이다.</li>
<li>코틀린의 Unit은 Java의 void와 동일하다.</li>
<li>코틀린에 있는 Nothing은 정상적으로 끝나지 않은 함수의 반환을 의미한다.</li>
<li>문자열을 가공할 때 ${변수}와 “““ “““ 를 사용하면 깔끔한 코딩이 가능하다.</li>
<li>문자열에서 문자를 가져올 때 Java의 배열처럼 []를 사용한다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[코틀린] 코틀린에서 null을 다루는 방법]]></title>
            <link>https://velog.io/@true_je0n/%EC%BD%94%ED%8B%80%EB%A6%B0-%EC%BD%94%ED%8B%80%EB%A6%B0%EC%97%90%EC%84%9C-null%EC%9D%84-%EB%8B%A4%EB%A3%A8%EB%8A%94-%EB%B0%A9</link>
            <guid>https://velog.io/@true_je0n/%EC%BD%94%ED%8B%80%EB%A6%B0-%EC%BD%94%ED%8B%80%EB%A6%B0%EC%97%90%EC%84%9C-null%EC%9D%84-%EB%8B%A4%EB%A3%A8%EB%8A%94-%EB%B0%A9</guid>
            <pubDate>Mon, 20 Mar 2023 04:46:27 GMT</pubDate>
            <description><![CDATA[<h2 id="1-kotlin에서의-null-체크">1. Kotlin에서의 null 체크</h2>
<h3 id="a-java에서의-null-체크방법">a. Java에서의 null 체크방법</h3>
<pre><code>public boolean startsWithA(String str) {
  return str.startsWith(&quot;A&quot;);
}</code></pre><p>위의 코드는 null 값을 갖고오면 NPE가 나기 때문에 안전한 코드가 아니다.</p>
<p>따라서 자바에서 null을 체크하는 안전한 코드로 고치는 방법은,</p>
<pre><code>public boolean startsWithA1(String str) {
  if(str == null) {
    throw new IllegalArgumentsException(&quot;null이 들어왔습니다.&quot;);
  }
  return str.startsWith(&quot;A&quot;);
}</code></pre><p>=&gt; 위와 같이 str이 null일 경우 Exception을 내거나,</p>
<pre><code>public Boolean startsWithA2(String str) {
  if(str == null) {
    return null;
  }
  return str.startsWith(&quot;A&quot;);
}</code></pre><p>=&gt; 위와 같이 str이 null일 경우 null을 반환하거나, (null 값을 반환해주므로 메소드 타입은 null 값 반환 가능한 Boolean)</p>
<pre><code>public boolean startsWithA3(String str) {
  if(str == null) {
    return false;
  }
  return str.startsWith(&quot;A&quot;);
}</code></pre><p>=&gt; 위와 같이 str이 null일 경우 false를 반환한다.</p>
<h3 id="b-kotlin에서의-null-체크방법">b. Kotlin에서의 null 체크방법</h3>
<pre><code>fun startsWithA1(str: String?): Boolean {
  if(str == null) {
    throw IllegalArgumentException(&quot;null이 들어왔습니다.&quot;)
  }
  return str.startsWith(&quot;A&quot;)
}
</code></pre><p>=&gt; 들어오는 값(파라미터, str)이 null 일 수 있으므로, 타입 지정 후 오른쪽에 “?(물음표)”를 붙여주고, 함수 값 반환 시 Boolean 타입으로 표시를 해주므로(단, null 값을 반환할 수는 없음), 함수의 타입(Boolean)을 맨 오른쪽에 표시해준다.</p>
<pre><code>fun startsWithA2(str: String?): Boolean? {
  if(str == null) {
    return null
  }
  return str.startsWith(&quot;A&quot;)
}</code></pre><p>=&gt; 들어오는 값(파라미터, str)이 null 이라면 null 값을 반환하므로, 파라미터 타입 뿐만 아니라, 함수의 타입(Boolean) 오른쪽에도 “?(물음표)”를 표시해준다.</p>
<pre><code>fun startsWithA3(str: String?): Boolean {
  if (str == null) {
    return false
  }
  return str.startsWith(&quot;A&quot;)
}</code></pre><p>=&gt; Kotlin은 null 일 수 있는 변수에 대해서 바로 메소드콜을 할 수 없는데, 문맥상 위에서 null 체크를 해주면 자동으로 아래에 있는 변수는 null이 아닐거라고 컴파일러가 자동으로 추측을 해준다.</p>
<p>따라서, 아래에 있는 str 변수는 null 값이 아니라는 의미로 초록색으로 표시되게 된다.</p>
<p>번외로,</p>
<pre><code>fun startsWithA(str: String?): Boolean {
  return str.startsWith(&quot;A&quot;)
}</code></pre><p>=&gt; 맨 위의 NPE 위험이 있는 JAVA 코드처럼 Kotlin에서도 null에 대한 체크 없이 바로 파라미터 값을 반환하려고 하면, str 값이 null 일수도 있기 때문에 바로 에러가 난다.</p>
<pre><code>fun startsWithA(str: String): Boolean {
  return str.startsWith(&quot;A&quot;)
}</code></pre><p>=&gt; 위의 코드의 경우, 파라미터 타입 뒤에 “?(물음표)” 가 없으므로, null 값일 때 파라미터 값으로 받아올 수 없는 것으로 간주하고, str.startsWith(“A”)가 바로 호출된다.</p>
<h2 id="2-safe-call과-elvis-연산자">2. Safe Call과 Elvis 연산자</h2>
<p>Kotlin에서는 null이 가능한 타입을 완전히 다르게 취급한다.
=&gt; null이 가능한 타입만을 위한 기능은 없을까?!
==&gt; 있다!!!</p>
<h3 id="a-safe-call">a. Safe Call</h3>
<pre><code>val str: String? = &quot;ABC&quot;
str.length // 불가능
srt?.length // 가능!!</code></pre><p>null이 아니면 실행하고, null이면 실행하지 않는다. (그대로 null)</p>
<h3 id="b-elvis-연산자">b. Elvis 연산자</h3>
<pre><code>val str: String? = &quot;ABC&quot; //null이 아니라면 그 값이 호출
str?.length ?: 0 // null인 경우 0 호출</code></pre><p>앞의 연산 결과가 null이면 뒤의 값을 사용
Elvis 연산은 early return 에도 사용할 수 있다.</p>
<p>&lt; JAVA.ver &gt;</p>
<pre><code>public long calculate(Long number) {
  if (number == null) {
    return 0;
  }
  //다음 로직
}</code></pre><p>↓</p>
<p>&lt; Kotlin.ver &gt;</p>
<pre><code>fun calculate(number: Long?): Long {
  number ?: return 0
  //다음 로직
}</code></pre><h2 id="3-null-아님-단언">3. null 아님 단언!!</h2>
<p>nullable type 이지만, 아무리 생각해도 null이 될 수 없는 경우</p>
<pre><code>fun startsWithA1(str: String?): Boolean {
  return str!!.startsWith(&quot;A&quot;)
}</code></pre><p>=&gt; 변수!!.startsWith(“값”) → 절대 null 이 아니야!! 라는 뜻</p>
<h2 id="4-플랫폼-타입">4. 플랫폼 타입</h2>
<p>Kotlin에서 Java 코드를 가져다 사용할 때 어떻게 처리될까?!</p>
<p>&lt;Person.java&gt;</p>
<pre><code>import org.jetbrains.annotation.Nullable;

public class Person {
  private final String name;

  public Person(String name) {
    this.name = name;
  }

  @Nullable
  public String getName() {
    return name;
  }
}</code></pre><p>&lt;Lec02Main.kt&gt;</p>
<pre><code>package com.lannstark.lec02

fun main() {
  val person = Person(&quot;공부하는 개발자&quot;) //Person.java를 불러옴
  startsWithA(person.name) //null 을 쓸 수 없으므로 빨간 밑줄이 쳐진다.
  //사용하고 싶다면 java 코드에서 @Nullable =&gt; @NotNull로 바꾸거나, startsWithA 함수의 파라미터의 타입 뒤에 &quot;?&quot; 를 붙여준다.
}

fun startsWithA(str: String): Boolean {
  return str.startsWith(&quot;A&quot;)
}</code></pre><ul>
<li>javax.annotation 패키지</li>
<li>android.support.annotation 패키지</li>
<li>org.jetbrains.annotation 패키지</li>
<li>Nullable이 없다면?! : Kotlin에서는 이 값이 nullable인지 non-nullable 인지 알 수가 없다.</li>
<li>플랫폼 타입 : 코틀린이 null 관련 정보를 알 수 없는 타입. Runtime 시 Exception이 날 수 있다.</li>
</ul>
<h4 id="-코틀린에서-null을-다루는-방법-정리-">※ 코틀린에서 null을 다루는 방법 (정리) ※</h4>
<ul>
<li>코틀린에서 null이 들어갈 수 있는 타입은 완전히 다르게 간주된다</li>
<li>한 번 null 검사를 하면 non-null임을 컴파일러가 알 수 있다</li>
<li>null이 아닌 경우에만 호출되는 Safe Call (?.) 이 있다</li>
<li>null인 경우에만 호출되는 Elvis 연산자 (?:) 가 있다</li>
<li>null이 절대 아닐 때 사용할 수 있는 널 아님 단언 (!!) 이 있다</li>
<li>Kotlin에서 Java 코드를 사용할 때 플랫폼 타입 사용에 유의해야 한다.</li>
<li>Java 코드를 읽으며 null 가능성 확인 / Kotlin으로 wrapping</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>