최근 그라운드 플립, 땅따먹기 만보기 서비스를 배포하고 실제 사용자들을 대상으로 운영하면서 많은 것을 배우게 되었다. 이전에는 실제 사용자를 염두에 두지 않은 프로젝트를 주로 진행했지만, 이번에는 사용자들이 직접 사용하는 서비스를 운영하며 새로운 경험을 하게 되었다.
이 과정에서 이전에는 미처 고려하지 못했던 다양한 문제들과 상황을 마주하게 되었고, 이러한 경험을 나 혼자만 알고 넘기기보다는, 프로젝트 배포를 준비하는 많은 분들과 공유하고자 이 글을 쓰게 되었다.
포스팅에 앞서 운영중인 서비스 그라운드 플립은 스토어에서 다운 받을 수 있습니다!! 🔥🔥
써보시고 피드백 남겨주시면 너무 감사할 것 같습니다!!🙏🙏
많은 관심 부탁드립니다!
이번 프로젝트에서는 특이하게 백엔드와 모바일 모두 개발에 참여하였기 때문에 모든 분야에서 느낀점을 각 항목별로 공유하고자 한다.
먼저 백엔드의 관점에서는 어떤 점을 고려하면 좋을지 알아보자.
우선 무엇보다 안정적인 서비스가 중요했다. 아무리 기능 좋은 서비스라도 수시로 접속이 안되거나 장애를 발생하면 사용자 경험에 매우 안좋은 경험을 줄 수 있다고 생각했다. 안정성을 위해 고려했던 점을 공유한다.
좋은 사용성을 위해 무중단 배포의 도입을 고려해보자. 우리 팀은 기존에 배포할 경우 원래 돌아가던 인스턴스를 중지시키고 새로 배포할 인스턴스를 띄우는 방식을 사용했었다. 이렇게 되면 새로운 인스턴스가 올라가는 동안 다운 타임이 발생해 서버를 사용할 수 없다.
다운 타임이란? 서버가 작동하지 않거나 서비스에 적속할 수 없는 시간을 의미한다. 다운 타임은 서비스 이용에 큰 영향을 미치기에 최소화하는 것이 좋다.
무중단 배포를 적용한다면 다운 타임이 발생하지 않아 배포하는 동안에도 서버가 요청을 처리 할 수 있어 사용성이 좋아진다. 만약 돈과 연관되어있다면 다운 타임 동안 요청을 처리하지 못하는 것은 꽤나 치명적일 수도 있기에 무중단 배포를 고려해보는 것은 어떨까?
무중단 배포를 구현하는 여러 방법이 있겠지만 우리팀은 위와 같이 nginx를 사용하여 Blue Green 방식을 사용했다. 새로운 인스턴스가 실행되는 동안에는 기존 인스턴스를 사용하고 새로운 인스턴스가 실행 완료된 순간 새로운 인스턴스를 사용하는 방식이다.
Blue Green 외에도 Rolling, Canary 등 여러 방법에 있으니 프로젝트에 맞는 방법을 찾아 구현하는 것을 추천한다.
실제 사용자들이 서비스를 사용하면서 트래픽이 예측할 수 없을 정도로 변동되는 경우가 많다. 이럴때를 대비해 오토 스케일링을 적용해둔다면 안정성이 크게 올라간다. 물론 아직 우리 서비스가 엄청난 트래픽이 몰리지는 않지만 혹시 모를 트래픽을 대비해서 최소한의 대비로 적용해두었다.
우리팀은 cpu 사용률이 50% 이상이 되면 자동으로 서버의 인스턴스가 증가하도록 설정해두었다. 물론 사용률이 내려갔을 때도 증가된 상태를 유지시키는 것은 비용낭비이기 때문에 서버의 부하가 줄어들면 인스턴스도 줄어들도록 설정했다.
서비스를 일시 중단 해야하는 경우도 있을 수도 있다. 예를 들어 데이터 베이스의 데이터를 집계하여 새로운 데이터를 만들어 서비스해야하는 경우를 보자. 집계하는 동안 새로운 요청이 들어와 데이터가 바뀐다면 데이터의 무결성이 깨질 수 있다. 때문에 서비스를 일시 중단하여 데이터가 바뀌지 못하도록 해야한다.
이럴때 아무 예고 없이 점검하기 보다는 점검하기전에 미리 사용자들에게 메일이나 공지사항을 통해 알리는 것이 사용자들을 위한 방법이라고 생각한다.
실제 우리 서비스도 성능 최적화를 위해 기존 데이터들을 미리 집계하여 캐시 해두거나 데이터 베이스의 컬럼을 변경하기 위해 사용자 들에게 서버 점검을 알리고 많이 사용하지 않는 시간에 작업을 수행했다.
동시성 문제도 고려해야했다. 사실 동시성 문제는 말로만 듣고 우리 서비스에도 생길줄은 몰랐지만 생기게 되었다. 동시성 문제를 해결한 내용은
실제 사용자들의 정보를 받고 서비스하는 만큼 보안도 중요했다. 보안적으로 신경 쓴 부분에 대해서 소개한다.
HTTP 프로토콜을 사용하면 암호화가 되지 않아 중요한 데이터를 전송할 때 치명적이다. 기존에 사용자들을 염두하지 않고 프로젝트를 할 때는 크게 신경쓰지 않았던 부분이지만 실 사용자들을 받는 만큼 암호화가 중요했다. 로그인 정보나 민감한 정보를 노출 시키지 않기위해서는 HTTPS는 필수다.
HTTP를 적용하는 방법도 여러개 있지만 우리팀은 aws 의 라우트 53을 사용했다. HTTPS 를 적용하기위해서는 도메인이 필수로 필요하기 때문에 참고하자.
라우트 53 외에도 nginx 를 사용할 수도 있고 스프링 내부에서 적용할 수도 있으니 상황에 맞는 방법을 사용하면 될 것 같다.
사용자 정보를 수정, 삭제 하는 등의 작업에도 신경써야한다. 아무 요청이나 수정 할 수 있다면 악성 사용자가 악의 적으로 데이터를 수정할 가능성이있다.
우리 팀은 요청을 수행할 때 보낸 사람의 jwt를 확인하여 본인의 자원만 수정하도록 구현하였다.
데이터 베이스의 보안도 중요하다. 데이터 베이스의 데이터를 기반으로 서비스가 이루어지는 만큼 아무나 접근하여 읽고 수정하고 삭제 해서는 안된다.
데이터 베이스의 접속 비밀번호를 간단하게 설정하거나 public으로 두어 어디서든 접근 가능하게 하면 쉽게 해킹당한다. 실제로 옛날에 연습삼아 간단한 비밀번호로 설정하고 public으로 열어두었을때 브루트포스 공격으로 인해 해킹당한적이있다.
I have backed up all your databases. To recover them you must pay 0.01 BTC (Bitcoin) to this address: 13eWyQW4YB6ecJjZasXyVNH6eAns5ogjto . Backup List: village. After your payment email me at sqlrecover471@onionmail.org with your server IP (27.96.131.159) and transaction ID and you will get a downloadlink to your backup. Emails with out transaction ID and server IP will be ignored.
해킹당한 데이터 베이스를 확인했을 때는 이미 데이터는 지워져있었고 비트코인을 보내라는 메시지만 남아있었다… 😢
절대 해킹 당하지 않을 것이라고 안심하지말자. 언제든 뚫릴 수 있다!
우리 팀은 DB 서버는 aws VPC 의 private 서브넷에 위치시켜 외부에서는 접근 불가능하도록 설정해두었다. DB에 접근하려면 반드시 VPC 내부의 서버에서만 접속 가능하도록 했다. DB 터널링 이라는 것을 찾아보면 좀 더 편리하게 DB에 접근할 수 있다.
이 부분은 실제 배포하고 나서야 깨달은 부분이다. API 설계를 꼼꼼히 해야 한다! 운영 중인 API의 스펙을 수정하는 것은 생각보다 어렵다. 이미 배포된 API를 사용하는 클라이언트들이 있기 때문에, 새로운 API로 바꾸면 기존 사용자는 업데이트를 하지 않는 이상 사용할 수 없게 된다.
예를 들면,
POST /api/user
Body :
{
"user_name",
"email",
}
이렇게 설계했지만, 나중에 사용자를 등록할 때 나이와 주소를 함께 받아야 한다는 요구사항이 생길 수 있다.
이 경우 기존 API를 사용하는 Client를 위해 기존 API를 그대로 유지하면서 수정된 버전을 새로 운영해야 하므로, 관리 비용이 증가한다. 따라서 API를 설계할 때는 신중하게 확장 가능성을 고려하는 것이 정말 중요하다고 느꼈다.
모바일 부분에서도 연습을 위해 프로젝트 할 때와 달리 신경써야할 부분이 많았다.
우선 이용약관이 필요하다. 실제 사용들이 사용하는 만큼 서비스에 문제가 생길 때 처리할 규칙이 필요하다. 사용자가 우리 서비스틀 통해 피해를 입거나, 분쟁이 발생하는 경우 등이 있을 경우를 대비해 반드시 필요한 부분이다.
실제 앱 스토어의 경우 앱을 올릴 때 반드시 약관을 확인 할 수 있는 URL 을 올리도록 되어있다.
만약 GPS 등을 이용해 위치 정보를 기반으로 한다면 ‘위치기반 서비스 이용약관’ 을 작성하는 등의 추가 사항이있다. 관심있다면 https://www.lbsc.kr/front/content/contentViewer.do?contentId=CONTENT_0000061 를 참고하자.
우리 서비스 같은 경우에도 회원가입시 반드시 동의해야만 서비스 사용을 할 수 있게 하였고 노션 페이지도 약관을 만들어 설정 창에서 언제나 확인할 수 있게 하였다.
다음으로 앱에서 접근 가능한 권한들에 대해 동의 하는 절차도 필요하다. 모바일은 기기는 사용자와 밀접한 만큼 민감한 정보를 많이 가지고 있기 때문에 해당 정보를 사용할 수 있도록 적절한 권한을 동의 받아야하는 것이 필수다.
실제 앱스토어 제출을 위해서도 어떤 데이터를 사용하는지 명시해야하고 iOS 의 경우에는 info.plist 에 명시 해주어야한다.
우리 서비스는 앱을 시작할 때 동의를 받고 각 항목에 대해 허용창이 나오게 하였다. 사소한 부분 같지만 신경쓰지 않으면 스토어 심사에 통과 받을 수 없기 때문에 주의해야한다.
모바일 환경은 웹과 달리 기능을 변경하려면 스토어에서 업데이트를 해야한다. 업데이트 사항을 개발하여도 바로 배포할 수 있는 것이 아니라 또 심사를 받아야하기 때문에 신중해야한다. 보통 iOS는 몇시간 ~ 1일, 안드로이드는 2~3일 걸리는 것 같다.
모든 사용자들이 자동 업데이트 기능을 켜두지는 않는다. 때문에 사용자들에게 업데이트를 해야한다는 것을 앱 내부에서 알릴 필요도 있다. 우리팀의 경우는 앱이 실행될 때 서버에 현재 앱 버전을 전송하여 업데이트 여부를 받아와 업데이트 해야한다면 모달 창으로 알리도록 구현했다.
또한 필수 업데이트와 권장 업데이트를 구분 했다. 이전 버전으로는 더 이상 서비스 이용이 불가능 한 경우와 이전 버전으로도 충분히 사용할 수 있는 두 가지 경우로 나누었다. 필수 업데이트인 경우에는 업데이트를 해야만 서비스 이용을 할 수 있도록 하였다.
버전 관리는 직접구현하지 않아도 파이어 베이스의 Remote Config 를 사용하여 쉽게 구현할 수도 있다.
웹뷰를 사용하는 등의 방식으로 스토어가 아닌 방식으로 개발자들이 업데이트 할 수도 있도록 구현하였다면 이 부분은 해당되지 않을 수 있다.
서비스를 운영하다보면 버그가 발생해 버그를 수정하거나 새로운 기능을 추가 할 때가 있다. 이럴 때 기존에 이미 설치한 사용자들도 고려해야한다.
예를 들자면 위치 권한을 필요로 하는 기능을 추가한다고 가정하자. 보통의 경우에는 권한을 앱을 첫 실행하거나 회원가입 할 때 동의를 받도록 구현 할 것이다. 이렇게 구현하면 기능을 추가한 후 새로 가입한 사용자들에게는 권한을 받을 수 있다. 하지만 기존에 이미 설치를 하고 회원가입 까지 다한 사용자들은 동의 할 수 없다. 따라서 기존에 앱을 이용하던 사람들도 새로 업데이트 하면 동의 할 수 있도록 해야한다.
우리 앱의 경우는 새로 권한을 받아야하는 경우 기존 사용자는 main 페이지에 들어갈 때 모달 창을 띄우도록 했다.
결론은 새로 설치할 사용자와 기존에 설치한 사용자 모두 새로운 기능을 사용하고 권한을 동의할 수 있게 개발해야한다!
많은 서비스들이 편리한 사용자 경험을 위해 소셜 로그인 기능을 도입한다. 이때 iOS 로 배포하기 위해서는 반드시 애플 로그인을 추가해야한다는 점이다.
앱스토어 심사 통과를 위해서는 심사를 위한 로그인 정보를 입력해야한다. 이때 소셜로그인으로만 로그인 하도록 구현했다면 애플 로그인을 추가하여 심사를 통과할 수 있다. 아이디, 비밀번호로도 로그인 가능하다면 테스트용 아이디와 비밀 번호를 입력하는 것으로 대체 할 수 있는 것 같다.
애플 로그인을 구현했다면 회원탈퇴도 구현해두어야한다. 이때 서비스 db에서 회원 정보를 지우는 것 뿐만아니라 애플 서버와의 연결도 끊어야한다. 이와 관련해서는 애플 회원탈퇴까지 구현해서 앱스토어 심사통과하기 에 과정을 자세히 써두었다.
개발은 새로운 기능을 만드는데 중점을 두었다면 운영에 있어서는 서비스를 안정적이고 효율적으로 관리하는 것에 집중해야했다. 이는 개발과는 또 다른 영역이었다. 운영하는데 있어서 고려했던 점을 소개한다.
팀원들이 일관적으로 운영할 수 있도록 체계화된 매뉴얼을 만들었다. 매뉴얼에 문서화한 내용은 다음과 같다.
크게 이런식으로 정하였고 만들지 않았을때에 비해서 팀에 체계가 잡힌것을 느꼈다.
위에서 말한 것 처럼 안정적인 운영을 위해 모니터링은 필수였다. 백엔드, 모바일 모두 모니터링하기 위해 여러 툴을 도입했다.
aws 를 사용하고 있기 때문에 간단하게 클라우드 워치를 이용해 모니터링하도록 하였다. 이전에 우리가 모르는 사이 서버가 다운 되어 서비스가 장애가 난적이 있었다. 이때 우리 팀은 모니터링의 중요성을 느끼고 적용했다.
에 자세히 기록 해두었다.
사용자들의 앱 사용패턴과 사용량을 확인하기 위해 Google Analytics 를 사용해 모니터링 할 수 있게 하였다. Google Analytics는 다양한 지표를 제공하는데
등과 같이 다양한 지표를 제공한다. 파이어 베이스를 사용하여 비교적 쉽게 모니터링 툴을 적용할 수 있기 때문에 꼭 달아 보는 것을 추천한다.
우리 팀은 그동안 Git Flow를 사용하여 개발을 진행해왔다. 출시 전에는 개발한 내용을 모두 develop 브랜치에 합친 후, 출시 시점에 develop 브랜치를 main에 머지하여 배포하는 방식이었다.
그러나 새로운 기능을 출시하기 위해 develop 브랜치에 내용을 합치려다 보니 문제가 발생했다. 기존 버전에 문제가 생기거나 보완해야 할 점이 있을 경우, develop 브랜치에 미완성된 기능이 포함된 채로 배포될 위험이 있었기 때문이다.
예를 들어, 그룹 기능을 새로 개발한다고 가정해보자.
• 그룹 가입 기능
• 그룹 탈퇴 기능
• 그룹 조회 기능
이 세 가지 기능을 모두 개발해야 하는데, 그룹 가입 기능만 완료된 상태에서 이를 develop 브랜치에 머지했다고 하자. 그런데 나머지 두 기능이 개발되기 전에 기존의 랭킹 기능에 보완할 점이 생겨, 이를 develop 브랜치에 머지하고 배포를 진행했다면, 아직 완성되지 않은 그룹 기능까지 배포되는 문제가 발생할 수 있다.
이런 상황을 방지하려면 기능별로 별도의 브랜치를 유지하거나, 미완성된 기능이 배포되지 않도록 주의가 필요하다는 것을 느꼈다.
배포 전 꼼꼼한 테스트도 필수다. 테스트 코드도 필수지만 실제 앱을 다양한 기기에 깔아보고 실제 상황을 테스트하는 것도 중요했다.
실제로 우리 팀이 겪은 몇가지 경우를 소개하자면 우선 백그라운드 기능이 에뮬레이터에서는 정상적으로 돌아가는데 실제 기기에서는 권한 문제 때문에 돌아가지 않는 경우도 있었다.
또한 실제 기기에서 어르신 분들이 눈이 잘 안보여서 글자 크기나 화면 크기를 크게 해두시는 경우가 꽤 많은데 이런 경우 앱의 글자도 같이 커져서 화면이 깨지는 문제도 있었다.
이렇듯 최대한 버그를 잡기 위해 배포전 다양한 상황으로 테스트하는 것이 중요하다는 것을 느꼈다. 우리 팀은 배포 전 테스트 할 사항을 체크 리스트로 만들어 모든 사항을 한번씩 테스트 해보고 배포한다.
물론 테스트 코드도 중요하다. 우리 팀은 테스트 커버리지를 70%로 유지하고 있는데, 이 덕분에 기능을 추가할 때 예상치 못한 버그가 발생하는 일이 크게 줄었다. 이전에는 테스트 코드를 작성하지 않았을 때 프론트엔드 팀에서 “서버가 작동하지 않는다”는 피드백을 자주 받았지만, 이제는 그런 일이 거의 발생하지 않아 프로젝트의 안정성이 크게 향상되었다.
요약하자면
이번 프로젝트를 통해 실제 사용자가 있는 서비스를 운영하며 많은 것을 배울 수 있었다. 단순히 기능을 개발하는 것뿐만 아니라, 안정성, 보안, 사용자 경험, 운영 등 다양한 측면에서 고려해야 할 부분이 많았다.
서비스를 개발하고 운영하는 것은 결코 쉬운 일이 아니지만, 이러한 과정을 통해 더 나은 사용자 경험을 제공하고 안정적인 서비스를 운영할 수 있는 능력을 키우는 중요한 기회라고 느껴졌다.
프로젝트를 완성한 분들이라면 반드시 실 사용자를 모집해보는 것을 추천한다. 개발할 때와는 또다른 경험을 분명할 수 있을 것이다.
궁금한 부분이나 운영 할 때 겪은 다른 사례가 있으면 댓글로 남겨주시면 감사하겠습니다!
앱을 실제로 런칭하기 위해서는 생각보다 신경써야 할 부분이 많은 것 같네요!
앱 런칭 준비중인 입장에서 많은 도움 받고 갑니다!!