본문 바로가기

우테코/level1(코틀린)

[우테코]02/10 level1 두번쨰 수업 자동차 경주 피드백

1.변수명

이름을 잘짓는건 항상 중요하다!!!

 

grep.app 이 사이트에서 검색해서 많이 적용한다고한다.

-> 깃에서 많은 스타를 많이 받은 레포지토리들이 어느 단어를 쓰나 검색할수 있는 사이트이다.

 

변수 명에서 다양한 고민이 있을때 사용하겠지만 이러한 예시도 있다

 

영국식 영어랑 미국식 영어중 어느 단어를 쓸까? ex) 영국식 : judgment  -> 미국식은 e가 하나 더들어간다고 한다.judgement

judgement 로 검색해보면 실제로 우테코가 나온다(밑의 이미지에 빨간색 부분이 유명한 레포지토리들을 나타낸거라고 한다.) 

이런것중 많이 사용한것으로 결과가 나오는것으로  사용하는것도 좋은 방법이라고 한다.

-> 우테코의 judgement 코드를 실제로 이렇게 적용한거라고 한다 .

tip: 주소창 뒤에 &filter[lang][0]=Kotlin 을 입력하면 코틀린 코드만 뜬다.

 

 

2.패키지명은 주제로 하자!!!

패키지명을 step 1이런식으로 안하고 calculator이런식으로 하는걸 더 추천한다고 한다.

-> 실무에서는 대부분 리펙터링 하기 때문에 기존의 코드를 뜯어고치는 연습을 하므로 step으로 패키지를 나눈다면 step2로 리펙토링할때 애매한 상황이 벌어지게 될것이다.

 

3.매개변수명도 고민하자(Random 같은거 붙일필요 있는가?)

car에서는 숫자를 받을때 random인지 아닌지 알필요가 없다 어짜피 숫자를 받아서 판단하는것이니까

-> 이런 random이 붙는다면 만약 이 함수를 사용하는 사람이 미래의 나이거나 다른 개발자라면 random을 돌려서 넣어야하나 한참더 고민하게 될것이다. (함수를 사용하는 다른 개발자는 random을 넘겨야하나? 뭐를 더 해야하나? 이런의문이 들것이다.)

어짜피 앞에서 random 을 뽑는 과정을 거친후 함수를 호출하니 맥락상 중복된 random은 필요없을것이다.

 

4. 코딩컨벤션 순서부터 지키자!!!

코딩컨벤션 순서를 항상 맞춰야 할것이다.

코딩컨벤션을 왜맞추는가? -> 다른 개발자랑 협업해야하니까 순서만 맞아도 코드를 읽는데 훨씬 편해질것이다.

 

5.주생성자와 부 생성자

주생성자는 특징이있다.

->생성자 파라미터를 지정하는것 뿐만 아니라 생성자 파라미터를 지정하고 파라미터에 의해 초기화되는 프로퍼티를 정의한다.

즉 외부에서 값을 받아서 초기화하는데 특화되어있다.

 

주생성자만으로는 부족할때가 있기 떄문에 초기화블록이 있다  -> 초기화블록은 여러개가 존재할수 있다(여러개 써도되겠지만 굳이?).

-> 초기화블록 실행 순서는 순서대로이다.

 

위의 Init block은 이런식으로 대체할수 있다.(당연한소리)

 

 

부생성자

주생성자를 무조건 마지막에 호출할수밖에없는 코드

부생성자 체인을 타서 여러개를 만날수도 있는것이다. -> 결국 주생성자를 호출해야함

부생성자의 용도는 결국 여러 여러종류(방법)으로 파라미터를 받고싶을때 부생성자를 통해서 입력값에 따라서 적절히 처리하게 될수 있을것이다.

근데 걍 기본값같은경우에는 주생성자에서 처리하자(매개변수 기본값으로)

 

6.가시성 변경자

코틀린은 프로퍼티 기반 언어이기 때문에  기본적으로 필드가 없다.

프로퍼티란? : 필드,getter,setter 을 합친것이다

그래서 코틀린은 변수를 선언할때 기본적으로 getter,setter도 자동으로 생성된다.

 

getter,setter관련된 학습은 이번 온보딩 피드백 반영한 글에 자세히 설명했다.

 

7.생성자

함수가 많을수록 단일책임원칙에 벗어날 가능성이크니 생성자를 이용해서 함수를 줄이자.

 

단일책임원칙은 solid의 s를 담당하는 Single Responsibility Principle이다.

뭐 간단하게 이야기하면 하나의 객체는 반드시 하나의 동작만의 책임을 갖는다 이다.

객체 하나가 책임을 많이 가질수록 많은 일들을 당연히 할테고 해당객체가 바뀌면 다른 코드들의 변경도 미친듯이 일어날테니 각각 객체를 한가지일만 하도록 만드는것이다.

https://blog.itcode.dev/posts/2021/08/13/single-responsibility-principle

 

[OOP] 객체지향 5원칙(SOLID) - 단일 책임 원칙 (Single Responsibility Principle) - 𝝅번째 알파카의 개발 낙

올바른 객체지향 설계를 위해 수립한 원칙이 있으며, 이 다섯 가지 원칙을 통틀어 객체지향 5원칙(SOLID)이라 명명한다. 필수로 적용하지는 않지만, 적어도 이 규칙을 준수하면 준수할 수록 올바

blog.itcode.dev

이글을 참고하자

 

8.상수

상수는 기본자료형 이외에는 const 를 사용할수 없다 하지만 그냥 snake case인데 대문자로 써도된다.

 

상수는 정의를 동반객체 혹은 최상위 수준에 할수 있는데 각각의 장단이 있기 때문에 각자의 기준을가지고 사용하여보자

 

9.유틸리티 클래스

여태 유틸리티 유틸함수 이런것들이 무슨의미인가 했는데 자바에서 여기저기 쓰여서 애매하게 어디에 포함시킬수가 없는것을 

자바는 객체없이는 아무것도 못하기 떄문에 상태와 인스턴스가 없는 정적 메서드만 있는 클래스가 유틸클래스였다.

 

당연하게 코틀린은 유틸리티 클래스가 필요없다.

 

함수 자체가 일급시민이기 떄문에 함수를 최상위에 놓을수 있다 -> 클래스에 포함안되도 되니 그냥 최상위에 넣으면 되는것이다.

자바는 람다 어쩌구 저쩌구 해봐야 다 편법이고 함수가 일급객체가 아니라 인자로 함수도 못넣고 리턴도 못한다.

코틀린 미만잡 ^^

 

10.스마트 캐스트

코틀린은 스마트 캐스트를 지원한다 . -> 컴파일러가 알아서 바꿔줌 고마워요 컴파일러

좀더 심화적인 개념을 보면 계약이라는것이 있다고 하는데 좀더 공부해보면 좋을것같다

 

IDE에 딸려있는 컴파일러가 알아서 돌면서 보정해주는것이다 고마워요 jetbrain 

어쨋든 같은 블록이 아니더라도 문맥상 위에서 타입검증같이 캐스팅 할것들이 일어나면 코드가 순서가 밑에있으면 알아서 컴파일러가 캐스팅해준다.

 

참고로 자바는 스마트 캐스트가 없다고한다.

코틀린 짱짱맨 코틀린미만잡.

 

11.require 와 check

require 블록을 잘 이용하자 (null 체크또한 이용하기 좋다)

require 의 두번째 인자로 람다를 넘겨주는것에서 반환값이 string 이면된다. -> 어떤 작업을 블록내에 거쳐도 string 만 리턴하면된다(오류 메세지가 됨)

 

 

라이브 코딩

이번 라이브 코딩에서는 random 함수 같이 테스트 하기 어려운것들을 

항상 인자로 받아서 테스트 하는 방법을 이용해왔는데

 

실무에서는 함수의 시그니쳐(함수의 이름,매개변수의 개수,매개변수의 자료형,반환값의 자료형)를 못바꿀 가능성이 엄청 크기 때문에 시그니쳐를 바꾸지않고 테스트 하는 방법을 이용해 본다고 한다.(하위 호환성을 보장하며 테스트 하는것)

 

실무에서 왜 함수의 시그니쳐를 못바꾸는가? -> 엄청 많은곳에서 호출되고 있을 수 있고 외부 모듈에서도 호출되고 있을수있다.

 

함부로 검증되지 않은 상태에서 랜덤을 테스트 하기위해 인자를 추가하는 형태로 가면 api의 사용처에서 모두 다 터져버릴 수 있다.

그냥 오버로딩 함수를 뚝딱 만들수있는 작은 프로그램이라면 상관없겠지만 실제 배포되고있는 어플리케이션 같은것들은 같은기능을 하는 함수를 단숨에 뚝딱 만들수 없을 뿐더러 그걸 한방에 바꾸는데 시간이 얼마나 걸릴지 모른다.(한달이 걸릴수도 있단다)

일단 돌아가고 있는 상태를 유지하면서 점진적으로 리펙터링을 수행해야하기 때문에 일단 기존의 랜덤같이 어려운 코드들을 시그니처를 바꾸지 않고 그냥 원상태를 유지하여 기존코드를 지원하며 우선 거지같이 짠 코드라도 테스트를 수행하도록 만들어 검증한다.

그리고 나중에 오버로드한 함수를 새로 만들어내 점진적으로 api 사용처에서 바꿔나가는 것이다.

 

즉 잘못짠 코드를 리펙터링 하기전에 임시로라도 테스트를 진행하는것이다 -> 현재 사용하고 있는 코드니까 

 

제이슨이 어여쁘게 짜준 코드를 내가 기억을 더듬으며 오염시킨 코드임을 가만하고 문맥만 파악하며 예시코드를 보는게 좋을것같다.

 

예를들어 car의 move를 테스트 하고싶다 하지만 car에 테스트 하기 힘든 random 을 뽑는 로직까지 한꺼번에 포함되어있는것이다. 

 

package racingcar.domain

class Car(var name: String) {
    var position: Int = 0
        private set

    fun move() {
        if ((1..9).random() >= 4) {
            position++
        } else {
            position--
        }
    }
}

 

이런경우 일단 테스트 하기 어려워지는 요인인 random을 함수로 분리하는것이다.

package racingcar.domain

class Car(var name: String) {
    var position: Int = 0
        private set

    fun move() {
        if (getRandomNumber() >= 4) {
            position++
        } else {
            position--
        }
    }

    private fun getRandomNumber(): Int = (1..9).random()
}

그리고 클래스와 테스트하기어려워 분리시켜놓은 함수를 둘다 open 으로 열어준다.

package racingcar.domain

open class Car(var name: String) {
    var position: Int = 0
        private set

    fun move() {
        if (getRandomNumber() >= 4) {
            position++
        } else {
            position--
        }
    }

    open fun getRandomNumber(): Int = (1..9).random()
}

그럼 현재 코드의 장애없이 상속을 받아 다른 코드로 테스트를 해볼수 있는 환경이 조성된 것이다.

package racingcar.domain

import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.Test
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.ValueSource

class CarTest {

    @ValueSource(ints = [0,1,2,3])
    @ParameterizedTest
    fun `3이하이면 정지`(condition: Int) {
        val car = object : Car("matpig") {
            override fun getRandomNumber(): Int = condition
        }
        car.move()
        assertThat(car.position).isEqualTo(0)
    }
}

 그후 이렇게 테스트 자체에서 익명클래스를 통해 상속을 받아서 테스트가 힘든 부분을 대체해주고 나머지 로직을 테스트한다.

이러면 이 로직이 제대로 굴러가는 로직인지 판단할수있다.

 

이렇게 돌아가고있는 로직을 일단 테스트 해서 여유시간을 확보한후

 

테스트 하는데 문제가 있었던 함수를 분리하는 형태로 오버로드 함수를 작성해주고 점진적으로 api 사용처에서 바꿔나가고 다 대체 되었을때 기존 함수를 지워버리는 방법을 이용하면 문제없이 코드를 이용하며 테스트와 리펙토링을 진행할수 있다.

 

출처: 우아한테크코스 짱짱 코치 제이슨의 뇌