Groo

Koin 코인 라이브러리 본문

Android

Koin 코인 라이브러리

김주엽 2020. 5. 7. 21:51

안녕하세요, 오늘은 이전 시간에 배운 DI 의존성 주입 디자인 패턴을 실제로 구현해보려고 합니다.
DI 디자인 패턴을 구현할 수 있도록 도와주는 다양한 기술들 중 먼저 Koin 코인 라이브러리에 대해 알아보겠습니다.

🛎 DI 의존성 주입 디자인 패턴 정리

먼저 Koin 라이브러리에 대해 알아보기 전 지난 시간에 배운 DI 의존성 주입에 대해 간략히 복습하겠습니다. 의존성이란 한 객체가 다른 객체를 참조하거나 필요로 하는 경우 이를 의존성이 존재한다고 부릅니다. 반면에 주입은 의존성이 존재하는 객체가 필요로하는 다른 객체를 간접적으로 대신 전달해주는 역할을 합니다. DI 의존성 주입에 대한 자세한 내용은 이전에 작성한 글을 참고해주세요.

 

Dependency Injection 디자인 패턴

안녕하세요, 오늘은 프로그래밍 과정에서 새롭게 추가하면 좋을만한 패턴인 의존성 주입에 대해 알아보겠습니다. 의존성 주입은 어느 프로그래밍 언어에서도 공통적으로 적용 가능하기 때문에 범용성이 높은 패턴..

juyeop.tistory.com

💰 Koin 코인은 뭘까?

이전 시간에 제가 DI 의존성 주입 디자인 패턴을 구현할 수 있도록 도와주는 다양한 종류의 기술들이 존재한다고 말하였습니다. Koin 코인 라이브러리 또한 다양한 종류의 기술들 중 한 가지입니다. 이는 다른 기술들보다 상대적으로 수준이 낮아 어렵지 않게 구현 가능합니다. 또한 Koin 코인은 코틀린 언어만을 위한 DI 라이브러리이며 InsertKoin.io 팀에서 순수 코틀린 코드로만 개발하였습니다.

 

🚀 Build.Gradle 설정하기!

그럼 이제 본격적으로 프로그램 내에 Koin 코인을 활용하여 DI 의존성 주입 디자인 패턴을 구성해보겠습니다. 먼저 Build.Gradle 파일에 Koin 코인 라이브러리를 활용할 수 있도록 구현 코드를 작성하겠습니다. 자세한 사항은 Koin Github를 참조 바랍니다.

 

dependencies {
    def koin_version = '2.1.5'

    // Koin AndroidX
    implementation "org.koin:koin-androidx-scope:$koin_version"
    implementation "org.koin:koin-androidx-viewmodel:$koin_version"
    implementation "org.koin:koin-androidx-fragment:$koin_version"
    implementation "org.koin:koin-androidx-ext:$koin_version"
}

🎯 간단한 예제 구현하기

프로그램 내에 Koin 라이브러리를 모두 추가하였다면 이제 간단한 예제를 구현해보겠습니다. 이번에 구현하는 예제는 Koin Github에 명시되어있는 Quickstart를 참고하였으며 딱히 크게 어렵지 않아 프로그램의 구조를 이해하는데 수월할 것이라고 생각합니다.

[SchoolService]

schoolName이라는 멤버 변수를 가지며 moveSchool 메서드를 통해 재학 중인 학교를 새로운 학교로 변경할 수 있습니다.

class SchoolService {
    var schoolName = "대구소프트웨어고등학교"

    fun moveSchool(newSchoolName : String){
        this.schoolName = newSchoolName
    }
}

[StudentController]

생성자로 SchoolService 클래스의 인스턴스를 전달받고 있으며 print 메서드를 통해 현재 재학 중인 학교의 이름을 출력합니다.

class StudentController(val schoolService: SchoolService) {
    fun print(){
        println("현재 재학 중인 학교 : ${schoolService.schoolName}")
    }
}

[AppModule]

Koin 코인 라이브러리의 DSL을 활용하여 각 클래스 모듈을 생성하고 있습니다. (자세한 내용은 아래에서 설명하도록 하겠습니다.)

val appModule = module {
    single { SchoolService() }
    single { StudentController(get()) }
}

DSL이라는 것은 Domain Specific Language라고 해석하며 도메인 특화 언어라고 부릅니다. DSL은 특정한 도메인을 적용하는데 특화된 언어입니다. 종류 또한 아래와 같이 정말 다양하며 각각의 DSL의 특징과 어떠한 역할을 수행하는지 자세히 알아야 합니다.

 

module 코틀린 모듈을 생성할 때 사용한다.
factory 매번 inject 할 때마다 새로운 인스턴스를 생성한다.
single 전역적으로 사용 가능한 인스턴스를 생성한다. (싱글톤 생성)
bind 생성할 객체를 다른 타입으로 변환할 때 사용한다.
get 필요로하는 곳에 알맞게 의존성을 주입한다.
applicationContext Contexxt를 주입한다.

그럼 이제 DSL을 참고하여 위의 코드를 살펴보겠습니다. 현재 appModule이라는 변수에 moudle 키워드를 사용하여 코틀린 모듈을 생성하고 있습니다. 또한 module 키워드 안에서는 single 키워드를 사용하여 위에서 생성한 각 클래스들을 감싸고 있습니다.

 

위의 코드를 자세히 본다면 첫 번째 single 키워드에서는 get 메서드를 사용하지 않았으면서 두 번째 줄에서는 왜 get 메서드를 사용한 것일까요? 그 이유는 SchoolService 클래스는 생성자로 매개변수를 입력받지 않는 반면에 StudentController 클래스는 생성자로 매개변수를 입력받기 때문입니다. 또한 get 키워드를 사용하여 알맞은 의존성 객체의 값을 주입하기 위해 작성하는 것입니다.

[Application]

Application() 클래스를 상속받은 클래스에서 startKoin 메서드를 통해 프로그램 내에 Koin 코인을 사용할 수 있도록 설정합니다.

class Application : Application() {

    @Override
    override fun onCreate() {
        super.onCreate()

        startKoin {
            androidContext(applicationContext)
            modules(appModule)
        }
    }
}

여기서 주의할 점은 Koin 코인을 프로그램 내에서 정상적으로 동작시키기 위해서는 1가지의 추가적인 Setting이 필요합니다.

그것은 AndroidManifest.xml 파일에서 android:name에 위에서 만든 Application 클래스 파일을 적용시켜야 하는 것입니다. 

<application
        android:name=".Application"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
 </application>

[MainActivity]

위에서 생성한 두 개의 클래스가 자료형인 각각의 멤버 변수를 만든 후 by inject()를 통해 의존성을 주입시킵니다.

class MainActivity : AppCompatActivity() {

    private val schoolService : SchoolService by inject()
    private val studentController : StudentController by inject()

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

        schoolService.moveSchool("청구중학교")
        studentController.print()
    }
}

위의 코드는 Koin 코인이 프로그램 내에서 정상적으로 잘 작동하는지를 테스트하기 위해 구현한 코드입니다. 굳이 MainActivity 클래스가 아닌 다른 일반 클래스여도 상관이 없으며 의존성을 주입할 때 by inject()를 통해 의존성을 주입시킨다는 것만 알면 됩니다. 그 후 moveSchool 메서드를 통해 학교의 이름을 변경하고 print 메서드를 통해 현재 재학 중인 학교의 이름을 출력하고 있습니다.

 

👍 글을 마치며

오늘은 DI 의존성 주입 디자인 패턴을 구현하기 위해 Koin 코인 라이브러리를 활용해보았습니다. Koin 코인을 배우기 이전까지 저희는 위와 같은 예제를 구현하기 위해 MainActivity에서 각 클래스에 따른 인스턴스 객체를 일일이 생성해주었습니다. 하지만 Koin 코인을 활용하면서 DI 의존성 주입 디자인 패턴이 구성되었으며 프로그래머가 직접 의존 관계를 설정해주지 않더라도 자동으로 의존성 관계 문제가 해결되는 모습을 볼 수 있었습니다. 오늘 구현한 예제와 같이 이렇게 간단한 프로그램을 구현할 때는 이러한 방식이 더욱 복잡하여 좋지 않을 것이지만 만약 프로젝트처럼 규모가 크고 객체 생성 과정이 많을 경우에는 훨씬 도움이 많이 될 것입니다. 오늘 배운 내용이 쉽지만은 않은 내용이었지만 공부를 열심히 하며 자주 연습한다면 앞으로 사용 빈도가 많아질 것 같습니다. 감사합니다.

Comments