<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>devANT</title>
        <link>https://velog.io/</link>
        <description>개미 같이 일하는 안드로이드 개발자💻</description>
        <lastBuildDate>Fri, 19 Apr 2024 03:59:55 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>devANT</title>
            <url>https://velog.velcdn.com/images/nter-developer/profile/bc889fb6-77b1-4ca3-a480-72411f4e462c/social_profile.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. devANT. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/nter-developer" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[안드로이드 코드 개선하기 (3) 관심사 분리]]></title>
            <link>https://velog.io/@nter-developer/project-sqli-time-to-refactoring-3</link>
            <guid>https://velog.io/@nter-developer/project-sqli-time-to-refactoring-3</guid>
            <pubDate>Fri, 19 Apr 2024 03:59:55 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>이 포스팅은 안드로이드 코드 개선을 위한 문제 파악 후의 코드 개선 작업을 다루고 있습니다. 문제 파악과 선행 리펙터링 작업은 <a href="https://velog.io/@nter-developer/project-sqli-time-to-refactoring-1">(1) 문제 파악</a>과 <a href="https://velog.io/@nter-developer/project-sqli-time-to-refactoring-2">(2) 함수 호출</a> 포스팅을 확인해주세요!</p>
</blockquote>
<h2 id="🎮-관심사-분리란-what">🎮 관심사 분리란? WHAT</h2>
<blockquote>
<p><a href="https://ko.wikipedia.org/wiki/%EA%B4%80%EC%8B%AC%EC%82%AC_%EB%B6%84%EB%A6%AC">관심사 분리</a>는 개발에서 중요한 디자인 원칙 중 하나이다. </p>
</blockquote>
<p>이는 코드를 작성할 때 다양한 책임을 가진 부분들을 분리하여 각각을 독립적으로 유지하는 것을 의미한다. 관심사 분리를 잘해야지만 유지보수성, 테스트 용이성, 재사용성 증가, 협업 용이성이 증가한다. </p>
<p>반대로 관심사 분리가 되어있지 않다는 것을 게임으로 비유하자면 파티에서 <code>마법사</code> 포지션이 딜과 힐과 탱킹을 하는 느낌이다. 이제는 딜 포지션을 강화하고 싶은데 모든 싸이클이 애매하게 딜과 힐과 탱킹이 섞여있어서 함부로 건드리다간 기존의 성능마저 망가질 수도 있는 것이다. 반면에 관심사 분리가 잘 되어 있는 코드는 포지션 분배가 잘 된 게임 파티 같아서 힐러에게는 힐러 아이템을 딜러에게 딜러 아이템을 주면 투자한만큼 성능이 잘 올라간다. 개발 용어로 다시 풀이하자면 확장 가능성이 높다는 뜻이다. 그러면 내 파티를 강화하기 위해 역할을 잘 분배해보도록 하자!</p>
</br>

<h3 id="💥-문제-분석">💥 문제 분석</h3>
<pre><code class="language-kotlin">class EditorViewModel(private val databaseRepository: DatabaseRepository) : ViewModel() {
    private val modelLineNumber = LineNumber()

    private val _editTextStatement = MutableLiveData&lt;EditText&gt;()
    val editTextStatement: MutableLiveData&lt;EditText&gt; = _editTextStatement

    private val _textViewLineNumber = MutableLiveData&lt;String&gt;()
    val textViewLineNumber: LiveData&lt;String&gt; = _textViewLineNumber

    // Data binding with Activity_editor.xml #afterTextChanged()
    fun onStatementChanged() {
        val editText = editTextStatement.value
        editText?.layout?.let { updateLineNumbers(it) }
    }

    // If line number changed, update model and render line number text view
    private fun updateLineNumbers(it: Layout) {
        if (it.lineCount != modelLineNumber.number) {
            modelLineNumber.content = generateLineNumber(it.lineCount)
            modelLineNumber.number = it.lineCount
            _textViewLineNumber.value = modelLineNumber.content
        }
    }

    // Generate line number like &quot;&quot;1/n2/n3/n ... count&quot;
    private fun generateLineNumber(count: Int): String {
        val stringBuilder = StringBuilder()
        for (i in 1..count) {
            stringBuilder.append(&quot;$i\n&quot;)
        }
        return stringBuilder.toString()
    }

    // ...
}</code></pre>
<p>자 다시 한번 문제의 코드를 보자. 리펙터링 전에 비하면 가독성이 많이 향상되었지만 아직 부족하다. 이제는 ViewModel이 사용자 인터페이스와 데이터 간의 중개자 역할에만 집중할 수 있도록 <code>Line Number</code> 로직을 분리해보자.</p>
</br>

<h3 id="🛠️-해결-step-1">🛠️ 해결 Step (1)</h3>
<pre><code class="language-kotlin">object LineNumberManager {
    private val modelLineNumber = LineNumber()

    // If line number changed, update model and render line number text view
    fun updateLineNumbers(it: Layout): String {
        if (it.lineCount != modelLineNumber.number) {
            modelLineNumber.content = generateLineNumber(it.lineCount)
            modelLineNumber.number = it.lineCount
        }
        return modelLineNumber.content
    }

    // Generate line number like &quot;&quot;1/n2/n3/n ... count&quot;
    private fun generateLineNumber(count: Int): String {
        return buildString {
            for (i in 1..count) {
                append(&quot;$i\n&quot;)
            }
        }
    }
}</code></pre>
<pre><code class="language-kotlin">class EditorViewModel(private val databaseRepository: DatabaseRepository) : ViewModel() {
    private val _editTextStatement = MutableLiveData&lt;EditText&gt;()
    val editTextStatement: MutableLiveData&lt;EditText&gt; = _editTextStatement

    private val _textViewLineNumber = MutableLiveData&lt;String&gt;()
    val textViewLineNumber: LiveData&lt;String&gt; = _textViewLineNumber

    // Data binding with Activity_editor.xml #afterTextChanged()
    fun onStatementChanged() {
        editTextStatement.value?.layout?.let {
            _textViewLineNumber.value = LineNumberManager.updateLineNumbers(it)
        }
    }

    // ...
}</code></pre>
<p>우선 LinenumberManager Object를 만들어주고 update 함수와 generate 함수를 분리해줬다. 이후에 <code>EditorViewModel</code>의 코드를 수정해서 EditeText가 null이 아니면 Line Number를 Line Number Manager를 통해서 업데이트할 수 있도록 해주었다.</p>
</br>

<h3 id="🛠️-해결-step-2">🛠️ 해결 Step (2)</h3>
<pre><code class="language-kotlin">object LineNumberManager {
    private val modelLineNumber = LineNumber()

    // If line number changed, update model and render line number text view
    fun updateLineNumbers(it: Layout): String? {
        if (it.lineCount != modelLineNumber.number) {
            modelLineNumber.content = generateLineNumber(it.lineCount)
            modelLineNumber.number = it.lineCount
            return modelLineNumber.content
        }
        return null
    }

    // Generate line number like &quot;&quot;1/n2/n3/n ... count&quot;
    private fun generateLineNumber(count: Int): String {
        return buildString {
            for (i in 1..count) {
                append(&quot;$i\n&quot;)
            }
        }
    }
}
</code></pre>
<pre><code class="language-kotlin">class EditorViewModel(private val databaseRepository: DatabaseRepository) : ViewModel() {
    private val _editTextStatement = MutableLiveData&lt;EditText&gt;()
    val editTextStatement: MutableLiveData&lt;EditText&gt; = _editTextStatement

    private val _textViewLineNumber = MutableLiveData&lt;String&gt;()
    val textViewLineNumber: LiveData&lt;String&gt; = _textViewLineNumber

    // Data binding with Activity_editor.xml #afterTextChanged()
    fun onStatementChanged() {
        editTextStatement.value?.layout?.let { layout -&gt;
            LineNumberManager.updateLineNumbers(layout)?.let { lineNumber -&gt;
                _textViewLineNumber.value = lineNumber
            }
        }
    }

    // ...
}
</code></pre>
<p>여기서 문제가 있었다. 함수를 분리하는 과정에서 <code>TextView</code>의 업데이트가 afterTextChanged() 실행마다 일어나도록 바껴버렸다. <a href="https://velog.io/@nter-developer/project-sqli-time-to-refactoring-2">지난 포스팅</a>의 generateLineNUmber() 호출 주기 제어와는 별개로 LineNumber만 계산하지 않을 뿐이지 같은 UI이더라도 계속 업데이트가 이뤄지고 있다. 이를 방지하기 위해서 updateLineNumbers()에서 만약 Line Number가 그대로라면 null을 반환하게 바꾸고 onStatementChanged()에서는 null safe 연산자를 통해서 UI 업데이트 여부를 결정한다.</p>
</br>

<h3 id="🛠️-해결-step-3">🛠️ 해결 Step (3)</h3>
<pre><code class="language-kotlin">class EditorViewModel(private val databaseRepository: DatabaseRepository) : ViewModel() {
    private val _editTextStatement = MutableLiveData&lt;EditText&gt;()
    val editTextStatement: MutableLiveData&lt;EditText&gt; = _editTextStatement

    private val _textViewLineNumber = MutableLiveData&lt;String&gt;()
    val textViewLineNumber: LiveData&lt;String&gt; = _textViewLineNumber

    // Data binding with Activity_editor.xml #afterTextChanged()
    fun onStatementChanged() {
        editTextStatement.value?.layout?.let { layout -&gt;
            renderLineNumberView(layout)
        }
    }

    // Render line number view when line number updated
    private fun renderLineNumberView(layout: Layout) {
        LineNumberManager.updateLineNumbers(layout)?.let { lineNumber -&gt;
            _textViewLineNumber.value = lineNumber
        }
    }

    // ...
}</code></pre>
<p>마지막으로 함수의 가독성을 위해서 <code>onStatementChanged()</code>에는 editTextStatement에 대한 null safe 연산자를 이용한 검사 기능을 남겨두고 UI 업데이트 로직은 <code>renderLineNumberView()</code>로 분리해주었다.</p>
</br>

<h2 id="✨-refactoring-result">✨ Refactoring Result</h2>
<p><img src="https://velog.velcdn.com/images/nter-developer/post/50737c72-b76c-4f21-9374-1733befc436b/image.png" alt=""></p>
<p>이번 리펙터링은 아예 로직 자체를 분리시켰기 때문에 EditorViewModel의 코드는 상당히 줄어들었다. 코드만 줄어든 것이 아니라 성능 향상의 효과도 있었다. 어쩌다보니 관심사 분리와 UI 업데이트 주기 튜닝을 같이 해줬는데, Line Number 로직 전체를 분리한 덕분에 EditorViewModel의 코드는 상당히 줄어들었다. 리펙터링 전체로 향상된 부분을 정리하면 아래와 같다.</p>
<ul>
<li><strong>변수명의 가독성 향상</strong>: 명확한 쓰임새를 파악하기 쉬워졌음</li>
<li><strong>ViewModel의 캡슐화 강화</strong>: LiveData를 read-only로 추가하고 MutableLiveData를 private으로 선언함으로써 클래스 외부에서 ViewModel의 데이터에 접근할 수 없게 했다.</li>
<li><strong>UI 업데이트 빈도 조정</strong>: Line Number 업데이트 로직의 발동 조건을 <code>Layout(EditText)</code>가 변경될 때로 수정했다.</li>
<li><strong>함수 관심사 분리</strong>: 하나의 함수가 여러 역할을 수행하는 것을 방지하기 위해 코드를 분리해줬다. 이를 통해서 테스트 용이성이 증가했다!</li>
<li><strong>함수 오류 수정</strong>: 쿼리 검증문을 대소문자 구분 없이 검증할 수 있도록 ignoreCase를 true로 설정해주었다.</li>
<li><code>NEW!</code> <strong>EditorViewModel의 관심사 분리</strong>: 사용자 인터페이스와 데이터 간의 중개자 역할에만 집중할 수 있게 되었다. </li>
</ul>
</br>

<p><img src="https://velog.velcdn.com/images/nter-developer/post/6bb34442-f1ca-406f-933f-c481075d6bb5/image.png" alt=""></p>
<p>두번에 걸친 리펙터링의 결과 중 가장 만족스러운 부분은 아무래도 관심사 분리와 UI 업데이트 주기 조절이 아닌가 싶다. 리펙터링 전후의 함수 호출 횟수를 로그로 찍어봤는데, 간단한 CREATE문 생성을 위해서 <code>리펙터링 전</code>에는 120회 호출되면 함수들이 <code>리펙터링 후</code>에는 5회 호출로 크게 감소했다. Compose 같은 경우에는 <code>Recomposition</code>의 중요성을 알고 있었기에 조심하겠지만 XML은 아무 생각 없이 기능만 구현해버렸던 거 같다. 앞으로는 UI 업데이트도 하나 하나 신경 쓴다는 생각으로 기능을 구현해야겠다.</p>
</br>

<p>이렇게 리펙터링을 3개의 포스트에 걸쳐서 진행해보았다. 아직까지는 부족한게 많은 코드다. 테스트 용이성과 예외 처리가 잘 되어있는지 확신이 없다. GitHub Actions의 Unit Test에 문제가 있어서 Test 코드를 작성하지 않고 있는데 문제가 해결되는대로 추가할 예정이니 앞으로의 리펙터링은 테스트 용이성 확보를 목표로 할 듯하다. 이전 글을 보면 알겠지만 SQL문 실행 로직이 EditorViewModel에 일부 남아있어 이와 비슷하게 포스팅을 하려하니 많은 관심! 부탁한다🙏🙏</p>
</br>

<h5 style="color:#FFFFFF8; text-align:right;">SQLI 프로젝트의 GitHub Repository가 궁금하다면? <a href="https://github.com/dev-ant/sql-interpreter-android" target="_blank">클릭</a></h5>

</br>

<hr>
<h2 id="📚-참고">📚 참고</h2>
<ul>
<li>실제 프로젝트 이슈 <a href="https://github.com/dev-ant/sql-interpreter-android/issues/45">Separate line number logic from editor view model #45</a></li>
<li>실제 프로젝트 이슈 <a href="https://github.com/dev-ant/sql-interpreter-android/issues/43">Improve editor model view readability and fun call frequency #43</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[안드로이드 코드 개선하기 (2) 함수 호출]]></title>
            <link>https://velog.io/@nter-developer/project-sqli-time-to-refactoring-2</link>
            <guid>https://velog.io/@nter-developer/project-sqli-time-to-refactoring-2</guid>
            <pubDate>Thu, 18 Apr 2024 12:10:45 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>이 포스팅은 안드로이드 코드 개선을 위한 문제 파악 후의 코드 개선 작업을 다루고 있습니다. 문제 파악 부분은 <a href="https://velog.io/@nter-developer/project-sqli-time-to-refactoring-1">안드로이드 코드 개선하기 (1) 문제 파악</a> 포스팅을 확인해주세요!</p>
</blockquote>
<h2 id="🚀-refactoring-start">🚀 Refactoring Start</h2>
<p>우선 지난 포스팅에서 파악한 문제점들이다. 조바심 내지말고 차근차근 하나씩 수정해보도록 하자.</p>
<h3 id="💥-문제-분석-1">💥 문제 분석 (1)</h3>
<pre><code class="language-kotlin">    private val lineNumberModel = LineNumber(&quot;&quot;)
    var statementEditView = MutableLiveData&lt;EditText&gt;()
    val lineNumberView = MutableLiveData&lt;String&gt;()</code></pre>
<ul>
<li>접근지정자와 변수명:<ul>
<li>변수명을 보고 의도를 파악하기 어렵다. <code>가독성</code></li>
<li>ViewModel의 상태가 캡슐화 되어 있지 않다. <code>객체지향</code></li>
</ul>
</li>
</ul>
<h3 id="🛠️-해결-방법-1">🛠️ 해결 방법 (1)</h3>
<pre><code class="language-kotlin">    private val modelLineNumber = LineNumber()

    private val _editTextStatement = MutableLiveData&lt;EditText&gt;()
    val editTextStatement: MutableLiveData&lt;EditText&gt; = _editTextStatement

    private val _textViewLineNumber = MutableLiveData&lt;String&gt;()
    val textViewLineNumber: LiveData&lt;String&gt; = _textViewLineNumber</code></pre>
<ul>
<li>LiveData와 MutableLiveData는 XML과 데이터바인딩되어 있기 때문에 정확히 어떤 컴포넌트와 바인딩되어있는지 명확히 보여주기 위해서 *&quot;컴포넌트 종류 + 용도&quot;로* 변경해주었다. </li>
<li><code>modelLineNumber</code>는 기본 매개변수를 명시해서 보기 싫은 &quot;&quot;를 제거해주었다.</li>
<li>Private 변수임을 보여주기 위해서 _을 변수명 앞에 붙여주었다.</li>
<li><code>_editTextStatement</code>와 <code>_textViewLineNumber</code>는 ViewModel 내부에서만 수정하도록 Private으로 설정하고 <code>textViewLineNumber</code>는 데이터바인딩한 컴포넌트를 read-only로 관찰하기 위해서 LiveData로 선언해주었다. <ul>
<li>다만 <code>editTextStatement</code>는 Activity에서 ViewBiding으로 넘겨받았기 때문에 MutableLiveData로 선언해주었다. 임의로 MutableLiveData의 값에 접근하지 않도록 주의해야한다. 권장할만한 방법은 아닌 것 같지만 곧 XML에서 Compose로 변경할 예정이기에 우선은 이렇게 변경해주었다.</li>
</ul>
</li>
</ul>
</br>

<h3 id="💥-문제-분석-2">💥 문제 분석 (2)</h3>
<pre><code class="language-kotlin">    // Activity_editor.xml과 data binding #EditText의 afterTextChanged()
    fun onStatementChanged() {
        val editText = statementEditView.value
        val layout = editText?.layout
        layout?.let {
            lineNumberModel.content = generateLineNumber(it.lineCount)
            lineNumberView.value = lineNumberModel.content
        }
    }</code></pre>
<ul>
<li>onStatementChanged(): <ul>
<li>generateLineNumber() 함수를 과하게 많이 호출한다. <code>자원 낭비</code></li>
<li>너무 많은 역할을 수행하고 있다. <code>관심사 분리 부족</code></li>
</ul>
</li>
</ul>
<h3 id="🛠️-해결-방법-2">🛠️ 해결 방법 (2)</h3>
<pre><code class="language-kotlin">    // Data binding with Activity_editor.xml #EditText afterTextChanged()
    fun onStatementChanged() {
        val editText = editTextStatement.value
        editText?.layout?.let { updateLineNumbers(it) }
    }

    private fun updateLineNumber(it: Layout) {
        if (it.lineCount != modelLineNumber.number) {
            modelLineNumber.content = generateLineNumber(it.lineCount)
            modelLineNumber.number = it.lineCount
            _textViewLineNumber.value = modelLineNumber.content
        }
    }</code></pre>
<p>우선은 Data Class인 LineNumber를 통해서 Line Count를 관리해주도록 바꿨다. 그리고 함수를 Null 검증과 Update로 분리해주면서 줄 수가 변했을 때만 generateLineNumber() 메소드를 호출하도록 바꿔주었다. 이렇게 해준 뒤 Log를 비교해보자.</p>
<pre><code class="language-kotlin">    Log.i(&quot;CALL_UPDATE&quot;, &quot;${++count}&quot;)</code></pre>
<p><img src="https://velog.velcdn.com/images/nter-developer/post/8ea3cfc8-d380-4a3f-8373-961af52bb1fc/image.png" alt=""></p>
<p>좌측의 화면의 글자를 타이핑하는 동안 리펙터링 전에는 총 41번의 generateLineNumber() 함수 호출이 있었고 리펙터링 후에는 10번의 함수 호출이 있었다. 이전에는 한글자 입력할 때마다 함수를 호출했는데 지금은 줄 수의 변화에 따라 호출하는 것이라 상당히 많은 차이가 있었다.</p>
</br>

<h3 id="💥-문제-분석-3">💥 문제 분석 (3)</h3>
<pre><code class="language-kotlin">    // SQL문이 쿼리인지 검증. 즉 SELECT로 시작하는지 확인
    private fun isQueryStatement(statement: String): Boolean {
        return statement.startsWith(&quot;SELECT&quot;) or statement.startsWith(&quot;select&quot;)
    }</code></pre>
<ul>
<li>isQueryStatement(): <ul>
<li>&quot;Select&quot; &quot;SELect&quot; 같은 키워드는 제대로 확인하지 못한다. <code>의도 실패</code></li>
<li>함수명이 의도를 명확하게 보여주지 못한다. <code>가독성</code></li>
</ul>
</li>
</ul>
<h3 id="🛠️-해결-방법-3">🛠️ 해결 방법 (3)</h3>
<pre><code class="language-kotlin">    // SQL문이 쿼리인지 검증. 즉 SELECT로 시작하는지 확인
    private fun isSelectStatement(statement: String): Boolean {
        return statement.startsWith(&quot;SELECT&quot;, ignoreCase = true)
    }</code></pre>
<p>ignoreCase를 적용해주고 함수명을 변경해주었다. <code>seLect</code>나 <code>SeleCT</code>처럼 대문자와 소문자를 혼합해서 사용하는 경우를 생각하지 못 해서 생긴 문제였다.</p>
</br>

<h2 id="✨-refactoring-result">✨ Refactoring Result</h2>
<pre><code class="language-kotlin">class EditorViewModel(private val databaseRepository: DatabaseRepository) : ViewModel() {
    private val modelLineNumber = LineNumber()

    private val _editTextStatement = MutableLiveData&lt;EditText&gt;()
    val editTextStatement: MutableLiveData&lt;EditText&gt; = _editTextStatement

    private val _textViewLineNumber = MutableLiveData&lt;String&gt;()
    val textViewLineNumber: LiveData&lt;String&gt; = _textViewLineNumber

    // Data binding with Activity_editor.xml #afterTextChanged()
    fun onStatementChanged() {
        val editText = editTextStatement.value
        editText?.layout?.let { updateLineNumbers(it) }
    }

    // If line number changed, update model and render line number text view
    private fun updateLineNumbers(it: Layout) {
        if (it.lineCount != modelLineNumber.number) {
            modelLineNumber.content = generateLineNumber(it.lineCount)
            modelLineNumber.number = it.lineCount
            _textViewLineNumber.value = modelLineNumber.content
        }
    }

    // View binding with Activity_editor.xml, Check Editor Activity
    fun execStatement(): Any? {
        return _editTextStatement.value?.let {
            if (isQueryStatement(editTextStatement.value.toString())) {
                databaseRepository.execQuery(it.text.toString())
            } else {
                databaseRepository.execStatement(it.text.toString())
            }
        }
    }

    // Check if a statement starts with &quot;SELECT&quot; ignore case
    private fun isSelectStatement(statement: String): Boolean {
        return statement.startsWith(&quot;SELECT&quot;, ignoreCase = true)
    }

    // Generate line number like &quot;&quot;1/n2/n3/n ... count&quot;
    private fun generateLineNumber(count: Int): String {
        val stringBuilder = StringBuilder()
        for (i in 1..count) {
            stringBuilder.append(&quot;$i\n&quot;)
        }
        return stringBuilder.toString()
    }
}</code></pre>
<p><img src="https://velog.velcdn.com/images/nter-developer/post/36745cfe-4c51-4552-809b-1651e265fd9f/image.png" alt=""></p>
<p>리펙터링 후에 오히려 코드 수가 늘어났다. <code>onStatementChanged()</code>의 관심사를 분리하기 위해서 별도의 함수를 구현한게 원인이다. 코드 수가 늘어난 점을 제외하면 많은 부분이 향상되었다. 정리해보자면 아래와 같다.</p>
<ul>
<li><strong>변수명의 가독성 향상</strong>: 명확한 쓰임새를 파악하기 쉬워졌음</li>
<li><strong>ViewModel의 캡슐화 강화</strong>: LiveData를 read-only로 추가하고 MutableLiveData를 private으로 선언함으로써 클래스 외부에서 ViewModel의 데이터에 접근할 수 없게 했다.</li>
<li><strong>UI 업데이트 빈도 조정</strong>: Line Number 업데이트 로직의 발동 조건을 <code>Layout(EditText)</code>가 변경될 때로 수정했다.</li>
<li><strong>함수 관심사 분리</strong>: 하나의 함수가 여러 역할을 수행하는 것을 방지하기 위해 코드를 분리해줬다. 이를 통해서 테스트 용이성이 증가했다!</li>
<li><strong>함수 오류 수정</strong>: 쿼리 검증문을 대소문자 구분 없이 검증할 수 있도록 ignoreCase를 true로 설정해주었다.</li>
</ul>
<p>이렇게 해서 리펙터링 1차를 마무리했다. 관심사 분리를 위해서 LineNumber 로직 전반을 별도의 클래스로 분리하려 했지만, 포스팅의 분량이 너무 길어질까봐 후에 다루도록 하겠다. <a href="https://www.youtube.com/watch?v=KzHOPckFmwc">추천 노래</a>를 남기면서 글을 마무리한다. 다들 안녕😉</p>
</br>

<h5 style="color:#FFFFFF8; text-align:right;">SQLI 프로젝트의 GitHub Repository가 궁금하다면? <a href="https://github.com/dev-ant/sql-interpreter-android" target="_blank">클릭</a></h5>

</br>

<hr>
<h2 id="📚-참고">📚 참고</h2>
<ul>
<li>실제 프로젝트 이슈 <a href="https://github.com/dev-ant/sql-interpreter-android/issues/43">Improve editor model view readability and fun call frequency #43</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[안드로이드 코드 개선하기 (1) 문제 파악]]></title>
            <link>https://velog.io/@nter-developer/project-sqli-time-to-refactoring-1</link>
            <guid>https://velog.io/@nter-developer/project-sqli-time-to-refactoring-1</guid>
            <pubDate>Thu, 18 Apr 2024 02:49:41 GMT</pubDate>
            <description><![CDATA[<h2 id="🛠️-refactoring-why">🛠️ Refactoring? WHY</h2>
<blockquote>
<p>플레이스토어 출시도 끝났겠다. 정리정돈을 좀 하자.</p>
</blockquote>
<p>앞으로 다양한 기능을 추가하기 전에 코드를 좀 더 간결하게 하기 위해 리팩터링을 진행해보려 한다. 지난 프로젝트로 배운 것은 리팩터링 작업은 선택이 아니라 필수라는 점이다. 물론 처음부터 완벽한 코드를 작성하는 고수분들은 다르시겠지만, 나 같은 경우에는 한 번에 완벽한 코드를 작성하기 쉽지 않다. 그렇다고 기능 구현만 간신히 되는 코드를 방치하면 나중에는 얽히고 섥혀서 기능을 확장하는 게 어려워진다. 여유가 될 때마다 기능 구현에 필요한 시간을 너무 잡아먹지 않는 선에서 코드를 개선하는 것이 좋아보인다. 그럼 어떤 의식의 흐름으로 리팩터링하는지 알아보도록 하자.</p>
</br>

<pre><code class="language-kotlin">// 이게 바로 문제의 코드이다.
class EditorViewModel(private val databaseRepository: DatabaseRepository) : ViewModel() {
    private val lineNumberModel = LineNumber(&quot;&quot;)
    var statementEditView = MutableLiveData&lt;EditText&gt;()
    val lineNumberView = MutableLiveData&lt;String&gt;()

    // Activity_editor.xml과 data binding #EditText의 afterTextChanged()
    fun onStatementChanged() {
        val editText = statementEditView.value
        val layout = editText?.layout
        layout?.let {
            lineNumberModel.content = generateLineNumber(it.lineCount)
            lineNumberView.value = lineNumberModel.content
        }
    }

    // Activity_editor.xml과 data binding #버튼 클릭시 실행
    fun execStatement(): Any? {
        return statementEditView.value?.let {
            if (isQueryStatement(statementEditView.value.toString())) {
                databaseRepository.execQuery(it.text.toString())
            } else {
                databaseRepository.execStatement(it.text.toString())
            }
        }
    }

    // SQL문이 쿼리인지 검증. 즉 SELECT로 시작하는지 확인
    private fun isQueryStatement(statement: String): Boolean {
        return statement.startsWith(&quot;SELECT&quot;) or statement.startsWith(&quot;select&quot;)
    }

    // Line Count를 입력받으면 Line Number을 생성.
    private fun generateLineNumber(count: Int): String {
        val stringBuilder = StringBuilder()
        for (i in 1..count) {
            stringBuilder.append(&quot;$i\n&quot;)
        }
        return stringBuilder.toString()
    }
}</code></pre>
</br>

<h2 id="🔍-어떻게-하는데-step-by-step">🔍 어떻게 하는데? Step by Step</h2>
<p>코드를 하나하나 뜯어보면서 어떤 점이 마음에 안 드는지 정리해보자. 내가 생각해는 좋은 코드에 대한 질문을 하나씩 던지면서 어떤 점이 문제인지 찾아보도록 하겠다.</p>
<blockquote>
<p>접근지정자와 변수명</p>
</blockquote>
<pre><code class="language-kotlin">private val lineNumberModel = LineNumber(&quot;&quot;)
var statementEditView = MutableLiveData&lt;EditText&gt;()
val lineNumberView = MutableLiveData&lt;String&gt;()</code></pre>
<ol>
<li>적절한 위치에 있는가? YES</li>
<li>모든 코드가 필요한 코드인가? YES</li>
<li>접근지정자가 올바르게 사용되었는가? YES</li>
<li>변수명이 가독성이 좋은가? <strong><span style="color:#F7625A;">NO</span></strong></li>
<li>데이터 흐름의 방향성이 안전한가? <strong><span style="color:#F7625A;">NO</span></strong></br>

</li>
</ol>
<blockquote>
<p> EditText 변화 탐지 함수 : <a href="">onStatementChanged()</a></p>
</blockquote>
<pre><code class="language-kotlin">    // Activity_editor.xml과 data binding #EditText의 afterTextChanged()
    fun onStatementChanged() {
        val editText = statementEditView.value
        val layout = editText?.layout
        layout?.let {
            lineNumberModel.content = generateLineNumber(it.lineCount)
            lineNumberView.value = lineNumberModel.content
        }
    }</code></pre>
<ol>
<li>적절한 위치에 있는가? YES</li>
<li>모든 코드가 필요한 코드인가? YES</li>
<li>가독성이 좋게 작성되었는가? YES</li>
<li>함수가 용도에 맞게 작동하는가? YES</li>
<li>함수와 변수의 명칭이 적합한가? YES</li>
<li>오류 처리가 되어 있는가? YES</li>
<li>테스트하기 쉬운 구조인가 YES</li>
<li>함수의 호출 빈도가 적당한가? <strong><span style="color:#F7625A;">NO</span></strong></br>

</li>
</ol>
<blockquote>
<p>SQL문 실행 함수 : <a href="">execStatement()</a></p>
</blockquote>
<pre><code class="language-kotlin">    // Activity_editor.xml과 view binding #버튼 클릭시 실행
    fun execStatement(): Any? {
        return statementEditView.value?.let {
            if (isQueryStatement(statementEditView.value.toString())) {
                databaseRepository.execQuery(it.text.toString())
            } else {
                databaseRepository.execStatement(it.text.toString())
            }
        }
    }</code></pre>
<ol>
<li>적절한 위치에 있는가? YES</li>
<li>모든 코드가 필요한 코드인가? YES</li>
<li>가독성이 좋게 작성되었는가? YES</li>
<li>함수가 용도에 맞게 작동하는가? YES</li>
<li>함수와 변수의 명칭이 적합한가? YES</li>
<li>오류 처리가 되어 있는가? YES</li>
<li>테스트하기 쉬운 구조인가 YES</li>
<li>함수의 호출 빈도가 적당한가? YES</br>

</li>
</ol>
<blockquote>
<p>쿼리문 검증 함수 : <a href="">isQueryStatement()</a></p>
</blockquote>
<pre><code class="language-kotlin">    // SQL문이 쿼리인지 검증. 즉 SELECT로 시작하는지 확인
    private fun isQueryStatement(statement: String): Boolean {
        return statement.startsWith(&quot;SELECT&quot;) or statement.startsWith(&quot;select&quot;)
    }</code></pre>
<ol>
<li>적절한 위치에 있는가? YES</li>
<li>모든 코드가 필요한 코드인가? YES</li>
<li>가독성이 좋게 작성되었는가? YES</li>
<li>함수가 용도에 맞게 작동하는가? <strong><span style="color:#F7625A;">NO</span></strong></li>
<li>함수와 변수의 명칭이 적합한가? <strong><span style="color:#F7625A;">NO</span></strong></li>
<li>오류 처리가 되어 있는가? YES</li>
<li>테스트하기 쉬운 구조인가 YES</li>
<li>함수의 호출 빈도가 적당한가? YES</br>

</li>
</ol>
<blockquote>
<p>Line Number 생성 함수 : <a href="">generateLineNumber()</a></p>
</blockquote>
<pre><code class="language-kotlin">    // Line Count를 입력받으면 Line Number을 생성.
    private fun generateLineNumber(count: Int): String {
        val stringBuilder = StringBuilder()
        for (i in 1..count) {
            stringBuilder.append(&quot;$i\n&quot;)
        }
        return stringBuilder.toString()
    }</code></pre>
<ol>
<li>적절한 위치에 있는가? <strong><span style="color:#F7625A;">NO</span></strong></li>
<li>모든 코드가 필요한 코드인가? <strong><span style="color:#F7625A;">NO</span></strong></li>
<li>가독성이 좋게 작성되었는가? YES</li>
<li>함수가 용도에 맞게 작동하는가? YES</li>
<li>함수와 변수의 명칭이 적합한가? YES</li>
<li>오류 처리가 되어 있는가? YES</li>
<li>테스트하기 쉬운 구조인가 YES</li>
<li>함수의 호출 빈도가 적당한가? <strong><span style="color:#F7625A;">NO</span></strong></br>

</li>
</ol>
<h2 id="❓-목표를-정하자-what">❓ 목표를 정하자 WHAT</h2>
<ul>
<li>접근지정자와 변수명:<ul>
<li>변수명을 보고 의도를 파악하기 어렵다. <code>가독성</code></li>
<li>ViewModel의 상태가 캡슐화 되어 있지 않다. <code>객체지향</code></li>
</ul>
</li>
<li>onStatementChanged(): generateLineNumber() 함수를 과하게 많이 호출한다. <code>자원 낭비</code></li>
<li>isQueryStatement(): <ul>
<li>&quot;Select&quot; &quot;SELect&quot; 같은 키워드는 제대로 확인하지 못한다. <code>의도 실패</code></li>
<li>함수명이 의도를 명확하게 보여주지 못한다. <code>가독성</code></li>
</ul>
</li>
<li>generateLineNumber(): <ul>
<li>StringBuilder 객체를 불필요하게 선언했다. <code>자원 낭비</code></li>
<li>ViewModel이 LineUpdate 과정을 알 필요가 없다. <code>관심사</code></li>
</ul>
</li>
</ul>
<p>기능 구현에 너무 화이팅이 넘친 나머지 상당히 엉망인 코드가 탄생했다. 그 중 상당 부분은 개발 당시에도 문제점을 알았지만 시간에 쫓기는 바람에 수정하지 못했다. 다음 포스팅을 통해서 문제를 하나 하나 수정해보도록 하자! Repository의 실제 Refactoring Issue가 궁금하다면 아래의 참고를 확인하는 걸 추천한다 :)</p>
</br>

<h5 style="color:#FFFFFF8; text-align:right;">SQLI 프로젝트의 GitHub Repository가 궁금하다면? <a href="https://github.com/dev-ant/sql-interpreter-android" target="_blank">클릭</a></h5>

</br>

<hr>
<h2 id="📚-참고">📚 참고</h2>
<ul>
<li>F-LAB <a href="https://f-lab.kr/insight/android-dev-refactoring">안드로이드 앱 개발의 첫걸음: 프로젝트 리팩토링으로 성장하기</a></li>
<li>실제 프로젝트 이슈 <a href="https://github.com/dev-ant/sql-interpreter-android/issues/43">Improve editor model view readability and fun call frequency #43</a>
&lt;!--
Color Code
SkyBlue : #6897BB
OliveGreen : #9CC36E
Orange : #C89352
pink : #E16970
retroRed:#F7625A</li>
<li>-&gt;</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[지난 프로젝트 복습하기]]></title>
            <link>https://velog.io/@nter-developer/project-now-medicine-review-the-project</link>
            <guid>https://velog.io/@nter-developer/project-now-medicine-review-the-project</guid>
            <pubDate>Wed, 17 Apr 2024 01:36:03 GMT</pubDate>
            <description><![CDATA[<h2 id="발전-프로젝트를시작하며">발전 프로젝트를시작하며</h2>
<blockquote>
<p>더 나은 사람이 되기 위해선 어떻게 해야될까. </p>
</blockquote>
<p>다양한 방법이 있겠지만 그 중 제일 쉽고 효율적인 방법은 지난 날의 약점을 기록하고 보완하는 것이라 생각한다. 그런 의미에서 이전에 진행했던 캡스톤 디자인 프로젝트를 처음부터 다시 구현해보려 한다. 구현에 앞서 간단하게 무엇이 아쉬웠는지, 이제는 그 아쉬운 점을 어떤 식으로 보완할 수 있는지 정리해보고자 글을 쓴다.</p>
</br>

<h2 id="어떤-점이-부족했을까">어떤 점이 부족했을까</h2>
<blockquote>
<p>안드로이드 네이티브는 Kotlin으로 개발하도록 합시다...</p>
</blockquote>
<p>캡스톤 디자인 프로젝트를 마친 후에 제일 아쉬웠던 점이 바로 안드로이드 공식 언어인 Kotlin이 아닌 <code>Java</code>를 사용했다는 점이다. 당시에는 안드로이드 개발 겨우 2회차였고 그전까지는 Java Spring을 공부 중이였으니 갑작스럽게 Kotlin을 사용하기 어려운 상황이었다. 팀원들이 Kotlin을 아예 모르는 건 덤이였다. </p>
<p>지금 생각해보니 어차피 안드로이드 어플리케이션 개발의 대부분을 내가 했는데 Kotlin을 강행했어도 되지 않았나 생각이 들지만 사람이 미래를 알 수 없었으니 그저 아쉬울 뿐이다.</p>
<p>그렇다고 개발 언어만 아쉬웠던 것은 아니다. 아키텍처 패턴, 코드 분리, 동시성 문제, 화면 디자인, 테스트 코드의 부재, CI/CD 등 개발 당시에도 부족함을 느꼈으나 개발 진도를 위해서 많은 부분을 포기했다.</p>
</br>

<h2 id="어떻게-보완할-수-있을까">어떻게 보완할 수 있을까</h2>
<p><img src="https://velog.velcdn.com/images/nter-developer/post/fa481535-2aa1-439e-b256-3a60aa782186/image.png" alt="Old to New"></p>
<p><img src="https://velog.velcdn.com/images/nter-developer/post/68058618-27bd-4d78-ae76-94539b4fa0a0/image.png" alt=""></p>
<p>앞서 내가 생각했던 프로젝트의 약점 언어, 아키텍처 패턴 기타 등등을 현재의 내가 알고있는 가장 좋은 방법으로 구현하는 것이다. 같은 기능이지만 상황에 따라 ~ 온라인 기능에서 오프라인 기능으로 전환, 로컬 데이터 저장방법을 <code>SharedPreference</code>에서 <code>Room</code>이나 <code>DataStore</code>을 이용하는 등 여러 부분을 바꾸면서 구현하려한다. 1년 남짓한 시간이지만 안드로이드 개발 트렌드에도 어떤 변화가 있는지 많은 부분을 느끼면서 글을 남기려한다. 이전과 다르게 시간의 압박도 팀원의 페이스에 맞출 필요도 없으니 차근차근 오답노트처럼 진행하면 좋을 것 같다.</p>
</br>

<h5 id="프로젝트가-궁금하다면-now-medicine-and-advanced-now-medicine">프로젝트가 궁금하다면? <a href="https://github.com/dev-ant/advanced-now-medicine-android">now medicine</a> and <a href="https://github.com/Nter-developer/bravo-health-park-android">advanced now medicine</a></h5>
]]></description>
        </item>
        <item>
            <title><![CDATA[
Compose UI 도구 키트에 대해서 알아보자]]></title>
            <link>https://velog.io/@nter-developer/compose-ui-tool-kit</link>
            <guid>https://velog.io/@nter-developer/compose-ui-tool-kit</guid>
            <pubDate>Mon, 08 Apr 2024 06:11:40 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>이 글은 <a href="https://developer.android.com/courses/jetpack-compose/course?hl=ko">ANDROID 개발자를 위한 JETPACK COMPOSE</a> 코스를 바탕으로 작성되었습니다.</p>
</blockquote>
<h2 id="material-design">Material Design</h2>
<p>Compose는 <code>Material Design 2</code>와 <code>Material Design 3</code>를 지원한다. Material Theme을 구성해놓으면 Color Scheme, Shapes, Typography, Content를 설정해둘 수 있다. 어떻게 보면 XML을 사용할 때, <code>style.xml</code>을 설정한 것과 비슷한 느낌이다. 다만 XML이 아닌 <code>Kotlin</code>으로 작성된만큼 런타임 중에 변경하기 용이하고, 사용자 정의 테마도 구현할 수 있다.</p>
<pre><code class="language-kotlin">MaterialTheme(
    colorScheme = colorScheme,
    shapes = Shapes
    typography = Typography,
    content = content
)</code></pre>
</br>

<h2 id="scaffold-and-surface">Scaffold and Surface</h2>
<p><code>Scaffold</code>는 앱의 기본적인 레이아웃 구조를 정의하고, Material Design의 디자인 원칙에 따라 앱의 주요 컴포넌트를 배치한다. 조금 다르긴 하지만 페이지의 전체적인 UI 요소들을 제공한다는 점에서 XML로 치면 가장 바깥에 있는 <code>Layout</code>과 비슷한 역할인 것 같다. 다만 <em>Scaffold는 TopAppBar, Content, Bottom Navigation Bar, Floating Action Button처럼</em> 정말 <em>주요 컴포넌트를</em> 다룬다는 점에서 차이가 있다. 
</br></p>
<p><code>Surface</code>는 배경 색상, 모서리 반경, 그림자 등의 속성을 정의하며 UI 요소들을 담는 컨테이너 역할을 한다. Scaffold는 앱의 최상위 레벨에서 전체 레이아웃을 관리하고 Surface는 그보다 아래에서 하위 요소들의 시각적 요소를 관리하는 역할이라 보면 되겠다.
</br></p>
<h2 id="layouts-and-modifiers">Layouts and Modifiers</h2>
<p><code>Compose</code>에서는 Constraint, Linear ~ 등으로 <code>Layout</code>을 명시하지 않는다. 대신에 <em>Row, Column, Box, LazyRow, LazyColumn</em> 등을 사용해서 UI 요소들을 구성하고 정렬, 크기, 간격 등을 설정할 수 있다. <code>Modifier</code>는 UI에 다양한 추가 기능을 제공한다. 말 그대로 <em>수정자</em>의 기능을 가지는 것이다. 크기, 여백, 패딩, 배경, 클릭 이벤트 등의 속성을 설정할 수 있고, XML과는 다르게 하나의 Modifier에 <code>수정자.속성().속성()</code> 형식으로 구현된다. 개인적으로는 이런 방식이 XML의 방식보다 훨씬 편리하다고 느껴진다.
</br></p>
<pre><code class="language-kotlin">@Composable
private fun Greetings(
    modifier: Modifier = Modifier,
    names: List&lt;String&gt; = listOf(&quot;World&quot;, &quot;Compose&quot;)
) {
    Column(modifier = modifier.padding(vertical = 4.dp)) {
        for (name in names) {
            Greeting(name = name)
        }
    }
}</code></pre>
<p></br></br></p>
<h5 id="이-스터디의-github-repository가-궁금하다면-링크">이 스터디의 GitHub Repository가 궁금하다면? <a href="https://github.com/dev-ant/jetpack-compose-for-android-developers">링크</a></h5>
<hr>
<h2 id="참고">참고</h2>
<ul>
<li>Android Developers 공식 문서 <a href="https://developer.android.com/courses/pathways/jetpack-compose-for-android-developers-1?hl=ko">Compose 기본사항</a></li>
<li>Android Developers Channel &gt; <a href="https://www.youtube.com/watch?v=oNOCE-bUhVs&amp;t=26s">Powerful: Compose toolkit - MAD Skills</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[GitHub Actions을 통한 Unit Test시 발생한 문제 - No Tests Found]]></title>
            <link>https://velog.io/@nter-developer/project-sqli-trouble-shooting-1</link>
            <guid>https://velog.io/@nter-developer/project-sqli-trouble-shooting-1</guid>
            <pubDate>Mon, 08 Apr 2024 04:41:01 GMT</pubDate>
            <description><![CDATA[<h2 id="💥-no-tests-found">💥 No Tests Found</h2>
<p><code>GitHub Actions</code>를 이용하면 다양한 이점이 있다. Jenkins와 같은 다른 CI/CD 툴을 사용해본 적은 없지만 별도의 서버를 운영하거나 여러 언어를 사용해야되는 등 난이도가 있다고 들었다. 그에 비해 <code>GitHub Actions</code>는 yaml만 잘 작성하면 <a href="https://github.com/dev-ant/sql-interpreter-android">SQLI</a> 같이 작은 프로젝트에서는 충분히 쓸만하다. 이번에는 GitHub Actions를 사용해서 Pull Request에 자동으로 유닛 테스트 결과를 댓글로 남기려다가 발생한 <code>No Tests Found</code> 문제를 해결해보겠다.</p>
<p><img src="https://velog.velcdn.com/images/nter-developer/post/9a26a300-e392-4b2d-b83d-b1c608c669dc/image.png" alt=""></p>
</br>

<h2 id="🔎-원인-분석">🔎 원인 분석</h2>
<p>우선 내가 작성한 <code>Workflows</code> 파일을 살펴보자.</p>
<pre><code class="language-yml"># dev 브랜치로 Push, PR 발생시 test 진행 후 결과를 댓글로 남긴다
name: Android CI

on:
  push:
    branches:
      - dev
  pull_request:
    branches:
      - dev

jobs: 
  Unit-Test:
    runs-on: ubuntu-latest

    permissions:
      checks: write
      pull-requests: write

    steps:
      - name: Checkout the code
        uses: actions/checkout@v3

      - name: set up JDK 17
        uses: actions/setup-java@v3
        with:
          distribution: &#39;corretto&#39;
          java-version: &#39;17&#39;

      - name: set up Android SDK
        uses: android-actions/setup-android@v2

      - name: Grant execute permission for gradlew
        run: chmod +x gradlew

      - name: Run unit tests
        run: |
          ./gradlew test

      - name: Publish Test Results
        if: always()
        uses: EnricoMi/publish-unit-test-result-action@v2
        with:
          files: &quot;build/test-results/test/TEST-*.xml&quot;</code></pre>
</br>

<p>예상되는 원인을 우선 정리했다. 나름의 우선순위를 부여해서 하나씩 확인해보자.</p>
<ol>
<li><strong>yml 문법 (뛰어쓰기, 들여쓰기)</strong>: yml은 굉장히 예민한 문법을 가졌기 때문에 조심해야 된다. 대신 오류를 발견하기 쉬운만큼 제일 첫번째로 확인하자.</li>
<li><strong><code>./gradlew test</code> 명령어</strong>: test 명령어가 내 테스트 코드를 무시한다던지, 혹은 특별한 옵션을 사용해야되는지 확인해보자. </li>
<li><strong>내 테스트 코드 (Junit5)</strong>: 테스트 코드 자체는 임의로 작성한 간단한 테스트 코드이다. 테스트 로직보다는 명칭 위주로 생각해보자.</li>
<li><strong>Workflows Actions</strong>: 유닛 테스트 결과를 Pull Request에 댓글로 남기기 위해<code>EnricoMi/publish-unit-test-result-action@v2</code> 액션을 사용하고 있다. 어쩌면 프레임워크 호환 문제가 있을 수도 있다. Junit4는 괜찮은데 Junit5를 사용한 테스트 코드를 못 찾는다던지?</li>
</ol>
</br>

<h2 id="✨-문제-해결">✨ 문제 해결</h2>
<h3 id="❌-1-yml-문법-뛰어쓰기-들여쓰기">❌ 1. yml 문법 (뛰어쓰기, 들여쓰기)</h3>
<p>혹시 들여쓰기나 안 보이는 뛰어쓰기가 있을까봐 확인해봤는데 문제는 없다. 또 문법상의 오류였다면 Unit Test 자체가 실행이 안 됐을 거 같다. 실제로 Unit Test 자체는 잘 수행되고 다만 그 결과를 남기는 과정에서 문제가 있는 거 같다.
<img src="https://velog.velcdn.com/images/nter-developer/post/c066cc6b-f61a-4ac3-bfab-c07c97276602/image.JPG" alt=""></p>
<h3 id="❌-2-gradlew-test-명령어">❌ 2. ./gradlew test 명령어</h3>
<p>혹시나 몰라서 로컬 프로젝트에서 <code>./gradlew test</code> 명령어를 실행하고 GitHub Actions의 옵션 <code>debugUnitTest</code> <code>./gradlew testDebugUnitTest --tests com.csapp.sqli.viewmodel.EditorViewModelTest</code> 등 다양하게 변경해보았으나 역시 문제 없이 실행되었다.</p>
<h3 id="❌-3-내-테스트-코드-junit5">❌ 3. 내 테스트 코드 (Junit5)</h3>
<pre><code class="language-kotlin">import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test

class EditorViewModelTest {
    @Test
    fun onStatementChangedTest() {
        Assertions.assertEquals(4, 2 + 2)
    }
}</code></pre>
<p>테스트 로직 자체는 GitHub Actions 작동을 확인하기 위한 임의의 코드임으로 문제가 있을 수 없다. 함수명도 *Test로 문제 없다. 로컬에서도 GitHub Actions에서도 정상적으로 실행된다.</p>
<h3 id="✔️-4-workflows-actions">✔️ 4. Workflows Actions</h3>
<p>이렇게 갈피를 못 잡고 있던 도중 GitHub Actions의 로그를 확인해봤다. 아무런 Error였다면 표시를 해주기 때문에 조금 더 빨리 알 수 있었겠지만 Warning이여서 작동은 하지만 결과를 추출할 XML 파일을 찾지 못해서 No Test Found라고 인식하는 상황이었다. 경로에서 TEST-<em>.xml 파일을 찾을 수 없다는데 분명히 로컬에서도 테스트를 진행하면 `TEST-</em>.xml` 파일이 생성된다. 그렇다면 정답은 둘 중 하나다. 하나는 내가 경로를 정말 잘못 설정한 경우다. 이 경우에는 경로만 바르게 설정해주면 된다. 아니면 Action이 문제가 있어서 TEST-*.xml 파일을 인식하지 못하는 경우다. 이렇게 되면 내가 해결할 수 없다. Action을 바꾸던지 테스트 결과 Publish 자동화를 포기해야 한다. </p>
<pre><code>2024-04-01 02:08:35 +0000 - publish - WARNING - Could not find any files for build/test-results/test/TEST-*.xml</code></pre><p>다행히도 경로를 한번 더 확인해본 결과 <code>build/test-results/test/</code>가 아닌 <code>app/build/test-results/testDebugUnitTest/</code>에 파일이 생성되는 걸 확인했다. 아마도 경로를 설정할 때 공식 문서를 읽고 안드로이드 스튜디오가 아닌 인텔리제이 자바 프로젝트의 경로를 생각해서 설정한 뒤에 SQLI 프로젝트에서는 파일이 제대로 생성되는지만 확인한 것이 문제였다.</p>
<p><img src="https://velog.velcdn.com/images/nter-developer/post/8bce6583-ce6a-4b43-9f03-0374c0675105/image.png" alt=""></p>
<p><code>Test Result Publish</code>가 정상 작동하는 모습을 보는 속이 뻥 뚫린다. 사실 GitHub Actions가 제대로 작동 안 한다는 핑계로 Unit Test 코드 작성을 미루고 있었는데 이제는 더 이상 핑계가 없어져서 섭섭하기도 하다. 이제는 정말 정식으로 운영하고 있는만큼 안정성을 위해서라면 테스트를 추가하면서 내실을 다져야할 것 같다.</p>
</br>

<h5 style="color:#FFFFFF8; text-align:right;">SQLI 프로젝트의 GitHub Repository가 궁금하다면? <a href="https://github.com/dev-ant/sql-interpreter-android" target="_blank">클릭</a></h5>

</br>

<hr>
<h2 id="📚-참고">📚 참고</h2>
<ul>
<li>실제 프로젝트 이슈 <a href="https://github.com/dev-ant/sql-interpreter-android/issues?q=is%3Aissue+is%3Aclosed">Resolve the no tests found issue in the github action unit test #41</a> </li>
<li>실제 프로젝트 PR <a href="https://github.com/dev-ant/sql-interpreter-android/pull/40">[infra] Configure github actions unit test workflow #40</a></li>
<li>EnricoMi <a href="https://github.com/EnricoMi/publish-unit-test-result-action">publish-unit-test-result-action</a> GitHub Repository</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[How to Composable function]]></title>
            <link>https://velog.io/@nter-developer/compose-composable-fun</link>
            <guid>https://velog.io/@nter-developer/compose-composable-fun</guid>
            <pubDate>Fri, 05 Apr 2024 08:54:26 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>이 글은 <a href="https://developer.android.com/courses/jetpack-compose/course?hl=ko">ANDROID 개발자를 위한 JETPACK COMPOSE</a> 코스를 바탕으로 작성되었습니다.</p>
</blockquote>
<h2 id="composable-function">Composable function</h2>
<p>코틀린에서 이것이 Compose UI 함수입니다라는 것을 알리기 위해서는 <code>@Composable</code> 어노테이션을 붙이기만 하면 됩니다. 그렇게 되면 Compose 컴파일러가 자동으로 이 함수의 Data를 UI로 전환시켜 버립니다. 이런 함수를 <a href="">Composable function</a> 짧게는 <a href="">Composable</a>이라고 부릅니다. 이런 어노테이션을 이용하는 방식은 굉장히 쉽고 빠르며 재사용성을 올려줍니다. </p>
<p>특히 코틀린 코드로 작성할 수 있다는 점이 강력한 장점입니다. 예를 들면 <code>XML</code>에서는 불가능하던 For-Each문과 같은 반복문을 사용해서 여러 UI 요소를 손쉽게 생성할 수 있습니다. 혹은 If문을 사용하면 상태에 따라서 다른 UI 요소를 생성할 수도 있습니다.</p>
<pre><code class="language-kotlin">// For example... Just put annotation on it!
@Composable
fun GreetingForEach(names: List&lt;String&gt;) {
    Column {
        names.forEach { name -&gt;
            Greeting(name = name)
        }
    }
}</code></pre>
</br>

<h2 id="recomposition">Recomposition</h2>
<p>만약에 지금 그려진 UI을 수정해야 된다면, 단순히 함수에 다른 값을 넣어서 다시 실행하거나 혹은 함수 내부 상태를 변경하면 UI는 다시 그려집니다. 이것을 <a href="">Recomposition</a>이라고 합니다. 내부 상태를 변경하고 이 사실을 관찰하기 위해서는 우리는 <code>Mutable State</code>를 사용해야 하는데 Mutable State는 Kotlin 런타임과 통합되어 있는 변경 가능한 Object입니다.</p>
<p>개인적으로 생각했을 때 Compose와 XML을 통한 UI 구현의 제일 큰 차이점은 바로 수동적으로 UI를 업데이트해주지 않아도 된다는 것이다. 예를 들어서 <em>XML의 <code>EditText</code>의 Text가 변경될 때마다 <code>TextVeiw</code>의 Text를 업데이트 해주려 한다면, findViewById()를 통해서 EditText와 TextView를 불러오고 <code>onTextChanged()</code>를 구현해서 원하는 문자열을 <code>TextView.setText()</code>에 인자로 넘겨줘야 한다.</em> 이것을 수동적인 업데이트라고 한다면, Compose에서는 내부 상태가 변경되기만 한다면 자동으로 UI를 다시 그려준다. 즉 상태의 변화에 따라서 자동으로 업데이트가 이루어진다는 것이다. XML을 이용하던 입장에서는 신세계를 경험하는 듯한 편리함이었다.
</br></p>
<h2 id="recomposition-주의사항">Recomposition 주의사항</h2>
<p>다만 마법과 같이 Composable 내부에 변수를 생성해서 사용하는데 Recomposition 이후에도 변수 값이 보존되는 것은 아니다. 예를 들어서 MuatbleState()을 선언하고 Text 값을 저장하는데 사용했다면 Recomposition 이후에는 MutableState가 처음 정의된대로 초기화된다. 함수가 처음부터 다시 시작이라고 생각하면 되겠다. 이를 방지하기 위해서 이번 강의에서는 <code>rememeberSaveable()</code>과 <code>remember</code>을 사용해서 MutableState()가 Recompostion 이후에도 값을 보존할 수 있도록 프로그래밍한 것을 볼 수 있었다.</p>
<p>또 Recomposition은 편리한 기능이지만 그렇다고 남용해서는 안 된다고 강의에서 말하고 있다. UI를 처음부터 끝까지 다시 그린다는 것은 많은 자원을 잡아먹는 일이기 때문에 필요한 부분만을 업데이트하는 것이 중점인 것 같다. 특히 애니메이션과 같이 매 프레임마다 Recomposition이 필요하다면 Composable의 업데이트 부분을 가능한 좁게 하여서 속도를 보장해야 한다. 그렇지 않으면 하나의 Recomposition이 완료되기 전에 다음 Recomposition을 위해서 현재 Recomposition을 취소하기 때문에, 프레임이 드랍될 수도 있다.
</br></p>
<h2 id="3줄-요약">3줄 요약</h2>
<ol>
<li><code>@Composable</code> 어노테이션을 사용하면 쉽고 간편하게 Composable 함수를 만들 수 있다.</li>
<li>Composable은 내부 state의 변화를 감지하고 UI를 자동으로 업데이트 해준다. (Recompostion)</li>
<li>다만 Recomposition을 사용할 때는 필요한 부분만 업데이트해야 자원을 아낄 수 있다.</li>
</ol>
<p></br></br></p>
<h5 id="이-스터디의-github-repository가-궁금하다면-링크">이 스터디의 GitHub Repository가 궁금하다면? <a href="https://github.com/dev-ant/jetpack-compose-for-android-developers">링크</a></h5>
<hr>
<h2 id="참고">참고</h2>
<ul>
<li>Android Developers 공식 문서 <a href="https://developer.android.com/courses/jetpack-compose/course?hl=ko">ANDROID 개발자를 위한 JETPACK COMPOSE</a></li>
<li>Android Developers Channel &gt; <a href="https://youtu.be/fFLBCgoHHys">Less code: Composable functions - MAD Skills</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Compose 기본 사항에 대한 이해]]></title>
            <link>https://velog.io/@nter-developer/compose-essentials</link>
            <guid>https://velog.io/@nter-developer/compose-essentials</guid>
            <pubDate>Fri, 05 Apr 2024 06:03:38 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>이 글은 <a href="https://developer.android.com/courses/jetpack-compose/course?hl=ko">ANDROID 개발자를 위한 JETPACK COMPOSE</a> 코스를 바탕으로 작성되었습니다.</p>
</blockquote>
<h2 id="compose와-view의-차이">Compose와 View의 차이</h2>
<p><code>Compose</code>와 <code>View</code>는 많이 차이가 있습니다. View를 사용해서 UI를 구현하려면 XML로 UI를 정의하고 코드에서 연결하고 원하는 방식으로 작동시키기 위해서 함수를 설정해야 합니다. 하지만 Compose를 사용한다면 코틀린 코드만으로 충분히 UI를 구현할 수 있습니다.Compose는 어떻게(HOW)보다 무엇을(WHAT)에 초점이 맞춰져 있습니다. 이것이 Compose를 사용하기에 훨씬 직관적으로 만들어줍니다.</p>
<p>예를 들어서 간단한 앱을 만든다고 가정합시다. View를 이용하기 위해서는 XML로 UI의 모든 View를 작성하고 <code>Listener</code> 함수를 이용해 변화를 탐지하고 다른 Component의 상태를 변경하는 로직을 구현해야 합니다. 하지만 이렇게 상태가 변화함에 따라 뷰를 수동으로 업데이트하는 것은 오류가 발생하기 쉽게 만듭니다. 이런 작동 방식은 여러 상태가 변화하면 예상치 못한 충돌을 일으키기도 합니다. </p>
<p>이제는 앱을 Compose로 만든다고 생각합시다. Compose에서는 요소들을 XML로 작성할 필요 없이 Kotlin으로 바로 작성할 수 있습니다. 더 이상 XML을 Kotlin 코드로 연결할 필요가 없어 코드가 간결해집니다. 또한 Compose에서 UI 요소들은 오브젝트가 아닌 함수입니다. 즉 UI 요소를 참고하거나 그들을 호출함으로서 수정하는 것이 아닌 내가 넘기는 state 혹은 argument로 변경할 수 있게 된 것입니다. </p>
<p><img src="https://velog.velcdn.com/images/nter-developer/post/799061c8-30cf-498c-ba8d-b723cc8449be/image.png" alt="XMLVersusCompose"></p>
<p>위 사진은 Compose VS XML 스터디를 진행하면서 같은 UI를 구현하기 위한 코드를 비교하고자 들고온 것이다. 심지어 XML은 UI만 구현한 코드이고 Activity와 바인딩한 코드를 포함하면 +@가 된다. 반면에 Compose는 한 줄 추가하기만 하면...</p>
</br>

<h2 id="compose의-직관성">Compose의 직관성</h2>
<p>이것이 Compose의 강력한 점입니다. View와는 다르게 단순히 우리가 원하는 UI를 명확히 하고 UI 요소를 다시 표현하기 위해서 함수를 호출하고 State를 넘기면 그만입니다. </p>
<p>단순히 XML을 바인딩하는 코드를 없앤 것만 해도 만족스러운데, 이를 함수형으로 구현함으로서 UI 구현과 상태의 변경을 동시에 성취한 느낌입니다. <code>Compose</code>와 <code>XML</code>을 동시에 사용 중이지만 확실히 Compose가 손에 익을 수록 편리하다는 사실을 부정할 수가 없네요.
</br></p>
<h2 id="3줄-요약">3줄 요약</h2>
<ol>
<li><code>Jetpack Compose</code>는 HOW가 아닌 WHAT에 초점을 둔 UI 구현 방식이다.</li>
<li>Compose는 많은 불필요한 코드를 제거하고 코틀린의 장점을 UI 구현에 적용할 수 있다.</li>
<li><code>XML</code>은 이제 조금씩 놔줄 때가 되었다.</li>
</ol>
<p></br></br></p>
<h5 id="이-스터디의-github-repository가-궁금하다면-링크">이 스터디의 GitHub Repository가 궁금하다면? <a href="https://github.com/dev-ant/jetpack-compose-for-android-developers">링크</a></h5>
<hr>
<h2 id="참고">참고</h2>
<ul>
<li>Android Developers 공식 문서 &gt; <a href="https://developer.android.com/courses/jetpack-compose/course?hl=ko">ANDROID 개발자를 위한 JETPACK COMPOSE</a></li>
<li>Android Developers Channel &gt; <a href="https://youtu.be/4zf30a34OOA">Intuitive: Thinking in Compose - MAD Skills</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[면접 준비 - 자기 소개]]></title>
            <link>https://velog.io/@nter-developer/interview-self-introduction</link>
            <guid>https://velog.io/@nter-developer/interview-self-introduction</guid>
            <pubDate>Wed, 03 Apr 2024 04:58:12 GMT</pubDate>
            <description><![CDATA[<p>면접은 정말 중요하다. 아무리 자기소개서를 열심히 작성하고 Git을 예쁘게 꾸미고 포트폴리오를 구성해도 결국 대면했을 때 제대로 대답하지 못하거나 자신감 없는 모습을 보인다면 회사 입장에서는 망설이지 않을까 싶다. 나도 면접 경험이 많은 건 아니지만 최소한 나 자신 정도는 자신감 있고 소개하자! 라는 취지에서 글을 정리해봤다.</p>
<blockquote>
<p>이 글은 신입 안드로이드 개발자로 취업 준비를 하는 과정에서 이력서, 회사의 자기소개서 문항 작성 및 자가점검을 위해 작성한 질문들을 정리한 내용입니다. 자기소개서를 작성하는 데 도움이 되도록 노션에 각 질문에 대한 대답을 1000자 이내로 정리하는 것을 권장합니다.</p>
</blockquote>
<h2 id="1-자기소개서">1. 자기소개서</h2>
<ul>
<li>본인의 지원직무에 대한 이해를 구체적으로 기술하고, 본인이 해당 분야에 적합하다고 판단할 수 있는 근거를 사례 및 경험을 바탕으로 제시해주세요.</li>
<li>선택한 분야에 관심을 갖게 된 계기와 자신 있는 이유를 설명해주세요.</li>
<li>최근 사회 이슈 중 중요하다고 생각되는 한 가지를 선택하고 이에 관한 자신의 견해를 기술해 주시기 바랍니다.</li>
<li>지원한 동기를 구체적으로 설명하고, 입사하게 된다면 20년 후 회사 내에서 어떤 모습의 구성원이 되어 있을지 설명해주세요.</li>
<li>본인의 지원직무와 관련된 임무를 진행하면서 힘들고 어려운 문제가 발생하였음에도 포기하지 않고 임무를 완수한 일을 구체적으로 기술해주세요.</li>
<li>본인의 지원직무를 수행하기 위해서 어떤 방식으로 공부를 진행해왔는지 구체적으로 기술해주세요.</li>
<li>입사 후 포부를 구체적으로 작성해 주세요.</br>

</li>
</ul>
<h2 id="2-개발자-전용">2. 개발자 전용</h2>
<ul>
<li>본인이 생각하는 좋은 코드란 무엇인지 구체적으로 기술해주세요.</li>
<li>개발 최신 트렌드에 대한 정보를 입수하는 경로에 대해서 기술해주세요. </li>
<li>최근 가장 관심있는 기술 한가지를 선택하고 이에 관한 자신의 견해를 기술해주세요.</li>
<li>개발과 관련된 대외활동 경험에 대해서 기술해주세요.</li>
<li>자사의 어플리케이션을 사용한 경험과 피드백 사항을 구체적으로 기술해주세요.</li>
<li>가장 열정을 가지고 임했던 개발 프로젝트를 수행 과정 및 결과를 포함하여 소개해 주세요.</br>

</li>
</ul>
<h2 id="3-회사에게-궁금한-것">3. 회사에게 궁금한 것</h2>
<blockquote>
<p>아래의 목록은 제 관심사가 반영된 질문들입니다. 개인적으로 따로 준비하시기를 바랍니다.</p>
</blockquote>
<ul>
<li>시니어 개발자와 주니어 개발자가 소통하는 시간이 있는지 궁금합니다.</li>
<li>자사의 개발 수준을 높이기 위해서 특별한 노하우가 있는지 궁금합니다.</li>
<li>사내에 정착되어 있는 개발 문화에 대해서 알고 싶습니다 (코드 리뷰 혹은 코딩 스터디 동아리 등).</br></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[기술 면접 준비 - 프로젝트 경험]]></title>
            <link>https://velog.io/@nter-developer/tech-interview-project</link>
            <guid>https://velog.io/@nter-developer/tech-interview-project</guid>
            <pubDate>Thu, 28 Mar 2024 07:53:24 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>이 글은 신입 안드로이드 개발자의 입장에서 면접에 대비하기 위해 정리한 글입니다. 기본적인 내용만을 담고 있으니 각자 프로젝트에 사용된 기술 스택을 자신 있게 말할 수 있도록 연습하시는 걸 추천합니다. 또한 내가 답변한 내용에 대해서 꼬리를 물고 나올 수 있는 질문들을 따로 정리하시면 좋을 것 같습니다.</p>
</blockquote>
<h2 id="1-프로젝트-관련">1. 프로젝트 관련</h2>
<ul>
<li>가장 최근에 작업한 안드로이드 프로젝트의 컨셉과 사용된 기술 스택에 대해서 설명해주세요.</li>
<li>그 프로젝트에서 어떤 기능을 구현했나요? 그 기능을 왜 선택했나요?</li>
<li>프로젝트에서 어떤 어려움을 겪었나요? 어떻게 해결했나요?</li>
<li>특정 디자인 패턴이나 아키텍처를 사용한 경험이 있나요? (예: MVP, MVVM)</li>
<li>자주 사용하는 라이브러리나 프레임워크가 있나요?</br>

</li>
</ul>
<h2 id="2-문제-해결-및-디버깅">2. 문제 해결 및 디버깅</h2>
<ul>
<li>개발 중에 발생한 버그나 이슈를 해결한 경험이 있나요? 어떻게 해결했나요?</li>
<li>디버깅 도구를 사용해본 경험이 있나요? 어떤 도구를 사용했나요?</li>
<li>코드 리뷰를 통해 얻은 피드백이 있나요? 그 피드백을 어떻게 반영했나요?</br>

</li>
</ul>
<h2 id="3-팀-협업-및-커뮤니케이션">3. 팀 협업 및 커뮤니케이션</h2>
<ul>
<li>버전 관리 시스템을 사용해 협업한 경험이 있나요? (예: Git)</li>
<li>팀 프로젝트에서의 역할과 기여에 대해 설명해주세요.</li>
<li>팀원들과의 협업 시 어떤 도구를 사용했나요? (예: Slack, JIRA)</li>
<li>팀 내에서 발생한 의견 차이나 갈등을 어떻게 해결했나요?</br>

</li>
</ul>
<h2 id="4-향후-발전-가능성">4. 향후 발전 가능성</h2>
<ul>
<li>앞으로 공부하고 싶은 안드로이드 개발 관련 기술이 있나요?</li>
<li>현재 부족하다고 느끼는 부분이 있나요? 어떻게 보완할 계획인가요?</li>
<li>개인 프로젝트를 통해 스스로 학습하거나 실험한 경험이 있나요?</br>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[신입 개발자 이력서 작성하기]]></title>
            <link>https://velog.io/@nter-developer/how-to-write-dev-resume</link>
            <guid>https://velog.io/@nter-developer/how-to-write-dev-resume</guid>
            <pubDate>Wed, 21 Feb 2024 08:02:25 GMT</pubDate>
            <description><![CDATA[<h2 id="이력서가-뭔데-what">이력서가 뭔데? WHAT</h2>
<blockquote>
<p>나는 경력이 없다. 그야 신입이니까... 하지만 <strong>이력은 있다</strong>.</p>
</blockquote>
<p>본격적인 취업 준비를 위해 이력서를 쓰다가 깨달은 점이 있다. 나에게는 경력이 없다는 것이다. 처음에는 &quot;아니 이제 졸업인데 내가 이력이 어딨어서 이력을 쓰라는 거야?&quot; &quot;이력이 있는 사람만 뽑으면 신입은 어디가서 이력을 쌓나&quot; 생각을 했다. 하지만 이건 &quot;경력&quot;과 &quot;이력&quot;을 구분하지 못해서 벌어진 사소한 오해였다. </p>
<p><img src="https://velog.velcdn.com/images/nter-developer/post/6544648d-f8e3-4cf5-b369-019bdd2e1e97/image.png" alt=""></p>
<p>이력은 &quot;1. 지금까지 거쳐 온 학업, 직업, 경험 등의 내력 2. 많이 겪어 보아서 얻게 된 슬기&quot; 경력은 &quot;현재까지 직업상의 어떤 일을 해 오거나 어떤 직위나 직책을 맡아 온 경험 또는 그 내용&quot; 을 의미한다. 즉, 경력은 내가 어떤 회사에서 어떤 일을 했는지라면 이력서는 <strong>경력을 포함하여 내가 어떤 노력과 경험으로 무엇을 얻었는지~</strong> 까지 포함하는 것이다. </p>
</br>

<h2 id="이력서를-왜-써야하는데-why">이력서를 왜 써야하는데? WHY</h2>
<blockquote>
<p>회사님... 저랑 같이 일해보실래요. 저는 이런 매력이 있어요😘</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/nter-developer/post/e340e078-98b9-4db6-9bc4-f9c5730eedfa/image.JPG" alt=""></p>
<p>이력서를 쓰는 목적은 회사에게 나를 홍보하기 위함이다. 즉 회사에 나의 매력 포인트를 어필하는 문서인 것이다. 특히 신입 개발자의 경우에는 지원하는 업무와 관련해서 내가 지금까지 어떤 노력을 해왔고 어떤 것을 할 수 있으며 어떤 태도를 가졌는지를 <strong>근거를 가지고 설명</strong>해야한다. 또 나의 이력서를 읽는 사람은 내가 아니라 회사이기에 내 노력보다는 <strong>내 경험과 능력을 바탕으로 회사에 기여할 수 있는 것</strong>으로 회사에 자신을 홍보해야한다.</p>
</br>

<h2 id="그래서-어떻게-쓰는-건데-how">그래서 어떻게 쓰는 건데? HOW</h2>
<blockquote>
<p>이력서에 뚜렷한 양식은 없다. 정답도 없다.</p>
</blockquote>
<p>다만 면접복장이 자유라고 해서 런닝을 입고 가면 안되듯이, 정답이 없다고 해서 오답도 없는 건 아니듯이 필수 사항 / 권장 사항 / 기피 사항이 있다. 이력서 작성법은 여러 글과 실제 이력서, 이미 취업한 분들의 의견을 종합한 결과로는 다음과 같이 정리할 수 있다. 사실 어느정도 준비가 되어있다면 권장 입력 사항까지가 필수라고 보면 된다.</p>
<p><strong>필수 입력 사항</strong></p>
<ul>
<li><strong>개인 정보</strong>: 성명, 연락처, 이메일 주소, 생년월일.</li>
<li><strong>경력 사항</strong>: 근무한 회사, 해당 회사에서의 직위, 근무 기간. </li>
<li><strong>학력</strong>: 최종 학위, 전공, 입학 연도, 졸업 연도.</li>
<li><strong>기술 스택</strong>: 사용 가능한 프로그래밍 언어, 프레임워크, 라이브러리 등.</li>
<li><strong>프로젝트 경험</strong>: 참여한 프로젝트의 설명, 역할 및 기여 내용.</li>
<li><strong>기술적 역량</strong>: 프로그래밍 언어에 대한 숙련도, 알고리즘과 데이터 구조에 대한 이해 수준 등.</li>
<li><strong>자기소개서</strong>: 개발 관련 관심사와 열정, 협업 능력 및 문제 해결 능력을 증명할 수 있는 내용을 포함한 소개서. </li>
</ul>
<p><strong>권장 입력 사항</strong></p>
<ul>
<li><strong>GitHub 및 기술 블로그 링크</strong>: 현재의 실력 혹은 성장 과정을 증명할 수 있는 사이트의 링크  </li>
<li><strong>자격증 및 수료증</strong>: 관련 있는 자격증이나 수료증 혹은 어학 증명 서류</li>
<li><strong>개인 프로젝트</strong>: 개인적으로 진행한 프로젝트나 오픈 소스 기여 등.</li>
<li><strong>언어 및 소프트 스킬</strong>: 팀원과의 커뮤니케이션 능력, 문제 해결 능력 및 경험 등.</li>
</ul>
<p><strong>기피 입력 사항</strong></p>
<ul>
<li><strong>거짓 정보</strong>: 경력, 학력, 기술 스택을 과장하거나 허위로 기재하는 것. </li>
<li><strong>불필요한 개인 정보</strong>: 종교, 정치적 성향 등과 같은 개인적인 정보는 생략하는 것이 좋음.</li>
</ul>
<p><strong>이력서 체크리스트</strong></p>
<ul>
<li><strong>오타 및 문법 오류 확인</strong>: 이력서에 오타나 문법 오류가 없는지 다시 한 번 확인합니다. 기본적인 이미지를 유지하기 위해 중요합니다.</li>
<li><strong>내용 일관성 확인</strong>: 이력서에 기재된 정보들이 일관되고 정확한지 확인합니다. 이전에 작성한 내용과 모순되는 부분이 없는지 확인해야 합니다.</li>
<li><strong>연락처 및 개인 정보 확인</strong>: 연락처와 개인 정보가 정확하게 기재되어 있는지 확인합니다. 회사가 연락할 때 필요한 정보이므로 정확성이 중요합니다.</li>
<li><strong>포맷 및 레이아웃 확인</strong>: 이력서의 포맷과 레이아웃이 깔끔하고 읽기 쉬운지 확인합니다. 일관된 서식을 사용하여 전문적인 인상을 주어야 합니다. <em>(노션 같은 경우는 Pdf로 내보낼 경우 레이아웃이 깨지는 경우가 있어서 꼭 확인이 필요하다)</em></li>
<li><strong>경험과 기술 부분 강조</strong>: 신입 개발자의 경우 경험과 기술을 강조하는 것이 중요합니다. 프로젝트 경험과 언어/도구에 대한 기술을 명확하게 표시해야 합니다.</li>
<li><strong>링크 작동 확인</strong>: GitHub, 기술 블로그 혹은 포트폴리오 등의 링크가 정상적으로 작동하는지 확인합니다.</li>
</ul>
</br>

<p>그리고 내가 이력서를 작성한 순서는 아래와 같다. 나같은 경우는 프로젝트 경험 내용은 자기소개서에 짧게 녹여내고 구체적인 것은 포트폴리오 링크로 대체했다.</p>
<ol>
<li>필수/권장/기피 사항과 체크리스트를 조사한다.</li>
<li>객곽전인 필수/권장 사항을 모두 작성한다. (자기소개서를 제외한 전부가 된다)</li>
<li>가장 강하게 어필할 수 있는 <strong>나의 경험, 개발 외 역량</strong>을 메모장에 정리한다.</li>
<li>이력서를 작성할 플랫폼(PPT, Notion, Word 등)을 정한다.</li>
<li>자기소개서를 임의의 텍스트(e.g. <code>Lorem Ipsum</code>)로 구성한뒤 객관적인 필수/권장 사항을 모두 입력할 수 있게 적당한 페이지 수와 레이아웃을 구성한다.</li>
<li>나의 경험, 개발 외 역량을 바탕으로 자기소개서를 작성한다. </li>
<li>이력서의 임의의 텍스트를 자기소개서로 대체하고 지인들에게 내용 및 레이아웃의 피드백을 받아 수정한다. </li>
</ol>
</br>

<p>그렇게해서 완성된 나의 첫 이력서. 졸업을 일주일 앞둔 시점에서 수정하고 수정하고 갈아엎은 후에야 간신히 기본적인 틀을 갖췄다... 앞으로도 수정할 일이 많겠지만 사회인으로서 한발을 내딘 느낌이 들어 뿌듯함을 감출 수 없다. 비록 부족한 점이 많지만 앞으로는 경력과 프로젝트와 깊은 탐구 내용 등 많은 것을 채워나갔으면 좋겠다.</p>
</br>

<hr>
<h2 id="참고-문서">참고 문서</h2>
<p>zero-base <a href="https://zero-base.co.kr/event/media_insight_contents_BE_resume" target="_blank">신입 합격 백엔드 자기소개서 예시 공개! 백엔드 자소서 이렇게 쓰세요!</a> 
우아한 기술 블로그 <a href="https://techblog.woowahan.com/11998/" target="_blank">왕초보 신입 개발자의 우당탕탕 이력서 작성기</a>
인프런 inflearn 유튜브 <a href="https://www.youtube.com/watch?v=ifGUz43GjdQ" target="_blank">2곳 중 1곳은 무조건 합격하는 개발자 이력서 만들기 | 인프콘2023</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로젝트 규칙 정하기]]></title>
            <link>https://velog.io/@nter-developer/sqli-convention-pattern</link>
            <guid>https://velog.io/@nter-developer/sqli-convention-pattern</guid>
            <pubDate>Sat, 03 Feb 2024 13:46:40 GMT</pubDate>
            <description><![CDATA[<h2 id="컨벤션과-패턴이-뭐야-what">컨벤션과 패턴이 뭐야? WHAT</h2>
<blockquote>
<p><strong>개발 전에 정해야 할 것들이 있다.</strong></p>
</blockquote>
<p>컨벤션은 개발자들 사이에 일종의 약속된 규칙 또는 관례를 뜻하고 코드를 작성하고 구성하면서 일관성을 유지하고 가독성을 향상하는 데 도움을 준다. 소프트웨어 디자인 패턴은 특정 문제를 해결하기 위한 반복적이고 일반화된 해결책이다. 잘 적용된 패턴은 시스템의 구조를 조직화하고 코드의 재사용성, 유지보수성, 확장성을 향상한다. 둘 다 개발 효율을 올려주는 것들이지만, 컨벤션과 패턴을 유지하기 위해 드는 비용이 개발에 드는 비용을 잡아먹는다면 의미가 없다. 언제나처럼 <strong>상황에 맞게 적용하도록 하자.</strong>
</br></p>
<h2 id="꼭-정해야-되나-why">꼭 정해야 되나? WHY</h2>
<blockquote>
<p><strong>많은 시행착오를 겪고 싶다면 안 정해도 될지도?</strong></p>
</blockquote>
<p>이번 프로젝트는 1인 개발이기 때문에 많은 규칙은 필요 없다. 막말로 문서화를 아예 하지 않고 Build and fix 일명 주먹구구식으로 개발해도 괜찮을까 싶지만 프로젝트 중간에 개발 외적인 부분에 힘을 빼고 싶지 않았다. 실제로 이전 프로젝트에서 팀원들의 코딩 실력은 준수했는데 규칙이 없다 보니까 Git 브랜치 관리, PR 규칙, 코드 리뷰 등 상당히 어지러웠던 기억이 있다. 혼자 하는 개발이라도 조금만 지나면 내 코드도 읽기 힘들어지기 때문에 <strong>간단한 컨벤션은 정해두는 게 좋다.</strong>
</br></p>
<h2 id="어떻게-정했어-how">어떻게 정했어? HOW</h2>
<blockquote>
<p><strong>문서화 비용이 프로젝트를 잡아먹지 않도록 해라.</strong></p>
</blockquote>
<p>아래는 이번 프로젝트를 위해 정한 컨벤션, 패턴 등의 리스트이다. 참고로 말하자면 아키텍처 디자인 패턴과 프로젝트 패키지 구조는 서로 다르지만 연관되어 있다. 소프트웨어 디자인 패턴은 소프트웨어 시스템의 구조와 흐름을 조직화하는 방법을 설명하는 패턴이며, 프로젝트 디렉터리 구조는 소프트웨어 프로젝트의 파일과 디렉터리를 조직화하는 방법이다. 이번 같은 경우는 디자인 패턴을 정하고 나니 디렉터리 구조가 대강 잡혀서 아래와 같이 묶어뒀다. 이 외에도 Git branch protection rule, 개발에 사용할 resources (icon, vector asset), color (프로그램 전역에서 사용될 테마 색상), dimen 등도 미리 설정해두면 좋다.</p>
<p><img src="https://velog.velcdn.com/images/nter-developer/post/b0333009-9cd9-4a85-bbe9-265814cf4c35/image.JPG" alt=""></p>
<h4 id="-sqli-프로젝트-컨벤션-및-패턴">** SQLI 프로젝트 컨벤션 및 패턴**</h4>
<ul>
<li>아키텍처 디자인 패턴: <a href="">MVP (Model View Presenter)</a><ul>
<li>프로젝트 패키지 구조</li>
</ul>
</li>
<li>Git and GitHub<ul>
<li>Git commit message convention</li>
<li>GitHub Issue / PR templates</li>
<li>Git Branch 전략: <a href="">Git-flow</a></li>
</ul>
</li>
<li>Code convention<ul>
<li>명명 규칙 (Naming Convention)</li>
<li>들여쓰기, 주석 규칙</li>
</ul>
</li>
</ul>
</br>


<p>이 외에도 다양한 컨벤션 등이 있다. 규모가 큰 프로젝트를 진행하는 회사에서는 정말 세세한 것까지 통일하는 컨벤션이 존재하는 거 같다. 자세한 컨벤션과 시행착오에 대한 경험담을 읽고 싶다면 아래 참고 문서의 글을 보면 도움이 될 것이다. 이제부터는 본격적인 개발을 시작하게 되는데, 프로젝트 중반에 일어날 수 있는 트러블 슈팅, CI/CD, 개발, 테스트, 리펙터링 등 다양한 글로 다시 찾아오도록 하겠다! 그때까지 다들 <strong>화이팅</strong></p>
<p></br></br></p>
<h5 style="color:#FFFFFF8; text-align:right;">SQLI 프로젝트의 GitHub Repository가 궁금하다면? <a href="https://github.com/dev-ant/sql-interpreter-android" target="_blank">클릭</a></h5>

<hr>
<h2 id="참고-문서">참고 문서</h2>
<ul>
<li>우아한 기술 블로그 <a href="https://techblog.woowahan.com/15572/" target="_blank">표준 개발 환경 개선 되돌아보기</a></li>
<li>우아한 기술 블로그 <a href="https://techblog.woowahan.com/2553/" targer="_blank">우린 Git-flow를 사용하고 있어요</a></li>
<li>Android Developers <a href="https://developer.android.com/kotlin/style-guide?hl=ko"> Kotlin 스타일 가이드</a></li>
<li>Android Developers <a href="https://developer.android.com/topic/architecture" target="_blank">앱 아키텍처 가이드</a></li>
<li>인텔리전스랩스 이야기 <a href="https://www.intelligencelabs.tech/88407556-c76e-49ea-8df2-2140a80ba2ad" target="_blank">우당탕탕 프론트웹개발팀의 개발 문화 정착기</a></li>
</ul>
<!--https://velog.io/@wisdom08/%EB%82%98%EB%A7%8C%EC%9D%98-%EC%BB%A8%EB%B2%A4%EC%85%98-%EB%AC%B8%EC%84%9C-%EB%A7%8C%EB%93%A4%EA%B8%B0
-->

<!--Check point
1. Use link effectively?
2. Create Index successfully?
3. Contain WHY, WHAT, HOW?
4. Is Grammar correct?
5. Apply feedback?
-->

<!--Feedback with chatGPT
제목의 명확성: 각 글의 제목은 내용을 명확하게 전달해야 합니다. 현재 제목들은 각각 "프로젝트를 시작하며", "아이디어 구체화하기 (feat. 스크럼)", "프로젝트 규칙 정하기"로 서로 다른 주제를 다루고 있어서 각각의 내용을 파악하기 어려울 수 있습니다. 더 구체적이고 목적을 명확히 드러내는 제목을 선택하는 것이 좋습니다.

내용의 구조화: 각 글의 내용은 시작, 중간, 끝으로 구분되어 있으나, 각 부분의 흐름이 조금 불분명할 수 있습니다. 개발 프로젝트 관련 글이라면 일반적인 구조 (소개, 목적, 구현 방법 등)를 따르면 독자가 내용을 이해하기 쉬울 것입니다.

문체와 어투: 글의 문체와 어투가 가볍고 대화체에 가깝습니다. 기술 블로그에서는 보다 전문적이고 공식적인 어투를 사용하는 것이 바람직합니다. 또한, 예를 들어 "막말로 문서화를 아예 하지 않고 Build and fix 일명 주먹구구식으로 개발해도 괜찮으려나...?"와 같은 문장은 전문성을 잃을 수 있으므로 피하는 것이 좋습니다.

기술적 세부사항: 프로젝트에 대한 구체적인 기술적인 내용이 부족합니다. 예를 들어, 어떤 기술 스택을 사용할 것인지, 어떤 디자인 패턴을 채택할 것인지, 어떤 테스트 전략을 사용할 것인지 등의 내용이 자세히 다뤄져야 합니다.

실제 경험과 교훈: 개발 프로젝트를 다룰 때는 실제 경험과 교훈을 나누는 것이 중요합니다. 현재 글들은 경험을 언급하고는 있지만 보다 구체적으로 어떤 문제를 마주하고 어떻게 해결했는지, 무엇을 배웠는지 등을 나누면 독자들에게 더 가치 있는 정보를 제공할 수 있습니다.

기술 용어의 사용: 전문 용어의 사용이 부족하거나, 부적절한 용어가 사용되었습니다. 개발 프로젝트에 관한 글에서는 해당 분야의 용어를 적절하게 사용하는 것이 중요합니다.

도식화 및 그래픽: 프로젝트 구조, 프로세스 모델 등을 설명할 때 도식화된 그림이나 그래픽을 사용하면 이해하기 쉽습니다. 독자들이 글을 읽으면서 시각적으로 정보를 받을 수 있도록 하면 좋습니다.
-->
]]></description>
        </item>
        <item>
            <title><![CDATA[아이디어 구체화하기 (feat. 스크럼)]]></title>
            <link>https://velog.io/@nter-developer/project-sqli-plan</link>
            <guid>https://velog.io/@nter-developer/project-sqli-plan</guid>
            <pubDate>Thu, 25 Jan 2024 08:52:02 GMT</pubDate>
            <description><![CDATA[<h5><a href="https://velog.io/@nter-developer/posts" target="_blank" style="color:gray;">NterDev</a><h style="color:gray;"> > </h><a href="https://velog.io/@nter-developer/series/SQLI" target="_blank" style="color:gray;">SQLI</a></h5></br>

<h3 id="프로젝트-구현-목표-what">프로젝트 구현 목표? WHAT</h3>
<blockquote>
<p><strong>개발자의 DB 개념 학습을 위한 SQL Interpreter 안드로이드 어플리케이션입니다.</strong></p>
</blockquote>
<p>프로젝트를 진행할 때 초기에 아이디어를 구체화시켜놓은 것은 굉장히 중요합니다. 이 과정 없이 무턱대고 코드부터 짜게 된다면 어느 순간 길을 잃어버린 자신을 발견하게 될 것입니다. 아무리 작은 규모라도 프로젝트를 관통하는 컨셉만큼은 명확하게 문서로 정리해놓도록 합시다. 구체적으로 <strong>누군가를 위한 어떤 의도를 가진 어떤 기능의 프로그램</strong>인지 정리하는 걸 추천합니다. 아래는 포스팅을 위해서 <a href="https://github.com/dev-ant/sql-interpreter-android" target="_blank">SQLI 프로젝트</a>의 주요 기능을 간단하게 정리해보았습니다. 실제 요구사항 문서가 궁금하다면 <a href="https://github.com/dev-ant/sql-interpreter-android/blob/dev/docs/SRS/SQLI-SRS-KR.md" target="_blank">SQLI SRS</a>를 참고해주세요.</p>

<ol>
<li><p><strong>SQL 문 해석 및 실행 기능</strong>: 사용자가 입력한 SQL 쿼리를 해석하고 실행하여 결과를 반환하는 기능을 구현합니다.</p>
</li>
<li><p><strong>사용자 인터페이스(UI) 개발</strong>: 사용자가 쿼리를 입력할 수 있는 인터페이스를 구현하여 사용자 편의성을 높입니다.</p>
</li>
<li><p><strong>에러 핸들링 및 안정성</strong>: 잘못된 쿼리 입력이나 예상치 못한 상황에 대비하여 적절한 에러 핸들링을 구현하여 안정성을 확보합니다.
</br></br></p>
</li>
</ol>
<h3 id="스크럼을-이용하자-how">스크럼을 이용하자 HOW</h3>
<p><img src="https://velog.velcdn.com/images/nter-developer/post/c595bbaa-1f2f-4130-a3da-5083fb6cbd12/image.PNG" alt=""></p>
<blockquote>
<p><strong>상황에 맞는 프로세스 모델은 개발 효율을 높여줍니다.</strong></p>
</blockquote>
<p><a href="https://ko.wikipedia.org/wiki/%EC%86%8C%ED%94%84%ED%8A%B8%EC%9B%A8%EC%96%B4_%EA%B0%9C%EB%B0%9C_%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4" target="_blank">소프트웨어 프로세스 모델</a>을 사용하면 <strong>개발 활동 관리 추적, 일정 및 예산 관리, 의사소통 관리</strong> 등 다양한 이점이 있습니다. 대표적인 모델은 폭포수, 반복진화형, 에자일이 있습니다. 필자는 여러 프로젝트를 진행하면서 폭포수와 스크럼 모델을 경험해봤고 이번 프로젝트에서는 에자일 모델 중 하나인 <a href="https://ko.wikipedia.org/wiki/%EC%8A%A4%ED%81%AC%EB%9F%BC_(%EC%95%A0%EC%9E%90%EC%9D%BC_%EA%B0%9C%EB%B0%9C_%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4)" target="_blank">스크럼(scrum)</a> 프레임워크를 사용하기로 했습니다. </p>

<p>물론 스크럼 마스터, 프로덕트 오너, 개발팀의 역할을 한명이서 하는만큼 어느정도 수정이 필요합니다. 개발 규모를 감안하여 진행 방식을 아래와 같이 수정하기로 했습니다. 수정한 프로세스 모델을 그림으로 표현해봤습니다.</p>

<ol>
<li><p><strong>느슨한 회의 주기</strong>: 개발 진도와 일정을 고려하여 <strong>스크럼 회의(그림에서는 셀프 피드백)를 주 2회</strong>로 변경합니다. </p>
</li>
<li><p><strong>보다 엄격한 변경</strong>: 개발자의 역량을 고려하여 요구사항 변경 및 추가에 있어 <strong>기존 애자일 원칙보다 엄격하게</strong> 판단하도록 합니다. </p>
</li>
<li><p><strong>1인 3역</strong>: 셀프 피드백 및 스프린트 회고시 개발자 관점에서 <strong>개발자의 역량</strong>, 스크럼 마스터 관점에서의 <strong>장애 요소 확인</strong>, 프로덕트 오너 관점에서 서비스 사용자의 <strong>니즈 분석</strong>을 시간과 역할을 분배하여 진행합니다.
</br></br></p>
</li>
</ol>
<h3 id="스크럼을-선택한-이유-why">스크럼을 선택한 이유 WHY</h3>
<blockquote>
<p><strong>스크럼은 잠재파워를 썼다! 효과는 굉장했다!</strong></p>
</blockquote>
<p>학부 프로젝트에서 Scrum 여러번 경험해봤습니다. 특히 교내 공모전 프로젝트에서는 스크럼 마스터, 프로덕트 오너, 개발팀, 스탠드업 미팅 등 꽤 높은 수준으로 스크럼을 진행했습니다. 초반에는 새로운 프로세스 모델과 잦은 회의에 피로를 느꼈지만 적응을 하니 주기적인 소통과 피드백, 유연한 접근 방식이 주는 이점을 크게 느꼈습니다. 물론 이름만 스크럼이고 사실상 주먹구구식 (Build and fix)인 프로젝트도 있었습니다. (과정도 결과물도 최악이었습니다...) 따라서 성공담과 경험당 그리고 현재의 상황을 염두에 두고 스크럼을 고쳐쓰면 효율적일 것이라는 판단을 내렸습니다.</p>

<ol>
<li><p><strong>확장 및 변화 가능성</strong>: 1인 프로젝트인만큼 요구사항 변동이 빈번할 것으로 예상됩니다. 내 요구사항이 곧 프로젝트의 요구사항입니다.</p>
</li>
<li><p><strong>동기부여</strong>: 고정된 짧은 주기로 스프린트를 반복함으로써 지속적으로 성취감을 줍니다. 1인 개발자의 의욕이 감소하는 상황을 방지합니다.</p>
</li>
<li><p><strong>스크럼 경험 유무</strong>: 스크럼을 이미 경험해봤고, 장점을 뚜렷하게 느꼈습니다.
</br></br></p>
</li>
</ol>
<h5 style="color:#FFFFFF8; text-align:right;">SQLI 프로젝트의 GitHub Repository가 궁금하다면? <a href="https://github.com/dev-ant/sql-interpreter-android" target="_blank">클릭</a></h5>

<hr>
<h3 id="참고-문서">참고 문서</h3>
<ul>
<li>카카오엔터프라이즈 <a href="https://tech.kakaoenterprise.com/65" target="_blank">기술 문서 작성 5단계</a></li>
<li>DropBox <a href="https://experience.dropbox.com/ko-kr/resources/how-to-create-a-project-plan" target="_blank">실패 없는 프로젝트 기획의 단계</a></li>
<li>AWS <a href="https://aws.amazon.com/ko/what-is/sdlc/" target="_blank">소프트웨어 개발 수명 주기(SDLC)란 무엇인가요?</a></li>
<li>Asana <a href="https://asana.com/ko/resources/what-is-scrum" target="_blank">스크럼의 정의와 장점</a></li>
<li>MOBINSIBE <a href="https://www.mobiinside.co.kr/2023/07/18/scrum-model/" target="_blank">개발 방법론: 스크럼(Scrum) 모델이란?</a></li>
</ul>
<!--
https://kka7.tistory.com/85
https://jinhojapan.tistory.com/72
-->

<!--    Posting point
        1. Use link effectively

-->

<!--    Color Code
        SkyBlue : #6897BB
        OliveGreen : #9CC36E
        Orange : #C89352
        Pink : #E16970
-->

<!-- 그라운드 룰
-->
]]></description>
        </item>
        <item>
            <title><![CDATA[프로젝트를 시작하며]]></title>
            <link>https://velog.io/@nter-developer/project-sqli-prologue</link>
            <guid>https://velog.io/@nter-developer/project-sqli-prologue</guid>
            <pubDate>Tue, 23 Jan 2024 07:32:58 GMT</pubDate>
            <description><![CDATA[<h5 style="display:block;">
  <a href="https://velog.io/@nter-developer/posts" target="_blank" style="display:inline">프로젝트</a>   
  <h> > </h>
  <a href="https://velog.io/@nter-developer/series/SQLI" target="_blank">SQLI</a>
</h5>

<h3 id="시작한-이유-why">시작한 이유? WHY</h3>
<blockquote>
<p><strong>재밌다! 근데 이건 배운 적이 없는데?</strong></p>
</blockquote>
<p style="margin-left:20px;">어느덧 졸업을 앞둔 컴퓨터공학도가 되어버렸다. 학교에 다니면서 교내 공모전, 개발 동아리, 캡스톤 디자인 프로젝트 등을 위해 안드로이드, 스프링 등 다양하게 개발을 해봤다. 엄청난 프로젝트는 아니었지만 개발을 하면서 첫 번째로 느낀 건 <strong><i>개발이란 건 재밌구나!</i></strong> 였고 두 번째로는 <strong><i>이런 건 배운 적이 없는데?</i></strong> 였다. 학교에서 자바, 자료구조, 알고리즘 등은 배웠어도 실제 개발을 해본 적이 없는 나에게 버전 관리, 프레임워크 등은 흥미로운 숙제 거리로 다가왔다.</p>

<blockquote>
<p><strong>지금 알고 있는 걸 그때도 알았더라면...</strong> </p>
</blockquote>
<p style="margin-left:20px;">학부생으로서 프로젝트를 진행하면서 늘 느꼈던 게 <strong><i>지난 프로젝트가 부끄럽다</i></strong>는 거였다. 당시에는 이것저것 공부하면서 잘 만들었다고 생각했지만, 다음 프로젝트가 되면 <span style="font-size:12px;">저번 프로젝트는 테스트 코드도 없었고... git log도 엉망이었고... 디자인 패턴도 없었고...</span> 많은 아쉬움이 생겨났다. 그래서 졸업을 앞둔 지금 <strong>포트폴리오</strong> 겸 <strong>플레이스토어 출시</strong>를 목적으로 지금까지 공부한 여러가지를 전부 적용해서 안드로이드 프로젝트를 진행하려 한다.</p></br></br>

<h3 id="무엇을-만들어-what">무엇을 만들어? WHAT</h3>
<blockquote>
<p><strong>SQL Interpreter 안드로이드 어플리케이션</strong> </p>
</blockquote>
<p style="margin-left:20px;">이번 프로젝트의 목표는 <strong>SQL</strong> 파일을 안드로이드에서 실행시켜주는 <strong>어플리케이션</strong>으로 정했다. 구현 자체야 오프라인만으로 가능하고 SQLite를 사용하면 간단할 듯하지만 간단하기만 해서야 이 프로젝트의 의미가 없다. 이전 프로젝트에서의 부족함에 대한 일종의 설욕전과 동시에 성장을 증명하기 위해서는 과감해질 필요가 있다.</p>

<blockquote>
<p><strong>조금 더 욕심을 담아서... Greedy</strong> </p>
</blockquote>
<p style="margin-left:20px;">CI/CD, Git 브랜치 관리, 트러블 슈팅 과정 등도 세세하게 기록할 예정이다. 다만 포트폴리오에 올리려면 무엇보다 <strong>추가적인 기능</strong>이 필요하다. <a href="https://velog.io/@nter-developer/project-sqli-plan" target="_blank">구체화 포스팅</a>에서 다루겠지만 <strong>SQL 키워드 하이라이트, 더미 데이터 생성, 고유 명령어 구현</strong> 등 여러 가지 계획이 있다. 3rd party 라이브러리를 이용하거나 직접 라이브러리를 만들어보는 것도 좋은 경험이 될 것 같다.</p></br></br>

<h3 id="어떻게-만들게-how">어떻게 만들게? HOW</h3>
<blockquote>
<p><strong>공부를 했으면 티를 내야지</strong> </p>
</blockquote>
<p style="margin-left:20px;">마지막 프로젝트였던 캡스톤 디자인 이후로 많은 공부를 했다. 제일 큰 변화는 개발 언어를 자바에서 코틀린으로 바꿨고, 이번 프로젝트도 <strong>코틀린</strong>으로 개발할 예정이다. 그뿐만 아니라 <strong>SOLID, 안드로이드 개발 패턴, Compose, GitHub Actions</strong> 등 다양하게 연습해왔다. 물론 공부한 내용 중 1인 규모의 작은 프로젝트에 굳이 필요없는 것들도 많다. 하지만 이번 프로젝트의 목표가 성장과 증명을 포함한만큼 어깨에 힘을 잔뜩 들여서 만들어보도록 하겠다. </p></br></br>

<p style="color:#FFFFFF88;" align="right">SQLI 프로젝트의 GitHub Repository가 궁금하다면? <a href="https://github.com/dev-ant/sql-interpreter-android" target="_blank">클릭</a></p> 


<!--Check point
1. Use link effectively?
2. Create Index successfully?
3. Contain WHY, WHAT, HOW?
4. Is Grammar okay?
-->

<!--Color Code
SkyBlue : #6897BB
OliveGreen : #9CC36E
Orange : #C89352
pink : #E16970
-->
]]></description>
        </item>
    </channel>
</rss>