오늘날 모바일 애플리케이션 시장은 그 어느 때보다 경쟁이 치열합니다. 수백만 개의 앱이 사용자의 선택을 받기 위해 경쟁하는 상황에서, 뛰어난 기능만큼이나 중요한 것이 바로 '안정성'입니다. 사용자는 사소한 버그나 멈춤 현상에는 어느 정도 관대할 수 있지만, 예고 없이 앱이 종료되는 '크래시(Crash)'는 매우 부정적인 경험을 남깁니다. 단 한 번의 크래시가 사용자를 영원히 떠나게 만들고, 구글 플레이 스토어에 부정적인 리뷰를 남기게 하는 결정적 계기가 될 수 있습니다. 이는 단순히 사용자 이탈 문제를 넘어 앱의 평판 하락, 신규 사용자 유입 감소, 그리고 궁극적으로는 비즈니스 수익에 직접적인 타격을 입힙니다. 따라서 개발자에게 크래시를 신속하게 파악하고 해결하는 능력은 선택이 아닌 필수 역량이 되었습니다.
문제는 안드로이드 생태계의 극심한 파편화(Fragmentation)에 있습니다. 수천 가지에 달하는 기기 모델, 각기 다른 화면 크기와 해상도, 제조사별로 수정된 안드로이드 운영체제(OS), 그리고 다양한 API 레벨이 복합적으로 얽혀있어 개발자의 테스트 환경에서 미처 발견하지 못한 수많은 예외 상황을 만들어냅니다. 내가 사용하는 최신 플래그십 기기에서는 완벽하게 동작하던 기능이, 특정 구형 모델이나 특정 OS 버전에서는 치명적인 크래시를 유발할 수 있습니다. 이러한 모든 경우의 수를 개발자가 직접 테스트하는 것은 물리적으로 불가능에 가깝습니다. 바로 이 지점에서 안드로이드 크래시 리포팅 시스템이 핵심적인 역할을 수행합니다.
크래시 리포팅 시스템은 사용자의 기기에서 앱이 비정상적으로 종료되었을 때, 그 원인에 대한 상세한 데이터를 수집하여 개발자에게 전송하는 자동화된 솔루션입니다. 이를 통해 개발자는 자신의 책상에 앉아서 전 세계 수백만 사용자의 기기에서 발생하는 문제를 실시간으로 확인하고, 마치 사용자의 어깨너머로 디버깅하는 것처럼 문제의 근본 원인을 추적할 수 있게 됩니다. 이 글에서는 크래시 리포팅의 기술적인 작동 원리부터 시장을 선도하는 다양한 도구들의 특징과 장단점, 그리고 우리 앱에 가장 적합한 도구를 선택하기 위한 실질적인 기준까지 체계적으로 살펴보겠습니다.
콘텐츠 목차
1. 안드로이드 크래시의 해부: 무엇을, 왜 리포팅해야 하는가?
크래시 리포팅 도구를 논하기에 앞서, 우리가 정확히 무엇을 추적해야 하는지 이해하는 것이 중요합니다. 안드로이드 앱에서 발생하는 '문제'는 크게 두 가지 유형으로 나눌 수 있습니다: 처리되지 않은 예외(Unhandled Exception)로 인한 크래시와 'Application Not Responding'(ANR)입니다.
1.1. 스택 트레이스(Stack Trace): 크래시의 DNA
사용자가 앱을 사용하던 중 화면이 멈추고 '앱이 중지되었습니다'라는 시스템 다이얼로그가 나타나는 현상이 바로 크래시입니다. 이는 대부분 코드 내에서 예상치 못했거나 처리되지 않은 예외(Exception)가 발생하여 앱의 실행 스레드가 강제 종료될 때 발생합니다. 예를 들어, 서버에서 받아온 데이터가 null인데 이를 참조하려 할 때 발생하는 NullPointerException
, 또는 배열의 범위를 벗어난 인덱스에 접근하려 할 때 생기는 ArrayIndexOutOfBoundsException
등이 대표적입니다.
이때 크래시 리포팅 시스템이 수집하는 가장 핵심적인 정보가 바로 스택 트레이스(Stack Trace)입니다. 스택 트레이스는 예외가 발생한 시점까지의 메소드 호출 순서를 역순으로 기록한 로그로, 크래시의 발생 지점과 경로를 알려주는 '지도'와 같습니다. 잘 분석된 스택 트레이스를 통해 개발자는 어떤 클래스의 어떤 메소드, 몇 번째 라인에서 문제가 발생했는지 정확히 파악할 수 있습니다.
1.2. ANR (Application Not Responding): 소리 없는 암살자
ANR은 앱이 크래시되지는 않았지만, 사용자의 입력(터치, 키 입력 등)에 일정 시간(UI 스레드에서 5초 이상) 동안 반응하지 못하는 상태를 말합니다. 사용자 입장에서는 앱이 완전히 '먹통'이 된 것으로 느끼며, 크래시만큼이나 치명적인 사용자 경험을 제공합니다. ANR은 주로 UI 스레드(메인 스레드)에서 시간이 오래 걸리는 작업을 처리할 때 발생합니다. 예를 들어, 네트워크 통신, 대용량 파일 읽기/쓰기, 복잡한 데이터베이스 쿼리 등을 별도의 워커 스레드(Worker Thread)로 분리하지 않고 UI 스레드에서 직접 실행하는 경우가 주된 원인입니다.
최신 크래시 리포팅 도구들은 단순 크래시뿐만 아니라 ANR 발생도 감지하여 리포트합니다. ANR 리포트는 당시의 스레드 상태를 덤프하여 어떤 작업이 UI 스레드를 막고 있었는지 분석할 수 있는 단서를 제공하므로, 앱의 반응성을 개선하는 데 매우 중요합니다.
1.3. 난독화와 심볼리케이션(Symbolication)
대부분의 상용 안드로이드 앱은 코드 보안과 앱 용량 최적화를 위해 R8(또는 ProGuard)을 이용한 코드 난독화(Obfuscation) 및 축소(Shrinking) 과정을 거칩니다. 이 과정에서 클래스, 메소드, 변수 이름이 'a', 'b', 'c'와 같이 의미 없는 문자로 변경됩니다. 문제는 이 상태에서 크래시가 발생하면 스택 트레이스 역시 난독화된 이름으로 기록되어 개발자가 해독하기 거의 불가능해진다는 점입니다.
이때 필요한 것이 심볼리케이션(Symbolication) 또는 디오비퓨스케이션(Deobfuscation) 과정입니다. 앱 빌드 시 생성되는 매핑 파일(mapping.txt
)에는 난독화 전후의 이름 정보가 담겨 있습니다. 크래시 리포팅 도구는 이 매핑 파일을 업로드받아, 수집된 난독화된 스택 트레이스를 개발자가 알아볼 수 있는 원래의 코드로 복원해 줍니다. 따라서 정확한 크래시 분석을 위해서는 매핑 파일을 안정적으로 관리하고 리포팅 도구에 업로드하는 파이프라인을 구축하는 것이 필수적입니다.
2. 크래시 리포팅 시스템의 작동 원리
크래시 리포팅 도구는 어떻게 앱의 비정상적인 종료를 감지하고 상세한 정보를 서버로 보낼 수 있을까요? 그 핵심은 '처리되지 않은 예외 핸들러(Uncaught Exception Handler)'에 있습니다.
- SDK 초기화 및 핸들러 등록: 개발자는 앱에 크래시 리포팅 도구의 SDK(Software Development Kit)를 추가하고, 앱이 시작되는 시점(주로
Application
클래스의onCreate()
)에서 SDK를 초기화합니다. 이때 SDK는 현재 스레드의 기본 예외 핸들러 대신 자신만의 커스텀 핸들러를 등록합니다. - 크래시 발생 및 데이터 수집: 앱 실행 중 처리되지 않은 예외가 발생하면, 시스템은 기본 핸들러 대신 등록된 SDK의 커스텀 핸들러를 호출합니다. 핸들러는 앱이 완전히 종료되기 직전의 짧은 순간을 활용하여 크래시에 대한 정보를 수집합니다.
- 스택 트레이스: 발생한 예외 객체로부터 스택 트레이스를 추출합니다.
- 기기 정보: 기기 모델명(e.g., "SM-G998N"), OS 버전(e.g., "Android 13"), API 레벨(e.g., 33), 화면 해상도, RAM 크기 등을 수집합니다.
- 앱 상태: 앱 버전(
versionCode
,versionName
), 포그라운드/백그라운드 상태, 실행 시간 등을 기록합니다. - 사용자 활동 기록 (Breadcrumbs): 사용자의 마지막 행동 순서를 기록한 '빵 부스러기' 정보입니다. "버튼 A 클릭 -> 화면 B로 이동 -> API 요청 시작"과 같은 로그는 크래시 재현에 결정적인 단서를 제공합니다.
- 보고서 저장 및 전송: 수집된 데이터는 하나의 보고서 형태로 기기 내부에 안전하게 저장됩니다. 만약 크래시 발생 시점에 네트워크 연결이 불안정할 수 있으므로, 대부분의 SDK는 즉시 전송을 시도하기보다 다음 번 앱 실행 시에 저장된 보고서를 서버로 전송하는 방식을 택합니다.
- 서버에서의 처리 및 분석: 개발자 대시보드로 전송된 보고서는 서버에서 다양한 방식으로 가공됩니다.
- 심볼리케이션: 업로드된 매핑 파일을 이용해 스택 트레이스를 복원합니다.
- 그룹핑(Grouping): 근본 원인이 동일한 크래시는 하나의 이슈로 묶어 관리의 효율성을 높입니다. (e.g., 코드 상의 동일한 라인에서 발생한
NullPointerException
은 수천 번 발생하더라도 하나의 이슈로 집계됩니다.) - 통계 분석: 크래시 발생 빈도, 영향받은 사용자 수, 특정 OS 버전이나 기기에서의 집중도 등을 시각화하여 보여줍니다.
이러한 자동화된 프로세스를 통해 개발자는 수동으로 버그 리포트를 받거나 로그 파일을 분석하는 고된 작업에서 벗어나, 문제 해결 자체에 집중할 수 있는 환경을 제공받게 됩니다.
3. 시장을 선도하는 크래시 리포팅 솔루션 비교 분석
시중에는 다양한 크래시 리포팅 도구가 있으며, 각기 다른 철학과 기능, 가격 정책을 가지고 있습니다. 여기서는 가장 널리 사용되는 네 가지 솔루션을 심층적으로 비교 분석합니다.
3.1. Firebase Crashlytics: 강력한 생태계의 중심
구글이 제공하는 Firebase 플랫폼의 일부인 Crashlytics는 안드로이드 개발자들에게 가장 친숙하고 인기 있는 선택지 중 하나입니다. 과거 Fabric이라는 독립적인 회사였으나 구글에 인수된 후 Firebase에 완전히 통합되었습니다.
- 핵심 강점:
- 비용: 완벽하게 무료입니다. 앱의 규모나 사용자 수에 관계없이 모든 기능을 무료로 사용할 수 있어 개인 개발자나 스타트업에게 압도적으로 유리합니다.
- 간편한 통합: 안드로이드 스튜디오 및 구글 플레이 콘솔과의 연동이 매우 자연스럽습니다. Gradle에 몇 줄의 의존성만 추가하면 기본적인 설정이 완료될 정도로 진입 장벽이 낮습니다.
- Firebase 생태계 연동: Google Analytics, Cloud Messaging(FCM), Remote Config 등 다른 Firebase 서비스와 유기적으로 연동됩니다. 예를 들어, 특정 크래시가 어떤 애널리틱스 이벤트를 겪은 사용자 그룹에서 주로 발생하는지 분석하거나, 특정 크래시를 경험한 사용자에게만 타겟 푸시 메시지를 보내는 등의 고급 활용이 가능합니다.
- 실시간 알림: 새로운 유형의 크래시가 발생하거나, 특정 크래시의 발생 빈도가 급증할 때 이메일이나 Slack 등으로 실시간 알림을 받을 수 있습니다.
- 고려할 점:
- 기본적인 크래시 리포팅 기능에 매우 충실하지만, 일부 고급 기능(예: 네트워크 요청 로깅, 사용자 피드백 직접 수집)은 제공하지 않습니다. 더 복합적인 분석이 필요하다면 다른 도구와 함께 사용하거나 추가적인 로깅 구현이 필요할 수 있습니다.
- 적합한 대상: 거의 모든 안드로이드 개발 프로젝트의 기본 선택지로 추천할 수 있습니다. 특히 예산이 제한적이거나 Firebase의 다른 서비스를 이미 활발하게 사용하고 있는 경우 최적의 선택입니다.
// build.gradle (app)
dependencies {
// Firebase BoM(Bill of Materials) 추가
implementation platform('com.google.firebase:firebase-bom:31.2.3')
// Firebase Crashlytics SDK 추가
implementation 'com.google.firebase:firebase-crashlytics-ktx'
implementation 'com.google.firebase:firebase-analytics-ktx' // 권장
}
3.2. Sentry: 개발자 중심의 오픈소스 강자
Sentry는 오픈소스로 시작하여 강력한 커뮤니티와 유연성을 바탕으로 성장한 애플리케이션 모니터링 플랫폼입니다. 크래시 리포팅(에러 트래킹)을 넘어 성능 모니터링(APM)까지 포괄하는 넓은 범위를 자랑합니다.
- 핵심 강점:
- 오픈소스 및 자체 호스팅(Self-hosting): Sentry의 핵심은 오픈소스입니다. 이는 원한다면 자체 서버에 Sentry를 직접 구축하여 모든 데이터를 내부적으로 관리할 수 있음을 의미합니다. 데이터 보안 및 규정 준수가 매우 엄격한 금융, 의료 분야 기업에게 큰 장점입니다.
- 광범위한 플랫폼 지원: 안드로이드뿐만 아니라 iOS, 웹 프론트엔드(JavaScript), 백엔드(Python, Java, Node.js 등)를 가리지 않고 거의 모든 주요 플랫폼과 언어를 지원합니다. 여러 플랫폼에 걸쳐 서비스를 운영하는 경우, 모든 에러를 Sentry라는 단일 플랫폼에서 통합 관리할 수 있습니다.
- 상세한 컨텍스트 정보: 'Breadcrumbs'나 커스텀 태그 기능이 매우 강력하여, 크래시가 발생하기까지의 사용자 행동과 앱 상태를 매우 상세하게 재구성할 수 있습니다. 성능 모니터링 기능과 연계하면 특정 API의 느린 응답이 크래시로 이어지는 과정 등을 추적할 수도 있습니다.
- 릴리즈 상태 추적: 새로운 버전을 배포했을 때 해당 버전의 안정성을 추적하는 기능이 뛰어납니다. 특정 릴리즈에서 크래시 발생률이 급증하는 것을 즉시 파악하고, 문제가 된 커밋(Commit)을 식별하는 데 도움을 줍니다.
- 고려할 점:
- 무료 플랜이 존재하지만 이벤트 수에 제한이 있어, 사용자가 많은 앱의 경우 유료 플랜으로 전환해야 할 가능성이 높습니다.
- Firebase Crashlytics에 비해 초기 설정 및 고급 기능 활용에 조금 더 학습이 필요할 수 있습니다.
- 적합한 대상: 자체 데이터 제어가 필요한 기업, 여러 플랫폼에 걸쳐 에러를 통합 관리하고 싶은 팀, 성능 모니터링과 크래시 리포팅을 하나의 도구에서 해결하고 싶은 개발자에게 적합합니다.
3.3. Instabug: 사용자와의 소통을 더하다
Instabug는 크래시 리포팅에서 한 걸음 더 나아가, 사용자의 직접적인 피드백과 버그 리포트를 받는 데 특화된 솔루션입니다. 사용자가 기기를 흔들거나 특정 버튼을 누르면 스크린샷, 화면 녹화, 주석 달기 기능이 포함된 피드백 창이 나타납니다.
- 핵심 강점:
- 인앱(In-app) 버그 리포팅: 사용자가 크래시가 아닌 UI 오류나 불편 사항을 발견했을 때, 앱을 떠나지 않고 즉시 개발팀에 리포트할 수 있는 강력한 채널을 제공합니다. 이 리포트에는 스크린샷뿐만 아니라 당시의 모든 네트워크 요청 로그, 콘솔 로그, 기기 정보 등이 자동으로 첨부되어 문제 재현에 큰 도움이 됩니다.
- 사용자 설문 및 피드백: 앱 내에서 특정 사용자 그룹에게 설문조사를 보내거나 새로운 기능에 대한 피드백을 요청하는 등, 사용자와의 적극적인 소통이 가능합니다.
- 상세한 네트워크 로깅: 모든 네트워크 요청과 응답(헤더, 파라미터 포함)을 자동으로 기록하여, API 관련 버그를 디버깅할 때 매우 강력한 무기가 됩니다.
- 고려할 점:
- 핵심 기능이 유료 플랜에 집중되어 있으며, 다른 도구에 비해 가격대가 높은 편입니다.
- 순수한 크래시 리포팅 기능 자체는 Crashlytics나 Sentry와 대등하지만, Instabug의 진가는 사용자 피드백 기능과 결합될 때 발휘됩니다.
- 적합한 대상: 베타 테스트를 활발하게 진행하거나, 사용자 피드백을 통해 서비스를 빠르게 개선하는 것을 중요하게 생각하는 팀. 특히 비개발자인 QA팀이나 기획자가 버그를 리포트하는 과정의 효율성을 높이고 싶을 때 매우 유용합니다.
3.4. Bugsnag: 안정성 스코어를 통한 관리
Bugsnag는 앱의 전반적인 '건강 상태'를 정량적인 지표로 보여주는 데 중점을 둔 플랫폼입니다. 모든 크래시를 심각도에 따라 분류하고, 앱 버전별 '안정성 점수(Stability Score)'를 제공하여 릴리즈 관리의 기준을 제시합니다.
- 핵심 강점:
- 안정성 점수: 각 릴리즈마다 크래시가 없는 사용자 세션의 비율을 계산하여 99.5%와 같은 직관적인 점수로 보여줍니다. 이를 통해 팀은 '다음 릴리즈의 안정성 목표는 99.9% 이상으로 한다'와 같은 명확한 목표를 설정하고 관리할 수 있습니다.
- 세분화된 필터링 및 검색: 특정 사용자 세그먼트(e.g., 유료 구독자), A/B 테스트 그룹, 특정 기능 플래그가 활성화된 사용자 등 매우 세분화된 조건으로 크래시를 필터링하고 분석하는 기능이 강력합니다.
- 뛰어난 통합성: Jira, Slack, GitHub 등 개발자들이 사용하는 거의 모든 주요 도구와의 통합을 지원하여, 크래시 발생 시 자동으로 이슈 티켓을 생성하는 등의 워크플로우 자동화가 용이합니다.
- 고려할 점:
- 유료 서비스이며, 가격은 사용자 수나 이벤트 수에 따라 책정됩니다.
- 데이터 기반의 체계적인 품질 관리를 지향하는 만큼, 소규모 팀보다는 어느 정도 규모가 있는 조직에서 그 가치를 최대한 활용할 수 있습니다.
- 적합한 대상: 데이터 기반으로 앱의 품질 목표(SLO/SLA)를 설정하고 관리하고자 하는 팀, 복잡한 사용자 세그먼트별로 안정성을 추적해야 하는 대규모 서비스에 적합합니다.
4. 우리 앱을 위한 최적의 도구 선택 프레임워크
이렇게 다양한 도구들 앞에서 어떤 것을 선택해야 할지 막막할 수 있습니다. '최고의' 도구는 존재하지 않으며, 오직 '우리 팀과 프로젝트에 가장 적합한' 도구만 있을 뿐입니다. 다음의 질문들을 따라가며 합리적인 결정을 내릴 수 있습니다.
- 프로젝트의 규모와 예산은 어느 정도인가?
- 개인 프로젝트 / 초기 스타트업: 예산 제약이 크다면 고민할 필요 없이 Firebase Crashlytics가 최고의 출발점입니다. 무료이면서도 핵심적인 기능은 모두 갖추고 있습니다.
- 중견 기업 / 대규모 서비스: 월간 활성 사용자(MAU)가 수십만 이상이고 안정성이 비즈니스에 직결된다면, Sentry, Bugsnag과 같은 유료 도구 도입을 적극 검토해야 합니다. 이들의 고급 필터링, 성능 모니터링, 체계적인 릴리즈 관리 기능은 투자 가치를 충분히 합니다.
- 팀의 개발 워크플로우는 어떠한가?
- Jira, Slack 등과의 연동이 중요한가?: 대부분의 유료 도구는 뛰어난 서드파티 통합을 제공합니다. 특히 Bugsnag과 Sentry는 워크플로우 자동화에 강점이 있습니다.
- QA팀이나 비개발자와의 협업이 잦은가?: QA 담당자가 버그를 리포트하는 과정을 간소화하고 싶다면 Instabug의 인앱 리포팅 기능이 매우 효과적입니다.
- 필요한 데이터의 깊이는 어느 정도인가?
- 기본적인 크래시 로그면 충분한가?: Firebase Crashlytics가 제공하는 정보만으로도 대부분의 크래시를 해결할 수 있습니다.
- 네트워크 요청/응답까지 봐야 하는가?: API 통신 오류가 잦고 디버깅이 어렵다면 Instabug가 제공하는 네트워크 로깅이 시간을 크게 절약해 줄 것입니다.
- 성능 저하 문제(느린 화면 전환, 긴 로딩 시간)도 함께 보고 싶은가?: Sentry의 APM(Application Performance Monitoring) 기능을 활용하면 크래시와 성능 문제를 한 곳에서 관리할 수 있습니다.
- 어떤 플랫폼을 지원해야 하는가?
- 안드로이드 네이티브 앱만 개발하는가?: 어떤 도구를 선택해도 무방합니다.
- iOS, 웹, 백엔드까지 함께 관리해야 하는가?: Sentry는 여러 플랫폼에 걸친 에러를 하나의 대시보드에서 통합 관리하는 데 가장 강력한 솔루션입니다.
많은 경우, 하나의 도구만 사용하는 대신 두 가지 도구를 조합하는 하이브리드 전략도 유효합니다. 예를 들어, 모든 개발자가 기본적으로 Firebase Crashlytics를 통해 전반적인 크래시를 모니터링하고, QA팀이나 베타 테스터에게는 Instabug SDK가 포함된 별도의 빌드를 배포하여 상세한 피드백을 받는 방식입니다.
5. 리포팅을 넘어 안정성 문화 구축으로
마지막으로 강조하고 싶은 것은, 크래시 리포팅 도구는 문제를 알려주는 '수단'일 뿐, 그 자체가 문제를 해결해주지는 않는다는 점입니다. 성공적인 앱 안정성 관리는 좋은 도구를 도입하는 것에서 시작하여, 그것을 중심으로 팀의 문화를 만들어나갈 때 완성됩니다.
- 체계적인 알림 설정: 모든 크래시에 대해 알림을 받으면 '알림 피로(Alert Fatigue)'에 빠지기 쉽습니다. 정말 중요한 알림(e.g., 새로운 버전에서 발생한 치명적인 크래시, 전체 사용자에게 영향을 미치는 크래시)에 집중할 수 있도록 알림 규칙을 현명하게 설정해야 합니다.
- 이슈 우선순위 결정: 모든 크래시를 즉시 해결할 수는 없습니다. 영향받는 사용자 수, 크래시 발생 빈도, 비즈니스에 미치는 영향(e.g., 결제 과정에서 발생하는 크래시) 등을 종합적으로 고려하여 우선순위를 정하는 프로세스를 수립해야 합니다. Bugsnag의 안정성 점수나 Sentry의 릴리즈 헬스 같은 기능이 이러한 의사결정에 도움을 줍니다.
- 정기적인 리뷰 문화: 매주 또는 매 스프린트 시작 시, 팀 전체가 모여 크래시 리포트 대시보드를 함께 리뷰하는 시간을 가지는 것이 좋습니다. 이를 통해 팀원 전체가 앱의 안정성 현황에 대한 공통된 인식을 가지고, 문제 해결에 대한 책임감을 공유하게 됩니다.
- 피드백 루프 완성: 크래시를 해결하고 새로운 버전을 배포했다면, 반드시 해당 이슈가 정말로 해결되었는지 크래시 리포팅 도구를 통해 확인해야 합니다. '수정됨(Fixed)'으로 표시된 이슈가 다음 버전에서 다시 나타나지 않는지 추적하는 것은 매우 중요합니다.
결론적으로, 안드로이드 크래시 리포팅 시스템은 불확실성으로 가득한 모바일 생태계에서 우리 앱의 건강 상태를 지속적으로 체크하고 문제 발생 시 신속하게 대응할 수 있게 해주는 필수적인 '청진기'와 같습니다. 우리 프로젝트의 특성과 목표에 맞는 최적의 도구를 선택하고, 이를 중심으로 체계적인 안정성 관리 문화를 구축한다면, 사용자에게는 사랑받고 비즈니스로는 성공하는 견고한 앱을 만들어나갈 수 있을 것입니다.
0 개의 댓글:
Post a Comment