자연의 코드
지난주 하루는 퇴근하고 넥스트 스승님들이 계신 코드스쿼드에 방문해서 프로그래밍을 배우고 있는 분들과 공부법, 취업, 면접 등등 나의 경험을 공유하고 잡담도 하고 질문도 받는 시간을 가졌다. 질의응답을 하던 와중에 내가 ‘자연의 코드’라는 단어를 사용했다. 네이버 핵데이에 멘토로 여러 번 참여하면서 멘티 분들의 코드를 봤을때 그렇게 느꼈다는 말이었다. (핵데이 멘티들 중에 인턴 합격한 사람도 있고 못한 사람도 있지만 결국 대부분 취업도 잘했고, 비록 아깝게 면접에 떨어진 분들도 결코 실력이 떨어지지 않는다. 그들이 개발을 못한다는 말이 아님). 그랬는데 어떤 분이 ‘자연의 코드’가 어떤 의미인지 더 자세히 말해달라고 했고, 즉흥적으로 내뱉은 단어인 탓에 설명이 좀 부족했던 것 같다. 그래서 이 글을 통해 다시 정리해봤다.
코드 생김새
멘토님의 표현을 빌리자면, “코드를 읽지 않고 생긴 윤곽만 봐도 기본이 안된 코드는 걸러낼 수 있다”. 소스코드를 딱 열어보면 정리가 필요해보인다는 느낌이 드는 경우가 있다. 그 이유는 거창한게 아니라 줄바꿈, 띄어쓰기, 메서드나 프로퍼티의 순서 등이 일관성이 없기 때문이다. 코드도 글의 일종이고 결국 사람이 그걸 읽는다. 글이 적절한 크기의 문단으로 나뉘어져 있고 논리적인 흐름이 일관되면 읽기 수월한 것처럼 코드도 시각적으로 보이는 생김새/윤곽이 가독성에 영향을 준다. 예를 들어,
👇 삐쭉삐쭉한 코드 예시
func doSomething() {
let blah = ...
for item in items {
print(item.name)
}
}
var myData: [String: Data]
private func somethingElse() -> String {
return "Hello World"
}
func justDoIt() {
// whatever..
}
👇 그나마 정돈된 코드 예시
var myData: [String: Data]
func doSomething() {
let blah = ...
for item in items {
print(item.name)
}
}
func justDoIt() {
// whatever..
}
private func somethingElse() -> String {
return "Hello World"
}
개발자마다 선호하는 줄바꿈 스타일이 다르긴 하지만, 최소한 하나의 파일 내에서는 일관성을 지키는게 좋다. 코드의 생김새가 들쭉날쭉하면 코드를 해석할 때 들어가는 시각적인 부하가 커져서 눈도 아프다. 물론 핵데이니까 시간이 부족해서 평소보다 정리를 더 못한 것일 수도 있다. 나도 멘토님에게 처절한 코드 리뷰를 받았었다. 엔터 하나도 목적없이 허투루 치지 말라고.
또한 메서드나 프로퍼티 선언 순서도 정리의 대상이 될 수 있다. 가장 흔한 정렬 방법은 IBOutlet과 프로퍼티를 먼저 선언하고 메서드를 그 뒤에 이어서 선언하는 방식일 것이다(프로퍼티 - 메서드 순
). 하지만 멘토님께서 클래스 내부 코드의 순서를 통해서도 개발자의 의도를 담을 수 있다고 알려주셨다. 개발자가 소스파일을 열어보는 이유는 그 클래스가 무슨 일을 하고 이걸 어떻게 사용하는지 알기 위해서일 것이다. 그래서 클래스에 대해 알고 싶은 사람이 가장 먼저 알아야 할 public 프로퍼티나 메서드가 맨 위에 있으면 파일을 열자마자 바로 보이기 때문에 쉽고 빠르게 정체를 파악할 수 있다. 만약 내부 동작까지 궁금하다면 점점 스크롤을 내려가면서 private 프로퍼티나 메서드를 참고하면 된다(public - private 순
). 멘토님에게서 이런 리뷰를 받고 나서 고민해보고 오랫동안 이렇게 저렇게 시도해보면서 나름의 규칙을 정립하게 되었다. 그래서 나는 public 프로퍼티 - public 메서드 - private 메서드 - private 프로퍼티/IBOutlet 순
으로 코드를 정리한다(샘플 앱 참고). IBOutlet이나 프로퍼티가 맨 밑에 있는 것이 처음에는 어색하기도 했지만, public 프로퍼티/메서드를 맨 위에 두는 장점을 살리면서 시각적으로도 정돈된 윤곽을 유지할 수 있다. 동료들도 딱히 거부감을 표현하지 않아서 고수하고 있다. 새로 가게 될 팀에서는 어떨런지. 이것도 정답은 없다. 자신의 기준을 세우고, 일관성 있게 적용하면 될 것 같다.
잘못된/생소한 구조
핵데이 멘토링하면서 상속을 잘못 쓴 코드를 많이 봤다. 객체 지향 프로그래밍을 처음 배울때 코드 재사용을 위해 상속을 사용한다고 배우기도 했고, 예제를 여러 개 보다보면 상속이 객체 지향 프로그래밍의 정점인 것처럼 느껴지기도 한다. 하지만 알고 보니 객체 지향 프로그래밍에서 가장 어려운 것이 상속이었다. 오죽하면 ‘상속을 잘 쓰는 방법은 상속을 쓰지 않는 것이다’라는 우스갯소리도 있고 상속보다는 구성(Composition over inheritance)이라는 OOP 원칙도 존재한다.
다음으로 자주 보는 것은 생소한 객체 간 정보 전달 방식(inter-object communication)이다. iOS 에서는 객체 간 정보를 전달할 때 쓰는 몇 가지 패턴이 있다. Delegate 패턴, NotificationCenter, Key-Value Observing, Closure를 전달하는 방법 등 네 가지 정도 사용할 수 있다. 정확한 코드는 기억이 안나지만 특이한 방법으로 데이터를 주고 받는 코드를 본 적이 있다. 당장에 원하는대로 작동은 할 수 있지만 iOS 컨포넌트들과 어울리지 않으면 나중에 코드 수정이 필요할 때 문제가 생길 수 있고 무엇보다 다른 iOS 개발자가 봤을 때 불필요하게 해석이 오래 걸리게 된다.
Human Interface Guidelines 위배
앞에 두 개에 비해서는 비중이 낮지만 iOS 개발자라면 HIG를 잘 알아야 한다고 생각한다. 핵데이 특성상 디자이너나 기획자 없이 개발자 본인이 앱의 UI/UX를 신경써야 하는데, 가끔씩 기묘한 플로우를 맞닥뜨릴 때가 있었다. 예를 들어 뷰컨트롤러 간의 이동은 크게 모달과 푸시 두 가지가 있는데 상황에 맞게 적절히 써줘야한다. 만약에 뷰컨트롤러를 모달로 띄웠다면 모달 작업이 끝난 후에는 해당 뷰컨트롤러를 dismiss 해줘서 원래 있던 뷰컨트롤러로 돌아가야한다. 그런데 모달 작업이 끝난 후 dismiss를 안하고 아래 깔려 있는 뷰컨의 새로운 인스턴스를 만들어서 또다시 모달로 띄우는 플로우를 본 적이 있다.
마무리
거창한 개발 지식도 아니고 엄청난 내공이 담긴 내용도 아니고, 단지 신입 개발자보다 고작 한 단계 앞에 있는 개발자가 올챙이적 시절을 돌이켜보며 쓴 글이다.
Tags: ios, swift, readable code