<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>ted_moon99.log</title>
        <link>https://velog.io/</link>
        <description>서버 및 모바일 앱 개발자</description>
        <lastBuildDate>Tue, 28 Jan 2025 11:29:32 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. ted_moon99.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/ted_moon99" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[GitHub] Github Actions]]></title>
            <link>https://velog.io/@ted_moon99/GitHub-Github-Actions</link>
            <guid>https://velog.io/@ted_moon99/GitHub-Github-Actions</guid>
            <pubDate>Tue, 28 Jan 2025 11:29:32 GMT</pubDate>
            <description><![CDATA[<h1 id="intro">Intro</h1>
<p>Github Actions은 소프트웨어 개발 작업흐름을 repository에서 즉시 자동화, 커스텀, 실행할 수 있게 해준다. 이를 사용하여 하고자 하는 작업, CI/CD를 수행하는 action을 발견하고 만들고 공유할 수 있으며 이를 커스텀된 작업흐름으로 조합할 수 있다.</p>
<h1 id="github-actions에-대한-이해">Github Actions에 대한 이해</h1>
<p>Github Actions는 CI(Continuous Integration) CD(Continuous Delivery) 플랫폼으로 빌드, 테스트, pipeline 개발을 자동화 할 수 있다.</p>
<blockquote>
<p>pipeline: 한 데이터 처리 단계의 출력이 다음 단계의 입력으로 이어지는 형태로 연결된 구조</p>
</blockquote>
<p>Github Actions를 사용하여 workflow를 만들 수 있으며, 이를 통해 repository에 <code>PR을 날릴 때</code>또는 <code>product 버전에 PR을 머지할 때</code>마다 빌드하고 테스트할 수있다.</p>
<p>Github Actions은 단순한 DevOps를 넘어서 repository에서 다른 이벤트가 발생할 대, workflow를 실행할 수 있게 해준다. 예를 들어, repository에서 새로운 이슈를 생성할 때 자동으로 적절한 label을 추가해주는 workflow를 실행할 수 있다</p>
<p><img src="https://velog.velcdn.com/images/ted_moon99/post/663ceaeb-2469-4896-bd86-783c30dd8565/image.png" alt="Github Actions"></p>
<h1 id="github-actions의-구성요소">Github Actions의 구성요소</h1>
<ul>
<li><p>Repository에서 <code>PR이 생성</code>되거나 <code>issue가 생성</code>되는 것과 같은 이벤트가 발생했을 때 동작하는 Github Actions workflow를 설정할 수 있다.</p>
</li>
<li><p>Workflow는 <code>순차적인 순서로 작동</code>하거나 <code>병렬적으로 작동</code>하는 한 개 이상의 작업을 포함하고 있다. </p>
<ul>
<li>각 작업은 작업이 보유하고 있는 <code>고유한 Virtual Machine Runner</code> 안에서 작동하거나 <code>Container</code> 안에서 작동한다. </li>
<li>각 작업은 <code>정의한 스크립트를 실행</code>하거나 <code>작업 흐름을 단순화할 수 있는 재사용 가능한 extension인 action</code>을 실행하는 한 개 이상의 단계를 가진다
<img src="https://velog.velcdn.com/images/ted_moon99/post/a52d4217-aca4-46da-9af4-7abe68aa3cc1/image.png" alt=""></li>
</ul>
</li>
</ul>
<h1 id="workflows">Workflows</h1>
<blockquote>
<p>workflow: 특정 순서에 따라 발생하는 <strong>반복적인</strong> 프로세스와 작업을 관리하는 시스템</p>
</blockquote>
<ul>
<li>Workflow는 개발자가 설정가능한 자동화된 process로 하나 이상의 작업을 실행한다.</li>
<li>Workflow는 repository에서 확인된 YAML 파일에 의해 정의되며, repository에서 <code>이벤트가 발생</code>하거나 <code>메뉴얼</code>에 의해 작동하거나 <code>정의된 스케줄</code>에 의해 작동한다.</li>
<li>Workflow는 repository 내에 있는 <code>.github/workflows</code> 디렉토리에 정의된다.</li>
<li>repository는 다수의 workflow를 가질 수 있으며 각 workflow는 작업들의 다른 집합을 수행할 수 있다. 예를들어, 다음의 작업을 할 수 있다<ul>
<li>Pull Requests(PR)을 빌드하고 테스트하는 작업</li>
<li>Release가 생성될 때마다 Application 배포</li>
<li>새로운 issue가 생성될 때마다 label을 추가</li>
</ul>
</li>
</ul>
<h1 id="events">Events</h1>
<ul>
<li><p>Event는 workflow의 실행이 동작하는 repository 내에서의 구체적인 활동을 의미한다. 예를 들어, 누군가가 PR을 생성, issue 생성, commit을 repository에 push했을 때 활동은 Github에서 비롯된다.</p>
</li>
<li><p>또한, <code>메뉴얼에 의해</code> 또는 <code>REST API를 올림으로서</code> 스케줄을 실행하는 workflow를 작동할 수 있다</p>
</li>
</ul>
<h1 id="jobs">Jobs</h1>
<ul>
<li><p>Job(작업)은 동일한 Runner에 의해 실행되는 Workflow 안에서 작업 단계들의 모임이다. </p>
</li>
<li><p>각 단계는 실행될 쉘 스크립트이거나 실행되는 action이다.</p>
</li>
<li><p>단계들은 순차적으로 실행되거나(Sequential Order) 서로 독립적으로 실행된다(병렬 실행, Parallel)</p>
</li>
<li><p>각 단계들이 동일한 Runner에 의해 실행되기 때문에, 한 단계의 데이터를 다른 단계와 공유할 수 있다. 예를 들어, 어떤 단계에서 Application을 빌드한 후 다음 단계에서 빌드된 Application을 테스트 할 수 있다.
<img src="https://velog.velcdn.com/images/ted_moon99/post/dfea44e0-93b1-4578-a2e8-7b01310af14e/image.png" alt=""></p>
</li>
<li><p>작업(jobA)을 다른 작업(jobB)에 의존하도록 설정할 수 있다.</p>
</li>
<li><p>기본설정은 병렬실행이다<code>즉, 작업(job)은 다른 작업에 의존하지 않는다.</code></p>
</li>
<li><p>작업(jobA)에 의존성을 설정하면, 작업(jobA)이 바로 실행되지 않고 의존하고 있는 작업(jobB)가 끝날 때까지 기다린다
예를 들어, 작업 의존성 없이 다양한 아키텍처에 대한 여러 빌드 작업과 해당 빌드에 의존하는 패키징 작업을 구성할 수 있다. 빌드 작업은 병렬로 실행되며 성공적으로 완료되면 패키징 작업이 실행됩니다.</p>
</li>
</ul>
<h1 id="actions">Actions</h1>
<h1 id="references">References</h1>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TroubleShooting] ViewModel 공유하기]]></title>
            <link>https://velog.io/@ted_moon99/TroubleShooting-ViewModel-%EA%B3%B5%EC%9C%A0%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@ted_moon99/TroubleShooting-ViewModel-%EA%B3%B5%EC%9C%A0%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 20 Jan 2025 14:55:15 GMT</pubDate>
            <description><![CDATA[<p>오늘은 회원가입을 개발하는 중이었는데 기존에 설계된 ViewModel 및 화면 전환에 있어서 문제점을 발견하였다.</p>
<h1 id="요구사항">요구사항</h1>
<p>회원가입에 필요한 화면이 ScreenA, ScreenB, ScreenC가 있다고 가정하자. </p>
<ul>
<li>ScreenA : <code>이메일</code> 및 <code>비밀번호</code> 입력</li>
<li>ScreenB : <code>이름</code> 및 <code>전화번호</code> 입력</li>
<li>ScreenC : 환영 페이지</li>
</ul>
<p>화면 플로우는 다음과 같다.</p>
<blockquote>
<p>ScreenA -&gt; ScreenB(회원가입 요청) -&gt; ScreenC</p>
</blockquote>
<h1 id="현재-상황">현재 상황</h1>
<h2 id="viewmodel">ViewModel</h2>
<pre><code class="language-kotlin">class SignUpViewModel @Inject constructor(
    private val repository: SignUpRepository
): ViewModel(){

}</code></pre>
<h2 id="screena">ScreenA</h2>
<pre><code class="language-kotlin">@Composable
fun ScreenA(
    // &#39;&#39;&#39; 
    viewModel: SignUpViewModel = hiltViewModel(),
    // &#39;&#39;&#39; 
) </code></pre>
<h2 id="screenb">ScreenB</h2>
<pre><code class="language-kotlin">@Composable
fun ScreenB(
    // &#39;&#39;&#39; 
    viewModel: SignUpViewModel = hiltViewModel(),
    // &#39;&#39;&#39; 
) </code></pre>
<h2 id="appnavhost">AppNavHost</h2>
<pre><code class="language-kotlin">val navController = rememberNavController()

composable(&quot;ScreenA&quot;){
    ScreenA(
        // &#39;&#39;&#39;
        onNextButtonClicked = {
            navController.navigate(&quot;ScreenB&quot;)
        },
        // &#39;&#39;&#39;
    )
}

composable(&quot;ScreenB&quot;){
    ScreenB(
        // &#39;&#39;&#39;
        onNextButtonClicked = {
            navController.navigate(&quot;ScreenC&quot;)
        },
        // &#39;&#39;&#39;
    )
}</code></pre>
<h1 id="문제점">문제점</h1>
<p>이러한 방식으로 그동안 많이 썼었는데 이번에 ScreenA와 ScreenB가 데이터를 공유해야 하는 과정에서 한계를 직면했다. 이 방식에는 문제가 존재한다.</p>
<p>위의 방식의 경우
ScreenA에서 ScreenB로 이동할 때 Hilt를 사용하더라도 ViewModel 인스턴스가 새로 생성된다.</p>
<blockquote>
<ol>
<li>각 화면은 자신의 고유한 NavBackStackEntry를 가진다. </li>
<li>navController.navigatie()로 새로운 화면을 추가하면, 새로운 NavBackStackEntry가 생성된다. 이때 해당 엔트리에 맞는 새로운 ViewModel이 생성된다.</li>
<li>hiltViewModel()은 현재 활성화된 NavBackStackEntry와 연결된 ViewModel을 제공한다.</li>
</ol>
</blockquote>
<p>위와 같은 이유 때문에 ScreenA에서 저장했던 데이터가 ScreenB에서 ViewModel을 통해 조회할 경우 초기값으로 나오는 문제가 발생하였다.</p>
<p><img src="https://velog.velcdn.com/images/ted_moon99/post/9e0c3029-2cd8-4826-b78b-71eb0a9f4286/image.png" alt=""></p>
<p>위와 같은 사유로 서버에 데이터를 전송할 때 BAD REQUEST인 HTTP 400 에러가 발생하였다.</p>
<h1 id="해결방법">해결방법</h1>
<p>Jetpack Compose에서는 navBackStackEntry를 활용해 네비게이션 스택이 ViewModel의 상태를 인지하도록 설정하여 불필요한 재생성을 방지할 수 있다.</p>
<h2 id="screena-1">ScreenA</h2>
<pre><code class="language-kotlin">@Composable
fun ScreenA(
    // &#39;&#39;&#39;
    viewModel: SignUpViewModel,
    // &#39;&#39;&#39;
) { 
    // &#39;&#39;&#39; 
}</code></pre>
<h2 id="screenb-1">ScreenB</h2>
<pre><code class="language-kotlin">@Composable
fun ScreenB(
    // &#39;&#39;&#39;
    viewModel: SignUpViewModel,
    // &#39;&#39;&#39;
) { 
    // &#39;&#39;&#39; 
}</code></pre>
<h2 id="viewmodel-1">ViewModel</h2>
<pre><code class="language-kotlin">@HiltViewModel
class SignUpViewModel @Inject constructor(
    // &#39;&#39;&#39;
): ViewModel() {
    // &#39;&#39;&#39;
}</code></pre>
<h2 id="appnavhost-1">AppNavHost</h2>
<pre><code class="language-kotlin">composable(&quot;ScreenA&quot;){ backStackEntry: NavBackStackEntry -&gt;
    val viewModel: SignUpViewModel = hiltViewModel(viewModelStoreOwner = backStackEntry)

    ScreenA(
        // &#39;&#39;&#39;
        viewModel = viewModel,
        onNextButtonClicked = {
            navController.navigate(&quot;ScreenB&quot;)
        },
        // &#39;&#39;&#39; 
    )
}</code></pre>
<p>val viewModel: SignUpViewModel = hiltViewModel(viewModelStoreOwner = backStackEntry)는 NavBackStackEntry를 기반으로 SignUpViewModel의 인스턴스를 새롭게 생성하거나 기존 인스턴스를 반환한다</p>
<pre><code class="language-kotlin">composable(&quot;ScreenB&quot;){ backStackEntry: NavBackStackEntry -&gt;
    val viewModel: SignUpViewModel = if (navController.previousBackStackEntry != null){
                // 이전 화면이 존재하는 경우
                hiltViewModel(viewModelStoreOwner = navController.previousBackStackEntry!!)
            } else {
                // 이전 화면이 존재하지 않는 경우
                hiltViewModel()
            }

    ScreenB(
        // &#39;&#39;&#39;
        viewModel = viewModel,
        onNextButtonClicked = {
            navController.navigate(&quot;ScreenC&quot;)
        },
        // &#39;&#39;&#39; 
    )
}</code></pre>
<p>이제 정상적으로 동작하는지 다시 로그를 찍어 확인해보겠다.
<img src="https://velog.velcdn.com/images/ted_moon99/post/006c8df9-1cab-4c96-b48e-8a25122a588f/image.png" alt="">
드디어 두 화면간에 데이터가 공유되어 정상적으로 API 호출이 이루어진 것이 확인된다.</p>
<h1 id="핵심정리">핵심정리</h1>
<ol>
<li><p>hiltViewModel(backStackEntry: NavBackStackEntry) : 특정 NavBackStackEntry를 기반으로 ViewModel을 생성하거나 반환한다</p>
</li>
<li><p>공유 ViewModel의 장점: </p>
<ul>
<li>ViewModel을 여러 화면에서 공유할 수 있으므로 API 호출 / 초기화 작업을 반복하지 않아도 된다.</li>
<li>내비게이션 스택을 활용해 ViewModel의 인스턴스를 효율적으로 관리할 수 있다</li>
<li>ViewModel을 공유하므로 이전 화면에서 ViewModel에 저장해둔 데이터를 사용할 수 있다.</li>
</ul>
</li>
</ol>
<h1 id="reference">Reference</h1>
<p><a href="https://velog.io/@thisyoon97/ViewModel-%EA%B3%B5%EC%9C%A0%ED%95%98%EA%B8%B0">ViewModel 공유하기</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TroubleShooting] 새로운 Module 생성하기]]></title>
            <link>https://velog.io/@ted_moon99/TroubleShootring-org.jetbrains.kotlin.backend.common.BackendException-Backend-Internal-error-Exception-during-IR-lowering</link>
            <guid>https://velog.io/@ted_moon99/TroubleShootring-org.jetbrains.kotlin.backend.common.BackendException-Backend-Internal-error-Exception-during-IR-lowering</guid>
            <pubDate>Sun, 19 Jan 2025 16:23:36 GMT</pubDate>
            <description><![CDATA[<h1 id="1-문제발생">1. 문제발생</h1>
<p>클린 아키텍처를 적용해보겠다고 <code>새로운 Module</code>을 생성하고 실행시켰더니 바로 이런 에러가 떴다.</p>
<blockquote>
<p>org.jetbrains.kotlin.backend.common.BackendException: Backend Internal error: Exception during IR lowering</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/ted_moon99/post/2b376034-7623-4d9e-9d1e-f1cac3739353/image.png" alt=""></p>
<h1 id="2-원인">2. 원인</h1>
<p>새로 추가한 모듈에서 @Composable 함수를 작성했는데, <code>Compose를 활성화하지 않아서</code> 발생한 문제</p>
<h1 id="3-해결방안">3. 해결방안</h1>
<ol>
<li>새로운 Module의 build.gradle 파일로 들어간다.</li>
<li>build.gradle 파일에 아래처럼 compose 활성화 코드를 작성해준다
<img src="https://velog.velcdn.com/images/ted_moon99/post/7aa3c131-5a22-43a4-a95f-12c527b8c20a/image.png" alt=""></li>
<li>Sync Now 클릭 후 다시 실행하고 작동하는지 확인한다</li>
</ol>
<h1 id="4-비고">4. 비고</h1>
<p>아직 새로운 Module에 Compose관련 Gradle 설정을 완료하지 않았다면 아래 글을 참고하여 설정을 완료해주자.
(참고 : alias(libs.plugins.compose.compiler) 대신 alias(libs.plugins.compose) 작성해줘도 됨)
<a href="https://developer.android.com/develop/ui/compose/compiler">Compose Compiler Gradle Plugin</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Jetpack] Navigation 화면간 이동]]></title>
            <link>https://velog.io/@ted_moon99/Jetpack-Navigation-%ED%99%94%EB%A9%B4%EA%B0%84-%EC%9D%B4%EB%8F%99</link>
            <guid>https://velog.io/@ted_moon99/Jetpack-Navigation-%ED%99%94%EB%A9%B4%EA%B0%84-%EC%9D%B4%EB%8F%99</guid>
            <pubDate>Sun, 19 Jan 2025 16:03:54 GMT</pubDate>
            <description><![CDATA[<p>오늘은 Jetpack Compose에서 화면을 이동을 하는 것을 정리해보고자 한다</p>
<h1 id="intro">Intro</h1>
<p>Navigation Architecture Component(이하 Navigation)은 18년도에 처음 소개된 Android Jetpack Library이다. Fragment, Activity와 같은 components 간 탐색을 돕기 위한 framework이다.</p>
<p>기본적으로 Navigation은 Single Activty - Multiple Fragments 구조에 대한 지원이 대부분이다. 따라서 다음의 경우에는 Navigation 적용이 적합하지 않다.</p>
<ol>
<li>Activity 탐색을 주로 다루는 경우</li>
<li>View 기반 어플</li>
<li>Result 송/수신 기능이 필요한 경우 (ActivityResult 제공x)</li>
</ol>
<p>Navigation에서 제공하는 것들</p>
<ol>
<li>Fragment transaction을 직접 다루지 않아도 된다 (프래그먼트 트랜잭션)</li>
<li>Back/Up 동작을 기본 지원 (뒤로이동 및 위로이동)</li>
<li>화면 전환에 Type이 지정된 데이터 전달이 가능 (유형 안전성)</li>
<li>Animation/Transition 설정이 가능 (애니메이션 및 전환)</li>
<li>DeepLink 지원</li>
<li>Navigation Drawer, Bottom Navigation 쉽게 지원(UI 패턴)</li>
<li>Android Studio - Navigation Editor 지원(navGraph에 XML을 사용하는 경우)</li>
</ol>
<h1 id="navigation-구성요소">Navigation 구성요소</h1>
<p>Navigation 구성요소에는 세 가지 주요 구성요소가 존재한다</p>
<ol>
<li>NavGraph: 이동할 Composable 대상을 맵핑한다</li>
<li>NavController : 대상(앱의 화면) 간 이동을 담당</li>
<li>NavHost: NavGraph의 현재 대상을 표시하는 Container 역할을 하는 Composable이다</li>
</ol>
<h2 id="navgraph">NavGraph</h2>
<p>앱 내에 존재하는  모든 Navigation Destination(즉, 화면)을 정의하고 화면들이 어떻게 서로 연결되는지 정의한 자료구조이다</p>
<blockquote>
<p>즉, Destination 목록을 가지고 있는 저장소</p>
</blockquote>
<p>NavGraph는 ID로 불러올 수 있는 NavDestination 노드들의 컬렉션이다
NavGraph는 <code>가상의</code> 목적지 역할을 한다. 즉, NavGraph 자체가 Back Stack에 나타나지는 않지만 NavGraph에 도착하는 것이 Starting Destination(즉, 첫 화면)이 Back Stack에 추가되도록 한다</p>
<h2 id="navcontroller">NavController</h2>
<ul>
<li>navGraph에 정의된 Navigation Destination 간에 화면이동을 관리하는 중앙 관리자 이다.</li>
<li>navController는 화면 간에 화면이동, Deep Link 처리, Back Stack 관리 등을 지원하는 method를 지원한다</li>
</ul>
<p>NavControlleer는 앱의 화면이동을 NavHost로 관리한다.
앱은 직접적으로 controller를 만들기보다는 일반적으로 host로부터 직접적으로 controller를 얻거나 Navigation 클래스에 있는 utility 메소드를 사용하여 controller를 얻는다.</p>
<h2 id="navhostview-navhostfragment">NavHost(View: NavHostFragment)</h2>
<ul>
<li>현재의 Navigation Destination을 포함하고있는 UI 요소이다.</li>
<li>즉, 사용자가 앱에서 화면전환을 할때에 앱은 반드시 Navigation Host 안팎으로 Navigation Destination을 교체해준다.</li>
</ul>
<blockquote>
<p>즉, NavHost는 NavController를 가지고 있는 Container이다</p>
</blockquote>
<h2 id="동작원리-이해">동작원리 이해</h2>
<p><img src="https://velog.velcdn.com/images/ted_moon99/post/c6049db9-47cb-4458-9f76-8e258429ad33/image.png" alt=""></p>
<p>다음과 같은 화면을 이해해보자. A화면에서 B화면으로 이동하고 싶다.</p>
<ol>
<li>각 화면은 NavDestination으로 매칭된다.</li>
<li>각 화면은 NavDestination을 요청하는 NavDirections도 갖고있다.</li>
<li>NavDirections를 NavController에 요청하면 <ol>
<li>내부적으로 Stack을 처리한다.</li>
<li>다음 NavDestination을 실행 ( NavArgs: 전달되어야 하는 데이터를 저장 )</li>
</ol>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[0. Spring의 탄생]]></title>
            <link>https://velog.io/@ted_moon99/0.-Spring%EC%9D%98-%ED%83%84%EC%83%9D</link>
            <guid>https://velog.io/@ted_moon99/0.-Spring%EC%9D%98-%ED%83%84%EC%83%9D</guid>
            <pubDate>Thu, 19 Sep 2024 10:23:29 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B8%B0%EB%B3%B8%ED%8E%B8/dashboard">스프링의 핵심원리-기본편</a></p>
<p><code>인프런</code>의 김영한 강사님의 강의를 듣고있는데 강의 내용이 아주 깔끔하고 잘 정리되어 있어 매우 추천한다. 원래는 강의를 듣고 바로바로 <code>Velog</code>에 정리를 할까 싶었지만 하루에 듣는 강의 수가 생각보다 많았고 강의 내용을 이해는 했지만 블로그로 정리할만큼 정리가 안 되어있었기 때문에 한 달 정도 지나서야 정리를 시작하는 것 같다.</p>
<p><del>다른 얘기는 이정도로 하고..</del></p>
<p>오늘은 Spring의 탄생 배경에 대해서 적어볼 예정이다. 옛날 옛적.. 내가 아마 태어났을 때 즈음..? 즉, 1990년대 후반 ~ 2000년대 초반의 이야기이다.</p>
<p>그 당시에는 Java를 사용한 개발자들은 EJB(Enterprise Java Beans)라는 기술을 사용하여 개발을 하였다고 한다.</p>
<h2 id="ejb">EJB</h2>
<h3 id="ejb의-탄생">EJB의 탄생</h3>
<blockquote>
<p>Java Beans : 자바 Instance를 <code>재사용</code> 가능하도록(즉, 컴포넌트화시킬 수 있도록) 코딩방침을 정의한 것 </p>
</blockquote>
<p>=&gt; EJB : 엔터프라이즈급 어플리케이션 개발을 편하게 하기위한 스펙</p>
<p>개발을 할 때에는 많은 객체를 생성하고 접근하게 되는데 이러한 <code>객체들을 관리하는 컨테이너</code>를 만들고 필요할 때마다 컨테이너에서 객체를 받는 식으로 관리하면 효율적이다는 취지에서 탄생했다고 한다.</p>
<h3 id="ejb의-문제점">EJB의 문제점</h3>
<blockquote>
<p>배보다 배꼽이 더 컸다..</p>
</blockquote>
<p>취지는 좋았으나 문제는 Service가 구현하는 실제 비즈니스 로직보다 EJB 컨테이너를 사용하기 위한 부수적인 코드(상속 or 구현 클래스)가 너무 많다는 점이 문제였다.</p>
<p>또 다른 문제로는 EJB 컨테이너를 사용하기 위한 설정 과정이 너무도 많고 복잡했다는 점, 그리고 객체 간의 의존성을 줄이기 위해 등장한 EJB 컨테이너를 사용하였으나 비즈니스 로직에 특정 기술(EJB)이 종속된다는 문제점이 있었다. 그리고 프로젝트의 크기가 커질수록 종속성이 강화되었다.</p>
<p>객체지향언어인 Java를 들고서 SOLID 원칙도 지킬 수 없는 코드를 짜며 고통받던 개발자들은 결국 POJO(Plain Old Java Object)를 지향하기 시작하였고 순수한 자바코드로만 개발하는 개발자들도 있었다.</p>
<h2 id="spring의-시작">Spring의 시작</h2>
<p>EJB 지옥에서 고통을 받고있던 개발자 중 한 명인 Rod Johnson과 Christian Bauer(feat. Gavin King)가 각각 책을 쓰게 되는데 이 두 책을 정리하여 추후에 스프링과 하이버네이트가 된다.</p>
<h3 id="rod-johnson의-spring">Rod Johnson의 Spring</h3>
<p>2002년 로드 존슨이 책을 출간하면서 EJB의 문제점을 지적하며, EJB 없이도 충분히 고품질의 확장 가능한 애플리케이션을 개발할 수 있음을 보여주고, 30,000줄 이상의 기반 기술을 예제 코드로 정리한다. 여기에 지금의 스프링 핵심 개념과 기반 코드가 들어가 있다.</p>
<p>로드 존슨은 이 책에서 <code>BeanFactory</code>, <code>ApplicationContext</code>, <code>POJO</code>, <code>제어의 역전</code>, <code>의존관계 주입</code>을 다루게 된다.</p>
<blockquote>
<p>스프링 컨테이너는 <code>ApplicationContext</code>와 <code>BeanFactory</code>을 의미하는데 대부분은 <code>ApplicatioinContext</code>를 지칭하는 용어이다</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/ted_moon99/post/6815a62d-3ae4-40d4-8a1f-90ac6ede051c/image.png" alt=""></p>
<p>위의 그림은 EJB의 구조이다. 이 중에서 Spring은 EJB 컨테이너를 대체하면서 개발자들이 EJB의 지옥에서 벗어날 수 있게 된다. Spring은 단순함의 승리를 이끌어냈고 현재 사실상 표준 기술로 자리잡고 있다.</p>
<p>책 출간 직후 Juergen Hoeller, Yann Caroff가 로드 존슨에게 오픈소스 프로젝트를 제안하였고 스프링의 핵심코드의 상당수는 유겐 휠러가 지금도 개발중이라고 한다. </p>
<blockquote>
<p>스프링이라는 이름은 전통적인 J2EE(EJB)라는 겨울을 넘어 새로운 시작이라는 뜻으로 지었다고 한다.</p>
</blockquote>
<h3 id="christian-bauerfeat-gavin-king의-hibernate">Christian Bauer(feat. Gavin King)의 Hibernate</h3>
<p>바우어와 가빈 킹이 쓴 하이버네이트는 EJB Bean 기술을 대체하면서 JPA(Java Persistence API) 새로운 표준을 정의하게 된다.</p>
<p><code>여기서 JPA는 표준 기술로서 인터페이스(Interface)로만 구성되어 있기 때문에, 구현체는 상속을 받아서 따로 만들어줘야 한다</code></p>
<h2 id="spring-생태계">Spring 생태계</h2>
<ol>
<li>스프링 프레임워크</li>
<li>스프링 부트 : <code>스프링 프레임워크</code>를 굉장히 편리하게 사용하도록 도와주는 기술</li>
<li>스프링 데이터 : DB의 기본적인 CRUD는 비슷하기 때문에 이런 것들을 편리하게 사용하도록 만들어주는 기술</li>
<li>스프링 세션 : 세션 기능을 편리하게 사용하도록 도와주는 기술</li>
<li>스프링 시큐리티 : 보안 관련 기술</li>
<li>스프링 Rest Docs : API 문서와 테스트를 편하게 엮어서 문서화를 편하게 해주는 기술</li>
<li>스프링 배치 : 실무에서 필요한 Batch 처리에 특화된 기술 </li>
<li>스프링 클라우드 : 클라우드 기술에 특화된 기술</li>
</ol>
<h3 id="spring-framework">Spring Framework</h3>
<ul>
<li>핵심 기술 : 스프링 DI 컨테이너, AOP, 이벤트, 기타</li>
<li>웹 기술 : 스프링 MVC, 스프링 WebFlux</li>
<li>데이터 접근 기술 : 트랜잭션, JDBC, ORM 지원, XML 지원</li>
<li>기술 통합 : 캐시, 이메일, 원격접근, 스케줄링</li>
<li>테스트 : 스프링 기반 테스트 지원</li>
<li>언어 Kotlin, Groovy</li>
</ul>
<h3 id="spring-boot">Spring Boot</h3>
<p>다음에 계속..</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Spring] 명령어 정리]]></title>
            <link>https://velog.io/@ted_moon99/Spring-%EB%AA%85%EB%A0%B9%EC%96%B4-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@ted_moon99/Spring-%EB%AA%85%EB%A0%B9%EC%96%B4-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Sat, 24 Aug 2024 01:47:08 GMT</pubDate>
            <description><![CDATA[<h3 id="서버-오픈">서버 오픈</h3>
<p><code>./gradlew bootRun</code> 또는 그냥 <code>Run</code>을 눌러주면 된다.</p>
<h3 id="서버-종료">서버 종료</h3>
<h4 id="이미-사용중인-포트가-있는-경우-portinuseexception">이미 사용중인 포트가 있는 경우 (<code>PortInUseException</code>)</h4>
<p><code>lsof -n -i -p | grep 8080</code> 입력 시
<img src="https://velog.velcdn.com/images/ted_moon99/post/b4185e6c-59e1-4734-8d32-2c1735b640d2/image.png" alt=""></p>
<p>같은 문구가 나오게 되는데 이들이 의미하는 바는 다음과 같다.</p>
<table>
<thead>
<tr>
<th align="center">Command</th>
<th align="center"><strong><em>PID</em></strong></th>
<th align="center">USER</th>
<th align="center">FD</th>
<th align="center">TYPE DEVICE</th>
<th align="center">SIZE/OFF</th>
<th align="center">NODE NAME</th>
</tr>
</thead>
<tbody><tr>
<td align="center">Java</td>
<td align="center">21418</td>
<td align="center">taejinmoon</td>
<td align="center">40u</td>
<td align="center">IPv6</td>
<td align="center">0x99a817397cda5ee7</td>
<td align="center">TCP *:8080 (LISTEN)</td>
</tr>
</tbody></table>
<p>PID(<code>프로세스를 사용하는 아이디</code>)</p>
<p><code>kill -9 [PID]</code>를 입력하여 해당 프로세스를 종료시켜준다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[android.bluetooth] BluetoothSocketException]]></title>
            <link>https://velog.io/@ted_moon99/android.bluetooth-BluetoothSocketException</link>
            <guid>https://velog.io/@ted_moon99/android.bluetooth-BluetoothSocketException</guid>
            <pubDate>Tue, 20 Aug 2024 08:33:44 GMT</pubDate>
            <description><![CDATA[<h1 id="intro">Intro</h1>
<hr>
<p>BluetoothSocketException은 Bluetooth Socket과 관련이 있는 에러가 발생하였을 때 throw되는데 현재는 <code>android.bluetooth.BluetoothSocket#connect()</code>작업 중에 실패했을 때에만 throw를 날린다고 한다.</p>
<h1 id="body">Body</h1>
<hr>
<h2 id="에러의-종류">에러의 종류</h2>
<blockquote>
<p>BLUETOOTH_OFF_FAILURE : Error code during connect when Bluetooth is off and socket connection is triggered( Bluetooth가 꺼져있는데 socket연결이 시작된 경우에 발생하는 에러코드 )</p>
</blockquote>
<blockquote>
<p>L2CAP_ACL_FAILURE : Error code during connect when there is an ACL connection failure( ACL 연결이 실패한 경우에 발생하는 에러코드 )</p>
</blockquote>
<blockquote>
<p>L2CAP_CLIENT_SECURITY_FAILURE : Error code during connect when security clearance fails on the client during L2CAP connection( L2CAP 연결을 하는 동안에 고객의 보안 확인이 실패한 경우에 발생하는 에러코드 )</p>
</blockquote>
<blockquote>
<p>L2CAP_INSUFFICIENT_AUTHENTICATION : Error code during connect when authentication fails on the peer device during L2CAP connection ( L2CAP 연결을 하는 동안에 peer device에서 인증에 실패한 경우 발생하는 에러 코드)</p>
</blockquote>
<blockquote>
<p>L2CAP_INSUFFICIENT_AUTHORIZATION : Error code during connect when authorization fails on the peer device during L2cAP connection( L2CAP 연결을 하는 동안에 peer device에서 권한 확인에 실패한 경우 발생하는 에러 코드)</p>
</blockquote>
<blockquote>
<p>L2CAP_INSUFFICIENT_ENCRYPTION : Error code during for insufficient encryption from the peer device during L2CAP connection( L2CAP 연결을 하는 동안 peer device로부터 불충분한 암호화로 인해 발생하는 에러코드)</p>
</blockquote>
<blockquote>
<p>L2CAP_INSUFFICIENT_ENCTYPT_KEY_SIZE : Error code during connect indicating insufficient encryption key size on the peer device during L2CAP connection( L2CAP 연결을 하는 동안 불충분한 암호화 키 사이즈로 인해 발생하는 에러코드)</p>
</blockquote>
<blockquote>
<p>L2CAP_INVALID_PARAMETERS : Error code during connect for invalid parameters from the peer device during L2CAP connection( L2CAP 연결을 하는 동안에 peer device로부터 올바르지 않은 parameter 값을 받은 경우에 발생하는 에러코드)</p>
</blockquote>
<blockquote>
<p>L2CAP_INVALID_SOURCE_CID : Error code during connect for invalid Channel ID from the peer device during L2CAP connection( L2CAP 연결을 하는 동안 peer device로부터 올바르지 않은 채널ID를 받은 경우 발생하는 에러코드)</p>
</blockquote>
<blockquote>
<p>L2CAP_NO_PSM_AVAILABLE : Error code during connect when no PSM is available for L2CAP connection ( L2CAP 연결을 하는 동안에 PSM이 이용 불가능할 경우에 발생하는 에러코드)</p>
</blockquote>
<blockquote>
<p>L2CAP_NO_RESOURCES : Error code during connect when no resources are available for L2CAP connection( L2CAP 연결을 하는 동안에 사용가능한 resource(채널)가 없을 경우에 발생하는 에러 코드) </p>
</blockquote>
<blockquote>
<p>L2CAP_SOURCE_CID_ALREADY_ALLOCATED : Error code during connect for already allocated Channel ID from the peer device during L2CAP connection( L2CAP 연결을 하는 동안 peer device로부터 채널ID를 이미 할당받은 경우에 발생하는 에러코드 )</p>
</blockquote>
<blockquote>
<p>L2CAP_TIMEOUT : Error code during connect when L2CAP connection timeout( L2CAP 연결이 시간초과 하였을 때에 발생하는 에러코드)</p>
</blockquote>
<blockquote>
<p>L2CAP_UNACCEPTABLE_PARAMETERS : Error code during connect for unacceptable Parameters from the peer device during L2CAP connection( L2CAP 연결을 하는 동안 peer device로부터 적용할 수 없는 parameter로 연결하는 경우 발생하는 에러코드)</p>
</blockquote>
<blockquote>
<p>L2CAP_UNKNOWN : Error code during connect when socket connection fails for unknown reason during L2CAP connection( L2CAP 연결하는 동안 소켓 연결이 알 수 없는 이유로 연결 실패했을 때 발생하는 에러코드) </p>
</blockquote>
<blockquote>
<p>NULL_DEVICE : Error code during connect when null device attempts to do socket connection( 연결하는 동안 존재하지 않는 장치에 연결을 socket 연결을 시도할 때 발생하는 에러코드 )</p>
</blockquote>
<blockquote>
<p>RPC_FAILURE : Error code during connect when a Runtime RPC exception occurs( 연결하는 동안 <code>Runtime RPC Exception</code>이 발생하였을 때 발생하는 에러코드)</p>
</blockquote>
<blockquote>
<p>SOCKET_CLOSED : Error code during connect when socket is closed.( 연결하는 동안 socket이 닫혔을 때 발생하는 에러코드)</p>
</blockquote>
<blockquote>
<p>SOCKET_CONNECTION_FAILURE : Error code during connect for generic socket connection failures( 연결하는 동안 <code>generic 소켓</code> 연결이 실패하였을 때 발생하는 에러코드 )</p>
</blockquote>
<blockquote>
<p>SOCKET_MANAGER_FAILURE : Error during connect when socket manager is not available( 연결하는 동안 <code>socket manager</code>를 사용할 수 없을 때 발생하는 에러코드 ) </p>
</blockquote>
<blockquote>
<p>UNIX_FILE_SOCKET_CREATION_FAILURE : Error code during connect when the UNIX socket connection creation fails.( 연결하는 동안에 <code>UNIX 소켓</code> 연결 생성이 실패했을 때 발생하는 에러 코드 ) - API 35</p>
</blockquote>
<blockquote>
<p>UNSPECIFIED : Error code representing a failure during BluetoothSocket( <code>BluetoothSocket</code> 동안의 실패를 나타내는 에러코드 )</p>
</blockquote>
<p>별도의 표기가 없는 에러들은 API 34부터 사용가능</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Flutter] CarouselSlider 사용]]></title>
            <link>https://velog.io/@ted_moon99/Flutter-CarouselSlider-%EC%82%AC%EC%9A%A9</link>
            <guid>https://velog.io/@ted_moon99/Flutter-CarouselSlider-%EC%82%AC%EC%9A%A9</guid>
            <pubDate>Tue, 13 Aug 2024 07:37:35 GMT</pubDate>
            <description><![CDATA[<p>Flutter를 이용하여 어플을 만들던 도중 Carousel을 사용하고 싶었는데 인식이 안 되길래 먼저 Material Design3 사이트를 들어가서 확인을 해보았다.</p>
<p>그러나 아쉽게도 아직은 Flutter에서는 지원하지 않고 지원예정이라고 한다.
<img src="https://velog.velcdn.com/images/ted_moon99/post/036ee74e-b456-495b-9205-1421628fc42b/image.png" alt=""></p>
<p><strong>그럼 뭐 쓰지..?</strong> 하면서 구글링을 해보니 다트의 Flutter 패키지 자체에서 지원하는 Carousel 기능이 있었다. 위젯의 이름은 <code>CarouselSlider</code>이다.</p>
<hr>
<p><img src="https://velog.velcdn.com/images/ted_moon99/post/9a816cab-9c6b-442c-8db4-8ad50d1ce3d3/image.png" alt="">
Flutter 패키지 자체에서 지원은 하지만 CarouselSlider 역시 자동적으로 지원되지는 않는 것을 확인해볼 수 있다.</p>
<p>그래서 결국 <a href="https://pub.dev/packages/carousel_slider">Dart 공식 문서의 Flutter 패키지 사이트</a>에 들어가서 어떻게 사용해야 하는지 알아보니 </p>
<ol>
<li>Dependency를 추가한다
<img src="https://velog.velcdn.com/images/ted_moon99/post/328292a5-67a5-4d37-8ca1-6767b312df88/image.png" alt=""><ul>
<li>프로젝트 <code>directory/pubspec.yaml</code>로 들어가서 <code>dependencies</code> 칸으로 간다
<img src="https://velog.velcdn.com/images/ted_moon99/post/5328ff42-d90c-4229-8be5-e6727ff517df/image.png" alt=""></li>
<li><code>carousel_slider: ^4.2.1</code> 을 추가해준다
<img src="https://velog.velcdn.com/images/ted_moon99/post/4739fd9e-e014-4cf6-aa93-a57779c557b6/image.png" alt=""></li>
</ul>
</li>
</ol>
<ul>
<li>잘 추가했으면 <code>pub get</code>을 누르거나 터미널에 <code>flutter pub get</code>을 입력하여 플러그인을 사용할 수 있게 프로젝트로 가져와준다</li>
</ul>
<ol start="2">
<li>사용하고자 하는 프로젝트에서 import 해준다
<img src="https://velog.velcdn.com/images/ted_moon99/post/33acabff-aa9f-4862-b1f4-3fcd565a44b1/image.png" alt=""></li>
</ol>
<p>(Test) 인식이 잘 되나 확인한다(<strong>...잘 되는군</strong>)
<img src="https://velog.velcdn.com/images/ted_moon99/post/b95d53f4-3efa-4e08-86bc-c7d9a0b39728/image.png" alt=""></p>
<ol start="3">
<li>사용법
나는 이번에 CarouselSlider로 보여줄 이미지 경로를 리스트로 만들어놓고 사용하기 때문에 <code>CarouselSlider.builder</code>를 사용할 것이다. </li>
</ol>
<ul>
<li>CarouselSlider 기본구조</li>
</ul>
<table>
<thead>
<tr>
<th>필수 속성</th>
<th align="left">설명</th>
</tr>
</thead>
<tbody><tr>
<td>itemCount</td>
<td align="left">슬라이드 되는 <code>아이템의 갯수</code>를 작성</td>
</tr>
<tr>
<td>itemBuilder</td>
<td align="left">슬라이드 되는 <code>아이템 위젯의 형태</code>를 작성</td>
</tr>
<tr>
<td>options</td>
<td align="left"><code>CarouselOptions</code>를 사용하여 <code>슬라이드 옵션</code>을 작성</td>
</tr>
</tbody></table>
<ol start="4">
<li>사용 예시
<img src="https://velog.velcdn.com/images/ted_moon99/post/61ecc93d-200a-490e-9765-7ad4489b4766/image.png" alt=""></li>
</ol>
<h3 id="the-end"><strong><em>The End</em></strong></h3>
<p>여기까지 쓰고 마치려고 글을 마치려고 했으나 화면을 확인해보니 이미지 사이즈가 화면과 너비가 맞지 않아서 안 이쁘다
<img src="https://velog.velcdn.com/images/ted_moon99/post/d1f1820f-5e91-4b9b-b15c-c04295441f7f/image.png" alt=""></p>
<p>이제 이 부분을 수정해보도록 하겠다.</p>
<ol start="5">
<li>CarouselOptions의 주요 속성</li>
</ol>
<table>
<thead>
<tr>
<th>주요속성</th>
<th align="left">내용</th>
</tr>
</thead>
<tbody><tr>
<td>autoPlay</td>
<td align="left">자동으로 슬라이드 되게 한다. <code>true</code>값을 주어 사용</td>
</tr>
<tr>
<td>height</td>
<td align="left">높이를 설정</td>
</tr>
<tr>
<td>viewportFraction</td>
<td align="left">화면에 표시되는 가로 너비 비율을 설정. 값이 1.0일 경우 너비가 전체 화면을 채운다</td>
</tr>
<tr>
<td>initialPage</td>
<td align="left">처음에 보이는 인덱스 순서를 정한다. default값은 0이다</td>
</tr>
</tbody></table>
<p>CarouselSlider.builder()에서 <code>options</code>속성에 <code>viewportFraction: 1.0</code>을 입력해주면 화면 전체 너비를 채우게 된다.</p>
<p><img src="https://velog.velcdn.com/images/ted_moon99/post/5354b7e1-4937-430a-9251-1d0405d92ed3/image.png" alt=""></p>
<p>위와 같이 입력해주면 다음 화면을 얻을 수 있다.
<img src="https://velog.velcdn.com/images/ted_moon99/post/50f467e4-cd63-47ad-bdc9-77eb33d0bc0a/image.png" alt=""></p>
<p>화면이 자동으로 슬라이드 되도록 CarouselOptions의 autoPlay 속성에 true 값을 넣어주면 다음과 같은 화면을 얻을 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/ted_moon99/post/27b55df5-2c5c-4e0f-90fd-c8b04560ccaa/image.gif" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Flutter] SafeArea]]></title>
            <link>https://velog.io/@ted_moon99/Flutter-SafeArea</link>
            <guid>https://velog.io/@ted_moon99/Flutter-SafeArea</guid>
            <pubDate>Sun, 04 Aug 2024 16:23:46 GMT</pubDate>
            <description><![CDATA[<h2 id="safearea란">SafeArea란?</h2>
<blockquote>
<p>안전한 공간을 확보해주는 위젯</p>
</blockquote>
<p>SafeArea 위젯은 Flutter로 구현한 화면이 상태바(Status Bar), 카메라 영역, 노치 영역 등에 그려지지 않도록 안전한 공간을 확보해주는 위젯이다.</p>
<p>현재는 기기별로 기기의 사이즈와 디자인 등이 다양하므로 스마트폰마다 노치 영역, 상태바, 카메라 영역 등 시스템에서 사용하는 물리적인 영역이 다양하다. 이러한 영역을 고려하여 화면이 스마트폰의 안전한 영역에서만 화면이 그려지도록 모바일 어플을 만들어야 한다. 하지만 이를 충족시키기 위해 모든 기기별로 테스트하기는 현실적으로 힘들다. 이를 위하여 나온 위젯이 SafeArea 위젯이다.</p>
<p>SafeArea 위젯을 사용하면 휴대폰 기기별로 예외 처리를 해주지 않아도, <code>화면이 안전 영역에서 그려지도록 자동으로 padding을 추가하여 출력해준다.</code></p>
<p>SafeArea의 기능을 살펴보기 위해 SafeArea 위젯을 사용했을 때와 사용하지 않았을 때의 화면을 비교해보도록 하겠다.</p>
<hr>
<h2 id="safearea를-사용하지-않은-경우">SafeArea를 사용하지 않은 경우</h2>
<p>
<div class=pull-left>
<img src="https://velog.velcdn.com/images/ted_moon99/post/8f2dd0ec-17f9-41d8-a9fb-4cef02c24ef1/image.png" width="300px" height="100px" align ="right">
</div>

<pre><code class="language-dart">
import &#39;package:flutter/material.dart&#39;;

void main(){
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: &#39;SafeArea Practice&#39;,

      home: Scaffold(
        body: Text(
          &#39;SafeArea Widget을 적용하지 않은 화면&#39;,
          style: TextStyle(
            color: Colors.green,
            fontSize: 20,
            fontWeight: FontWeight.bold
          ),
        ),
      ),
    );
  }
}</code></pre>
</p>

<hr>
<p>사진에서 볼 수 있듯이 상태바(Status Bar), 카메라 영역, 노치 영역에 상관없이 화면이 출력되고 있다.</p>
<p>그러면 이제 SafeArea 위젯을 사용한 경우를 살펴보도록 하자.</p>
<h2 id="safearea-위젯을-사용한-경우">SafeArea 위젯을 사용한 경우</h2>
<p>
<div class=pull-left>
<img src="https://velog.velcdn.com/images/ted_moon99/post/b48aa719-aa2e-4dd3-9d18-ac71b55a8ea1/image.png" width="300px" height="100px" align ="right">
</div>

<pre><code class="language-dart">import &#39;package:flutter/material.dart&#39;;

void main(){
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: &#39;SafeArea Practice&#39;,

      home: SafeArea(
        // 왼쪽, 오른쪽, 위쪽, 아래쪽에 최소마진을 적용여부 지정
        // 기본값은 전부 true로 설정
        top: true,
        left: true,
        bottom: true,
        right: true,
        minimum: const EdgeInsets.all(10.0), // 최소 마진 설정
        child: Scaffold(
          body: Text(
            &#39;SafeArea를 적용한 화면&#39;,
            style: TextStyle(
              color: Colors.green,
              fontSize: 20,
              fontWeight: FontWeight.bold
            ),
          ),
        ),
      ),
    );
  }
}</code></pre>
</p>

<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Dart] 03. Operator(연산자)]]></title>
            <link>https://velog.io/@ted_moon99/Dart-03.-Operator%EC%97%B0%EC%82%B0%EC%9E%90</link>
            <guid>https://velog.io/@ted_moon99/Dart-03.-Operator%EC%97%B0%EC%82%B0%EC%9E%90</guid>
            <pubDate>Fri, 05 Jul 2024 08:04:40 GMT</pubDate>
            <description><![CDATA[<h1 id="03-operator연산자">03. Operator(연산자)</h1>
<hr>
<p>Dart의 연산자 중에서 <code>기본 연산자(+, - * , / , %)</code>, <code>논리연산자</code>, <code>비교연산자</code>들은 다른 언어와 완전히 동일하기 때문에 생략하고
Dart 언어에서 볼 수 있는 독특한 부분인 <code>null 관련 연산자</code>와 <code>타입 비교연산자</code>에 대해서만 정리하도록 한다</p>
<h2 id="null-관련-연산자">null 관련 연산자</h2>
<hr>
<ul>
<li>null : <code>아무것도 없음</code>을 뜻하는 것으로 0과는 다른 의미이다 </li>
<li>Dart 언어에서는 변수 타입이 null 값을 가지는지 여부를 직접 지정해주어야 한다</li>
<li>기본적으로 변수는 null 값이 지정될 수 없다.</li>
<li>따라서, 타입 뒤에 <code>?</code>를 추가해주어야 null 값이 저장될 수 있다.(null 허용)</li>
</ul>
<pre><code class="language-dart">void main(){
  double? number1 = null;
  double nuber2 = null; // 오류 발생 
  // A value of type &#39;Null&#39; can&#39;t be assigned to a variable of type &#39;double&#39;.
}</code></pre>
<ul>
<li>또한 <code>현재 변수의 값이 null일 때에만 값을 대입</code>하는 <code>??=</code> 연산자가 있다.</li>
</ul>
<pre><code class="language-dart">void main(){
  double? number; // 자동으로 초기값은 Null
  print(number); // 출력 결과 : null

  number ??= 3; // number의 값이 null이면 3을 대입해라!
  print(number); // 3.0

  number ??= 4; // number의 값이 null이면 4를 대입해라!
  print(number); // 3.0
}</code></pre>
<h2 id="타입-비교-연산자">타입 비교 연산자</h2>
<hr>
<ul>
<li><code>is</code> 키워드를 사용하여 변수의 타입을 비교할 수 있다. </li>
<li>비교 후 반환되는 값은 bool 타입이다</li>
<li><code>is!</code>를 사용하면 반대를 의미하여 같지 않은지에 대한 결과를 리턴한다</li>
</ul>
<pre><code class="language-dart">void main(){
  int number = 1;

  print(number is int); // 출력 결과 : true
  print(number is String); // 출력 결과 : false
  print(number is! int); // 출력 결과 : false
  print(number is! String); // 출력 결과 : true
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Dart] 01. Variable(변수) & Constant(상수)]]></title>
            <link>https://velog.io/@ted_moon99/Dart-Variable%EB%B3%80%EC%88%98-Constant%EC%83%81%EC%88%98-laex12fj</link>
            <guid>https://velog.io/@ted_moon99/Dart-Variable%EB%B3%80%EC%88%98-Constant%EC%83%81%EC%88%98-laex12fj</guid>
            <pubDate>Fri, 05 Jul 2024 07:38:10 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-dart">import &#39;dart:ffi&#39;;

void main(){
  print(&#39;Hello World&#39;);
  print(&#39;안녕하세요&#39;);

  // 변수 선언
  var name = &quot;찐공 테스트&quot;;
  // A value of type &#39;int&#39; can&#39;t be assigned to a variable of type &#39;String&#39;.
  // name = 1; -&gt; 오류 발생 : 처음 할당된 값과 다른 타입의 값을 넣으면 오류가 발생하는 var
  print(name);

  name = &#39;홍길동&#39;;
  print(name);

  // dynamic 키워드 사용하기
  dynamic name2 = &quot;홍길동&quot;; // 처음 할당된 값과 다른 타입의 값을 넣어도 오류가 발생하지 않는 dynamic
  name2 = 1;
  print(name2);

  // 상수 선언
  /*
  final, const는 변수를 상수로 만드는 기능을 제공한다
    const : 코드를 실행하지 않은 상태에서 값이 확정됨
    final : 코드를 실행하는 순간에 값이 확정됨
   */

  final String name3 = &quot;테스트1&quot;;
  // name3 = 1; 오류 발생( final -&gt; 상수 )
  // The final variable &#39;name3&#39; can only be set once.

  const String name4 = &quot;테스트2&quot;;
  // name4 = 1; 오류 발생( const -&gt; 상수 )
  // Constant variables can&#39;t be assigned a value.

  // final : 런타임(RunTime) 시에 값이 결정된다
  final DateTime now = DateTime.now();
  print(now);

  // const : 빌드타임(Build-Time) 시에 값이 결정된다
  // const DateTime now2 = DateTime.now(); -&gt; 오류 발생 : 런타임 시에만 반환되는 값은 const로 지정이 불가능하다
  // The constructor being called isn&#39;t a const constructor. (Documentation)  Try removing &#39;const&#39; from the constructor invocation.

}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Dart] 02. Collection(컬랙션)]]></title>
            <link>https://velog.io/@ted_moon99/Dart-Collection%EC%BB%AC%EB%9E%99%EC%85%98</link>
            <guid>https://velog.io/@ted_moon99/Dart-Collection%EC%BB%AC%EB%9E%99%EC%85%98</guid>
            <pubDate>Fri, 05 Jul 2024 07:36:16 GMT</pubDate>
            <description><![CDATA[<h1 id="02-collection컬랙션">02. Collection(컬랙션)</h1>
<hr>
<p>컬랙션은 서로의 타입으로 자유롭게 형변환이 가능하다</p>
<h2 id="list">List</h2>
<hr>
<ul>
<li>List에 값을 대입할 때 대괄호[]를 이용한다는 점에 주의</li>
</ul>
<h3 id="데이터-추가-add-">데이터 추가( add() )</h3>
<ul>
<li>add() : 매개변수에 값을 입력하여 <code>리스트의 끝</code>에 데이터를 추가한다</li>
</ul>
<h3 id="데이터-필터링-where-">데이터 필터링( where() )</h3>
<ul>
<li>where() : 리스트를 <code>순회</code>하면서 <code>특정 조건에 맞는 값만 필터링</code>한다</li>
<li>리스트 순회가 끝나면 필터링된 값들로만 구성된 <code>이터러블(iterable)타입이 반환</code>된다</li>
<li>이터러블은 출력 시 ()를 사용하는 부분을 확인할 수 있다</li>
<li>반환된 이터러블은 toList()를 사용하면 다시 리스트로 변환할 수 있다</li>
</ul>
<h3 id="데이터-변경-map-">데이터 변경( map() )</h3>
<ul>
<li>map() : 리스트를 <code>순회</code>하면서 <code>요소 값의 변경이 가능</code>하다 </li>
<li>매개변수에는 함수(익명함수, 람다함수 등)를 입력해야 하며, 입력된 함수는 리스트 값을 하나씩 매개변수로 입력받는다</li>
<li>반환하는 값은 <code>변경된 값으로 구성된 iterable이 반환</code>된다</li>
</ul>
<p><img src="https://velog.velcdn.com/images/ted_moon99/post/2da6e337-e0c4-4445-b86b-e3131f3a70d4/image.png" alt=""></p>
<h3 id="데이터-누적합--reduce-">데이터 누적합 ( reduce() )</h3>
<ul>
<li>reduce() : 리스트를 <code>순회</code>하면서 값을 누적하여 <code>누적한 값</code>을 <code>List 멤버의 타입과 동일한 타입으로 반환</code>한다<blockquote>
<p><code>리스트를 구성하는 값들의 타입</code>과 <code>반환되는 리스트를 구성할 값들의 타입</code>이 완전히 같아야 한다</p>
</blockquote>
</li>
<li>형식 : map(value, element)</li>
<li>첫 번째 매개변수인 value에는 첫 번째 요소 값이 들어가고 두 번째 매개변수인 element에는 두 번째 요소 값이 들어간다</li>
<li>이후에는 value에는 앞서 만들어진 값이 들어가고 element에는 세번째 요소값이 들어간다 </li>
</ul>
<p><img src="https://velog.velcdn.com/images/ted_moon99/post/b288192f-c604-4635-a751-660caee3d290/image.png" alt=""></p>
<h3 id="데이터-누적합2--fold-">데이터 누적합2 ( fold() )</h3>
<ul>
<li>fold() : reduce()와 기본적으로 실행되는 방법은 동일하다<blockquote>
<p>fold는 어떠한 타입이든 반환할 수 있다.</p>
</blockquote>
</li>
<li>첫 번째 매개변수에 <code>시작할 값</code>을 지정하고, 두 번째 매개변수에 <code>함수</code>를 지정한다</li>
<li>단 시작할 값은 value에 첫 번째 인자로 들어가게 된다. 그리고 element는 첫 번째 요소부터 들어가게 된다</li>
</ul>
<h2 id="map">Map</h2>
<hr>
<ul>
<li>다른 언어에서 사용되는 Map의 개념과 동일</li>
<li>키(Key), 값(value)으로 자료를 저장하는 자료구조</li>
<li>Map.keys : 전체 키(key)를 반환</li>
<li>Map.values : 전체 값(values)을 반환</li>
</ul>
<h2 id="set">Set</h2>
<hr>
<ul>
<li>집합. <code>데이터의 중복이 불가능</code>. <code>유일한 값만 저장</code></li>
<li>Set을 출력할 시 { }로 감싸 출력되는 것을 확인할 수 있다</li>
<li>어떤 원소가 Set안에 들어있는지를 확인하려면 <code>contain()</code>을 이용한다</li>
<li>Set을 List로 변경하고 싶다면 <code>toList()</code>을 사용한다</li>
<li>List를 Set으로 변경하고 싶다면 <code>from()</code>을 사용한다</li>
</ul>
<h2 id="enum">Enum</h2>
<hr>
<ul>
<li>열거형(enum)은 한 변수가 가질 수 있는 값을 미리 설정해 두고 그 값만 가질 수 있도록 한다</li>
<li>변수가 가져야 하는 값이 제한적일 때 자주 사용한다</li>
<li><code>열거형 이름.values</code>는 열거형에 정의된 모든 값들을 리스트로 반환해준다 </li>
</ul>
<h1 id="전체-코드">전체 코드</h1>
<hr>
<pre><code class="language-dart">void main(){
  // Collection(컬랙션)
  // 컬렉션은 서로의 타입으로 자유롭게 형변환이 가능하다


  // List

  // List.add(value)
  List&lt;String&gt; foodName = [&#39;피자&#39;, &#39;당근&#39;, &#39;토끼&#39;, &#39;네이버&#39;, &#39;쿠팡&#39;];
  foodName.add(&#39;토스&#39;);
  print(foodName); // [피자, 당근, 토끼, 네이버, 쿠팡, 토스]


  // List.where((element) =&gt; 조건식 )
  List&lt;String&gt; foodList = [&#39;피자&#39;, &#39;당근&#39;, &#39;토끼&#39;];
    // final : Runtime시에 값이 결정됨
  final newList1 = foodList.where((name) =&gt; name == &quot;피자&quot; || name  == &quot;콜라&quot;);

  // iterable로 반환
  print(newList1); // 출력결과 : (피자)
  // toList : iterable을 List로 반환
  print(newList1.toList()); // 출력결과 : [피자]

  // List.map( (element) =&gt; 변형식 )
  List&lt;String&gt; furnitureList = [&#39;소파&#39;, &#39;텔레비전&#39;, &#39;침대&#39;];
  final furniture = furnitureList.map((element) =&gt; &quot;우리집에는 ${element}가 있어요~!&quot;);
  print(furnitureList); // 출력 결과 : [소파, 텔레비전, 침대] =&gt; 원래의 리스트에는 영향을 미치지 않음
  print(furniture); // 출력 결과 : (우리집에는 소파가 있어요~!, 우리집에는 텔레비전가 있어요~!, 우리집에는 침대가 있어요~!)  =&gt; iterable 반환
  print(furniture.toList()); // 출력 결과 : [우리집에는 소파가 있어요~!, 우리집에는 텔레비전가 있어요~!, 우리집에는 침대가 있어요~!] =&gt; List로 변환

  // List.reduce( (value, element) =&gt; 수행식 )
  List&lt;String&gt; foodList2 = [&#39;pizza&#39;, &#39;hamburger&#39;, &#39;chicked&#39;, &#39;coke&#39;];
  final allList1 = foodList2.reduce((value, element) =&gt; &quot;$value, $element&quot;);
  print(allList1); // &lt;String&gt; : pizza, hamburger, chicked, coke

  // List.fold( startElement, (value, elemenet) =&gt; 수행식 )
  List&lt;String&gt; foodList3 = [&#39;피자&#39;, &#39;햄버거&#39;, &#39;치킨&#39;, &#39;콜라&#39;];
  final allList2 = foodList3.fold(1, (value, element) =&gt; value + element.length);
  print(allList2);

  // Map
  Map&lt;String, String&gt; firstName = {
    &quot;Kim&quot; : &quot;김씨&quot;,
    &quot;Park&quot; : &quot;박씨&quot;,
    &quot;Kwon&quot; : &quot;권씨&quot;,
  };

  print(firstName[&#39;김씨&#39;]); // null =&gt; Key값으로 조회를 해야함
  print(firstName[&#39;Park&#39;]); // 박씨

  // 전체 키(key)를 반환
  print(firstName.keys); // (Kim, Park, Kwon)
  // 전체 값(value)을 반환
  print(firstName.values); // (김씨, 박씨, 권씨)


  // Set
  Set&lt;String&gt; colors = {
    &#39;red&#39;, &#39;blue&#39;, &#39;orange&#39;, &#39;black&#39;, &#39;yellow&#39;
  };
  print(colors); // {red, blue, orange, black, yellow}
  print(colors.contains(&#39;red&#39;)); // &lt;Boolean&gt; true
  print(colors.toList()); // [red, blue, orange, black, yellow]

  List&lt;String&gt; months = [&#39;Jan&#39;, &#39;Apr&#39;, &#39;Jul&#39;, &#39;Sep&#39;, &#39;Dec&#39;];
  // List -&gt; Set으로 반환
  print(Set.from(months)); // {Jan, Apr, Jul, Sep, Dec}

  // Enum
  Answers answer = Answers.Yes;
  print(answer); // Answers.Yes
  print(answer.name); // No
  print(Answers.values); // [Answers.Yes, Answers.No, Answers.None]
}

enum Answers {
  Yes,
  No,
  None
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[앱 구성 파일 분석]]></title>
            <link>https://velog.io/@ted_moon99/%EC%95%B1-%EA%B5%AC%EC%84%B1-%ED%8C%8C%EC%9D%BC-%EB%B6%84%EC%84%9D</link>
            <guid>https://velog.io/@ted_moon99/%EC%95%B1-%EA%B5%AC%EC%84%B1-%ED%8C%8C%EC%9D%BC-%EB%B6%84%EC%84%9D</guid>
            <pubDate>Sat, 15 Jun 2024 11:19:10 GMT</pubDate>
            <description><![CDATA[<h2 id="1-프로젝트의-폴더-구성-알아보기">1. 프로젝트의 폴더 구성 알아보기</h2>
<p>Android앱 프로젝트를 만들면 많은 폴더와 파일이 생성된다. 하지만 대부분은 빌드 도구와 관련된 것이며 개발자가 관심을 두어야 할 파일은 프로젝트 폴더에서 <code>[Module명 -&gt; src -&gt; main]</code>안에 존재한다. </p>
<p>참고로 안드로이드 스튜디오의 프로젝트 탐색 창은 모든 폴더와 파일을 보여주지 않는다. 개발자가 분석하거나 작성해야 하는 폴더와 파일만 보여준다.
<img src="https://velog.velcdn.com/images/ted_moon99/post/074130df-3852-4115-8238-e2517f5d2ec7/image.jpeg" alt=""></p>
<p>프로젝트를 만들면 app이라는 Module이 자동으로 생성된다. Module 하나가 app 하나이며 프로젝트는 여러 모듈을 묶어서 관리하는 개념이다. </p>
<p>하나의 프로젝트에는 자동으로 만들어지는 app Module 이외에 여러 모듀을 추가할 수 있다. Module은 App 단위이므로 새로운 Module을 추가한다는 것은 새로운 App을 개발하는 것과 같다.</p>
<h2 id="2-모듈의-폴더-구성-알아보기">2. 모듈의 폴더 구성 알아보기</h2>
<table>
<thead>
<tr>
<th align="center">이름</th>
<th align="center">설명</th>
</tr>
</thead>
<tbody><tr>
<td align="center">build.gradle</td>
<td align="center">빌드 설정 파일</td>
</tr>
<tr>
<td align="center">AndroidManifest.xml</td>
<td align="center">앱의 메인 환경 파일</td>
</tr>
<tr>
<td align="center">res</td>
<td align="center">리소스 폴더</td>
</tr>
<tr>
<td align="center">activity_main.xml</td>
<td align="center">레이아웃 XML 파일</td>
</tr>
<tr>
<td align="center">MainActivity.kt</td>
<td align="center">메인 액티비티 파일</td>
</tr>
<tr>
<td align="center">### 2.1 그래들 빌드 설정 파일(build.gradle)</td>
<td align="center"></td>
</tr>
<tr>
<td align="center">그래들(gradle)은 안드로이드 앱의 빌드 도구이다.</td>
<td align="center"></td>
</tr>
</tbody></table>
<p>그래들의 설정 파일이 바로 <code>build.gradle</code>이다.</p>
<p>build.gradle파일은 안드로이드 스튜디오의 탐색 창에서 코끼리 모양의 아이콘이 있는 <code>Gradle Scripts</code>에서 찾을 수 있다.
<img src="https://velog.velcdn.com/images/ted_moon99/post/4242700d-e3be-4b0c-a8fa-5651b06577a5/image.png" alt=""></p>
<p>탐색 창을 보면 build.gradle 파일은 2개가 존재한다. 하나는 프로젝트 수준의 그래들 파일이고, 다른 하나는 모듈 수준의 그래들 파일이다. 모듈은 앱을 의미하므로 대부분의 빌드 설정은 모듈 수준의 그래들 파일에 작성을 하게 된다.</p>
<h4 id="프로젝트-수준의-그래들-파일buildgradlektsproject-component_practice">프로젝트 수준의 그래들 파일<code>build.gradle.kts(Project: Component_Practice)</code></h4>
<h4 id="모듈-수준의-그래들-파일buildgradlektsmodule-app">모듈 수준의 그래들 파일<code>build.gradle.kts(Module: app)</code></h4>
<ul>
<li>플러그인 선언</li>
</ul>
<pre><code class="language-kotlin">plugins {
    alias(libs.plugins.android.application)
    alias(libs.plugins.jetbrains.kotlin.android)
}</code></pre>
<p>위의 코드는 플러그인을 선언한 것이다. 기본적으로 <code>com.andoid.application</code>과 <code>kotlin-android</code> 플러그인이 선언되어 있으며, 필요에 따라 추가한다.</p>
<ul>
<li>컴파일 버전 설정</li>
</ul>
<p><img src="https://velog.velcdn.com/images/ted_moon99/post/3555209f-3e07-4c3b-9f96-1e95ee490005/image.png" alt=""></p>
<p>이 코드는 앱을 컴파일하거나 빌드할 때 적용할 버전을 설정한다. compileSdk 34로 지정되어 있으면 안드로이드 SDK34 버전을 적용해서 컴파일하라는 의미이다.</p>
<ul>
<li>앱의 식별자 설정</li>
</ul>
<p><img src="https://velog.velcdn.com/images/ted_moon99/post/72084eab-8e4e-415d-ad82-4cf110fba53a/image.png" alt=""></p>
<p><code>applicationId</code>는 앱의 식별자를 설정ㅇ한다. 이곳에 지정한 문자열은 앱의 식별자가 되므로 고유한 문자열로 지정해야 한다. 만약 구글의 플레이 스토어에 등록된 어떤 앱이 똑같은 식별자를 이미 사용하고 있다면 이 앱은 플레이 스토어에 등록될 수 없다. 또한, 스마트폰에 식별자가 똑같은 앱이 설치되어 있다면 이 앱은 설치되지 않는다.</p>
<ul>
<li>SDK 버전 설정</li>
</ul>
<p><img src="https://velog.velcdn.com/images/ted_moon99/post/3f45e9c1-eb55-4df4-a7c4-dc29acef274e/image.png" alt=""></p>
<p>targetSdk는 개발할 때 적용되는 SDK 버전이다. 34로 지정하면 34 버전의 SDK 로 개발한다는 의미이다. </p>
<p>minSdk는 이 앱을 설치할 수 있는 기기의 최소 SDK 버전을 의미하는데, 최대한 많은 사용자를 수용하기 위해서는 최소 SDK는 낮을수록 좋다.</p>
<p><del>물론 최대한 많은 사용자를 모집하기 위해서 minSdk를 1로 설정할 수도 있지만 하위 버전에서도 오류가 발생하지 않도록 개발하는 하위 호환성이 좋아야 하는데 이는 그렇게 쉬운 작업은 아니다</del></p>
<ul>
<li>앱의 버전 설정</li>
</ul>
<p><img src="https://velog.velcdn.com/images/ted_moon99/post/ccef9da1-080b-4e82-a995-e77e5deb7588/image.png" alt=""></p>
<p>앱의 버전을 설정한다. 초기값은 1이지만 앱이 사용자의 스마트폰에 설치되어 이용되다가 업데이트될 때 이 버전을 올려 다시 배포한다</p>
<ul>
<li>컴파일 옵션</li>
</ul>
<p><img src="https://velog.velcdn.com/images/ted_moon99/post/7ea2ab88-f6b1-4b69-a5c2-c86ccba6c06c/image.png" alt=""></p>
<p>개발 언어의 버전을 설정한다. 참고로 자바 버전 선언을 생략하면 기본으로 1.6이 적용된다.</p>
<ul>
<li>라이브러리 설정</li>
</ul>
<p><img src="https://velog.velcdn.com/images/ted_moon99/post/7aa16cbe-28d2-40bc-b5c4-cbf32fd14bb0/image.png" alt=""></p>
<blockquote>
<p>targetSdk에 명시한 안드로이드 SDK는 기본으로 적용되지만 그 외에 <code>개발자가 추가하는 오픈소스 라이브러리</code>나 <code>구글의 adnroidx 라이브러리</code> 등 <code>SDK 라이브러리가 아닌 것</code>들은 모두 dependencies에 선언해야 한다.</p>
</blockquote>
<blockquote>
<p>실제 개발할 때에는 dependencies에 많은 라이브러리를 선언한다. 즉, 안드로이드 앱은 보통 안드로이드 SDK 라이브러리만으로 개발하지는 않는다.</p>
</blockquote>
<h3 id="22-메인-환경-파일매니패스트-파일">2.2 메인 환경 파일(매니패스트 파일)</h3>
<p>AndroidManifest.xml은 안드로이드 앱의 메인 환경 파일이다. 안드로이드 시스템은 이 파일에 설정한 대로 사용자의 폰에서 앱을 실행한다. 즉, 매니페스트 파일은 개발부터 실행까지 중요한 역할을 한다.</p>
<ul>
<li>네임스페이스 선언</li>
</ul>
<p><img src="https://velog.velcdn.com/images/ted_moon99/post/2ee0b988-b61b-4197-9f34-1ab177d66bbc/image.png" alt=""></p>
<blockquote>
<p>manifest는 매니페스트 파일의 루트 태그이다. xmlns는 XML의 네임스페이스 선언이며 URL이 <code>http://schemas.android.com/apk/res/android</code>로 선언되었다면 안드로이드 표준 네임스페이스이다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/ted_moon99/post/199e022d-beb0-4085-b8cd-163a5b5c4774/image.png" alt=""></p>
<blockquote>
<p>application 태그는 <code>앱 전체를 대상으로 하는 설정</code>이다.</p>
</blockquote>
<blockquote>
<p>application 태그에는 앱의 아이콘을 설정하는 icon 속성이 있는데, 이곳에 지정한 이미지가 앱을 설치한 사용자의 폰에 보이는 실행 아이콘이다. <code>res/mipmap/ic_launcher.png</code> 파일을 의미한다. </p>
</blockquote>
<blockquote>
<p>label 속성에는 앱의 이름을 등록한다. <code>res/values/strings.xml</code> 파일에 <code>app_name</code>으로 등록된 문자열 리소스를 가리킨다.</p>
</blockquote>
<blockquote>
<p>theme 설정은 앱에 적용하는 테마를 설정하는 것으로 <code>res/value/themes.xml</code> 파일에 존재하는 <code>Theme.ComponentPractice</code> 이름으로 선언한 테마를 적용하겠다는 의미이다.</p>
</blockquote>
<ul>
<li>Component 선언</li>
</ul>
<p><a href="https://velog.io/@ted_moon99/Android-%EC%95%B1-%EA%B0%9C%EB%B0%9C%EC%9D%98-%ED%8A%B9%EC%A7%95">Android 앱 개발의 특징</a>에서 보는 것처럼 안드로이드 Component는 Android System에서 생명주기를 관리한다. </p>
<p>Android System은 Manifest 파일에 있는 대로 앱을 실행한다. 따라서 Component는 Manifest 파일에 등록을 해야 시스템이 인지할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/ted_moon99/post/504e07d9-2bdd-4bc1-9709-d1b458231e76/image.png" alt=""></p>
<p>Component 하나당 태그 하나로 등록한다</p>
<table>
<thead>
<tr>
<th align="center">Component</th>
<th align="center">태그</th>
</tr>
</thead>
<tbody><tr>
<td align="center">액티비티</td>
<td align="center">&lt;activity&gt;</td>
</tr>
<tr>
<td align="center">서비스</td>
<td align="center">&lt;service&gt;</td>
</tr>
<tr>
<td align="center">브로드캐스트 리시버</td>
<td align="center">&lt;receiver&gt;</td>
</tr>
<tr>
<td align="center">콘텐츠 프로바이더</td>
<td align="center">&lt;provider&gt;</td>
</tr>
</tbody></table>
<blockquote>
<p>액티비티를 등록할 때 필수 속성은 name이다. name 속성에는 클래스 이름을 등록한다.</p>
</blockquote>
<p>예를 들어 name=&quot;.MainActivity&quot;라고 했다면 MainActivity 클래스를 액티비티로 등록하겠다는 의미이다.</p>
<blockquote>
<p>클래스 이름 앞에 있는 점(.)은 해당 클래스가 &lt;manifest&gt; 태그에 등록한 package 경로에 있다는 의미이다.</p>
</blockquote>
<blockquote>
<p>&lt;intent-filter&gt; 태그는 생략할 수 있다. <del>이에 대한 내용은 추후에 작성하도록 하겠다.</del></p>
</blockquote>
<h3 id="23-리소스-폴더">2.3 리소스 폴더</h3>
<p>Module이 만들어지면 res 폴더 아래에 다음과 같은 폴더가 기본적으로 생성된다.
<img src="https://velog.velcdn.com/images/ted_moon99/post/4811314f-5ade-4522-b9ac-1f4663233c5e/image.png" alt=""></p>
<p>res 폴더 아래에 리소스를 만들면 이 리소르를 식별하기 위한 <code>상수 변수(drawable, layout의 경우 int형 변수)</code>가 자동으로 R.java 파일에 리소스가 등록되며 코드에서는 이 상수 변수로 리소스를 이용한다. R.java는 개발자가 만드는 파일이 아니고 res 폴더에 있는 리소스를 보고 자동으로 만들어진다.<del>이전에는 R.java 파일을 확인할 수 있었으나 업데이트 되면서 볼 수 없게 되었다고 한다. for 개발자의 직접 접근 차단</del></p>
<p>어떻게 사용하는가?</p>
<table>
<thead>
<tr>
<th align="center">리소스</th>
<th align="center">코드</th>
</tr>
</thead>
<tbody><tr>
<td align="center">drawable</td>
<td align="center">R.drawable.블라블라</td>
</tr>
<tr>
<td align="center">layout</td>
<td align="center">R.layout.블라블라</td>
</tr>
<tr>
<td align="center">mipmap</td>
<td align="center"><del>R.mipmap.블라블라</del></td>
</tr>
<tr>
<td align="center">values</td>
<td align="center"><del>R.values.</del></td>
</tr>
</tbody></table>
<p>안드로이드 리소스 파일이 R.java 파일에 상수 변수로 등록되어 이용된다.</p>
<ol>
<li>각 리소스 폴더에 다시 하위 폴더를 정의할 수 없다.
(지정된 폴더명을 사용해라 ex. drawble, layout, values, mipmap 등등)</li>
<li>리소스 파일명은 자바의 이름 규칙을 위배할 수 없다.
(자바 이름은 숫자로 시작할 수 없다)</li>
<li>리소스 파일명에는 알파벳 대문자를 이용할 수 없다.
(그래서 두 단어를 연결할 때에는 add_user.xml처럼 밑줄(_)을 이용한다)</li>
</ol>
<h3 id="24-레이아웃-xml-파일">2.4 레이아웃 xml 파일</h3>
<p>화면을 구성하는 레이아웃 XML 파일이다. 사용자에게 보여지는 화면을 만들 때 사용한다.</p>
<pre><code class="language-xml">[activity_main.xml]

&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;androidx.constraintlayout.widget.ConstraintLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    xmlns:app=&quot;http://schemas.android.com/apk/res-auto&quot;
    xmlns:tools=&quot;http://schemas.android.com/tools&quot;
    android:id=&quot;@+id/main&quot;
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;match_parent&quot;
    tools:context=&quot;.MainActivity&quot;&gt;

    &lt;TextView
        android:layout_width=&quot;wrap_content&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:text=&quot;Hello World!&quot;
        app:layout_constraintBottom_toBottomOf=&quot;parent&quot;
        app:layout_constraintEnd_toEndOf=&quot;parent&quot;
        app:layout_constraintStart_toStartOf=&quot;parent&quot;
        app:layout_constraintTop_toTopOf=&quot;parent&quot; /&gt;

&lt;/androidx.constraintlayout.widget.ConstraintLayout&gt;</code></pre>
<p>Android Studio에서는 3가지 모드를 지원한다.</p>
<ol>
<li>코드만 보기</li>
</ol>
<p><img src="https://velog.velcdn.com/images/ted_moon99/post/4247a2a6-2aa0-49f7-aa83-062261a135f1/image.png" alt=""></p>
<ol start="2">
<li>코드와 화면 보기</li>
</ol>
<p><img src="https://velog.velcdn.com/images/ted_moon99/post/440ddfc6-3ba2-4c50-a479-0fe3af30653d/image.png" alt=""></p>
<ol start="3">
<li>화면만 보기</li>
</ol>
<p><img src="https://velog.velcdn.com/images/ted_moon99/post/17fb3e88-318f-43ab-a0ca-7c36ea175ab7/image.png" alt=""></p>
<p>개인적으로 초보자에게는 화면만 보기(3번) 모드로 시작하는 것을 추천한다. 왼쪽에 보이는 palette로 요소들을 끌어다가 넣는 방식으로 화면을 만들 수 있다. </p>
<p>어느 정도 익숙해졌다 싶으면 코드와 화면 보기(2번) 모드로 작업하는 것을 추천한다. 코드를 작성하면서 화면이 어떻게 변화하는지 관찰할 수 있다.</p>
<p>리팩토링 작업을 할 때에는 코드만 보기(1번) 모드로 작업하는 것을 추천한다.</p>
<h3 id="25-메인-액티비티-파일">2.5 메인 액티비티 파일</h3>
<pre><code class="language-kotlin">[MainActivity.kt]

package com.example.componentpractice

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

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContentView(R.layout.activity_main)
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets -&gt;
            val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
            insets
        }
    }
}</code></pre>
<p>MainActivity 클래스는 AppCompatActivity()를 상속받고 있는데 AppCompatActivity는 Activity의 하위 클래스이다. 즉, 결국에는 Activity를 상속받고 있으므로 MainActivity는 Activity Component Class이다. 즉, 이 클래스는 화면 출력을 목적으로 하는 액티비티 클래스이다.</p>
<p><a href="https://velog.io/@ted_moon99/Android-%EC%95%B1-%EA%B0%9C%EB%B0%9C%EC%9D%98-%ED%8A%B9%EC%A7%95">참고- 1.4문단</a></p>
<p>MainActivity 클래스가 실행되면 onCreate() 함수가 자동으로 호출되면 onCreate() 함수 안의 구문을 실행한다. 여기서 setContentView() 함수는 매개변수에 지정한 내용을 액티비티 화면에 출력한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Android 앱 개발의 특징]]></title>
            <link>https://velog.io/@ted_moon99/Android-%EC%95%B1-%EA%B0%9C%EB%B0%9C%EC%9D%98-%ED%8A%B9%EC%A7%95</link>
            <guid>https://velog.io/@ted_moon99/Android-%EC%95%B1-%EA%B0%9C%EB%B0%9C%EC%9D%98-%ED%8A%B9%EC%A7%95</guid>
            <pubDate>Sat, 15 Jun 2024 09:07:41 GMT</pubDate>
            <description><![CDATA[<h2 id="1-component를-기반으로-한-개발">1. Component를 기반으로 한 개발</h2>
<ul>
<li>안드로이드 앱 개발의 핵심은 <code>component</code>이다
안드로이드 앱 개발의 구조를 이해하려면 component가 무엇이고 어떻게 동작하는지 반드시 이해해야 한다</li>
</ul>
<h3 id="11-component란-무엇인가">1.1 Component란 무엇인가?</h3>
<p>Component란 Android 앱뿐만 아니라 여러 Application을 개발할 때 사용하는 개념이다. Component를 정의하자면 Application의 구성요소이다. 즉, 하나의 Application은 여러 개의 Component로 구성되어 있다. Android에서는 클래스로 컴포넌트를 개발한다. 즉, 하나의 클래스가 하나의 Component가 된다</p>
<ul>
<li>Application의 구성요소</li>
<li>즉, Application을 구성하는 단위</li>
<li>하나의 Application은 여러 개의 Component로 구성</li>
<li>Android에서는 클래스로 Component를 개발한다.</li>
<li>하나의 클래스가 하나의 Component</li>
</ul>
<p>위의 내용을 정리해보면 다음과 같다.</p>
<h4 id="111-한-개의-application은-여러-개의-component로-구성">1.1.1 한 개의 Application은 여러 개의 Component로 구성</h4>
<p><img src="https://velog.velcdn.com/images/ted_moon99/post/2987c196-7cbf-47ca-9feb-2a8694f8896b/image.png" alt=""></p>
<h4 id="112-한-개의-클래스가-한-개의-component">1.1.2 한 개의 클래스가 한 개의 Component</h4>
<p><img src="https://velog.velcdn.com/images/ted_moon99/post/ef14cc72-49ab-4562-9f87-acd8e9812521/image.png" alt=""></p>
<h4 id="113한-개의-application은-여러-개의-class로-구성">1.1.3한 개의 Application은 여러 개의 Class로 구성</h4>
<p><img src="https://velog.velcdn.com/images/ted_moon99/post/95cdfdf3-f46f-40ea-ad36-e3619432007c/image.png" alt=""></p>
<h3 id="12-그렇다면-android-앱을-구성하는-클래스는-모두-component인가요">1.2 그렇다면 Android 앱을 구성하는 클래스는 모두 Component인가요?</h3>
<p>위에서 안드로이드에서는 클래스로 컴포넌트를 개발한다고 했지만 Application을 구성하는 모든 클래스가 Componet라는 이야기는 아니다. </p>
<p>Application은 여러개의 클래스로 구성되는데 클래스는 크게 Component 클래스와 일반 클래스로 구분한다.
<img src="https://velog.velcdn.com/images/ted_moon99/post/df820e73-0b84-4370-93ff-04e92344aed8/image.png" alt="">
Component 클래스와 일반 클래스 둘 다 개발자가 만드는 클래스이다. 하지만 런타임(Runtime) 때 <code>생명주기(lifecycle)을 누가 관리하는가</code>에 따라 차이가 존재한다.</p>
<h4 id="121-일반-클래스">1.2.1 일반 클래스</h4>
<p>: 런타입(Runtime) 때 클래스의 객체 생성 ~ 소멸까지의 생명주기 관리를 <code>개발자 코드</code>에서 한다. 즉, Android 앱의 구성요소인 Component가 아닌 개발자가 <code>임의의 목적</code>으로 만든 클래스이다.</p>
<h4 id="122-component-클래스">1.2.2 Component 클래스</h4>
<p>: 런타임(Runtime) 때 클래스의 객체 생성 ~ 소멸까지의의 생명주기 관리를 <code>안드로이드 시스템</code>에서 관리한다.</p>
<h3 id="13-android-component는-4종류이다">1.3 Android Component는 4종류이다.</h3>
<p>Component는 <code>Activity</code>, <code>Service</code>, <code>Contents Provider</code>, <code>Broadcaast Reveiver</code> 이렇게 4가지로 구분한다. 이 Component들은 어플이 실행될 때 각각 다른 기능을 수행한다.</p>
<h4 id="131-activity">1.3.1 Activity</h4>
<p>Activity는 화면을 구성하는 Component로 앱의 화면을 안드로이드 폰에 출력을 하기 위해서는 Activity를 만들어야 한다. 어플이 실행되면 Activity에서 출력한 내용이 안드로이드 폰에 나온다.</p>
<ul>
<li>화면을 구성하는 컴포넌트</li>
</ul>
<h4 id="132-service">1.3.2 Service</h4>
<p>Service는 백그라운드(Background) 작업을 하는 Component이다. 화면 출력 기능이 없으므로 Service가 실행되더라도 화면에는 출력되지 않는다. Service Component는 화면과 상관없이 백그라운드에서 장시간 실행해야 할 업무를 담당한다.</p>
<ul>
<li>백그라운드(Background) 작업을 하는 컴포넌트</li>
</ul>
<h4 id="133-contents-provider">1.3.3 Contents Provider</h4>
<p>Contents Provider는 앱의 데이터를 공유하는 Component이다. 안드로이드 폰에는 많은 앱이 설치되어 있으며 <code>앱 간에 데이터를 공유</code>할 수도 있다.</p>
<blockquote>
<p>한 개의 앱이 자신의 데이터를 다른 앱에 공유하려면 Contents Provider를 만들어야 하며, 다른 앱에서는 그 Contents Provider를 이용해 데이터에 접근한다</p>
</blockquote>
<p>예를들어, 카카오톡 앱에서 프로필을 변경할 때 갤러리 앱의 사진을 이용할 수 있다. 이때, Contents Provider를 이용해 데이터를 주고받는다.</p>
<p><img src="https://velog.velcdn.com/images/ted_moon99/post/573fa9f6-0741-49c5-b4ac-fd31025f70c8/image.png" alt=""></p>
<ul>
<li>앱의 데이터를 공유하는 컴포넌트</li>
</ul>
<h4 id="134-broadcast-reveiver">1.3.4 Broadcast Reveiver</h4>
<p>시스템 이벤트가 발생할 때 실행되게 하는 Component이다. 여기서 이벤트는 화면에서 발생하는 사용자 이벤트가 아닌 <code>시스템에서 발생하는 특정 상황</code>을 의미한다. 예를 들어 부팅완료, 배터리 방전 같은 상황을 의미한다.</p>
<h3 id="14-4가지-component를-어떻게-구분할-수-있는가very-easy">1.4 4가지 Component를 어떻게 구분할 수 있는가(Very Easy~^^)</h3>
<p>Component는 앱이 실행될 때 안드로이드 시스템에서 생명주기를 관리하는 클래스이지만 개발자가 만들어야 하는 클래스이다. 개발자가 Component 클래스를 만들 때에는 지정된 클래스를 상속받아야 하는데 이 상속받는 상위 클래스를 보고 구분할 수 있다.</p>
<ul>
<li>액티비티 - Activity 클래스 상속</li>
<li>서비스 - Service 클래스 상속</li>
<li>컨텐츠 프로바이더 - ContentsProvider 클래스 상속</li>
<li>브로드캐스트 리시버 - BroadcastReceiver 클래스 상속</li>
</ul>
<h3 id="15-앱을-개발할-때-component를-어떻게-구성해야-하나요">1.5 앱을 개발할 때 Component를 어떻게 구성해야 하나요?</h3>
<blockquote>
<p>Component는 개발자가 만들고자 하는 앱의 기능과 화면 등을 고려해 필요한 만큼 구성하면 된다.</p>
</blockquote>
<blockquote>
<p>앱을 만들 때 어떤 Component를 어떻게 구성할 지는 <code>설계에 따라 달라지며 정해진 규칙은 없다</code> 심지어 ACtivity가 없는 어플 그래서 사용자에게 보여지는 화면을 제공하지 않는 앱도 개발할 수 있다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/ted_moon99/post/b25ac80c-9639-48af-9079-dfcd894470f0/image.png" alt=""></p>
<h3 id="16-component는-앱-안에서-독립된-실행-단위다">1.6 Component는 앱 안에서 독립된 실행 단위다</h3>
<p>Componet는 Application 안에서 <code>독립된 실행 단위</code>라는 중요한 특징이 존재한다.</p>
<blockquote>
<p><code>독립된 실행단위</code>란 Component끼리 서로 종속되지 않아서 코드 결합이 발생하지 않는다는 의미</p>
</blockquote>
<p>예를 들어, Activity1과 Activity2가 존재한다고 했을 때, Activity1 -&gt; Activity2로 이동을 해야 한다고 가정하자. 단순하게 생각하면 Activity1에서 Activity2를 실행하는 코드를 작성하면 될 것 같기도 하다. 즉, 두 클래스를 결합하여 실행하는 방법(직접 실행)을 하면 될 것 같지만 결론부터 말하면 이 방식은 Android에서는 불가능하다.</p>
<p><img src="https://velog.velcdn.com/images/ted_moon99/post/3e25f308-5684-4e5c-b517-76b930f5f2d7/image.png" alt=""></p>
<h4 id="161-component를-코드-안에서-직접-실행하는-방식은-왜-안-되는가">1.6.1 Component를 코드 안에서 직접 실행하는 방식은 왜 안 되는가?</h4>
<blockquote>
<p>Component의 생명주기를 Android System에서 관리하기 때문에 코드에서 직접 객체를 생성해 실행할 수 없기 때문</p>
</blockquote>
<h4 id="162-그러면-어떻게-해야되는가">1.6.2 그러면 어떻게 해야되는가?</h4>
<p>Activity1에서 Activity2를 실행해야 한다면 <code>Android System</code>에 의뢰해서 시스템이 Activity2를 실행하도록 해야한다.</p>
<blockquote>
<p>Android System에 의뢰함으로써 두 클래스가 서로 종속되지 않고 독립해서 실행되게 해야한다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/ted_moon99/post/1a331a88-26fc-4304-9e47-26db9eb6464e/image.png" alt=""></p>
<h3 id="17-앱-실행-시점이-다양하다">1.7 앱 실행 시점이 다양하다</h3>
<p>Component는 방금 위에서 설명한 앱 내에서 독립해서 실행되는 특징 덕분에 <code>앱의 실행 시점이 다양</code>할 수 있다. 예를 들어 앱의 첫 화면이 Activity1인 경우 Activity1부터 실행되어 화면이 출력된다. 하지만 앱은 사용자가 직접 실행하지 않은 경우에도 얼마든지 실행될 수 있다. 카카오톡에서 사용자가 알림 창에서 메시지 수신 알림을 터치하면 바로 채팅화면이 출력된다. 즉, Activity1부터 앱이 실행되지 않고 바로 Activity2가 실행되는 구조이다.</p>
<p>그래서 안드로이드 앱에서는 메인 함수(main function) 개념이 없다고 말한다.</p>
<h3 id="18-애플리케이션-라이브러리를-사용할-수-있다">1.8 애플리케이션 라이브러리를 사용할 수 있다.</h3>
<h4 id="181-애플리케이션-라이브러리란">1.8.1 애플리케이션 라이브러리란?</h4>
<blockquote>
<p>다른 애플리케이션을 라이브러리처럼 이용하는 것</p>
</blockquote>
<h4 id="182-예시">1.8.2 예시</h4>
<p>카카오톡의 경우, 채팅화면에서 카메라 앱을 실행하여 사진을 찍은 뒤 사진 데이터를 반환받아 다시 채팅화면에 출력할 수 있다. 이때 카카오톡 앱에서 카메라 앱을 이용해 사진을 찍었으므로 카카오톡 앱이 카메라 앱을 라이브러리처럼 이요한 것이다.</p>
<p>이처러 안드로이드 앱은 사용자가 실행하지 않아도 실행될 수 있고, 실행 시점도 다양해서 다른 앱과 연동할 수 있다. </p>
<h2 id="2-리소스resource를-활용한-개발">2. 리소스(Resource)를 활용한 개발</h2>
<p>안드로이드 앱 개발의 또 다른 특징은 리소스를 많이 활용한다는 점이다.</p>
<h3 id="21-리소스resource란">2.1 리소스(Resource)란?</h3>
<blockquote>
<p>코드에서 정적인 값을 분리한 것</p>
</blockquote>
<p>사용자 이벤트에 따른 동적인 값이 아닌 항상 똑같은 값이라면 굳이 코드에 담지 않고 분리해서 개발하는 것이다. 이렇게 할 경우 코드가 짧아져서 <code>개발 생산성</code>과 <code>유지·보수성</code>이 좋아진다.</p>
<p>예를들어, 안드로이드에서 문자열을 사용한다고 가정할 경우 이렇게 작성할 수 있다.</p>
<h4 id="211-리소스resource-이용-x">2.1.1 리소스(Resource) 이용 X</h4>
<p>Kotlin 파일</p>
<pre><code class="language-kotlin">textView.text = &quot;&quot;&quot;
    동해 물과 백두산이 마르고 닳도록
    하느님이 보우하사 우리나라 만세
    무궁화 삼천리 화려 강산
    대한 사람, 대한으로 길이 보전하세
&quot;&quot;&quot;</code></pre>
<h3 id="212-리소스resource-이용-o">2.1.2 리소스(Resource) 이용 O</h3>
<p>res/values/strings.xml파일</p>
<pre><code>&lt;string name =&quot;myText&quot;&gt;
    동해 물과 백두산이 마르고 닳도록
    하느님이 보우하사 우리나라 만세
    무궁화 삼천리 화려 강산
    대한 사람, 대한으로 길이 보전하세
&lt;/string&gt;
</code></pre><p>Kotlin 파일</p>
<pre><code>textView.text = resurces.getString(R.string.myText)</code></pre><p>안드로이드 앱을 개발할 때는 많은 요소를 리소스로 등록해서 사용한다.
문자열 이외에도 색상, 크기, 레이아웃, 이미지, 메뉴 등 많은 요소를 리소스로 활용한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[1. Git이란 무엇인가]]></title>
            <link>https://velog.io/@ted_moon99/1.-Git%EC%9D%B4%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80</link>
            <guid>https://velog.io/@ted_moon99/1.-Git%EC%9D%B4%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80</guid>
            <pubDate>Sun, 31 Mar 2024 17:36:16 GMT</pubDate>
            <description><![CDATA[<ul>
<li>Git</li>
<li>협업 도구</li>
<li>버전관리 도구</li>
</ul>
<h2 id="어떻게-설명을-할-것인가">어떻게 설명을 할 것인가?</h2>
<ul>
<li>Git에 대한 설명은 이미 차고 넘친다. [구글에 git 사용법] 검색하면 된다</li>
<li>개발자의 가장 기본적인 협업도구 중 하나</li>
<li>너무나 당연하게 쓸 줄 안다고 생각하는 것<hr>

</li>
</ul>
<h2 id="git-기본-명령어-살펴보기">Git 기본 명령어 살펴보기</h2>
<ul>
<li>git init : git 생성하기</li>
<li>git clone <code>git_path</code> : 코드 가져오기</li>
<li>git checkout <code>branch_name</code> : 브랜치 선택하기</li>
<li>git checkout -t <code>remote_path/branch_name</code> : 원격 브랜치 생성하기</li>
<li>git branch <code>branch_name</code> : 브랜치 생성하기</li>
<li>git branch -r(remote): 원격 브랜치 목록보기</li>
<li>git branch -a : 로컬 브랜치 목록보기<hr>

</li>
</ul>
<h2 id="왜-git이-어려운가">왜 Git이 어려운가</h2>
<ul>
<li>기본적인 개발의 흐름을 모른다</li>
<li>개발자 용어를 모른다
=&gt; 우리 일상과 관련해서 설명을 한다</li>
</ul>
<p><code>이 글은 프로그래머스 사이트의 리이오 강사의 무료 강의를 기반으로 작성된 글입니다</code></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Material Library(머티리얼 라이브러리)]]></title>
            <link>https://velog.io/@ted_moon99/Material-Library%EB%A8%B8%ED%8B%B0%EB%A6%AC%EC%96%BC-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC</link>
            <guid>https://velog.io/@ted_moon99/Material-Library%EB%A8%B8%ED%8B%B0%EB%A6%AC%EC%96%BC-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC</guid>
            <pubDate>Tue, 06 Feb 2024 07:38:12 GMT</pubDate>
            <description><![CDATA[<h1 id="material-library">Material Library</h1>
<hr>

<h4 id="서론">서론</h4>
<p>Material Design이란 구글의 디자인 지침이다. 그리고 이 디자인 지침에 맞게 앱을 만드는 여러 가지 뷰를 라이브러리(Library)로 제공한다. 이 글에서는 Material Library에서 자주 사용하는 앱바 레이아웃, 내비게이션 뷰, 확장된 플로팅 액션 버튼 등을 살펴보겠다.</p>
<hr>


<h2 id="1-material-library란">1. Material Library란?</h2>
<p>위에서도 말했듯이 Material Design이란 일관된 애플리케이션 디자인 지침이다. 구글에서는 Material Design을 다음과 같이 소개한다.</p>
<blockquote>
<p>질감이 느껴지는 표면(tactile surfaces)과 대담하고 선명한 그래픽 디자인(bold graphic design), 그리고 아름답고 직관적인 사용자 경험을 위한 자연스러운 애니메이션을 특징으로 한다.</p>
</blockquote>
<p>예를 들어, 사용자가 디스플레이를 터치했을 때 동그라미 포인트를 준다거나 물결 모양의 효과를 주어 어느 부분을 터치했는지 직관적으로 알 수 있게 할 수 있다. 즉, 사용자의 사용편의성과 직관성을 높이는데 도움이 된다.</p>
<p>이러한 Material Design의 지침을 지키기 위해서는 다양한 View들이 필요한데, 이 또한 구글이 지원하고 있다. 그것이 바로 Material Library이다.</p>
<p><a href="material.io">Material Design 홈페이지</a></p>
<h4 id="기본적인-사용방법---i-buildgradlektsmoduleapp에-dependencies-선언">기본적인 사용방법 - i) <del>build.gradle.kts(Module:app)에 dependencies 선언</del></h4>
<p>Material Library를 Android Studio에서 사용하기 위해서는
<img src="https://velog.velcdn.com/images/ted_moon99/post/f3c68d2f-789c-40fa-9b5b-9bf15e3b87f7/image.png" alt=""></p>
<p>build.gradle.kts(Module :app)의 파일에 들어가서 dependencies 항목에 다음과 같이 선언을 해주어야 한다.</p>
<p>implementation(&#39;com.google.android.material:material:1.7.0&#39;)</p>
<p><img src="https://velog.velcdn.com/images/ted_moon99/post/2b8c20bd-de08-4f7b-906f-171595256853/image.png" alt="">
교재에서는 material 버전 1.7.0으로 적혀있지만 현 시점(2024.02.06)기준으로는 버전 1.11.0이 최신 버전인 듯 하다.</p>
<p>Android Studio 4.1버전부터는 프로젝트나 모듈을 만들면 dependencies에 자동으로 Material Library가 선언되기 때문에 위의 과정이 필요하지 않다.</p>
<hr>

<h2 id="2-appbar-layout앱바-레이아웃">2. AppBar Layout(앱바 레이아웃)</h2>
<h3 id="앱바appbar란">앱바(AppBar)란?</h3>
<ul>
<li>화면 위쪽의 꾸밀 수 있는 영역 </li>
</ul>
<h3 id="앱바appbar로-무엇을-할-수-있는가">앱바(AppBar)로 무엇을 할 수 있는가?</h3>
<ul>
<li>AppBar를 이용해 화면 위쪽 영역의 크기 조절</li>
<li>메뉴를 출력하는 툴바(Toolbar) 삽입</li>
<li>이미지 출력 or  문자열 출력</li>
<li><blockquote>
<p>화면 위쪽 영역을 아주 다양하게 꾸밀 수 있다</p>
</blockquote>
</li>
</ul>
<h3 id="material-library에서의-앱바appbar-제공">Material Library에서의 앱바(AppBar) 제공</h3>
<p>Material Library에서는 이러한 앱바(AppBar)를 구현하는데 도움이 되는 앱바 레이아웃(AppBar Layout)을 제공한다.</p>
<p>앱바 레이아웃(AppBar Layout)도 하나의 뷰(View)이므로 레이아웃 xml파일에 등록할 수 있다.</p>
<h3 id="앱바appbar-안에-툴바toolbar-포함하기">앱바(Appbar) 안에 툴바(Toolbar) 포함하기</h3>
<p>참고로 앱바를 사용할 때에는 대부분 앱바 레이아웃(Appbar Layout)안에 툴바(Toolbar)를 포함해서 사용한다.</p>
<pre><code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;LinearLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    xmlns:app=&quot;http://schemas.android.com/apk/res-auto&quot;
    xmlns:tools=&quot;http://schemas.android.com/tools&quot;
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;match_parent&quot;
    android:backgroundTintMode=&quot;src_in&quot;
    android:orientation=&quot;vertical&quot;
    tools:context=&quot;.MainActivity&quot;&gt;

    #### 앱바(Appbar) `시작`
    &lt;com.google.android.material.appbar.AppBarLayout
        android:layout_width=&quot;match_parent&quot;
        android:layout_height=&quot;242dp&quot;
        android:backgroundTint=&quot;#F44336&quot;
        android:theme=&quot;@style/ThemeOverlay.AppCompat.Dark.ActionBar&quot;
        app:expanded=&quot;false&quot;&gt;

        #### 앱바(Appbar) 안에 있는 툴바(Toolbar) #### 
        &lt;androidx.appcompat.widget.Toolbar
            android:id=&quot;@+id/toolbarTest&quot;
            android:layout_width=&quot;match_parent&quot;
            android:layout_height=&quot;wrap_content&quot;
            android:background=&quot;#673AB7&quot; /&gt;

    #### 앱바(Appbar) `끝`
    &lt;/com.google.android.material.appbar.AppBarLayout&gt;


    #### 앱바(Appbar) `밖에 있는 영역`
    &lt;TextView
        android:id=&quot;@+id/textView&quot;
        android:layout_width=&quot;match_parent&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:text=&quot;TextView&quot;
        android:textAppearance=&quot;@style/TextAppearance.AppCompat.Large&quot;/&gt;

&lt;/LinearLayout&gt;</code></pre><p>위처럼 코드를 작성해주면 다음과 같은 화면을 얻을 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/ted_moon99/post/00ba3cf7-34f0-4fe8-9d47-8fe1790f9ebb/image.png" alt="">
Appbar 영역과 Toolbar 영역을 구분하기 위해 Appbar는 빨간색 배경을 적용했고 Toolbar 영역은 보라색 배경을 적용했다.</p>
<h3 id="앱바appbar-안에-이미지imageview-넣기">앱바(Appbar) 안에 이미지(ImageView) 넣기</h3>
<p>앱바(Appbar) 안에는 이미지도 넣어줄 수 있다. 동영상도 가능할 것 같은데 이 부분은 나중에 알아보도록 한다. 앱바(Appbar) 안에 이미지를 넣어주기 위해 &lt;com.google.android.material.Appcompat.Dark.ActionBar&gt;태그 안에 다음의 ImageView 코드를 추가해준다.</p>
<pre><code>&lt;ImageView
            android:layout_width=&quot;match_parent&quot;
            android:layout_height=&quot;match_parent&quot;
            android:src=&quot;@drawable/funny_face&quot;/&gt;</code></pre><p>위의 코드를 추가해주면 다음의 코드 전문을 얻을 수 있다.</p>
<pre><code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;LinearLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    xmlns:app=&quot;http://schemas.android.com/apk/res-auto&quot;
    xmlns:tools=&quot;http://schemas.android.com/tools&quot;
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;match_parent&quot;
    android:backgroundTintMode=&quot;src_in&quot;
    android:orientation=&quot;vertical&quot;
    tools:context=&quot;.MainActivity&quot;&gt;

    &lt;com.google.android.material.appbar.AppBarLayout
        android:layout_width=&quot;match_parent&quot;
        android:layout_height=&quot;242dp&quot;
        android:backgroundTint=&quot;#F44336&quot;
        android:theme=&quot;@style/ThemeOverlay.AppCompat.Dark.ActionBar&quot;
        app:expanded=&quot;false&quot;&gt;

&lt;!--        앱바 안에 있는 Toolbar--&gt;
        &lt;androidx.appcompat.widget.Toolbar
            android:id=&quot;@+id/toolbarTest&quot;
            android:layout_width=&quot;match_parent&quot;
            android:layout_height=&quot;wrap_content&quot;
            android:background=&quot;#673AB7&quot; /&gt;

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


    &lt;/com.google.android.material.appbar.AppBarLayout&gt;

    &lt;TextView
        android:id=&quot;@+id/textView&quot;
        android:layout_width=&quot;match_parent&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:text=&quot;TextView&quot;
        android:textAppearance=&quot;@style/TextAppearance.AppCompat.Large&quot;/&gt;

&lt;/LinearLayout&gt;</code></pre><p>그리고 이 코드의 실행화면..</p>
<p><img src="https://velog.velcdn.com/images/ted_moon99/post/031bbc47-99d3-4b19-ace7-a149f53f7b77/image.png" alt=""></p>
<p>&lt;com.google.android.material.appbar.AppBarLayout&gt;태그 안에서 height의 크기를 조절하여 AppBar의 높이를 조정하는 것도 가능하다.</p>
<hr>

<h2 id="참고-코디네이터-레이아웃coordinatorlayout">#참고 코디네이터 레이아웃(CoordinatorLayout)</h2>
<p>CoordinatorLayout은 사실 Material Library는 아니고 jetpack의 androidx Library에서 제공하지만 앱바(Appbar Layout)에서 가장 많이 이용되기 때문에 이 글에서 같이 살펴보도록한다.</p>
<p>참고로 coordinate는 &#39;조화시키다 / 조정하다&#39;의 뜻을 가지고 있으므로 직역을 해보면 조정해주는 레이아웃이다. 즉, 2개의 View들의 차이를 조정해준다고 생각해주면 된다.</p>
<blockquote>
<p>Coordinator Layout은 View들끼리 상호 작용을 해야할 때 사용한다.</p>
</blockquote>
<blockquote>
<p>View 2개를 Coordinator Layout에 넣어주면 하나의 View에서 발생한 Event 정보를 Coordinator Layout이 받아서 다른 View에 전달해준다.</p>
</blockquote>
<p>그러나 Coordinator Layout에 2개의 View를 넣어줌으로써 2개의 View가 동시에 <U>연동된다고 할 수 없다.</U></p>
<blockquote>
<p>즉, Coordinator layout으로 감싼 모든 자식 View들끼리 상호작용을 지원하지는 않는다.</p>
</blockquote>
<h3 id="스크롤scroll-연동하기">스크롤(Scroll) 연동하기</h3>
]]></description>
        </item>
    </channel>
</rss>