본문 바로가기
대회ㆍ수상/🍀새싹톤

서울의 미래를 바꾸는 청년, 새싹톤 해커톤을 마치고

by 굿햄 2024. 2. 5.

글의 초안은 6월에 작성해놓았으나 회사 이직 후 미루고 미루다 후기를 작성하게 되었습니다.

SBA X Goorm, 2023 새싹톤에 참여하기까지

올해 이직하기 전인 5월, 지인의 소개로 서울시와 구름에서 진행하는 해커톤에 참여하게 되었습니다.

 

 

 

이번 해커톤은 팀 별로 5명을 모아야 했는데, 심사를 통해 팀을 구성받게 되었고, 

팀원은 기획자, 디자이너, 백엔드 개발자, 프론트엔드 개발자, 안드로이드 개발자(저)로 구성되었습니다.

 

 

 

노션과 카톡으로 일정을 진행

새싹톤에서 제시한 주제로 약 10가지의 의견이 나왔습니다. 

좋은 주제가 없으면 어떡하지 고민했는데, 오히려 너무 많이 나와버려서 고르느라 애를 먹었습니다. 

 

노션과 카톡으로 일정을 진행하며 투표를 통해 "생활속의 소음분쟁을 해소하자!"를 주제로 선정하고 "잡았다 소음" 앱을 제작하기로 결정했습니다. 

팀 이름은 환경 관련 주제에 맞춰 Greenie(그리니)로 정해졌습니다.


프로젝트 소개 - "잡았다 소음"

일상 생활에서 발생하는 생활 소음의 원인을 분석하고, 해결 방안을 마련할 수 있도록 도움을 주는 Android 앱입니다.


앱의 주요 기능은 크게 세 가지입니다.


소음 녹음 및 AI 분석: 실시간으로 소음을 녹음하면서 데시벨을 측정하고, 녹음이 끝나면 온디바이스 AI가 소음원을 14개 카테고리로 분류하여 분석 결과를 제공합니다.
트래킹 검사: 최대 50분 동안 장시간 소음을 모니터링하며, 분 단위로 최대 데시벨을 기록하여 어느 시간대에 큰 소리가 발생했는지 확인할 수 있습니다.
녹음 기록 관리: 과거 녹음 기록을 날짜별로 조회하고, 평균/최대 dB 그래프를 확인하거나 재생할 수 있습니다.
이 외에도 WebView를 통해 소음 관련 팁, 상담 정보, 건강 정보 등의 콘텐츠를 제공합니다.


기술 스택 및 아키텍처


약 3주라는 짧은 기간이었지만, 추후 유지보수와 확장성을 고려하여 Clean Architecture와 MVVM 패턴을 멀티모듈 구조에 적용했습니다.

 

전체 구조는 Data -> Domain <- UI의 의존성 방향을 가지며, Dagger Hilt를 통한 DI로 각 레이어 간의 결합도를 낮췄습니다.


모듈은 크게 app, core, feature 세 가지 영역으로 나뉩니다.
app 모듈은 앱의 진입점으로, 네비게이션 제어와 Firebase Token 생성을 담당합니다.
core 모듈은 앱 전반에서 공통으로 사용되는 기능들을 담고 있으며, 총 9개의 하위 모듈로 구성했습니다.

core:common - 공통 유틸리티 클래스
core:data - Database 및 SharedPreference를 통한 데이터 접근
core:database - Room ORM을 이용한 녹음 기록 DB 관리
core:designsystem - Theme, Color, TextStyle, Shape 등 UI 기반 요소
core:domain - UseCase와 Repository 인터페이스 (비즈니스 로직)
core:model - 앱 전체에서 사용되는 공통 모델 클래스
core:service - 녹음 및 트래킹을 위한 Foreground Service
core:sharedpreference - 사용자 설정 저장
core:ui - 공통 UI 컴포넌트 및 리소스

 

feature 모듈은 각 화면별로 단일 책임을 가지도록 분리했습니다. 

feature:home - 앱의 메인 화면
feature:record - 녹음 및 실시간 데시벨 측정 화면
feature:tracking - 장시간 소음 트래킹 화면
feature:history - 녹음 기록 조회 화면
feature:result - AI 소음원 분석 결과 화면
feature:web - WebView 기반 콘텐츠 화면

 

사용한 주요 라이브러리는 Jetpack Compose(UI), TensorFlow Lite(AI 분석), Dagger Hilt(DI), Retrofit2(네트워크), Room(로컬 DB), Flow(비동기 데이터 흐름)입니다.


핵심 기능 구현

소음 녹음

녹음 기능은 Foreground Service 위에서 동작합니다. AudioRecord를 16kHz, Mono, 16bit PCM 설정으로 초기화한 뒤, 실시간으로 PCM ShortArray 데이터를 수신합니다.


수신된 데이터에서 Amplitude의 최대값을 추출하고, 다음 공식으로 데시벨을 연산합니다.

p = getMaxAmplitude() / 51805.5336
X = 20 * log10(p / p0)


연산된 데시벨 값은 SharedFlow를 통해 UI로 전달되어, 실시간/최소/평균/최대 데시벨 미터를 갱신합니다.


녹음 중에는 버퍼가 500,000 samples를 초과할 때마다 PCM 파일로 flush하여 메모리를 관리하고, 녹음 종료 시 PCM 데이터에 RIFF 헤더를 추가하여 WAV 파일로 변환한 뒤 Room DB에 기록을 저장합니다.

 

AI 소음원 분석

녹음이 완료된 WAV 파일을 TensorFlow Lite로 분석하는 파이프라인입니다.


TensorFlow Hub에서 제공하는 YAMNet 모델을 기반으로 사용했습니다. 

YAMNet은 오디오 파형을 입력으로 받아 AudioSet 온톨로지의 521개 오디오 이벤트 각각에 대해 독립적인 예측을 하는 오디오 이벤트 분류기로, MobileNet v1 아키텍처를 사용하며 AudioSet 코퍼스로 학습된 모델입니다.


추가로 애완동물, 대화 소리 등의 정확도를 높이기 위해 AI Hub에서 제공하는 '도시 소리 데이터'를 커스텀 학습시켰습니다.
분석 과정은 다음과 같습니다.

1. WAV 파일 전체를 0.1초 단위로 분할합니다 (0.9초는 건너뜁니다).
2. 분할된 구간의 데시벨이 45dB 이상인 경우에만 YAMNet 모델로 추론합니다.
3. 추론 결과의 정확도가 66% 이상이면 해당 카테고리에 점수 1점을 부여합니다.
4. 모든 구간의 분석이 끝나면 14개 카테고리별 점수를 집계하여 Room DB에 저장합니다.
5. 결과는 WebView를 통해 시각적으로 표시됩니다.

 

트래킹 검사

트래킹은 최대 50분 동안 소음 수준을 모니터링하는 기능입니다.

CountDownTimer와 AudioRecord를 조합하여 100ms 주기로 PCM 데이터를 수신하고 데시벨을 연산합니다.


매분마다 해당 분의 최대 데시벨을 기록하여 NoiseHistoryData로 저장하고, SharedFlow를 통해 UI에 전달합니다. 일시정지와 재개가 가능하며, 결과 화면에서 시간대별 최대 dB을 확인할 수 있습니다.


녹음 서비스와 트래킹 서비스는 ServiceState를 통해 관리되며, 두 서비스가 동시에 실행되지 않도록 제어합니다.


기술적 도전과 해결

MediaRecorder 대신 AudioRecord를 선택한 이유

처음에는 Android에서 녹음에 일반적으로 사용하는 MediaRecorder를 고려했습니다. 

하지만 MediaRecorder는 인코딩된 파일을 제공하기 때문에 녹음된 파형을 실시간으로 분석할 수 없다는 문제가 있었습니다.


저희 앱은 실시간 데시벨 측정과 AI 소음원 분석 두 가지를 모두 수행해야 했기 때문에, 

PCM 원시 데이터를 직접 수신할 수 있는 AudioRecord를 선택했습니다. 

 

이를 통해 실시간으로 소음의 제공원을 TensorFlow 모델로 분석하는 것이 가능해졌습니다.

 

장시간 녹음 시 OutOfMemoryError

초기 구현에서는 녹음된 ByteArray를 지역변수에 할당하여 Stack 메모리에 적재하는 방식이었습니다. 

 

짧은 녹음에서는 문제가 없었지만, 장시간 녹음을 테스트하자 OutOfMemoryError가 발생했습니다. 

해커톤 마감이 다가오는 상황이라 빠르게 원인을 파악해야 했습니다.


해결책으로, 녹음 데이터를 메모리에 쌓아두지 않고 App의 Data 영역에 파일을 생성하여 InputStream/OutputStream을 통해 주기적으로 PCM 형식으로 저장하도록 변경했습니다. 

버퍼가 500,000 samples를 초과하면 파일로 flush하고, 녹음 종료 시 PCM 파일을 WAV로 변환하는 방식입니다. 

수정 후 장시간 녹음에서도 안정적으로 동작하는 것을 확인하였습니다.

 

TensorFlow 모델 학습 오류

AI Hub의 '도시 소리 데이터'를 TensorFlow Model Maker로 YAMNet 모델에 추가 학습시키려 했으나, 

학습 과정에서 원인 모를 오류가 반복되어 2~3일을 꼬박 매달렸습니다. 

 

해커톤 일정은 계속 흘러가는데 AI 모델이 학습조차 안 되니 초조해질 수밖에 없었습니다.


원인을 하나하나 추적해보니 학습 데이터 중 일부 파일이 학습 요건(16bit, Mono)에 충족하지 않아 발생하는 문제였습니다. 

 

Shell script를 직접 작성하여 모든 학습 데이터를 요건에 맞게 일괄 변환한 후, YAMNet 모델에 새로운 카테고리로 학습시켜 해결했습니다. 드디어 학습이 정상적으로 돌아가는 걸 확인했을 때의 안도감은 지금도 기억납니다.


관련 소스: https://github.com/Greenie-crew/tensorflow-model-maker-script

 

GitHub - Greenie-crew/tensorflow-model-maker-script

Contribute to Greenie-crew/tensorflow-model-maker-script development by creating an account on GitHub.

github.com

 

서비스 반복 실행/중지 시 오류

녹음 버튼을 빠르게 반복적으로 누르면 서비스가 비정상적으로 동작하는 오류가 있었습니다. 

 

마이크 활성화 후 앱에서 데이터를 전달받기까지 약 1초 내외의 빈 ByteArray가 전달되는데, 이 대기 시간 중에 서비스를 다시 시작하면 충돌이 발생했습니다.


CoroutineScope를 활용하여 이전 작업이 완전히 종료되었는지 판단한 후, 안정적으로 작업이 종료되어야 다음 작업이 실행되도록 변경하여 해결했습니다.

 

TensorFlow 분석 속도 최적화

초기에는 녹음 형식이 44.1KHz였는데, 음원 전체를 AI로 분석하다 보니 처리 시간이 과도하게 오래 걸렸습니다.

녹음은 금방 끝나는데 분석 결과가 한참 뒤에 나오니, 실제 사용자 입장에서는 답답할 수밖에 없는 상황이었습니다.


두 가지 방법으로 최적화했습니다. 

 

첫째, 녹음 형식을 44.1KHz에서 16KHz로 낮추어 음성 분석이 가능한 최소 요건으로 조정했습니다. 

 

둘째, 음원 전체를 분석하지 않고 1초 중 0.1초만 추출하여 검사하도록 재설계했습니다. 

 

이를 통해 분석 속도를 크게 개선하면서도 결과의 정확도를 유지할 수 있었습니다. 

체감될 정도로 빨라진 분석 결과를 보고 나서야 비로소 "이건 실제로 쓸 수 있겠다"는 확신이 들었습니다.

 

개발 과정

팀원들이 실무를 진행하던 분들이어서 각자의 분야에서 어떻게 진행하는 것이 좋을지 공유할 수 있었습니다. Google Meet과 Slack을 통해 기능, 방향, 유사 서비스 벤치마킹을 진행했습니다.

 

기능이 추가되었다 빠지는 경우가 많았지만, 초기 과도기에는 어쩔 수 없는 부분이라 생각합니다. 

시간이 지나며 방향이 맞춰졌고 제작 속도에 맞춰 기획과 디자인도 완성되어 갔습니다. 

 

심플한 디자인에 완성을 향해 밤새 코드를 열심히 쳤던 기억이 납니다. 

3~4번 정도 오프라인으로 만나며 주기적으로 현 상황과 개선 방향을 주고받았습니다.

 

개발 타임라인은 대략 다음과 같습니다.


6/4: 소음 측정 기본 기능 완성 (실시간/최소/평균/최대 dB), PCM to WAV 변환, 서비스 안정성 수정
6/9: 장시간 녹음 안정화 (버퍼 기반 저장), AI 분석 알고리즘 구체화, WebView 결과 표시, 기록 화면 구현
6/10: TensorFlow 처리 속도 최적화 (44.1KHz to 16KHz, 0.1초 샘플링), Navigation 개선
6/11: 트래킹 서비스 구현, WebView 인터페이스 확장 (클립보드 복사, intent 스키마 처리)
6/12: 트래킹 결과 페이지, 기록 그래프 개선
6/15: 그래프 개선 및 Firebase 연동

 

 

제작 과정의 코드는 GitHub를 통해 확인할 수 있습니다: https://github.com/Greenie-crew

 

Greenie-crew

Greenie-crew has 6 repositories available. Follow their code on GitHub.

github.com


발표날

생각보다 많은 인원이 모인 광경을 보니 긴장감이 달아올랐습니다. 

 

각 팀의 기획자가 앱 소개를 진행했는데, 저희 팀의 기획자분이 발표하는 모습이 너무 멋져보였습니다. 

 

3주 동안 함께 고민하고 만들어온 결과물을 앞에서 설명하는 모습을 보니 뿌듯했습니다.


인기상 3등을 받다!

결승에는 진출하지 못해 아쉬웠지만, 다른 팀들의 추천으로 인기상 3등을 받았습니다. 

 

그리고 막판에 진행된 로봇대결에서도 우승하여 선물까지 챙길 수 있었습니다. 

결승 진출은 못 했어도 다른 팀들이 저희 앱에 관심을 가져주셨다는 것 자체가 기뻤습니다.


회고

이번 해커톤을 통해 몇 가지를 배울 수 있었습니다.


첫째, 약 3주라는 제한된 시간 안에서도 Clean Architecture와 멀티모듈 구조를 적용할 수 있다는 것을 확인했습니다. 

 

오히려 시간이 촉박할수록 잘 설계된 구조가 개발 속도를 높여준다는 것을 체감했습니다. 

모듈이 분리되어 있으니 기능별로 독립적으로 작업할 수 있었고, 한 곳을 수정해도 다른 곳에 영향을 주지 않아 빠르게 기능을 추가할 수 있었습니다.


둘째, TensorFlow Lite를 활용한 온디바이스 AI가 모바일 환경에서도 충분히 실용적이라는 것을 경험했습니다. 

서버 없이 단말기에서 직접 소음원을 분류할 수 있었고, 샘플링 최적화를 통해 분석 속도도 실사용 가능한 수준으로 개선할 수 있었습니다.


셋째, 기획자분들의 역할이 정말 중요하다는 것을 느꼈습니다. 

개인간 일정이 다르다보니 회의 일정 조율과 개발 간 내용 전달이 쉽지 않았을 텐데, 예상보다 수월하게 진행된 것은 기획 쪽에서 많이 신경 써주셨기 때문이라 생각합니다.


넷째, 제 스스로를 파악하는 좋은 경험이었습니다. 

일정을 언제까지 마감할 수 있는지, 어느 정도 기술을 구현할 수 있는지 확인할 수 있었습니다.


마지막으로 함께 밥을 먹고 다음을 기약하며 흩어졌습니다. 다음에 또 기회가 있다면 참여해보고 싶습니다.

 

 

ⓒ 굿햄 2023. daryeou@gmail.com all rights reserved.

댓글