Kotlin - 회사에서는 어떻게 도입하고? 학습해볼 수 있을까?



개인 광고 영역

2017년 Google I/O 이후 Kotlin에 대한 관심도가 증가하였고, 샘플 코드와 각종 블로그 포스팅에서 Java보다 Kotlin에 대한 이야기가 많이 나오고 있다, 특히 Jake Wharton은 내부 Android 코드의 Kotlin 적용 적용 프로젝트에 참여로 구글에 입사하였고, 최근에는 자신이 가지고 있던 Retrofit2 모듈에 coroutines을 적용하기도 하였다.

다양한 사례들이 많이 나오고 있지만, 아직은 진행 중인 단계이다. 1.0 정식 버전이 나온 이후로부터 벌써 2년이라는 시간이 거의 끝나가고 있지만 실제 어떻게 적용할 수 있으며, 안정성은 어떤지에 대한 의문이 들 수밖에 없다.

이 글은 주관적으로 작성한 글이며, 일부는 회사에 적용한 사례를 바탕으로 적었다.

RGP Korea 블로그에서 자세한 내용 확인 가능 - 요기요는 Kotlin을 어떻게 적용했나?


Kotlin

Kotlin은 2016년 2월 정식 버전이 나오기까지 5년간 0.x 버전을 통해 다양한 시도를 하였다.

0.x 시절에는 새로운 버전이 나오면 수많은 메소드들의 변화가 일어났었지만, 현재는 그런 부분이 많이 없어졌다.

다행히 1.x 버전을 안정적으로 적용하고, 버전 업데이트가 일어나더라도 새로 추가된 부분들만 학습하면 문제가 없다.

2017년 1월 현재 나온 가장 최신 버전은 1.2 버전이다. 1.0에서 1.2까지 올라오면서 없어진 것보단 추가사항이 많다. 예를 들면 lateinit에 대한 값이 init되었는지 체크하는 로직도 추가되었고, 1.1에서는 coroutines이 추가되었다.(coroutines은 아직은 도전적인 부분이라 실제 적용하기는 어렵겠지만 학습하면 재미있게 학습이 가능하다)

그리고 Kotlin의 최고 장점은 역시 JVM 위에서 동작하는 언어라는 점이며, Java와 100% 호환성을 가진 언어이다. 그렇기에 Java 개발자가 손쉽게 Kotlin을 학습할 수 있고, 실제 적용하는데 부담감이 없다. 부담이더라도 Java로 작성하고, Kotlin에서 가져와서 쓰거나, 반대로 Kotlin으로 작성한 코드를 Java에서 자유롭게 불러다 사용하는 게 가능하다.

다시 말해, Util 클래스 정도를 Kotlin으로 작성하고, 그냥 호출만 해서 쓰면 Java/Kotlin 둘 다를 맛볼 수 있게 된다.

kotlin-01


Kotlin을 적용하는 방법

Android Studio 3.0 정식 버전이 10월에 나왔다. 3.0 버전부터는 Kotlin 프로젝트를 바로 시작할 수 있다. 한가지 아쉬운 건 Kotlin 옵션이 별도로 추가되었다는 점이다. 한번 선택해두면 이후에는 자동으로 선택되겠지만, 선택하지 않으면 Java 프로젝트로 생성한다는 것이다.

툴에서는 이제 쉽게 Kotlin을 적용할 수 있다. 기본 문법들을 익혀서 이제 Kotlin을 실무에 적용해볼 수 있어야 한다.

다행히도 수많은 책들이 쏟아져 나오고 있다는 점이다. 필자가 만든 Kotlin 동영상 강의도 있지만, 현재까지 나온 책이 5권 정도가 있다.(앞으로 더 나올 예정이다)

가장 좋은 기본서는 역시 Kotlin 사이트에서 제공하는 기본 문서이다.

이 기본 문서는 이제까지 학습하였던 언어들보다 잘 정리가 되어 있어서 문서만 보고도 충분히 따라 해볼 수 있다.

그리고 Android Studio Kotlin 플러그인에는 다행히 Java to kotlin 컨버터가 제공되고 있다. 이러한 컨버터를 활용하여 빠른 학습이 가능하다.

그리고 정말 모르더라도, Java로 작성하고 복사 붙여넣기를 통해 Kotlin 학습도 가능하다.


나부터 시작하기

Google I/O 세션 중 Pinterest - Christina LeeJake Wharton이 함께 한 세션이 있다.

maxresdefault

이 세션에서의 주요 내용은 개발이 아닌, 어떻게 설득하고 어떻게 적용하는 게 좋은 방법인지 설명해주고 있다.

그 첫 번째 단계로 나부터로 시작해야 한다. 아무리 좋은 게 있더라도, 나부터 관심이 없다면 모든 건 의미 없어진다. 내가 학습하고 나서, 주위에 같이 할 수 있는 사람을 설득하고, 참여하게 만들어야 한다. 그러려면 내가 이 기술에 대한 학습이 필요하며, 어느 정도 알고 있어야 함께해 볼 수 있는 환경이 만들어지게 될 것이다.

그리고 그렇게 한 명 두 명 팀원을 설득하다 보면 모두가 좋아하는 해볼 수 있겠다는 마음이 들게 된다. 그럼 그다음 단계로 상위의 사람을 설득하는 과정이 또 필요하게 된다.

이러한 내용들로 채워진 세션이다. 개발 능력도 좋지만 이런 부분이 아무래도 더 중요한 부분이라고 생각되어 추가해보았다.

필자의 회사에서는 아주 쉽게 적용하는 게 가능했다. 바로 무기는 Google I/O였다. Google I/O 다녀온 다음 바로 적용해도 괜찮을까라는 질문에 ok을 받았고(이미 다른 곳에 적용해보기로 했었다), 바로 실제 프로젝트에 적용해볼 수 있었다.

아래에서는 실제 적용하면서 생겼던 사소한 문제를 짧게 정리해보려고 한다.


필자의 적용 방법은?

회사에서 적용하는 건 Google I/O 이후 아주 간단하게 적용할 수 있게 되었다. 그렇다고 하더라도, 알고 있는 필자와 함께 해야 하는 팀원 간의 격차를 줄여야 한다.

팀원 간의 격차를 줄이기 위해서 페어 프로그래밍을 진행하였다.

페어 프로그래밍을 진행함으로써 서로 알고 있는 부분을 공유하고, kotlin을 학습하는 시간을 가질 수 있었다.

우선 아래와 같은 단계를 거쳐가면서 실제 학습을 할 수 있었다.

  • 그냥 java로 작성하기
  • kotlin으로 컨버팅 하기(툴에서 제공하는 컨버터 활용)
  • 모를 때마다 그냥 java로 코드 작성하여 복사 붙여넣기(이때 컨버팅이 자동으로 일어난다)
    • 컨버팅이 일어나지만 완전한 코드가 나오지는 않는다. 그때마다 옆에서 조정을 도와준다.
  • 코틀린 기본 문법(함수 만들기 변수 만들기)이 어느 정도 익숙해지면 그냥 java처럼 작성한다(이때 문법은 kotlin 베이스)
  • 어느 정도 오르면 코드 줄이는 기법을 함께 추가하여 학습한다.(재미 추가)

위와 같은 과정을 반복하였다. 필자의 팀원과 약 2주 정도 위와 같이 진행하였는데 익숙해지는데 도움이 되었던지 어느 순간 그냥 kotlin으로 작성하고 있었다.

코드 줄이는 부분은 사실 해도 좋고, 하지 않는다고 문제 될 건 없다. 오히려 많이 써두면 독이 될 수 있긴 하다. 바로 Standard library인데 정말 유용한 문법들이 가득하니 참고해보길 바란다.


프로젝트에 적용하기까지?

실제 프로젝트에 적용하려면 아무리 잘하는 사람이라도 부담감을 가질 수밖에 없다.

  • 오류 나면 어떡하지? 정말 문제가 없을까?

등의 당연한 불안감일 수 있다. 오류 나면 어떡하지는 내가 생각한 베스트 코드 작성 방법이 없다고 느낄 때 나타날 수 있을 것 같다.

예를 들어 내가 null 처리를 아래와 같이 했다면

String name = null;
if (name != null) {
  name.length();
}

자바에서는 객체에 대한 null 체크를 위와 같이 할 수 있다.

하지만 kotlin은 더 짧게 줄여서 할 수 있는데 아래와 같다.

val name: String? = null
name?.length

물음표와 null이 보인다. 실제 위의 코드를 Java 디컴파일 해보면 아래와 같다.

String var10000 = this.name;
if(this.name != null) {
   var10000.length();
}

디컴파일 결과로 보면 java와 동일하다. 다만 몇 가지 더 들어가서 이쁘게 보이지는 않지만 오히려 Java보다 더 간결하게 작성하고, java와 같은 결과를 볼 수 있다.

내가 작성하는 코드가 문제가 아닌 사실은 kotlin to java byte을 변환하는 컴파일 과정에서 다르게 나오지 않을까?라는 의문이 생길 수 있지만, 그 부분은 젯브레인과 구글이 계속 적으로 수정해줄 부분이다.

사실 코드는 java처럼 아래와 같이 작성하더라도 전혀 문제가 없다.

val name: String? = null
if (name != null) {
    name!!.length
}
  • 다음 버전이 업데이트되면 어떡하지?

다행히도 kotlin은 현재 1.2 버전이다. 1.0이 나온 게 2016년 2월이니 2018년 1월인 지금까지 크게 달라진 건 없다.

오히려 java sdk 버전을 고를 수 있게 되었고, 더 쉽게 Android Parcelable 사용할 수 있는 Annotation 제공과 coroutines 등 다양한 기능이 계속 추가되고 있다.

아직은 퓨처 버전이 계속 추가되고 있어서, 과거 0.x 버전으로 개발하시던 분들에 비해서는 많이 안정적이다.(0.x 버전에서 개발하시던 분들은 버전 업데이트가 일어나면 수많은 코드를 다시 훑어보아야 하는 문제가 있었다.)

코드 베이스가 크게 변하지 않고 있어서 다행히 안정적으로 운영 중에 있다.


null 처리에 주의할 것

필자는 실제 프로젝트에 적용할 때 다음을 간과해버리고 적용하였다.

  • Java : 언제든 null을 허용할 수 있다.
  • Kotlin : null을 허용하려면 명시해야 한다.

Kotlin의 장점인 Safety Nullable을 허용하지 않았다가 생긴 문제이다. 바로 ? 하나를 빼먹으면서 발생한 문제이다.

Kotlin은 기본 NotNull을 적용한다. Java는 기본 Nullable을 사용하고 있다.

두 언어 null을 바라보는 방식이 반대인 것이다.

그래서 디컴파일 해보면 아래와 같은 코드를 자주 볼 수 있다.

public final void print(@NotNull String a) {
   Intrinsics.checkParameterIsNotNull(a, "a");
   System.out.println(a);
}

바로 이 코드인데 Intrinsics.checkParameterIsNotNull(a, "a"); NotNull을 명시했기 때문에 Nullable이 들어오면 바로 예기치 못한 NullPointerException을 일으키는 코드이다.

kotlin에서 NotNull의 명시는 변수에 아무것도 붙여주지 않으면 기본 NotNull인 것이다. 반대로 Nullable을 만들려면 val name: String? = null으로 명시해주어야 이를 해결할 수 있다.

코틀린을 처음 적용할 때 아무래도 저런 부분의 실수가 생길 수 있다. 다만 100% Kotlin만을 활용하는 경우에는 명시가 큰 의미가 없다. Android Studio에서 알아서 null의 값을 추가할 수 없음을 알려주기 때문에 전혀 문제가 없다.(다만 Json으로 받은 데이터를 파싱 하는 과정에서는 null이라는 값이 들어오면 즉시 NullPointerException이 발생할 수밖에 없다.)

간단한 샘플 코드를 통해서 이를 확인해보자.

간단하게 스트링을 출력하는 메소드이다.

fun printTest(a: String) {
    println(a)
}

위의 메소드를 kotlin에서 아래와 같이 불러보겠다.

fun ab() {
    printTest(null)
}

이 경우는 null 일 수 없기 때문에 즉시 아래와 같이 오류가 발생한다.

sample-01

이번엔 전역 변수에 Nullable 변수를 통해 printTest을 불러보더라도 같은 결과이다.

fun ab() {
    printTest(name)
}

이번엔 type이 맞지 않다고 즉시 오류가 발생한다.

sample-02

하지만 위의 코드를 디컴파일 해서 java로 보면 사실 큰 차이가 없다. Java 코드였다면 문법상 오류는 나오지 않았을 것이다.

다만 Runtime에 죽었겠지만…

private String name;

public final void ab() {
   this.printTest(this.name);
}

public final void printTest(@NotNull String a) {
   Intrinsics.checkParameterIsNotNull(a, "a");
   System.out.println(a);
}

Java로 생각하면 위 문법 자체는 전혀 오류가 없다. 다만 언제든지 null이 될 수 있음을 명확히 나타내고 있으니 Java보다는 좋은 코드로 보인다.

java로의 디컴파일 코드까지 확인해봤으니 이제 저 문법을 자바에서 불러보자.

@Test
public void test() {
    NullableTest test = new NullableTest();
    test.printTest(null);
}

kotlin과 동일하게 접근해보았는데 이 경우는 NotNull 임을 알 수 있기 때문에 바로 워닝을 뛰어준다.

sample-03

이번엔 전역 변수를 통해 접근해보았다.

이 경우는 Json을 파싱 하거나, 누군가 null로 값을 대치시킬 수 있을 경우다.

private String name = "Name";

@Before
public void setUp() {
    name = null;
}

@Test
public void test() {
    NullableTest test = new NullableTest();
    test.printTest(name);
}

이 경우는 IDE에서는 아무런 워닝이 없다. 어디선가 name에 대한 값을 대치시켰을 뿐 printTest을 부르는 곳에서는 전혀 문제가 없기 때문이다.

이러면 Runtime에서 즉시 NullPointerException이 발생할 수 있는 코드이다.


NullPointerException을 피하려면

NullPointerException을 피하려면 결국 뭔가 명시를 해야 하는데 아래와 같이 명시하는 게 가능할지도 모른다.

private String name = "Name";

@Before
public void setUp() {
    name = null;
}

@Test
public void test() {
    NullableTest test = new NullableTest();
    if (name != null) {
      test.printTest(name);
    }
}

바로 if (name != null) {을 이용해서 처리하거나, try/catch 묶는 방법이 있을 것이다. 하지만 혼자서 호출하는 printTest가 아니고, 협업 중에 여러 명이서 이를 부르게 될 것이다.

그러면 누구는 if 체크해서 부르고, 누구는 그냥 부르고 하는 일이 생길 수밖에 없다.

그래서 명확하게 해결하려면 fun 내부에서 처리하는 게 가장 좋은 방법일 수 밖에 없다.

java였으면 아래와 같이 처리해야 했을 것이다.

public void printTest(String a) {
  if (a != null) {
    System.out.println(a);
  } else {
    System.out.println("");
  }
}

하지만 kotlin은 아주 단순하게 처리하는 게 가능하다.

fun printTest(a: String?) {
  println(a ?: "")
}

받는 쪽의 변수에 ?을 명시해주고, a의 값이 비어있을 때 ?:을 이용해서 뒤쪽의 공백을 출력하도록 만들어주면 간단하다.

이렇게 접근하면 Java에서 null을 보내더라도, 오류가 나지 않는 코드로 kotlin을 활용하는 게 가능해진다.


마무리

코틀린은 그냥 언어이다. 언어이다 보니 업데이트했을 때 정말 안정적일까라는 문제가 남을 수밖에 없다. 다행히도, 1.0 이후로는 안정적으로 빌드가 일어나고 있다. 그리고 디컴파일 결과물은 우리가 해결할 수 있는 부분은 없다.

디컴파일 하다 보면 오히려 코드가 내가 작성한 java보다도 더 보수적이라서 느릴 수 있어 보인다. 하지만 kotlin으로 문법을 작성하는 시간을 아끼는 게 오히려 더 절약일 수 있다는 생각이 든다.

필자의 회사에서는 약 20% 정도의 kotlin을 적용한 상태이다. Google I/O 이후 가능한 한 손댈 수 있는 부분을 컨버팅 하였다. 화면 단위로 홈/레스토랑 리스트/메뉴 상세 페이지 정도가 코틀린으로 들어가 있다.



About Taehwan

My name is Taehwan Kwon. I have developed Android for 6 years and blog has been active for eight years.

Comments