우당탕탕 개발_𝒍𝒐𝒈

kotlin) 표준함수 이론 정리와 고민에 대한 생각의 과정 본문

대외 활동/데브코스

kotlin) 표준함수 이론 정리와 고민에 대한 생각의 과정

hojeong01 2024. 10. 16. 23:14

이번주부터 코틀린을 새롭게 배우기 시작했다. 

먼저 짧게 소감을 말하자면.. 굉장히 자유로운 친구다!

그래서 재미있지만 또 한편으로 아직까지 낯가림의 시간을 가지고 있다. 

좋은 점은 자바의 이론을 슬슬 잊어갈 때 자바와 비교하며 공부를 하게 되니 일석 이조의 효과를 보고 있는 것 같다!!

 

열심히 공부하고 얼른 only 코틀린 플젝을 해보고 싶다🔥

 

오늘은 코틀린에서 제공해 주는 다양한 표준함수에 대해 간단히 복습해 보고 

공부하면서 생긴 고민점과 그것을 해결하는 과정을 블로깅해보려고 한다.


한눈에 보는 kotlin의 표준 함수 

표준함수 반환값 내용
apply 객체 '나 자신' 객체의 초기화 및 수정
run 결과 값 객체 안에서 작업을 수행 후 결과를 반환
with 결과 값 객체와 관련된 작업 처리, 객체는 인자로 넘김
let 결과 값 객체를 인자로 받아 변환 및 작업 수행
also 객체 '나 자신' 추가 작업을 수행하고 객체 반환

 


고민점 1. 자바는 왜 표준 함수를 제공하지 않은 걸까?

apply 함수를 통해 해결해 보기

1) 코드 비교 

 

1 코틀린

class Person(var name: String?= null, var age: Int? = 0)

fun main() {
    //객체 초기화
    val person = Person().apply{
        name = "ho"
        age = 20
    }

    println("이름 : ${person.name}, 나이 : ${person.age}")

    //객체 수정
    person.apply { name = "go"}
    println("이름 : ${person.name}, 나이 : ${person.age}")
}

 

2 자바

public class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "이름 :" + name +"  나이 :" + age;
    }
}
public class ApplyTest {
    public static void main(String[] args) {
        Person person = new Person("ho",20);
        System.out.println(person.toString());

        person.setName("go");
        System.out.println(person.toString());
    }
}

참고로 자바가 getter, setter를 설정하는 부분을 보기 위해 lombok 어노테이션은 사용하지 않았다. 

 

왜 자바에서는 표준함수를 제공하지 않았을까?

이 편리한 함수가 만들어지게 된 배경이 궁금하여 곰곰이 생각해 보았다. 

자바와 코틀린의 프로퍼티 설정 차이에서부터 이해하면 위 질문에 대한 고민이 정리가 될 것 같았다. 

 

우선 자바는 위 코드처럼 개발자가 필드(프로퍼티)와 geetter, setter 메서드를 명시적으로 작성해 주어야 하고 

객체의 초기와, 수정을 하는 과정 또한 수동으로 이루어지는 구조를 가졌다. 따라서  애초에 apply와 같은 

편리한 함수를 사용할 수 없는 구조라고 생각하면 된다. 

 

반면 코틀린은 필드를 자동으로 관리할 수 있는 '프로퍼티' 개념이 있기 때문에 getter, setter를 자동으로 생성해 주는 구조를 가지고 있다. 즉 코틀린이 추구하는 목적자체가 개발의 생산성을 높이는 것이기 때문에 객체를 수정할 때 apply와 같은 편리한 표준함수들이 제공되고 이를 사용할 수 있다고 생각하면 된다. 

 

만약 자바에서도 위처럼 쓰고 싶다면... 메서드를 따로 만들어 주면 되지 않을까? 

 

아래는 자바와 코틀린의 객체 수정 코드를 간결하게 비교한 내용이다.

// 코틀린 객체 수정
person.apply { name = "go"; age = 30 }

//자바 객체 수정 
person.setName("go");
person.setAge(20);

자바는 각각 별도의 set 메서드를 호출하여 객체의 수정을 진행하였지만 

반면 코틀린은 한 블록 안에서 간결히 처리하는 모습을 발견할 수 있다. 

 


고민점 2.  run / with  함수가  유사해 보여 , 어떤 차이점을 두고 활용해야 해?

세 가지 함수에 대해 공부를 하다가 문득 

"수행하는 내용은 유사한 것 같은데 무슨 차이가 있지?"라는 의문이 생겼다. 

왜냐면 이론으로 보았을 때는 차이를 이해했는데 막상 코드를 작성하고 보니 결국 수신의 방법에만 차이가 있는 거 아니야? 왜 굳이 두 가지의 수신 방법을 만들어 놨을까 고민이 되었다.  

 

아래는 간단한 정의와 코드를 적어두었다. 

 

  세 가지의 함수 비교하기  

1.run - 수신 객체(나)에 대한 작업을 하고, 그 결과를 반환한다.

2.with - 다른 객체를 전달받아 그 객체에 대한 작업을 하고 결과를 반환한다.

    val value = person.run {
        "name : ${name}, age : ${age}"
    }
    
    val result = with(person){
        "name : ${name}, age : ${age}"
    }

 

 

이렇게 보면 아무리 봐도 정확히 어떤 상황에서 사용이 되는지 도저히 이해가 안 가서.. 실제 프로젝트에서 두 가지의 함수를 어떻게 사용할까 고민해보았다. 

 

두가지의 관점에서 각각 run과 with의 쓰임을 나누어 보았다.

1. 회원정보 수정 = run

보통 회원정보를 수정하려고 할 때 이미 인증된 유저의 정보를 가지고 있다 

이런 경우에는 '내가 가진 정보'를 수정하기 때문에  주로 run을 활용한다.

class User(val name: String, var email: String) {
    fun updateEmail(newEmail: String) {
        run {
            email = newEmail
        }
    }
}

 

2. 회원가입 작업 = with

 dto처럼 외부에서 정보를 객체로 전달받아 올 때 

이 객체를 사용하기 위해서 주로 with 함수를 사용한다.

data class UserDTO(val name: String, val email: String, val password: String)

fun SignUpData(userDTO: UserDTO) {
    with(userDTO) {
        println("user: $name, $email")
    }
}

 

즉 결과를 반환하는 것은 동일하지만 각 상황에 따라 적절히 사용해야 하는 함수들인 것 같다.. 

 

이 두 가지의 함수는 실제 프로젝트하다 보면 자연스럽게 이해될 부분인 것 같다.


여기까지 공부하다 보니 run과 with으로 충분히 null 처리가 가능할 것 같은데 굳이 let이 추가로 제공된 이유가 궁금해지기 시작했다.   

 

3.let으로 null 처리를 하는 이유

그 이유를 정말 간단하게 설명하면 

let 함수는 null이 아닌 경우에만 작업을 수행할 수 있는 구조로 되어 있기 때문이다.

 

let은?. 연산자와 함께 쓰이기 때문에 객체가 null 이 아닐 때만 작업을 진행할 수 있게 설계되어 있다.

val nameLength = name?.let {
    it.length
} ?: 0  // null인 경우 0 반환

 

코틀린의 안전한 Null 연산자 

더보기
  • 코틀린은 다양한 널 관련 연산자를 통해 기존 자바에서 볼 수 있던 NullPointerException을 방지할 수 있게 해준다.
    • Nullable 타입 선언
      • | var name:String? = null
    • 호출 연산자
      • ?. | A?. B | A가 null -> null / notnull -> B 실행
      • ?: | A?:B | A가 null -> B 실행
      • !! | A!!B | A가 notnull -> 실행 / null -> 예외 발생

지금까지 코틀린의 표준함수를 공부하며 생겼던 고민점들을 기록해 보았다. 

나와 같은 고민을 하는 사람들에게 

이 글이 도움이 되길 바라며 마무리하겠다!