<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>₍ᐢ.  ̫ .ᐢ₎♡ </title>
        <link>https://velog.io/</link>
        <description>Android Developer..+ iOS 슬쩍 🌱  ✏️끄적끄적,,개인 기록용 👩🏻‍💻</description>
        <lastBuildDate>Mon, 14 Jul 2025 09:25:51 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>₍ᐢ.  ̫ .ᐢ₎♡ </title>
            <url>https://images.velog.io/images/soyoung-dev/profile/2b3b0227-527f-4a77-b82b-277c64f996bc/KakaoTalk_20210708_173757155.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. ₍ᐢ.  ̫ .ᐢ₎♡ . All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/soyoung-dev" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[Android/Kotlin] Secret Gradle 플러그인, local.properties를 사용하여 Key 숨기기]]></title>
            <link>https://velog.io/@soyoung-dev/AndroidKotlin-Secret-Gradle-%ED%94%8C%EB%9F%AC%EA%B7%B8%EC%9D%B8-local.properties%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EC%97%AC-Key-%EC%88%A8%EA%B8%B0%EA%B8%B0</link>
            <guid>https://velog.io/@soyoung-dev/AndroidKotlin-Secret-Gradle-%ED%94%8C%EB%9F%AC%EA%B7%B8%EC%9D%B8-local.properties%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EC%97%AC-Key-%EC%88%A8%EA%B8%B0%EA%B8%B0</guid>
            <pubDate>Mon, 14 Jul 2025 09:25:51 GMT</pubDate>
            <description><![CDATA[<h1 id="📌-androidkotlin-secret-gradle-플러그인-localproperties를-사용하여-key-숨기기">📌 [Android/Kotlin] Secret Gradle 플러그인, local.properties를 사용하여 Key 숨기기</h1>
<p>프로젝트에서 api 키 값 등 중요한 정보들을 숨겨보자.</p>
<h2 id="📝-localproperties-방식">📝 local.properties 방식</h2>
<p>먼저 local.properties 방식을 이용한 방법 부터 알아보자.</p>
<h3 id="💙-localproperties-에-추가">💙 local.properties 에 추가</h3>
<p>local.properties 파일에 숨길 키값들을 넣어준다.</p>
<pre><code># key
SECRET_KEY=aaabbbcccddd</code></pre><h3 id="💙-buildgradlekts-moduleapp-에-추가">💙 build.gradle.kts (Module:app) 에 추가</h3>
<h4 id="1️⃣-localproperties-파일-읽기">1️⃣ local.properties 파일 읽기</h4>
<pre><code>val properties = Properties()
val localPropertiesFile = rootProject.file(&quot;local.properties&quot;)
if (localPropertiesFile.exists()) {
   properties.load(localPropertiesFile.inputStream())
}</code></pre><p>local.properties 파일이 프로젝트 루트에 존재하는지 확인 후, 존재하면 Properties 객체에 해당 파일의 내용을 로드한다.</p>
<h4 id="2️⃣-키-값-추출하기">2️⃣ 키 값 추출하기</h4>
<pre><code>val secretKey: String = properties.getProperty(&quot;SECRET_KEY&quot;) ?: &quot;&quot;
</code></pre><p>SECRET_KEY라는 이름의 값을 가져온다.
값이 없으면 빈 문자열(””)을 사용.</p>
<h4 id="3️⃣-buildconfig에-값-주입">3️⃣ BuildConfig에 값 주입</h4>
<pre><code>defaultConfig {
    buildConfigField(&quot;String&quot;, &quot;SECRET_KEY&quot;, &quot;\&quot;$secretKey\&quot;&quot;)
}</code></pre><p>build.gradle의 <code>defaultConfig</code> 블록에서 <code>buildConfigField</code>를 사용해 <code>SECRET_KEY</code>라는 이름의 BuildConfig 상수를 생성.</p>
<h3 id="💙-key-사용하기">💙 key 사용하기</h3>
<pre><code>val key = BuildConfig.SECRET_KEY // &quot;abcdefg123456&quot;
</code></pre><p>이렇게 local.properties 를 이용하여 키 값을 숨길 수 있다.</p>
<blockquote>
<h3 id="🚨-gitignore-에-추가하는-것을-잊지말자">🚨 .gitignore 에 추가하는 것을 잊지말자</h3>
<p>/local.properties</p>
</blockquote>
<h2 id="📝-secrets-gradle-플러그인-방식">📝 Secrets Gradle 플러그인 방식</h2>
<p>숨겨야 할 키값이 많아진다면 일일히 직접 읽어서 buildConfigField 에 주입해줘야하는 불편함이 있다.
이를 해결하기 위해 구글 공식 플러그인인 <a href="https://github.com/google/secrets-gradle-plugin"><code>Secrets Gradle 플러그인 방식</code> </a> 을 사용해보자.</p>
<h3 id="💙-secrets-gradle-플러그인-추가">💙 Secrets Gradle 플러그인 추가</h3>
<h4 id="1️⃣-version-catalog로-플러그인-추가-libsversionstoml-파일">1️⃣ Version Catalog로 플러그인 추가 (libs.versions.toml 파일)</h4>
<pre><code>[versions]
secretsGradlePlugin = &quot;2.0.1&quot;

[libraries]
secrets-gradle-plugin = { module = &quot;com.google.android.libraries.mapsplatform.secrets-gradle-plugin:secrets-gradle-plugin&quot;, version.ref = &quot;secretsGradlePlugin&quot; }

[plugins]
secrets-gradle-plugin = { id = &quot;com.google.android.libraries.mapsplatform.secrets-gradle-plugin&quot;}</code></pre><h4 id="2️⃣-프로젝트-수준의-buildgradlekts-에-추가">2️⃣ 프로젝트 수준의 build.gradle.kts 에 추가</h4>
<pre><code>buildscript {
    dependencies {
        classpath(libs.secrets.gradle.plugin)
    }
}</code></pre><h4 id="3️⃣-모듈-수준-buildgradlekts-추가">3️⃣ 모듈 수준 build.gradle.kts 추가</h4>
<pre><code>plugins {
    alias(libs.plugins.secrets.gradle.plugin)
}</code></pre><h3 id="💙-secretsproperties-파일-추가">💙 <code>secrets.properties</code> 파일 추가</h3>
<p>플러그인이 자동으로 BuildConfig, Manifest에 주입 해줘서 키 값을 사용할 수 있다.
읽어오는 파일의 위치는 기본값은 프로젝트 루트의 <code>local.properties</code>이나, 보통 별도의 파일을 사용한다.
local.properties 가 있는 위치에 secrets.properties 파일을 생성해준다.</p>
<p><img src="https://velog.velcdn.com/images/soyoung-dev/post/e5416da3-f9d3-4f19-b606-ded5743fb6eb/image.png" alt=""></p>
<h3 id="💙-secretsproperties-에-키-추가">💙 secrets.properties 에 키 추가</h3>
<p>secrets.properties 파일에 숨길 키값들을 넣어준다.</p>
<pre><code># key
SECRET_KEY=aaabbbcccddd</code></pre><h3 id="💙-모듈-수준의-buildgradlekts-에-추가">💙 모듈 수준의 build.gradle.kts 에 추가</h3>
<p>secrets 블록 코드를 추가 해준다.</p>
<pre><code>secrets {
    // 비밀 키 파일명
    propertiesFileName = &quot;secrets.properties&quot; 
    // 기본값
    defaultPropertiesFileName = &quot;local.defaults.properties&quot;
    // 빌드에 포함하지 않을 키
    ignoreList.add(&quot;keyToIgnore&quot;)
    ignoreList.add(&quot;sdk.*&quot;)
}
</code></pre><blockquote>
<h3 id="각-속성-설명">각 속성 설명</h3>
</blockquote>
<h4 id="1-propertiesfilename">1. <code>propertiesFileName</code></h4>
<pre><code>• 실제 키 값(예: API 키 등)을 저장하는 파일명을 지정. 
• 기본값: 
`&quot;local.properties&quot;`</code></pre><p>→ secrets.properties 파일에서 키 값을 가져옴.</p>
<h4 id="2-defaultpropertiesfilename">2. <code>defaultPropertiesFileName</code></h4>
<pre><code>• 키 값의 기본값을 저장하는 파일명을 지정. 이 파일은 버전 관리(git)에 포함해도 됨.
• 예시: `defaultPropertiesFileName = &quot;local.defaults.properties&quot;`</code></pre><p>→ secrets.properties가 없거나 값이 없을 때 기본값을 사용.</p>
<h4 id="3-ignorelist">3. <code>ignoreList</code></h4>
<pre><code>• secrets 파일에서 무시할 키(정규식 포함)를 지정.
예시: `
&quot;sdk.dir&quot;`(Android SDK 경로)은 기본적으로 무시됨.
사용법:
`ignoreList.add(&quot;keyToIgnore&quot;)`→ “keyToIgnore”라는 키는 무시
`ignoreList.add(&quot;sdk.*&quot;)`→ “sdk”로 시작하는 모든 키는 무시</code></pre><h3 id="💙-key-사용하기-1">💙 key 사용하기</h3>
<pre><code>val key = BuildConfig.SECRET_KEY // &quot;abcdefg123456&quot;


AndroidManifest.xml file
&lt;meta-data android:value=&quot;${key}&quot; /&gt;
</code></pre><blockquote>
<h3 id="🚨-잊지말자">🚨 잊지말자</h3>
</blockquote>
<ol>
<li>모듈 수준 build.gradle 파일에서 targetSdk 및 compileSdk를 34로 설정</li>
<li>.gitignore 에 추가 (secrets.properties)</li>
</ol>
<p>문서 : <a href="https://developers.google.com/maps/documentation/android-sdk/secrets-gradle-plugin?hl=ko">https://developers.google.com/maps/documentation/android-sdk/secrets-gradle-plugin?hl=ko</a>
<a href="https://github.com/google/secrets-gradle-plugin">https://github.com/google/secrets-gradle-plugin</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Android/Kotlin] 'LocalBroadcastManager' is deprecated. Deprecated in Java]]></title>
            <link>https://velog.io/@soyoung-dev/AndroidKotlin-LocalBroadcastManager-is-deprecated.-Deprecated-in-Java</link>
            <guid>https://velog.io/@soyoung-dev/AndroidKotlin-LocalBroadcastManager-is-deprecated.-Deprecated-in-Java</guid>
            <pubDate>Mon, 09 Sep 2024 06:22:56 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>&#39;LocalBroadcastManager&#39; is deprecated. Deprecated in Java</p>
</blockquote>
<p>개발을 하던 중 LocalBroadcastManager 가 deprecated 되었다는 오류가 떴다.</p>
<pre><code>override fun onResume() {
    super.onResume()
    LocalBroadcastManager.getInstance(this).registerReceiver(
        mRegistrationBroadcastReceiver,
        IntentFilter(Constants.Intent.FCM_UNREGISTRATION_COMPLETE)
    )

}

override fun onDestroy() {
    super.onDestroy()
    LocalBroadcastManager.getInstance(this).unregisterReceiver(mRegistrationBroadcastReceiver)
}</code></pre><p>다음과 같이 수정해주면 된다.</p>
<pre><code>    override fun onResume() {
        super.onResume()
        registerReceiver(
            mRegistrationBroadcastReceiver,
            IntentFilter(Constants.Intent.FCM_UNREGISTRATION_COMPLETE)
        )

    }

    override fun onDestroy() {
        super.onDestroy()
        unregisterReceiver(mRegistrationBroadcastReceiver)
    }</code></pre><p>참고 : <a href="https://developer.android.com/jetpack/androidx/releases/localbroadcastmanager?hl=ko">https://developer.android.com/jetpack/androidx/releases/localbroadcastmanager?hl=ko</a>
<a href="https://stackoverflow.com/questions/74264850/localbroadcastmanager-is-now-deprecated-how-to-send-data-from-service-to-activi">https://stackoverflow.com/questions/74264850/localbroadcastmanager-is-now-deprecated-how-to-send-data-from-service-to-activi</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Android/Kotlin] 스크린 사이즈 구하기]]></title>
            <link>https://velog.io/@soyoung-dev/AndroidKotlin-%EC%8A%A4%ED%81%AC%EB%A6%B0-%EC%82%AC%EC%9D%B4%EC%A6%88-%EA%B5%AC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@soyoung-dev/AndroidKotlin-%EC%8A%A4%ED%81%AC%EB%A6%B0-%EC%82%AC%EC%9D%B4%EC%A6%88-%EA%B5%AC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Fri, 30 Aug 2024 02:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="💌-androidkotlin-스크린-사이즈-구하기">💌 [Android/Kotlin] 스크린 사이즈 구하기</h1>
<p>스크린 사이즈를 구하는 메소드들의 차이를 정리해보았다.</p>
<blockquote>
<h4 id="💙-getsize">💙 getSize()</h4>
</blockquote>
<ul>
<li>시스템 UI 제외 또는 포함 (API에 따라 다름).</li>
<li>Deprecated in API level 30
@return Point</li>
</ul>
<blockquote>
<h4 id="💙-getrealsize">💙 getRealSize()</h4>
</blockquote>
<ul>
<li>실제 화면 크기 (시스템 UI 포함). 상태바에 달린 카메라 영향 O</li>
<li>Deprecated in API level 31
@return Point</li>
</ul>
<blockquote>
<h4 id="💙-getmetrics">💙 getMetrics()</h4>
</blockquote>
<ul>
<li>시스템 UI 제외, 사용 가능 영역. status bar 에 카메라가 달린 폰과 아닌 폰에서 가져오는 높이 값이 다름.</li>
<li>Deprecated in API level 30
@return DisplayMetrics</li>
</ul>
<blockquote>
<h4 id="💙-getrealmetrics">💙 getRealMetrics()</h4>
</blockquote>
<ul>
<li>실제 화면 크기 (시스템 UI 포함).  status bar 에 카메라가 달린 폰과 아닌 폰 상관없이 디바이스의 실제 해상도 값을 리턴.</li>
<li>Deprecated in API level 31
@return DisplayMetrics</li>
</ul>
<p>스크린 사이즈를 구하는 중 deprecated 된 메소드들이 있어서 분기 처리를 해주었다.</p>
<blockquote>
<p>&#39;getRealMetrics(DisplayMetrics!): Unit&#39; is deprecated. Deprecated in Java
&#39;getter for defaultDisplay: Display!&#39; is deprecated. Deprecated in Java</p>
</blockquote>
<pre><code>   fun getScreenSize(context: Context): Pair&lt;Int, Int&gt; {
            val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager

            return when {
                Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.S -&gt; {
                    val windowMetrics = windowManager.currentWindowMetrics
                    val bounds = windowMetrics.bounds
                    Pair(bounds.width(), bounds.height())
                }
                Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.R -&gt; {
                    val display = DisplayManagerCompat.getInstance(context).getDisplay(Display.DEFAULT_DISPLAY)
                    val displayMetrics = DisplayMetrics()
                    display?.getRealMetrics(displayMetrics)
                    Pair(displayMetrics.widthPixels, displayMetrics.heightPixels)
                }
                else -&gt; {
                    val displayMetrics = DisplayMetrics()
                    windowManager.defaultDisplay.getRealMetrics(displayMetrics)
                    Pair(displayMetrics.widthPixels, displayMetrics.heightPixels)
                }
            }
        }</code></pre><p>참고 : <a href="https://developer.android.com/reference/android/view/WindowManager?_gl=1*1gtlwsa*_up*MQ..*_ga*MTczMTY5NTM5Mi4xNzI0OTE5NTg3*_ga_6HH9YJMN9M*MTcyNDkxOTU4Ny4xLjAuMTcyNDkxOTU4Ny4wLjAuMA">https://developer.android.com/reference/android/view/WindowManager?_gl=1*1gtlwsa*_up*MQ..*_ga*MTczMTY5NTM5Mi4xNzI0OTE5NTg3*_ga_6HH9YJMN9M*MTcyNDkxOTU4Ny4xLjAuMTcyNDkxOTU4Ny4wLjAuMA</a>..
<a href="https://developer.android.com/reference/android/view/Display?_gl=1*1ivze56*_up*MQ..*_ga*MTIzODA2MTA2NS4xNzI0ODk3ODE4*_ga_6HH9YJMN9M*MTcyNDg5NzgxNy4xLjAuMTcyNDg5NzgxNy4wLjAuMA">https://developer.android.com/reference/android/view/Display?_gl=1*1ivze56*_up*MQ..*_ga*MTIzODA2MTA2NS4xNzI0ODk3ODE4*_ga_6HH9YJMN9M*MTcyNDg5NzgxNy4xLjAuMTcyNDg5NzgxNy4wLjAuMA</a>..
<a href="https://appdev-room.com/android-devise-size">https://appdev-room.com/android-devise-size</a>
<a href="https://stackoverflow.com/questions/70015724/display-getrealmetrics-is-deprecated">https://stackoverflow.com/questions/70015724/display-getrealmetrics-is-deprecated</a>
<a href="https://doa-oh.tistory.com/180">https://doa-oh.tistory.com/180</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Android/Kotlin] 'getter for skus: ArrayList<String!>' is deprecated. Deprecated in Java]]></title>
            <link>https://velog.io/@soyoung-dev/AndroidKotlin-getter-for-skus-ArrayListString-is-deprecated.-Deprecated-in-Java</link>
            <guid>https://velog.io/@soyoung-dev/AndroidKotlin-getter-for-skus-ArrayListString-is-deprecated.-Deprecated-in-Java</guid>
            <pubDate>Mon, 19 Aug 2024 05:05:11 GMT</pubDate>
            <description><![CDATA[<pre><code>override fun onPurchasesUpdated(billingResult: BillingResult, purchases: List&lt;Purchase&gt;?) {
   if (billingResult.responseCode == BillingResponseCode.OK &amp;&amp; purchases != null) {
       for (purchase in purchases) {
           handlePurchase(purchase)
       }
   } else if (billingResult.responseCode == BillingResponseCode.USER_CANCELED) {
       // Handle an error caused by a user cancelling the purchase flow.
   } else {
       // Handle any other error codes.
   }
}

suspend fun handlePurchase(purchase: Purchase) {

    val skus = purchase.skus

}</code></pre><p>구글 인앱 결제를 구현하던 중 오류가 떴다.</p>
<blockquote>
<p>&#39;getter for skus: ArrayList&lt;String!&gt;&#39; is deprecated. Deprecated in Java</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/soyoung-dev/post/b40c23b2-4c88-4324-b67b-85f4fa45bf03/image.png" alt=""></p>
<p>getSkus() 는 deprecated 되어 getProducts() 를 사용해야 한다고 한다.</p>
<pre><code>suspend fun handlePurchase(purchase: Purchase) {

    val productsId = purchase.products

}</code></pre><p>참고 : 
<a href="https://developer.android.com/google/play/billing/integrate?hl=ko&amp;_gl=1*1c9locg*_up*MQ..*_ga*OTI4NTk4MzcxLjE3MjM1MzczNzA.*_ga_6HH9YJMN9M*MTcyMzU5ODQxOC4yLjAuMTcyMzU5ODQxOC4wLjAuMA">https://developer.android.com/google/play/billing/integrate?hl=ko&amp;_gl=1*1c9locg*_up*MQ..*_ga*OTI4NTk4MzcxLjE3MjM1MzczNzA.*_ga_6HH9YJMN9M*MTcyMzU5ODQxOC4yLjAuMTcyMzU5ODQxOC4wLjAuMA</a>..
<a href="https://developer.android.com/reference/com/android/billingclient/api/Purchase#getProducts()">https://developer.android.com/reference/com/android/billingclient/api/Purchase#getProducts()</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Android/Kotlin] 안드로이드 스튜디오 adb 연결 / adb: more than one device/emulator 에러 ]]></title>
            <link>https://velog.io/@soyoung-dev/AndroidKotlin-%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-%EC%8A%A4%ED%8A%9C%EB%94%94%EC%98%A4-adb-%EC%97%B0%EA%B2%B0-adb-more-than-one-deviceemulator-%EC%97%90%EB%9F%AC</link>
            <guid>https://velog.io/@soyoung-dev/AndroidKotlin-%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-%EC%8A%A4%ED%8A%9C%EB%94%94%EC%98%A4-adb-%EC%97%B0%EA%B2%B0-adb-more-than-one-deviceemulator-%EC%97%90%EB%9F%AC</guid>
            <pubDate>Tue, 02 Apr 2024 09:33:10 GMT</pubDate>
            <description><![CDATA[<h1 id="💌-androidkotlin-android-studio-adb-연결">💌 [Android/Kotlin] Android Studio adb 연결</h1>
<p>안드로이드 스튜디오 (Android Studio Giraffe | 2022.3.1 Patch 1), 맥북 M1 사용 환경에서
Android 버전 9 (API level 28) 디바이스에 앱을 Run/Debug 해주려고 했으나,
USB 연결이 안되고 안드로이드 스튜디오 Wifi 무선 디버깅도 안드로이드 11 이상 부터 지원이 가능하였다.</p>
<p>터미널을 이용하여 adb 연결을 해주자.</p>
<h2 id="📌-adb-경로-확인">📌 adb 경로 확인</h2>
<p>안드로이드 스튜디오를 설치하면 기본으로 adb 가 내장되어 있다.
경로는 아래와 같다.</p>
<blockquote>
<p>/Users/사용자명/Library/Android/sdk/platform-tools/</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/soyoung-dev/post/9b9c9f7a-cfba-4ff4-93cd-76b7333c2fdf/image.png" alt=""></p>
<h3 id="🤦🏻♀️-맥북에서-adb-명령어를-실행하면-source-bash_profile-이-실행되지-않았을-때-다음과-같은-에러가-난다">🤦🏻‍♀️ 맥북에서 adb 명령어를 실행하면 source .bash_profile 이 실행되지 않았을 때 다음과 같은 에러가 난다.</h3>
<pre><code>zsh: command not found: adb</code></pre><h2 id="📌-source-bash_profile-실행">📌 source .bash_profile 실행</h2>
<pre><code>source .bash_profile</code></pre><p><img src="https://velog.velcdn.com/images/soyoung-dev/post/068ff00b-73fd-493d-8c68-a38bf754f63c/image.png" alt=""></p>
<h2 id="📌-adb-경로로-이동">📌 adb 경로로 이동</h2>
<pre><code>cd /Users/사용자명/Library/Android/sdk/platform-tools/   
</code></pre><h2 id="📌-adb-연결">📌 adb 연결</h2>
<p>PC와 디바이스가 동일한 네트워크 사용해야 함.</p>
<pre><code>adb connect ip주소</code></pre><h2 id="📌-연결된-adb-디바이스-확인">📌 연결된 adb 디바이스 확인</h2>
<pre><code>adb devices </code></pre><h2 id="📌-adb-shell">📌 adb shell</h2>
<pre><code>adb shell</code></pre><p><img src="https://velog.velcdn.com/images/soyoung-dev/post/da1a5850-a5ae-481a-b708-7a16f7eb0500/image.png" alt=""></p>
<blockquote>
<h2 id="🚨-adb-more-than-one-deviceemulator">🚨 adb: more than one device/emulator</h2>
<p>위와 같이 adb를 연결해주고 디버깅을 했는데 &quot;adb: more than one device/emulator&quot; 가 무제한으로 뜨고 화면이 멈춰있었다.</p>
</blockquote>
<h2 id="🩵-해결-방법">🩵 해결 방법</h2>
<ul>
<li>USB 연결된 디바이스가 2개가 되어서 발생한 에러였다.
다음과 같이 클리어 시켜준 뒤 터미널에서 재실행 시켜준다 !!</li>
</ul>
<pre><code>1. adb devices
2. adb -s 디바이스이름 shell
3. adb -s 디바이스이름 shell am clear-debug-app
4. adb -s 디바이스이름 shell</code></pre><p><img src="https://velog.velcdn.com/images/soyoung-dev/post/bbe0a8fc-52db-4a5e-ab00-48aac1824eef/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[IntelliJ] 맥(M1)에서 SVN 설치 및 프로젝트 불러오기]]></title>
            <link>https://velog.io/@soyoung-dev/IntelliJ-%EB%A7%A5M1%EC%97%90%EC%84%9C-SVN-%EC%84%A4%EC%B9%98-%EB%B0%8F-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EB%B6%88%EB%9F%AC%EC%98%A4%EA%B8%B0</link>
            <guid>https://velog.io/@soyoung-dev/IntelliJ-%EB%A7%A5M1%EC%97%90%EC%84%9C-SVN-%EC%84%A4%EC%B9%98-%EB%B0%8F-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EB%B6%88%EB%9F%AC%EC%98%A4%EA%B8%B0</guid>
            <pubDate>Fri, 23 Feb 2024 06:14:07 GMT</pubDate>
            <description><![CDATA[<h1 id="💌-맥m1에서-svn-설치-및-프로젝트-불러오기">💌 맥(M1)에서 SVN 설치 및 프로젝트 불러오기</h1>
<p>맥북(M1)에서 SVN 프로젝트 불러오려고 한다.
먼저 Homebrew 를 이용해서 SVN 을 설치해준다.</p>
<h2 id="📌-subversion-설치">📌 Subversion 설치</h2>
<p>터미널을 열어 홈브루를 이용해 아래 명령어로 SVN 을 설치해준다.</p>
<pre><code>% brew options subversion
% brew install subversion</code></pre><p><img src="https://velog.velcdn.com/images/soyoung-dev/post/655d76d9-0107-4cd5-bcb3-942c0aed6267/image.png" alt=""></p>
<h2 id="📌-svn-설치-경로-확인">📌 SVN 설치 경로 확인</h2>
<p>다음 명령어로 경로를 확인한다.</p>
<pre><code>% which svn</code></pre><p><img src="https://velog.velcdn.com/images/soyoung-dev/post/e2772d69-ff84-4db3-94af-22f6601b4417/image.png" alt=""></p>
<h2 id="📌-intellij-svn-설정하기">📌 IntelliJ SVN 설정하기</h2>
<p>Preferences &gt; Version Control &gt; Subversion &gt; Path to Subversion executable 의 경로를 변경해준다.
(위에서 확인했던 경로)</p>
<p><img src="https://velog.velcdn.com/images/soyoung-dev/post/70236551-0756-41a6-9a25-ab97cc99a0be/image.png" alt=""></p>
<h2 id="📌-프로젝트-가져오기">📌 프로젝트 가져오기</h2>
<p>IntelliJ 를 열어 Get from VCS 를 클릭한다.
<img src="https://velog.velcdn.com/images/soyoung-dev/post/bf01f302-0d76-4077-851f-fe9365a55cb9/image.png" alt=""></p>
<p>Version control &gt; Subversion 로 선택해준다.
<img src="https://velog.velcdn.com/images/soyoung-dev/post/4e64d15a-5f5c-4def-a0ab-90df28da7279/image.png" alt="">
New Repository Location &gt; Repository URL 을 입력해준다.
<img src="https://velog.velcdn.com/images/soyoung-dev/post/03949b1e-17fb-4199-b99f-c29b935a39c3/image.png" alt="">
계정 정보 입력.
<img src="https://velog.velcdn.com/images/soyoung-dev/post/744b8561-2293-4496-a63e-81a7870dc783/image.png" alt="">
프로젝트를 Checkout 해준다.
<img src="https://velog.velcdn.com/images/soyoung-dev/post/d56cfd31-aab0-44d0-8179-d8cf853c5335/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/soyoung-dev/post/e7643fb0-746c-4906-97fb-2649b087af2a/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Eclipse 에서 SVN 프로젝트 불러오기 / SVN 플러그인 설치]]></title>
            <link>https://velog.io/@soyoung-dev/Eclipse</link>
            <guid>https://velog.io/@soyoung-dev/Eclipse</guid>
            <pubDate>Tue, 20 Feb 2024 01:19:09 GMT</pubDate>
            <description><![CDATA[<h1 id="💌-eclipse-에서-svn-프로젝트-불러오기--svn-플러그인-설치">💌 Eclipse 에서 SVN 프로젝트 불러오기 / SVN 플러그인 설치</h1>
<p>이클립스에서 SVN Repository 에서 프로젝트를 불러오기 위해 SVN 플러그인을 설치해주자.</p>
<ul>
<li>사용중인 버전 : Eclipse IDE 2023-12 (4.30.0)</li>
</ul>
<h2 id="📌-eclipse-marketplace">📌 Eclipse Marketplace</h2>
<p>Help &gt; Eclipse Marketplace &gt; svn 을 검색해준다
<img src="https://velog.velcdn.com/images/soyoung-dev/post/914e18f6-51f6-400e-88a0-91b8c05be109/image.png" alt="">
Subversive - SVN Team Provider 4.8 설치
<img src="https://velog.velcdn.com/images/soyoung-dev/post/4039ade8-c6bc-4f24-84a6-deb9d2e3f797/image.png" alt="">
Confirm
<img src="https://velog.velcdn.com/images/soyoung-dev/post/89bf1e69-04b8-4cae-bcab-d7fc07af3159/image.png" alt="">
동의 후 Finish
<img src="https://velog.velcdn.com/images/soyoung-dev/post/049921cd-fd12-4c5d-ab69-05514a2fdb33/image.png" alt=""></p>
<h2 id="📌-svn-설치-확인">📌 SVN 설치 확인</h2>
<p>이클립스 재부팅 후 Window &gt; Show View &gt; Other 
<img src="https://velog.velcdn.com/images/soyoung-dev/post/dc00eee8-82e6-4942-868d-f909923a88a3/image.png" alt="">
svn 을 검색해서 다음과 같으면 설치가 완료된 것.
<img src="https://velog.velcdn.com/images/soyoung-dev/post/3ad2d496-7fb0-4b70-a457-a18ccb886781/image.png" alt=""></p>
<p>여기서 끝이 아니라 추가로 SVN Connector 설치가 필요하다.</p>
<h2 id="📌-svn-connector-설치">📌 SVN Connector 설치</h2>
<p>Help &gt; Install New Software...
<img src="https://velog.velcdn.com/images/soyoung-dev/post/170cbba9-1985-4647-a0c0-0c8192520197/image.png" alt="">
Add &gt; Location 에 <a href="https://osspit.org/eclipse/subversive-connectors/">https://osspit.org/eclipse/subversive-connectors/</a> 입력 후 Add
<img src="https://velog.velcdn.com/images/soyoung-dev/post/87b23e92-85d0-4f37-8d1e-262e817bb1d3/image.png" alt="">
Subversive SVN Connectors 체크 후 Next
<img src="https://velog.velcdn.com/images/soyoung-dev/post/5c8a03d3-a047-4ddd-b0c7-078ea1e432a1/image.png" alt="">
Finish
<img src="https://velog.velcdn.com/images/soyoung-dev/post/83f1dbeb-ab67-474a-804e-d8c4ec7825cb/image.png" alt=""></p>
<h2 id="📌-svn-connector-설치-확인">📌 SVN Connector 설치 확인</h2>
<p>Eclipse &gt; Settings &gt; Version Control (Team) &gt; SVN &gt; SVN Connector
에서 아래와 같이 설치 완료된 것을 확인 할 수 있음.
<img src="https://velog.velcdn.com/images/soyoung-dev/post/728669bb-6e6b-46e9-aa0f-c44cd5639015/image.png" alt=""></p>
<h2 id="📌-svn-프로젝트-불러오기">📌 SVN 프로젝트 불러오기</h2>
<p>이클립스 재부팅 후 Window &gt; Show View &gt; Other &gt; SVN Repositories &gt; Open
<img src="https://velog.velcdn.com/images/soyoung-dev/post/e562cac0-2905-45cb-9564-68a8914267fb/image.png" alt=""></p>
<p>오른쪽 마우스 클릭 &gt; New &gt; Repository Location...
<img src="https://velog.velcdn.com/images/soyoung-dev/post/89c78053-1b1d-40d1-b69d-9d348add51c9/image.png" alt=""></p>
<p>가져올 SVN Repository URL 과 계정 정보를 입력 &gt; Finish
<img src="https://velog.velcdn.com/images/soyoung-dev/post/c46ac8b4-1186-4211-b83b-1b2c8f22a602/image.png" alt="">
다음과 같이 뜨면 파일이 잘 받아와진 것!
<img src="https://velog.velcdn.com/images/soyoung-dev/post/d9a4547c-1e84-4801-b687-b3470819ab2d/image.png" alt=""></p>
<p>참고 : 
<a href="https://exhibitlove.tistory.com/184">https://exhibitlove.tistory.com/184</a>
<a href="https://velog.io/@joyoo1221/%EC%9D%B4%ED%81%B4%EB%A6%BD%EC%8A%A4-SVN-Connector">https://velog.io/@joyoo1221/%EC%9D%B4%ED%81%B4%EB%A6%BD%EC%8A%A4-SVN-Connector</a>
<a href="https://bba-jin.tistory.com/17">https://bba-jin.tistory.com/17</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Kotlin] Spring Boot 프로젝트 구현하기 (2) - Model 사용하기]]></title>
            <link>https://velog.io/@soyoung-dev/Kotlin-Spring-Boot-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0-2</link>
            <guid>https://velog.io/@soyoung-dev/Kotlin-Spring-Boot-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0-2</guid>
            <pubDate>Fri, 16 Feb 2024 02:29:13 GMT</pubDate>
            <description><![CDATA[<h1 id="💌-kotlin-spring-boot-프로젝트-구현하기-2---model-사용하기">💌 [Kotlin] Spring Boot 프로젝트 구현하기 (2) - Model 사용하기</h1>
<p><a href="https://velog.io/@soyoung-dev/Kotlin-Spring-boot">첫번째 포스팅</a> 처럼 단순히 창을 띄우는 것이 아닌 <strong>Controller 에서 Mustache 로 데이터를 옮기는 동작</strong>을 진행해보려 한다.</p>
<h2 id="📌-메소드에-model-추가">📌 메소드에 Model 추가</h2>
<p>메소드에 model 인자를 추가해주고 그 모델의 attribute 를 추가해준다.</p>
<pre><code>@Controller
class HtmlController {

    @GetMapping(&quot;/header&quot;)
    fun sendHeader(model: Model): String{
        val properties = System.getProperties() // System 정보 불러오기
        model[&quot;os_name&quot;] = properties.getProperty(&quot;os.name&quot;) // OS 명
        model[&quot;os_arch&quot;] = properties.getProperty(&quot;os.arch&quot;) // OS 환경, 인터페이스
        model[&quot;os_version&quot;] = properties.getProperty(&quot;os.version&quot;) // OS 버전
        return &quot;header&quot;
    }

}</code></pre><h2 id="📌-mustache-에서-데이터-출력">📌 Mustache 에서 데이터 출력</h2>
<p>model 에서 받은 데이터를 출력해준다.</p>
<pre><code>&lt;html&gt;
&lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;meta name=&quot;viewport&quot;
          content=&quot;width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0&quot;&gt;
    &lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;ie=edge&quot;&gt;
    &lt;title&gt;Document&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;ul&gt;
    &lt;li&gt;os_name ==&gt; {{os_name}} &lt;/li&gt;
    &lt;li&gt;os_arch ==&gt; {{os_arch}} &lt;/li&gt;
    &lt;li&gt;os_version ==&gt; {{os_version}} &lt;/li&gt;
&lt;/ul&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><h2 id="📌-결과-확인">📌 결과 확인</h2>
<p>application 을 재빌드 후 
localhost:8080/header url 로 이동하면 다음과 같이 데이터가 출력된다.
<img src="https://velog.velcdn.com/images/soyoung-dev/post/0b3e3fde-f9be-47bd-a119-98a226db8cd8/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Kotlin] Spring Boot 프로젝트 구현하기 (1) - template, Controller 사용하기]]></title>
            <link>https://velog.io/@soyoung-dev/Kotlin-Spring-boot</link>
            <guid>https://velog.io/@soyoung-dev/Kotlin-Spring-boot</guid>
            <pubDate>Tue, 13 Feb 2024 01:38:34 GMT</pubDate>
            <description><![CDATA[<h1 id="💌-kotlin-spring-boot-프로젝트-구현하기-1---template-controller-사용하기">💌 [Kotlin] Spring Boot 프로젝트 구현하기 (1) - template, Controller 사용하기</h1>
<p>Kotlin + Spring Boot를 이용하여 프로젝트를 구현해보려한다.</p>
<h2 id="📌-spring-이란">📌 Spring 이란</h2>
<ul>
<li>엔터프라이즈용 Java 애플리케이션 개발을 편하게 할 수 있게 해주는 오픈소스 경량급 애플리케이션 프레임워크,
즉 스프링을 더 쉽게 이용하기 위한 도구.<h2 id="📌-spring-boot-란">📌 Spring Boot 란</h2>
</li>
<li>스프링으로 애플리케이션을 만들 때에 필요한 설정을 간편하게 처리해주는 별도의 프레임워크 </li>
</ul>
<h2 id="📌-프로젝트-추가">📌 프로젝트 추가</h2>
<h3 id="spring-initializr">Spring initializr</h3>
<p><a href="https://start.spring.io/">https://start.spring.io/</a> 에 접속해서 다음과 같은 셋팅으로 프로젝트를 만들어준다.</p>
<h4 id="project">Project</h4>
<ul>
<li>Gradle - Kotlin<h4 id="language">Language</h4>
</li>
<li>Kotlin<h4 id="spring-boot">Spring Boot</h4>
</li>
<li>3.2.2<h4 id="packaging">Packaging</h4>
</li>
<li>Jar<h4 id="java">Java</h4>
</li>
<li>17<h4 id="dependencies-추가">Dependencies 추가</h4>
</li>
<li>Spring Web</li>
<li>Mustache</li>
<li>Spring Data JPA</li>
<li>H2 Database</li>
<li>Spring Boot DevTools
<img src="https://velog.velcdn.com/images/soyoung-dev/post/ca0974da-5891-42b1-a743-67e33461861c/image.png" alt=""></li>
</ul>
<h2 id="📌-프로젝트-open">📌 프로젝트 Open</h2>
<p>다운받은 zip 파일을 압축을 푼 후 IntelliJ 에서 열어준다.
<img src="https://velog.velcdn.com/images/soyoung-dev/post/7b40c48a-a4d6-46f6-b8c6-76b57a9e35a8/image.png" alt=""></p>
<h2 id="📌-gradle-설정">📌 Gradle 설정</h2>
<p>Preferences/Settings &gt; Build, Execution, Deployment &gt; Build Tools &gt; Gradle 로 이동
Build and run using/Run tests using 을 IntelliJ IDEA 로 설정.
Gradle JVM을 Java 17 이상으로 설정.</p>
<p><img src="https://velog.velcdn.com/images/soyoung-dev/post/1ba7e6e5-04fd-447d-9267-44fb3fcbad54/image.png" alt=""></p>
<h2 id="📌-application-빌드">📌 Application 빌드</h2>
<p><img src="https://velog.velcdn.com/images/soyoung-dev/post/d4654915-9491-4492-b90e-c1aae3408430/image.png" alt=""></p>
<p>Application 빌드 후 localhost:8080 url 로 이동하면 Whitelabel Error Page 가 뜨면 성공.
<img src="https://velog.velcdn.com/images/soyoung-dev/post/83f93658-ddd8-4136-a28c-00a1ac52627c/image.png" alt=""></p>
<h2 id="📌-spring-boot-실행">📌 Spring Boot 실행</h2>
<p>localhost:8080 을 들어가면 가장 먼저 나오는 화면을 만들어준다.
resources &gt; Static &gt; index.html
<img src="https://velog.velcdn.com/images/soyoung-dev/post/df4d4d9c-408d-4ef6-adb6-346565981a0a/image.png" alt=""></p>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
&lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;title&gt;Title&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;h1&gt;Kotlin Spring Boot&lt;/h1&gt;
&lt;p&gt;Hello World&lt;/p&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><p>다음과 같이 입력해주고 다시 애플리케이션 빌드를 시켜준 후 localhost:8080 으로 이동한다.
<img src="https://velog.velcdn.com/images/soyoung-dev/post/d54d83e6-63cb-4d8e-9213-f0eb25644c9c/image.png" alt="">
짠
<img src="https://velog.velcdn.com/images/soyoung-dev/post/8be427da-5db9-4b07-85ef-364b0b75c3b0/image.png" alt=""></p>
<h2 id="📌-mustache-템플릿-생성">📌 Mustache 템플릿 생성</h2>
<p>templates 경로에 mustache 파일을 생성해준다.
<img src="https://velog.velcdn.com/images/soyoung-dev/post/296d2f67-1dd1-4839-bcd5-9cfbe662c378/image.png" alt="">
회색 문서모양의 header.mustache 파일이 생성된다.
intelliJ에서 플러그인이 설치되어 있지 않아 제대로 해당 파일을 인식하지 못한 상태.</p>
<p>Setting &gt; Plugin &gt; Mustache 플러그인을 설치해준다
<img src="https://velog.velcdn.com/images/soyoung-dev/post/91443ed2-17e6-4bb2-94cb-f417ac424842/image.png" alt="">
처음에 빈 파일인데 doc을 입력 후 tab을 누르면 기본적인 내용으로 채워짐
<img src="https://velog.velcdn.com/images/soyoung-dev/post/56b79f85-b17b-4563-b8a4-08c14e28883a/image.png" alt="">
<img src="https://velog.velcdn.com/images/soyoung-dev/post/fc0f48e0-b0f2-422d-a931-9f51ab3b6f85/image.png" alt="">
다음과 같이 body 를 채워준다.
<img src="https://velog.velcdn.com/images/soyoung-dev/post/828e849d-e3e5-4c04-b7ec-6081be285f87/image.png" alt=""></p>
<h2 id="📌-controller-작성">📌 Controller 작성</h2>
<p><strong>@Controller</strong> 어노테이션을 추가해 해당 클래스가 컨트롤러임을 Spring Framework 에 알려줌.</p>
<h3 id="메소드-추가">메소드 추가</h3>
<p><strong>@GetMapping</strong> 어노테이션을 추가하여 /header 로 들어오는 데이터를 처리할 메소드 추가
<strong>header.mustache</strong> 파일을 찾아서 유저에게 보여줌. </p>
<pre><code>@Controller
class HtmlController {

    @GetMapping(&quot;/header&quot;)
    fun sendHeader(): String{
        return &quot;header&quot;
    }
}</code></pre><h2 id="📌-결과">📌 결과</h2>
<p>localhost:8080/header url로 이동하면 다음과 같이 결과가 잘 뜨면 성공!
<img src="https://velog.velcdn.com/images/soyoung-dev/post/21b2a729-6788-45a1-97a6-438bc4ef04d2/image.png" alt=""></p>
<p>참고 : <a href="https://spring.io/guides/tutorials/spring-boot-kotlin">https://spring.io/guides/tutorials/spring-boot-kotlin</a>
<a href="https://melonicedlatte.com/2021/07/25/202100.html">https://melonicedlatte.com/2021/07/25/202100.html</a>
<a href="https://onedelay.github.io/2018/11/14/kotlin-spring-boot-1/">https://onedelay.github.io/2018/11/14/kotlin-spring-boot-1/</a>
<a href="https://velog.io/@lyh990517/Back-end-Kotlin-Spring-boot%EC%9C%BC%EB%A1%9C-%EA%B0%84%EB%8B%A8%ED%95%9C-API%EB%A5%BC-%EB%A7%8C%EB%93%A4%EC%96%B4%EB%B3%B4%EC%9E%90">https://velog.io/@lyh990517/Back-end-Kotlin-Spring-boot%EC%9C%BC%EB%A1%9C-%EA%B0%84%EB%8B%A8%ED%95%9C-API%EB%A5%BC-%EB%A7%8C%EB%93%A4%EC%96%B4%EB%B3%B4%EC%9E%90</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[iOS/Error] Command PhaseScriptExecution failed with a nonzero exit code
]]></title>
            <link>https://velog.io/@soyoung-dev/SwiftError-Command-PhaseScriptExecution-failed-with-a-nonzero-exit-code</link>
            <guid>https://velog.io/@soyoung-dev/SwiftError-Command-PhaseScriptExecution-failed-with-a-nonzero-exit-code</guid>
            <pubDate>Thu, 11 Jan 2024 08:42:40 GMT</pubDate>
            <description><![CDATA[<h1 id="💌-swifterror-command-phasescriptexecution-failed-with-a-nonzero-exit-code">💌 [Swift/Error] Command PhaseScriptExecution failed with a nonzero exit code</h1>
<p>Xcode 15.2 에서 iOS 프로젝트 빌드를 하다 오류가 났다.</p>
<p><img src="https://velog.velcdn.com/images/soyoung-dev/post/8ace9291-863a-4aeb-9b71-0bc5a49cc0fe/image.png" alt=""></p>
<pre><code>Command PhaseScriptExecution failed with a nonzero exit code
</code></pre><p>검색해보니 프로젝트 경로 <strong>ProjectName/Pods/Target Support Files/Pods-ProjectName</strong>
의 <strong>Pods-ProjectName-frameworks.sh</strong> 파일에서</p>
<pre><code>source=&quot;$(readlink &quot;${source}&quot;)&quot; </code></pre><p>를</p>
<pre><code>source=&quot;$(readlink -f &quot;${source}&quot;)&quot; </code></pre><p>로 변경해주라고 했다.</p>
<p>변경해도 동일한 빌드 에러 ..ㅠ</p>
<p>두번째 해결방법으로는 또 <strong>Pods-ProjectName-frameworks.sh</strong> 파일에서 
set -e 부분을 +e로 수정해주는 것 이었다.</p>
<pre><code>#!/bin/sh
set +e
set -u
set -o pipefail</code></pre><p>로 수정해줘도 동일했다 ㅠ</p>
<h2 id="📌-해결-방법">📌 해결 방법</h2>
<p>마지막으로 <strong>Build Settings &gt; Excluded Architectures 에서 arm64 를 삭제</strong>해주는 것이었다.
그랬더니 빌드 성공 !!🥹</p>
<p>참고 : <a href="https://github.com/facebook/react-native/issues/36762">https://github.com/facebook/react-native/issues/36762</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Swift/Error] Sandbox: codesign(86688) deny(1) 에러 / ENABLE_USER_SCRIPT_SANDBOXING]]></title>
            <link>https://velog.io/@soyoung-dev/SwiftError-Sandbox-codesign86688-deny1-%EC%97%90%EB%9F%AC-ENABLEUSERSCRIPTSANDBOXING</link>
            <guid>https://velog.io/@soyoung-dev/SwiftError-Sandbox-codesign86688-deny1-%EC%97%90%EB%9F%AC-ENABLEUSERSCRIPTSANDBOXING</guid>
            <pubDate>Thu, 11 Jan 2024 08:07:14 GMT</pubDate>
            <description><![CDATA[<h1 id="💌-swifterror-sandbox-codesign86688-deny1-에러--enable_user_script_sandboxing">💌 [Swift/Error] Sandbox: codesign(86688) deny(1) 에러 / ENABLE_USER_SCRIPT_SANDBOXING</h1>
<p>iOS 프로젝트 중 ENABLE_USER_SCRIPT_SANDBOXING 빌드 에러가 났다.
<img src="https://velog.velcdn.com/images/soyoung-dev/post/66439639-40c7-469a-bd12-d93779d2fd39/image.png" alt=""></p>
<pre><code>Sandbox: codesign(86688) deny(1) file-read-data /Users/.../Library/Developer/Xcode/DerivedData/...-bmorfhjfhicmvzakukoxywaojdne/Build/Products/Debug-iphoneos/MyWebRTC.app/Frameworks/SocketRocket.framework/Info.plist

</code></pre><p>검색해보니 프로젝트의 빌드 설정에서 ENABLE_USER_SCRIPT_SANDBOXING 가 비활성화 되어있는지 확인하라고 하였다.</p>
<h2 id="📌-해결-방법">📌 해결 방법</h2>
<p>프로젝트 &gt; Build Settings &gt; Build Options &gt; User Script Sandboxing 옵션을 NO 로 수정해준다.
<img src="https://velog.velcdn.com/images/soyoung-dev/post/268960ef-7303-46bf-853e-32caee8e03d1/image.png" alt=""></p>
<p>참고 : <a href="https://developer.apple.com/forums/thread/731041">https://developer.apple.com/forums/thread/731041</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Kotlin/Error] java.io.EOFException: End of input at line 1 column 1 path  ]]></title>
            <link>https://velog.io/@soyoung-dev/KotlinError-java.io.EOFException-End-of-input-at-line-1-column-1-path</link>
            <guid>https://velog.io/@soyoung-dev/KotlinError-java.io.EOFException-End-of-input-at-line-1-column-1-path</guid>
            <pubDate>Fri, 08 Dec 2023 04:09:41 GMT</pubDate>
            <description><![CDATA[<h1 id="💌-kotlinerror-javaioeofexception-end-of-input-at-line-1-column-1-path">💌 [Kotlin/Error] java.io.EOFException: End of input at line 1 column 1 path</h1>
<p>Retrofit2 를 이용해 API 통신을 하던 도중 오류가 떴다.
<img src="https://velog.velcdn.com/images/soyoung-dev/post/52256087-aa4b-4859-be95-e61424d7a387/image.png" alt=""></p>
<blockquote>
<p>java.io.EOFException: End of input at line 1 column 1 path $</p>
</blockquote>
<p>찾아보니 Response body 값이 null 일 때 발생하는 오류였다.</p>
<h2 id="📌-해결-방법">📌 해결 방법</h2>
<ul>
<li><p>🩵 Retrofit2 API 통신 시 응답 값이 없을 때 오류 처리를 위한 클래스 만들어준다.</p>
<pre><code>class NullOnEmptyConverterFactory : Converter.Factory() {
  fun converterFactory() = this
  override fun responseBodyConverter(type: Type, annotations: Array&lt;out Annotation&gt;, retrofit: Retrofit) = object : Converter&lt;ResponseBody, Any?&gt; {
      val nextResponseBodyConverter = retrofit.nextResponseBodyConverter&lt;Any?&gt;(converterFactory(), type, annotations)
      override fun convert(value: ResponseBody) = if (value.contentLength() != 0L) {
          try{
              nextResponseBodyConverter.convert(value)
          }catch (e:Exception){
              e.printStackTrace()
              null
          }
      } else{
          null
      }
  }
}</code></pre></li>
<li><p>🩵 Retrofit 빌더를 이용해 만들어 줄 때 addConverterFactory 에 적용해준다.</p>
</li>
</ul>
<pre><code>val retrofit: Retrofit
    get() = Retrofit.Builder()
        .baseUrl(URL)
        .addConverterFactory(NullOnEmptyConverterFactory())
        .addConverterFactory(GsonConverterFactory.create())
        .build()</code></pre><h3 id="🚨-주의할-점">🚨 주의할 점</h3>
<blockquote>
<p> ** NullOnEmptyConverterFactory 설정을 GsonConverterFactory 보다 더 위에 설정해줘야한다.
아래에 하면 Exception 이 그대로 발생함 !**</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Android/Warning] 'create(MediaType?, String): RequestBody' is deprecated. ]]></title>
            <link>https://velog.io/@soyoung-dev/AndroidcreateMediaType-String-RequestBody-is-deprecated.-Moved-to-extension-function.-Put-the-content-argument-first-to-fix-Java</link>
            <guid>https://velog.io/@soyoung-dev/AndroidcreateMediaType-String-RequestBody-is-deprecated.-Moved-to-extension-function.-Put-the-content-argument-first-to-fix-Java</guid>
            <pubDate>Mon, 13 Nov 2023 23:25:07 GMT</pubDate>
            <description><![CDATA[<h1 id="💌-androidwarning-createmediatype-string-requestbody-is-deprecated-moved-to-extension-function-put-the-content-argument-first-to-fix-java">💌 [Android/Warning] &#39;create(MediaType?, String): RequestBody&#39; is deprecated. Moved to extension function. Put the &#39;content&#39; argument first to fix Java</h1>
<p><img src="https://velog.velcdn.com/images/soyoung-dev/post/7d782c4a-b20f-4729-a934-fdb475ee8db4/image.png" alt=""></p>
<blockquote>
<p>&#39;create(MediaType?, String): RequestBody&#39; is deprecated. Moved to extension function. Put the &#39;content&#39; argument first to fix Java</p>
</blockquote>
<p>코틀린으로 안드로이드 프로젝트를 하는 중 오류가 떴다.</p>
<p>okhttp 버전에 따른 RequestBody 를 만드는 방법이 변경되었다고 한다.</p>
<h3 id="📌-3x-버전에는-기존-대로-createmediatype-string-requestbody-를-사용">📌 3.X~ 버전에는 기존 대로 &#39;create(MediaType?, String): RequestBody&#39; 를 사용</h3>
<pre><code>val body = RequestBody.create(&quot;application/json&quot;.toMediaTypeOrNull(), obj)</code></pre><h3 id="📌-4x-버전에서는-변경된-contenttorequestbodymediatype-string-requestbody-를-사용">📌 4.X~ 버전에서는 변경된 &#39;content.toRequestBody(MediaType?, String): RequestBody&#39; 를 사용</h3>
<pre><code>val body = obj.toRequestBody(&quot;application/json&quot;.toMediaTypeOrNull())</code></pre><p>참고 : <a href="https://stackoverflow.com/questions/60240980/okhttp3-requestbody-create-companion-deprecated-docs-confusing">https://stackoverflow.com/questions/60240980/okhttp3-requestbody-create-companion-deprecated-docs-confusing</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Swift/Error] Lost connection to the debugger on (Xcode 15.0.1)]]></title>
            <link>https://velog.io/@soyoung-dev/iOSError-Lost-connection-to-the-debugger-on-Xcode-15.0.1</link>
            <guid>https://velog.io/@soyoung-dev/iOSError-Lost-connection-to-the-debugger-on-Xcode-15.0.1</guid>
            <pubDate>Fri, 27 Oct 2023 02:23:41 GMT</pubDate>
            <description><![CDATA[<h1 id="💌-ioserror-lost-connection-to-the-debugger-on-xcode-1501">💌 [iOS/Error] Lost connection to the debugger on (Xcode 15.0.1)</h1>
<p>Xcode 15.0.1 버전과 iOS 17.0.3 으로 아이폰과 맥북 USB를 연결해 앱 테스트 빌드 후 디버깅을 하는데 오류가 뜬다.</p>
<p><img src="https://velog.velcdn.com/images/soyoung-dev/post/30c54c06-f542-4688-8d04-ca049453123b/image.png" alt=""></p>
<p>첫번째 해결 방법은 iPhone에서 개발자 모드를 활성화하기..</p>
<h3 id="📌-1-아이폰-개발자-모드-켜기">📌 1. 아이폰 개발자 모드 켜기</h3>
<blockquote>
<p>설정 &gt; 개인정보 보호 및 보안 &gt; 맨 아래 “개발자모드” 켜기 후 재시동</p>
</blockquote>
<p>-&gt; 해결 안됨 ㅎㅎ</p>
<p>두번째 해결 방법은 멀티패스 네트워크 끄기</p>
<h3 id="📌2-멀티패스-네트워크-끄기">📌2. 멀티패스 네트워크 끄기</h3>
<blockquote>
<p>iPhone의 설정 &gt; 개발자에서 &quot;멀티패스 네트워크&quot;를 비활성화</p>
</blockquote>
<p>-&gt; 이미 비활성화 상태였어서 해결 안됨 ㅎㅎ</p>
<h3 id="📌-3-unpair-device">📌 3. Unpair Device</h3>
<p>🩵 내가 해결한 방법은 장치를 언페어링 했다가 다시 페어링 하기 
 window &gt; Device and Simulators &gt; 연결된 디바이스 오른쪽 마우스 &gt; Unpair Device
 or 상단에 연결된 디바이스/시뮬레이터 창 누른 후 맨 밑 Manage Run Destinations 클릭
<img src="https://velog.velcdn.com/images/soyoung-dev/post/be9ead67-e9ab-463a-89dc-0fbe9a2f1fb6/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/soyoung-dev/post/dd026151-ba1f-49fb-b46a-e58a921917f1/image.png" alt=""></p>
<p> ➕ 또 다른 방법으로는 맥북과 아이폰을 무선 연결하는 방법도 있음 ❕</p>
<p>참고 : <a href="https://developer.apple.com/forums/thread/737907">https://developer.apple.com/forums/thread/737907</a>
<a href="https://developer.apple.com/forums/thread/735705">https://developer.apple.com/forums/thread/735705</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[iOS/Swift] iOS 푸시알림 사운드,진동 넣는 법 (Postman 테스트)]]></title>
            <link>https://velog.io/@soyoung-dev/iOSSwift-iOS-%ED%91%B8%EC%8B%9C%EC%95%8C%EB%A6%BC-%EC%82%AC%EC%9A%B4%EB%93%9C%EC%A7%84%EB%8F%99-%EB%84%A3%EB%8A%94-%EB%B2%95-Postman-%ED%85%8C%EC%8A%A4%ED%8A%B8</link>
            <guid>https://velog.io/@soyoung-dev/iOSSwift-iOS-%ED%91%B8%EC%8B%9C%EC%95%8C%EB%A6%BC-%EC%82%AC%EC%9A%B4%EB%93%9C%EC%A7%84%EB%8F%99-%EB%84%A3%EB%8A%94-%EB%B2%95-Postman-%ED%85%8C%EC%8A%A4%ED%8A%B8</guid>
            <pubDate>Thu, 26 Oct 2023 07:04:31 GMT</pubDate>
            <description><![CDATA[<h1 id="💌-iosswift-ios-푸시알림-사운드진동-넣는-법-postman-테스트">💌 [iOS/Swift] iOS 푸시알림 사운드,진동 넣는 법 (Postman 테스트)</h1>
<p>iOS 프로젝트 푸시 테스트를 하던 중 발견한 부분이 있었다.</p>
<blockquote>
<ol>
<li>PUSH 알림 발생 시 알림음(소리/진동)이 나오지 않음</li>
<li>화면 잠금 시 PUSH 알림이 오면 애플워치, 핸드폰 둘다 소리/진동 알림 없이 스택에만 표시됨</li>
</ol>
</blockquote>
<p>평소에 알림 진동/소리를 켜지않고 무음으로만 해서 발견하지 못했던 부분이라 찾아봤는데
iOS 푸시 알림음은 서버에서 처리를 해주어야 한다는 것..😂❕</p>
<p>그래서 포스트맨으로 테스트를 해보았다.</p>
<h2 id="📌-postman-으로-푸시-테스트-하기">📌 Postman 으로 푸시 테스트 하기</h2>
<blockquote>
<ol>
<li>포스트맨에서 새 워크스페이스 생성 후 HTTP Request 생성,</li>
<li>POST 로 변경 후 <a href="https://fcm.googleapis.com/fcm/send">https://fcm.googleapis.com/fcm/send</a> 로 입력.</li>
<li>헤더에 Content-Type 을 application/json 로 입력,</li>
<li>헤더에 Authorization 에 key=FCM 서버키 입력.</li>
</ol>
</blockquote>
<p><img src="https://velog.velcdn.com/images/soyoung-dev/post/bdf68947-ef26-4efe-86e9-52c354df0f70/image.png" alt=""></p>
<h2 id="📌-푸시에-사운드-추가">📌 푸시에 사운드 추가</h2>
<p>기존 iOS 푸시 데이터 바디는 아래와 같았다.</p>
<pre><code>{
    &quot;to&quot;: &quot;푸시토큰&quot;,
    &quot;content_available&quot;: true, 
    &quot;notification&quot;: {
        &quot;title&quot;: &quot;fcm test 1&quot;,
        &quot;body&quot;: &quot;postman 1&quot;
    }
}</code></pre><p>보낼 푸시 데이터에 sound 페이로드를 추가해줘야 iOS 푸시알림에 사운드(진동/소리)가 난다.</p>
<pre><code>{
    &quot;to&quot;: &quot;푸시토큰&quot;,
    &quot;content_available&quot;: true, 
    &quot;notification&quot;: {
        &quot;title&quot;: &quot;fcm test 1&quot;,
        &quot;body&quot;: &quot;postman 1&quot;,
        &quot;sound&quot; : &quot;default&quot;
    }
}</code></pre><p><strong>default</strong> 로 보내주면 기본 알림음.
프로젝트 파일 내에 sound 파일을 추가해준 후, sound 파일 명을 보내주면 원하는 소리를 울리게 할 수 있음!
(단 30초 미만의 aiff, wav또는 caf파일이어야 함)</p>
<p>참고 : <a href="https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/generating_a_remote_notification">https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/generating_a_remote_notification</a>
<a href="https://developer.apple.com/documentation/usernotifications/unnotificationsound">https://developer.apple.com/documentation/usernotifications/unnotificationsound</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Android/Kotlin] 구글스토어 배포 시 "App Bundle 유형과 연결된 가독화 파일이 없습니다." 경고 해결]]></title>
            <link>https://velog.io/@soyoung-dev/AndroidKotlin-%EA%B5%AC%EA%B8%80%EC%8A%A4%ED%86%A0%EC%96%B4-%EB%B0%B0%ED%8F%AC-%EC%8B%9C-App-Bundle-%EC%9C%A0%ED%98%95%EA%B3%BC-%EC%97%B0%EA%B2%B0%EB%90%9C-%EA%B0%80%EB%8F%85%ED%99%94-%ED%8C%8C%EC%9D%BC%EC%9D%B4-%EC%97%86%EC%8A%B5%EB%8B%88%EB%8B%A4.-%EA%B2%BD%EA%B3%A0-%ED%95%B4%EA%B2%B0</link>
            <guid>https://velog.io/@soyoung-dev/AndroidKotlin-%EA%B5%AC%EA%B8%80%EC%8A%A4%ED%86%A0%EC%96%B4-%EB%B0%B0%ED%8F%AC-%EC%8B%9C-App-Bundle-%EC%9C%A0%ED%98%95%EA%B3%BC-%EC%97%B0%EA%B2%B0%EB%90%9C-%EA%B0%80%EB%8F%85%ED%99%94-%ED%8C%8C%EC%9D%BC%EC%9D%B4-%EC%97%86%EC%8A%B5%EB%8B%88%EB%8B%A4.-%EA%B2%BD%EA%B3%A0-%ED%95%B4%EA%B2%B0</guid>
            <pubDate>Fri, 06 Oct 2023 02:25:46 GMT</pubDate>
            <description><![CDATA[<h1 id="💌-androidkotlin-구글스토어-배포-시-app-bundle-유형과-연결된-가독화-파일이-없습니다-경고-해결">💌 [Android/Kotlin] 구글스토어 배포 시 &quot;App Bundle 유형과 연결된 가독화 파일이 없습니다.&quot; 경고 해결</h1>
<p>구글 스토어에 AAB 파일을 업로드 하려는 중 경고가 떴다. 
<img src="https://velog.velcdn.com/images/soyoung-dev/post/14355782-d5c5-49be-9bd0-3fefa7fa0469/image.png" alt=""></p>
<blockquote>
<p>이 App Bundle 유형과 연결된 가독화 파일이 없습니다. 난독화된 코드(R8/proguard)를 사용하는 경우 가독화 파일을 업로드하면 비정상 종료 및 ANR을 더 쉽게 분석하고 디버그할 수 있습니다. R8/proguard를 사용하면 앱 크기를 줄이는 데 도움이 됩니다. <a href="https://developer.android.com/studio/build/shrink-code?hl=ko#decode-stack-trace">자세히 알아보기</a></p>
</blockquote>
<h2 id="📌-해결-방법">📌 해결 방법</h2>
<p>모듈수준의 build.gradle &gt; release 의 <strong>minifyEnabled true</strong> 로 변경 &gt; Sync Now
<img src="https://velog.velcdn.com/images/soyoung-dev/post/e84002b0-ad6f-470b-bd85-118063f68525/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Android/Error] Cause: failed to decrypt safe contents entry: java.io.IOException: getSecretKey failed: Password is not ASCII]]></title>
            <link>https://velog.io/@soyoung-dev/AndroidKotlin-cause-failed-to-decrypt-safe-contents-entry-java.io.ioexception-getsecretkey-failed-password-is-not-ascii</link>
            <guid>https://velog.io/@soyoung-dev/AndroidKotlin-cause-failed-to-decrypt-safe-contents-entry-java.io.ioexception-getsecretkey-failed-password-is-not-ascii</guid>
            <pubDate>Mon, 11 Sep 2023 10:07:23 GMT</pubDate>
            <description><![CDATA[<h1 id="💌-androiderror-cause-failed-to-decrypt-safe-contents-entry-javaioioexception-getsecretkey-failed-password-is-not-ascii">💌 [Android/Error] Cause: failed to decrypt safe contents entry: java.io.IOException: getSecretKey failed: Password is not ASCII</h1>
<p>안드로이드 앱을 빌드해서 사인한 AAB/APK 을 만들던 중 오류가 났다. 
[Build &gt; Generate Signed Bundle or APK]</p>
<p><img src="https://velog.velcdn.com/images/soyoung-dev/post/ab10d2cb-0b80-4009-b7c4-d704ba40c8e9/image.png" alt=""></p>
<blockquote>
<p>cause: failed to decrypt safe contents entry: java.io.ioexception: getsecretkey failed: password is not ascii</p>
</blockquote>
<h2 id="🩵-해결-방법">🩵 해결 방법</h2>
<h3 id="🚨-key-store-password-key-password-가-한글로-되어있지-않은-지-체크-🚨">🚨 key Store Password, Key Password 가 한글로 되어있지 않은 지 체크 🚨</h3>
<p>안드로이드 스튜디오에서 비밀번호 입력 시 한글과 영어를 구분하므로, 제일 먼저 꼭 체크 후 영어로 비밀번호를 직접 키보드로 타이핑한다.</p>
<p>-&gt; 안되면 Export encrypted key for enrolling published apps in Google Play App Signing 체크 해제 후 다시 빌드</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Android/Kotlin] 앱 삭제 후 재설치해도 앱 데이터가 남아있는 경우]]></title>
            <link>https://velog.io/@soyoung-dev/AndroidKotlin-%EC%95%B1-%EC%82%AD%EC%A0%9C-%ED%9B%84-%EC%9E%AC%EC%84%A4%EC%B9%98%ED%95%B4%EB%8F%84-%EC%95%B1-%EB%8D%B0%EC%9D%B4%ED%84%B0%EA%B0%80-%EB%82%A8%EC%95%84%EC%9E%88%EB%8A%94-%EA%B2%BD%EC%9A%B0</link>
            <guid>https://velog.io/@soyoung-dev/AndroidKotlin-%EC%95%B1-%EC%82%AD%EC%A0%9C-%ED%9B%84-%EC%9E%AC%EC%84%A4%EC%B9%98%ED%95%B4%EB%8F%84-%EC%95%B1-%EB%8D%B0%EC%9D%B4%ED%84%B0%EA%B0%80-%EB%82%A8%EC%95%84%EC%9E%88%EB%8A%94-%EA%B2%BD%EC%9A%B0</guid>
            <pubDate>Wed, 06 Sep 2023 02:13:37 GMT</pubDate>
            <description><![CDATA[<h1 id="💌-androidkotlin-앱-삭제-후-재설치해도-앱-데이터가-남아있는-경우">💌 [Android/Kotlin] 앱 삭제 후 재설치해도 앱 데이터가 남아있는 경우</h1>
<h3 id="✅-android-10-api-29-디바이스-기준">✅ Android 10 (API 29) 디바이스 기준</h3>
<p>설정&gt; 계정 및 백업&gt; 백업 및 복원&gt; 자동 복원이 켜져있는지 확인.</p>
<p><img src="https://velog.velcdn.com/images/soyoung-dev/post/2db31f34-413f-42f3-bb5e-1790657df2df/image.png" alt=""></p>
<p>Android 6.0(API 수준 23) 이상을 타겟팅하는 앱은 자동 백업에 자동으로 포함된다.</p>
<h2 id="📌-androidmanifestxml">📌 AndroidManifest.xml</h2>
<pre><code>android:allowBackup=&quot;false&quot;
android:fullBackupOnly=&quot;false&quot;
android:fullBackupContent=&quot;false&quot;</code></pre><ul>
<li><strong>allowBackup</strong> : 이 속성은 시스템이 사용자 데이터를 백업하고 복원할 수 있는지 여부를 설정</li>
<li><strong>fullBackupOnly</strong> :  이 속성은 앱의 데이터를 전체 백업만 허용할지 여부를 지정</li>
<li><strong>fullBackupContent</strong> : 이 속성은 Auto Backup을 사용할 때 어떤 파일들이 백업 대상에 포함되거나 제외될지를 정의하는 XML 파일을 지정</li>
</ul>
<p>true 로 설정하면 앱 삭제 후 재설치해도 앱 데이터가 남아있는 경우가 있고, 
보안 상의 문제로 false 로 설정하는 것이 좋다.</p>
<p>참고 : <a href="https://developer.android.com/guide/topics/data/autobackup?hl=ko#EnablingAutoBackup">https://developer.android.com/guide/topics/data/autobackup?hl=ko#EnablingAutoBackup</a>
<a href="https://dazemonkey.tistory.com/42">https://dazemonkey.tistory.com/42</a>
<a href="https://lifeinprogram.tistory.com/31">https://lifeinprogram.tistory.com/31</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Android/Error] Error inflating class ImageView]]></title>
            <link>https://velog.io/@soyoung-dev/AndroidKotlin-Error-inflating-class-ImageView</link>
            <guid>https://velog.io/@soyoung-dev/AndroidKotlin-Error-inflating-class-ImageView</guid>
            <pubDate>Wed, 06 Sep 2023 01:08:55 GMT</pubDate>
            <description><![CDATA[<h1 id="💌-androidkotlin-error-inflating-class-imageview">💌 [Android/Kotlin] Error inflating class ImageView</h1>
<p>안드로이드 6 (API 수준 23) 기기에서 테스트를 하려고 빌드를 했는데 Exception 이 떴다.</p>
<p><img src="https://velog.velcdn.com/images/soyoung-dev/post/cc46e867-b22d-4c7b-8365-c43bcaf5865c/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/soyoung-dev/post/cd0f28bf-fdf4-40c4-af83-44641d3b1ff7/image.png" alt=""></p>
<blockquote>
<p>Caused by: android.view.InflateException: Binary XML file line #10: Binary XML file line #10: Error inflating class ImageView</p>
</blockquote>
<p>에러난 이미지 파일이 drowable 폴더에 없고 drowable-v24 폴더에 있어서 그렇다.</p>
<h2 id="📌-해결방법">📌 해결방법</h2>
<p>에러난 이미지를 복사해서 drawable 폴더에 붙여넣기 한 후 빌드헤준다.
<img src="https://velog.velcdn.com/images/soyoung-dev/post/456cf596-48f4-445f-8bb0-2f2ef4905a98/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/soyoung-dev/post/c7488202-5428-434c-813b-9b67ff14d892/image.png" alt=""></p>
<p>참고 : <a href="https://stackoverflow.com/questions/47526417/binary-xml-file-line-0-error-inflating-class-imageview">https://stackoverflow.com/questions/47526417/binary-xml-file-line-0-error-inflating-class-imageview</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[iOS/Swift] WKWebView mailto: 태그 메일 보내기 기능 (기본 메일 앱 열기)]]></title>
            <link>https://velog.io/@soyoung-dev/iOSSwift-WKWebView-mailto-%ED%83%9C%EA%B7%B8-%EB%A9%94%EC%9D%BC-%EB%B3%B4%EB%82%B4%EA%B8%B0-%EA%B8%B0%EB%8A%A5-%EA%B8%B0%EB%B3%B8-%EB%A9%94%EC%9D%BC-%EC%95%B1-%EC%97%B4%EA%B8%B0</link>
            <guid>https://velog.io/@soyoung-dev/iOSSwift-WKWebView-mailto-%ED%83%9C%EA%B7%B8-%EB%A9%94%EC%9D%BC-%EB%B3%B4%EB%82%B4%EA%B8%B0-%EA%B8%B0%EB%8A%A5-%EA%B8%B0%EB%B3%B8-%EB%A9%94%EC%9D%BC-%EC%95%B1-%EC%97%B4%EA%B8%B0</guid>
            <pubDate>Wed, 23 Aug 2023 07:32:32 GMT</pubDate>
            <description><![CDATA[<h1 id="💌-iosswift-wkwebview-mailto-태그-메일-보내기-기능-기본-메일-앱-열기">💌 [iOS/Swift] WKWebView mailto: 태그 메일 보내기 기능 (기본 메일 앱 열기)</h1>
<p>웹뷰에서 mailto: 태그로 되어있는 링크를 누르면 기본 메일 앱을 열어주려고 한다.</p>
<h2 id="📌-uiwebviewdelegate">📌 UIWebViewDelegate</h2>
<p><strong>UIWebViewDelegate</strong> 프로토콜 메소드에서 링크 클릭 이벤트를 처리해주어야 한다.
링크가 mailto: 형식인 경우에 대한 처리를 구현해주면 된다.</p>
<pre><code>mailto:abc@com</code></pre><p>형태로 오기때문에 <a href="https://developer.apple.com/documentation/foundation/nsstring/1412937-replacingoccurrences">replacingOccurrences</a> 메소드를 이용하여 &quot;mailto:&quot;를 제거하고 이메일 주소를 추출했다.</p>
<pre><code>extension WebViewController: UIWebViewDelegate, MFMailComposeViewControllerDelegate {
    func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -&gt; Void) {
        if let url = navigationAction.request.url, url.scheme == &quot;mailto&quot; { // &#39;mailto:&#39; 링크 클릭 이벤트 처리
            let emailAddress = url.absoluteString.replacingOccurrences(of: &quot;mailto:&quot;, with: &quot;&quot;)
            openMailCompose(emailAddress: emailAddress)
            decisionHandler(.cancel)
        } else {
            decisionHandler(.allow)
        }
    }
}</code></pre><h2 id="📌-mfmailcomposeviewcontrollerdelegate">📌 MFMailComposeViewControllerDelegate</h2>
<p><strong>MFMailComposeViewController</strong> 를 사용하여 기본 메일 앱을 열어주면 된다.
<a href="https://developer.apple.com/documentation/messageui/mfmailcomposeviewcontroller/1616879-cansendmail">MFMailComposeViewController.canSendMail()</a> 을 이용하여 기기에서 이메일을 보낼 수 있는지 여부를 판단한다.
가능하면 아까 위에서 추출한 이메일 주소를 수신자에 지정해준다.
이메일 보내기 기능을 지원하지 않는 기기일 경우 사용자에게 <a href="https://developer.apple.com/documentation/uikit/uialertcontroller">UIAlertController</a> 을 이용해 간단하게 알림창을 띄워주도록 구현했다.
사용자가 이메일을 보내거나 인터페이스를 취소하기 위해 버튼을 탭하면 메일 작성 뷰 컨트롤러가 닫히도록  <a href="https://developer.apple.com/documentation/messageui/mfmailcomposeviewcontrollerdelegate/1616880-mailcomposecontroller">mailComposeController</a> 구현.</p>
<pre><code>import MessageUI

// 이메일 작성 화면 표시
func openMailCompose(emailAddress: String) {
    if MFMailComposeViewController.canSendMail() {
        let mailComposeVC = MFMailComposeViewController()
        mailComposeVC.mailComposeDelegate = self
        mailComposeVC.setToRecipients([emailAddress])

        present(mailComposeVC, animated: true, completion: nil)
    } else {
        // 이 기기에서는 이메일 보내기 기능을 지원하지 않음
        let alertController = UIAlertController(title: &quot;알림&quot;, message: &quot;이 기기에서는 이메일 보내기 기능을 지원하지 않습니다.&quot;, preferredStyle: .alert)

        let okayAction = UIAlertAction(title: &quot;확인&quot;, style: .default, handler: nil)
        alertController.addAction(okayAction)

        present(alertController, animated: true, completion: nil)
    }
}

// 메일 작성 완료 후 호출
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
    controller.dismiss(animated: true, completion: nil)
}</code></pre><blockquote>
<p>🚨 구현해줬는데 링크를 클릭해도 아무런 반응이 없었다.
웹뷰 컨트롤러에서 WKNavigationDelegate 설정해주는 걸 빼먹었었다 ㅠ
웹페이지 로딩상황을 추적하거나 콘텐츠나 사용자의 액션을 제어할 수 있도록 델리게이트 설정해주는걸 잊지말자
<strong>webView.navigationDelegate = self</strong></p>
</blockquote>
<p>참고 : <a href="https://developer.apple.com/documentation/messageui/mfmailcomposeviewcontrollerdelegate">https://developer.apple.com/documentation/messageui/mfmailcomposeviewcontrollerdelegate</a>
<a href="https://developer.apple.com/forums/thread/90374">https://developer.apple.com/forums/thread/90374</a></p>
]]></description>
        </item>
    </channel>
</rss>