안드로이드는 왜 코틀린을 채택했을까? + 안전한 코드란

woga·2022년 12월 11일
0

코틀린 공부

목록 보기
1/54
post-thumbnail

안드로이드는 자바를 잘 쓰다가 왜 코틀린을 채택했을까?

쉬운 언어라서? 함수형 프로그래밍이 유행이니깐?
우린 이 질문에 대해 근본적으로 생각해본적이 있을까

이 책에서 조금 더 근본적인 걸 이야기해보고 코틀린을 어떻게 하면 잘 다룰지 같이 고민해보는 시리즈를 시작해볼까 한다.

가장 첫 번째는

왜 코틀린일까? 자바는 어떤 특성을 지녔길래?

코틀린은 2011년에 탄생했지만, 자바 생태계에서는 최신 언어에 속한다. 최초 발표 이후 자바스크립트 가상 머신에서 실행되는 코틀린 버전과 네이티브 코드로 컴파일되는 코틀린 버전도 배포됐다. 이 때문에 코틀린은 자바보다 범용적인 언어가 됐다.

현재 코틀린은 JVM 버전이 가장 많이 사용된다. 구글이 안드로이드 개발 공식 언어로 채택하면서 JVM에서 코틀린 사용이 많이 늘어났다.

안드로이드에서 코틀린을 사용하면 자바 11이 제공하는 기능이나 그 이상의 기능을 제공할 수 있다는 점이 구글이 코틀린을 채택한 중요한 이유 중 하나다.

심지어 Gradle도 Groovy를 대체할 언어로 코틀린을 공식 채택했다. 빌드할 대상 언어와 빌드 스크립트에 모두 코틀린을 쓸 수 있게 됐다. 이 얼마나 놀라운 일인가? 대체 코틀린.. 이 친구의 정체는 뭐길래 너도나도 코틀린을 채택하는 걸까?

일단 먼저 자바는 이 언어를 이루는 근본적인 concept이 있다.
바로, "WORA (Write Once Run Anywhere) : 한번 작성하면 어디서든 실행된다."라는 약속이다.

즉, JVM만 존재하면 어디서나 자바를 사용할 수 있다.
Java, when compiled, creates a bytecode (. class file), which can be run in any machine which supports JVM. So once compiled it doesn't require re-compilation at every machine it runs, JVM converts the bytecode to be understood by the underlying hardware

https://www.geeksforgeeks.org/why-is-java-write-once-and-run-anywhere/

약간의 반론이 있을 수 있지만 대체로 이 약속은 이뤄졌다.

거의 모든 플랫폼에서 자바로 작성된 프로그램을 실행할 수 있음은 물론이고, 다른 언어로 작성하여 JVM용으로 컴파일된 프로그램들도 실행할 수 있다. 코틀린도 바로 이런 JVM 기반의 언어이다.

또한, 자바는 이런 약속이 깨지지 않도록 존중해왔다. 그러나 이 약속 때문에 자바는 다른 언어가 채택해 온 여러 가지 개선 사항을 호환성 보존을 위해 포기해야만 했다.
실제로 자바는 설계된 지 20년도 넘었는데 그동안에 프로그래밍 방법론이나 패러다임에 아주 많은 변화가 있어도 하위 호환성을 깰 수 없어서 자바에는 도입할 수 없었다.
설령 도입되더라고 하위 호환성을 유지하고자 사용성을 일정 부분 포기하는 형태로 도입됐다.

이전 자바 버전에서 컴파일되던 프로그램은 다시 컴파일하지 않아도 새 자바 버전에서 실행되어야 한다.
이 하위 호환성에 대한 집착은 자바의 변화를 계속해서 방해하는 걸림돌이 됐다.

게다가 자바는 수를 처리하는 속도는 다른 언어만큼 빠른데, 원시 타입(primitive type)을 제공함으로써 빠른 계산이 가능하게 했다. 그러나 이 원시 타입은 List, Set, Map 등의 컬렉션에 원시 타입을 직접 넣을 수 없다. 이 때문에 원시 타입 값을 감싸고 또 이 객체를 벗겨내 원시 타입 값을 얻어내는 과정을 boxing , unboxing이라고 부르기도 한다.

그래서 이런 한계를 해결하려고 그루비, 스칼라, 클로저 같은 새 언어가 발표됐고, 이 언어들은 어느 정도 자바와 호환된다.
(이런 언어로 개발한 라이브러리를 자바에서 사용할 수 있는 정도다, 스칼라는 별도의 프로젝트로 빌드되거나 최소한 별도의 모듈로 빌드되어야한다.)

그러나 코틀린은 다르다. 코틀린은 한 프로젝트 안에서 자바와 코틀린 소스 코드를 혼합해 사용해도 될 정도다. 게다가 코틀린은 자바가 하위 호환성을 포기하고 발전해 왔다면 택했을 법한 모습을 하고 있다.

더 중요한 사실은, 코틀린이 함수형 프로그래밍에서 비롯된 수많은 기법에 더 친화적으로 설계됐다는 점이다.

코틀린은 불변 참조와 가변 참조를 모두 제공한다.

또한, 코틀린은 여러가지 이점이 있는데.

  • 전통적인 제어 구조를 회피할 수 있게 도와주는 다양한 함수형 추상화를 제공한다.
    toFloat(), toInt() 등등

  • 보일러플레이트 코드 최소화
    단 한줄의 코드로 프로퍼티가 들어 있는 (아예 없거나) 클래스를 만들수도 있다. data class
    equals, hashCode, toString, copy 함수도 자동으로 추가된다.

  • 같은 프로젝트에서 자바와 코틀린 소스 파일을 한 빌드 체인 안에서 사용할 수 있다.
    이는 서드 파티 라이브러리를 사용하는 것과 마찬가지라서 팀 프로그래밍에서 판도를 바꿔놓았다.

이로써 자바에서 코틀린으로 이전하기 쉽고, 다음과 같은 특징의 프로그램을 작성할 수 있다

  • 더 안전하다.
  • 프로그램 작성, 텍스트, 유지 보수가 더 쉽다.
  • 규모 변경이 더 쉽다.

그럼 다시 제일 첫 질문으로 돌아가 생각해보자. 이런 언어가 있는데 코틀린을 채택하지 않을 이유가 없지 않겠는가?
코틀린을 쓰면 즉, 안전한 프로그래밍을 할 수 있다. 그럼 이 안전한 프로그래밍은 뭘까?

안전한 프로그래밍

프로그래밍은 위험한 활동이다. 앉아서 코딩만 하는데 손목과 허리 건강이 위험해지는 정도만 있지 않을까 생각하겠지만, 주된 위험은 코드에 숨어있는 버그다.

예를 들면 미니애폴리스 연방 준비은행 프로젝트의 Y2K 버그가 있다. 1960년대부터 1990년까지 작성된 코드 상당수는 프로그래머들이 그 코드가 21세까지 존재하리라는 예상을 하지 못했다. 그렇기 때문에 연도를 저장할 때 2자리 숫자만을 사용했고 2000년을 1990년으로 취급하게 됐다. 이 버그로 인해 2017년에 실제 계산한 결과 4,170억 불에 달한 손해를 입었다.

또 다른 버그론 운항 시스템에 있던 버그로 1996년 6월 4일 프랑스 아리안 5호 로켓이 36초만에 추락했다. 이 버그는 바로 정수 연산 오버플로였고 때론 단순하게 해결될 버그가 이 프로젝트에서는 3억 7천만 불이라는 손실이 발생했다.

이 사례들을 보고 얼마나 프로그래밍을 안전하게 해야하는지 더욱 느껴보게 되지 않았는가
개발자들은 아래와 같이 시간을 소비한다.

버그에는 눈에 띄는 버그와 눈에 띄지 않는 버그가 있기 때문에 우리의 시간은 때론 버그를 찾기 위해 더욱 더 소비하게 된다. 이런 문제, 버그를 줄이고 버그를 해결하기 위해 어떻게 하면 될까? 버그를 완전히 없애지는 못한다. 차라리 서비스가 실행되지 않거나 실행되는데 누구보다 눈에 띄는 버그면 운이 좋다. 프로그램을 실행하자마자 바로 문제를 알 수 있기 때문이다.

버그 발생을 줄이기 위해 여러 프로그래밍 기법도 있다. 예를 들면 복잡하게 코드 짜는 것보단 버그 없음이 명확히 보이는 단순한 프로그램을 작성하는 방법이다.

그 외에도 안전하게 만들기 위한 몇 가지 방법이 있다.

  • 가변 참조(변수) 사용을 피하고, 상태 변이를 피할 수 없는 경우에는 그 부분을 추상화하라
  • 제어 구조를 피하라
  • Effect를 작성하는 코드의 일부 영역 안에서만 일어나도록 제한하자. (즉, 파일 입출력, 데이터베이스, 네트워크 등의 장치에 데이터를 쓰는 등의 행위를 특정 영역으로 한정하고 그 외 나머지 영역을 하지 말도록 하는 것이다)
  • 예외를 웬만하면 던지지 말자.(서비스의 중단까지 필요한 버그가 아닌 경우) 예외를 던지는 것은 무조건 분기(GOTO)의 현대적인 변형이다. 이로 인해 스파게티 코드가 될 수도 있다.

스파게티 코드란? 프로그램(코드) 흐름이 어디서 시작하는지 알 수 있지만, 어디로 흘러가는지 제대로 따라갈 수 없다.

안전하게 부수 효과(Side Effect) 처리하기

앞에서 Effect 이야기를 했는데 이 Effect는 외부 세계(네트워크, 파일 혹은 콘설 입출력, 데이터베이스 등등)와의 모든 상호 작용을 뜻하는 말이다.

프로그램은 일반적으로 자신만의 영역이 있는 작은 블록으로 이뤄진다. 자바에서는 이를 Method라 부르고 코틀린에서는 Function이라 한다. 이 함수는 수학적인 함수기 보단 기본적으로 메서드다.

메서드라는 코드 블록에는 영역이 있다. 이 메서드들은 다양하게 구성되는데, 일부 메서드는 값을 반환한다. 또 다른 메서드들은 클래스 속 상태를 변경한다. 그리고 다른 메서드들은 값을 반환하는 동시에 상태를 변경하기도 한다.

값을 반환하는 메서드나 함수가 외부 상태를 변경하는 경우 이를 Side Effect(부수 효과)라고 한다.

즉, 우리가 어떤 값을 반환하는 메서드를 사용했는데 이를 이용해 함수 외부 상태를 변경하면 이 의존으로 하여금 변화가 발생할 수 있다는 뜻이다.

안전한 프로그램(코드)은 인자를 받아서 값을 반환하는 여러 함수를 합성해 만들어진다. 우리는 함수 안에서 어떤 일이 벌어지는지 신경 쓰지 않는다. 그렇기 때문에 이 함수 안에서 일어난 변화로 외부 세계까지 변경되는 사이드 이펙트를 없애는 것이 좋다.

그럼 이 사이드 이펙트를 없애는 것만으로 프로그램(코드)은 충분히 안전해지는 걸까?

참조 투명성으로 프로그램을 더 안전하게 만들기

안전하게 만들려면 외부 세계로부터 영향을 받아서도 안된다.

외부 세계(입출력, DB, API 등)의 상태를 변경하지도 않고 외부 상태에 의존하지도 않는 코드를 일컬어 참조 투명(referentially tranparent)하다고 한다.

  • 참조 투명한 코드는 자기 완결적이다.
    어떤 문맥에서나 그 코드를 사용할 수 있다. 단지 올바른 인자를 제공하기만 하면 된다.

  • 결정적이다.
    같은 인자에 대해 같은 결과를 보장하기 때문에 개발자를 놀라게 하는 일이 없다.

  • 절대 예외를 던지지 않는다.
    다만, OOM이나 SOE를 발생시킬 수 있다. 이는 API를 사용하는 쪽에서 처리할 수 있는 오류 상황이 아니다 프로그램에 버그가 있는 거다.

  • 예기치 않게 다른 코드가 실패하는 상황을 만들지 않는다.
    인자를 변경하거나 다른 외부 데이터를 변경하지 않으며, 그에 따라 코드를 호출하는 쪽의 데이터가 오염되거나 동시 접근으로 오류가 발생하는 경우가 없다.

  • 자신이 제대로 작동하기 위해 외부 장치에 의존하지 않는다.
    외부 장치(DB, 파일 시스템, 네트워크)를 사용할 수 없거나, 외부 장치가 너무 느리거나 고장 나서 코드가 계속 대기 상태에 머무는 경우가 없다.

참조 투명 프로그램

참조 투명하지 않은 프로그램

혹시 순수함수의 개념이 생각나지 않는가? 조금 더 카테고리화 한 개념이라고 보면 된다. 참조 투명하다는 말은 아래 위키와 덧붙인 링크에서 자세하게 확인 할 수 있다.

표현식과 관련된 모든 함수가 순수 함수라면, 이 표현식은 참조 상 투명하다.
https://ko.wikipedia.org/wiki/%EC%B0%B8%EC%A1%B0_%ED%88%AC%EB%AA%85%EC%84%B1

참고로 부수 효과(side effect)가 있는 함수는 비순수 함수(non-pure function)라 한다.

이로 인한 안전한 프로그래밍의 이점

  • 프로그램이 결정적이기 때문에 추론하기 더 쉽다.
    입력이 같으면 항상 출력도 같다.

  • 프로그램을 더 쉽게 테스트할 수 있다.
    사이드 이펙이 없으므로 테스트하면서 프로그램 컴포넌트를 외부와 격리하기 위해 쓰는 mock을 사용할 필요 없다!

  • 프로그램을 더 모듈화할 수 있다.
    입력과 출력만 있는 함수로 구성하기 때문이다. 사이드 이펙이 없기 때문에 처리하지 않아도 되고 예외도 없으며 상태에 대한 변경도 없어서 추적하지 않아도 되고 가변 상태를 공유하지 않으므로 동시 변경이 일어나지 않는다.

  • 프로그램을 훨씬 쉽게 합성하고 재조합 할 수 있다!

  • 공유 상태 변이를 피하므로 프로그램이 태생적으로 thread-safe하다.
    공유하려는 데이터가 모두 불변이어야 한다. observing 되는 상태 변수라도 불변 데이터가 더 안전하다.

마치며

간단하고도 어렵게 왜 코틀린이 채택되었는가 + 안전한 프로그래밍에 대해 알아보았다. 이 모든 글은 코틀린을 다루는 기술 이라는 책을 요약한거며 자세한 이야기는 해당 책의 1장을 보면 된다.

간단한 예제와 더 많은 설명이 있는데 하나하나 옮기기보단 요렇게 마무리하는게 좋을 거 같다!

이 파트를 읽으면서 용어 정리를 한 번 한 기분이었고 근본적인 궁금증과 왜 다들 UseCase는 순수함수여야 한다고 주장하는지 더욱 잘 알게 되는 깊이 있는 파트였다.

사실 이해하기 쉽지 않았는데 읽을수록 이해된다! 앞으로 더 많은 파트에서 다룰 이야기들이 궁금해진다

profile
와니와니와니와니 당근당근

0개의 댓글