Computer + programming - 해당되는 글 7건

기록적인 폭염이 예정된 가운데, 냉장고 안 상태가 궁금하기도 하고, 정리도 할 겸...미뤄뒀던 샤오미 온습도계 값 읽어오기를 해봤다.

온습도 궁금하다고 냉장고 문 열어서 값을 읽으면 당연히 부정확하니까.

근데 막상 해보니까 생각보다 너무 금방 끝남;;;

 

* BLE가 되면 좋겠는데...하면서 생각 없이 봤더니 어? 되네? BT보다 편하겠네.

* 오 BLE...안잊어버렸어! 다 기억나!! 역시 인간은 고생을 해봐야.....

* 아니 이것들은 왜 멀쩡히 규격에 정의된 sensor profile 안쓰고 개나소나 자체 profile이야...라고 하기엔 좀 찔리긴 한데;;

* 지들이 만든거라 뭐가 뭔지 모르겠.........어라? 생긴게 ASCII같은데...? 어? 정말이다. 오...이걸로 걍 되네.

* SwiftUI는 참 편한데, 복잡한 경우는 잘 안될 듯. 어차피 gsync같은 걸 할 것도 아니긴 한데..Jetpack compose는 어떻게 될라나~

 

온도계 몇 개 더 살까...?

아예 위 아래 앞 뒤 다 넣어놓고, 차이가 심하게 난다 싶으면 fan도 강제로 돌리고...rpi나 남는 폰으로 아예 alert까지........는 당연히 귀찮;;;



Trackbacks  | Comments 

Java8에서부터, 그리고 Kotlin에도 lambda를 지원하면서, 익명 함수를 쓰는 경우가 대폭 늘어났다.

분명 작성해야 하는 code의 양을 줄여준다는 장점이 있지만, 익명 함수, 그리고 익명 클래스를 쓰는 건 VM입장에서 봤을 때 좋은 습관은 아니다.

 

이번에도 예를 들어, 달력 UI component를 만들 때 달력의 각 날짜에 onTouchListener를 붙인다고 가정해보자.

// 1월부터 그 달의 마지막 날까지
int lastDateOfTheMonth = getLastDateOfTheMonth();
for(int date = 1; date <= lastDateOfTheMonth; date++) {
	Date dateUI = new Date(date); // "날"에 해당하는 새로운 UI component를 만들고
    dateUI.setColor(...); // 기타 등등 설정을 하고
    dateUI.setOnClickListener((v) -> { parent.onTouchDate(date); });
    this.addDate(dateUI);
}

확실히 짧아서 좋은데, 문제는 이 code가 완전한 functional language 기반 위에서 수행되는 것이 아니라, JVM 위에서 수행된다는 점이다.

JVM 구현에 달려있긴 하지만, 위의 code는 일반적으로 아래와 같이 수행되는 셈이다.

// 1월부터 그 달의 마지막 날까지
int lastDateOfTheMonth = getLastDateOfTheMonth();
for(int date = 1; date <= lastDateOfTheMonth; date++) {
	Date dateUI = new Date(date); // "날"에 해당하는 새로운 UI component를 만들고
    dateUI.setColor(...); // 기타 등등 설정을 하고
    dateUI.setOnClickListener(new OnTouchListener() {
    	public void onTouch(View v) {
    		parent.onTouchDate(date);
        }
    });
    this.addDate(dateUI);
}

즉, setOnClickListener를 한 번 부를 때 마다 객체를 하나씩 생성한다.

위의 예에서는 달력이니까 30개 정도의 OnTouchListener를 만든다.

JVM에서 객체를 하나 만들면 거기에 따른 비용을 고려해야 한다.

더군다나 위의 예에서는 그냥 setOnClickListener(this)로 처리해도 되는 code인데....

 

게다가 요새는 UI 요구사항에 따라 하나의 class안에서 여러 개의 익명 함수, 익명 클래스를 만드는데, 간단한 code도 좋지만 성능도 성능이고, debugging 용이성에 미치는 영향도 잘 고려하는 게 필요하다.

물론, code 읽기도 편하고 쓰기도 편하겠지만, 어차피 같은 생명주기를 가지는 code들이라면 차라리 성능에 초점을 맞추는 걸 추천하는 편이다.



Trackbacks  | Comments 

개발자에게 가장 중요한 덕목에는 들어가지 않겠지만, 그래도 필요한 덕목 10가지 중에는 읽기, 쓰기가 들어간다.

거창하게 techincal writing이 아니더라도, 독해력은 간접적으로 programming에 꽤 많은 역할을 하고 있다고 생각한다. (programming도 결국 language로 하는 거라니까?)

 

그리고 많은 초보 개발자들이 "난 뭐 그 정도는 다들 알고 있는데"라고 하면서도 실천을 못하는 게 이름 짓기를 잘 하는 것이다.

변수, 함수, 하다못해 레이블을 하나 적더라도 이름을 지어야 한다.

이름 짓는 방법이야 이미 많고, 장점과 적용해야 하는 경우가 분명한데, 가장 중요한 건 "기준을 정하고 그 기준을 지키는 것"이다.

물론 여기의 기준에는 표기법도 포함되는 건 당연하다. 어디서는 camel case 적용하고, 또 다른 곳에선 hungarian 쓰고, 또 어디서는 섞어 쓰는 건 전체적인 일관성을 떨어뜨리고, 통일된 약속을 무너뜨려 결과적으로는 code를 읽고 해석하는 데 더 많은 자원이 들어간다.

 

그 중에서도 가장 기본적인 건, 변수는 명사로 짓고, 함수는 동사로 지어야 한다.

그리고 반드시 그 이름은 내용이 가리키는 바를 명확하게 명시해야 한다.

게다가 영어가 모국어가 아니니까 그렇다 쳐도, 그렇다고 요즘같은 시대에 internet에서 click 몇 번 하면 한영사전이 나오는데, "gogaek-bunho"라는 식으로 적혀있는 걸 보면.....

 

달력과 관련된 UI component를 만드는데, 처음에는 "padding"이라는 변수를 선언해두고, 나중에는 padding 계산이 날짜에 따라 바뀌는 code가 올라왔다.

자세히 보니, 그 달의 1일이 무슨 요일인지를 이 변수에 넣어두고, 이걸 이용해서 매월 "1"일을 왼쪽에서 빈 칸을 줘서 사용하는 것이다.

좀 양보해서 정확하게 그 용도로만 쓴다면야 상관 없을텐데, 이 변수의 값이 사실상 "그 달 1일의 요일 값"을 가지고 있다보니, 다른 계산들도 이 변수 값에 의지하게 되고, UI와 관계업는 내부 계산에서도 이 값을 의지하는 code까지 등장한다.

그럴 바엔 변수 이름을 애초에 바꾸는 게 맞는데, 처음 이름을 저렇게 지어놨으니 바꾸기 싫어하면.........

 

아무튼, 기초니까 이거 쉽네 하고 넘어가는 사람들이 꽤 많은데, 기초니까 잘 지키는 모습을 봤으면 하는 항목 중의 하나이다.



Trackbacks  | Comments 

조건문 얘기는 저번에 했으니...

if (1 <= someA && 9 >= someA && 1 == someA % 2) {
    someFunc(true);
} else if (1 <= someA && 9 >= someA && 0 == someA % 2) {
    someFunc(false);
}

정말 이런 걸 실전에서 볼까 싶은데도 방심할 수 없는 게 이 바닥이라..(사실 저번 else도 너무 당연한 거였지만)

위와 같은 code도 정말 자주 본다.

그래도 이 정도는 대부분 다시 생각 해보라고 하면 금방 아래처럼 가져온다.

if (1 <= someA && 9 >= someA) {
    if (1 == someA % 2) {
        someFunc(true);
    } else {
        someFunc(false);
    }
}

그런데 의외로 여기서 끝나는데...사실 이건 3항 연산자를 사용해주는 게 더 눈에 잘 들어온다.

if (1 <= someA && 9 >= someA) {
    someFunc( (1 == someA % 2) ? true : false );
    // Kotlin의 경우는 if를 쓴다.
    someFunc( if (1 == someA % 2) true else false );
    // 그리고 정말 이런 조건이면 3항 연산자를 쓸 필요도 없다.
    someFunc(1 == someA % 2);
}

뭐, 단순화 해놨으니 눈에 더 잘 보이는 거일수도.

 

그리고 동일한 형태의 함수 호출을 조건에 따라 여러 번 하는 건 가능한 자제하는 게 좋다.



Trackbacks  | Comments 

개발할 때 조건문 만큼 많이 쓰이고, 또 직관적인 구문이 또 있을까.

그런데 실용적인 측면에서 보면 쓰기 전에 한 번씩 잘 썼나 확인하는 구문이다.

조건문을 잘 쓰면, 보다 더 직관적이고, 간결하면서도 성능 좋은 결과물이나온다.

뿐만아니라, 안정성의 큰 줄기는 조건문에서 나온다고 본다.

그런 의미에서, 조건문을 쓸 때는 그 외, 즉 "else"일 때를 더 신경 써줘야 한다.

 

switch(transmissionType) {
    case manual:
        // 처리
        break;
      
    case automatic:
        // 처리
        break;
        
    case unknown:
        // 처리
        break;
}

자동차의 변속기 종류에 따라 처리하는 code를 작성한다고 가정해보자.

만일 transmissionType이 enum 형의 변수라면 위의 구문으로 충분할 수도 있다. (IDE에 따라 모든 경우가 없다고 자동 경고해주기도 함)

그런데 만일 int형이라면, 혹은 다른 machine에서 전송받은 값을 처리해서 enum으로 바꾸는 구문이라면 위의 경우는 문제가 될 수 있다.

위의 transmissionType의 변수에 정확히 저 3가지 값만 들어오라고 강제할 수 없는 경우는, unknown을 차라리 else나 default같은 fallback 처리를 넣어주는 게 좋다. (비정제 data 분류할 때도 자주 이렇게 한다)

 

물론, 지정한 3가지 값 이외의 숫자가 들어오는 것 자체가 문제다. (그래서 추가로 예상치 못한 값이 들어왔을 때의 처리를 해야 함)

상황에 따라 다르겠지만, 모르는 값이 들어와서 아예 처리를 하지 않는 경우와, 기타의 경우로 대치하여 처리를 계속 하는 경우가 각각 필요한 경우가 있다.

적어도, 아예 비정상 종료를 하거나, 원치 않는 동작을 하는 것은 미리 막아두는 게 제일 낫다.



Trackbacks  | Comments 

Java에는 Serializable이 있어서, 다른 thread나 다른 processor, 혹은 다른 machine에 object instance의 상태를 그대로 복사할 수 있다.

물론 다 되는 건 아니고, 당연하지만 다른 object reference를 옮길 수는 없고, 주로 data class의 내용을 쉽게 옮기는 데 사용한다.

 

그래서 Serializable을 쓴 code에 이걸 왜 썼냐고 물어봤더니, 대답은 "쓰기 쉽고 간편해서"라고 한다.

하지만 Serializable은 reflection을 사용해서 전송한다.

 

reflection을 사용하게 되면 보통 나오는 문제는 당연히 속도 저하.

뭐 이거야 짧게 쓰면 큰 문제가 안되는 경우도 있으니..

 

그리고 두 번째는 난독화에 걸리면, 각 field 이름이 제멋대로 변한다.

이 경우는 같은 binary를 사용한다는 전제하에, 바뀐 이름을 공유하게 될테니 넘어갈 수도 있고..

 

Android의 경우는 Parcelable이 있어서, Serializable과 거의 같은 용도로 사용할 수 있다.

대신, Parcelable의 구현에는 reflection을 사용하지 않는다.

 

Serializable의 경우도, writeObject(), readObject()를 직접 구현하면 적은 overhead로 동일한 동작을 할 수 있고, 별도의 추가 처리도 직접 넣을 수 있다.

대신 약간의 bytecode가 더 늘어나지만, 가능하면 Serializable을 사용할 때는 writeObject(), readObject()등을 함께 구현하는 습관을 들이는 걸 추천.

 



Trackbacks  | Comments 

iOS의 UIColor의 생성자 중, RGB값을 사용하는 생성자는 다음과 같다.

init(red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) // 참고 : https://developer.apple.com/documentation/uikit/uicolor/1621925-init

각 색상값들의 변수형이 CGFloat인 이유는, 0 ~ 1.0 사이의 값으로 지정하게 되어 있기 때문이다.
하지만 개발업무에서 대부분의 색상값 전달은 hex 혹은 int를 사용하다보니, 애초에 iOS 용도로 나온 안내가 아니라면 불편한 경우가 많다. 사실, 따져보면 iOS도 최소한 driver 수준에서는 정수를 사용하고 있을텐데.
어쨌든, 이번에도 designer들에게 전달받은 색상값이 전부 정수로 되어있다보니, 이걸 공통함수화 하겠다는 commit이 올라왔다.

func UICOLOR_RGB(_ r: CGFloat, _ g: CGFloat, _ b: CGFloat) -> UIColor {
	return UIColor.init(red: r/255, green: g/255, blue: b/255, alpha: 1)
}

 

함수명을 대문자로 한 이유는 “UICOLOR 값 세팅이기 때문에 함수명을 대문자로 작성” 이라고 한다.
이건 기존에 명명규칙 등을 정의한 개발안내서를 참고하라는 이야기와 함께 기각.

Extension을 쓰는 게 낫지 않겠느냐는 이야기엔...”그게 더 번거로울 거 같아서”라고.
이건 가능하면 extension으로 생성자 추가하는 게 나을거라는 이야기는 했지만, 선택은 본인 몫이니 알아서 하라고 했다.

매개변수를 실수가 아니라 int처럼 정수 계열로 하는 게 낫겠다는 건...납득은 못한 분위기다.
그래서 이건 꼭 바꿔야 한다고 기각했고, 곧 “float가 아니라 int면 나누기가 안됩니다. 제가 뭘 못하는건지 계산 값이 제대로 이루어지지않아 검정색으로 나옵니다(0으로 처리) 그래서 CGFloat으로 두었습니다. 해결방법있으면 알려주세요~” 라고 한다.
.......순간 암담했지만, 말로 쓰기보단 type casting 관련 link를 보내줬다.

해당 PR에는 아래와 같은 예제 구문을 남겨두었다.

extension UIColor {
	convenience init(_ r: UInt8, _ g: UInt8, _ b: Uint8, _ a: UInt8=255) {
		self.init(red:CGFloat(r) / 255.0, green:CGFloat(g) / 255.0, blue:CGFloat(b) / 255.0, alpha: CGFloat(a) / 255.0)
	}
}



일단 변수형을 UInt8로 제한했다. 아래 생성자 구문에서 볼 수 있다시피, 각 값은 0 ~ 255의 범위를 갖는 RGB888 체계를 사용한다고 가정했기 때문이다. 표현값의 범위가 UInt8과 정확히 일치하기 때문에, 변수형을 제한시켜두면 compile time에서 잘못된 값을 넣는 걸 방지할 수 있다.
마지막 alpha에는 기본값을 255로 넣어두어, 필요한 경우 생략해서 사용할 수 있게 했다.
그리고 extension으로 생성자를 추가했기 때문에, 보다 더 직관적으로 사용할 수 있지 않을까?

 

하지만 이 사진구문이 사용되는 일은 없었다.
대신 올라온 code가 어떤지, 그냥 기록 삼아 남겨두자......

func UIColor_RGB(_ r: Int, _ g: Int, _ b: Int) -> UIColor {
	let red: CGFloat(r)
	let green: CGFloat(g)
	let blue: CGFloat(b)
	return UIColor.init(red: red/255, green: green/255, blue: blue/255, alpha: 1)
}


Trackbacks  | Comments