본문 바로가기
안드로이드 스터디

[Kotlin] 언제 뭘 써야 돼? 헷갈리는 스코프 함수 한 방 정리

by HAERO_KR 2021. 3. 15.

 

코틀린은 기본적으로 '표준 스코프 함수 (Scope Function)' 라는 것을 제공한다.

스코프 함수는 특정 객체의 컨텍스트 내에서 특정 동작 (프로퍼티 초기화, 활용 등) 을 실행하기 위한 목적만을 가진 함수다.

스코프 함수를 람다 함수로 사용하게 되면 임시로 스코프를 형성하는데, (그래서 이름이 스코프 함수다.)

이 스코프 내에서는 객체의 이름을 통해 일일히 참조할 필요 없이 객체를 접근하고 핸들링할 수 있다는 편리하다는 장점이 있다.

 

코틀린이 제공하는 스코프 함수는 총 5가지로, [ apply, run, with, alse, let ] 로 이루어져있다.

그런데, 얼핏 보기에는 역할이나 수행하는 기능들이 너무 비슷해서 무슨 상황에 무엇을 써야할 지 헷갈리게 된다.

필자도 엄청 헷갈려서 매번 닿는대로 아무거나 썼기에, 이게 잘 쓰고 있는게 맞는건지 의구심이 점차 쌓이게 되었다. 

 

따라서 각 스코프 함수들의 차이점, 특징들을 열심히 찾아보고 정리한 내용을 이렇게 블로그에 담아보기로 했다.


1. apply

위와 같이, Book 이라는 클래스가 있다고 가정해보자. Book 객체는 name 과 price 를 프로퍼티로 갖게 된다.

그리고 price 를 핸들링하는 discount() 라는 함수를 갖게 된다. (책 값을 2000원 할인하는 기능을 갖는다)

이 때 apply 함수는 아래와 같이 사용할 수 있게 된다.

 

실행 결과

쉽게 말해 apply 함수는, 인스턴스를 새로 생성하고 특정 변수에 할당하기 전에 초기화 작업을 해줄 수 있는 스코프를 만들어 준다.

따라서 apply 함수 내의 모든 명령이 수행되고 나면 명령들이 적용되어 새로 생성된 인스턴스를 반환한다는 특징을 갖고 있다.

 

2. run

run 은 apply 와 명확한 차이점이 있다. 바로 반환하는 것이 생성된 인스턴스가 아닌 스코프 내 명령 실행 결과 값이라는 점이다.

이미 만들어진 인스턴스의 값 혹은 그를 이용한 특정 계산 결과를 필요로 하는 경우, run 을 활용해 이를 반환받아볼 수 있다.

 

아래 예제에서는 run 스코프 내에서, 위 예제에서 a 라는 Book 인스턴스를 생성할 때 discount() 를 통해 할인을 적용했었기 때문에

이 책의 원가를 계산하여 반환하는 동작을 구현해보았다. 또한 인스턴스의 프로퍼티를 출력하는 구문도 넣었다.

run 의 스코프 내의 마지막 라인을 보면 'price + 2000' 이라고 되어있는데, 실제로 이 값이 반환된다.

실행 결과를 보면 실제로 10000 이 반환되는 것을 알 수 있다

run 은 특정 인스턴스의 프로퍼티를 출력하거나 계산값으로 활용하는 등의 핸들링을 할 때 사용하면 편리할 것 같다.

 

3. with

살짝 허무하게도, with 는 run 과 생긴 것만 다를 뿐 동작 상, 특징 상 차이점이 전혀 없다 (...)

아래와 같이, run 참조 연산자가 아닌 파라미터 형태로 스코프 함수를 열게 된다.

이 외에는 짚을 점이 없으니 넘어가도록 하자. (입맛대로 쓰면 될 것 같다)

 

4. also / let

also 와 let 함수는 위에서 언급했던 함수들과 동작 (리턴 값) 이 아래와 같이 일치한다.

생성된 인스턴스 반환 최종 실행 결과 반환
apply run
also let

하지만 also / let 은 apply / run리턴 형식이 다를 뿐, 조금 다른 특징을 갖고 있다.

바로 'it' 키워드의 사용이 가능하다는 점이다.

 

'it' 키워드가 무슨 역할을 하는지 이해하기 위해 아래와 같은 상황을 보자.

위와 같은 코드를 실행했을 때, a 라는 인스턴스 (책) 의 이름과 가격이 출력되길 기대했으나

아래와 같이 이상한 값이 출력되는 것을 확인할 수 있다.

그 이유는 바로, main() 스코프 내에 인스턴스 프로퍼티와 이름이 같은 변수가 있어서 이를 출력해버렸기 때문이다.

run 에서 상위 스코프인 main() 스코프의 동명의 변수를 참조한 것이다.

 

실제로 개발을 하다보면, 변수 이름으로 마땅한 것이 한정적일 때 위와 같은 혼란이 일어날 수 있다.

also 와 let 은, 이와 같은 혼란을 방지하기 위하여 'it' 이라는 키워드를 제공해준다.

 

위 예제에서 run 을, 같은 리턴 값을 갖지만 'it' 키워드를 지원하는 let 으로 변경해보자.

이제야 main() 스코프의 price 가 아닌 a 인스턴스의 프로퍼티를 출력하게 되었다.

사용법은 간단하다. it 키워드에 참조 연산자를 통해 프로퍼티 및 함수를 접근하면 된다.

 

이 때문에, 스코프 함수라는 것이

특정 객체 컨텍스트 내에서 특정 동작을 한다는 점에서는 also 와 let 이 가장 좋은 스코프 함수같다. (개인적인 견해이다)


이제 상황에 맞게, 용도에 맞게 적절한 스코프 함수를 사용할 수 있을 것 같다.

필자처럼 이것들의 차이가 헷갈렸던 사람들에게 이 글이 도움이 되었으면 좋겠다.

 

이제 상황에 맞게, 용도에 맞게 적절한 스코프 함수를 사용할 수 있을 것 같다.

필자처럼 이것들의 차이가 헷갈렸던 사람들에게 이 글이 도움이 되었으면 좋겠다.