Object-oriented Programming Seen Through Swift: Coding Habits to Avoid
Background
Someone who majored in computer and painting and was quite successful said that programmers have more in common with painters than with scientists. Indeed, as I try to do better in programming, it feels more like a sport or art that requires skills built through experience rather than science.
Just as the ability to recognize good paintings and painting well are different, the ability to judge whether a code is good or bad by looking at it and the skills to build good codes, unfortunately, don’t come together. As I see, build, and maintain a lot of codes built by others, it seems like I’ve been equipped with a sense that can judge codes a little through experience. But as I start typing on the keyboard, I face frustration. I get a strong feeling that I shouldn’t build it this way from instinct. Experience? But I don’t have a clear answer to how to build it in a different, better way. When you take a test as a student, there are times when you choose your answer with confidence, but also times when you eliminate the ones you know are not correct and choose your answer from the remaining options. If you exclude the ones that are clearly not the answer, you can spend more time thinking about the remaining options, and there’s a higher probability that you’ll choose the right answer. Coming back to programming, I thought if I knew what a code with a wrong pattern or that is not good for maintenance looks like and avoid it, won’t I be able to use better codes?
Last year, I joined a study group for functional Swift programming in the company. On the first day, we invited a mentor to teach a class in the company about functional Swift programming to get advice for the direction of our study. There, the mentor said something that became the direction of my programming study for the next whole year.
To see how well functional programming is, you need to compare a well-written functional code with a well-written object-oriented code, but since I can’t say I write good object-oriented code it’s difficult to compare them. “Oh.. my unconscious thoughts that thought I was doing OOP because that was the only thing I knew how to do and I code every day in the ‘OOP style’ started to shake from the roots. So I thought I should do well from the basics again, and this is a summary of knowledge I have acquired over the past few months.
Swift and SOLID
SOLID is the 5 fundamental principles of object-oriented programs and design, named by Robert Martin. Swift is a multi-paradigm language, but Cocoa Touch frameworks such as Foundation, UIKit, etc. are fundamentally based on OOP. Looking at the SOLID principles and iOS in relation to each other, I could understand why the several classes that I’m using were created that way, and how I could apply these principles. In the text, I focused on how to code in order to keep these principles, and what it looks like to violate these rules, along with examples of the iOS framework for each rule it has been created for (or has violated). As I said earlier, the concept is to at least know what a wrong code looks like, and avoid it, because you can’t write good code from the beginning. Then I believe you can somehow move toward that direction even if you didn’t understand all the difficult things.
However, this text only deals with SOL, excluding the last two principles. There are several reasons for this. Because I did not understand the latter part enough to explain them, and also because it is much more difficult to judge the situations where these principles are violated for the ID part according to the concept of this text. However, it was enough with only SOL to find countless improvements for my codes and I still can’t see the end!
Single Responsibility Principle
The Single Responsibility Principle is a principle that all classes should have only one responsibility. Robert Martin defined it as a reason to change responsibility. The most significant violation of SRP is the Massive View Controller phenomenon. Many different patterns are being created and tried in order to solve the MVC problem, but what they have in common is that they try to split up the view controller, which has too many roles, and creates multiple classes that have only a single responsibility.
What we can try right now to keep the SRP is to create small classes. Small classes are not sufficient conditions of the SRP but they are essential conditions. To achieve this goal, I’m coding based on the 10-200 rule as suggested by the mentor. The 10-200 rule is to write functions within 10 lines, and classes within 200 lines. But it’s difficult to strictly go by this, and this is still considered an ideal. In reality, if it goes over 200 lines, you alert yourself inside your head and try not to make it longer or try refactoring. If you actually try the 10-200 rule, it makes you think a lot and makes you uncomfortable. However, it seems that learning comes from overcoming these inconveniences.
First, to keep the ‘10 lines for functions’ rule, you’d have to abide by the principle that a function needs to have only one task, and then you start to think about the level of abstraction. The level of abstraction means how much information are you willing to expose in that function. Let’s look at the cooking directions on the back of the Shin Ramyun bag. Boil 550ml of water, and add noodles, soup base, and vegetable mix. It says to boil for another 4 minutes and 30 seconds. The 3 steps of the abstraction level are similar. If you break down the water boiling step, you can say take a pot, turn on the water purifier, get water, put the pot on the stove, turn on the fire, etc. But these are not included in the cooking directions. This is because they are expressed in one step(function) where 500ml of water is boiled. This way, it makes me think about how far I should divide the task(function) and explain(abstract) it.
To keep the ‘200 lines for the class rule, you can’t just create and use properties, functions, and methods anywhere you want. You need to find a reason why they should be there. If you can’t find a reason for them to be in that class, it’s a violation of the SRP. The easiest way to judge is to see how many of the properties or methods of self are being used inside the function or the method. If there aren’t any being used, there is no reason for them to be in that class. Or, the less frequent the calls are made for self, it means it’s less related to the class, so if you have to refactor or take the class through a diet, this will be the first candidate to be kicked out.
SRP is called the beginning of all patterns. If you code without properly complying with the SRP, it’s highly probable that it will not work with any patterns. Classes need to have only one role, it’s the very first principle learned in object-oriented programming, but so hard to comply with.
Open-Closed Principle
The Open-Closed Principle is a principle that should be open to scalability but closed to modifications. Being open means that you can add features or make modifications, and being closed means that you must not modify the codes using the module line per line when adding a feature. A typical example of a violation of OCP is repetitive branching statements for a certain type. That means you need to think about it if you’re repeatedly using if/switch statements in several places for one enum. Because in cases like this, adding a feature is as easy as adding one line of case, but you need to modify every single code that is switching the enum once you add the feature. If it was planned in an exhaustive manner, you can get help from the compiler, but if you added a default statement, this won’t be possible.
OCP can be practiced by not using if/switch as much as possible. Not eliminating all branching statements(impossible) but for points like an enum that branch a type. When I first heard about this, I thought it was ridiculous. Isn’t the if statement the most basic of programming.? How can you branch without an if/switch? It was so awkward and frustrating at first, but as I tried it I’m starting to feel how it is helpful in my codes little by little. It is also complementary to the aforementioned 10-200 rule. Eliminating branching statements alone can greatly reduce the length of functions and classes.
Let me introduce to you two ways to replace if/switch statements. First is creating a protocol (or a class), inheriting and using them. This is a method that directly abides by the OCP. Refer to the contents of this blog for further details. Another simple method is using the dictionary.
But this method does not directly abide by the OCP. Since this has the same weakness as the switch statement which has a default clause, avoid it when it’s likely to have frequent changes, and use it on a limited basis when you want to eliminate branching statements.
Original switch statement code
switch reason {
case .initializing:
self.instructionMessage = "Move your phone".localized
case .excessiveMotion:
self.instructionMessage = "Slow down".localized
case .insufficientFeatures:
self.instructionMessage = "Keep moving your phone".localized
case .relocalizing:
self.instructionMessage = "Move your phone".localized
}
Code that deleted branching statements using the dictionary
//dictionary generated in appropriate spots
let trackingStateMessages: [TrackingState.Reason : String]
= [.initializing. : "Move your phone".localized,
.excessiveMotion : "Slow down".localized,
.insufficientFeatures : "Keep moving your phone".localized,
.relocalizing : "Move your phone".localized]
//switch statement replaced대체
self.instructionMessage = trackingStateMessages[reason]
Liskov Subtitution Principle
Lastly, the Liskov Substitution Principle is a principle where changing the upper type (superclass) to a lower type (subclass) instance should not harm the program’s action. The definition is difficult but the way to follow it is surprisingly simple. The child should not restrict the action of its parents. A typical violation would be a square class created by inheriting a rectangle. Since a square has the same width and height, it can only create the desired action only by limiting the action of the rectangle parent class which can freely change its width and height. This is a violation of the LSP. To abide by the LSP, the rectangle can inherit a square, or make the width and height as a let. (If the action of changing the value is removed, there is no action to limit)
An LSP violation case can be also found within the iOS framework.
var label = UILabel(frame: .zero)
var button = UIButton(frame: .zero)
var segmentedControl = UISegmentedControl(frame: .zero)
var textField = UITextField(frame: .zero)
var slider = UISlider(frame: .zero)
var switchButton = UISwitch(frame: .zero)
var activityIndicator = UIActivityIndicatorView(frame: .zero)
var progressView = UIProgressView(frame: .zero)
var stepper = UIStepper(frame: .zero)
var imageView = UIImageView(frame: .zero)
let views: [UIView] = [...] //Save the above views in UIView array
views.forEach { $0.frame.size.height = 30 } //Change the height for the views to 30
let height = views.reduce(0) { $0 + $1.frame.height }
print(height) //The result?
Since the height of the 10 UIView subclasses has been changed to 30 you may expect it to become 300 but the actual result is 272. This is because the intrinsicSize of some views cannot be changed. These views (UIProgressView, UISwitch, etc) that limit the action(changing the height) of UIViews(parent type) are LSP violations.
As you’re in iOS development, you may have seen the code below and have created it as well. If a function that overrides and degenerates the parent’s function is created, it’s a violation of the LSP.
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
It’s difficult to program without ever violating the LSP. But if you violate the LSP in too many places, there will be a problem. Code can’t be based on the upper class, and if this happens, the inheritance will be meaningless and the OCP won’t be followed as well. For example, UITableView is created based on the UITableViewCell, so if the custom cell isn’t following the LSP, the table view won’t work properly as well. Also, the reason for creating a protocol is to write codes based on an abstract interface, but if the inherited type degenerates the protocol’s method, the codes written based on the protocol will inevitably break one after another.
Inheritance is an important part of object-oriented programming and is useful if applied well, but could cause problems if wrong inheritances are created. On the other hand, convenience and simplicity may be obtained by violating the LSP. In conclusion, it is inefficient to follow the LSP everywhere, but it harms the stability if the LSP is violated too often because of unexpected consequences. Therefore, know whether it is violating the LSP or not when creating an inheritance.
Conclusion
The purpose of this text is not to perfectly understand the SOLID principles. In addition, trying to write codes that adhere to all principles can be seen as inefficient. For example, it’s very inconvenient if you try to create everything according to the OCP.
If you need to create protocols and inherit every time to avoid using branching statements, it only increases unnecessary complexity and increases development time. Therefore, a proper trade-off is needed. Among the five principles, the SRP and the OCP is understandable, but the hardest to adhere to and need constant hard work.
Just like eliminating the wrong answer when you’re not sure of the correct one, figure out the habit which violates the above principles, and eliminate it to improve.
Additional references (coding example): OOD Principles in Swift, Design Patterns in Swift
이 글의 초안을 읽어준 김형중, 김찬희, 김창기님에게 고마움을 전합니다.
Tags: Objected Oriented Programming, SOLID, Swift, iOS
스위프트 API 디자인 가이드라인 초초초-요약본
https://swift.org 에서 스위프트 API 디자인 가이드라인의 존재 이유를 다음과 같이 설명하고 있다.
스위프트 3.0의 출시 목표는 프로그래머에게 일관된 사용자 경험을 제공하기 위한 표준을 세우는 것이고, API에 등장하는 이름과 표현법을 통해 이를 달성하고 있다. 이 가이드라인은 여러분의 코드가 더 큰 스위프트 생태계의 일부인 것처럼 느껴지게 하는 방법을 설명하고 있다.
‘프로그래머의 사용자 경험’이라는 단어를 썼다는 것이 어색하기도 하고 놀랍기도 하고 한편으론 정말 애플스럽다. 그런데 왠지 익숙한 표현법 아닌가?
애플 플랫폼과 매끄럽게 어우러지는 훌륭한 앱을 디자인 하기 위한 … (후략)
잘 알려진 아이콘, 텍스트 스타일, 통일성 있는 용어, 시스템이 제공하는 인터페이스 요소들을 활용하여 유저에게 일관된 경험을 제공 … (후략)
Human Interface Guidelines에 등장하는 문장들이다. 최종 사용자들에게 좋은 사용자경험을 주는 애플 플랫폼 앱을 만들기 위해 따라야하는 것이 ‘휴먼 인터페이스 가이드라인’이라면, 스위프트 개발자에게 좋은 사용자경험을 주는 코드를 만들기 위해 따라야하는 것은 ‘스위프트 API 디자인 가이드라인’인 셈이다.
가이드라인에는 꽤 많은 내용이 담겨 있다. 그렇다고 문서가 눈에 쏙쏙 들어오는 편도 아니고 누구나 들었을때 ‘음 그렇지’라고 고개가 끄덕여지는 그런 내용도 아니다. 이런 이유로 가이드라인에 무슨 내용이 담겨 있는지, 어디서부터 시작해야될지 잘 모르겠는 분들이 많은 것 같다. 약 일 년 정도 API 디자인 가이드라인과 애플 개발자 문서를 옆구리에 끼고, 아니 모니터에 띄워놓고 개발했던 경험을 토대로 내 마음속 중요도 순으로 추리고 요약해봤다. 그룹핑도 적용 상황을 기준으로 내 마음대로 재구성했다.
⚠️ 가이드라인의 모든 내용을 담고 있지 않다. 한 20% 정도? 하지만 적용되는 빈도로 따지면 아래 내용들을 합치면 전체 케이스의 50%는 넘는 것 같다.
함수 및 프로퍼티 이름
-
메서드나 함수는 사이드 이펙트의 유무에 따라 이름 짓는다.
- 사이드 이펙트 없는 함수는
명사구
로 읽혀야한다.
⛔️ 잘못된 예
x.getDistance(to: y) //"y까지의 거리를 가져와라"
✅ 올바른 예
x.distance(to: y) //"y까지의 거리"
사이드 이펙트 없이 값을 리턴하는 메서드(일명 “getter”)에 get을 붙이는 것이 대표적인 가이드라인 위배이다. 코코아터치 프레임워크에서
get
,fetch
,request
로 시작하는 메서드는 전부 completion handler를 받는 비동기 작업 뿐이다.참고 : Swift 개발자처럼 변수 이름 짓기
- 사이드 이펙트가 있는 함수는
명령형 동사구
로 읽혀야한다. (명령형이라는 것은 동사원형을 쓴다는 것이다.)
print(x) x.sort() x.append(y)
위 함수들은 모두 함수 스코프 밖에까지 영향을 미친다(사이드 이펙트). print는 콘솔에 찍히고 sort와 append는 x의 값을 바꿔버린다.
- 사이드 이펙트 없는 함수는
-
mutating과 nonmutating 메서드 쌍은 일관성있게 이름 짓는다. 보통 mutating 함수는 명령형 동사로 쓰고 nonmutating은 “ed”나 “ing”를 뒤에 붙여서 사용한다.
Mutating | Nonmutating |
---|---|
x.sort() | z = x.sorted() |
x.append(y) | z = x.appending(y) |
-
Bool 메서드나 프로퍼티 이름은 인스턴스에 대한 평서문처럼 읽혀야한다.
e.g.
x.isEmpty
,line1.intersects(line2)
-
대소문자 규칙을 따른다.
- 타입이나 프로토콜은 UpperCamelCase를 따르고 그 외에는 lowerCamelCase를 따른다.
- 대문자 약어는 낙타표기법에 따라 전체 대문자 혹은 소문자로 통일한다.
var utf8Bytes: [UTF8.CodeUnit] var isRepresentableAsASCII = true var userSMTPServer: SecureSMTPServer
우리가 많이 쓰는 대문자 약어 중에 URL이 이 조건에 해당될 것이다. 규칙을 따르려면 아래처럼 사용해야 한다.
let urlString = "https://soojin.ro" let blogURL = URL(string: urlString)
파라미터명(Parameter Names)
파라미터 명명 규칙은 좀 더 세부적으로 생성자
일 때와 메서드
일 때로 나뉜다.
생성자
-
생성자의 첫번째 파라미터명은 타입 이름과 구(phrase)를 이뤄서는 안된다. 다시 말해 생성자의 파라미터명에는 아래처럼 전치사나 접속사 등을 써서 문장처럼 이어지게 만들지 말라는 말이다.
⛔️ 잘못된 예
let foreground = Color(havingRGBValuesRed: 32, green: 64, andBlue: 128) let newPart = factory.makeWidget(havingGearCount: 42, andSpindleCount: 14) let ref = Link(to: destination)
대신 아래처럼 having, and, to, with 등의 단어들을 제거한다.
✅ 올바른 예
let foreground = Color(red: 32, green: 64, blue: 128) let newPart = factory.makeWidget(gears: 42, spindles: 14) let ref = Link(target: destination)
-
무손실 타입 변환(value preserving type conversion)을 하는 생성자는 첫번째 파라미터명을 생략한다. 추가적으로, 손실이 일어나는 타입 변환이라면 어떤 손실이 일어나는지 명시해주는 것을 추천한다.
✅ 올바른 예
Int64(someUInt32) String(someNumber) String(someNumber, radix: 16) UInt32(truncating: someUInt64) //64비트 -> 32비트로 손실 발생 명시
즉, 무손실 타입 변환이 일어나는 생성자가 아니라면 첫번째 파라미터에는 이름을 꼭 부여한다.
메서드 및 함수
-
첫번째 파라미터가 전치사(to, in, at, from, with 등등)구의 일부라면 파라미터명을 부여한다.
✅ 올바른 예
x.removeBoxes(havingLength: 12) employees.remove(at: x)
-
그렇지 않고 첫번째 파라미터가 자연스럽게 이어지면 생략한다.
✅ 올바른 예
x.addSubview(y) allViews.remove(cancelButton) x.insert(y, at: z) x.append(y)
-
그 외 모든 경우에는 파라미터 이름을 부여한다.
끝.
Tags: Swift API Design Guidelines, abriged
대학생이라면 네이버 캠퍼스 핵데이에 참가해야하는 이유 + 지원 꿀팁
네이버 캠퍼스 핵데이란?
네이버 캠퍼스 핵데이(이하 핵데이)는 연 2회, 1박 2일로 진행되는 해커톤 행사이다. 다만 일반적으로 우리가 해커톤이라고 부르는 것과는 다르다. 일반적인 해커톤은 하나의 테마가 주어지고 그 테마에 맞는 소프트웨어를 개발, 마지막 날에 시상식으로 마무리한다. 반면 핵데이는 시상을 위해 경쟁하는 대회가 아니다. 참여 멘티와 멘토가 서로서로 배워가는 시간이다. 멘티는 네이버 현업 개발자에게 멘토링을 받고 멘토도 업무와 연관있거나 평소에 관심 있던 주제를 발제해 함께 도전하고 배우기 위해 참가한다.
핵데이에서는 현업 네이버 개발자 한 명과 대학생 멘티 2~3 명이 한 팀이 된다. 멘티는 수 십 개의 주제 중에 참여하고 싶은 주제를 3순위까지 선택해서 지원한다. 멘토는 미션을 가장 잘 수행할 것 같은 지원자를 선발해서 자신의 팀을 꾸린다. 합격 발표가 나면 멘티는 깃헙 리포와 메신저 방에 초대를 받게 된다. 그로부터 핵데이까지 2주 정도의 기간이 있는데 이 기간에 온라인으로 멘토링을 하기도 한다. 핵데이는 시작부터 끝까지 팀이 중심이 되며 매우 자율성이 높다. 그래서 멘토와 멘티의 팀워크가 중요하다. 적극적으로 임할수록 더 많은 것을 배워갈 수 있다.
커넥트원
핵데이는 물 좋고 공기 좋은 춘천 네이버 커넥트원에서 1박 2일 동안 진행된다. 오후 12시 ~ 1시쯤 그린팩토리에 모여서 오리엔테이션을 하고 버스에 탄다. 약 한 시간 반을 달려 도착하면 짧은 오리엔테이션을 마친 후 자리 세팅을 하고 본격적으로 해커톤이 시작된다. 무제한 제공되는 스낵과 커피와 음료수를 먹으면서, 다같이 개발 방향을 논의하기도 하고 막히는 부분을 서로서로 도우면서 문제를 해결한다. 깃헙에 코드를 푸시하면 멘토가 보고 리뷰를 해주기도 한다. 시작할 때만 해도 ‘이걸 어떻게 개발하지’ 라고 생각했던 문제들이 조금씩 풀린다. 자매품으론 ‘아니 벌써 10시야?’ 라는 말도 있다.
핵데이 참가자들은 평가 받기 위한 결과물을 만들지 않고 스스로 배우기 위해 개발을 한다. 그래서 이튿날 시상식이나 발표회 같은 건 없다. 대신 팀 별로 모여서 자유롭게 회고를 한다. 참가자 사이에 경쟁도 없다. 멘티들은 협업하는 관계이다. 협업해야 모두가 각자 더 많은걸 배워갈 수 있다.
핵데이에서 얻어 가는 것
이 행사의 VIP는 대학생 개발자다. 핵데이를 준비하시는 분들은 참가 멘티들이 더 배우고 성장할 수 있게 세심한 부분까지 신경쓰신다. 행사가 거듭될수록 멘티들의 만족도도 높아지고 있다고 한다. 나는 여태까지 멘티로 한 번, 멘토로 두 번 참가했다. 왜냐면 멘토로서 얻어 갈 수 있는 것도 정말 많다. 하물며 해커톤 + 멘토링 + 인턴십 3종 세트를 얻어가는 멘티들에게는 이 행사가 풀 패키지나 다름 없다.
좀 더 자세히 적어본다면,
-
기술적 성장: 앞서 언급했듯이 캠퍼스 핵데이는 상을 받는 대회가 아니다. ‘어렵지만 해결할 수 있는’ 난이도의 문제와 직접 부딪히면서 나의 실력을 한 단계 업그레이드할 수 있는 자리이다. 뿐만 아니라 개발자 커리어나 평소 궁금했던 것들을 멘토에게 물어보고 조언을 얻을 수도 있다. 기술적 성장에는 코딩 실력 뿐 아니라 커뮤니케이션, 협업, 코드 리뷰, 토론, 문제 해결 능력이 포함된다. 핵데이에서는 이 모든걸 경험해볼 수 있다. 학교와 회사의 딱 중간이라는 느낌이 든다. 그래서 중요한 것이 자기 주도성이다.
-
인턴십:
채용형 인턴십
,체험형 인턴십
두 가지가 있다. 주제별로 인턴십 종류가 정해져 있어서 지원할 때 유의하여 지원하면 된다. 4학년이채용형 인턴십
에 합격하면 인턴십이 끝난 후 최종 면접을 통해 채용으로 이어진다. 1 ~ 3학년은 채용 전환 면접이 없는체험형 인턴십
을 하게 된다. -
네트워크: 관심사가 비슷한 친구들을 만날 좋은 기회다. 보통은 주제가
iOS
,안드로이드
,웹프론트엔드
,백엔드
,머신러닝/딥러닝
등으로 분류되기 때문에 나와 같은 걸 개발하는 또래 친구들을 만나 새로운 자극을 받을 수 있다. 그 뿐 아니라 인턴까지 하게 되면 네이버 팀분들과 계속 알고 지낼 수 있다. 나도 체험형 동계 인턴십을 했었는데 인턴이 끝난 뒤에도 팀 회식에 불러주시고, 여름 방학이 다가올 즈음에 방학 때 할 일 없으면 인턴 또 하러 오라고 제안해주시는 등 좋은 기회를 많이 얻을 수 있었다.
지원서 작성 팁
멘토로 참여하면서 100여 명의 지원자를 검토해보고 느낀 점을 토대로 지원서 작성 팁을 몇 개 정리해봤다.
지원서는 네이버 공식 사이트에서 작성하고 제출한다. 핵데이에 등록된 주제를 볼 수 있는 깃헙 리포가 공유되는데 거기서 흥미를 끄는 주제를 고른다. 그리고 관심 있는 주제를 선호순으로 작성한다. 단순히 선택하고 끝이 아니라, 내가 왜 이 프로젝트를 잘 할 수 있는지 설명을 해야한다. 프로젝트마다 ‘요구사항’이 있는데 내가 이걸 잘 해낼 수 있다는걸 설득한다는 마음으로 써본다. 비슷한 주제의 프로젝트를 해본 경험이 있다면 꼭 밝힌다. 없더라도 왜 관심을 가지게 됐는지, 관심을 갖고 나서 어떤 활동을 했는지 등을 쓴다. 핵데이 지원 전에는 잘 몰랐더라도 주제를 보고나서 관심이 생겼다면 공고가 뜬 시점부터 지원 마감까지 몇 주의 기간이 있으니 이 때를 활용해서 튜토리얼을 찾아서 해보거나 프로토타입을 직접 만들어 보고나서 지원서를 작성하는 것도 좋은 방법이다.
주제별 멘토가 직접 멘티를 선발하기 때문에 통일된 기준은 없다. 어떤 멘토님은 깃헙 활동과 커밋 메시지를 보고 뽑았다고 한 경우도 있고, 해커톤 수상 경력을 좋게 보고 뽑았다고 하는 분들도 있다. 내가 멘토링하는 주제는 iOS
개발이다. 그래서 지원자들은 보통 본인이 개발한 앱을 적는다. 여태 지원자 중에 iOS 앱을 아예 개발해보지 않은 사람은 없었던 것 같다. 대부분의 지원자가 앱 개발 경험이 있기 때문에 결과물을 잘 어필하는게 중요한 것 같다. 앱 소개도 여러 방법으로 할 수 있다. 앱스토어에 출시되어 있다면 링크 하나만으로도 충분하고 직접 써볼 수 있기 때문에 제일 좋다. 공개되어 있지 않아도 소개 페이지나 시연 영상이 있다면 충분하다. 만약 깃헙에 프로젝트가 올라가 있다면 README를 활용해서 소개 글을 넣을 수도 있다. README에 스크린샷이나 gif가 있다면 어떤 앱인지 한 눈에 파악할 수 있어서 좋다. (혹시나 빌드가 안되서 앱을 설치해 볼 수 없다면 말짱 도루묵). 그리고 그 경험이 핵데이에서 어떻게 도움이 될지 간략하게 써주면 좋을 것 같다. 만약 여러 명이 같이 개발한 프로젝트라면 본인이 개발한 부분을 명시해주는 것이 좋다.
캠퍼스 핵데이는 채용과 연계된 행사이기도 해서 주제와 딱 맞는 경험이 없더라도 기본 프로그래밍 실력을 갖추고 있고 관심 분야가 팀과 맞는 지원자를 뽑기도 한다. 지원서에 대회 수상 경력, 대외 활동, 관심 분야, 기술 실력을 나타낼 수 있는 프로젝트를 적을수 있는 항목이 있다. 특히 채용을 염두해 둔 4학년을 뽑을 때는 기술 스택이 일치하는지를 보게 된다. 앱 개발과는 달리 백엔드 분야는 언어나 프레임워크가 훨씬 다양하다. 백엔드라 하더라도 노드를 쓰느냐 자바를 쓰느냐 파이썬을 쓰느냐 등등에 따라 많이 달라서 아무래도 팀이 쓰는 언어나 프레임워크를 써본 경험이 있는 후보를 선호할 것 같다.
사실 매번 뽑고 싶은 지원자들이 많아서 합격자를 선발하는 일이 정말 힘들다. 멘티당 최대 세 명까지 선정할 수 있는데 보통 뽑고 싶은 지원자가 9 ~ 10명 정도는 된다 😱. 흥미로운 프로젝트를 해봤거나 iOS 개발 외에도 머신러닝이나, 혹은 아예 개발과 무관한 다양한 분야를 경험해 본 멘티에게서 새로운 걸 배우고 자극 받기도 한다. 그래서 더 많은 대학생들이 캠퍼스 핵데이에 와서 성장하고 좋은 네트워크를 쌓고 무엇보다 새로운 경험을 해갔으면 하는 바람이 있다. 그리고 더 많은 회사들이 이런 핵데이를 열었으면 하는 바람도.
이 글의 초안을 읽어준 조소현에게 고마움을 전합니다.
Tags: NAVER CAMPUS HACKDAY, mentoring, application tips
앱스토어 1위를 하면서 배운 것 (feat. 사이드 프로젝트)
2014년 12월 17일 보안카드 위젯을 출시했다. 며칠 뒤 금융 카테고리 1위, 한 달 뒤엔 유료앱 전체 2위까지 올랐다. 그 후 꾸준한 인기에 힘입어 2015년, 2016년, 2017년 3년 연속 올해를 빛낸 인기 앱에 선정됐고 현재 별점 4.7점(누적 리뷰 1,285개)을 달고 있다.
보안카드 위젯은 모바일 뱅킹을 할 때 필요한 은행별 ‘보안카드’를 아이폰에 암호화해서 저장해두고 알림센터 위젯을 통해 앱 전환 없이 간편하고 빠르게 조회할 수 있는 유틸리티성 금융 앱이다.
(2017년꺼는 스크린샷을 안 찍어놨다..)
수업 과제로 시작했던 사이드 프로젝트지만 많은 사람들이 좋아해준 덕분에 값진 경험을 할 수 있었다. 유저들의 칭찬에 들뜨기도 했고 부정적인 리뷰와 꾸짖는 메일로 인해 상처도 받고 감정이 상할때도 있었다. 하지만 그런 경험들로 인해 다양한 유저들을 이해하고 그들의 입장에서 생각해 볼 수 있었다. 무엇보다 사용자들과 함께 만드는 과정도 매우 즐거웠다.
토스나 카카오뱅크 같은 편리하고 혁신적인 앱들의 등장으로 이제는 내리막길을 가고 있지만 보안카드 위젯을 만들고 운영하면서 배운 것들은 또 새로운 프로젝트를 시작할 때 도움이 될 것 같아서 그 동안의 과정을 회고해보며 정리해봤다.
1. 일단 만든다
보안카드 위젯은 넥스트 재학 시절 iOS 수업의 기말 과제로 제출하기 위해 시작한 프로젝트였고, 엄청 독창적이고 새로운 걸 만들기보다는 내가 스마트폰으로 이미 쓰고 있던 앱 중 하나를 비슷하게 만들어보면서 개발 공부를 할 목적이었다. 다만 과제 제출로 끝내지 않고 사람들이 돈 주고 살 만한 완성도로 만들어서 실제 출시까지 해보자는 목표가 있었다. 그래서 여러 후보 중에 개인적으로 가장 오랫동안 유용하게 쓰고 있던 보안카드 앱을 만들어보기로 결정했다.
괜찮은 아이디어라는 생각이 들고 재밌을 것 같으면 일단 만들어 보자. 만들기 전 주변 사람들의 의견을 들어보는 것도 도움이 될 수는 있다. 하지만 부정적이거나 미적지근한 주위의 말에 너무 휘둘릴 필요 없다고 생각한다. 긍정적인 피드백이 오히려 빨간불일 수도 있다. 듣자마자 사람들이 ‘괜찮은데?’라고 하는 아이디어는 누구나 한번쯤은 떠올렸을만한, 괜찮아 보이는 아이디어에 그칠 가능성이 높다. 실리콘밸리의 스타텁 인큐베이터 Y Combinator에서는 이를 시트콤 아이디어
라고 부른다고 한다. [1] 사업할 것도 아니고, 너무 거창하게 생각할 필요 없이 재밌으면 된 것이다.
2. 기능은 적게 출시는 빠르게
Minimum Viable Product(최소 기능 제품, MVP)를 첫 출시 목표로 삼는다. 처음부터 기능을 많이 넣으려다보면 사이드 프로젝트가 질질 끌리고 결국 끝을 보지 못하고 흐지부지될 수 있다. 일단 가장 중요한 기능 한 두 개만 만들어서 출시를 하고, 나머지 기능은 유저들의 의견을 받아 가면서 차차 추가하면 된다. 같은 일을 하더라도 실사용자들이 직접 요청한 것이라면 더 의욕이 생기고, 본업에 소진한 집중력과 체력도 다시 샘솟는다. 보안카드 위젯을 출시하고 얼마 후 입사했는데 유저들이 지속적으로 기능을 제안해주고 개선 사항을 남겨준 덕분에 퇴근 후에도 지치지 않고 앱을 발전시킬 수 있었다.
출시를 빠르게 하고 유저를 모아보자. 실사용자들의 피드백은 사이드 프로젝트의 가장 큰 원동력이다.
3. 차별화를 한다
보안카드 위젯의 제일 중요한 차별점은 위젯에서 바로 조회 할 수 있게 만든 것이다. 하지만 처음부터 차별점을 염두해두고 시작한 것은 아니었다. 개발을 한창 하던 와중에 당시 릴리즈된 iOS 8의 다양한 기능들을 살펴보다가 알림센터 위젯 기능을 결합해야겠다는 아이디어가 떠올랐다. 이 아이디어가 처음 떠오른 뒤 주위 사람들에게 설명을 해줬을 때 특별히 반응을 보인 사람은 없었다. 대부분 그냥 ‘아 그렇구나’ 정도였다. 하지만 개발을 해서 프로토타입을 보여주고나니 사람들이 ‘아이디어 좋다’ 이런 피드백을 주었다. 말로 듣는 것과 실물 사이의 간격은 꽤 크다는 걸 느꼈다.
🎯 보안카드 위젯의 차별점: Today Extension 기능
깔끔한 디자인과 심플한 UX만으로도 충분한 차별점을 만들 수 있다. 그리고 그런 차별점이 있다면 유저들은 비슷한 앱을 이미 쓰고 있더라도 새로 구매하는 경향이 분명 있는 것 같다. 특히 기존에 쓰던 앱이 오랫동안 업데이트가 되지 않는다면 유저들은 디자인이 좀 더 이쁘고 최신 iOS 기능을 탑재한 앱으로 갈아타기 때문에 기회는 항상 열려 있다.
🎯 앱 아이콘 개선
(좌) 1.0 버전 앱 아이콘 (우) 개선된 디자인
4. 한국 유료 앱스토어 공략
아래는 2015년 ~ 2017년 사이 판매량 그래프인데, 1년에 한번씩 판매량이 눈에 띄게 증가할 때가 있었다.
바로 매년 아이폰이 출시 되는 시기이다. 안드로이드에서 아이폰으로 넘어오는 사람들 뿐 아니라 새 아이폰으로 바꾼 사람들이 이때 앱스토어를 구경하면서 새 앱을 찾아보는게 아닌가 싶다. 그리고 실제로 노트, 캘린더, 카메라 앱 등은 정말 꾸준히 새로운 앱들이 나오고 있다. 그러니 이미 앱스토어에 비슷한 앱이 있다고 프로젝트를 접지 말고 차별점을 가지고 계속해서 관리하면 유저층을 쌓을 수 있다고 생각한다.
랭킹별 다운로드 규모
주위 사람들에게 판매량을 알려주면 다들 생각보다 적다면서 놀란다. 보안카드 위젯은 특성상 ‘국내용’ 앱이기 때문에 96%는 한국 앱스토어에서 판매됐다. 그래서 순수 국내 앱스토어 만의 규모를 파악해볼 수 있었다. (씁쓸..😢) [2]
유료앱 차트 랭킹 | 1일 평균 다운로드 |
---|---|
5위 이내 | 200 ~ 300 |
10 ~ 30위 | 30 ~ 50 |
30 ~ 50위 | 10 ~ 20 |
50 ~ 100위 | 5 ~ 15 |
금융 카테고리 내에서 1위를 할 때는 하루 평균 150 ~ 200 정도 였다. 한국 앱스토어 규모가 작다고 볼 수도 있지만 뒤집어 생각하면 인기 차트에 올라가기 좀 더 쉽다는 뜻 아닐까. 그리고 순위에 오르면 마케팅 비용 없이도 사람들에게 지속적으로 노출되기 때문에 매우 좋다. 노출
👉 다운로드 발생
👉 순위 유지
👉 노출
의 선순환이 발생하는 것 같다. 그래서 사이드 프로젝트를 띄우기에 괜찮은 환경이지 않나 생각도 든다.
UI 업데이트
4년 동안 약 20번의 업데이트를 해보면서 유저들은 UI 업데이트 자체를 꺼린다는 걸 느꼈다. 너무나 심플해서 바꿀 UI가 있나 싶은 정도의 앱인데도 버튼이나 글자 색, 모양, 크기, 배치 등을 바꾸면 한동안은 낮은 별점과 악플 같은 리뷰들을 많이 받는다. 거지같다, 업뎃하지 마라, 어이없다, 돌려놔라 등등.. 익숙함이 최고의 UI인 것이다. 그래도 바꿀건 바꿔야한다는 생각에, UI를 개선할때는 당분간 욕 먹을 각오하고 감행해야 한다.
결론
사이드 프로젝트를 하자!
✏️ Notes
[1] Paul Graham, How to Get Startup Ideas: 시트콤 작가들이 각본을 쓰기 위해 만들어낸 그럴싸한 아이디어라는 뜻
[2] 보안카드 위젯의 총 누적 다운로드 수는 5.5만
📍 Special
이바닥늬우스 : 찰지고 신나는 테크바닥 늬우스
이 글의 초안을 읽어준 조소현, 강한용, 김결, 김남훈, 김영, 이승원에게 고마움을 전합니다.
Tags: side project, Security Cards Widget, App Store, MVP