들어가기 전에
저의 앱 출시기에 많은 관심 가져주셔서 감사합니다. 지난 주말부터 갑자기 조회수가 늘더니 벨로그 이번 주 트렌딩이라니 감개무량하네요. 부족한 글이지만 읽어주셔서 감사합니다. 스크린샷은 제가 간직하기 위해 첨부합니다 ㅎㅎ
2022 년 하반기 필자에게 가장 큰 이벤트였던 Post Black Belt 앱이 드디어 출시되었다. 본 글에서는 주니어 프론트엔드 개발자가 앱을 개발하게된 계기와 개발 과정 중에 느낀 점들을 종합한 1 인 개발기를 적어보려 한다. 힘든 과정이었지만 프론트엔드에 국한하여 나 자신을 가두었다면 경험하지 못했을 값진 경험들과 구글 플레이스토어로부터의 전화(..^^)를 받을 수 있어서 개발자 인생에서도 좋은 터닝 포인트였다.
먼저 필자에 대해 간단한 소개를 하자면 아래와 같다.
- 2021 년 7 월부터 웹 개발(JavaScript) 입문
- 기존 학업을 마무리짓고 2022 년 8 월부터 본격적으로 프론트엔드 개발 중
본 앱을 처음 기획했을때 1 년(심지어 학업과 병행하였기에 반쪽짜리..)이라는 짧은 웹 개발 경력을 가지고 있어 커리어에 고민이 많았지만, 지금은 번듯한 앱 하나를 만들어 낸 것에 큰 자긍심을 가지고 있다. 이 글이 앱 개발을 고민해보고 있는 다른 웹 개발자 분들에게도 도움이 되길 바라며 본격적인 개발기를 적어보겠다. 구글의 전화가 궁금하신 분들은 글의 가장 하단부를 확인하러 가시면 된다.
본래 이 프로젝트는 백엔드 개발자 1 명과 함께 기획한 웹 프로젝트였다. 요즘 필자는 주짓수라는 운동에 빠져있는데, 도장에 백엔드를 공부하고 있는 개발자 친구를 발견하여 함께 주짓수 기술을 분류하는 웹사이트를 만들어보기로 하였다.
주짓수(Jiu-jitsu)는 일본의 유도를 브라질 격투술에 접목하여 관절 꺾기나 조르기 등을 이용하여 상대방을 제압하는 무술이다. 아래 영상은 상대방의 경동맥을 차단해 기절시키는 초크를 필자가 가장 좋아하는 선수가 구사하는 모습이다(출처 링크). 이 영상을 보고 가슴이 두근거린 분들은 어서 도장에 등록하시고 앱을 다운받으시길 바란다.
주짓수에는 상대를 제압하는 다양한 기술들이 존재하며 인간 체스라는 별칭이 있을 정도로 나와 상대의 움직임에 따라 수많은 기술을 구사할 수 있다. 우리 팀은 통상적으로 주짓수의 가장 높은 단계로 여겨지는 블랙 벨트로 가기 위해 10 년이라는 긴 기간이 소요되는 요인 중 하나가 이 때문이라 생각하였다. 따라서 주짓수 수련자들의 운동을 돕는 서비스의 목적을 달성하기 위해 다양한 기술들을 일러스트레이트와 설명으로 분류하고 연관 영상들을 첨부하는 웹 사이트를 기획하게 되었다.
그러나 첫 아이디어에는 두 가지 큰 문제점이 있었다.
- 두 개발자 모두 주짓수를 수련한지 1 년이 채 되지 않아 기술에 대한 이해도가 높지 않다.
- 기술 별 일러스트와 설명에 대한 콘텐츠가 필요하다.
첫 번째는 관장님의 도움을 받아 일정 부분 해결이 가능하지만 지속적으로 아이디어를 발전시키기에는 한계가 존재했다. 두 번째의 경우 지인의 소개를 받아 관련 일러스트를 그리시는 분을 찾았지만 주짓수 특성상 동일한 기술에도 주관적인 견해가 많이 주입되는 것에 어려움을 겪었다.
결국 우리는 기술 콘텐츠를 서비스에서 제공하는 것이 아니라 사용자가 직접 기록하는 방향으로 설정하여 주짓수 다이어리를 만드는 것으로 아이디어를 변경하였다. 본 서비스의 특별화된 점은 다음과 같다. 이해를 돕기위해 실제 서비스 스크린샷을 첨부하였다. 필자는 아직 화이트 벨트지만 예쁜 스크린 샷을 위해 퍼플 벨트를 잠시 꿈꾸어보았다.
- 사용자는 일기를 주짓수 카테고리(ex. 기술 연습, 대회 등)와 기술 카테고리(ex. 가드, 가드 패스 등)와 함께 작성하고 월간 달력 형식으로 확인한다.
- 작성된 일기를 기술 별로 분류하여 수련 기록을 돕고 기술 분포도 및 개인 정보를 통해 사용자의 수련 현황을 체크할 수 있다.
아이디어를 변경하고 나니 일기장을 웹 페이지에서 작성하는 것에 대한 새로운 논점이 발생하였다. 일기는 사용자들이 자주 가지고 다니는 스마트폰에 앱으로 설치하는 것이 적합하는 의견이 모아졌고 여러 서치와 고민 끝에 React에서 React Native로 프론트엔드 라이브러리를 변경하기로 하였다. 참고 개발기를 보면 아직 React도 잘 다루지 못하는데 새로운 라이브러리를 학습하는 것에 두려움을 가진 필자의 모습을 확인할 수 있다. 결과적으로는 React와 유사하게 앱 개발을 할 수 있다는 큰 장점(비슷하다고 했지 똑같다고는 안했다..)을 직접 느껴볼 수 있었고, React Native만의 API를 사용하면서 비교를 통해 React를 더 깊게 이해하게 되는 좋은 계기가 되었다.
위와 같이 2022 년 9 월부터 아이디어를 확정하여 개발하기 시작하였으나 기쁘고 안타깝게도 함께 프로젝트를 하던 백엔드 개발자 친구가 교육 프로그램에 합격하여 함께하지 못하게 되었다. 따라서 필자는 따로 서버를 띄우지 않고 사용자의 기기에만 데이터를 저장하여 프론트엔드 개발에만 더 집중하기로 하였다.
그러나 개발하다보면 어쩔 수 없이 풀스택을 건드리게 되는 모습을 #2 장의 개발기에서 확인할 수 있다... To be continued
참고 개발기: [PBB(2)] 앱📱 만드는데 왜 프론트엔드 개발자를 뽑을까? | Web ➡️ Mobile App 전환하기
필자는 규모가 큰 기술을 새로 접할 때 학습 시간을 단축하기 위해 유튜브나 무료 강의들을 빠르게 수강한 후 프로젝트에 적용하는 편이다. React Native를 처음 배우기 위해 노마드 코더의 무료 강의를 2 일정도 수강하였고, React Native는 JavaScript 언어를 사용하여 앱 개발을 할 수 있지만 앱 개발자와 동일한 셋업을 모두 갖춰야 한다는 단점을 알게 되었다. 따라서 이를 보완하기 위해 인프라 구조가 갖춰진 Expo를 추가로 붙이기로 결정하였고, 빠르게 앱을 내 기기에 띄워볼 수 있었다.(고마워요 니꼬쌤!!)
당연한 이야기이지만 React Native는 React와 유사한 개발 경험을 가지게 만든 앱 개발 라이브러리이므로 우리(웹 개발자)가 사용하는 HTML 태그와 다른 이름들을 활용하게 된다. 아래의 코드는 간단한 버튼을 구현한 예시인데 컴포넌트에 글자를 넣기 위해서는 꼭 Text
태그 내에 넣어줘야 한다는 차이점이 있다. 또한 CSS도 웹과 동일하게 적용되지 않는 경우가 간혹 발생한다.
import { Text, TouchableOpacity, StyleSheet } from "react-native";
export default function WideBtn({ children, backgroundColor, onPress }) {
return (
<TouchableOpacity
style={{ ...styles.btn, backgroundColor }}
onPress={onPress}
>
<Text>{children}</Text>
</TouchableOpacity>
);
}
const styles = StyleSheet.create({
btn: {
paddingHorizontal: 70,
paddingVertical: 15,
borderRadius: 10,
alignItems: "center",
},
});
웹의 경우 onClick
이라는 이벤트를 통해 대부분의 태그에서 발생하는 사용자의 클릭 이벤트를 받아올 수 있지만, React Native에서는 TouchableOpacity
등의 터치를 가능하게 해주는 컴포넌트로 감싸주어야 사용자의 터치 이벤트를 받아올 수 있다. 이 외에도 웹에서는 <a>
태그로 간편하게 다른 페이지로의 이동이 가능했지만, React Native에서는 Linking API를 사용해야 앱 내에서 다른 URL로 이동이 가능하다.React 쓸 때가 좋았다.
#2.1.1은 공식 문서에서 API 사용법을 익히면 해결되는 사소한 차이점들이었다. 필자가 앱 개발을 하며 가장 혼돈을 겪었던 부분은 웹의 Routing과 앱의 Stack Navigation의 차이다. 화면이 변경될 때 React에서 사용하던 useEffect
hook이 예상처럼 적용되지 않는다는 것을 깨달았고, 화면을 이동하는 방식이 아래 그림과 같이 차이점을 갖는다는 사실을 알게되었다. 이 부분을 공부하면서 React의 라우팅 방식에 대해서도 더 면밀히 살펴볼 수 있게 되었고, 패키지를 사용할 때 작동 방식을 이해하고 프로젝트에 적용해야 한다는 말을 개발기를 작성하며 몸소 실천하게 되었다.
참고 개발기: [React Navigation] useEffect가 아닌 useFocusEffect 사용하여 stack 구조 화면 초기화하기
노마드 코더 니꼬쌤의 강의에 따르면 React Native팀은 처음에 많은 컴포넌트와 API를 제공했다고 한다. 그러나 해당 기능들에서 버그가 많아지면서 서비스 규모를 줄이고 해당 부분들을 커뮤니티의 패키지에서 사용하는 것으로 변경하였다. 다행히도 Expo를 사용하게 되면 React Native에서 지원이 종료된 패키지들을 손쉽게 사용할 수 있다.
위의 장점도 있지만 나의 경험에서는 Expo SDK를 사용하는 경우 제한된 접근으로 인해 몇몇의 패키지를 사용하지 못하는 경우가 있었다. 따라서 관련 패키지를 검색할 때는 반드시 Expo를 함께 검색어에 입력하여 찾아야 한다. 그렇지 않으면 패키지를 다운받고 예시 코드를 열심히 읽어 구현한 후, 에러 메시지가 떠서 구글링하면 "Expo는 지원하지 않는다"는 친절한 후기들을 확인할 수 있다....
필자는 사용자 정보를 로컬 디바이스에 저장하기 위해 Expo에서 지원하는 AsyncStorage를 사용하였다. 해당 API는 key-value 데이터를 웹의 Local Storage와 같이 저장할 수 있게 해주며, 본래 React Native에서도 지원하였으나 현재는 지원이 종료되어 커뮤니티 패키지를 사용해야 하나 Expo에서는 공식으로 지원해주고 있다. 사용하는 방법도 아래와 같이 Local Storage와 유사하다.
import AsyncStorage from "@react-native-async-storage/async-storage";
export const saveStorageData = async (key, value) => {
try {
const stringValue = JSON.stringify(value);
await AsyncStorage.setItem(key, stringValue);
} catch (e) {
console.error(e.message);
}
};
export const getStorageData = async (key) => {
try {
const value = await AsyncStorage.getItem(key);
if (value !== null) {
const data = JSON.parse(value);
return data;
}
} catch (e) {
console.log(e.message);
}
};
export const removeStorageData = async (key) => {
try {
await AsyncStorage.removeItem(key);
} catch (e) {
console.error(e.message);
}
};
본 프로젝트에서 웬만하면 백엔드 개발을 하지 않으려 했지만 일기 데이터를 저장하고 날짜 및 기술 별로 가져오기 위해서는 데이터를 표 형태로 저장하는 SQL DB가 필요했다. 따라서 Expo에서 제공하는 expo-sqlite 패키지를 사용하여 이를 비교적 간단하게(필자에게는 간단하지 않았다. DB를 직접 구현하다니... 장족의 발전이다) 구현할 수 있었다.
import * as SQLite from "expo-sqlite";
export const db = SQLite.openDatabase("MainDB"); // returns Database object
export const getDiaryById = (id, handleSuccess = printResult) => {
try {
db.transaction((tx) => {
tx.executeSql(
`SELECT ${TB.ALL} FROM ${TB_NAME} WHERE ${TB.ID} = ?`,
[id],
handleSuccess,
handleError
);
});
} catch (e) {
console.log(e);
}
};
export const saveNewDiary = (data, handleSuccess = printResult) => {
try {
db.transaction((tx) => {
tx.executeSql(
`INSERT INTO ${TB_NAME} (${TB.DATE}, ${TB.DIARY_CAT}, ${TB.TECH_CAT}, ${TB.TITLE}, ${TB.CONTENT}) values (?, ?, ?, ?, ?)`,
[
data.date,
data.diaryCategory,
data.techCategory,
data.title,
data.content,
],
handleSuccess,
handleError
);
});
} catch (e) {
console.log(e);
}
};
사실 이 출시기에는 아주 슬픈 이야기가 포함되어 있다. 바로 앱을 처음 개발해본 필자가 Firebase 구글 소셜 로그인을 구현하려다가 앱의 key를 변경하여 잃어버렸기 때문이다. 로그인이 필요했던 이유는 1. 앱을 삭제하면 일기 데이터가 삭제된다는 문제점을 해결하고 2. 추후 친구 기능을 추가해 사용자 유입 및 사용 시간을 증대하기 위해서이다.
해당 기능이 구현되지 않아 key store의 키를 마구 생성하다 key가 아예 변경되어 버리는 불상사가 일어났고, 사실 이 문제는 구글에 문의해 해결 가능하지만 설치 사용자수가 미미하고 앱의 패키지명 또한 변경해야 했기 때문에 앱을 재출시 하는 것으로 결정하였다.
이 문제에 대해 설명한 글에 따르면 Firebase의 소셜 로그인은 팝업창이 뜨는 signInWithPopup
메소드를 사용하는데 해당 로직이 브라우저에서 실행되는 것으로 구현되어 React Native를 사용하면 문제가 발생한다고 한다. 따라서 Expo에서 지원하는 구글 로그인 패키지를 사용하여 계정 정보를 받아오고 해당 정보를 Firebase로 보내주는 방식을 채택하게 되는데, 많은 개발자들이 이 과정에서 iOS에서는 작동하지만 Android에서는 버그가 발생한다고 이야기하고 있다. 많은 검색을 통해 비교적 최근 해당 부분을 구현한 다른 개발자 분의 글을 발견하였고 앱의 slug, pacakge name, scheme을 일치시켜야 한다는 것을 알게되었다.
Expo에서는 OS에 따라 패키지 명에 _
나-
를 지원하지 않는 경우가 있었는데 필자는 Expo 사용자명에 _
가 포함되어 있어 자동생성된 패키지명들이 불일치하고 있었다. 따라서 이 부분을 해결하기 위해 패키지명을 고쳐 앱을 재출시하게 되었다. 그러나 필자의 경우 development 단계에서는 구현이 되지만 production 모드에서는 계속 작동이 되지 않았고 커뮤니티에도 유사한 경험들이 많이 존재했다.
React Native 특히 Expo의 패키지를 사용할 때 이러한 자잘한 오류들이 많이 존재하지만 커뮤니티가 작아 해결되지 않고 남아있는 경우가 종종 있었다. 필자는 결국 소셜 로그인 대신 아이디-비밀번호 로그인 방식으로 선회하여 서버 데이터 동기화 기능을 추가할 수 있었다. Expo 계정을 만들 때 언더바를 사용하지 말라는 경고는 없었으며... 소셜 로그인 기능을 구현하다 성격을 버릴 것 같아 차선책을 선택하였다
React Native의 가장 큰 장점은 JavaScript 언어만으로 Android, iOS, Web 등 여러 플랫폼으로 앱을 빌드할 수 있다는 것이다. 필자는 MacBook을 사용하여 개발하고 있으므로 시뮬레이터 활용이 쉬운 iOS로 개발을 진행하였고, 실기기 검증은 안드로이드 스마트폰을 사용하였다. 따라서 두 운영체제 모두 앱 출시가 가능했으나 2022 년에는 Google Play Store에만 앱을 출시하기로 결정하였다.
첫 번째 이유는 개발 모드와 프로덕션 모드에서의 기능 작동 차이(#2.2.2)를 겪은 후 아이폰에서 검증 후 출시가 필요하다고 느꼈기 때문이다. 필자는 테스트용 iOS 실기기가 존재하지 않았으므로 2023 년에 아이폰으로 기기를 변경하면 앱스토어 출시를 도전해볼 계획이다. 절대 새로운 스마트폰을 구매하려는 수작이 아니다. 두 번째 이유는 앱스토어는 앱 출시가 까다롭고 매년 99 달러를 지불해야 하기 때문이다. Google Play Store의 경우 최초 25 달러를 지불하면 개발자 등록이 완료되지만 앱스토어는 매년 개발자 등록비를 지불해야 앱을 운영할 수 있다. 필자는 아직 웹 개발을 주로 하기 때문에 앞으로 앱을 출시할 일이 많아지거나 안드로이드에서 Post Black Belt 앱의 이용자 수가 많아진다면 iOS 개발자 등록을 하는 것으로 선택하였다.
많은 고난과 역경을 겪은 뒤 드디어 Google Play Store에 앱을 출시할 수 있었다. 앱을 올리고 구글에서 검토하는 기간이 일주일 가량 소요되었는데 매일 Google Play Console을 드나들며 검토중
이 바뀌기를 기다리고 있었던 것 같다. 앱을 재출시하면서 2022 년 내에 프로젝트를 마무리짓지 못하겠다는 생각도 있었는데 다행히 구글에서 크리스마스 연휴가 끝난 뒤 열일해주어 2022 년 12 월 30 일에 웹 개발자의 앱 출시기를 마무리할 수 있었다.
필자가 만들고 싶은 프로젝트를 직접 구현해보며 정말 많은 경험을 했다. 그동안의 프로젝트는 클론 코딩이나 단발성으로만 사용하는 웹 사이트를 제작했었는데, 이번 프로젝트를 통해 실사용자에게 필요한 기능들을 생각해보고 구현해보는 것에 큰 성취감을 느꼈다. 첫 개인 프로젝트라 현 버전 1.1 에서는 의욕이 앞서 나열한 기능의 1/4 정도만 최종 구현된 것 같다. 앞으로는 테스트 코드를 적용하고 코드 리팩토링 후 기능을 더 붙여갈 예정이다.
또한 새로운 라이브러리인 React Native를 배우면서도 많은 것을 깨달았다. API와 패키지를 동영상 강의 없이 공식 문서를 한 줄 한 줄 읽어가며 직접 적용해보며 공식 문서의 중요성을 몸소 깨닫게 되었다. 또한 Expo를 사용하면서 개발 편의성을 위해 라이브러리/프레임워크를 도입하는 것의 장단점도 느꼈다. 그 중에서도 가장 크게 와닿은 점은 커뮤니티가 큰 라이브러리는 개발자에게 축복이라는 것이다. 따봉 React야 고맙다..
아직까지 앱의 실사용자는 필자뿐이다. 관원들과 지인들에게 앱을 소개해주고 있지만 아직 보완할 점이 많다고 생각해 홍보를 본격적으로 하지 않고 있었다. 이제 새해가 되었으니 새로운 마음으로 앱을 추천해주고 실사용자들에게 보완점을 직접 받아보려 한다. 실제로 필자는 주짓수 수련을 기록하면서 기술 실력이 많이 늘었으며 이번 달 한 그랄을 승급하였다! 🎉
이제 글의 제목과 썸네일에 적은 구글에서 온 국제 전화 후기를 이야기해보려 한다. 두 번째 앱이 출시되고 며칠이 되지 않아 구글에서 아래와 같은 메일을 보냈다. 메일의 내용은 2 영업일 이후에 전화를 할테니 준비를 해두라는 것이었다.
상세히 알아보니 2022 년 6 월부터 구글은 선별된 개발자들에게 앱에 관해 질문을 한다고 하며, Google Play 사용자의 안전을 위한 목적이라고 한다. 필자의 경우 동일한 앱을 2 번 출시하였으므로 해당 부분을 문제 삼을 것으로 예상하였고 당연히(..?) 구글 코리아에서 전화가 올 것으로 생각하여 크게 개의치 않았다.
그리고 메일이 온지 3 일 후 오전 10 시에 국제전화를 받게 된다.... Hello...? 핑계로 들릴 수는 있으나 필자는 정확히 오전 10 시부터 팀 미팅이 있었기에 빠르게 일을 해결하려는 생각으로 전화를 받았고 갑자기 쏟아지는 영어에 굉장히 당황하였다. 이름이 무엇이냐는 물음도 3 번 되묻고 알아들었다. 질문자분도 내 발음을 잘 못알아듣는 것 같았다.. 질문 내용은 아래와 같다.
- 이름 > 본명을 대답
- 출시한 앱 이름 > Post Black Belt
- 앱의 목적 > Diray for jiu-jitsu
- 사실 주짓수 수련자라고 말하고 싶었는데 athlete밖에 떠오르지 않았다..- 앱이 수익을 어떻게 창출하고 있는지? > 현재는 수익 창출 계획이 없으며 나와 친구들을 위해 제작한 앱이다.
- 이때부터 조금 정신을 차려 문장을 구사하였다.
필자의 경우 잘 들리지 않는 리스닝과 짧은 스피킹으로 대화를 하였으나 이 글을 읽고 구글의 전화를 받는 개발자 분들은 미리 준비하여 잘 대답하시기를 바란다. 동일 앱 재출시에 관한 질문은 없었으며 나의 앱에 대한 정보만 간단히 묻고 종료되었다.
전화를 받고 나니 캠블리 구독을 취소했던 지난날에 대한 후회가 극심하게 밀려왔다. 사실 한국어로도 앱을 소개해본 적이 없어 더 말이 안나왔던 것 같다. 돌이켜 생각해보면 필자가 웹 프론트엔드 개발에만 국한되어 있었다면 구글 플레이 스토어의 전화를 받을 기회도 없었을 것이라 생각한다. 구글의 전화는 2023 년을 시작하는 필자에게 좋은 개발 원동력이 되었다. 또한, 최근 취업을 준비하며 고민이 많은 상태인 필자에게 미래의 개발자 커리어에 대해 생각해보는 큰 자극제가 되었다.
앞으로 더 좋은 웹, 앱 개발을 통해 더 나은 회고록을 작성할 예정이다. 2023 년도 해피 코딩을 할 수 있기를!