<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>simjae____.log</title>
        <link>https://velog.io/</link>
        <description>언제나 개발중</description>
        <lastBuildDate>Mon, 18 Nov 2024 02:36:07 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>simjae____.log</title>
            <url>https://velog.velcdn.com/images/simjae____/profile/ca8553c5-3a4c-4155-b41c-7e37e22883b4/image.webp</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. simjae____.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/simjae____" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[언제 prebuild --clean을 사용해야 하나요?
]]></title>
            <link>https://velog.io/@simjae____/%EC%96%B8%EC%A0%9C-prebuild-clean%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%95%BC-%ED%95%98%EB%82%98%EC%9A%94</link>
            <guid>https://velog.io/@simjae____/%EC%96%B8%EC%A0%9C-prebuild-clean%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%95%BC-%ED%95%98%EB%82%98%EC%9A%94</guid>
            <pubDate>Mon, 18 Nov 2024 02:36:07 GMT</pubDate>
            <description><![CDATA[<p>네이티브 설정에 변경이 있을 때:</p>
<p>iOS와 Android 관련 네이티브 설정(android/build.gradle, MainActivity.java, AppDelegate.m 등)을 수정했다면, 네이티브 파일을 최신 상태로 재생성하는 것이 필요합니다.
네이티브 모듈이 추가되거나 업데이트된 경우, 그에 맞는 설정이 반영되지 않아 빌드 오류가 발생할 수 있습니다. 이럴 때 --clean 옵션을 사용하면 문제를 해결할 수 있습니다.
빌드 에러가 발생할 때:</p>
<p>네이티브 파일이 손상되거나 설정이 꼬여서 앱이 정상적으로 빌드되지 않을 때, 기존 설정을 완전히 초기화하고 새로 생성하는 것이 도움이 될 수 있습니다.
특히 의존성이나 빌드 설정의 불일치로 인해 문제가 발생하는 경우가 많습니다.
새로운 네이티브 모듈을 추가한 경우:</p>
<p>새로운 네이티브 모듈을 추가한 경우 네이티브 설정이 제대로 반영되지 않을 수 있습니다. 이럴 때 prebuild --clean을 사용하면 해당 모듈에 필요한 설정 파일을 모두 새로 만들기 때문에 오류를 방지할 수 있습니다.
언제 prebuild --clean을 사용하지 않아도 되나요?
작은 변경:</p>
<p>예를 들어, JavaScript/TypeScript 코드만 수정했거나, 단순한 UI 변경이나 설정 파일을 약간만 변경했을 때는 prebuild --clean 없이도 빌드를 진행할 수 있습니다.
이미 존재하는 iOS/Android 파일들이 변경된 사항과 관련이 없을 때, 다시 빌드할 필요가 없습니다.
잦은 클린 빌드의 부담:</p>
<p>--clean 옵션은 iOS 및 Android 폴더를 완전히 삭제하고 다시 생성하기 때문에 빌드 시간이 많이 소요될 수 있습니다.
자주 사용하면 프로젝트에서 빌드와 개발 시간이 길어질 수 있으며, 특정 설정이 매번 삭제되기 때문에 유지 관리가 어렵습니다.
대신 사용할 수 있는 방법
expo prebuild: 단순히 expo prebuild만 사용하면 기존 네이티브 파일을 그대로 유지하면서 필요한 부분만 업데이트합니다. 만약 설정을 완전히 초기화할 필요가 없다면, 이 방법으로 충분합니다.</p>
<p>빌드 캐시 클리어: 만약 빌드 관련 문제를 해결하고 싶다면 빌드 캐시를 삭제하는 방법도 있습니다.</p>
<p>Metro 서버를 실행할 때 캐시 삭제:
bash
코드 복사
expo start -c
iOS/Android 빌드 캐시 삭제:
bash
코드 복사
cd android &amp;&amp; ./gradlew clean
cd ios &amp;&amp; xcodebuild clean
어떤 경우에 적용할까요?
설정이 제대로 반영되지 않을 때: 만약 서비스 워커 코드나 네이티브 모듈이 추가된 후에도 빌드에 문제가 생긴다면, prebuild --clean을 한 번 실행해보는 것이 좋습니다.
기존 설정 유지 필요: 자주 --clean을 사용하기보다 필요한 상황에서만 사용하는 것이 좋습니다. 일반적으로 설정이 크게 변경되지 않는다면 expo prebuild만으로도 충분합니다.
요약하면, 무조건 prebuild --clean을 해야 하는 것은 아니지만, 네이티브 설정 변경이나 빌드 문제가 발생할 때 유용하게 사용할 수 있습니다. 필요에 따라 상황에 맞는 명령어를 선택해서 사용해 주세요. 문제가 계속된다면 언제든지 추가로 문의해 주세요. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[layout_constraintDimensionRatio 사례]]></title>
            <link>https://velog.io/@simjae____/layoutconstraintDimensionRatio-%EC%82%AC%EB%A1%80</link>
            <guid>https://velog.io/@simjae____/layoutconstraintDimensionRatio-%EC%82%AC%EB%A1%80</guid>
            <pubDate>Wed, 23 Oct 2024 07:23:37 GMT</pubDate>
            <description><![CDATA[<p>layout_constraintDimensionRatio:
설명: 이 속성은 뷰의 너비와 높이 간의 비율을 설정합니다. 예를 들어, 1:1 비율을 설정하면 뷰가 정사각형 형태로 표시되며, 화면 크기에 따라 유동적으로 크기가 조정됩니다. 또한, 가로/세로 비율뿐만 아니라, 너비 또는 높이를 기준으로 비율을 설정할 수 있습니다.
구문: app:layout_constraintDimensionRatio=&quot;너비:높이&quot; 또는 app:layout_constraintDimensionRatio=&quot;H, 너비:높이&quot;로 사용할 수 있습니다.
H 또는 W로 시작하여 너비(Width) 또는 높이(Height)를 우선적으로 지정할 수 있습니다.
사용 예시:</p>
<ol>
<li>정사각형 비율 (1:1) 설정:
이 설정은 뷰의 너비와 높이가 동일한 정사각형이 되도록 만듭니다. 예를 들어, 너비를 50%로 설정하고 layout_constraintDimensionRatio=&quot;1:1&quot;을 설정하면, 높이도 자동으로 너비에 맞춰집니다.<pre><code></code></pre></li>
</ol>
<p>&lt;androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;wrap_content&quot;&gt;</p>
<pre><code>&lt;!-- 첫 번째 뷰, 정사각형 비율 --&gt;
&lt;View
    android:layout_width=&quot;0dp&quot;
    android:layout_height=&quot;0dp&quot;
    android:background=&quot;@android:color/holo_blue_light&quot;
    app:layout_constraintTop_toTopOf=&quot;parent&quot;
    app:layout_constraintStart_toStartOf=&quot;parent&quot;
    app:layout_constraintWidth_percent=&quot;0.5&quot;
    app:layout_constraintDimensionRatio=&quot;1:1&quot;/&gt;  &lt;!-- 1:1 비율 (정사각형) --&gt;</code></pre><p>&lt;/androidx.constraintlayout.widget.ConstraintLayout&gt;</p>
<pre><code>2. 가로를 기준으로 비율 설정:
W를 지정하면 **가로(너비)**를 기준으로 **세로(높이)**를 비율에 맞춰 조정합니다.
예시에서는 가로를 기준으로 3:2 비율을 설정했습니다.</code></pre><p>&lt;androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;wrap_content&quot;&gt;</p>
<pre><code>&lt;!-- 가로를 기준으로 3:2 비율 설정 --&gt;
&lt;ImageView
    android:layout_width=&quot;0dp&quot;
    android:layout_height=&quot;0dp&quot;
    android:src=&quot;@drawable/sample_image&quot;
    app:layout_constraintStart_toStartOf=&quot;parent&quot;
    app:layout_constraintEnd_toEndOf=&quot;parent&quot;
    app:layout_constraintTop_toTopOf=&quot;parent&quot;
    app:layout_constraintWidth_percent=&quot;0.7&quot;
    app:layout_constraintDimensionRatio=&quot;W, 3:2&quot;/&gt;  &lt;!-- 너비 기준 3:2 비율 --&gt;</code></pre><p>&lt;/androidx.constraintlayout.widget.ConstraintLayout&gt;</p>
<pre><code>3. 세로를 기준으로 비율 설정:
H를 지정하면 **세로(높이)**를 기준으로 **가로(너비)**를 비율에 맞춰 조정합니다.
예시에서는 세로를 기준으로 4:3 비율을 설정했습니다.
</code></pre><p>&lt;androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;wrap_content&quot;&gt;</p>
<pre><code>&lt;!-- 세로를 기준으로 4:3 비율 설정 --&gt;
&lt;ImageView
    android:layout_width=&quot;0dp&quot;
    android:layout_height=&quot;200dp&quot;
    android:src=&quot;@drawable/sample_image&quot;
    app:layout_constraintStart_toStartOf=&quot;parent&quot;
    app:layout_constraintEnd_toEndOf=&quot;parent&quot;
    app:layout_constraintTop_toTopOf=&quot;parent&quot;
    app:layout_constraintDimensionRatio=&quot;H, 4:3&quot;/&gt;  &lt;!-- 높이 기준 4:3 비율 --&gt;</code></pre><p>&lt;/androidx.constraintlayout.widget.ConstraintLayout&gt;</p>
<p>```
비율 설정 요약:
정사각형: app:layout_constraintDimensionRatio=&quot;1:1&quot;
가로를 기준으로 비율 설정: app:layout_constraintDimensionRatio=&quot;W, 너비:높이&quot;
세로를 기준으로 비율 설정: app:layout_constraintDimensionRatio=&quot;H, 너비:높이&quot;</p>
<p>활용 예시:
이 속성은 이미지나 영상과 같은 콘텐츠의 가로세로 비율을 유지해야 할 때 유용합니다. 예를 들어, 화면의 너비에 맞춰 이미지를 표시하면서, 원래의 비율을 유지해야 할 때 사용하면 매우 유용합니다.</p>
<p>중요 사항:
비율 설정을 할 때 너비 또는 높이 중 하나는 반드시 0dp로 설정해야 합니다. 그래야 비율에 따라 크기가 결정됩니다.
비율은 유동적으로 조정되며, 다양한 화면 크기에 맞게 레이아웃을 최적화할 수 있습니다.
layout_constraintDimensionRatio는 ConstraintLayout의 레이아웃을 더욱 유연하게 만들어 주는 강력한 도구 중 하나입니다. 이를 통해 고정 크기 없이도 비율에 따라 크기를 조정할 수 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[constraint 속성]]></title>
            <link>https://velog.io/@simjae____/constraint-%EC%86%8D%EC%84%B1</link>
            <guid>https://velog.io/@simjae____/constraint-%EC%86%8D%EC%84%B1</guid>
            <pubDate>Wed, 23 Oct 2024 07:22:01 GMT</pubDate>
            <description><![CDATA[<ol>
<li><p>layout_constraintHorizontal_bias와 layout_constraintVertical_bias:
설명: 뷰를 부모나 다른 뷰 사이에서 비율로 위치시킬 수 있습니다. 기본적으로 값은 0.5로 중앙에 위치하지만, 이를 변경해 원하는 위치로 조정할 수 있습니다.</p>
<pre><code>&lt;androidx.constraintlayout.widget.ConstraintLayout
 android:layout_width=&quot;match_parent&quot;
 android:layout_height=&quot;match_parent&quot;&gt;

 &lt;View
     android:layout_width=&quot;0dp&quot;
     android:layout_height=&quot;wrap_content&quot;
     app:layout_constraintStart_toStartOf=&quot;parent&quot;
     app:layout_constraintEnd_toEndOf=&quot;parent&quot;
     app:layout_constraintHorizontal_bias=&quot;0.3&quot;  &lt;!-- 수평으로 30% 위치 --&gt;
     app:layout_constraintTop_toTopOf=&quot;parent&quot;
     app:layout_constraintBottom_toBottomOf=&quot;parent&quot;
     app:layout_constraintVertical_bias=&quot;0.8&quot;  &lt;!-- 수직으로 80% 위치 --&gt;
     android:background=&quot;@android:color/holo_blue_light&quot;/&gt;
&lt;/androidx.constraintlayout.widget.ConstraintLayout&gt;</code></pre></li>
<li><p>layout_constraintStart_toEndOf / layout_constraintEnd_toStartOf:
설명: 뷰를 다른 뷰의 시작/끝과 연결하여 배치할 수 있습니다. 이 속성은 좌우 관계를 기반으로 한 상대적 위치를 쉽게 지정할 수 있습니다.</p>
<pre><code>&lt;androidx.appcompat.widget.AppCompatImageButton
 android:id=&quot;@+id/first_button&quot;
 android:layout_width=&quot;wrap_content&quot;
 android:layout_height=&quot;wrap_content&quot;
 android:src=&quot;@drawable/ic_launcher_background&quot;
 app:layout_constraintEnd_toStartOf=&quot;@+id/second_button&quot; /&gt;
</code></pre></li>
</ol>
<p>&lt;androidx.appcompat.widget.AppCompatImageButton
    android:id=&quot;@+id/second_button&quot;
    android:layout_width=&quot;wrap_content&quot;
    android:layout_height=&quot;wrap_content&quot;
    android:src=&quot;@drawable/ic_launcher_foreground&quot;
    app:layout_constraintStart_toEndOf=&quot;@+id/first_button&quot; /&gt;</p>
<pre><code>
3. layout_goneMarginStart, layout_goneMarginEnd:
설명: 뷰가 GONE 상태일 때 적용되는 특정 마진을 지정할 수 있습니다. 기본 마진과는 별도로, 뷰가 보이지 않을 때 다른 뷰와의 간격을 조절하는 데 유용합니다.</code></pre><p>&lt;View
    android:id=&quot;@+id/viewA&quot;
    android:layout_width=&quot;0dp&quot;
    android:layout_height=&quot;wrap_content&quot;
    app:layout_constraintStart_toStartOf=&quot;parent&quot;
    app:layout_constraintEnd_toStartOf=&quot;@+id/viewB&quot;
    app:layout_marginEnd=&quot;8dp&quot;
    app:layout_goneMarginEnd=&quot;16dp&quot;  <!-- GONE 상태일 때 마진을 다르게 설정 -->
    android:background=&quot;@android:color/holo_blue_light&quot; /&gt;</p>
<p><View
    android:id="@+id/viewB"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    app:layout_constraintStart_toEndOf="@+id/viewA"
    app:layout_constraintEnd_toEndOf="parent"
    android:background="@android:color/holo_red_light" /></p>
<pre><code>
4. layout_constraintHeight_percent / layout_constraintWidth_percent:
설명: 화면이나 부모 뷰의 일정 비율로 뷰의 크기를 설정할 수 있습니다. 화면 크기에 따라 자동으로 크기가 조정됩니다.
</code></pre><p>&lt;View
    android:layout_width=&quot;0dp&quot;
    android:layout_height=&quot;0dp&quot;
    app:layout_constraintWidth_percent=&quot;0.5&quot;  <!-- 부모의 50% 너비 -->
    app:layout_constraintHeight_percent=&quot;0.3&quot;  <!-- 부모의 30% 높이 -->
    android:background=&quot;@android:color/holo_green_light&quot;/&gt;</p>
<pre><code>
5. layout_constraintGuide_begin, layout_constraintGuide_end, layout_constraintGuide_percent:
설명: 가이드라인을 통해 뷰를 배치하는 기준을 세울 수 있습니다. 고정된 크기 또는 비율에 따라 가이드라인을 설정하여 레이아웃을 더 유연하게 조절할 수 있습니다.</code></pre><p>&lt;androidx.constraintlayout.widget.Guideline
    android:layout_width=&quot;wrap_content&quot;
    android:layout_height=&quot;wrap_content&quot;
    app:layout_constraintGuide_begin=&quot;100dp&quot;  <!-- 100dp에서 가이드라인 시작 -->
    android:orientation=&quot;vertical&quot;/&gt;</p>
<p>&lt;androidx.constraintlayout.widget.Guideline
    android:layout_width=&quot;wrap_content&quot;
    android:layout_height=&quot;wrap_content&quot;
    app:layout_constraintGuide_percent=&quot;0.5&quot;  <!-- 부모의 50% 지점에 가이드라인 설정 -->
    android:orientation=&quot;horizontal&quot;/&gt;</p>
<pre><code>6. layout_constraintCircle, layout_constraintCircleRadius, layout_constraintCircleAngle:
설명: 뷰를 원형으로 배치하는 속성으로, 다른 뷰를 중심으로 특정 각도와 반경을 사용하여 원형 배치할 수 있습니다.</code></pre><p><View
    android:id="@+id/center_view"
    android:layout_width="50dp"
    android:layout_height="50dp"
    android:background="@android:color/holo_blue_bright"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintEnd_toEndOf="parent"/></p>
<p>&lt;View
    android:layout_width=&quot;30dp&quot;
    android:layout_height=&quot;30dp&quot;
    android:background=&quot;@android:color/holo_green_light&quot;
    app:layout_constraintCircle=&quot;@id/center_view&quot;  <!-- 중심 뷰 -->
    app:layout_constraintCircleRadius=&quot;100dp&quot;  <!-- 중심으로부터 100dp 떨어짐 -->
    app:layout_constraintCircleAngle=&quot;45&quot;  <!-- 45도 위치 -->
    /&gt;</p>
<pre><code>
7. layout_constraintBaseline_toBaselineOf:
설명: 텍스트나 버튼과 같은 뷰의 기준선(baseline)을 다른 뷰의 기준선에 맞추는 속성입니다. 텍스트 기반 뷰들을 정렬하는 데 유용합니다.
</code></pre><p><TextView
    android:id="@+id/text1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="TextView 1"/></p>
<p><TextView
    android:id="@+id/text2"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="TextView 2"
    app:layout_constraintBaseline_toBaselineOf="@id/text1"/>  <!-- text1 기준선과 정렬 --></p>
<p>```
<strong>layout_constraintDimensionRatio</strong>와 같은 속성은 다양한 제약 조건을 쉽게 처리할 수 있도록 도와줍니다.
이 외에도 비율, 바이어스, 체인, 원형 배치, 가이드라인 등 여러 유용한 속성들이 있으니, 복잡한 레이아웃을 구현할 때 적극적으로 활용하면 좋습니다.
ConstraintLayout을 사용하면 복잡한 UI 레이아웃도 쉽게 구성할 수 있고, 위에서 소개한 다양한 속성을 활용하면 더 유연하고 강력한 디자인을 만들 수 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[상황별 레이아웃 선택]]></title>
            <link>https://velog.io/@simjae____/%EC%83%81%ED%99%A9%EB%B3%84-%EB%A0%88%EC%9D%B4%EC%95%84%EC%9B%83-%EC%84%A0%ED%83%9D</link>
            <guid>https://velog.io/@simjae____/%EC%83%81%ED%99%A9%EB%B3%84-%EB%A0%88%EC%9D%B4%EC%95%84%EC%9B%83-%EC%84%A0%ED%83%9D</guid>
            <pubDate>Fri, 20 Sep 2024 01:25:41 GMT</pubDate>
            <description><![CDATA[<ol>
<li><p>간단한 수직/수평 배열이 필요한 경우
추천 레이아웃: LinearLayout</p>
<p>이유: LinearLayout은 수평 또는 수직으로 단순히 정렬할 때 적합합니다. 이 경우에는 복잡한 레이아웃이 필요하지 않으므로 가장 가벼운 레이아웃을 선택하는 것이 좋습니다.
사용 예시:
두 개의 버튼을 수평으로 나란히 배치하거나, 여러 텍스트 뷰를 세로로 나열할 때.</p>
<pre><code class="language-xml">&lt;LinearLayout
   android:orientation=&quot;vertical&quot;
   android:layout_width=&quot;match_parent&quot;
   android:layout_height=&quot;wrap_content&quot;&gt;
   &lt;TextView
       android:layout_width=&quot;wrap_content&quot;
       android:layout_height=&quot;wrap_content&quot;
       android:text=&quot;첫 번째 텍스트&quot; /&gt;
   &lt;TextView
       android:layout_width=&quot;wrap_content&quot;
       android:layout_height=&quot;wrap_content&quot;
       android:text=&quot;두 번째 텍스트&quot; /&gt;
&lt;/LinearLayout&gt;</code></pre>
</li>
<li><p>뷰를 겹쳐서 배치할 때
추천 레이아웃: FrameLayout</p>
<p>이유: FrameLayout은 뷰를 겹치게 배치할 때 가장 적합합니다. 이는 뷰가 서로 위에 쌓여야 하는 상황에서 효율적입니다.
사용 예시:
배경 이미지 위에 텍스트나 버튼을 겹쳐서 표시할 때.
로딩 화면에서 로딩 애니메이션을 가운데 배치하고, 배경에 다른 뷰가 있을 때.</p>
<pre><code class="language-xml">&lt;FrameLayout
   android:layout_width=&quot;match_parent&quot;
   android:layout_height=&quot;match_parent&quot;&gt;
   &lt;ImageView
       android:layout_width=&quot;match_parent&quot;
       android:layout_height=&quot;match_parent&quot;
       android:src=&quot;@drawable/background_image&quot; /&gt;
   &lt;TextView
       android:layout_width=&quot;wrap_content&quot;
       android:layout_height=&quot;wrap_content&quot;
       android:text=&quot;Hello, World!&quot;
       android:layout_gravity=&quot;center&quot; /&gt;
&lt;/FrameLayout&gt;</code></pre>
</li>
<li><p>상대적인 배치가 필요할 때
추천 레이아웃: RelativeLayout</p>
</li>
</ol>
<p>이유: 뷰들이 서로를 기준으로 배치되어야 할 때 사용됩니다. ConstraintLayout으로 대부분 대체되었지만, 간단한 경우에는 RelativeLayout이 적합합니다.
사용 예시:
한 뷰를 다른 뷰 옆에 놓거나, 부모의 특정 부분에 고정할 때.</p>
<pre><code class="language-xml">&lt;RelativeLayout
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;wrap_content&quot;&gt;
    &lt;TextView
        android:id=&quot;@+id/label&quot;
        android:layout_width=&quot;wrap_content&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:text=&quot;Label&quot; /&gt;
    &lt;Button
        android:layout_width=&quot;wrap_content&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:layout_toRightOf=&quot;@id/label&quot;
        android:text=&quot;Button&quot; /&gt;
&lt;/RelativeLayout&gt;</code></pre>
<ol start="4">
<li><p>복잡한 레이아웃을 구성해야 할 때
추천 레이아웃: ConstraintLayout</p>
<p>이유: ConstraintLayout은 복잡한 배치를 효율적으로 처리할 수 있어 레이아웃의 중첩을 최소화하고 성능을 최적화합니다. 여러 뷰를 상호 관계로 자유롭게 배치할 수 있습니다.
사용 예시:
여러 버튼, 텍스트, 이미지 등을 복잡한 방식으로 배치해야 할 때.
디자인이 복잡하고 요소들이 서로 의존하는 경우.
xml</p>
<pre><code>&lt;androidx.constraintlayout.widget.ConstraintLayout
   android:layout_width=&quot;match_parent&quot;
   android:layout_height=&quot;wrap_content&quot;&gt;
   &lt;ImageView
       android:id=&quot;@+id/image&quot;
       android:layout_width=&quot;wrap_content&quot;
       android:layout_height=&quot;wrap_content&quot;
       android:src=&quot;@drawable/sample_image&quot;
       app:layout_constraintStart_toStartOf=&quot;parent&quot;
       app:layout_constraintTop_toTopOf=&quot;parent&quot; /&gt;
   &lt;TextView
       android:layout_width=&quot;wrap_content&quot;
       android:layout_height=&quot;wrap_content&quot;
       android:text=&quot;Sample Text&quot;
       app:layout_constraintStart_toEndOf=&quot;@id/image&quot;
       app:layout_constraintTop_toTopOf=&quot;@id/image&quot; /&gt;
&lt;/androidx.constraintlayout.widget.ConstraintLayout&gt;</code></pre></li>
<li><p>레이아웃이 재사용 가능해야 할 때
추천 레이아웃: include, merge</p>
<p>이유: 여러 곳에서 동일한 레이아웃 구조를 재사용해야 할 때는 include 태그를 사용하여 레이아웃을 재사용할 수 있습니다. 성능을 위해 merge를 사용하면 불필요한 계층을 제거할 수 있습니다.
사용 예시:
동일한 헤더나 푸터 레이아웃이 여러 화면에서 반복적으로 사용될 때.</p>
<pre><code class="language-xml">&lt;!-- header.xml --&gt;
&lt;LinearLayout
   android:orientation=&quot;horizontal&quot;
   android:layout_width=&quot;match_parent&quot;
   android:layout_height=&quot;wrap_content&quot;&gt;
   &lt;TextView
       android:layout_width=&quot;wrap_content&quot;
       android:layout_height=&quot;wrap_content&quot;
       android:text=&quot;Header&quot; /&gt;
&lt;/LinearLayout&gt;

&lt;!-- main_layout.xml --&gt;
&lt;LinearLayout
   android:layout_width=&quot;match_parent&quot;
   android:layout_height=&quot;match_parent&quot;
   android:orientation=&quot;vertical&quot;&gt;
   &lt;include layout=&quot;@layout/header&quot; /&gt;
   &lt;!-- 나머지 컨텐츠 --&gt;
&lt;/LinearLayout&gt;</code></pre>
</li>
<li><p>스와이프, 드래그 등 제스처가 필요한 경우
추천 레이아웃: SwipeRefreshLayout, CoordinatorLayout</p>
<p>이유: 사용자 인터랙션을 통해 특정 레이아웃이 스크롤되거나 드래그되어야 할 때 사용됩니다.
사용 예시:
리스트를 스와이프하여 새로고침할 때(SwipeRefreshLayout).
스크롤 시 앱바가 애니메이션과 함께 나타나거나 사라지게 할 때(CoordinatorLayout).</p>
<pre><code class="language-xml">&lt;androidx.swiperefreshlayout.widget.SwipeRefreshLayout
   android:layout_width=&quot;match_parent&quot;
   android:layout_height=&quot;match_parent&quot;&gt;
   &lt;RecyclerView
       android:layout_width=&quot;match_parent&quot;
       android:layout_height=&quot;match_parent&quot; /&gt;
&lt;/androidx.swiperefreshlayout.widget.SwipeRefreshLayout&gt;</code></pre>
</li>
<li><p>아이템의 배치가 복잡할 때 (여러 개의 뷰를 배치해야 할 때)
추천 레이아웃: ConstraintLayout</p>
<p>이유: ConstraintLayout은 복잡한 배치를 효과적으로 해결할 수 있습니다. 여러 뷰를 상호 연관된 위치에 배치할 수 있으며, 중첩된 레이아웃을 피할 수 있어 성능에도 유리합니다.
사용 예시:
여러 개의 뷰를 서로 상하좌우로 배치할 때.
아이콘, 텍스트, 버튼 등을 세밀하게 정렬해야 하는 상황.</p>
<pre><code class="language-xml">&lt;androidx.constraintlayout.widget.ConstraintLayout
   android:layout_width=&quot;match_parent&quot;
   android:layout_height=&quot;wrap_content&quot;&gt;

   &lt;ImageView
       android:id=&quot;@+id/icon&quot;
       android:layout_width=&quot;wrap_content&quot;
       android:layout_height=&quot;wrap_content&quot;
       android:src=&quot;@drawable/icon&quot;
       app:layout_constraintStart_toStartOf=&quot;parent&quot;
       app:layout_constraintTop_toTopOf=&quot;parent&quot;/&gt;

   &lt;TextView
       android:id=&quot;@+id/title&quot;
       android:layout_width=&quot;wrap_content&quot;
       android:layout_height=&quot;wrap_content&quot;
       android:text=&quot;Title&quot;
       app:layout_constraintStart_toEndOf=&quot;@id/icon&quot;
       app:layout_constraintTop_toTopOf=&quot;@id/icon&quot;/&gt;

&lt;/androidx.constraintlayout.widget.ConstraintLayout&gt;</code></pre>
</li>
<li><p>간단한 수직 또는 수평 정렬이 필요한 경우
추천 레이아웃: LinearLayout</p>
<p>이유: LinearLayout은 단순하게 뷰를 세로 또는 가로로 배치할 때 적합합니다. 뷰가 많지 않거나 간단한 구조라면 가장 효율적인 레이아웃입니다.
사용 예시:
여러 텍스트 뷰를 세로로 나열하거나, 이미지를 가로로 나란히 배치할 때.
중첩 레이아웃을 피하고 깔끔한 정렬을 원할 때.</p>
<pre><code class="language-xml">&lt;LinearLayout
   android:orientation=&quot;vertical&quot;
   android:layout_width=&quot;match_parent&quot;
   android:layout_height=&quot;wrap_content&quot;&gt;

   &lt;TextView
       android:layout_width=&quot;wrap_content&quot;
       android:layout_height=&quot;wrap_content&quot;
       android:text=&quot;Title&quot; /&gt;

   &lt;TextView
       android:layout_width=&quot;wrap_content&quot;
       android:layout_height=&quot;wrap_content&quot;
       android:text=&quot;Subtitle&quot; /&gt;
&lt;/LinearLayout&gt;</code></pre>
</li>
<li><p>뷰의 겹침이 필요한 경우
추천 레이아웃: FrameLayout</p>
<p>이유: FrameLayout은 뷰를 단순히 겹쳐서 표현해야 할 때 사용합니다. 여러 개의 뷰를 쌓아서 보여주고, 특정 뷰만 화면에 드러내거나 배경 위에 다른 뷰를 덧붙여서 보여주기 적합합니다.
사용 예시:
이미지 위에 텍스트를 겹치거나, 배경 위에 로딩 애니메이션을 추가할 때.
복잡하지 않은 레이아웃에서 여러 레이어를 쌓아야 할 때.</p>
<pre><code class="language-xml">&lt;FrameLayout
   android:layout_width=&quot;match_parent&quot;
   android:layout_height=&quot;wrap_content&quot;&gt;

   &lt;ImageView
       android:layout_width=&quot;match_parent&quot;
       android:layout_height=&quot;wrap_content&quot;
       android:src=&quot;@drawable/background_image&quot;/&gt;

   &lt;TextView
       android:layout_width=&quot;wrap_content&quot;
       android:layout_height=&quot;wrap_content&quot;
       android:text=&quot;Hello, World!&quot;
       android:layout_gravity=&quot;center&quot;/&gt;
&lt;/FrameLayout&gt;</code></pre>
</li>
<li><p>복잡하지 않은 상대적 배치가 필요한 경우
추천 레이아웃: RelativeLayout</p>
<p>이유: RelativeLayout은 뷰 간의 상대적 위치 관계를 기반으로 배치하는 데 적합합니다. ConstraintLayout처럼 복잡하지 않으면서도 뷰를 서로 상하좌우로 정렬할 수 있습니다.
사용 예시:
특정 뷰를 다른 뷰의 왼쪽, 오른쪽, 아래에 배치할 때.
더 세밀한 정렬이 필요하지 않고 기본적인 위치 설정만 필요한 경우.</p>
<pre><code class="language-xml">&lt;RelativeLayout
  android:layout_width=&quot;match_parent&quot;
  android:layout_height=&quot;wrap_content&quot;&gt;

  &lt;ImageView
      android:id=&quot;@+id/icon&quot;
      android:layout_width=&quot;wrap_content&quot;
      android:layout_height=&quot;wrap_content&quot;
      android:src=&quot;@drawable/icon&quot;
      android:layout_alignParentLeft=&quot;true&quot; /&gt;

  &lt;TextView
      android:layout_width=&quot;wrap_content&quot;
      android:layout_height=&quot;wrap_content&quot;
      android:text=&quot;Title&quot;
      android:layout_toRightOf=&quot;@id/icon&quot;/&gt;
&lt;/RelativeLayout&gt;</code></pre>
</li>
<li><p>다양한 Material Design 효과가 필요한 경우
추천 레이아웃: MaterialCardView</p>
<p>이유: MaterialCardView는 기본 CardView에 Material Design의 다양한 효과를 추가한 버전입니다. 클릭 효과, 터치 반응, 그림자 등 Material Design의 가이드를 따르는 레이아웃을 쉽게 구현할 수 있습니다.
사용 예시:
앱에서 Material Design을 적극적으로 사용할 때.
카드 형태의 요소에 고급 터치 효과나 애니메이션이 필요할 때.</p>
<pre><code class="language-xml">&lt;com.google.android.material.card.MaterialCardView
  android:layout_width=&quot;match_parent&quot;
  android:layout_height=&quot;wrap_content&quot;
  app:cardElevation=&quot;4dp&quot;
  app:cardCornerRadius=&quot;8dp&quot;&gt;

  &lt;TextView
      android:layout_width=&quot;wrap_content&quot;
      android:layout_height=&quot;wrap_content&quot;
      android:text=&quot;Material Card&quot;/&gt;
&lt;/com.google.android.material.card.MaterialCardView&gt;</code></pre>
</li>
<li><p>성능 최적화가 필요할 때
추천 레이아웃: LinearLayout (중첩을 최소화한 레이아웃)</p>
<p>이유: 복잡한 UI는 성능 저하를 일으킬 수 있습니다. 이럴 때는 LinearLayout이나 FrameLayout과 같이 중첩을 최소화하고 단순한 레이아웃을 사용하는 것이 좋습니다. 또한 ConstraintLayout을 사용하여 복잡한 구조를 한 번에 해결하는 것도 방법입니다.
사용 예시:
수백 개의 리스트 아이템을 효율적으로 처리해야 할 때.
단순한 레이아웃을 성능 최적화 목적에서 구성해야 할 때.</p>
<pre><code class="language-xml">&lt;LinearLayout
  android:layout_width=&quot;match_parent&quot;
  android:layout_height=&quot;wrap_content&quot;
  android:orientation=&quot;horizontal&quot;&gt;

  &lt;ImageView
      android:layout_width=&quot;wrap_content&quot;
      android:layout_height=&quot;wrap_content&quot;
      android:src=&quot;@drawable/icon&quot;/&gt;

  &lt;TextView
      android:layout_width=&quot;wrap_content&quot;
      android:layout_height=&quot;wrap_content&quot;
      android:text=&quot;Simple Item&quot;/&gt;
&lt;/LinearLayout&gt;</code></pre>
</li>
<li><p>동적으로 뷰를 추가해야 할 때
추천 레이아웃: ViewGroup + Custom Layout</p>
<p>이유: 복잡한 인터페이스를 구성하거나 동적으로 뷰를 추가해야 하는 경우, ViewGroup을 사용해 커스텀 레이아웃을 작성할 수 있습니다. 또한 기존 레이아웃을 확장하여 원하는 동작을 구현할 수 있습니다.
사용 예시:
런타임에 동적으로 여러 뷰를 추가해야 하는 경우.
특별한 배치 요구사항이 있는 경우 커스텀 레이아웃 작성.</p>
<pre><code class="language-java">코드 복사
public class CustomLayout extends ViewGroup {
  // Custom layout logic
}</code></pre>
<p>요약:
상황에 따라 어떤 레이아웃이 적합한지 선택하는 것이 중요합니다. 간단한 수직, 수평 정렬은 LinearLayout, 복잡한 배치는 ConstraintLayout, 겹침이 필요한 경우는 FrameLayout, 상대적 배치는 RelativeLayout, 고급 Material Design 요소가 필요한 경우는 MaterialCardView가 적합합니다. 성능 최적화를 염두에 둔 경우에는 중첩을 최소화한 레이아웃을 선택하는 것이 좋습니다.</p>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[Fragment와 RecyclerView.Adapter의 차이]]></title>
            <link>https://velog.io/@simjae____/Fragment%EC%99%80-RecyclerView.Adapter%EC%9D%98-%EC%B0%A8%EC%9D%B4</link>
            <guid>https://velog.io/@simjae____/Fragment%EC%99%80-RecyclerView.Adapter%EC%9D%98-%EC%B0%A8%EC%9D%B4</guid>
            <pubDate>Thu, 19 Sep 2024 05:00:56 GMT</pubDate>
            <description><![CDATA[<ol>
<li><p>Fragment와 RecyclerView.Adapter의 차이
Fragment: 안드로이드에서 화면의 특정 부분을 구성하는 컴포넌트입니다. 하나의 액티비티(Activity) 안에 여러 프래그먼트를 넣어서 다양한 화면을 보여줄 수 있습니다. Fragment는 UI 요소와 상호작용하거나, 사용자에게 화면을 제공하는데 쓰입니다.</p>
<p>RecyclerView.Adapter: 리스트 또는 그리드처럼 반복되는 데이터를 화면에 보여주는 역할을 합니다. 데이터를 화면에 그릴 때 각각의 아이템을 효율적으로 관리하고, 스크롤할 때 성능을 최적화합니다.
중요한 점: Fragment는 화면의 특정 부분을 나타내는 반면, Adapter는 데이터를 화면에 뿌려주는 도구입니다. Adapter를 사용한다고 해서 Fragment를 사용하지 않을 수는 없지만, 상황에 따라 RecyclerView.Adapter만으로도 리스트를 처리할 수 있습니다. 리스트 화면을 만들기 위해 꼭 Fragment가 필요한 것은 아닙니다.</p>
</li>
<li><p>Adapter란?
Adapter는 RecyclerView와 같은 UI 컴포넌트에서 데이터를 화면에 표시하기 위해 사용하는 중개자입니다. 데이터를 받아서 RecyclerView에 하나씩 그려주는 역할을 합니다.</p>
<p>데이터를 어떻게 표시할지 결정하는 뷰를 생성.
스크롤할 때 성능을 높이기 위해 재사용할 수 있는 뷰들을 관리.</p>
</li>
<li><p>ListItem 클래스란?
ListItem은 데이터를 저장하는 모델 클래스입니다. 주로 앱에서 사용할 데이터를 객체로 관리하기 위해 클래스 형태로 만듭니다. 예를 들어, 리스트의 각 항목이 &#39;문장형 (4지 1답)&#39;, &#39;사진형 (5지 2답)&#39; 등의 정보와 진행 상태를 가지고 있다면, 이 정보를 각각 ListItem 객체로 만들어서 관리합니다.</p>
<p>이 클래스를 만드는 이유는 데이터를 구조적으로 관리하기 위함입니다. 데이터가 많아질 때, 이를 각각 변수로 관리하면 복잡해지기 때문에 하나의 클래스에 정보를 담아서 쉽게 다루고, 이 데이터를 Adapter에 전달하여 화면에 표시하는 것입니다.</p>
</li>
<li><p>RecyclerView.Adapter와 ViewHolder 설명
RecyclerView.Adapter: RecyclerView의 아이템을 관리하는 클래스입니다. 데이터를 받아서 아이템 뷰에 그릴 수 있도록 onCreateViewHolder(), onBindViewHolder(), getItemCount() 같은 메서드를 구현합니다.</p>
<p>ViewHolder: 아이템의 뷰를 관리하는 클래스입니다. Adapter는 ViewHolder를 통해 아이템에 표시할 데이터를 뷰에 바인딩합니다.</p>
</li>
<li><p>RecyclerView.Adapter 기본 구조</p>
<pre><code class="language-java">public class CategoriesAdapter extends RecyclerView.Adapter&lt;CategoriesAdapter.ItemViewHolder&gt; {

   // ListItem 타입의 리스트를 어댑터가 가지고 있음
   private ArrayList&lt;ListItem&gt; listItems;

   // 생성자를 통해 데이터를 받아옴
   public CategoriesAdapter(ArrayList&lt;ListItem&gt; listItems) {
       this.listItems = listItems;
   }

   // ViewHolder를 생성 (item의 레이아웃을 inflate)
   @NonNull
   @Override
   public ItemViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
       View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item, parent, false);
       return new ItemViewHolder(view);
   }

   // 데이터를 ViewHolder에 바인딩 (아이템 뷰에 데이터를 설정)
   @Override
   public void onBindViewHolder(@NonNull ItemViewHolder holder, int position) {
       ListItem item = listItems.get(position);
       holder.title.setText(item.getTitle());
       holder.progressText.setText(item.getProgress() + &quot; / 100&quot;);
       holder.progressBar.setProgress(item.getProgress());
   }

   // 전체 아이템의 개수를 반환
   @Override
   public int getItemCount() {
       return listItems.size();
   }

   // ViewHolder: 아이템 뷰의 데이터를 관리하는 클래스
   public static class ItemViewHolder extends RecyclerView.ViewHolder {
       TextView title, progressText;
       ProgressBar progressBar;

       public ItemViewHolder(@NonNull View itemView) {
           super(itemView);
           title = itemView.findViewById(R.id.item_title);
           progressText = itemView.findViewById(R.id.item_progress_text);
           progressBar = itemView.findViewById(R.id.item_progress_bar);
       }
   }
}
</code></pre>
</li>
</ol>
<ol start="6">
<li><p>플래그먼트에 RecyclerView 적용 예시
이제 위의 어댑터를 Fragment에 적용해 보겠습니다.</p>
<p>CategoriesFragment.java</p>
<pre><code class="language-java">public class CategoriesFragment extends Fragment {

   private RecyclerView recyclerView;
   private CategoriesAdapter categoriesAdapter;
   private ArrayList&lt;ListItem&gt; listItems;

   @Nullable
   @Override
   public View onCreateView(@NonNull LayoutInflater inflater,
                            @Nullable ViewGroup container,
                            @Nullable Bundle savedInstanceState) {

       // 프래그먼트의 레이아웃을 inflate
       View view = inflater.inflate(R.layout.fragment_categories, container, false);

       // RecyclerView 설정
       recyclerView = view.findViewById(R.id.recyclerView);
       recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));

       // 데이터 준비 (ListItem 객체로 리스트 생성)
       listItems = new ArrayList&lt;&gt;();
       listItems.add(new ListItem(&quot;문장형 (4지 1답)&quot;, 85));
       listItems.add(new ListItem(&quot;사진형 (5지 2답)&quot;, 90));

       // 어댑터 설정
       categoriesAdapter = new CategoriesAdapter(listItems);
       recyclerView.setAdapter(categoriesAdapter);

       return view;
   }
}
</code></pre>
<p>fragment_categories.xml</p>
<pre><code class="language-xml">&lt;androidx.constraintlayout.widget.ConstraintLayout
   xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
   android:layout_width=&quot;match_parent&quot;
   android:layout_height=&quot;match_parent&quot;&gt;

   &lt;!-- RecyclerView --&gt;
   &lt;androidx.recyclerview.widget.RecyclerView
       android:id=&quot;@+id/recyclerView&quot;
       android:layout_width=&quot;match_parent&quot;
       android:layout_height=&quot;wrap_content&quot; /&gt;
&lt;/androidx.constraintlayout.widget.ConstraintLayout&gt;

</code></pre>
</li>
</ol>
<ol start="7">
<li>전체 흐름 요약
ListItem: 데이터(타이틀, 진행률)를 관리하는 모델 클래스입니다.
CategoriesAdapter: ListItem 데이터를 RecyclerView에 바인딩해 주는 클래스입니다. 아이템이 화면에 어떻게 보일지 정의합니다.
CategoriesFragment: RecyclerView를 사용해서 리스트를 화면에 표시하는 곳입니다.
RecyclerView는 각 아이템을 효율적으로 그리기 위해 어댑터와 데이터를 받아 아이템을 화면에 표시합니다.
결론:
자바는 객체 지향 언어라서 데이터를 잘 구조화하여 관리하는 것이 중요합니다. ListItem 클래스는 데이터를 담고, Adapter는 그 데이터를 기반으로 UI에 그리는 중개 역할을 하며, RecyclerView는 그 데이터 리스트를 효율적으로 관리합니다.</li>
</ol>
<p>앞으로 자바와 안드로이드에서 RecyclerView와 Adapter를 사용할 때는, 데이터를 모델 클래스(ListItem)로 구조화한 후 어댑터를 통해 그 데이터를 화면에 표시하는 작업 흐름을 이해하면 더 쉽게 다가갈 수 있을 것입니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[clipChildren 문제 해결]]></title>
            <link>https://velog.io/@simjae____/%EB%B6%80%EB%AA%A8-%EC%98%81%EC%97%AD%EC%97%90%EC%84%9C-%EC%9E%90%EC%8B%9D%EC%9A%94%EC%86%8C-%EB%82%B4%EB%B3%B4%EB%82%B4%EA%B8%B0</link>
            <guid>https://velog.io/@simjae____/%EB%B6%80%EB%AA%A8-%EC%98%81%EC%97%AD%EC%97%90%EC%84%9C-%EC%9E%90%EC%8B%9D%EC%9A%94%EC%86%8C-%EB%82%B4%EB%B3%B4%EB%82%B4%EA%B8%B0</guid>
            <pubDate>Wed, 11 Sep 2024 02:20:14 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>FrameLayout이 최상위 부모일 때는 clipChildren=&quot;false&quot;와 clipToPadding=&quot;false&quot;를 설정해도 자식 뷰가 짤리는 문제가 발생하지만, ConstraintLayout으로 FrameLayout을 감쌌을 때 문제가 해결된 이유는 레이아웃의 동작 방식과 관련이 있습니다. 다음은 그 이유를 설명하는 주요 포인트입니다.</p>
</blockquote>
<pre><code>&lt;FrameLayout
  xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
  xmlns:app=&quot;http://schemas.android.com/apk/res-auto&quot;
  android:layout_width=&quot;match_parent&quot;
  android:layout_height=&quot;100dp&quot;
  android:elevation=&quot;5dp&quot;
  android:clipChildren=&quot;false&quot;
  android:clipToPadding=&quot;false&quot;
  android:background=&quot;@color/red&quot;
  app:layout_constraintTop_toTopOf=&quot;parent&quot;
  app:layout_constraintBottom_toBottomOf=&quot;parent&quot;
  app:layout_constraintStart_toStartOf=&quot;parent&quot;
  app:layout_constraintEnd_toEndOf=&quot;parent&quot;&gt;

  &lt;TextView
      android:layout_width=&quot;100dp&quot;
      android:layout_height=&quot;100dp&quot;
      android:layout_gravity=&quot;center&quot;
      android:background=&quot;@color/black&quot;
      android:translationY=&quot;100dp&quot;
      android:text=&quot;child&quot;
      android:textColor=&quot;@android:color/white&quot;
      android:gravity=&quot;center&quot;/&gt;
&lt;/FrameLayout&gt;</code></pre><p>위와 같은 소스였을때 textView가 부모영역 즉 FrameLayout으로 짤림 없이 내보내고 싶었는데 
계속 짤림 현상이 있었다.<code>clipChildren</code> , <code>clipToPadding</code> 속성을 <code>false</code>를 주었음에도 해결이 안되었다. </p>
<h4 id="framelayout의-기본-동작">FrameLayout의 기본 동작:</h4>
<p>FrameLayout은 자식 뷰를 겹쳐서 배치하는 단순한 레이아웃이지만, 내부적으로 자식 뷰가 부모 경계를 넘어서 보이도록 하는 것에는 제한적입니다.</p>
<p>clipChildren=&quot;false&quot;와 clipToPadding=&quot;false&quot;를 설정해도 FrameLayout의 경우 제대로 작동하지 않을 때가 있습니다. 특히 자식 뷰가 큰 이동을 하는 경우 FrameLayout 자체의 경계 설정 때문에 자식 뷰가 잘릴 수 있습니다.</p>
<h5 id="constraintlayout의-유연한-배치-특성">ConstraintLayout의 유연한 배치 특성:</h5>
<p>ConstraintLayout은 자식 뷰의 위치를 제약 조건으로 자유롭게 지정할 수 있는 레이아웃으로, 자식 뷰가 부모 경계를 넘어서도록 하는 데 더 유연하게 작동합니다.
ConstraintLayout으로 FrameLayout을 감싸면, ConstraintLayout의 경계 처리 방식이 더 유연하기 때문에 FrameLayout 내부의 자식 뷰가 경계를 넘어가더라도 잘리지 않게 됩니다.
자식 뷰의 배치 및 부모 경계 문제 해결:</p>
<p>ConstraintLayout은 제약 조건을 통해 자식 뷰를 배치하기 때문에, 자식 뷰가 부모 경계를 넘더라도 자연스럽게 보이도록 하는 기능이 내장되어 있습니다. 반면, FrameLayout은 이러한 경계 관리에 제한이 있습니다.</p>
<h5 id="프리뷰-환경에서의-차이">프리뷰 환경에서의 차이:</h5>
<p>안드로이드 스튜디오의 프리뷰에서는 FrameLayout의 경계 처리 방식이 실제 앱과 다르게 보일 수 있습니다. 이 때문에 ConstraintLayout을 사용하여 감싸면 프리뷰에서도 의도한 대로 자식 뷰가 잘리지 않고 보일 수 있습니다.</p>
<h4 id="결론">결론:</h4>
<h5 id="constraintlayout-사용의-장점">ConstraintLayout 사용의 장점:</h5>
<p>ConstraintLayout은 더 유연하게 자식 뷰를 관리할 수 있으며, 제약 조건을 통해 자식 뷰가 부모 경계를 벗어날 수 있도록 효과적으로 처리합니다.</p>
<h5 id="framelayout의-한계">FrameLayout의 한계:</h5>
<p>단순한 배치에는 유용하지만, 자식 뷰가 부모 경계를 넘어서 보이는 상황에서 유연하지 않을 수 있습니다.</p>
<h5 id="프리뷰-차이">프리뷰 차이:</h5>
<p>실제 기기와 프리뷰에서의 차이를 고려해 ConstraintLayout을 사용해 감싸는 것이 더 안정적인 해결책이 될 수 있습니다.
따라서 ConstraintLayout을 사용하여 FrameLayout을 감싸면 자식 뷰가 의도대로 표시되면서 경계 밖으로 보이게 되는 문제가 해결될 수 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Fragment]]></title>
            <link>https://velog.io/@simjae____/Fragment</link>
            <guid>https://velog.io/@simjae____/Fragment</guid>
            <pubDate>Wed, 11 Sep 2024 00:54:00 GMT</pubDate>
            <description><![CDATA[<p><strong>Fragment는 Android에서 UI 구성 요소를 재사용 가능하고 독립적으로 관리할 수 있는 작은 부분으로 분리할 수 있게 해주는 기능입니다. Fragment는 자체적인 생명 주기를 가지고 있으며, 하나의 Activity 내에서 여러 개의 Fragment를 추가, 제거, 교체할 수 있습니다. 이는 특히 복잡한 UI를 가진 앱에서 UI를 구성하고 관리하는 데 매우 유용합니다.</strong></p>
<h3 id="1-fragment란">1. Fragment란?</h3>
<blockquote>
<p>정의: Fragment는 UI의 일부를 구성하는 모듈형 컴포넌트입니다. Activity 내에서 여러 개의 Fragment를 함께 사용할 수 있으며, 독립적으로 동작하고, 독자적인 UI와 로직을 가질 수 있습니다.</p>
</blockquote>
<p>특징:
독립적인 생명 주기를 가짐.
재사용성이 높음.
UI와 비즈니스 로직을 분리하여 코드 유지보수를 쉽게 만듦.
동적 UI 변경에 유리.</p>
<h3 id="2-fragment의-생명-주기">2. Fragment의 생명 주기</h3>
<p>Fragment는 Activity와 밀접하게 연동되며, 독립적인 생명 주기를 가집니다. 생명 주기 메서드는 다음과 같습니다:</p>
<p>onAttach(): Fragment가 Activity에 부착될 때 호출됩니다.
onCreate(): Fragment가 생성될 때 호출되며, 초기화 작업을 수행합니다.
onCreateView(): Fragment의 UI를 생성할 때 호출되며, 레이아웃을 설정합니다.
onViewCreated(): onCreateView가 끝난 후, 뷰와 관련된 초기화 작업을 수행합니다.
onStart(): Fragment가 사용자에게 보이기 시작할 때 호출됩니다.
onResume(): Fragment가 활성 상태가 되어 사용자와 상호작용할 수 있을 때 호출됩니다.
onPause(): Fragment가 일시 중지 상태가 될 때 호출됩니다.
onStop(): Fragment가 더 이상 사용자에게 보이지 않을 때 호출됩니다.
onDestroyView(): Fragment의 뷰가 파괴될 때 호출됩니다.
onDestroy(): Fragment가 완전히 소멸되기 전에 호출됩니다.
onDetach(): Fragment가 Activity에서 분리될 때 호출됩니다.</p>
<h3 id="3-fragment-구현-방법">3. Fragment 구현 방법</h3>
<h4 id="1-fragment-클래스-생성하기">1. Fragment 클래스 생성하기</h4>
<p>먼저, Fragment 클래스를 생성하여 원하는 기능을 구현합니다.</p>
<pre><code class="language-kotlin">// MyFragment.kt
class MyFragment : Fragment() {

    // onCreateView를 오버라이드하여 Fragment의 레이아웃을 설정
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Fragment의 레이아웃을 Inflate
        return inflater.inflate(R.layout.fragment_my, container, false)
    }
}</code></pre>
<h4 id="2-fragment-레이아웃-파일-생성하기">2. Fragment 레이아웃 파일 생성하기</h4>
<p>Fragment가 사용할 레이아웃 파일을 생성합니다.</p>
<pre><code>&lt;!-- fragment_my.xml --&gt;
&lt;androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;match_parent&quot;&gt;

    &lt;TextView
        android:id=&quot;@+id/textView&quot;
        android:layout_width=&quot;wrap_content&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:text=&quot;Hello, Fragment!&quot;
        app:layout_constraintTop_toTopOf=&quot;parent&quot;
        app:layout_constraintStart_toStartOf=&quot;parent&quot;/&gt;
&lt;/androidx.constraintlayout.widget.ConstraintLayout&gt;</code></pre><h4 id="3-activity에-fragment-추가하기">3. Activity에 Fragment 추가하기</h4>
<p>XML로 추가: Activity의 레이아웃 파일에서 Fragment를 직접 선언할 수 있습니다.
xml</p>
<pre><code>&lt;!-- activity_main.xml --&gt;
&lt;androidx.fragment.app.FragmentContainerView
    xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    android:id=&quot;@+id/fragment_container_view&quot;
    android:name=&quot;com.example.MyFragment&quot;
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;match_parent&quot; /&gt;</code></pre><p>코드로 추가: Activity에서 코드로 Fragment를 추가할 수 있습니다.</p>
<pre><code>// MainActivity.kt
class MainActivity : AppCompatActivity() {

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

        // Fragment 추가
        val fragment = MyFragment()
        supportFragmentManager.beginTransaction()
            .replace(R.id.fragment_container_view, fragment) // R.id.fragment_container_view는 Fragment가 들어갈 컨테이너의 ID
            .commit()
    }
}</code></pre><h4 id="4-fragment의-동적-처리">4. Fragment의 동적 처리</h4>
<p>Fragment 교체:
Fragment는 Activity 내에서 동적으로 교체할 수 있습니다. 이는 네비게이션이 필요하거나 UI가 상황에 따라 변해야 할 때 유용합니다.</p>
<pre><code>// 다른 Fragment로 교체하는 코드
val newFragment = AnotherFragment()
supportFragmentManager.beginTransaction()
    .replace(R.id.fragment_container_view, newFragment)
    .addToBackStack(null) // 뒤로가기 버튼으로 이전 Fragment로 돌아갈 수 있도록 Back Stack에 추가
    .commit()</code></pre><h4 id="5-fragment의-장점과-활용-사례">5. Fragment의 장점과 활용 사례</h4>
<p>장점:</p>
<p>재사용성: 하나의 Fragment를 여러 Activity에서 재사용할 수 있어 코드 중복을 줄일 수 있습니다.
유연성: 화면 크기에 따라 Fragment 레이아웃을 변경할 수 있어, 태블릿과 스마트폰의 UI를 별도로 관리할 수 있습니다.
동적 UI: Activity에서 Fragment를 동적으로 추가하거나 제거할 수 있어, 앱의 구조를 유연하게 만들 수 있습니다.
활용 사례:</p>
<p>탭 구조: ViewPager와 함께 사용하여 탭 기반의 UI를 구성할 때 유용합니다.
네비게이션: Fragment를 사용하여 화면 전환과 네비게이션을 관리할 수 있습니다.
모듈화: 복잡한 UI를 여러 Fragment로 나누어 관리함으로써 코드의 가독성과 유지보수성을 높일 수 있습니다.</p>
<h4 id="6-fragment의-최신-트렌드-navigation-component">6. Fragment의 최신 트렌드: Navigation Component</h4>
<blockquote>
<p>Navigation Component는 Fragment를 사용하여 네비게이션을 관리할 수 있는 최신 Android Jetpack 라이브러리입니다.</p>
</blockquote>
<p>장점:
코드로 작성하는 복잡한 네비게이션 로직을 XML로 시각화하여 쉽게 관리할 수 있습니다.
Safe Args를 사용하여 타입 안전한 인수 전달이 가능.
Fragment는 안드로이드 앱의 UI 구성에서 중요한 역할을 하며, 특히 복잡한 앱 구조에서 Fragment를 사용하면 UI를 효율적으로 관리하고, 유지보수성과 유연성을 크게 향상시킬 수 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[include 이후...]]></title>
            <link>https://velog.io/@simjae____/include-%EC%9D%B4%ED%9B%84</link>
            <guid>https://velog.io/@simjae____/include-%EC%9D%B4%ED%9B%84</guid>
            <pubDate>Wed, 11 Sep 2024 00:24:53 GMT</pubDate>
            <description><![CDATA[<h1 id="최신-레이아웃-재사용-방법">최신 레이아웃 재사용 방법</h1>
<h4 id="fragment-사용">Fragment 사용:</h4>
<p><code>용도</code>: 레이아웃을 독립적인 구성 요소로 만들고, 복잡한 UI에서 재사용할 때 유용합니다.</p>
<p><code>장점</code>: Fragment는 독립적인 생명 주기를 가지며, 동적으로 UI를 추가하고 제거하는 데 적합합니다.</p>
<p><code>사용 예시</code>:
네비게이션, 탭을 포함한 구조에서 주로 사용.
복잡한 레이아웃을 여러 부분으로 나눠 관리할 때 사용.</p>
<h4 id="view-binding-및-data-binding-사용">View Binding 및 Data Binding 사용:</h4>
<p><code>용도</code>: 레이아웃을 직접 코드로 조작할 때 유용하며, XML과 Kotlin/Java 코드를 더 강력하게 연결할 수 있습니다.
장점: Null Pointer Exception을 방지하고, XML 속성을 코드에서 더 간편하게 사용할 수 있습니다.
예시:</p>
<pre><code class="language-kotlin">// ViewBinding을 이용하여 레이아웃 재사용
val binding = LayoutHeaderBinding.inflate(layoutInflater)
parentLayout.addView(binding.root)
&lt;merge&gt; 태그 사용:</code></pre>
<p><code>용도</code>: include의 성능을 개선하고 불필요한 부모 뷰 계층을 줄일 때 유용합니다.
<code>장점</code>: 뷰 계층을 줄여 렌더링 성능을 향상시킬 수 있습니다.
<code>사용 방법</code>:
include되는 레이아웃 파일에 <merge> 태그를 사용하면 포함된 뷰들이 바로 부모에 추가되어 중복 계층을 제거합니다.
사용 예시:</p>
<pre><code>&lt;!-- layout_header.xml --&gt;
&lt;merge xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;&gt;
    &lt;TextView
        android:id=&quot;@+id/tvTitle&quot;
        android:layout_width=&quot;wrap_content&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:text=&quot;Header Title&quot;/&gt;
&lt;/merge&gt;</code></pre><h4 id="custom-view-커스텀-뷰-사용">Custom View (커스텀 뷰) 사용:</h4>
<p><code>용도</code>: 반복되는 레이아웃을 하나의 뷰로 만들어 재사용할 때 사용합니다.
<code>장점</code>: 재사용성이 높고, 컴포넌트화되어 코드가 깔끔해집니다.
<code>사용 방법</code>:
공통된 UI 요소를 커스텀 클래스로 만들어 XML에서 사용할 수 있게 만듭니다.
사용 예시:</p>
<pre><code class="language-java">// CustomView.kt
class CustomHeaderView @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr) {
    init {
        LayoutInflater.from(context).inflate(R.layout.layout_header, this, true)
    }
}</code></pre>
<h4 id="결론">결론</h4>
<p>include는 잘못된 방법이 아니며, 여전히 사용될 수 있습니다. 그러나 레이아웃이 복잡하거나 동적으로 UI를 추가해야 할 때, 또는 더 효율적인 관리를 위해 최신 방식(예: Fragment, View Binding, Custom View 등)을 고려해 보는 것이 좋습니다. 상황에 맞는 방식을 선택하여 유지보수성과 성능을 개선할 수 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Leaflet와 SVG/Canvas를 사용하여 Pixel Art 이미지 선택 기능 구현하기]]></title>
            <link>https://velog.io/@simjae____/Leaflet%EC%99%80-SVGCanvas%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EC%97%AC-Pixel-Art-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%84%A0%ED%83%9D-%EA%B8%B0%EB%8A%A5-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@simjae____/Leaflet%EC%99%80-SVGCanvas%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EC%97%AC-Pixel-Art-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%84%A0%ED%83%9D-%EA%B8%B0%EB%8A%A5-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0</guid>
            <pubDate>Fri, 02 Aug 2024 01:55:58 GMT</pubDate>
            <description><![CDATA[<p>이번 포스팅에서는 Leaflet와 SVG/Canvas를 사용하여 Pixel Art 이미지를 표시하고, 픽셀을 선택하여 색상을 변경하는 기능을 구현하는 과정을 공유하고자 합니다. 이 과정에서 겪었던 시행착오와 해결 방법을 상세히 기록하였습니다.</p>
<h3 id="프로젝트-개요">프로젝트 개요</h3>
<p>Leaflet는 주로 지도 시각화를 위해 사용되지만, 이 프로젝트에서는 Leaflet의 줌 및 팬 기능을 활용하여 Pixel Art 이미지를 표시하고, 각 픽셀을 선택하는 기능을 구현했습니다. 초기에는 SVG를 사용했지만, 이후 Canvas로 전환하여 최적화 및 기능 향상을 도모했습니다.</p>
<h3 id="1-기본-설정">1. 기본 설정</h3>
<p>먼저, 기본적인 Leaflet 설정을 진행했습니다.</p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
  &lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot; /&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot; /&gt;
    &lt;title&gt;Leaflet with Pixel Art&lt;/title&gt;
    &lt;link rel=&quot;stylesheet&quot; href=&quot;https://unpkg.com/leaflet/dist/leaflet.css&quot; /&gt;
    &lt;style&gt;
      #map {
        height: 600px;
        width: 800px;
      }
    &lt;/style&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;div id=&quot;map&quot;&gt;&lt;/div&gt;
    &lt;script src=&quot;https://unpkg.com/leaflet/dist/leaflet.js&quot;&gt;&lt;/script&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre>
<h3 id="2-svg를-사용한-초기-구현">2. SVG를 사용한 초기 구현</h3>
<p>처음에는 SVG를 사용하여 이미지를 픽셀 단위로 표시하고, 클릭 이벤트를 통해 색상을 변경하는 기능을 구현했습니다.</p>
<pre><code class="language-javascript">document.addEventListener(&#39;DOMContentLoaded&#39;, () =&gt; {
    if (L.DomUtil.get(&#39;map&#39;)._leaflet_id) {
        L.DomUtil.get(&#39;map&#39;)._leaflet_id = null;
    }

    const map = L.map(&#39;map&#39;, {
        crs: L.CRS.Simple,
        minZoom: -5
    }).setView([0, 0], 0);

    const imageUrl = &#39;./sample.webp&#39;;
    const imageBounds = [[0, 0], [512, 512]];

    const imageOverlay = L.imageOverlay(imageUrl, imageBounds).addTo(map);
    map.fitBounds(imageBounds);

    imageOverlay.on(&#39;load&#39;, () =&gt; {
        const img = document.querySelector(&#39;.leaflet-image-layer&#39;);
        const svg = document.createElementNS(&quot;http://www.w3.org/2000/svg&quot;, &quot;svg&quot;);
        svg.setAttribute(&quot;width&quot;, img.naturalWidth);
        svg.setAttribute(&quot;height&quot;, img.naturalHeight);
        svg.style.position = &quot;absolute&quot;;
        svg.style.top = &quot;0&quot;;
        svg.style.left = &quot;0&quot;;

        const pixelSize = 10; // 픽셀 블록 크기
        const selectedPixels = {}; // 선택된 픽셀의 원래 색상 저장

        // SVG에 픽셀 아트를 그림
        const drawPixelatedImage = () =&gt; {
            const ctx = document.createElement(&#39;canvas&#39;).getContext(&#39;2d&#39;);
            ctx.canvas.width = img.naturalWidth;
            ctx.canvas.height = img.naturalHeight;
            ctx.drawImage(img, 0, 0);

            for (let y = 0; y &lt; ctx.canvas.height; y += pixelSize) {
                for (let x = 0; x &lt; ctx.canvas.width; x += pixelSize) {
                    const pixelData = ctx.getImageData(x, y, pixelSize, pixelSize).data;
                    const r = pixelData[0];
                    const g = pixelData[1];
                    const b = pixelData[2];

                    const rect = document.createElementNS(&quot;http://www.w3.org/2000/svg&quot;, &quot;rect&quot;);
                    rect.setAttribute(&quot;x&quot;, x);
                    rect.setAttribute(&quot;y&quot;, y);
                    rect.setAttribute(&quot;width&quot;, pixelSize);
                    rect.setAttribute(&quot;height&quot;, pixelSize);
                    rect.setAttribute(&quot;fill&quot;, `rgb(${r}, ${g}, ${b})`);
                    rect.addEventListener(&#39;click&#39;, () =&gt; {
                        const pixelKey = `${x},${y}`;
                        if (selectedPixels[pixelKey]) {
                            rect.setAttribute(&quot;fill&quot;, `rgb(${selectedPixels[pixelKey][0]}, ${selectedPixels[pixelKey][1]}, ${selectedPixels[pixelKey][2]})`);
                            delete selectedPixels[pixelKey];
                        } else {
                            selectedPixels[pixelKey] = [r, g, b];
                            rect.setAttribute(&quot;fill&quot;, &quot;red&quot;);
                        }
                        console.log(`Coordinates: (${x}, ${y}), Color: rgb(${r}, ${g}, ${b})`);
                    });
                    svg.appendChild(rect);
                }
            }
        };

        drawPixelatedImage();
        img.style.display = &#39;none&#39;;
        img.parentNode.insertBefore(svg, img);

        const updateSvgPosition = () =&gt; {
            const bounds = imageOverlay._bounds;
            const topLeft = map.latLngToLayerPoint(bounds.getNorthWest());
            const bottomRight = map.latLngToLayerPoint(bounds.getSouthEast());

            svg.style.width = `${bottomRight.x - topLeft.x}px`;
            svg.style.height = `${bottomRight.y - topLeft.y}px`;
            svg.style.transform = `translate(${topLeft.x}px, ${topLeft.y}px)`;
        };

        map.on(&#39;zoomend&#39;, updateSvgPosition);
        map.on(&#39;moveend&#39;, updateSvgPosition);
        updateSvgPosition();
    });
});</code></pre>
<h3 id="3-canvas로-전환한-이유">3. Canvas로 전환한 이유</h3>
<p>SVG를 사용한 초기 구현은 픽셀 단위의 세부 조작이 가능했지만, 성능 문제가 발생했습니다. 특히 큰 이미지의 경우 SVG 요소가 많아지면 브라우저 성능이 저하되었습니다. 이를 해결하기 위해 Canvas로 전환했습니다.</p>
<h3 id="4-canvas를-사용한-최종-구현">4. Canvas를 사용한 최종 구현</h3>
<p>Canvas를 사용하여 픽셀 단위 이미지를 그리고, 클릭 이벤트를 통해 색상을 변경하는 기능을 구현했습니다.</p>
<pre><code class="language-javascript">document.addEventListener(&#39;DOMContentLoaded&#39;, () =&gt; {
    if (L.DomUtil.get(&#39;map&#39;)._leaflet_id) {
        L.DomUtil.get(&#39;map&#39;)._leaflet_id = null;
    }

    const map = L.map(&#39;map&#39;, {
        crs: L.CRS.Simple,
        minZoom: -5
    }).setView([0, 0], 0);

    const imageUrl = &#39;./sample.webp&#39;;
    const imageBounds = [[0, 0], [512, 512]];

    const imageOverlay = L.imageOverlay(imageUrl, imageBounds).addTo(map);
    map.fitBounds(imageBounds);

    imageOverlay.on(&#39;load&#39;, () =&gt; {
        const img = document.querySelector(&#39;.leaflet-image-layer&#39;);
        const canvas = document.createElement(&#39;canvas&#39;);
        canvas.width = img.naturalWidth;
        canvas.height = img.naturalHeight;
        const ctx = canvas.getContext(&#39;2d&#39;);
        ctx.drawImage(img, 0, 0);

        img.style.display = &#39;none&#39;;
        img.parentNode.insertBefore(canvas, img);

        canvas.style.position = &#39;absolute&#39;;
        canvas.style.top = &#39;0&#39;;
        canvas.style.left = &#39;0&#39;;

        const pixelSize = 10; // 픽셀 블록 크기
        const selectedPixels = {}; // 선택된 픽셀의 원래 색상 저장

        // 캔버스에 픽셀 아트를 그림
        const drawPixelatedImage = () =&gt; {
            for (let y = 0; y &lt; canvas.height; y += pixelSize) {
                for (let x = 0; x &lt; canvas.width; x += pixelSize) {
                    const pixelData = ctx.getImageData(x, y, pixelSize, pixelSize).data;
                    const r = pixelData[0];
                    const g = pixelData[1];
                    const b = pixelData[2];

                    ctx.fillStyle = `rgb(${r}, ${g}, ${b})`;
                    ctx.fillRect(x, y, pixelSize, pixelSize);
                }
            }
        };

        drawPixelatedImage();

        const updateCanvasPosition = () =&gt; {
            const bounds = imageOverlay._bounds;
            const topLeft = map.latLngToLayerPoint(bounds.getNorthWest());
            const bottomRight = map.latLngToLayerPoint(bounds.getSouthEast());

            canvas.style.width = `${bottomRight.x - topLeft.x}px`;
            canvas.style.height = `${bottomRight.y - topLeft.y}px`;
            canvas.style.transform = `translate(${topLeft.x}px, ${topLeft.y}px)`;
        };

        map.on(&#39;zoomend&#39;, updateCanvasPosition);
        map.on(&#39;moveend&#39;, updateCanvasPosition);
        updateCanvasPosition();

        let isDragging = false;

        canvas.addEventListener(&#39;mousedown&#39;, () =&gt; {
            isDragging = false;
        });

        canvas.addEventListener(&#39;mousemove&#39;, () =&gt; {
            isDragging = true;
        });

        canvas.addEventListener(&#39;mouseup&#39;, (e) =&gt; {
            if (!isDragging) {
                const rect = canvas.getBoundingClientRect();
                const scaleX = canvas.width / rect.width;
                const scaleY = canvas.height / rect.height;
                const x = Math.floor((e.clientX - rect.left) * scaleX / pixelSize) * pixelSize;
                const y = Math.floor((e.clientY - rect.top) * scaleY / pixelSize) * pixelSize;



 const pixelKey = `${x},${y}`;
                const pixel = ctx.getImageData(x, y, pixelSize, pixelSize).data;

                if (selectedPixels[pixelKey]) {
                    const originalColor = selectedPixels[pixelKey];
                    ctx.fillStyle = `rgb(${originalColor[0]}, ${originalColor[1]}, ${originalColor[2]})`;
                    delete selectedPixels[pixelKey];
                } else {
                    selectedPixels[pixelKey] = [pixel[0], pixel[1], pixel[2]];
                    ctx.fillStyle = &quot;red&quot;;
                }
                ctx.fillRect(x, y, pixelSize, pixelSize);
                console.log(`Coordinates: (${x}, ${y}), Color: rgb(${pixel[0]}, ${pixel[1]}, ${pixel[2]})`);
            }
        });
    });
});</code></pre>
<h3 id="5-시행착오-및-문제-해결">5. 시행착오 및 문제 해결</h3>
<h4 id="문제-1-확대-및-축소-기능이-정상-작동하지-않음">문제 1: 확대 및 축소 기능이 정상 작동하지 않음</h4>
<ul>
<li><strong>해결 방법</strong>: <code>updateCanvasPosition</code> 함수를 통해 줌 및 이동 이벤트에 따라 캔버스의 위치를 업데이트하여 문제를 해결했습니다.</li>
</ul>
<h4 id="문제-2-선택한-픽셀이-잘못된-위치에서-선택됨">문제 2: 선택한 픽셀이 잘못된 위치에서 선택됨</h4>
<ul>
<li><strong>해결 방법</strong>: <code>getBoundingClientRect</code>와 <code>scaleX</code>, <code>scaleY</code>를 사용하여 정확한 좌표를 계산하도록 수정했습니다.</li>
</ul>
<h4 id="문제-3-드래그-중에도-픽셀-선택-이벤트-발생">문제 3: 드래그 중에도 픽셀 선택 이벤트 발생</h4>
<ul>
<li><strong>해결 방법</strong>: <code>mousedown</code>, <code>mousemove</code>, <code>mouseup</code> 이벤트를 통해 드래그 중에는 픽셀 선택 이벤트가 발생하지 않도록 했습니다.</li>
</ul>
<h4 id="문제-4-svg-성능-문제">문제 4: SVG 성능 문제</h4>
<ul>
<li><strong>해결 방법</strong>: SVG 대신 Canvas를 사용하여 성능 문제를 해결했습니다.</li>
</ul>
<h3 id="최종-결과">최종 결과</h3>
<ul>
<li>확대 및 축소 기능이 잘 작동하며, 각 픽셀을 선택하고 다시 선택하여 색상을 변경할 수 있습니다.</li>
<li>선택된 픽셀의 좌표와 색상 정보는 콘솔에 로그로 출력됩니다.</li>
<li>Canvas를 사용하여 성능을 최적화했습니다.</li>
</ul>
<h3 id="결론">결론</h3>
<p>이번 프로젝트를 통해 Leaflet와 SVG/Canvas를 결합하여 Pixel Art 이미지를 표시하고, 각 픽셀을 선택하는 기능을 구현했습니다. 이 과정에서 여러 시행착오를 겪었지만, 이를 해결하며 많은 것을 배울 수 있었습니다. 이 글이 비슷한 기능을 구현하고자 하는 분들께 도움이 되길 바랍니다.</p>
<hr>
<p>위의 글을 바탕으로 필요에 따라 추가 정보를 삽입하거나 수정하여 블로그에 게시하시면 됩니다. 추가 질문이 있으시면 언제든지 문의해주세요!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[RN 키보드 이슈]]></title>
            <link>https://velog.io/@simjae____/RN-%ED%82%A4%EB%B3%B4%EB%93%9C-%EC%9D%B4%EC%8A%88</link>
            <guid>https://velog.io/@simjae____/RN-%ED%82%A4%EB%B3%B4%EB%93%9C-%EC%9D%B4%EC%8A%88</guid>
            <pubDate>Thu, 09 May 2024 02:09:42 GMT</pubDate>
            <description><![CDATA[<p>키보드가 올라오면서 입력 필드를 가리는 문제</p>
<p>KeyboardAvoidingView 사용하기: React Native에 내장된 KeyboardAvoidingView 컴포넌트를 사용하여 키보드가 활성화될 때 입력 필드를 자동으로 위로 밀어올릴 수 있습니다. 이 컴포넌트는 키보드가 올라올 때 자동으로 뷰를 조정하여 입력 필드를 가리지 않도록 합니다.예를 들어, MyPageScreen에서 SafeAreaTooltipOverlay와 NavigationHeader 사이에 KeyboardAvoidingView를 추가할 수 있습니다:
jsx
Copy code
<SafeAreaTooltipOverlay>
  &lt;NavigationHeader title={&quot;My Page&quot;} handleGoBack={handleGoBack} /&gt;
  &lt;KeyboardAvoidingView behavior={Platform.OS === &quot;ios&quot; ? &quot;padding&quot; : &quot;height&quot;} style={{flex: 1}}&gt;
    <View style={styles.formContainer}>
      ...
    </View>
    <View style={styles.buttonWrapper}>
      <CustomButton
        title="Save"
        size="large"
        variant="filled"
        onPress={handleSubmit(onUpdateUserInfo)}
      />
    </View>
  </KeyboardAvoidingView>
</SafeAreaTooltipOverlay>
behavior 속성은 플랫폼에 따라 조정할 수 있습니다. iOS는 일반적으로 &quot;padding&quot;을, Android는 &quot;height&quot;를 사용합니다.
ScrollView 사용하기: 폼의 입력 필드가 많은 경우 ScrollView를 사용하여 사용자가 필요에 따라 스크롤하여 숨겨진 입력 필드를 볼 수 있도록 할 수 있습니다. 이 방법은 사용자에게 더 많은 제어를 제공하고 키보드가 활성화되었을 때 자연스럽게 스크롤할 수 있게 합니다.ScrollView로 전체 또는 일부 레이아웃을 감쌀 수 있습니다:
jsx
Copy code
<SafeAreaTooltipOverlay>
  &lt;NavigationHeader title={&quot;My Page&quot;} handleGoBack={handleGoBack} /&gt;
  <ScrollView>
    <View style={styles.formContainer}>
      ...
    </View>
    <View style={styles.buttonWrapper}>
      <CustomButton
        title="Save"
        size="large"
        variant="filled"
        onPress={handleSubmit(onUpdateUserInfo)}
      />
    </View>
  </ScrollView>
</SafeAreaTooltipOverlay></p>
<p>키보드가 표시될 때 스크린 크기 조정: 특정 경우에는 ScrollView나 KeyboardAvoidingView 외에도 키보드가 활성화될 때 전체 스크린 레이아웃을 조정해야 할 수 있습니다. 이는 더 복잡한 로직을 필요로 하지만, 키보드가 활성화될 때 발생하는 이벤트를 청취하고 그에 따라 상태를 업데이트하여 UI를 조정할 수 있습니다.
.</p>
]]></description>
        </item>
    </channel>
</rss>