Seonghyeon

내 목소리로 깨우는 아침, 퍼뜩 개발기 A to Z

개발

안녕하세요!
내 목소리로 깨워주는 알람 앱 퍼뜩을 만들었어요!
기획부터 개발, 디자인, 배포까지 4일 동안의 A to Z를 소개해보려고 해요.

퍼뜩 스크린샷

앱 소개

퍼뜩은 이런 앱이에요!

  • 내 목소리를 알람음으로 설정해요!
    • 시원하게 화내면서 녹음하면 다음 날 깨워줘요.
  • 알람 시간까지 남은 시간을 알려줘요!
    • 다시 알림, 활성화/비활성화 등 기본적인 알람 앱의 기능을 모두 제공해요.
  • 백그라운드에서도, 앱이 꺼져 있어도 무조건 목소리 알람이 울려요
    • 대부분의 알람 앱들은 앱이 꺼지면 시스템 혹은 기본 알람으로 대체돼요.
  • 방해금지 > 수면 모드에서도 알람이 울려요 (퍼뜩 알림 허용)
    • 대신 무음모드가 켜져 있을 때는 알람이 안 들려요
  • 간편함, 귀여움을 동시에 잡았어요
    • 직관적이고 편하게 사용할 수 있어요. 또 귀여운 폰트를 적용했어요.

앱 스토어에 퍼뜩이라고 쳐보세요!
Appstore: https://apps.apple.com/kr/app/%ED%8D%BC%EB%9C%A9-%EB%82%B4-%EB%AA%A9%EC%86%8C%EB%A6%AC%EB%A1%9C-%EC%95%84%EC%B9%A8%EC%9D%84-%EA%B9%A8%EC%9A%B0%EB%8A%94-%EC%95%8C%EB%9E%8C/id6758655516

🤷‍♂️ 알람 앱을 만든 이유

일어나기 너무 힘들다

요즘엔 주로 개발하다가 새벽에 자다보니 아침에 일어나기가 너무 힘들었어요. '시끄러운 알람 시계를 사야겠다' 하고 쿠팡을 찾아보던 중 갑자기 이런 생각이 들었어요.

아침에 누군가 옆에서 '야 김성현 일어나!! 더 자면 지각한다' 라고 외쳐주면 좋겠다. 예전에 인스타에 베개에 주먹질을 10번하고 자면, 진짜 그 시간에 일어난다는 영상처럼,,

'차라리 내 목소리를 녹음해서 알람으로 사용하면 어떨까?' 하는 생각이 들었고, 곧장 스벅으로 달려갔어요.

내 맘대로 기획해보자

저는 싸피 때 별명이 '아이디어 뱅크'였을 정도로 자유롭게 의견을 내는 걸 좋아해요. 당시 3주가 넘게 기술 중심으로 고민하다 주제를 결국 못 정했는데, 기술보단 페르소나에서 출발하자는 제안을 했고 40분 만에 수십 개 이상의 아이디어를 이끌어 낼 수 있었어요.

회사에선 기획대로 개발하거나 개발자가 의견을 내도 반영이 안 될 때가 많아요. 물론 지금도 의견을 많이 내는 편이지만 더 자유롭게 기획적인 역량을 펼쳐보고 싶었어요. 또 '차라리 내 서비스를 만들어서 잘 돼보면 어떨까?'라는 생각이 들었어요.

⁉️ 성공 가능성

사실 '오 이거 좋은데?!' 라고 생각한 기능은 이미 누군가 생각했거나 잘 된 앱이 있는 경우가 대부분이에요. 그래서 앱 스토어를 둘러봤는데 내 목소리를 등록할 수 있는 알람 앱은 안 보이더라고요. 앱을 만드는 중에 알라미가 녹음 기능을 제공하는 걸 알았는데 목록을 끝까지 넘겨야 보였어요.

목소리 알람이 좀 B급 감성이긴 한데 오히려 좋았어요. 만들면 재밌게 사용할 사람들이 분명 있을 것 같았거든요.

💻 개발 목표

먼저 이런 목표와 함께 개발을 시작했어요.

  1. AI를 적극 활용해 빠르게 개발한다 (Claude, Gemini)

  2. 애자일하게 개발한다

    • 추가 기능은 유저 의견을 모아 업데이트하자
  3. 직관적인 UI를 만든다

    • 특히, 사용자가 불편함을 느낄만한 Depth는 없앤다.
  4. 홍보 영상을 직접 촬영해서 홍보해보자

    • #1 내 목소리 듣고 기상하는 장면
    • #2 엄마 목소리 녹음해와서 비몽사몽 하면서 엄마 찾는 장면
    • #3 여친 or 남친 목소리 울리는 장면
  5. 단기 목표 사용자 수: 10,000명

걱정되는 부분

걱정되는 부분도 적어놨어요.

  • 목소리 녹음했는데 알람이 너무 작게 들리면 어떻게 하지? 증폭해야 하나? 이 부분이 제일 걱정이다.
  • 무음모드를 뚫고 알람이 울려야 한다.
  • 심사 통과하도록 권한 같은 것 잘 신경 써야 할듯
  • 앱 업데이트 안 거치려면 WebView로 하는 게 나을 것 같기도 하다.
    • 나중에 든 생각: 웹뷰로 했으면 배포 못했다 ㅋㅋ

웹 개발자가 왜 SwiftUI를?

1. 조금은 익숙하다

예전에 홈트친구라는 iOS 앱을 배포한 적이 있어요. 당시 인프런에서 애구마님의 SwiftUI 강의를 통해 기본적인 내용을 배우고 앱을 출시했는데, 이렇게 빠르게 실행한 수강생은 처음이라고 하시면서 제 앱 이 강의소개 탭에 실렸었어요 😃

https://www.inflearn.com/course/%EC%A7%84%EC%A7%9C-%EC%99%95%EC%B4%88%EB%B3%B4-ios-swiftui?cid=332723

당시 꽤 많은 학생들이 앱이 귀엽다면서 '이 앱 이름 뭐에요?' 등 관심을 보였는데 안드로이드 쓰는 친구들이 많아서 다운을 많이 못받았어요.

2. 사용감이 너무 좋아

iOS 컴포넌트는 기본적으로 스프링 기반 인터렉션을 사용해서 엄청 부드러워요. SwiftUI 쓰다가 Flutter 쓰면 장난감 쓰는 기분이 들 정도에요. 한국엔 아직 안드로이드 유저가 많아서 Flutter도 생각나긴 했지만 전 SwiftUI를 더 좋아해요.

📝 개발 일지

바이브 코딩으로 만들어서 손 코딩은 거의 안했는데요, 고민한 부분들은 정말 많았어요.

1일차

백그라운드, 앱 종료 시 어떻게 알람을 울려야 할까?

앱을 만들면서 가장 신경을 많이 쓴 부분은 백그라운드나 앱 종료 시에도 목소리 알람이 잘 울리게 하자였어요.

다른 알람 앱을 둘러보니 FAQ에 '알람 소리가 안나요', '알람이 안 울려요' 같이 대처 방법이 적혀 있을 정도로 유저들의 문의가 많더라구요. 앱측의 답변은 보통 '앱을 완전히 종료하지 마세요. 안그러면 시스템 알람이 울려요' 였어요.

저는 '왜 앱을 굳이 백그라운드에 남겨놔야 하지?' 라는 생각이 들었어요. 앱이 꺼지든 말든 언제든지 안정적으로 울리는 알람 앱을 만들고 싶었어요. 그런데 막상 해보니 앱 생명주기와 iOS 정책이 너무 까다롭더라구요.

처음에는 AVAudioPlayer를 사용해서 유저의 녹음 파일을 재생했는데, 포그라운드에서는 반복 재생이 잘 됐지만 앱이 백그라운드로 가거나 종료되면 한 번만 울리거나 아예 녹음파일이 재생되지 않았어요. 백그라운드에선 알람 시간에 맞춰서 앱이 알림 소리를 한 번 딱 틀고 OS에게 제어권을 반환하기 때문에 alarmPlayer.numberOfLoops = -1같은 앱 프로세스 위에서 돌아가는 무한 반복 코드가 아예 적용이 되질 않더라구요.

참고로 파일 포맷같은 부분도 AI도 자꾸 말을 바꿨어요. 이런 문제는 Apple Developer같은 공식 문서를 참고하는 게 더 빠르게 해결하는 방법이더라구요. 우리가 특정 기업의 API를 연동하려면 API 명세서를 보고 해야 하는 것처럼요.

https://developer.apple.com/documentation/usernotifications/unnotificationsound

결국 첫 날에는 이 문제를 해결하지 못하고 잤습니다.

2일차

대체할 방법을 찾다, UNNotification

이 부분은 Claude에게 아무리 물어봐도 계속 어렵다고만 하거나 백그라운드에서 Silent audio(무음 파일)를 돌리라고 말했어요. 백그라운드에서 계속 소리를 재생해서 오디오 세션을 잡아놓고 앱이 완전히 종료되지 않게 막는 거죠. 근데 딱 봐도 이건 절대 하지 말아야겠다는 생각이 들었어요. 알람 앱이 계속 배터리를 소모하게 되고, 저렇게 백그라운드에서 틀어놓으면 앱스토어 심사에 통과할 것 같지도 않았어요.

몇 시간 정도 고민하다가 퍼뜩 이런 생각이 들었어요. (틈새 홍보)

'AudioPlayer로 재생하는 게 아닌 알림(Notification) 방식으로 구현하면 어떨까?'

당근이나 카톡에서 나오는 '당근!' 이나 '카톡!' 같은 알람음 있잖아요. 이거 대신 유저가 녹음한 목소리를 들려주는 거죠. 이런 알람은 앱이 백그라운드에 있어도, 완전히 꺼져도 울리니까요.

UNNotification https://developer.apple.com/documentation/usernotifications/unnotification

저도 일반적인 앱에서는 짧은 알림 소리만 들어왔지만, UNNotification에서 울릴 수 있는 알림음의 최대 지속 시간은 30초더라구요. 퍼뜩에서는 약간의 여유를 두고 최대 녹음 시간을 25초로 설정했어요.

그런데 생각해보면 유저가 알람음을 3초간 녹음할 수도 있고, 5초 또는 10초간 녹음할 수도 있어요. 이렇게 짧게 녹음하면, 알림음(Notification)은 한 번만 울릴텐데 어떻게 알람을 계속 울리게 하지? 고민 됐어요.

처음에 든 생각은 만약 유저가 짧게 녹음했다면 녹음 파일을 30초 정도의 분량이 될 때까지 이어붙이는 방식이었어요. 하지만 이렇게 해보니 잘 재생이 되지도 않았고, 파일 크기도 너무 커졌어요. 예를 들면 400KB 파일을 생성했다고 치면, 이어 붙이면 2400KB 이런식으로 나오더라구요. 유저가 알람 2-3개만 등록해도 앱 번들보다 더 큰 음성 파일들이 생겨요.

클로드에게 천재 소리 들은 썰 푼다;

여기서도 애를 좀 먹다가 아이디어가 떠올라서 클로드에게 이런 제안을 했어요.

클로드 대화 1

⛓️‍💥 알람 체인 방식

파일을 확장하는 대신, 짧은 녹음 파일을 그대로 두고 알람 체인 방식으로 여러 번 반복 재생하는 거에요. 이러면 더 이상 30초 제한에 걸리지 않고, 몇 분도 재생할 수 있어요. 그럼 알람 간의 간격은 어떻게 정해야 할까? 생각해봤고, 파일 크기로 재생 시간을 예측한 뒤 1초 간 텀을 두고 다음 알람을 예약하는 방식으로 구현했어요.

클로드 대화 2

ㅋㅋㅋㅋ 드디어 해결해서 너무 기뻤어요

이렇게 구현을 하니 백그라운드에서도, 앱이 꺼져도 목소리 알람이 연속으로 잘 들릴 수 있었어요.

3일차

알람 제한 개수 피해가기

너무 많은 알림을 보내면 심사에서 리젝되지 않을까? 하고 물어봤는데 이 때 iOS 알림 제한이 64개라는 걸 알았어요. 사실 알림 체인 방식은 사용자가 매주, 5분 간격 다시 알림, 짧게 녹음한 경우... 이런 것들이 겹치면 엄청나게 많은 알람이 예약돼요.

이걸 효과적으로 해결할 방법이 뭐가 있을까? 하고 Gemini에게 물어보니 Sliding Window 방식을 추천해줬어요. 이건 진짜 천재적이란 생각이 들었습니다. 알림을 바로 등록하지 않고 남은 시간을 바탕으로 High, Medium, Low 등 우선순위를 두고 Queue로 관리하면서 우선순위가 높은 것부터 알림으로 등록하는 방식이에요. 사용자가 알람을 등록하거나, 알람을 끄러 앱에 들어왔을 때 우선순위를 판단해 알람 예약을 업데이트하는 거죠.

이렇게 되면 아무리 많은 알람이 쌓여도 실제 알람 등록은 64개까지만 돼요. 전 High 우선순위인 경우에는 알람 하나에 15개의 체인을 두고 울리도록 했고, 최대 등록 가능한 알림 개수는 60개로 정했어요.

UX

화면이 별로 없는데도 신경 쓸 부분이 많더라구요. 사소한 부분이 사용자 경험을 결정한다고 생각해서 UI는 최대한 꼼꼼하고 귀엽게 만들었습니다. 귀여워야 학생들이 좋아해요 ㅋㅋ

  • 열린 키보드 잘 닫히게 하기
  • 알람 토글 시 CLS 방지
  • 귀여운 폰트 적용 (omyu)
  • 초기화 로직은 백그라운드에서 실행해서 UI 보여주기 & SplashView
  • 메모리 누수 방지 - 알람, 타이머 확실히 제거하기

Omyu 다예쁨체 https://omyudiary.com/product/detail.html?product_no=73

4일차

이 글을 쓰고 있습니다...

앱 스토어 배포는 개발 만큼 빡센 것 같아요. 우선 미리디로 앱 로고를 제작했어요. 원래 앱 스토어에 올릴 스크린샷은 무료 사이트에서 만들었는데, 이번엔 좀 더 이쁘게 만들고 싶어서 Figma 템플릿을 받아서 직접 커스텀해줬어요.

피그마

앱 만들면서 느낀 것

의사 결정과 사용자 경험은 개발자의 몫

클로드는 코드를 대신 짜주지만, iOS 규제를 피한다거나 더 좋은 코드를 만드는 방법을 처음부터 알려주진 않아요. 사용자 경험을 개선하는 일 또한 개발자의 몫이라고 생각합니다.

요즘 비 개발자분들도 병렬로 클로드 6개씩 돌리거나 일주일에 앱 3개씩 만드는 시대지만, 기술에 관한 의사 결정에 대해서는 개발자가 좀 더 나은 결정을 내리지 않을까 생각이 들었어요. 그래서 앞으로 더 좋은 코드를 많이 익히고 싶어요.

테스트 코드를 작성하자

저는 사실 지금까지 테스트 코드를 거의 안 짜봤어요. 이번에 짜보라고 시켜봤는데요, 테스트하기 쉬운 코드를 작성하려면 자연스럽게 SOLID 원칙을 따르게 된다는 걸 느꼈어요. 앞으론 TDD에 대해 틈틈히 공부해보려고 해요.

자연스럽게 로직과 UI를 분리한다

2분 후에 알람이 울립니다!, 1분 안에 알람이 울립니다! 같은 문구를 테스트한다고 해보겠습니다. 만약, View에서 시간 계산을 하면 테스트 코드에서 ViewController를 참조해야 하니 테스트 코드가 무거워져요. 내가 원하는 문자열과viewController.label.text의 문자열이 UI 요소를 파고 들어가서 확인해야 할 수도 있죠. 하지만 TimeFormatter 같은 클래스를 따로 구현하면 순수 함수처럼 입력값을 넣으면 결과만 딱 비교하면 되니까 가볍고 테스트하기 편합니다.

자연스럽게 추상화하게 된다

로깅하는 걸 logger로 추상화하면 실제로는 Firebase로 전송하고, 테스트에서는 Mock으로만 기록할 수 있어요. 또한 추상화하지 않는다면, 실제로 로그를 Mixpanel 같은 걸로 갈아탔을 때 logEvent 찍는 부분을 일일히 찾아다녀야 해요.

  • Before
class AnalyticsManager {
	func logAlarmCreated() {
		Analytics.logEvent(...) // Firebase에 의존
	}
}
  • After
class AnalyticsManager {
	private let logger: AnalyticsLogging
	
	func logAlarmCreated() {
		logger.logEvent(...)
	}
}

✅ 앞으로 할 일

이번엔 수익화를 목표로 홍보와 마케팅을 제대로 해보려고 해요. 동생이 원래 칭찬 잘 안하는데 웬일로 잘 만들었다고 하더라구요 ㅋㅋ 요즘 마침 부업으로 쇼츠 영상을 제작하는데 앱 쇼츠를 찍어주겠다고 해서 주말에 본가 가서 홍보 영상을 찍을 생각입니다.

다음엔 앱 로고좀 바꾸고, 목소리 2배 증폭 기능, 무음 모드 뚫기 등 생각나는 대로 업데이트해보고, iOS 개발자 분들과 얘기하면서 알람체인보다 더 좋은 방식도 고민해보려고 합니다.

밤샘 출근은 너무 힘들었지만 ,,, 생각보다 배우는 게 많았던 것 같습니다.

긴 글 읽어주셔서 감사해요!