<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>spring-yong.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Sun, 23 Nov 2025 02:49:47 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>spring-yong.log</title>
            <url>https://velog.velcdn.com/images/spring-yong/profile/850ee025-1e51-43d2-adf9-2acea8ab5a2a/image.webp</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. spring-yong.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/spring-yong" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[OWASP - uncrackable 2 풀이 ]]></title>
            <link>https://velog.io/@spring-yong/OWASP-uncrackable-2-%ED%92%80%EC%9D%B4</link>
            <guid>https://velog.io/@spring-yong/OWASP-uncrackable-2-%ED%92%80%EC%9D%B4</guid>
            <pubDate>Sun, 23 Nov 2025 02:49:47 GMT</pubDate>
            <description><![CDATA[<p>OWASP에서 제공하는 Android 모의 워게임 중, 
uncrackable 2를 풀이해보았습니다. </p>
<h3 id="uncrackable-2">uncrackable 2?</h3>
<p>우선 문제 풀이를 위해 에뮬레이터에 앱을 설치한 후 실행시켜보았습니다. </p>
<img src="https://velog.velcdn.com/images/spring-yong/post/e9c4349c-100d-49cc-ac8f-0ad5dfe98a46/image.png" width = "40%" height="40%">

<p>제가 사용중인 에뮬레이터는, Frida 사용을 위해 루팅을 진행한 상태라 
앱에 접근하면 곧바로 Root감지 팝업 발생 후 앱이 종료되는 모습을 볼 수 있습니다. </p>
<h3 id="jadx를-통한-apk-디컴파일">Jadx를 통한 APK 디컴파일</h3>
<p>어플리케이션 정적 분석을 위해, JADX를 통해 APK 파일을 디컴파일 해보겠습니다. </p>
<img src="https://velog.velcdn.com/images/spring-yong/post/90c9cf06-386c-4f26-ad8e-827e695d6c20/image.png" width = "40%" height="40%">

<p>디컴파일 후 코드 구조를 확인해보니, 바로 MainActivity가 존재하는 것을 확인했습니다. </p>
<p>그렇다면 MainActivity의 코드 구조를 확인해보겠습니다. </p>
<img src="https://velog.velcdn.com/images/spring-yong/post/150d520a-72cf-4595-9f2e-32be04eb69cb/image.png" width = "70%" height="60%">

<p>가장 주가 될 함수인 onCreate와 a, verify 그리고 init 이라는 메서드가 존재합니다. </p>
<p>init 메서드는 특이하게 구현체가 없네요 .. 
확인해보니 <strong>&#39;native&#39;</strong> 예약어를 사용한 메서드네요. 
JNI를 통해 <strong>타 언어로 작성된 함수</strong>임을 알 수 있습니다. (<a href="https://velog.io/@spring-yong/JNI%EB%9E%80">JNI란?</a>)</p>
<p>JNI 파트는 우선 넘어가고, 다시 코드를 분석해보겠습니다. </p>
<img src="https://velog.velcdn.com/images/spring-yong/post/ff72d0af-d129-4abf-8241-c473eb1a4da2/image.png" width = "70%" height="60%">

<p>가장 먼저 a 메서드 입니다. 
아 ... 이녀석이 제 루팅을 감지하고 바로 앱을 종료시킨 버릇없는 녀석이군요.
위에서 만난 루팅 탐지 팝업과 동일한 형태를 정의하고, 호출하는 것을 확인할 수 있습니다. </p>
<p>이번 문제 풀이에서는 사용하지 않으나, Frida를 통해 onClick 메서드를 후킹하면 루팅 탐지 팝업을 무력화시킬 수 있을 것 입니다. </p>
<img src="https://velog.velcdn.com/images/spring-yong/post/448db554-048b-4cd7-b77e-67148ae400f9/image.png" width = "70%" height="60%">

<p>아래에 위치한 verify 메서드를 확인해보니, m 객체의 a메서드를 호출한 결과값으로 성공 팝업을 출력하는 것을 볼 수 있습니다. </p>
<img src="https://velog.velcdn.com/images/spring-yong/post/c685dd6a-bdb6-484a-97dd-637e0c52c0ed/image.png" width = "70%" height="60%">
a 메서드의 선언부를 확인해보겠습니다. 
JNI를 통해 선언한 bar 메서드를 그저 반환하고 있음을 확인할 수 있습니다. 

<p>JNI로 선언된 코드를 확인해보겠습니다. 
<img src="https://velog.velcdn.com/images/spring-yong/post/b7a4174e-9e2d-4b63-ae67-b11c4fc65048/image.png" width = "70%" height="60%">
우선, JNI는 시스템의 아키텍처에 따라 필요한 native 파일이 달라지기 때문에, 제 아키텍처와 맞는 폴더에 선언된 .so 파일을 Export 해주겠습니다. </p>
<p>그 이후, IDA를 통해 정적 분석을 해보겠습니다.
<img src="https://velog.velcdn.com/images/spring-yong/post/6ff798a7-f6b8-4ddd-989f-096de392cf93/image.png" width = "70%" height="60%">
IDA를 통해 .so 파일에 선언된 함수들을 보니, 
가장 아래에 JNI 네이밍 규칙에 맞게 CodeCheck 클래스의 bar 메서드가 선언되어 있음을 확인할 수 있습니다.</p>
<p>해당 함수를 디컴파일 해보겠습니다.
<img src="https://velog.velcdn.com/images/spring-yong/post/0354b281-937e-4207-a91a-721ff90b5c63/image.png" width = "100%" height="100%"></p>
<p>문자열을 비교하는 코드들이 있고, 바로 Flag를 확인할 수 있습니다. </p>
<p><img src="https://velog.velcdn.com/images/spring-yong/post/4227b290-0afe-43bd-ae2b-2ea6f3e56147/image.png" alt=""></p>
<p>성공</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JNI란 ?]]></title>
            <link>https://velog.io/@spring-yong/JNI%EB%9E%80</link>
            <guid>https://velog.io/@spring-yong/JNI%EB%9E%80</guid>
            <pubDate>Mon, 03 Nov 2025 06:47:10 GMT</pubDate>
            <description><![CDATA[<h3 id="jni란--">JNI란 .. ?</h3>
<p>Java Native Interface의 약자입니다.
Java는 JVM이라는 독립적인 플랫폼을 통해 컴파일 / 동작하기 때문에, 하드웨어에 직접 <strong>접근하여 코드를 실행하는 네이티브 코드</strong>의 사용이 어렵습니다.</p>
<p>이에 JAVA &lt;-&gt; 네이티브 코드끼리 서로 호출할 수 있도록 도와주는 프레임워크가 JNI 입니다. </p>
<h4 id="왜-쓸까">왜 쓸까?</h4>
<p>JNI를 사용하는 이유는 크게 세가지가 있습니다. </p>
<ul>
<li>성능 최적화 </li>
<li>기존에 존재하는 라이브러리 사용 (플랫폼 특화 라이브러리 등)</li>
<li>플랫폼 종속 기능</li>
</ul>
<h4 id="사용-방법">사용 방법</h4>
<pre><code class="language-java">public class JniTest {
    public native void nativeMethod();

    static {
        System.loadLibrary(&quot;jni_test&quot;);
    }

    public static void main(String[] args) {
        new JniTest().nativeMethod();
    }
}</code></pre>
<p>위 코드처럼, 예약어 &#39;native&#39; 를 사용하게 되면 해당 메서드가 네이티브 라이브러리에서 구현됨을 의미합니다. </p>
<p>객체 생성 후 nativeMethod 메서드를 호출하게 되면, 네이티브 코드가 호출됩니다.</p>
<p>네이티브 코드를 호출하는 JAVA 코드를 먼저 작성한 이유는, 
JNI 연결을 위한 헤더파일을 만들 때, Java의 파일이 지정되어야 하기 때문입니다. </p>
<p>그럼 다음으로 네이티브 코드를 작성하는 방법은 다음과 같습니다. </p>
<pre><code class="language-bash">javac -h . JniTest.java</code></pre>
<p>위 bash 코드를 실행하게 되면, JniTest.h 파일이 생성됩니다.</p>
<pre><code class="language-c">#include &lt;jni.h&gt;
#include &lt;stdio.h&gt;
#include &quot;JniTest.h&quot;

JNIEXPORT void JNICALL Java_JniTest_sayHello(JNIEnv *env, jobject obj) {
    printf(&quot;C에서 호출됨 !\n&quot;);
}</code></pre>
<p>위에서 만든 헤더파일을 참조하는 C언어 코드를 다음 형식과 같이 작성합니다.</p>
<p>이후 만든 파일들을 빌드해주면 완료입니다.
플랫폼 별 명령어가 다르니, 참고 부탁드립니다. </p>
<p><strong>MAC or Linux</strong></p>
<pre><code class="language-bash">gcc -shared -fpic -o libjnitest.so -I&quot;$JAVA_HOME/include&quot; -I&quot;$JAVA_HOME/include/darwin&quot; JniTest.c</code></pre>
<p><strong>Windows</strong></p>
<pre><code class="language-bash">gcc -shared -o jnitest.dll -I&quot;%JAVA_HOME%\include&quot; -I&quot;%JAVA_HOME%\include\win32&quot; JniTest.c</code></pre>
<p>이후 자바 코드를 실행해보면, 원하는 출력 결과가 보이는 것을 확인할 수 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[mobilehacking.kr Project App 풀이]]></title>
            <link>https://velog.io/@spring-yong/mobilehacking.kr-Project-App-%ED%92%80%EC%9D%B4</link>
            <guid>https://velog.io/@spring-yong/mobilehacking.kr-Project-App-%ED%92%80%EC%9D%B4</guid>
            <pubDate>Mon, 03 Nov 2025 05:44:08 GMT</pubDate>
            <description><![CDATA[<h3 id="mobile-hackingkr">Mobile hacking.kr</h3>
<h4 id="project-app">Project App</h4>
<p>처음 사이트에 가입한 후 가장 먼저 풀어본 문제이다. </p>
<p>우선, apk 분석을 위해 jadx로 apk 파일을 열어보았다.</p>
<p>Android App을 구성할 때에, 보통 가장 축이 되는 Activity의 이름을 
Main Activity라고 짓는다. </p>
<p>문제의 APK의 MainActivity를 열어보자
<img src="https://velog.velcdn.com/images/spring-yong/post/60a2b386-aa6f-4852-bb0b-c10fbbadd870/image.png" alt=""></p>
<p>플래그가 존재할 것 같은 수상한 함수를 발견했다. 
함수의 로직을 직역하면, 다음으로 표현 가능하다.</p>
<ul>
<li>decodeSecret 함수를 호출한 결과값과 serialEditText에 입력한 값이 동일한가?<ul>
<li>Yes : Correct 출력</li>
<li>No  : Incorrect 출력 </li>
</ul>
</li>
</ul>
<p>editText의 getText 메서드는 그저 입력된 값을 가져오는 코드이니, 
decodeSecret 메서드의 로직을 확인해보겠다.</p>
<p><img src="https://velog.velcdn.com/images/spring-yong/post/f34f5187-ad0a-460e-9c0f-ca71f45cc319/image.png" alt=""></p>
<p>decodeSecret의 코드이다. 
R.raw.secret의 값을 가져와서, Base 64로 디코딩하여 반환하고 있다.</p>
<p>그렇다면 R.raw.secret의 값이 Flag일까 ? 
-&gt; 그렇지 않다 !!</p>
<p>안드로이드는 키값을 직접 하드코딩 하는 행위를 권장하지 않는다.
그렇기 때문에 보편적으로, 리소스 파일에 텍스트나 이미지를 저장하고 Key값을 통해 리소스를 불러오는 형식을 주로 사용한다. </p>
<p>그렇기 때문에 R.raw.secret의 경우에도 단순한 Key에 불과하며, 저장된 값은 Int형 변수이다. </p>
<p>따라서, 우리는 Apk의 리소스 파일 중, R.raw.secret에 해당하는 파일을 직접 찾아야 한다. </p>
<p><img src="https://velog.velcdn.com/images/spring-yong/post/a7a897ea-7a24-490f-bca3-2ee7f955d5f8/image.png" alt=""></p>
<p>jadx를 통해 APK의 리소스 폴더에 접근해보니, 찾던 secret 파일이 보인다. 
바로 열어서 Flag를 확인하면 된다.</p>
<p><strong>그렇지 않았다 ....</strong> 
내 경우, Mac 환경으로 문제풀이를 진행하고 있는데 그래서인지 jadx로 리소스 파일에 직접 접근하려고 하니, jadx 자체가 멈추며 강제 종료되는 현상이 발생했다. (다른 Windows 사용자들에겐 발생하지 않은 이슈이다.)</p>
<p>이에 다른 툴을 추가로 사용하여 해결했다. </p>
<p><img src="https://velog.velcdn.com/images/spring-yong/post/6ce47605-16fa-4224-8929-039d7bbd5168/image.png" alt="">
<strong>ApkTool</strong>
APK의 리소스를 디컴파일 하는 툴이다. 
jadx도 디컴파일 툴임은 동일하나, <strong>리소스를 디컴파일 한다는 것이 더 강조</strong>되어 있다.</p>
<p>ApkTool을 통해 리소스를 열어보니, Flag를 확인할 수 있었다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[코틀린 기초 문법 - 2 (객체지향과 함께)]]></title>
            <link>https://velog.io/@spring-yong/%EC%BD%94%ED%8B%80%EB%A6%B0-%EA%B8%B0%EC%B4%88-%EB%AC%B8%EB%B2%95-2-%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5%EA%B3%BC-%ED%95%A8%EA%BB%98</link>
            <guid>https://velog.io/@spring-yong/%EC%BD%94%ED%8B%80%EB%A6%B0-%EA%B8%B0%EC%B4%88-%EB%AC%B8%EB%B2%95-2-%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5%EA%B3%BC-%ED%95%A8%EA%BB%98</guid>
            <pubDate>Mon, 29 Sep 2025 11:49:23 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/spring-yong/post/5150283d-0b30-446d-8371-1c9289f76333/image.svg" alt=""></p>
<h2 id="객체지향이란">객체지향이란?</h2>
<p>객체지향이란, 객체들간의 상호작용으로 프로그램을 작성하는 것을 목적으로 하는 프로그래밍 방식입니다. </p>
<p>객체지향은 절차지향과 항상 비교군에 있는 대표적인 프로그래밍 방식입니다. 
객체지향 프로그래밍을 Object Oriented Programming의 약자인 OOP 라고도 부릅니다.</p>
<p>절차지향이 아니라고 해서 <strong>절차가 존재하지 않는다는 뜻은 아닙니다</strong>.
<strong>절차는 존재하되, 객체들간의 상호작용이 추가</strong>된 개념으로 이해하면 됩니다.</p>
<p>그럼 객체들간의 상호작용이 가능한 객체지향 프로그래밍을 왜 사용할까요?</p>
<p>객체 지향을 사용하면, 프로젝트를 구조적이고 효율적이게 만들 수 있도록 합니다.</p>
<p>객체지향이 어떻게 프로젝트를 구조적이고 효율적일 수 있도록 하는지 알기 위해 
결제 서비스를 만드는 상상을 해보겠습니다.</p>
<hr>
<h3 id="절차지향-개발">절차지향 개발</h3>
<p>우리는 결제 서비스를 개발하기 위해, 대한민국에 존재하는 모든 카드사에서 결제가 가능한 프로그램을 개발하고자 합니다. </p>
<p>우선 절차지향의 대표적인 언어중 하나인 C언어로 개발을 시작합니다.</p>
<p>절차지향과 객체지향의 극단적 비교를 위해... 멍청한 코드를 만들어보겠습니다.</p>
<p>각 카드사별 결제에 필요한 API 호출이 다를 것이기에 
각 카드사별 결제 함수가 필요할 것입니다.</p>
<pre><code class="language-c">void hyundai_card_payment_service() {
    // 현대카드 결제
}

void kookmin_card_payment_service() {
    // 국민카드 결제
}

void hana_card_payment_service() {
    // 하나카드 결제
}
.
.
.</code></pre>
<p>위 코드처럼, 모든 카드사의 결제를 정상적으로 처리하기 위해 
카드사 개별 함수를 구현해야 합니다.</p>
<p>이렇게 프로그램을 제작한다면 어떤 문제가 있을까요? </p>
<p>여러가지가 있겠지만 대표적인 이슈는 다음과 같습니다.</p>
<ol>
<li>유지보수가 어렵습니다. <ul>
<li>각각의 함수가 존재하기 때문에, <strong>프로그램 수정 시 모든 함수를 각각 수정</strong>해야합니다. </li>
<li>수정의 범위가 넓어질수록 <strong>테스트 또한 방대</strong>해집니다. </li>
<li>코드의 길이가 길어지기에 <strong>코드를 보기 어렵습니다</strong>.</li>
</ul>
</li>
<li>확장이 어렵습니다.<ul>
<li>만약 대한민국에 새로운 카드사가 생긴다면, 그에 맞추어 <strong>새로운 함수를 개발</strong>해야 합니다.</li>
</ul>
</li>
<li>호출부를 생각하면 어지럽습니다.<ul>
<li>각 카드사별 함수 호출을 위해 호출부마다 현재 유저의 카드 <strong>정보에 대한 조건문이 필요</strong>할 것입니다. <pre><code class="language-c">if (strcmp(card_type, &quot;현대카드&quot;) == 0) {
hyundai_card_payment_service();
} else if (strcmp(card_type, &quot;국민카드&quot;) == 0) {
kookmin_card_payment_service();
} else if (strcmp(card_type, &quot;하나카드&quot;) == 0) {
hana_card_payment_service();
}
.
.
.</code></pre>
쏟아지는 조건문 ... </li>
</ul>
</li>
</ol>
<p>물론 절차지향에서도 이러한 문제점을 개선할 방법이 분명 존재하지만, 
복잡하고 거대한 프로젝트를 설계할수록, 지속적으로 이러한 문제에 부딛힐 것입니다. </p>
<hr>
<h3 id="객체지향-개발">객체지향 개발</h3>
<p>위 절차지향에서 발생했던 문제점들을 객체지향 언어인 코틀린을 사용한다면 
어떻게 개선할 수 있을까요? </p>
<p>우선 객체지향 프로그래밍의 큰 특징으로 다음이 있습니다. </p>
<ul>
<li>추상화</li>
<li>다형성</li>
<li>상속</li>
<li>캡슐화</li>
</ul>
<h4 id="추상화">추상화</h4>
<p>우선 추상화, 우린 다시 카드 결제사의 개발자가 되어 결제 흐름을 구성해보겠습니다.
각 카드 결제사별 공통적으로 필요한 함수가 있을 것 입니다. (결제, 환불, 결제 내역 출력 등 ..)</p>
<p>추상화란, 특정 코드를 작성하기 전에 미리 추상적으로 어떤 코드(동작)가 필요할지 설계할 수 있음을 의미합니다. </p>
<pre><code class="language-kotlin">interface CardService {
    val name: String

    fun pay()

    fun refund()

    fun print_payment_record()
}</code></pre>
<p>위 코드 예제를 보면, 변수와 함수에 대한 정의는 있지만, 변수 및 함수에 대한 구현은 빠져있음을 확인할 수 있습니다. </p>
<p>이처럼 추상화된 필요한 기능을 정의만 할 수 있는 기능을 인터페이스 라고 부르며
코틀린에서는 &#39;interface&#39; 키워드를 통해 정의합니다. </p>
<p><strong>인터페이스만 정의한다면 아무 동작도 할 수 없으며</strong>, 인터페이스를 통해 구현하는 방법은 다형성 파트에서 다루겠습니다.</p>
<p>추상화를 할 수 있는 다른 키워드가 있습니다. </p>
<pre><code class="language-kotlin">abstract class CardService {
    abstract val name: String

    abstract fun pay()

    abstract fun refund()

    abstract fun print_payment_record()

    fun printLog() {
        print(&quot;log&quot;)
    }
}</code></pre>
<p>바로 &#39;abstract class&#39; 입니다. 
직역하면 추상화 클래스입니다. </p>
<p>interface와의 차이점은, interface는 내부에 어떠한 구현도 할 수 없지만, 
<strong>abstract class는 일부 공통 로직에 대한 구현이 가능</strong>합니다. </p>
<p>interface처럼 틀만 작성하고 싶은 함수라면, abstract 키워드를 앞에 붙여 추상화 함수임을 명시하고, 공통 로직을 구현하기 위한 함수라면 일반 함수와 동일하게 선언하면 됩니다.</p>
<h4 id="다형성">다형성</h4>
<p>형태가 다양하다는 뜻입니다. </p>
<p>우선 예시로, 다형성에 대해 이해하기 위해 위에서 다루었던 인터페이스인 CardService를 통해 class를 만들어보겠습니다. </p>
<pre><code class="language-kotlin">class HyundaiCardService : CardService {
    override val name: String = &quot;현대카드&quot;

    override fun pay() {
        // 현대카드 결제
    }

    override fun refund() {
        // 환불 요청
    }

    override fun print_payment_record() {
        // 결제 내역 반환
    }
}</code></pre>
<p>이처럼 위에서 정의한 CardService 인터페이스를 통해, 
새로운 Class를 정의하고 카드사 각각의 개별 함수를 정의할 수 있습니다. </p>
<p>새롭게 만든 Class가 해당 인터페이스를 따른다는 것을 명시하기 위해, Class 이름 옆에 해당하는 인터페이스의 이름을 &#39;:&#39; 뒤에 작성해줍니다.</p>
<p>인터페이스에서 이미 정의한 함수와 변수를 <strong>재정의</strong>하는 것이기 때문에, override 키워드를 함께 작성해주어야 합니다. </p>
<p>또다른 추상화 방법이었던 abstract class의 경우, 새로운 Class를 정의하는 방법이 조금 다릅니다. </p>
<pre><code class="language-kotlin">class HyundaiCardService : CardService() {
    override val name: String = &quot;현대카드&quot;

    override fun pay() {
        // 현대카드 결제
    }

    override fun refund() {
        // 환불 요청
    }

    override fun print_payment_record() {
        // 결제 내역 반환
    }
}</code></pre>
<p>기본적으론 전부 유사하지만 무엇이 바뀌었냐면 ... 
&#39;:&#39; 뒤에 작성해주던 Abstract class 뒤에 &#39;()&#39; 이 붙은 것을 확인할 수 있습니다. </p>
<p>Kotlin에는 인스턴스라는 개념이 있습니다. 
우리가 위에서 정의해온 인터페이스, Class .. 등은 모두 &#39;틀&#39;에 불과하고, <strong>각각 동작하기 위해선 인스턴스를 생성</strong>해주어야 합니다.</p>
<p>우리가 다른 언어에서 사용하던 Int와 같은 Type 처럼, 선언만 하면 사용할 수 없지만 초기화를 통해 사용할 수 있던 것을 생각하면 편합니다.</p>
<p>인스턴스를 생성하기 위해 클래스명 뒤에 &#39;()&#39;를 사용합니다.</p>
<p>또한 Class인 틀은 하나여도, 인스턴스는 여러개여도 괜찮습니다. </p>
<pre><code class="language-kotlin">class HyundaiCardService : CardService() {}</code></pre>
<p>다시 abstract class를 재정의하는 코드를 보겠습니다.</p>
<p>인터페이스와 달리 공통 로직에 대한 구현이 들어있는 class이기 때문에 재정의한 class에 인스턴스를 생성할 때, <strong>해당 absract class에 대한 인스턴스 생성 또한 필요하기에 &#39;()&#39;를 반드시 붙여</strong>줘야 합니다.</p>
<br>
이러한 다형성 특징을 통해 다양한 구현체 (현대카드, 국민카드 ..)를 구조적으로 만들 수 있습니다.

<p>또한 이를 협업. 즉, 팀 프로젝트에 적용해보겠습니다.</p>
<p>인터페이스 하나만 잘 설계해둔다면, 다른 팀원에게 각각의 구현체를 구현하도록 할 수 있습니다. <strong>서로의 작업 영역이 분리</strong>됨으로써 빠른 개발, 오류 방지 등의 효과를 얻을 수 있습니다.</p>
<h4 id="상속">상속</h4>
<p>단어 그대로 다른 Class에게
<strong>원하는 로직을 &#39;상속&#39;</strong> 시킬 수 있습니다.</p>
<p>위에서 살펴본 abstract class에 대한 구현체 또한 하나의 상속 사례로 볼 수 있습니다. </p>
<p>상속을 할 Class를 <strong>&#39;부모 클래스&#39;</strong> 라고 부르고,
상속을 받는 Class를 <strong>&#39;자식 클래스&#39;</strong> 라고 부릅니다.</p>
<p>상속에 대한 예제를 만들기 위해 
새로운 시나리오를 만들겠습니다. </p>
<p>그간 만들어온 현대카드 서비스에 현대카드 V2가 출시되어 결제할 때, 캐시백을 추가로 지급하도록 예제를 만들어보겠습니다. </p>
<pre><code class="language-kotlin">open class HyundaiCardService : CardService() {}</code></pre>
<p>위 코드는 이전에 선언했던 HyndaiCardService의 선언부 입니다. 
조금 달라진 부분이 있습니다. </p>
<p>바로 open 이라는 키워드가 가장 앞에 추가된 것입니다. </p>
<p>코틀린에서 class를 생성하게 되면, <strong>기본적으로는 상속이 불가능</strong>합니다. (자바의 final이 기본적으로 붙어있음)
따라서 상속이 가능한 class로 선언하기 위해 open 이라는 키워드를 추가해줘야 합니다.</p>
<pre><code class="language-kotlin">class HyundaiCardV2Service : HyundaiCardService() {
    override fun pay() {
        super.pay()
        // 캐시백 지급
    }
}</code></pre>
<p>이제 상속을 받을 차례입니다. </p>
<p>이전 코드들과 동일하게 &#39;:&#39; 옆에 부모 class를 명시해준 후
수정을 원하는 함수를 override 해줍니다. </p>
<p>이렇게 하면 자식 class에서 원하는대로 부모 class의 함수를 수정하는 것이 가능합니다. 
또한 예제처럼 부모 class의 함수를 동일하게 사용하고 싶다면, 
<strong>super 키워드를 통해 부모 class의 인스턴스에 접근</strong>할 수 있습니다.</p>
<p>이를 통해 상속의 장점을 살리는 프로그래밍을 할 수 있습니다.</p>
<h4 id="캡슐화">캡슐화</h4>
<p>우리는 프로그램을 제작하며 많은 사람과 함께 작업을 하고, 과거의 나 자신과 싸우기도 합니다. </p>
<p>결제 서비스를 만들며 한땀한땀 열심히 만든 내 결제 로직을 누군가 접근하여 코드를 수정하면 어떨까요?
-&gt; 높은 확률로 오류가 발생하고 시말서 작성이 필요할겁니다 .. </p>
<p>이를 막기 위해 객체지향에선 캡슐화 개념을 적용하였습니다. </p>
<p>단어 그대로 캡슐처럼 코드 영역을 감싸서 외부의 접근을 제어하는 것 입니다. </p>
<p>private, public, protected 등의 키워드를 통해 제어하며 </p>
<p>다음처럼 제어가 가능합니다.</p>
<p>private : 같은 클래스 내에만 접근 가능
public : 모든 클래스에서 접근 가능 (아무것도 사용하지 않으면 기본적으로 public)
protected : 상속 관계에서만 접근 가능</p>
<p>이를 상속을 위한 부모 class 등의 상위 class에 적용 가능하며 
적절히 사용하여 외부 접근을 막거나 허용할 수 있습니다. </p>
<pre><code class="language-kotlin">open class CardService {
    private val secretKey: String = &quot;&quot;
    protected var name: String = &quot;CardService&quot;
    public var temp: String = &quot;public&quot;
}

class CardServiceImpl: CardService() {
    fun test() {
        // private로 선언한 변수이기 때문에 접근 불가
        // print(secretKey) // 오류 발생
        // protected로 선언한 변수이기 자식 class에서는 접근 가능
        print(name) // CardService
    }
}

class MainTest {
    fun test() {
        val service = CardService()
        // public으로 선언한 변수이기 때문에, 인스턴스만 존재한다면 어디서든 접근 가능
        print(service.temp) // public
    }
}</code></pre>
<p>예시로 코드를 작성하며 반드시 필요한 상수값을 private으로 선언하여 외부에서 변경을 막을 수 있고, 
같은 결제 서비스 내 공통적으로 필요한 변수가 있다면, 이는 protected로 선언하여 
결제 class끼리만 공유할 수 있습니다. </p>
<hr>
<p>이번 포스팅에서는 객체지향의 4가지 특징별로 정리를 하며 
코틀린의 문법과 함께 포스팅했습니다. </p>
<p>활용도가 높아질수록 명확한 구조로 설계가 가능하기 때문에, 계속 공부해야하는 부분이라고 생각합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[안드로이드에 대하여 (APK, 4대 컴포넌트)]]></title>
            <link>https://velog.io/@spring-yong/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C%EC%97%90-%EB%8C%80%ED%95%98%EC%97%AC-APK-4%EB%8C%80-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8</link>
            <guid>https://velog.io/@spring-yong/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C%EC%97%90-%EB%8C%80%ED%95%98%EC%97%AC-APK-4%EB%8C%80-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8</guid>
            <pubDate>Sat, 27 Sep 2025 15:29:27 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/spring-yong/post/ce2c4fc4-9c5e-44dc-a257-259877d9bef7/image.png" alt=""></p>
<h2 id="apk-란">APK 란?</h2>
<p>APK란, Android Package의 약자로, 
안드로이드 앱을 <strong>배포, 실행, 설치</strong>하기 위해 사용합니다.</p>
<p>APK엔 다음의 파일이 포함됩니다.</p>
<ul>
<li>Manifest 파일<ul>
<li>앱의 이름, 권한, 실행 정보 등을 포함</li>
</ul>
</li>
<li>Dex 파일<ul>
<li>코드를 컴파일하여 만들어진 실행 파일</li>
</ul>
</li>
<li>리소스 파일<ul>
<li>이미지, 레이아웃, 문자열 등 앱 UI에 필요한 리소스 파일</li>
</ul>
</li>
<li>라이브러리 파일<ul>
<li>C/C++ 네이티브 코드</li>
</ul>
</li>
<li>서명 정보<ul>
<li>앱의 무결성 검증과 개발자 인증 서명 파일</li>
</ul>
</li>
</ul>
<hr>
<h2 id="aab-">AAB ..?</h2>
<p>AAB는 Android App Bundle의 약자로, <strong>앱의 모듈과 리소스를 묶은 패키지</strong>를 말합니다. </p>
<p>2018년 구글에서 APK를 대체하기 위해 만들어졌고, 
Playstore에서 2021년 08월부터 APK 대신 AAB만 배포가 가능하도록 변경되었습니다. 
<br>
AAB는 안드로이드 디바이스에 설치하기 위해, 
플레이스토어에서 <strong>최적화된 APK를 제작</strong>하여 설치합니다.
<br>
위에서 살펴본 APK의 내부 파일 중, <strong>리소스 파일은 앱의 크기에 큰 영향</strong>을 미칩니다. </p>
<p>여러 국가의 유저를 대상으로 배포하기 위해 방대한 언어셋 파일이 존재할 수 있고, 
다양한 해상도의 디바이스를 모두 최적화하기 위해 필요 이상으로 많은 이미지 파일을 APK 파일에 포함해야 합니다. </p>
<p>AAB는 이중 설치할 디바이스에 필요한 리소스만 골라 APK로 만들기 때문에, 
앱의 크기를 줄이는데에 매우 큰 효과를 보이고 있습니다.</p>
<hr>
<h2 id="안드로이드-4대-컴포넌트">안드로이드 4대 컴포넌트</h2>
<p>안드로이드의 앱을 구성하는 가장 중요한 요소들을 
<strong>4대 컴포넌트</strong>라고 부릅니다. </p>
<p>4대 컴포넌트엔 다음들이 있습니다.</p>
<ul>
<li>Activity</li>
<li>Service</li>
<li>Broadcast reciever</li>
<li>Content provider</li>
</ul>
<h3 id="activity">Activity</h3>
<p>Activity란 쉽게 말하자면 &#39;화면&#39; 입니다. 
예를 들어 앱에 로그인하기 위해 표시되는 로그인 화면도 Activity이고,
우리가 <strong>앱에 상호작용하는 모든 화면은 Activity</strong>로 구성되어있습니다. </p>
<p><strong>앱 개발의 편의성을 위해 Fragment를 통해 화면을 구성</strong>하기도 하지만, 
Frament 또한 Activity 위에서 구성되는 요소입니다. </p>
<h3 id="service">Service</h3>
<p>Service는 Activity에서 화면에서 필요한 작업 외에 
<strong>백그라운드에서 작업이 필요한 경우 사용</strong>하는 컴포넌트입니다.</p>
<p>음악 재생, 파일 다운로드 등의 작업에서 유용하게 사용됩니다. 
또한 앱이 종료되어도 계속 작업 실행이 가능하다는 특징이 있습니다. </p>
<h3 id="broadcast-receiver">Broadcast receiver</h3>
<p>Broadcast receiver는 다른 앱이나 시스템에서 
<strong>데이터를 수신하고 싶을 때 사용</strong>하는 컴포넌트 입니다. </p>
<p>디바이스의 인터넷이 끊겼거나 배터리 부족 등의 상황을 감지하기 위해 사용됩니다. </p>
<p>앱이 여러 상황에서도 에러 없이 정상적으로 작동하기 위해 
필수적으로 처리해줘야 하는 컴포넌트입니다. </p>
<h3 id="content-provider">Content Provider</h3>
<p>Content Provider는 서로 다른 앱 간의 데이터 공유를 위해 사용하는 컴포넌트입니다. </p>
<p>예를 들어 디바이스에 저장된 연락처를 조회하기 위해 사용할 수 있습니다.</p>
<p>이외에 다른 데이터를 저장한 SQLite, 파일 등에 접근하기 위해
사용할 수 있는 컴포넌트입니다. </p>
<hr>
<p>이번 포스팅에서는 APK 파일과 안드로이드의 4대 컴포넌트에 대해 포스팅을 진행했습니다. </p>
<p>안드로이드 개발을 하며 항상 마주했던 개념들이기 때문에 앞으로 잘 기억해야할 것 같습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[코틀린 기초 문법 - 1]]></title>
            <link>https://velog.io/@spring-yong/%EC%BD%94%ED%8B%80%EB%A6%B0-%EA%B8%B0%EC%B4%88-%EB%AC%B8%EB%B2%95-1</link>
            <guid>https://velog.io/@spring-yong/%EC%BD%94%ED%8B%80%EB%A6%B0-%EA%B8%B0%EC%B4%88-%EB%AC%B8%EB%B2%95-1</guid>
            <pubDate>Mon, 22 Sep 2025 10:34:12 GMT</pubDate>
            <description><![CDATA[<h2 id="코틀린은">코틀린은?</h2>
<p>코틀린은 자바의 문법과 비슷합니다.
기본적인 문법. 즉, 작성 방법이 다를 뿐 <strong>내부 클래스, 모듈의 동작은 유사</strong>하며 
<strong>자바 &lt;-&gt; 코틀린 간 높은 호환성</strong>을 보여줍니다.</p>
<p>서로 다른 언어가 어떻게 높은 호환성을 보여줄까요?
우선 자바는 프로그램을 실행시키기 위해, JVM 위에서 실행될 수 있도록 
.class 타입의 실행 파일을 컴파일합니다. </p>
<p>이것과 동일하게 코틀린 또한 JVM 위에서 실행될 수 있도록 
.class 타입의 실행 파일을 동일하게 컴파일합니다.</p>
<p>즉, 서로 다른 언어이지만, <strong>같은 .class 실행 파일로 컴파일</strong> 되기 때문에, 
높은 호환성을 보일 수 있는 것입니다.</p>
<h2 id="변수-선언">변수 선언</h2>
<pre><code class="language-kotlin">fun main() {    
    var num: Int = 0
    num = 123

    val constNum: Int = 7
    // constNum = 123 &lt;- 변경 불가 !!!
}
</code></pre>
<p>코틀린에서 변수를 선언하는 방법은 위와 같습니다. 
자바와 달리 var 혹은 val로 변수 선언을 시작하고, 변수의 타입은 변수명 뒤에 지정해주면 됩니다.</p>
<p>var은 변경 가능한 변수를 선언하기 위해 사용하고, val은 변경이 불가능한 변수를 선언할 때 사용하게 됩니다.</p>
<hr>
<h2 id="nullable-타입-널-허용">Nullable 타입 (널 허용)</h2>
<pre><code class="language-kotlin">fun main() {
    val nullableNum: Int? = null // null 괜찮아요 ~~

    val num: Int = null // Non-null 에러 발생 ! (컴파일 에러)
}
</code></pre>
<p>우리는 코틀린 코드를 읽을 때, 타입 뒤에 ?(물음표)가 붙는 경우를 자주 목격하게 됩니다. </p>
<p>이 문법은 타입을 잘 모르겠을 때 모른다고 표현하는 방법일까요?
<br>
코틀린은 그렇게 애매한 자세의 프로그래밍 언어가 아닙니다.
이건 코틀린의 특징이자 자바에 비해 가장 강력한 장점으로 꼽히는 Nullable 문법입니다. </p>
<p>?(물음표)가 붙은 타입의 변수는 null이 들어갈 수 있는 변수이고,
붙지 않은 변수 (Ex. var a: Int)는 null이 허용되지 않는 변수로 사용됩니다. 
?를 붙이지 않고 선언한 변수에 null을 할당하려고 하면, 컴파일 에러가 발생하게 됩니다. 
<br></p>
<p>우리는 개발하며 수많은 NPE (Null Pointer Exception)에 지난 과오를 반성하며 무수히 많은 null-check 구문을 만들어야 합니다.</p>
<p>하지만 코틀린은 이러한 Nullable 타입을 제공함으로써 변수 선언시부터 null에 대한 허용 여부를 강제로 지정하게 함으로써 초반엔 귀찮고 힘들지만 예기치 못한 NPE를 줄일 수 있도록 도와줍니다. </p>
<hr>
<h2 id="출력">출력</h2>
<pre><code class="language-kotlin">fun main() {
    print(&quot;하이 세상&quot;)
    println(&quot;하이 세상 줄바꿈&quot;) //print(&quot;하이 세상 줄바꿈\n&quot;)
}</code></pre>
<p>print 문법입니다. 
println을 사용하면 자동으로 줄바꿈이 가능합니다. </p>
<hr>
<h2 id="조건문">조건문</h2>
<pre><code class="language-kotlin">fun main() {
    val test: Int = 3

    if (test == 3) {
        print(&quot;삼&quot;)
    } else {
        print(&quot;낫삼&quot;)
    }
}</code></pre>
<p>코틀린의 if 조건문은 다른 언어들과 매우 비슷합니다. </p>
<pre><code class="language-kotlin">fun main() {
    val test: Int = 3

    when (test) {
        1 -&gt; print(&quot;1&quot;)
        2 -&gt; print(&quot;2&quot;)
        3 -&gt; print(&quot;3&quot;)
        else -&gt; print(&quot;Else&quot;)
    }
}</code></pre>
<p>이건 코틀린에 존재하는 when 조건문 입니다. 
자바의 switch 문과 유사하게, case 별 조건을 설정할 수 있습니다. 
<br> 
제가 가장 좋아하는 신기한 점은 when 문을 사용해 바로 변수에 값을 지정할 수 있다는 점 입니다. </p>
<pre><code class="language-kotlin">fun main() {
    val test: Int = 3

    val num = when (test) {
        1 -&gt; 1
        2 -&gt; 2
        3 -&gt; 3
        else -&gt; 0
    }
}</code></pre>
<p>위처럼 num에 when 조건문을 사용하여 값을 설정할 수 있습니다.</p>
<hr>
<h2 id="반복문">반복문</h2>
<pre><code class="language-kotlin">fun main() {
    // while 반복문
    var i: Int = 0
    while (i &lt; 3) {
        println(i)
        i++
    }
    // for 반복문
    for (j in 0..3) {
        println(j)
    }
}</code></pre>
<p>반복문 입니다. 다른 언어와 크게 다르지 않습니다. 
while엔 조건식을 넣어주고, for 문엔 반복 가능한 변수(Range, List)를 넣어주면 됩니다.</p>
<hr>
<h2 id="함수-선언">함수 선언</h2>
<pre><code class="language-kotlin">fun main() {
    val returnMsg = fun1(1)
    val returnMsg2 = fun2(2)

    println(returnMsg)
    println(returnMsg2)
}

fun fun1(a : Int): String {
    return &quot;return&quot;
}

fun fun2(a : Int): String = &quot;return direct&quot;</code></pre>
<p>코틀린에서 함수를 선언하는 방법은 위와 같습니다. 
fun1 함수를 예시로, 파라미터의 타입을 변수 선언하듯 타입을 지정해주고, 
함수의 리턴 타입 또한 같은 형식으로 지정해줍니다. </p>
<p>하지만 함수가 매우 짧은 경우엔 굳이 중괄호를 사용하지 않아도 됩니다. 
fun2처럼 &#39;=&#39; 기호를 사용하여 return 구문을 생략하고 함수를 매우 간결하게 선언하는 것 또한 가능합니다.
<br> </p>
<pre><code class="language-kotlin">fun main() {
    val returnMsg = fun1(1)
    val returnMsg2 = fun1(2, 3)

    println(returnMsg)
    println(returnMsg2)
}

fun fun1(a : Int): String {
    return &quot;return&quot;
}

fun fun1(a : Int, b : Int): String {
    return  &quot;overloaded function&#39;s return&quot;
}</code></pre>
<p>코틀린은 <strong>&#39;메서드 오버로딩&#39;</strong> 또한 지원합니다. 
같은 함수명을 사용하여도 파라미터가 다르다면 다른 동작을 하도록 구현할 수 있습니다. </p>
<hr>
<h2 id="코틀린에서-null을-다루는-방법">코틀린에서 Null을 다루는 방법</h2>
<h3 id="엘비스-연산자">엘비스 연산자</h3>
<pre><code class="language-kotlin">fun main() {
    var nullableNum: Int? = null

    print(nullableNum ?: &quot;null이라서 등장&quot;)
}</code></pre>
<p>위 문법은 특정 변수가 null인 경우 정해진 값을 반환시키기 위해 사용할 수 있습니다. 
따라서 위 코드의 출력 결과는 <strong>&#39;null이라서 등장&#39;</strong> 입니다.
<br>
왜 이 문법을 엘비스 연산자라고 부를까요? </p>
<img src="https://velog.velcdn.com/images/spring-yong/post/b222b31b-5ded-4472-b780-8db12be2c8a5/image.png" width="30%" height="30%">
이 연산자( ?: ) 가 생긴게 마치 로큰롤의 왕 '엘비스 프레슬리' 의 앞머리가 
누워있는 것 처럼 생겼기 때문입니다. (진짜에요)

<h3 id="-문법">!! 문법</h3>
<pre><code class="language-kotlin">fun main() {
    var nullableNum: Int? = null
    func1(nullableNum)
}

fun func1(a: Int) {
    print(a)
}</code></pre>
<p>위에서 언급했듯이, 코틀린엔 Nullable 타입과 Null을 허용하지 않는 타입이 있습니다. 
만약 내가 갖고 있는 변수는 Nullable 한데, 호출하고자 하는 함수의 파라미터가 Null을 허용하지 않는다면 어떻게 될까요?
<br>
코틀린에선 이를 컴파일 에러로 인식하여 빌드가 성공하지 않습니다. 
이를 위해 다음의 방법을 시도해볼 수 있습니다.</p>
<ol>
<li><p>Nullable 변수에 값을 할당 or null-check</p>
<pre><code class="language-kotlin">fun main() {
 var nullableNum: Int? = null
 nullableNum = 1
 func1(nullableNum)

 nullableNum = null

 // null-check
 if (nullableNum != null) {
     func1(nullableNum)
 }
}
</code></pre>
</li>
</ol>
<p>fun func1(a: Int) {
    print(a)
}</p>
<pre><code>위처럼 코드를 작성하게 되면, nullable 변수였던 &#39;nullableNum&#39;에 값이 할당 혹은 null이 아님을 검증되어 더이상 nullable 하지 않다고 코틀린에서 자동으로 판단하게 됩니다.

따라서 정상적인 함수 호출이 가능합니다. 

하지만 이는 같은 영역에서 선언되고 할당되는 변수의 경우에만 허용되며 영역이 달라지는 경우엔 어디서든 값이 다시 null이 될 수 있기 때문에, 허용되지 않는 방법입니다. 

이 영역의 개념은 추후 포스팅에서 다루도록 하겠습니다. 지금은 서로 구분된 파일 정도로만 알고있으면 충분합니다.

2. !! 문법 사용

```kotlin
fun main() {
    var nullableNum: Int? = null
    func1(nullableNum!!) // &lt;- !! 문법 사용
}

fun func1(a: Int) {
    print(a)
}</code></pre><p>본 단락의 제목인 &#39;!! 문법&#39; 입니다.
이 문법은 변수 뒤에 &#39;!!&#39; 을 사용함으로써, 개발자가 이 변수는 null이 아니다! 라고 지정할 때에 사용하게 됩니다. </p>
<p>1번 내용에서 설명했듯, 영역이 달라지는 경우 코틀린은 해당 변수가 null인지 아닌지 확인할 수 없습니다. 따라서 함수를 호출하기 위해 개발자가 강제로 !!를 사용하여 null이 아님을 명시할 수 있습니다. </p>
<p>하지만 이는 null에 대한 처리를 한다기 보단, 강제로 프로그램이 돌아갈 수 있도록 하는 것 이기에 <strong>권장되지 않습니다.</strong></p>
<p>실제로 위 코드를 실행해보면, 런타임에서 NPE가 발생하게 됩니다.</p>
<hr>
<p>이번 포스팅에선 코틀린의 기초 문법 중, class와 연관된 내용은 전부 덜어내고 포스팅을 진행했습니다. 
이후 후속 포스팅에선 객체지향의 의미와 코틀린에서의 class 사용에 대하여 포스팅하도록 하겠습니다.</p>
]]></description>
        </item>
    </channel>
</rss>