<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>hjseo-dev.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Thu, 20 Feb 2025 02:16:29 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>hjseo-dev.log</title>
            <url>https://velog.velcdn.com/images/hyejiseo-dev/profile/e2ad0827-56ea-483f-9c6c-6b384a9a3f69/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. hjseo-dev.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/hyejiseo-dev" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Android Hilt 에 관하여]]></title>
            <link>https://velog.io/@hyejiseo-dev/Android-Hilt-%EC%97%90-%EA%B4%80%ED%95%98%EC%97%AC</link>
            <guid>https://velog.io/@hyejiseo-dev/Android-Hilt-%EC%97%90-%EA%B4%80%ED%95%98%EC%97%AC</guid>
            <pubDate>Thu, 20 Feb 2025 02:16:29 GMT</pubDate>
            <description><![CDATA[<h3 id="💡-android-hilt란">💡 Android Hilt란?</h3>
<p>Hilt는 Dagger를 기반으로 한 Android 공식 의존성 주입(Dependency Injection, DI) 라이브러리입니다. Google이 제공하며, Android 앱에서 의존성 관리를 쉽게 할 수 있도록 설계되었습니다.</p>
<h3 id="📍-hilt-사용법">📍 Hilt 사용법</h3>
<h4 id="1-프로젝트-설정">1. 프로젝트 설정</h4>
<p>Gradle에 Hilt 의존성을 추가해야 합니다.</p>
<p>(1) build.gradle (Project)</p>
<pre><code class="language-kotlin">plugins {
    id &#39;com.android.application&#39;
    id &#39;kotlin-kapt&#39;
    id &#39;dagger.hilt.android.plugin&#39;
}</code></pre>
<p>(2) build.gradle (Module)</p>
<pre><code class="language-kotlin">dependencies {
    implementation &quot;com.google.dagger:hilt-android:2.50&quot;
    kapt &quot;com.google.dagger:hilt-android-compiler:2.50&quot;
}</code></pre>
<h4 id="2-application-클래스에-hilt-설정">2. Application 클래스에 Hilt 설정</h4>
<p>모든 앱의 최상위에서 DI를 적용하기 위해 _@HiltAndroidApp_을 추가합니다.</p>
<pre><code class="language-kotlin">@HiltAndroidApp
class MyApplication : Application()</code></pre>
<h4 id="3-의존성-주입-module">3. 의존성 주입 (Module)</h4>
<p>Hilt에서 @Module과 @InstallIn을 사용하여 의존성을 주입합니다.</p>
<pre><code class="language-kotlin">@Module
@InstallIn(SingletonComponent::class) // 앱 전체에서 사용 가능
object AppModule {

    @Provides
    @Singleton
    fun provideRetrofit(): Retrofit {
        return Retrofit.Builder()
            .baseUrl(&quot;https://api.example.com/&quot;)
            .addConverterFactory(GsonConverterFactory.create())
            .build()
    }
}</code></pre>
<h4 id="4-viewmodel에서-di-사용">4. ViewModel에서 DI 사용</h4>
<p>Hilt를 사용하면 ViewModel에서도 간단하게 DI를 적용할 수 있습니다.</p>
<pre><code class="language-kotlin">@HiltViewModel
class MyViewModel @Inject constructor(
    private val retrofit: Retrofit
) : ViewModel() {
    fun fetchData() {
        // Retrofit을 사용하여 API 호출
    }
}
</code></pre>
<h4 id="5-activity에서-의존성-주입">5. Activity에서 의존성 주입</h4>
<p>Activity 또는 Fragment에서 @AndroidEntryPoint를 추가하면 Hilt가 자동으로 의존성을 주입합니다.</p>
<pre><code class="language-kotlin">@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
    @Inject lateinit var retrofit: Retrofit

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // Retrofit 사용 가능
    }
}</code></pre>
<h3 id="📍-hilt의-scope-종류">📍 Hilt의 Scope 종류</h3>
<p><strong>Scope    설명</strong>
<img src="https://velog.velcdn.com/images/hyejiseo-dev/post/ec3feafd-4ba3-4243-89e7-6c225d28bfb8/image.png" alt=""></p>
<h4 id="정리">정리</h4>
<p>Hilt는 Dagger를 기반으로 하면서도 더 간편하게 의존성 주입을 사용할 수 있도록 도와줍니다. 특히, Android 컴포넌트에 특화된 DI 지원과 자동으로 Component를 설정해주는 기능 덕분에 코드가 훨씬 간결해지고 유지보수가 쉬워집니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Android Background 처리와 WorkManager]]></title>
            <link>https://velog.io/@hyejiseo-dev/Android-Background-%EC%B2%98%EB%A6%AC%EC%99%80-WorkManager</link>
            <guid>https://velog.io/@hyejiseo-dev/Android-Background-%EC%B2%98%EB%A6%AC%EC%99%80-WorkManager</guid>
            <pubDate>Wed, 14 Sep 2022 12:50:15 GMT</pubDate>
            <description><![CDATA[<h2 id="📍-thread-vs-service">📍 Thread vs Service</h2>
<p>**
Q. Service 내에서 Thread를 사용해야 한다면 그냥 일반적인 코드에서 Thread를 사용하는 것이 무엇이 다를까?**</p>
<p><strong>Thread의 문제</strong>: 안드로이드 컴포넌트가 아니므로 독자적인 생명주기도 없고 Main Thread가 아니기 때문에 앱을 나가면 프로세스가 유지되지 않는다. 만약에 OOM Killer(메모리 부족에 의한 특정 프로세스 종료)에 의해서 프로세스가 종료되면 다시 재시작 될 것이라는 보장도 없다.</p>
<p><strong>Service 내부 동작</strong>: 안드로이드 4대 컴포넌트 중 하나로서 독자적인 생명주기를 가지고 있고 Main Thread에서 동작하기 때문에 사용자가 앱을 나가도 프로세스가 유지된다. 만약 강제로 프로세스가 죽을 경우 다시 살아날 수도 있다.</p>
<h2 id="📍-service">📍 Service</h2>
<ul>
<li><p>백그라운드 작업에 사용, ui thread에서 사용하나 앱이 느려지므로 별도의 스레드 처리 필요</p>
</li>
<li><p>시작: startService, 종료: stopService, 실행 중 종료 : stopSelf
ex) 음악재생, 파일 입출력, 네트워크 트랜잭션.. etc</p>
</li>
<li><p>백그라운드, 바인더, 포그라운드 3가지 유형</p>
</li>
<li><p>서비스는 잘못 사용할 확률이 높아 스레드 처리를 직접하지 않기위해
IntentService가 등장</p>
</li>
<li><p>Thread 처리할 때 동시에는 안되며 하나씩 작업</p>
</li>
<li><p>API 30 에서 부터 IntentService가 deprecated되어 JobIntentService로 대체됨</p>
</li>
</ul>
<h2 id="📍-jobintentservice">📍 JobIntentService</h2>
<ul>
<li>IntentService와 동작은 비슷하나 코드가 다름</li>
<li>실행 : enqueueWork(), 안드로이드 오레오 8.0 이상에서 동작하고 그 이하에서는 startService()로 동작, onHandleWork()에서 동작 처리 </li>
<li>제한시간 10분으로 강제종료 되며 그 안에 끝나는 서비스에만 사용하도록 해야함</li>
<li>BIND_JOB_SERVICE 권한 필수</li>
</ul>
<h2 id="📍-workmanager">📍 WorkManager</h2>
<ul>
<li>JetPack에 포함되며 서비스로 할수 있는 것 대체 가능</li>
<li>오래걸리는 작업은 Worker로 구현</li>
<li>WorkRequest는 한번수행 or 반복수행 있음
(네트워크, 배터리 상태 변경시 트리거 가능)</li>
<li>WAKE_LOCK 관리하여 권한 추가 가능 &gt; 오레오 이상에서는 가능, 이전에는 원래 쓰던 것으로 관리</li>
<li>foreground 서비스로 동작 가능</li>
</ul>
<h3 id="언제-workmanager를-사용하는가">언제 WorkManager를 사용하는가?</h3>
<ul>
<li><p>백그라운드에서 작업을 실행하면 RAM 및 배터리와 같은 제한된 리소스가 소비됨에 따라서 사용자 환경이 저하될 수 있다</p>
</li>
<li><p>WorkManager 는 모든 OS 백그라운드 실행 제한을 고려하여 백그라운드 실행에 권장되는 솔루션이다</p>
</li>
</ul>
<p>사용 예시</p>
<p><img src="https://velog.velcdn.com/images/hyejiseo-dev/post/861a630d-c537-4025-8fea-68f48b8dadfb/image.png" alt=""></p>
<ol>
<li>지연가능한 작업의 보장된 실행 :** WorkManager**
서버에 로그 업로드하는 경우
업로드 / 다운로드 할 콘텐츠 암호화 / 복호화</li>
<li>외부 이벤트에 대한 응답으로 시작된 작업 : <strong>FCM + WorkManager</strong>
이메일과 같은 새로운 온라인 컨텐츠 동기화
Firebase Cloud Messaging을 사용해서 앱에 알리고
WorkManager로 작업 요청을 생성해 콘텐츠를 동기화 한다
모든 작업을 WorkManager를 사용하는 것은 올바른 사용 방법이 아니다</li>
</ol>
<p>사용자가 현재 보고있는 UI를 빠르게 변경하는 작업이나 결제 진행 등 즉시 처리해야 하는 작업은 ForgroundService를 사용하거나 ThreadPool, Rx등을 사용</p>
<p>참조 : <a href="https://android-developers.googleblog.com/2018/10/modern-background-execution-in-android.html">Modern background execution in Android</a></p>
<h3 id="💡-예시">💡 예시</h3>
<ul>
<li>구현 순서
<img src="https://velog.velcdn.com/images/hyejiseo-dev/post/4df0e41a-5661-4ff6-b5d9-076f9d4be855/image.png" alt=""></li>
</ul>
<p>WorkManager 클래스 생성 -&gt; WorkManager Request 생성 -&gt; Equeue Request (워크매니저 실행 요청) -&gt; 상태 업데이트 얻기(작업 대기 및 수신)</p>
<p>WorkManager를 이용해 작업을 등록하고 실행하려면 아래의 클래스를 이용합니다.</p>
<blockquote>
<ul>
<li>Worker: 작업 내용을 가지는 추상 클래스</li>
</ul>
</blockquote>
<ul>
<li>WorkRequest: 작업 의뢰 내용으로 이를 상속한 </li>
<li>OneTimeWorkRequest, PeriodicWorkRequest 두 개의 클래스를 사용</li>
<li>Constraints: WorkRequst의 제약 조건 명시</li>
</ul>
<p>Worker 클래스를 생성한다
doWork() 매서드로 백그라운드 스레드에서 동기식으로 실행됨</p>
<pre><code class="language-kotlin">class SimpleWorker : Worker() {
  override fun doWork(): WorkerResult {
  // 처리해야할 작업 명시 
  //return은 SUCCESS, FAILURE, RETRY가 있다. 
    return WorkerResult.SUCCESS
  }
}</code></pre>
<p>경우에 따라 작업을 수행하는 코드를 작성..</p>
<h4 id="1-한번-수행-여러번-반복하는-경우">1. 한번 수행, 여러번 반복하는 경우</h4>
<pre><code class="language-kotlin">// 한번만 수행
val workRequest = OneTimeWorkRequestBuilder&lt;SimpleWorker&gt;().build()
//15분마다 작업 실행 
val workRequest = PeriodicWorkRequestBuilder&lt;SimpleWorker&gt;(15, TimeUnit.MINUTES).build()
val workManager = WorkManager.getInstance()
workManager?.enqueue(workRequest)</code></pre>
<h4 id="2-특정-제약조건을-충족해야-실행되도록-할-경우">2. 특정 제약조건을 충족해야 실행되도록 할 경우</h4>
<p>constraints로 표시한다</p>
<pre><code class="language-kotlin">//배터리 충전중일때 실행(예시)
val constraints = Constraints.Builder()
                .setRequiredNetworkType(NetworkType.CONNECTED)
                                .build()

val requestConstraint  = OneTimeWorkRequestBuilder&lt;SimpleWorker&gt;()
                .setConstraints(constraints)
                .build()

val workManager = WorkManager.getInstance()
workManager?.enqueue(requestConstraint)                </code></pre>
<h4 id="3-연결된-작업을-실행할-경우">3. 연결된 작업을 실행할 경우</h4>
<p>A 작업 이후 B 작업을 해야한다면, beginWith과 then 사용하여 표시함</p>
<pre><code class="language-kotlin">val workA = OneTimeWorkRequestBuilder&lt;AWorker&gt;().build()
val workB = OneTimeWorkRequestBuilder&lt;BWorker&gt;().build()

WorkManager.getInstance()?.apply{
        beginWith(workA).then(workB).enqueue() 
}</code></pre>
<p>더 자세한 정보 참고
<a href="https://dongsik93.github.io/til/2020/05/15/til-jetpack-workmanager/">https://dongsik93.github.io/til/2020/05/15/til-jetpack-workmanager/</a></p>
<h4 id="ex-fcm-알람">ex) FCM 알람</h4>
<p>WorkerParameters &gt; getExtra처럼 데이터를 전달 받는데 사용</p>
<pre><code class="language-kotlin">class ScheduledWorker(appContext: Context, workerParams: WorkerParameters) :
    Worker(appContext, workerParams) {

    override fun doWork(): Result {
        // Get Notification Data
        val title = inputData.getString(EXTRA_TITLE).toString()
        val message = inputData.getString(EXTRA_BODY).toString()
        // FCM 전송(FCM 전송하는 Utils 클래스 생성)
        NotificationUtil(applicationContext).showNotification(
            title,
            message
        )
        //성공
        return Result.success()
    }
}</code></pre>
<p>foreground, background, doze mode일 경우 모두 10분전에 알람 받을 수 있도록 설정</p>
<pre><code class="language-kotlin">private fun workManagerAlarm(
        scheduledTime : String, title : String, body : String
    ) {
        //workMAnager 에 넘겨줄 파라미터 값 
        val data =
            workDataOf(
                EXTRA_BODY to body, EXTRA_TITLE to title
            )
        //지정한 시간의(schedudleTIme) 10분 이전의 타임스탬프 계산
        val timeDiff =
            scheduledTime.toLong() - System.currentTimeMillis() - TimeUnit.MINUTES.toMillis(10)
        //파라미터와 지연 시간 설정
        val workRequest = OneTimeWorkRequestBuilder&lt;ScheduledWorker&gt;().setInputData(data)
            .setInitialDelay(timeDiff, TimeUnit.MILLISECONDS).build()
        //워크매니저 생성
        val workManager = WorkManager.getInstance(applicationContext)
        //워크매니저 실행
        workManager.enqueue(workRequest)
    }]</code></pre>
<h2 id="📍-백그라운드-처리">📍 백그라운드 처리</h2>
<p><img src="https://velog.velcdn.com/images/hyejiseo-dev/post/79777ce9-c0af-4874-9bf1-4bfb1d695141/image.png" alt=""></p>
<ul>
<li>즉각적인 실행이 필요한가? ex) 누르면 바로 동작
(코틀린 coroutine 사용 / 자바 API 30 excuterService, WorkManager 등 사용)
정확한 시간에 해야하면 AlarmManager 사용, 잠자기 모드 해제 가능</li>
<li>여유를 줄 수 있는가? ex) 파일 다운시.. 지연이 필요 / WorkManager 사용 가능</li>
</ul>
<h3 id="💡-결론">💡 결론</h3>
<p>서비스 직접 실행은 되도록 피해야하며, JobIntentService를 사용 가능함 (10분 지연)
이외 WorkManager 로 사용 -&gt; 간단하고 짧은 작업 / 긴 작업(foreground로) 등 사용</p>
<ul>
<li>앱에 오랜 시간 표시해야 할 경우..
workManager에 foreground로는 무리가 있어 따로 서비스를 구현해야한다</li>
</ul>
<p>이외의 Background Task</p>
<ul>
<li>만일 사용자가 보고 있는 빠르게 변경하는 작업이나 즉시 처리해야 하는 작업이라면 WorkManager의 사용 대신 Foreground Service를 사용하는 것이 좋습니다.</li>
</ul>
<p>또한 WorkManager은 지연이 될 수 있기 때문에, 정확한 시간에 동작해야하는 작업은 AlarmManager을 사용하는 것이 좋습니다.</p>
<h3 id="버전-별-이슈">버전 별 이슈</h3>
<ul>
<li>Android 12의 Foreground Service 실행 제한이 생김. 따라서 Foreground Service의 권장 대안으로 WorkManager을 제안한다.</li>
<li>WorkManager 2.7.0 부터 앱은 setEcpedited() 메서드를 활용해 가능하며 이 새로운 API는 Android 12에서 실행되면 신속 처리 작업을 사용하고, 이전 버전에서는 Foreground Service를 이용하여 이를 처리하기 때문에 이전 버전과의 호환성을 제공한다.</li>
</ul>
<p>관련 내용 : <a href="https://developer.android.com/topic/libraries/architecture/workmanager/how-to/define-work#expedited">https://developer.android.com/topic/libraries/architecture/workmanager/how-to/define-work#expedited</a></p>
<p>출처 : <a href="youtube.com/watch?v=ZqhrZ8_3jlg&amp;t=125">youtube.com/watch?v=ZqhrZ8_3jlg&amp;t=125</a>
<a href="https://youngest-programming.tistory.com/361">https://youngest-programming.tistory.com/361</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Android Compose 시작하기]]></title>
            <link>https://velog.io/@hyejiseo-dev/Compose-%EA%B8%B0%EC%B4%88-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@hyejiseo-dev/Compose-%EA%B8%B0%EC%B4%88-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Wed, 24 Aug 2022 14:20:46 GMT</pubDate>
            <description><![CDATA[<h2 id="📍-android-jetpack-compose">📍 Android JetPack Compose</h2>
<p>시작은.. 간단하게 compose 기본 ui를 만들어보았다!
먼저 MainActivity의 onCreate 안에 있는 기본적인 구성이다.</p>
<p>setContent는 xml과 같은 역할로, 앱의 큰 틀을 만들어준다.
Greeting 을 통해 내부 화면 구성을 그리도록 되어있다. Surface는 화면 크기와 배경색 등 테마를 바꿔줄 수 있다.</p>
<pre><code class="language-kotlin"> setContent {  // xml
            ComposeBasicTheme {
                // A surface container using the &#39;background&#39; color from the theme
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colors.background
                ) {
                    Greeting(&quot;Android&quot;)
                }
            }
        }</code></pre>
<p>이제 Greeting을 구성해 보았다.</p>
<h3 id="💡-scaffold">💡 Scaffold</h3>
<p>Scaffold는 기본 material 디자인 레이아웃 구조를 구현하는 레이아웃이다. TopBar, BottomBar, FAB 또는 서랍과 같은 항목을 추가할 수 있습니다.</p>
<p>아래 문서에 따라 앱바와 floating버튼을 추가하는 코드이다.</p>
<p>참고문서
<a href="https://foso.github.io/Jetpack-Compose-Playground/material/scaffold/">https://foso.github.io/Jetpack-Compose-Playground/material/scaffold/</a></p>
<pre><code class="language-kotlin">@Composable
fun Greeting(name: String) {
    Scaffold(topBar = { // material 디자인 삽입 (앱바, 플로팅버튼)
        TopAppBar(
            title = { Text(&quot;테스트앱&quot;) },
            backgroundColor = Color(0xfff25287)
        )
    },floatingActionButtonPosition = FabPosition.End,
        floatingActionButton = { FloatingActionButton(onClick = {}){
            Text(&quot;click&quot;)
        } }
    ) {
       // Text(text = &quot;안녕하세용 $name!&quot;)
        MyComposableView()
    }
}</code></pre>
<p>Scaffold 안에 또 다른 @Composable 만들어 추가
간단하게 텍스트를 여러줄로 출력하는 코드이다.</p>
<pre><code>@Composable
fun MyComposableView(){
    Log.d(&quot;tag&quot;,&quot;myComposableView : &quot;)
    //vertical linearlayout
    Column (
        Modifier
            .background(Color.Green)
            .verticalScroll(rememberScrollState())  // 스크롤 가능
            ){
        for (i in 1..20){
            MyRowItem()
        }
    }
}</code></pre><p>MyRowItem으로 텍스트를 만들고 Colum 안에서 for 문으로 여러개 호출한다.
Row 와 Colum은 각각 horizental / vertical 리니어 레이아웃과 같기 때문에
잘 섞어서 사용하도록 한다.</p>
<h3 id="💡-modifier">💡 Modifier</h3>
<p>색, 크기, 가로, 세로 등등 여러 기능을 커스텀 할 수 있다.</p>
<p>*<em>✏️ Modifier 기능 *</em></p>
<ul>
<li>Composable의 크기, 레이아웃, 동작, 모양 변경</li>
<li>접근성 라벨 같은 정보 추가</li>
<li>사용자 입력 처리</li>
<li>아이템 클릭, 스크롤, 드래그, 확대/축소 등의 상호작용 </li>
</ul>
<p>가로 길이 설정 : Modifier.width(width: dp)
세로 길이 설정 : Modifier.height(height:dp)
스크롤 설정 : .verticalScroll(rememberScrollState())  </p>
<p>.. 이외도 많은 것 같다</p>
<p>생각보다 처음 입문하기에 flutter와 비슷한 느낌이 들었다. 앞으로 더 공부해봐야지!!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Kotlin : Flow 기초]]></title>
            <link>https://velog.io/@hyejiseo-dev/Kotlin-Flow-%EA%B8%B0%EC%B4%88</link>
            <guid>https://velog.io/@hyejiseo-dev/Kotlin-Flow-%EA%B8%B0%EC%B4%88</guid>
            <pubDate>Mon, 06 Jun 2022 04:11:59 GMT</pubDate>
            <description><![CDATA[<h3 id="📍-flow-란">📍 Flow 란?</h3>
<blockquote>
<p>데이터 스트림이며 코루틴 상에서 반응형 프로그래밍을 지원하기 위한 구성요소이다</p>
</blockquote>
<p>중간에 갱신되는 값을 받아올 수 있도록 함
builder로 생성, suspend를 사용하지 않아도 됨
프로듀서가 비동기적으로 생성, 컨슈머가 소비함</p>
<p><img src="https://velog.velcdn.com/images/hyejiseo-dev/post/af26cb16-a518-439f-8309-3aa8f4c4e0cc/image.png" alt=""></p>
<ul>
<li><strong>Producer</strong> : 스트림에 추가되는 데이터를 생산합니다. 코루틴 덕분에 흐름에서 비동기적으로 데이터가 생산될 수도 있습니다. 
flow{} 내부의 <strong>emit()</strong>를 통해 데이터 생성</li>
<li><strong>Intermediary</strong>(선택사항) : 중개자는 스트림에 내보내는 각각의 값이나 스트림 자체를 수정할 수 있습니다.</li>
<li><em>map*</em>을 사용해서 <strong>필터링</strong> 하여 필요한 데이터만 가져온다</li>
<li>*<em>Consumer *</em>: 스트림의 값을 사용합니다.</li>
<li><em>collect*</em> 를 사용하여 전달된 데이터를 소비한다 (UI에서 보통 사용)</li>
</ul>
<h4 id="✏️-flow-vs-livedata">✏️ Flow vs Livedata</h4>
<p>Livedata는 생명주기에 영향을 받음
Coroutine Scope에서 사용, 풍부한 연산자로 유연하게 데이터 변화 가능
안드로이도 의존성에서 자유로움, Cold Stream </p>
<p>-&gt; Sharedflow로 대체!!
두가지 방식의 특징을 가질 수 있음</p>
<h3 id="📍-stateflow의-등장-flow의-한계-극복">📍 StateFlow의 등장, Flow의 한계 극복</h3>
<blockquote>
<p>데이터 저장소 역할과 데이터 스트림까지 할 수 있다. UI에서 StateFlow를 구독하여 UIState를 업데이트 하면 재구성시 다시 요청 할 필요가 없어짐</p>
</blockquote>
<h4 id="statein-사용하여-변환하기-세가지-변수">stateIn 사용하여 변환하기 (세가지 변수)</h4>
<ul>
<li>scope : 구독 받을 coroutineScope를 명시</li>
<li>stated : 언제 구독 할지 명시함</li>
<li>initialValue : 저장될 초기값을 설정</li>
</ul>
<pre><code class="language-kotlin">class LocationRepository(
    private val locationDataSource: LocationDataSource,
    private val externalScope: CoroutineScope
) {
    val locations: Flow&lt;Location&gt; = 
        locationDataSource.locationsSource.stateIn(externalScope, WhileSubscribed(), EmptyLocation)
}</code></pre>
<p>참조 : <a href="https://kotlinworld.com/233?category=973477">Dev World의 블로그</a> </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[SQLite와 Room의 기초]]></title>
            <link>https://velog.io/@hyejiseo-dev/SQLite%EC%99%80-Room%EC%9D%98-%EA%B8%B0%EC%B4%88</link>
            <guid>https://velog.io/@hyejiseo-dev/SQLite%EC%99%80-Room%EC%9D%98-%EA%B8%B0%EC%B4%88</guid>
            <pubDate>Sun, 05 Jun 2022 15:25:50 GMT</pubDate>
            <description><![CDATA[<h3 id="📍-sqlite-란">📍 SQLite 란?</h3>
<blockquote>
<p>android, ios에 채택
 android API에 내장된 버전이 있음
 오류가 쉽게 나오고 쿼리가 복잡함</p>
</blockquote>
<h4 id="💡-안드로이드에서의-sqlite-사용">💡 안드로이드에서의 SQLite 사용</h4>
<p>SQLite를 사용하기 위해서는 DB를 만들고 그 DB에 대해 SQL query를 전달하여 CRUD를 수행하면 됩니다. 안드로이드에서는 SQLiteDatabase 클래스를 이용해, 작성한 데이터베이스에 대해 CRUD를 수행할 수 있습니다.</p>
<p>그런데 구글 공식문서인 Save data using SQLite에서는 다음과 같은 이유로 이 작업을 직접 수행하는것을 권하지 않고 있습니다.</p>
<blockquote>
<p>원시 SQL 쿼리에 관한 컴파일 시간 확인이 없습니다. 따라서 데이터 그래프가 변경됨에 따라 영향을 받는 SQL 쿼리를 수동으로 업데이트해야 합니다. 이 과정은 시간이 오래 걸리고 오류가 쉽게 발생할 수 있습니다.
SQL 쿼리와 데이터 객체 간에 변환하려면 많은 상용구 코드를 사용해야 합니다.</p>
</blockquote>
<h3 id="📍-room의-사용">📍 Room의 사용</h3>
<blockquote>
<p>구글에서는 sqlite를 안전하게 다루기 위해 room을 만듬</p>
</blockquote>
<p> <img src="https://velog.velcdn.com/images/hyejiseo-dev/post/ab5d97b5-2220-4558-a462-3401ed9b4525/image.png" alt=""></p>
<ul>
<li><p><strong>오브젝트 매핑하여 entity 정의함</strong>
테이블명, 칼럼 명, 칼럼 타입, 기본 키, 외래 키 등등을 정의할 수 있게하는 다양한 annotation을 제공합니다.</p>
</li>
<li><p>** Dao 간접적인 조작을 한다 (쿼리를 사용)**
데이터베이스에 접근하는 데 필요한 메서드를 포함합니다.
데이터 삽입, 삭제, 업데이트, 쿼리등등을 사용할 수 있게 하는 다양한 annotation을 제공합니다.</p>
</li>
<li><p>** RoomDatabase 만들어 entitiy를 관리, Dao를 가짐**
RoomDatabase 클래스를 상속받는 abstract class여야 합니다.
@Database annotation안에 해당 database와 관련된 Entity 리스트를 포함해야 합니다. Arguments가 0개인 @Dao를 반환하는 abstract method를 포함해야 합니다.</p>
</li>
<li><p>** 장점**
보일러 플레이트 코드 감소, 컴파일에 쿼리 체크, 테스트 용이, 데이터베이스를 메인 스레드에서 다루지 않음</p>
</li>
</ul>
<p>참조 : <a href="https://medium.com/%EC%8A%AC%EA%B8%B0%EB%A1%9C%EC%9A%B4-%EA%B0%9C%EB%B0%9C%EC%83%9D%ED%99%9C/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-room-%EC%82%AC%EC%9A%A9%EB%B2%95-1b7bd07b4cee">안드로이드 Room 사용법</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Kotlin: Coroutine 기초]]></title>
            <link>https://velog.io/@hyejiseo-dev/Kotlin-Coroutine-%EA%B8%B0%EC%B4%88</link>
            <guid>https://velog.io/@hyejiseo-dev/Kotlin-Coroutine-%EA%B8%B0%EC%B4%88</guid>
            <pubDate>Fri, 03 Jun 2022 14:23:21 GMT</pubDate>
            <description><![CDATA[<h3 id="📍-코루틴과-스레드">📍 코루틴과 스레드</h3>
<h4 id="1-특징">1. 특징</h4>
<ul>
<li>메모리 구조의 차이
프로세스 안에 메모리 할당받음 - 스레드 (스택할당)
코루틴은 메모리를 할당을 받지 않고 힙(heap) 영역을 공유해서 사용 = 함수와 비슷함</li>
<li>코루틴은 비선점형 멀티태스킹이다 : 동시에 돌아가도 실제로는 같은 시간에 수행되지 않는 것이다. 전환되며 속도가 빨라서 동시에 수행되는 것 처럼 보인다<h4 id="2-장점-메모리-오버헤드">2. 장점 (메모리, 오버헤드)</h4>
3개의 스레드를 코루틴으로 대체하면, 메모리를 할당하지 않아도 됨
분리된 영역을 처리할때 그 사이에서 데이터의 참조를 하는 것(context switching)을 하는 것이 아님 = 오버헤드가 생기지 않음<h4 id="3-사용법">3. 사용법</h4>
suspend를 붙여서 쓴다 / 중간에 다른 작업이 가능!
(중간에 멈출 수 있는 함수라는 뜻)</li>
</ul>
<h3 id="📍-코루틴-구조">📍 코루틴 구조</h3>
<ul>
<li>Coroutine Scope : 코루틴이 어떤 범위에서 동작할 지</li>
<li>Coroutine Context : 어떤 스레드에서 코루틴을 사용할 지 </li>
<li>Coroutine Builder : 작업을 수행하여 값을 리턴할 지</li>
</ul>
<h4 id="💡-coroutine-scope-vs-globalscope">💡 Coroutine Scope vs GlobalScope</h4>
<ul>
<li>보통 스코프는 쓰고 버리고 가능 / global scope는 최상위 레벨에서 사용, 싱글톤이어서 만들어서 쓰고 버리고.. 하지 못함 (추천하지 않음)</li>
</ul>
<h4 id="💡-coroutine-context의-구성">💡 Coroutine Context의 구성</h4>
<p>*<em>1. Dispatchers *</em>: 코루틴이 실행되는 스레드를 지정한다 (4가지 타입)</p>
<ul>
<li>Default : CPU 연산을 많이 필요하는 작업에서 사용</li>
<li>IO : 파일, 네트워크 작업에서 사용</li>
<li>Main : UI 스레드(메인스레드) UI 관련 변경 시 사용</li>
<li>Uncomfined : 일반적으로 사용하지 않음</li>
</ul>
<p>** 2. Job &amp; Deffered**
 코루틴이라는 흐름을 job 이라는 오브젝트로 사용
 = 오브젝트를 취소, 예외처리를 가능하게 하여 작업을 컨트롤 가능하게 해준다 (흐름제어 가능)</p>
<pre><code>Job states cycle
                                          wait children
    +-----+ start  +--------+ complete   +-------------+  finish  +-----------+
    | New | -----&gt; | Active | ---------&gt; | Completing  | -------&gt; | Completed |
    +-----+        +--------+            +-------------+          +-----------+
                     |  cancel / fail       |
                     |     +----------------+
                     |     |
                     V     V
                 +------------+                           finish  +-----------+
                 | Cancelling | --------------------------------&gt; | Cancelled |
                 +------------+                                   +-----------+
</code></pre><h4 id="3-coroutine-builder">3. Coroutine Builder</h4>
<ul>
<li>launch : 코루틴 시작, Job 객체 반환</li>
<li>async : 코루틴 시작 가능 - Deffered 객체 반환 </li>
<li><blockquote>
<p>async는 마지막에 숫자를 반환한다 / 메인 스레드에서 관계 없는 동작이 가능</p>
</blockquote>
</li>
<li>runblocking : 사용 안함 / 메인 스레드에 영향을 준다</li>
<li>withContext : Dispatcher switch - 오버헤드가 적음</li>
</ul>
<pre><code class="language-kotlin">suspend fun fetchDocs() {                      // Dispatchers.Main
    val result = get(&quot;developer.android.com&quot;)  // Dispatchers.Main
    show(result)                               // Dispatchers.Main
}

suspend fun get(url: String) =                 // Dispatchers.Main
    withContext(Dispatchers.IO) {              // Dispatchers.IO (main-safety block)
        /* perform network IO here */          // Dispatchers.IO (main-safety block)
    }                                          // Dispatchers.Main
}</code></pre>
<h4 id="💡-코루틴-지연">💡 코루틴 지연</h4>
<ul>
<li>delay : 정해진 시간 동안 코루틴 처리 불가 (대기상태)</li>
<li>join : launch로 실행한 코루틴은 join으로 대기 가능</li>
<li>await : async로 실행한 코루틴은 await로 대기 가능 / deffered 값을 반환하게 된다</li>
</ul>
<h4 id="💡-코루틴-취소">💡 코루틴 취소</h4>
<ul>
<li>cancel : job을 Cancelling (transient state)로 변화</li>
<li>cancelAndJoin : job을 캔슬하고 Cancelled가 될때 까지 기다림</li>
<li>withTimeout : 제한시간을 설정하고 그때까지 처리가 끝나지 않았을 경우 블럭을 취소하고 TimeoutCancellationException을 throw</li>
<li>withTimeoutOrNull : 제한 시간 안에 처리 못할 경우 null 반환</li>
</ul>
<h4 id="💡-코루틴-사용">💡 코루틴 사용</h4>
<p>스코프는 CoroutineScope를 사용합니다. CPU 작업이냐 IO 작업이냐를 판단하여 Dispatchers를 정의하고 마지막으로 코루틴 처리후 값이 나와야 되는가 아닌가에 따라 launch나 async를 구별해서 사용하면 되겠습니다.</p>
<p>참조 : <a href="https://cliearl.github.io/posts/android/coroutine-principle/">냉동코더의 기술블로그</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Kotlin : Singleton Pattern 이란]]></title>
            <link>https://velog.io/@hyejiseo-dev/Kotlin-Singleton-Pattern-%EC%9D%B4%EB%9E%80</link>
            <guid>https://velog.io/@hyejiseo-dev/Kotlin-Singleton-Pattern-%EC%9D%B4%EB%9E%80</guid>
            <pubDate>Wed, 01 Jun 2022 13:35:59 GMT</pubDate>
            <description><![CDATA[<h3 id="singleton-pattern">Singleton Pattern</h3>
<blockquote>
<p>클래스 인스턴스를 단 하나만 만들어야 할 경우 사용</p>
</blockquote>
<p>코틀린은 object를 사용한다</p>
<pre><code class="language-kotlin">object DBHandler {...}
val dbHandler = DBHandler</code></pre>
<ul>
<li>object를 사용하면 인스턴스를 생성할 때 파라미터를 전달할 수가 없다는 한계</li>
<li>파라미터를 전달하기 위해서 자바의 static을 companion object로 구현</li>
</ul>
<pre><code class="language-kotlin">class DBHandler private constructor(context: Context) {
    companion object {
        private var instance: DBHandler? = null

        fun getInstance(context: Context) =
            instance ?: DBHandler(context).also {
                instance = it
            }
    }
}</code></pre>
<p>스래드가 인스턴스를 확인하고 null이면 새로 만들고 아니면 그대로 반환, 인스턴스는 하나만 가지게 된다!</p>
<p>두개의 스레드가 동시에 인스턴스 상태를 확인하면, 객체가 중복 생성 할 수 있게 되어 싱글턴이 깨진다 (스레드 동기화 문제) 
-&gt;  <strong>double checked locking</strong></p>
<pre><code class="language-kotlin">class DBHandler private constructor(context: Context) {
    companion object {
        @Volatile //인스턴스가 메인 메모리를 바로 참조하여 인스턴스 중복 생성 방지
        private var instance: DBHandler? = null

        //동시성 체크 - 각 스레드가 동시에 실행되지 못하도록
        fun getInstance(context: Context) =
            instance ?:   synchronized(DBHandler::class.java) { 
                instance ?: DBHandler(context).also {
                    instance = it
                }
            }
    }
}
</code></pre>
<p>참조 : <a href="https://cliearl.github.io/posts/android/understanding-singleton-pattern/">https://cliearl.github.io/posts/android/understanding-singleton-pattern/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Android의 Http 통신에 대하여]]></title>
            <link>https://velog.io/@hyejiseo-dev/Android%EC%9D%98-Http-%ED%86%B5%EC%8B%A0%EC%97%90-%EB%8C%80%ED%95%98%EC%97%AC</link>
            <guid>https://velog.io/@hyejiseo-dev/Android%EC%9D%98-Http-%ED%86%B5%EC%8B%A0%EC%97%90-%EB%8C%80%ED%95%98%EC%97%AC</guid>
            <pubDate>Wed, 01 Jun 2022 13:17:41 GMT</pubDate>
            <description><![CDATA[<h3 id="📍-소켓-연결">📍 소켓 연결</h3>
<ul>
<li>소켓: 두 프로그램 사이에 일어나는 한쪽의 앤드포인트</li>
<li>클라이언트 &amp; 서버가 특정 포트를 통해 연결 유지 (동영상 스트리밍, 온라인 게임)</li>
</ul>
<h3 id="📍-http-연결">📍 http 연결</h3>
<ul>
<li>80번 포트를 사용해 정보를 주고받는 프로토콜 </li>
<li>클라이언트가 서버에 헤더/바디로 이루어진 메세지 요청(request)</li>
<li>서버가 처리하여 응답(response)반환</li>
<li>connectionless : 통신이 필요할때만 연결 / stateless: 클라이언트가 서버를 기억해야하면 쿠키,세션으로 구분</li>
</ul>
<p><img src="https://velog.velcdn.com/images/hyejiseo-dev/post/5bad075c-a97e-4412-8eeb-2ffba07a6345/image.png" alt=""></p>
<h3 id="📍-rest-api">📍 Rest Api</h3>
<blockquote>
<p>http기반으로 필요한 자원에 접근하는 네트워트 아키텍쳐</p>
</blockquote>
<ul>
<li>클라이언트와 서버의 분리</li>
<li>무상태(Stateless)</li>
<li>캐시 처리가 가능해야 함</li>
<li>시스템이 계층화(Layered) 되어있어야 함</li>
<li>일관성 있는 인터페이스</li>
</ul>
<p><img src="https://velog.velcdn.com/images/hyejiseo-dev/post/94f57fda-cf87-4919-a21c-86c4d5b6d558/image.png" alt=""></p>
<h3 id="📍-android에서-http-통신">📍 Android에서 Http 통신</h3>
<ul>
<li>httpClient &gt; HttpUrlConnection &gt; Volley(2013 출시) &gt; 최근 OkHttp 발표</li>
</ul>
<h4 id="retrofit">Retrofit</h4>
<ul>
<li>OkHttp : Retrofit</li>
<li>squareup에서 발표한 라이브러리</li>
<li>인터페이스 생성 &gt; 인스턴스 &gt; 동기/비동기적 구성</li>
<li>Ktor : 코틀린을 통해 비동기 서버를 구축</li>
</ul>
<pre><code class="language-java">public interface GitHubService {
  @GET(&quot;users/{user}/repos&quot;)
  Call&lt;List&lt;Repo&gt;&gt; listRepos(@Path(&quot;user&quot;) String user);
}

Retrofit retrofit = new Retrofit.Builder()
    .baseUrl(&quot;https://api.github.com/&quot;)
    .build();
GitHubService service = retrofit.create(GitHubService.class);

Call&lt;List&lt;Repo&gt;&gt; repos = service.listRepos(&quot;octocat&quot;);
</code></pre>
<p>Volley vs Retrofit -&gt; Retrofit 코드 가독성이 더 좋음..!
구글의 권장 앱 아키텍처에서는 HTTP 통신에 Volley가 아닌 Retrofit을 추천하고 있다
<img src="https://velog.velcdn.com/images/hyejiseo-dev/post/a52c7124-31f9-4ba3-8e8c-45104fdf658a/image.png" alt=""></p>
<p>출처 : <a href="https://cliearl.github.io/posts/android/android-http-library-review/">냉동코더의 기술블로그</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Kotlin : 스코프 함수, 람다함수란?]]></title>
            <link>https://velog.io/@hyejiseo-dev/Kotlin-%EC%8A%A4%EC%BD%94%ED%94%84-%ED%95%A8%EC%88%98-%EB%9E%8C%EB%8B%A4%ED%95%A8%EC%88%98%EB%9E%80</link>
            <guid>https://velog.io/@hyejiseo-dev/Kotlin-%EC%8A%A4%EC%BD%94%ED%94%84-%ED%95%A8%EC%88%98-%EB%9E%8C%EB%8B%A4%ED%95%A8%EC%88%98%EB%9E%80</guid>
            <pubDate>Mon, 09 May 2022 13:02:05 GMT</pubDate>
            <description><![CDATA[<h3 id="📍-람다함수">📍 람다함수</h3>
<p>람다함수 안에 여러 파라미터가 있으면 맨 마지막 값이 반환값이 된다
하나이면 it을 사용할 수 있다</p>
<pre><code class="language-java">
//1. 파라미터가 여러개인 람다함수
val calculate:(Int,Int) -&gt; Int = {a,b -&gt;
    print(a)
    print(b)
    a+b  //맨 마지막 값을 반환
}

//2. 파라미터가 없는 람다함수
val a:() -&gt; Unit = {println(&quot;파라미터가 없어요&quot;)}

//3. 파라미터가 하나인 람다함수
val c:(String) -&gt; Unit = {println(&quot;$it 람다함수&quot;)}</code></pre>
<h3 id="📍-스코프함수">📍 스코프함수</h3>
<blockquote>
<p>함수형 언어의 특징을 더 편리하게 사용할 수 있도록 기본 제공하는 함수들
instance의 속성이나 함수를 더 편하게 사용하도록 해준다</p>
</blockquote>
<p>종류는 apply, run, with, also, let 5가지가 있다</p>
<h4 id="1-apply">1. apply</h4>
<blockquote>
<p>인스턴스 생성 후 변수에 담기 전 초기화 과정 수행, main 함수와 별도의 스코프에서 인스턴스의 변수와 함수를 조작하여 코드가 깔끔해진다</p>
</blockquote>
<pre><code class="language-java">fun main() {
   val a Book(&quot;dada&quot;,10000).apply{
   name = &quot;name is&quot; + name
   discount()
}

class Book{
    fun discount(var name : String, var price: Int){
        price -= 2000
    }

}</code></pre>
<p><strong>2. run</strong></p>
<blockquote>
<p>인스턴스 대신 결과값을 반환한다. 이미 만들어진 후 인스턴스의 함수나 속성을 스코프로 쓸 수 있다</p>
</blockquote>
<pre><code class="language-java">fun main() {
   val a = Book(&quot;dada&quot;,10000).apply{
   name = &quot;name is&quot; + name
   discount()

   //run으로 변수의 접근가능
   a.run{
           println(&quot;상품명 : ${name}, 가격 : ${price}원&quot;)
   }
}

class Book{
    fun discount(var name : String, var price: Int){
        price -= 2000
    }

}</code></pre>
<h4 id="3-with">3. with</h4>
<p>run과 같은 기능, 인스턴스를 패러미터로 받는다는 차이가 있다</p>
<p>a.run{} / with(a){}</p>
<h4 id="4-also--let">4. also / let</h4>
<p>각각 apply, run 같은 기능을 한다
<strong>처리가 끝나면 인스턴스 반환 apply / also
처리가 끝나면 최종 값 반환 run / let</strong></p>
<ul>
<li>차이점은 <strong>it</strong>으로 인스턴스를 사용해야한다 : 같은 이름의 변수, 함수가 스코프 밖에 중복되어 있으면 혼란이 있을 수 있다</li>
</ul>
<pre><code class="language-java">a.let{
    println(&quot;상품명 : ${it.name}, 가격 : ${it.price}원&quot;)
}</code></pre>
<p><em><strong>스코프 함수는 인스턴스의 속성과 함수를 scope안에서 분리해 사용하여 코드의 가독성을 향상해준다!</strong></em></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Kotlin 변수 : 상수, lateinit, lazy]]></title>
            <link>https://velog.io/@hyejiseo-dev/Kotlin-%EB%B3%80%EC%88%98-%EC%83%81%EC%88%98-lateinit-lazy</link>
            <guid>https://velog.io/@hyejiseo-dev/Kotlin-%EB%B3%80%EC%88%98-%EC%83%81%EC%88%98-lateinit-lazy</guid>
            <pubDate>Mon, 09 May 2022 12:37:54 GMT</pubDate>
            <description><![CDATA[<h3 id="📍-kotlin의-변수">📍 kotlin의 변수</h3>
<ul>
<li><p><strong>var</strong> : 한번 할당 된 객체를 다른 객체로 변경하여 사용할 수 있다</p>
<pre><code class="language-java">var p = Person(&quot;hyeji&quot;,2004)
p = Person(&quot;eunji&quot;,1998)</code></pre>
</li>
<li><p><strong>val</strong> : 한번 할당하면 변경할 수 없다 (단, 객체 내부 속성은 변경가능)</p>
<pre><code class="language-java">val a = Person(&quot;key&quot;,2000)</code></pre>
</li>
<li><p>*<em>상수 (const) *</em>: 컴파일 시점에 결정되어 절대 변경이 불가하다 (기본 자료형만 선언 가능)</p>
<pre><code class="language-java">const val CONST_INT = 1234
</code></pre>
</li>
</ul>
<p>//반드시 companion object 안에 선언해야한다!
class Sample{
    companion object{
        const val CONST_A = 1312
    }
}</p>
<pre><code>- 사용할 경우 Sample.CONST_A **고정적인 값**으로만 사용한다
- 상수의 이름은 대부분 대문자와 언더바를 사용한다
- 변수는 런타임 시 시간이 더 소요되기 때문에 고정적으로 사용할 값을 선언해주어 성능을 향상시킬 수 있다!


### 📍 lateinit (늦은 초기화)

#### Q. 자료형만 지정하고 나중에 객체를 할당하려면...
&gt; 선언과 할당을 동시에 할 수 없을 경우에는 **lateinit**을 사용하여 먼저 선언해두고, 나중에 할당하도록 할 수 있다!

```java
//먼저 선언만 해둔다
lateinit var a : Int</code></pre><ul>
<li>선언 후 할당하기 전 까지는 변수 사용이 불가하다</li>
<li>기본 자료형에는 사용할 수 없다 (String 가능)</li>
<li>초기화 되었는지 확인하는 방법은 변수 앞에 &quot;::&quot;을 붙이고 .isInitalized를 사용하면 된다 (오류방지)<pre><code class="language-java">::a.isInitialized</code></pre>
</li>
</ul>
<h3 id="📍-lazy-지연-대리자-속성">📍 lazy (지연 대리자 속성)</h3>
<blockquote>
<p>변수를 사용하는 시점까지 초기화를 늦춰준다. 코드상에서는 선언과 동시에 초기화 되는 것 처럼 보이지만 실제 실행하면 사용될 때 초기화 된다 </p>
</blockquote>
<pre><code class="language-java">//선언
val a :Int by lazy{7}
.
.
.
print(a) //이때 실제로 초기화 된다!</code></pre>
<ul>
<li>코드의 실행 시간을 <strong>최적화</strong> 할 수 있다</li>
<li>람다함수 형태이고, 여러 할당자 중 맨 마지막 구문이 최종적으로 지정된 값이다</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Android : Progress Dialog 사용하기(Thread, Handler 이해)]]></title>
            <link>https://velog.io/@hyejiseo-dev/Android-Progress-Dialog-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0Thread-Handler-%EC%9D%B4%ED%95%B4</link>
            <guid>https://velog.io/@hyejiseo-dev/Android-Progress-Dialog-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0Thread-Handler-%EC%9D%B4%ED%95%B4</guid>
            <pubDate>Tue, 29 Mar 2022 08:32:30 GMT</pubDate>
            <description><![CDATA[<h3 id="📍-progressdialog-란">📍 ProgressDialog 란?</h3>
<p>ProgressDialog는 안드로이드가 어떤 작업을 진행할때 그 작업의 <strong>진행상황과 부가적인 문장</strong>을 팝업창으로 보여주는 모듈입니다. 시간이 걸리는 파일 다운로드나 복사를 수행할 때 이런창을 띄워주면 기다리는데 유저의 거부감을 줄일 수 있습니다.</p>
<p><img src="https://images.velog.io/images/hyejiseo-dev/post/73dbddc5-8547-48e9-b231-05157c98530e/image.png" alt=""></p>
<ul>
<li>ProgressDialog는 다이얼로그가 표시되는 동안 유저가 다른 조작을 못하게 된다는 이유로 <strong>Android 8.0(API level 26)부터는 deprecated 되고 대신 ProgressBar를 사용하도록 권장하고 있습니다.</strong></li>
</ul>
<h3 id="프로그램-작성">프로그램 작성</h3>
<p>버튼을 터치하면 진행율이 0.2초당 10%씩 증가하는 ProgressDialog를 만들어 보면서 Thread를 구현해 보겠습니다.</p>
<pre><code class="language-java">//초기화
private lateinit var progressDialog: ProgressDialog

//프로그래스 설정 (초기값, 스타일)
progressDialog = ProgressDialog(this)
progressDialog.max = 100
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL)
progressDialog.setTitle(&quot;Download Task&quot;)
progressDialog.setMessage(&quot;Please wait, we are downloading your files...&quot;)
progressDialog.setCancelable(false)
progressDialog.show()
</code></pre>
<h3 id="📍-스레드와-핸들러">📍 스레드와 핸들러</h3>
<p><img src="https://images.velog.io/images/hyejiseo-dev/post/452633fd-990c-45a5-911b-824b48018db5/image.png" alt=""></p>
<p>출처 : <a href="https://academy.realm.io/kr/posts/android-thread-looper-handler/">https://academy.realm.io/kr/posts/android-thread-looper-handler/</a></p>
<p>한 프로세스 안에서 여러개의 스레드를 사용하게 되면 서로다른 스레드가 버튼이나 텍스트뷰 등의 UI 컴포넌트에 동시에 접근하는 문제가 생길 수 있습니다. 안드로이드에서는 이것을 막기 위해 처리흐름 중간에 Looper와 Handler를 끼워 넣었습니다.</p>
<p>스레드는 수행해야 할 UI 작업이 발생하면 Message 또는 Runnable을 만들어내고 이걸 Message queue에 담습니다. Looper는 무한히 루프를 돌며 Message queue속의 객체를 핸들러에 전달합니다. Handler는 메시지를 받으면 운영체제가 정해주는 타이밍에 따라 UI조작을 수행하게 되므로 UI요소에 대한 동시접근을 막을 수 있게 됩니다.</p>
<pre><code class="language-java">val handler: Handler = object : Handler() {
    override fun handleMessage(msg: Message) {
        super.handleMessage(msg)
        progressDialog.incrementProgressBy(10)
    }
}

Thread {
    try {
        while (progressDialog.progress &lt;= progressDialog.max) {
            Thread.sleep(200)  //0.2초 간격으로 정지시킴
            handler.sendMessage(handler.obtainMessage())
            if (progressDialog.progress == progressDialog.max) {
                progressDialog.dismiss()
            }
        }
    } catch (e: Exception) {
        e.printStackTrace()
    }
}.start()
</code></pre>
<p>쓰레드에서는 매 루프마다 현재 progress 값을 체크하고 그 값이 최대값보다 작으면 핸들러에 메시지를 보냅니다. 이 때 Thread.sleep을 이용해 0.2초 간격으로 스레드를 정지시킵니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[자바 예외처리]]></title>
            <link>https://velog.io/@hyejiseo-dev/%EC%9E%90%EB%B0%94-%EC%98%88%EC%99%B8%EC%B2%98%EB%A6%AC</link>
            <guid>https://velog.io/@hyejiseo-dev/%EC%9E%90%EB%B0%94-%EC%98%88%EC%99%B8%EC%B2%98%EB%A6%AC</guid>
            <pubDate>Fri, 18 Mar 2022 07:24:39 GMT</pubDate>
            <description><![CDATA[<h3 id="1-error에러-vs-exception예외">1. Error(에러) vs Exception(예외)</h3>
<p><img src="https://images.velog.io/images/hyejiseo-dev/post/713c818d-bac1-45b8-ab6b-4ff6cc7d30a2/image.png" alt=""></p>
<p>Checked Exception은 컴파일을 할 경우에 예측가능한 예외로 ClassNotFoundException, FileNotFoundException 등이 있습니다.
UnChecked Exception은 런타임 시 발생할 수도 있고 발생하지 않을 수 있는 예외로 ArithmeticException, NullpointerException 등이 있습니다.</p>
<h3 id="2-예외처리란">2. 예외처리란?</h3>
<p>프로그래밍을 하면서 에러와 예외는 필연적으로 만나게 되는 존재입니다.
잘못된 코딩으로 인해 예외가 발생하더라도 무시하고 다음 코드를 실행하거나 다른 메소드를 호출하는 것처럼 다양하게 사용자가 원하는 예외처리를 할 수 있습니다.
try, catch, finally, throw, throws 다음 키워드를 이용하여 예외처리를 합니다.</p>
<p>**    ① try - catch 구문을 이용하여 메소드 내에서 예외를 처리하도록 정의한다.
② 키워드 throws 를 이용하여 메소드를 호출한 영역으로 예외가 전달되도록 정의한다.**</p>
<h4 id="java-예외처리-종류-exception">JAVA 예외처리 종류 (Exception)</h4>
<pre><code>ArthmeticException  : 정수를 0으로 나눌때 발생
NullPointException  : Null 레퍼런스를 참조할때 발생
ClassCastException  : 변환할 수 없는 타입으로 객체를 변환할 때 발생
OutOfMemoryException  : 메모리가 부족한 경우 발생 
ArrayIndexOfBoundsException  : 배열의 범위를 벗어난 접근 시 발생
IllegalArgumentException : 잘못된 인자 전달 시 발생
IOException : 입출력 동작 실패 또는 인터럽트 시 발생
NumberFormatException  : 문자열이 나타내는 숫자와 일치하지 않는 타입의 숫자로 변환시 발생
IllegalStateException  : 객체의 상태가 매소드 호출에는 부적절한 경우 
ConcurrentModificationException : 금지된 곳에서 객체를 동시에 수정하는것이 감지될 경우 발생
UnsupportedOperationException : 객체가 메소드를 지원하지 않는 경우 발생</code></pre><pre><code class="language-java"> // 파일이 존재하지 않는 경우에 대한 예외 처리
    catch (FileNotFoundException e) {
        String exceptionMessage = textFileName + &quot; 파일 또는 그것의 경로가 존재하지 않습니다.&quot;;
        Toast.makeText(this.context, exceptionMessage, Toast.LENGTH_SHORT).show();
        e.printStackTrace();
    }

// 입출력 관련된 예외 처리
    catch (IOException e) {
        String exceptionMessage = &quot;파일을 읽는 도중에 오류가 발생했습니다.&quot;;
        Toast.makeText(this.context, exceptionMessage, Toast.LENGTH_SHORT).show();
        e.printStackTrace();
    }

// 기타 예외 처리
    catch (Exception e) {
        Toast.makeText(this.context, &quot;알 수 없는 오류입니다.&quot;, Toast.LENGTH_SHORT).show();
        e.printStackTrace();
    }
</code></pre>
<pre><code>e.getMessage() = 에러 이벤트와 함께 들어오는 메세지를 출력한다.
e.getMessage(): 출력문구

e.toString() = 에러 이벤트의 toString()을 호출해서 간단한 에러 메시지를 확인한다.
e.toString(): java.lang.Exception: 출력문구

e.printStackTrace() = 에러 메세지의 발생 근원지를 찾아서 단계별로 에러를 출력한다.
e.printStackTrace(): java.lang.Exception: 출력문구</code></pre><p>출처 : <a href="https://math-coding.tistory.com/170">https://math-coding.tistory.com/170</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Android : 프로젝트 버전, 빌드 넘버]]></title>
            <link>https://velog.io/@hyejiseo-dev/Android-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EB%B2%84%EC%A0%84-%EB%B9%8C%EB%93%9C-%EB%84%98%EB%B2%84</link>
            <guid>https://velog.io/@hyejiseo-dev/Android-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EB%B2%84%EC%A0%84-%EB%B9%8C%EB%93%9C-%EB%84%98%EB%B2%84</guid>
            <pubDate>Fri, 04 Mar 2022 13:15:39 GMT</pubDate>
            <description><![CDATA[<h3 id="버전-넘버">버전 넘버</h3>
<p><strong>일반적인 규칙은 1.0.0으로 세자리로 결정함</strong></p>
<ul>
<li>첫째자리 : 뒤로 되돌릴수 없는 수준의 큰 변경이 있을때</li>
<li>둘째자리 : 여러가지 새 기능 추가 시</li>
<li>셋째자리 : 자잘한 버그 픽스</li>
</ul>
<h3 id="빌드-넘버">빌드 넘버</h3>
<p>: 어떤 버전을 출시 하기 위해 반복한 빌드의 횟수 기록
빌드 수행 시 마다 변화해야 한다.</p>
<h4 id="프로젝트의-버전넘버와-빌드넘버를-자동으로-업데이트-시켜주는-코드-만들기">프로젝트의 버전넘버와 빌드넘버를 자동으로 업데이트 시켜주는 코드 만들기</h4>
<pre><code class="language-java">android {
    defaultConfig {
        versionCode 1
        versionName &quot;1.0&quot;
    }
}
</code></pre>
<p>여기서 버전 코드와 빌드 넘버를 알 수 있음
version.properties 파일에 저장시킨다. </p>
<pre><code>VERSION_NUMBER=&#39;1.0.0&#39;
VERSION_BUILD=1
</code></pre><p>마지막으로 읽어온 후에 자동으로 업데이트 하는 코드를 추가한다.</p>
<pre><code class="language-java">android {
    // 저장된 값을 불러오는 부분
    def versionPropsFile = file(&#39;version.properties&#39;)
    def versionBuild
    def versionNumber
    if (versionPropsFile.canRead()) {
        def versionProps = new Properties()
        versionProps.load(new FileInputStream(versionPropsFile))
        versionBuild = versionProps[&#39;VERSION_BUILD&#39;].toInteger()
        versionNumber = versionProps[&#39;VERSION_NUMBER&#39;].toString()
    } else {
        throw new FileNotFoundException(&quot;Could not read version.properties!&quot;)
    }

    // 읽어온 빌드넘버를 증가시키는 부분
    ext.autoIncrementBuildNumber = {
        if (versionPropsFile.canRead()) {
            def versionProps = new Properties()
            versionProps.load(new FileInputStream(versionPropsFile))
            versionBuild = versionProps[&#39;VERSION_BUILD&#39;].toInteger() + 1
            versionProps[&#39;VERSION_BUILD&#39;] = versionBuild.toString()
            versionProps.store(versionPropsFile.newWriter(), null)
        } else {
            throw new FileNotFoundException(&quot;Could not read version.properties!&quot;)
        }
    }

    // 함수를 실행시키는 부분
    gradle.taskGraph.whenReady { taskGraph -&gt;
        if (taskGraph.hasTask(assembleDebug)) {
            autoIncrementBuildNumber()
        } else if (taskGraph.hasTask(assembleRelease)) {
            autoIncrementBuildNumber()
        }
    }

    compileSdkVersion 29

    // 수정한 내용을 앱에 반영해 줌
    defaultConfig {
        applicationId &quot;com.example.autobuildnum&quot;
        minSdkVersion 21
        targetSdkVersion 29
        versionCode versionBuild
        versionName versionNumber

        testInstrumentationRunner &quot;androidx.test.runner.AndroidJUnitRunner&quot;
    }
}
</code></pre>
<p><a href="https://cliearl.github.io/posts/android/autoset-buildnumber/">+ 참고자료 링크</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Android : thread와 postDelayed 실행(지연처리)]]></title>
            <link>https://velog.io/@hyejiseo-dev/Android-thread%EC%99%80-postDelayed-%EC%8B%A4%ED%96%89%EC%A7%80%EC%97%B0%EC%B2%98%EB%A6%AC</link>
            <guid>https://velog.io/@hyejiseo-dev/Android-thread%EC%99%80-postDelayed-%EC%8B%A4%ED%96%89%EC%A7%80%EC%97%B0%EC%B2%98%EB%A6%AC</guid>
            <pubDate>Tue, 04 Jan 2022 08:44:49 GMT</pubDate>
            <description><![CDATA[<h3 id="thread-란">Thread 란?</h3>
<p>프로세스가 시작될 때, 최초의 실행 시작점이 되는 main() 함수, 그리고 그 곳부터 순차적으로 진행되는 실행 흐름, 이 또한 하나의 스레드이며, 메인 스레드(Main Thread)라고 부른다</p>
<h4 id="1-스레드를-만드는-방법-1">1) 스레드를 만드는 방법 1</h4>
<p>Thread 클래스를 상속(extends)한 클래스를 만들고 run() 메서드를 오버라이드(override)한 다음, 클래스 인스턴스를 생성하고 start() 메서드를 호출</p>
<pre><code class="language-java">  class NewThread extends Thread {
        NewThread() {

        }

        public void run() {
            // TODO : thread running codes.
        }
    }

    NewThread nt = new NewThread() ;
    nt.start() ;</code></pre>
<h4 id="2-스레드를-만드는-방법-2">2) 스레드를 만드는 방법 2</h4>
<p>Runnable 인터페이스를 구현(implements)하는 클래스를 선언하고 run() 메서드를 구현한 다음, 클래스 인스턴스를 Thread 클래스 인스턴스의 생성자에 전달하고 Thread 클래스 인스턴스의 start() 메서드를 호출</p>
<pre><code class="language-java">   class NewRunnable implements Runnable {
        NewRunnable() {

        }

        public void run() {
            // TODO : thread running codes.
        }
    }

    NewRunnable nr = new NewRunnable() ;
    Thread t = new Thread(nr) ;
    t.start() ;</code></pre>
<h3 id="q-스레드-만드는-방법-두가지의-차이점은">Q. 스레드 만드는 방법 두가지의 차이점은?</h3>
<p><img src="https://images.velog.io/images/hyejiseo-dev/post/6210246d-f474-4577-9a96-9ff5f413496f/image.png" alt=""></p>
<ul>
<li><p>첫번째 방법, thread 메서드를 상속(extends)하는 방법은 비효율적 일 수 있다! &gt; why? 
위의 방법에서는 run() 메소드만을 override한다. 다른 기능들도 재사용 되어야 하는데, 하나만 사용하는 것은 좀 비효율적인 느낌이다.
그리고 한번에 하나씩 상속되는 특성에 따라, Thread가 아닌 다른 클래스를 상속해야 한다면, 다중상속이 허용되지 않는 자바에서 Thread 클래스를 상속(extends)하여 스레드를 만드는 것이 불가능하다!</p>
</li>
<li><p>두번째 방법, Runnable 인터페이스룰 사용하면, abstract로 선언된 단 하나의 메서드, run() 메서드만을 가지기 때문에 효율적일 수 있다. 하지만 run()이외에 다른 것을 쓰려면 thread 메서드가 필요하다!</p>
</li>
</ul>
<h3 id="android의-메인-스레드">Android의 메인 스레드</h3>
<blockquote>
<p>안드로이드 앱의 메인 UI 스레드 동작은 루프(Loop)와 핸들러(Handler) 클래스 구현을 통해 메인 UI 스레드를 작성할 수 있게 해준다.</p>
</blockquote>
<ul>
<li>Handler : 메시지 수신 시 그 처리를 담당, Handler는 Looper가 가진 메시지 큐(Message Queue)를 다룰 수 있기 때문에, 새로운 메시지를 보내거나, 수신된 메시지에 대한 처리를 담당하는 주체가 되는 것</li>
</ul>
<h3 id="postdelayed-사용">postdelayed 사용</h3>
<p>안드로이드 앱 개발 시 일정시간 지연 후 무엇인가를 실행해야 하는 경우에 쓰는 방법이다.</p>
<pre><code class="language-java">import android.os.Handler;

Handler(Looper.getMainLooper()).postDelayed({
    //Do something
}, 1000) //1초 후 실행

Handler(Looper.getMainLooper()).postDelayed({
    //Do something
}, 4000) //위쪽 코드 실행 후 3초 후 실행</code></pre>
<p>작업을 일정한 시간 후에 실행해야 할 경우 넣어주면 좋다!</p>
<p>추가로, 일정시간 동안 멈추는 상태</p>
<pre><code class="language-java">Thread.sleep(1000);</code></pre>
<p>출처 : <a href="https://recipes4dev.tistory.com/143">https://recipes4dev.tistory.com/143</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Android kotlin : Data 클래스]]></title>
            <link>https://velog.io/@hyejiseo-dev/Android-kotlin-Data-%ED%81%B4%EB%9E%98%EC%8A%A4</link>
            <guid>https://velog.io/@hyejiseo-dev/Android-kotlin-Data-%ED%81%B4%EB%9E%98%EC%8A%A4</guid>
            <pubDate>Mon, 03 Jan 2022 05:35:41 GMT</pubDate>
            <description><![CDATA[<h3 id="data클래스">Data클래스</h3>
<p>kotlin에서 아주 유용하고 간편하게 사용할 수 있는 클래스이다. kotlin의 데이터 클래스는 기본적으로 getter와 setter를 자동으로 제공해주며 이 외에도 다양한 기능들을 제공해준다.</p>
<pre><code class="language-java">data class KotlinPerson(var name: String, var age: Int)</code></pre>
<p>위와 같이 선언해주면 된다. </p>
<pre><code class="language-java">//선언
var kotlinPerson = KotlinPerson(&quot;hyeals&quot;, 22)

//사용법
kotlinPerson.name = &quot;hhhhh&quot;
kotlinPerson.age = 25</code></pre>
<p>자바는 .getperson, .setperson등으로 호출하지만 코틀린은 프로퍼티의 이름으로 접근하면 된다.</p>
<pre><code class="language-java">//클래스 복사 기능
 var kotlinPerson_copy = kotlinPerson.copy()

// 문자열 변환
kotlinPerson.toString()
kotlinPerson_copy.toString()</code></pre>
<p>copy()와 toString()함수로 간단하게 재사용할 수 있다.</p>
<p>종합해서 활용한 결과는 아래와 같다</p>
<pre><code class="language-java">    var kotlinPerson = KotlinPerson(&quot;hyeals&quot;, 22)
    var kotlinPerson_copy = kotlinPerson.copy()

    kotlinPerson.name = &quot;hhhhh&quot;
    kotlinPerson.age = 25

    println(kotlinPerson.toString())
    println(kotlinPerson_copy.toString())</code></pre>
<p><img src="https://images.velog.io/images/hyejiseo-dev/post/9a610421-3a34-4d64-b52e-e250e1b268f9/image.png" alt=""></p>
<p>출처 : <a href="https://hyeals.tistory.com/60?category=837054">https://hyeals.tistory.com/60?category=837054</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Android : RxJava에 대하여]]></title>
            <link>https://velog.io/@hyejiseo-dev/Android-RxJava%EC%97%90-%EB%8C%80%ED%95%98%EC%97%AC</link>
            <guid>https://velog.io/@hyejiseo-dev/Android-RxJava%EC%97%90-%EB%8C%80%ED%95%98%EC%97%AC</guid>
            <pubDate>Mon, 03 Jan 2022 05:34:36 GMT</pubDate>
            <description><![CDATA[<h3 id="rxjava란">RxJava란?</h3>
<ul>
<li>RxAndroid 는 RxJava에 안드로이드용 스케쥴러 등 몇 가지 클래스를 추가해 안드로이드 개발을 쉽게 해주는 라이브러리이다. </li>
<li>RxJava 는 ReactiveX(Reactive Extensions)를 Java로 구현한 라이브러리</li>
</ul>
<blockquote>
<p><strong>Rxjava는 반응형 프로그래밍이다.</strong> 반응형이란 데이터가 변하면 알아서 캐치하여 결과로 반영된다. 따라서 데이터를 관찰할 수 있고 데이터를 스트림으로 처리한다.</p>
</blockquote>
<hr>
<h4 id="rx--observable--observers--schedulers">RX = Observable + Observers + Schedulers</h4>
<ul>
<li>Observable: Observable은 <strong>데이터 스트림</strong>입니다. Observable은 하나의 스레드에서 다른 스레드로 전달 할 데이터를 압축합니다. 주기적으로 또는 설정에 따라 생애주기동안 한번만 데이터를 방출합니다. Observable은 데이터를 처리하고 다른 구성요소에 전달하는 역할을 한다고 생각하면 됩니다.</li>
<li>Observers: Observers는 Observable에 의해 방출된 데이터 스트림을 <strong>소비</strong>합니다. Observers는 <strong>subscribeOn()</strong> 메서드를 사용해서 Observable을 <strong>구독</strong>하고 Observable이 방출하는 데이터를 <strong>수신</strong>할 수 있다. </li>
<li>Schedulers: Schedulers 는 Observable과 Observers 에게 그들이 실행되어야 할 <strong>스레드를 알려줍니다</strong>. observeOn() 메서드로 observers에게 관찰해야 할 스레드를 알려줄 수 있다. 또한, <strong>scheduleOn()</strong> 메서드로 observable이 실행해야 할 스레드를 알려줄 수 있다. </li>
</ul>
<h3 id="rxjava-예제">RxJava 예제</h3>
<ol>
<li>먼저 아래의 라이브러리를 설치해준다.<pre><code class="language-java">dependencies {
 implementation &#39;io.reactivex.rxjava3:rxandroid:3.0.0&#39;
 implementation &#39;io.reactivex.rxjava3:rxjava:3.0.7&#39;
}</code></pre>
</li>
<li>Observer를 만든다.</li>
</ol>
<ul>
<li>observer에는 세가지 함수를 통해 데이터 흐름을 처리한다.
1) <strong>onNext()</strong> : 하나의 소스 Observable에서 Observer까지 한 번에 하나씩 순차적으로 데이터를 발행함
2)** onComplete() <strong>: 데이터 발행이 끝났음을 알리는 완료 이벤트를 Observer에 전달하여 onNext()를 더 호출하지 않음을 나타냄
3)</strong> onError()** : 오류가 발생했음을 Observer에 전달함</li>
</ul>
<pre><code class="language-java">Observer&lt;Integer&gt; observer = new Observer&lt;Integer&gt;() {

    @Override
    public void onCompleted() {
        System.out.println(&quot;All data emitted.&quot;);
    }

    @Override
    public void onError(Throwable e) {
        System.out.println(&quot;Error received: &quot; + e.getMessage());
    }

    @Override
    public void onNext(Integer integer) {
        System.out.println(&quot;New data received: &quot; + integer);
    }
};</code></pre>
<ol start="3">
<li>Scheduler 처리하기</li>
</ol>
<ul>
<li>scheduler는 동시성을 관리함. Scheduler에는 스레드 관리를 제어하는 두 개의 메서드가 있다.
1)** subscribeOn() <strong>: observable이 어느 스레드에서 동작할 것인지 정의할 수 있음
2)</strong> observeOn() **: observer가 어느 스레드에서 동작할 것인지 정의할 수 있음</li>
</ul>
<pre><code class="language-java">Subscription subscription = observable 
    .subscribeOn(Schedulers.io()) // observable을 IO 스레드에서 실행
    .observeOn(AndroidSchedulers.mainThread()) // Observer 메인 스레드에서 실행
    .subscribe(observer); // observer 구독


subscription.unsubscribe(); // 구독 취소</code></pre>
<p>참고 : <a href="https://soohyun6879.tistory.com/123">https://soohyun6879.tistory.com/123</a>
<a href="https://blog.yena.io/studynote/2020/10/11/Android-RxJava(1).html">https://blog.yena.io/studynote/2020/10/11/Android-RxJava(1).html</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Android kotlin 공공데이터 포털 활용하기 (기상청 단기예보)]]></title>
            <link>https://velog.io/@hyejiseo-dev/kotlin-%EA%B3%B5%EA%B3%B5%EB%8D%B0%EC%9D%B4%ED%84%B0-%ED%8F%AC%ED%84%B8-api-%EB%8D%B0%EC%9D%B4%ED%84%B0-%ED%99%9C%EC%9A%A9%ED%95%98%EA%B8%B0%EA%B8%B0%EC%83%81%EC%B2%AD-%EB%8B%A8%EA%B8%B0%EC%98%88%EB%B3%B4</link>
            <guid>https://velog.io/@hyejiseo-dev/kotlin-%EA%B3%B5%EA%B3%B5%EB%8D%B0%EC%9D%B4%ED%84%B0-%ED%8F%AC%ED%84%B8-api-%EB%8D%B0%EC%9D%B4%ED%84%B0-%ED%99%9C%EC%9A%A9%ED%95%98%EA%B8%B0%EA%B8%B0%EC%83%81%EC%B2%AD-%EB%8B%A8%EA%B8%B0%EC%98%88%EB%B3%B4</guid>
            <pubDate>Sat, 01 Jan 2022 04:19:37 GMT</pubDate>
            <description><![CDATA[<h3 id="📍-기상청-단기예보-조회-서비스-활용">📍 기상청 단기예보 조회 서비스 활용</h3>
<p>기상청 데이터를 가져오기 위해 아래의 링크에 접속하여 키를 발급받았다.</p>
<p><a href="https://www.data.go.kr/tcs/dss/selectApiDataDetailView.do?publicDataPk=15084084">https://www.data.go.kr/tcs/dss/selectApiDataDetailView.do?publicDataPk=15084084</a></p>
<p><img src="https://images.velog.io/images/hyejiseo-dev/post/8770af96-f45c-4a5e-899f-8611a644d161/image.png" alt=""></p>
<p>활용신청을 하면 키를 발급 받을 수 있다!</p>
<p><img src="https://images.velog.io/images/hyejiseo-dev/post/1bdac8d8-a10a-4a87-92ed-e90fb493ddc5/image.png" alt=""></p>
<p>가이드 문서를 다운받아 api 활용 방법대로 적용해본다.
그리고 테스트를 해본다! JSON형태로 불러들여 잘 나오는 것을 확인했다
<img src="https://images.velog.io/images/hyejiseo-dev/post/944d8c47-75a0-4620-9d2b-1f4a93c63d39/image.png" alt=""></p>
<p>이제 코드에 적용해보자!</p>
<h3 id="적용-예제">적용 예제</h3>
<h4 id="네트워크-구성-파일-추가">네트워크 구성 파일 추가</h4>
<p>res폴더에 xml &gt; network_security_config.xml 파일을 생성하고 안에 아래의 내용을 추가한다. android가 https가 아닌 http 프로토콜 접속시 제한되는 오류 발생을 막기 위함!</p>
<pre><code class="language-java">&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;network-security-config&gt;
    &lt;!--Set application-wide security config using base-config tag.--&gt;
    &lt;base-config cleartextTrafficPermitted=&quot;true&quot;/&gt;
&lt;/network-security-config&gt;</code></pre>
<p>그리고, AndroidManifest.xml의 application안에 추가한다.</p>
<pre><code class="language-java">android:networkSecurityConfig=&quot;@xml/network_security_config&quot;
android:usesCleartextTraffic=&quot;true&quot;</code></pre>
<p>api url을 적용할 수 있는 함수를 생성했다. HttpURLConnection을 사용해 파싱하고 json타입을 분석하여 가져왔다. </p>
<pre><code class="language-java">public String lookUpWeather(String baseDate, String time, String nx, String ny) throws IOException, JSONException {

        String baseTime = timeChange(time); //&quot;0500&quot; 조회하고 싶은 시간
        String type = &quot;json&quot;;    //타입

        String apiUrl = &quot;http://apis.data.go.kr/1360000/VilageFcstInfoService_2.0/getVilageFcst&quot;;

//         홈페이지에서 받은 키
        String serviceKey = &quot;6iluJmdqNbNA7LYj6Dq3vA%2BxH0K7n%2FmHVhMzYxQPk%2FNR%2FgudH5H4zfH4GzpwLcXgPE17zicZ35PqjRfwZU9MMQ%3D%3D&quot;;

        StringBuilder urlBuilder = new StringBuilder(apiUrl);
        urlBuilder.append(&quot;?&quot; + URLEncoder.encode(&quot;ServiceKey&quot;, &quot;UTF-8&quot;) + &quot;=&quot; + serviceKey);
        urlBuilder.append(&quot;&amp;&quot; + URLEncoder.encode(&quot;nx&quot;, &quot;UTF-8&quot;) + &quot;=&quot; + URLEncoder.encode(nx, &quot;UTF-8&quot;)); //경도
        urlBuilder.append(&quot;&amp;&quot; + URLEncoder.encode(&quot;ny&quot;, &quot;UTF-8&quot;) + &quot;=&quot; + URLEncoder.encode(ny, &quot;UTF-8&quot;)); //위도
        urlBuilder.append(&quot;&amp;&quot; + URLEncoder.encode(&quot;base_date&quot;, &quot;UTF-8&quot;) + &quot;=&quot; + URLEncoder.encode(baseDate, &quot;UTF-8&quot;)); /* 조회하고 싶은 날짜*/
        urlBuilder.append(&quot;&amp;&quot; + URLEncoder.encode(&quot;base_time&quot;, &quot;UTF-8&quot;) + &quot;=&quot; + URLEncoder.encode(baseTime, &quot;UTF-8&quot;)); /* 조회하고 싶은 시간 AM 02시부터 3시간 단위 */
        urlBuilder.append(&quot;&amp;&quot; + URLEncoder.encode(&quot;dataType&quot;, &quot;UTF-8&quot;) + &quot;=&quot; + URLEncoder.encode(type, &quot;UTF-8&quot;));    /* 타입 */

        /*
         * GET방식으로 전송해서 파라미터 받아오기
         */
        URL url = new URL(urlBuilder.toString());
        //어떻게 넘어가는지 확인하고 싶으면 아래 출력분 주석 해제
        //System.out.println(url);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod(&quot;GET&quot;);
        conn.setRequestProperty(&quot;Content-type&quot;, &quot;application/json&quot;);

        BufferedReader rd;
        if (conn.getResponseCode() &gt;= 200 &amp;&amp; conn.getResponseCode() &lt;= 300) {
            rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
        } else {
            rd = new BufferedReader(new InputStreamReader(conn.getErrorStream()));
        }
        StringBuilder sb = new StringBuilder();
        String line;
        while ((line = rd.readLine()) != null) {
            sb.append(line);
        }
        rd.close();
        conn.disconnect();
        String result = sb.toString();


        // response 키를 가지고 데이터를 파싱
        JSONObject jsonObj_1 = new JSONObject(result);
        String response = jsonObj_1.getString(&quot;response&quot;);

        // response 로 부터 body 찾기
        JSONObject jsonObj_2 = new JSONObject(response);
        String body = jsonObj_2.getString(&quot;body&quot;);

        // body 로 부터 items 찾기
        JSONObject jsonObj_3 = new JSONObject(body);
        String items = jsonObj_3.getString(&quot;items&quot;);
        Log.i(&quot;ITEMS&quot;, items);

        // items로 부터 itemlist 를 받기
        JSONObject jsonObj_4 = new JSONObject(items);
        JSONArray jsonArray = jsonObj_4.getJSONArray(&quot;item&quot;);

        for (int i = 0; i &lt; jsonArray.length(); i++) {
            jsonObj_4 = jsonArray.getJSONObject(i);
            String fcstValue = jsonObj_4.getString(&quot;fcstValue&quot;);
            String category = jsonObj_4.getString(&quot;category&quot;);

            if (category.equals(&quot;SKY&quot;)) {
                weather = &quot;현재 날씨는 &quot;;
                if (fcstValue.equals(&quot;1&quot;)) {
                    weather += &quot;맑은 상태로&quot;;
                } else if (fcstValue.equals(&quot;2&quot;)) {
                    weather += &quot;비가 오는 상태로 &quot;;
                } else if (fcstValue.equals(&quot;3&quot;)) {
                    weather += &quot;구름이 많은 상태로 &quot;;
                } else if (fcstValue.equals(&quot;4&quot;)) {
                    weather += &quot;흐린 상태로 &quot;;
                }
            }

            if (category.equals(&quot;TMP&quot;)) {
                tmperature = &quot; 기온은 &quot; + fcstValue + &quot;℃&quot;;
            }

        }

        return weather + tmperature;
    }

</code></pre>
<p>다음으로 시간을 적용하기 위한 함수를 넣어 3시간 동안 조회한다.</p>
<pre><code class="language-java"> public String timeChange(String time)
    {
        // 현재 시간에 따라 데이터 시간 설정(3시간 마다 업데이트) //
        /**
         시간은 3시간 단위로 조회해야 한다. 안그러면 정보가 없다고 뜬다.
         0200, 0500, 0800 ~ 2300까지
         그래서 시간을 입력했을때 switch문으로 조회 가능한 시간대로 변경해주었다.
         **/
        switch(time) {

            case &quot;0200&quot;:
            case &quot;0300&quot;:
            case &quot;0400&quot;:
                time = &quot;0200&quot;;
                break;
            case &quot;0500&quot;:
            case &quot;0600&quot;:
            case &quot;0700&quot;:
                time = &quot;0500&quot;;
                break;
            case &quot;0800&quot;:
            case &quot;0900&quot;:
            case &quot;1000&quot;:
                time = &quot;0800&quot;;
                break;
            case &quot;1100&quot;:
            case &quot;1200&quot;:
            case &quot;1300&quot;:
                time = &quot;1100&quot;;
                break;
            case &quot;1400&quot;:
            case &quot;1500&quot;:
            case &quot;1600&quot;:
                time = &quot;1400&quot;;
                break;
            case &quot;1700&quot;:
            case &quot;1800&quot;:
            case &quot;1900&quot;:
                time = &quot;1700&quot;;
                break;
            case &quot;2000&quot;:
            case &quot;2100&quot;:
            case &quot;2200&quot;:
                time = &quot;2000&quot;;
                break;
            default:
                time = &quot;2300&quot;;

        }
        return time;
    }</code></pre>
<p>호출시, 네트워크 스레드를 활용한다. 현재 시간 기준으로 최신값을 가져왔다.</p>
<pre><code class="language-java">// 네트워크를 이용할 때는 쓰레드를 사용해서 접근해야 함
    inner class NetworkThread2 : Thread() {
        override fun run() {
            val wd = WeatherData()
            try {
                // date와 time에 값을 넣어야함, 오늘 날짜 기준으로 넣기!
                // ex) date = &quot;20210722&quot;, time = &quot;0500&quot;
                val now = System.currentTimeMillis()
                val date = Date(now)

                val simpleDateFormatDay = SimpleDateFormat(&quot;yyyyMMdd&quot;)
                val simpleDateFormatTime = SimpleDateFormat(&quot;HH00&quot;)
                val getDay = simpleDateFormatDay.format(date)
                val getTime = simpleDateFormatTime.format(date)
                Log.i(&quot;datetime.../&quot;,getDay + getTime)

                weather = wd.lookUpWeather(getDay, getTime, nx, ny)

            } catch (e: IOException) {
                Log.i(&quot;THREE_ERROR1&quot;, e.message!!)
            } catch (e: JSONException) {
                Log.i(&quot;THREE_ERROR2&quot;, e.message!!)
            }
            Log.i(&quot;현재날씨&quot;, weather!!)
        }
    }</code></pre>
<p>추가로, 현재 위치 기반으로 가져오기 위해 위도, 경도를 받는 로직에 주소를 읽어왔다. 이제 기상청 가이드 문서에 있는 엑셀 시트를 적용하여 각 도, 시, 구 별 nx, ny값을 받아 보여주기 위해 폴더에 추가해준다</p>
<p>엑셀 시트 적용 방법은 아래 링크에서 확인할 수 있다!
<a href="https://toubi-tobap.tistory.com/11">https://toubi-tobap.tistory.com/11</a></p>
<pre><code class="language-java">   private fun readExcel(localName: String?) {
        try {
            val localFile: InputStream = baseContext.resources.assets.open(&quot;local_name.xls&quot;)
            val wb: Workbook = Workbook.getWorkbook(localFile)
            if (wb != null) {
                val sheet = wb.getSheet(0) // 시트 불러오기
                if (sheet != null) {
                    val colTotal = sheet.columns // 전체 컬럼
                    val rowIndexStart = 1 // row 인덱스 시작
                    val rowTotal = sheet.getColumn(colTotal - 1).size
                    var row = rowIndexStart
                    while (row &lt; rowTotal) {
                        val contents = sheet.getCell(0, row).contents
                        if (contents.contains(localName!!)) {
                            nx = sheet.getCell(1, row).contents
                            ny = sheet.getCell(2, row).contents
                            row = rowTotal
                        }
                        row++
                    }
                }
            }
        } catch (e: IOException) {
            Log.i(&quot;READ_EXCEL1&quot;, e.message!!)
            e.printStackTrace()
        } catch (e: BiffException) {
            Log.i(&quot;READ_EXCEL1&quot;, e.message!!)
            e.printStackTrace()
        }

        Log.i(&quot;격자값&quot;, &quot;x = &quot; + nx + &quot;  y = &quot; + ny)
    }</code></pre>
<p>이제 여기에 나온 nx, ny 값에 따라 해당 위치 값을 가져와 api에 적용할 수 있게 되었다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Android kotlin GPS 위치 권한 및 주소 가져오기]]></title>
            <link>https://velog.io/@hyejiseo-dev/Android-kotlin-GPS-%EC%9C%84%EC%B9%98-%EA%B6%8C%ED%95%9C-%EB%B0%8F-%EC%A3%BC%EC%86%8C-%EA%B0%80%EC%A0%B8%EC%98%A4%EA%B8%B0</link>
            <guid>https://velog.io/@hyejiseo-dev/Android-kotlin-GPS-%EC%9C%84%EC%B9%98-%EA%B6%8C%ED%95%9C-%EB%B0%8F-%EC%A3%BC%EC%86%8C-%EA%B0%80%EC%A0%B8%EC%98%A4%EA%B8%B0</guid>
            <pubDate>Sat, 01 Jan 2022 04:16:14 GMT</pubDate>
            <description><![CDATA[<h3 id="📍-gps-위치-권한-가져오기">📍 GPS 위치 권한 가져오기</h3>
<h4 id="gradle-파일에-추가">gradle 파일에 추가</h4>
<p>build.gradle 에 구글 플레이 서비스에서 제공하는 API를 주입시켜줍니다.  </p>
<pre><code class="language-java">// google map api &amp; location
 implementation &#39;com.google.android.gms:play-services-maps:18.0.0&#39;
implementation &#39;com.google.android.gms:play-services-location:19.0.0&#39;</code></pre>
<h4 id="androidmanifestxml-권한-설정-코드-추가">AndroidManifest.xml 권한 설정 코드 추가</h4>
<p>Sync Projects를 완료하고나면 원하는 곳에 위치 API를 사용할 수 있다. </p>
<blockquote>
<p><strong>checkSelfPermission</strong>을 통해 <strong>ACCESS_FINE_LOCATION</strong>과 <strong>ACCESS_COARSE_LOCATION</strong> 권한이 있는지 런타임에 확인해주고 권한이 있으면 위치 서비스에 연결시켜주고 <strong>lastLocation</strong>을 통해 현재위치를 가져올 수 있다.</p>
</blockquote>
<pre><code class="language-java">&lt;!--    정확한 위치    --&gt;
&lt;uses-permission android:name=&quot;android.permission.ACCESS_FINE_LOCATION&quot; /&gt;
&lt;!--    대략적인 위치    --&gt;
&lt;uses-permission android:name=&quot;android.permission.ACCESS_COARSE_LOCATION&quot; /&gt;</code></pre>
<h3 id="📍-위치-권한-체크-및-요청-함수">📍 위치 권한 체크 및 요청 함수</h3>
<pre><code class="language-java">//변수 선언
private lateinit var fusedLocationClient: FusedLocationProviderClient

//등록
fusedLocationClient = LocationServices.getFusedLocationProviderClient(this);

 //퍼미션 체크 및 권한 요청 함수
    @SuppressLint(&quot;MissingPermission&quot;)
    private fun checkLocationPermission() {
        if (ActivityCompat.checkSelfPermission(
                this,
                Manifest.permission.ACCESS_FINE_LOCATION
            ) == PackageManager.PERMISSION_GRANTED &amp;&amp; ActivityCompat.checkSelfPermission(
                this,
                Manifest.permission.ACCESS_COARSE_LOCATION
            ) == PackageManager.PERMISSION_GRANTED
        ) {
            fusedLocationClient.lastLocation
                .addOnSuccessListener { location: Location? -&gt;
                    // Got last known location. In some rare situations this can be null.
                    var geocoder = Geocoder(this, Locale.KOREA)
                    if (location != null) {
                        Toast.makeText(
                            this,
                            &quot;현재위치...&quot; + location.latitude + &quot; / &quot; + location.longitude,
                            Toast.LENGTH_SHORT
                        ).show()

                    }
                }
        } else {
            Toast.makeText(this, &quot;위치권한이 없습니다..&quot;, Toast.LENGTH_SHORT).show()

        }
    }</code></pre>
<h3 id="📍-권한-요청-결과-처리">📍 권한 요청 결과 처리</h3>
<blockquote>
<p>결과를 처리하기 위해 <strong>onRequestPermissionsResult</strong> 를 사용하여 requestCode로 구별해준다. 실패 시 수행되는 동작을 지정할 수 있다.</p>
</blockquote>
<pre><code class="language-java">//퍼미션 응답 처리 코드 (선언)
private val multiplePermissionsCode = 100

//권한 요청 결과 처리 함수
@SuppressLint(&quot;MissingSuperCall&quot;)
override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array&lt;String&gt;, grantResults: IntArray
    ) {
        when (requestCode) {
            multiplePermissionsCode -&gt; {
                if (grantResults.isNotEmpty()) {
                    for ((i, permission) in permissions.withIndex()) {
                        if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {
                            //권한 획득 실패시 동작
                            Toast.makeText(
                                this,
                                &quot;The user has denied to $permission&quot;,
                                Toast.LENGTH_SHORT
                            ).show()
                            Log.i(&quot;TAG&quot;, &quot;I can&#39;t work for you anymore then. ByeBye!&quot;)
                        } else {
                            gpsGranted = true
                        }
                    }
                }
            }
        }
    }</code></pre>
<h3 id="📍-gps-현재-위치-주소-가져오기">📍 GPS 현재 위치 주소 가져오기</h3>
<h4 id="💡-geocoder-사용하기">💡 GeoCoder 사용하기</h4>
<p>Geocoder를 사용해 사용자가 입력한 주소값의 위도 및 경도를 받아오는 것을 구현했다. 먼저 geocoder선언을 해준 후 위에서 받은 위/경도 값을 getfromLocation함수에 담아 가져오고 하나씩 &quot; &quot;으로 구분하여 도, 시, 군, 동 을 순서대로 꺼내온다. </p>
<pre><code class="language-java">//주소 초기화 
    var address: List&lt;String&gt; = listOf(&quot;서울특별시&quot;, &quot;중구&quot;, &quot;명동&quot;)

 // Got last known location. In some rare situations this can be null.
                    var geocoder = Geocoder(this, Locale.KOREA)
                    if (location != null) {
                        Toast.makeText(
                            this,
                            &quot;현재위치...&quot; + location.latitude + &quot; / &quot; + location.longitude,
                            Toast.LENGTH_SHORT
                        ).show()

                        val addrList =
                            geocoder.getFromLocation(location.latitude, location.longitude, 1)

                        for (addr in addrList) {
                            val splitedAddr = addr.getAddressLine(0).split(&quot; &quot;)
                            address = splitedAddr
                        }
                        //경기도, 성남시, 분당구, 삼평동
                        textView.append(&quot;현재위치 : ${address[1]}, ${address[2]}, ${address[3]}, ${address[4]}&quot;)
                    }</code></pre>
<p>위와 같이 현재 위치를 로컬 주소로 표시할 수 있다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Android 아키텍쳐 MVP 패턴]]></title>
            <link>https://velog.io/@hyejiseo-dev/Android-%EC%95%84%ED%82%A4%ED%85%8D%EC%B3%90-MVP-%ED%8C%A8%ED%84%B4</link>
            <guid>https://velog.io/@hyejiseo-dev/Android-%EC%95%84%ED%82%A4%ED%85%8D%EC%B3%90-MVP-%ED%8C%A8%ED%84%B4</guid>
            <pubDate>Sat, 01 Jan 2022 04:13:05 GMT</pubDate>
            <description><![CDATA[<h3 id="mvp-패턴이란">MVP 패턴이란?</h3>
<blockquote>
<p><strong>model, View, Presenter</strong>의 첫글자를 따서 지어진 패턴, View가 수동적으로 동작하여 Presenter가 명령을 내려 View에게 데이터를 보여달라고 하는 형태이다. </p>
</blockquote>
<p>Model, View, Presenter 의 정의는 MVC 패턴과 크게 다르지는 않다.</p>
<ul>
<li><p><strong>Model</strong> : 애플리케이션 데이터와 상태에 대한 비즈니스 로직을 수행한다</p>
</li>
<li><p><strong>View</strong> : 실제 View 에 대한 직접적인 접근을 담당한다</p>
</li>
<li><p>*<em>Presenter *</em>: 뷰와 모델 사이에서 데이터를 전달하는 역할을 한다</p>
</li>
<li><p>도식화
<img src="https://images.velog.io/images/hyejiseo-dev/post/880ebdd6-32b4-4df0-8203-eb66fef6b23e/image.png" alt=""></p>
</li>
</ul>
<p>View에서 클릭이나 사용자 액션이 발생하면 presenter를 호출하여 presenter.do()와 같은 로직이 수행된다. presenter는 모델을 변형하고 작업을 수행한 후에 결과를 다시 View에 전달한다.
결국, <strong>Presenter는 중재인으로 Model과 View사이의 필요한 작업을 수행한다.</strong></p>
<p>단점은, 앱이 복잡해질 수록 View와 Presenter의 의존성이 강해진다. 너무 거대해지면 다루기 어렵고 분리하기 힘든 Presenter가 된다.</p>
<pre><code class="language-java">class Impl {
interface Presenter {
void loadData();
void createModel();
}

interface View {
void completed(String data);
}
}


class View implements Impl.View {

Presenter presenter;

public View (){
presenter = new Presenter(this);
presenter.createModel();
presenter.loadData();
}

@Override
public void completed(String data){
System.out.print(data);
}
}


class Presenter implements Impl.Presenter{

private Impl.View view;
private Model model;

public Presenter(Impl.View view){
this.view = view;
}

@Override
public void createModel(){
model = new Model();
}

@Override
public void loadData() {
if(model == null){
System.out.print(&quot;model is null&quot;);
return;
}

view.completed(model.getName());
}
}

class Model {
private String name = &quot;kim&quot;;
public String getName(){
return name;
}
}

// 출처: https://pppdwer.tistory.com/entry/MVP-Model-VIew-Presenter-Pattern-의-기본-예 [Dev.]</code></pre>
<p>위 예제를 통해 보면 view 는 Presenter에 선언된 행동을 받아, 필요한 정보를 요청하고 요청이 들어오면 Presenter의 callback으로 처리한다. 모델에 있는 데이터를 프레젠터에서 동작을 처리해 주어 view에서는 단지 데이터를 받아들이면 된다. </p>
<p>앞으로 실무에서 mvp 패턴을 활용할 기회가 많은 것 같아 간단히 정리해 보았다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Android Soap 통신 (java)]]></title>
            <link>https://velog.io/@hyejiseo-dev/Android-Soap-%ED%86%B5%EC%8B%A0</link>
            <guid>https://velog.io/@hyejiseo-dev/Android-Soap-%ED%86%B5%EC%8B%A0</guid>
            <pubDate>Mon, 06 Dec 2021 06:17:03 GMT</pubDate>
            <description><![CDATA[<h3 id="soap-통신이란">Soap 통신이란?</h3>
<blockquote>
<p>프로토콜 스타일, simple object access protocol 의 약자, 보안 수준이 엄격함, 성공 / 반복 실행로직이 규정되어 있어 처음부터 끝까지 신뢰성을 제공 ACID를 준수하여 데이터의 변형을 줄어주고 정의를 명확하게 한다.</p>
</blockquote>
<p>HTTP, HTTPS, SMTP 등을 통해 XML 기반의 메시지를 컴퓨터 네트워크 상에서 교환되는 프로토콜이다.</p>
<p>SOAP은 웹 서비스에서 기본적인 메시지를 전달하는 기반이 된다. SOAP에는 몇 가지 형태의 메시지 패턴이 있지만, 보통의 경우 원격 프로시져 호출(RPC)패턴으로, 클라이언트에서 서버쪽으로 메시지를 요청하고, 서버는 메시지를 즉시 응답하게 된다. SOAP는 XML-RPC와 WDDX에서 envelope/header/body로 이루어진 구조와 전송과 상호 중립성의 개념을 가져왔다.</p>
<p>SOAP은 XML을 근간으로 헤더와 바디를 조합하는 디자인 패턴으로 설계되어 있다. 헤더는 선택사항으로 반복이나 보안 및 트랜잭션을 정보로 하는 메타 정보를 가지고 있다. 바디부분은 주요한 대부분의 정보를 다룬다.</p>
<p><img src="https://images.velog.io/images/hyejiseo-dev/post/c2df5cb4-9f57-4b1c-be1f-d73a7fae7bc1/image.png" alt=""></p>
<ul>
<li>SOAP는 서비스 인터페이스를 이용해서 서버에 접근</li>
<li>SOAP는 WS-Security를 지원하는데, WS-Security는 전송 레벨에서 아주 뛰어나며 SSL보다도 조금 더 복잡하기 때문에 기업용 보안 도구에 통합하는데 보다 이상적입니다.</li>
<li>기업용 애플리케이션인 경우에는 보다 많은 리소스와 아주 엄격한 보안 그리고 여러 다양한 요구 사항들을 만족해야 하기 때문에 SOAP 방식을 택하는 경우가 많습니다.</li>
</ul>
<h3 id="android에서의-soap통신">Android에서의 Soap통신</h3>
<blockquote>
<p>ksoap2 라이브러리 활용 / SoapObject를 </p>
</blockquote>
<h3 id="1-변수-선언-wsdl에-들어있는-정보">1. 변수 선언 (WSDL에 들어있는 정보)</h3>
<p>각 요청에 대한 SOAP 요청 템플릿을 만듭니다.</p>
<ul>
<li>NAMESPACE : 웹서비스 만들 때 기재</li>
<li>URL : 웹서비스 위치</li>
<li>SOAP_ACTION : 웹에서 확인하면 함수 설명이 나옴</li>
<li>METHOD_NAME : 호출되는 함수 이름</li>
</ul>
<pre><code class="language-java">
//SOAP 통신을 위한 변수 
 private static final String NAMESPACE = &quot;https://api.authorize.net/soap/v1/&quot;;
 private static final String URL =&quot;https://apitest.authorize.net/soap/v1/Service.asmx?wsdl&quot;; 
 private static final String SOAP_ACTION = &quot;https://api.authorize.net/soap/v1/AuthenticateTest&quot;;
 private static final String METHOD_NAME = &quot;AuthenticateTest&quot;;</code></pre>
<h3 id="2-soap-object-생성-후-파라미터-입력">2. Soap Object 생성 후 파라미터 입력</h3>
<p>request.addProperty(&quot;sType&quot;, sType); -&gt; 전달 변수 명 / 파라미터 값</p>
<pre><code class="language-java">  //request 설정
    request = new SoapObject(NAMESPACE, METHOD_NAME);
    request.addProperty(&quot;sType&quot;, sType);
    request.addProperty(&quot;sInputType&quot;, &quot;JSON&quot;);
    request.addProperty(&quot;sOutputType&quot;, &quot;JSON&quot;);
    request.addProperty(&quot;sEncYn&quot;, &quot;N&quot;);

  //createJson, putJson
    request.addProperty(&quot;sParam&quot;, jsonObject.toString());

  // Envelope 설정
     envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
     envelope.setOutputSoapObject(request);
     envelope.dotNet = true;</code></pre>
<h3 id="3-웹서비스-호출하기">3. 웹서비스 호출하기</h3>
<pre><code class="language-java">try {
       // 웹서비스 호출
         HttpTransportSE androidHttpTransport = new HttpTransportSE(URL, 2000);
         androidHttpTransport.call(SOAP_ACTION, envelope);
       // 웹서비스 응답
         SoapPrimitive soapPrimitive = (SoapPrimitive) envelope.getResponse();
         result = envelope.getResponse().toString();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (XmlPullParserException e) {
            e.printStackTrace();
        }
  }</code></pre>
]]></description>
        </item>
    </channel>
</rss>