이호준 멘토님의 우당탕탕 멘토링 5 - 푸시 알람 편

멘토님께서 숙제로 내주신 것 중 하나로 당근마켓 푸시 알림을 구현해보라는 것이 있었다.

첫 번째 트라이에서는 단순히 푸시알람만 구현해 갔다가 .. 야무지게 욕을 먹었다.

이제는 다시 그들만의 핵심 비즈니스 로직을 파악해서 가져가야 하는 상황이다.

그래서 이번에는 당근마켓처럼 내 주변에 있는 사람이 게시글을 등록하면 그것을 감지해 내가 푸시 알람을 받도록 하는 것을 구현해보려 한다.


목표를 잡고 자료조사를 시작하면서 처음에는 한 이틀 정도 삽질을 했다. 푸시 알림의 원리를 파악하지 않고 '이거랑 이거 그리고 이거 공부하면 내 생각대로 잘 구현되겠지...' 하면서 엉뚱한 백그라운드 프로세스만 죽어라 공부했다. 도저히 이것만 공부해서는 구현까지 이어나갈 수 없을 것 같아서 앱의 라이프 사이클 및 푸시 알람의 원리에 대해 공부했고, 그것을 지금부터 공유해보고자 한다.


앱의 라이프 사이클

애플리케이션 자체의 생명주기라는 것은 총 5가지 상태를 갖는다.

그리고 그 5가지 상태는 꽤나 직관적으로 이해하기 쉬운 것들이다.


스크린샷 2020-10-21 오후 4 39 40

Not Running

첫 번째는 Not Running 상태로,

의미는 앱이 아직 실행되지 않은 상태. 또는 완전히 종료되어 동작하지 않는 상태. 이다.


Foreground - Inactive

두 번째는 Inactive 상태이다.

app이 실행중이더라도 사용자와 상호작용할 수 없는 상태를 말한다.

multitasking window에서 사용자가 다른 앱을 사용하고 있거나 app 실행 중 전화 등의 이유로 app을 사용할 수 없는 경우가 이에 해당한다.


Foreground - active

앱을 켜서 사용중인 상태이다.

참고로 앱이 실행되면 app은 Not running -> foreground-Inactive -> foreground-active의 과정을 거친다.


Background - Running

앱을 실행했다가 홈 버튼을 눌러서 나온 경우나 다른 앱으로 전환한 경우 background 상태에 있다고 함.

이 때 음악 앱처럼 백그라운드에서도 계속 동작하는 경우 background - running 상태에 있다고 한다.


Background - Suspended

앱은 background 상태에 진입했을 때 다른 작업을 하지 않으면 Suspended 상태로 진입한다.

이 상태에서 os는 앱을 다시 실행했을 때 빠르게 로드할 수 있게 관련 데이터만 메모리에 올려놓는다.

만약 메모리가 부족해진다면 os는 이들 앱부터 메모리에서 해제한다.


추가로 알아둘만한 점

iOS13 이상부터는 UI Life Cycle은 Scene Delegate가, Process Life Cycle은 App Delegate가 관리한다.

이전에는 둘을 합쳐 UIApplicationDelegate에서 관리했다고.


앱의 라이프 사이클과 관련된 함수들


image


앱 실행 시

앱이 실행될 때 앱은 Not running 상태에서 바로 foreground - Active 상태가 되는 것이 아니라,

Not Running -> Inactive -> Active 의 과정을 거친다.

이와 관련된 함수는

  • application(_:didFinishLaunchingWithOptions:)
    앱이 실행되고 앱을 화면에 띄우기 위한 모든 설정이 완료된 뒤에 호출된다. Storyboard를 사용한다면 storyboard에서 entry point를 찾아 내부적으로 UIWindow를 생성한다. 코드로 Window를 생성한다면 해당 함수 안에서 생성한다.
  • applicationDidBecomeActive(_:)
    앱이 Inactive에서 Active 상태로 전환되는 경우 호출된다.

앱이 백그라운드로 갈 때

앱 실행 중 홈 버튼을 눌러서 백그라운드로 가게 되는 상황에서는 다음과 같은 과정을 거친다.

Active -> Inactive -> Background (-> Background-suspended).

이런 과정을 거치면서 앱은 다음 메서드를 호출하게 된다.

  • applicationWillResignActive(_:)

    앱이 Active->Inactive로 갈 때 호출.

  • applicationDidEnterBackground(_:)

    앱이 Background 상태로 전환되었을 때 호출된다.


백그라운드에서 포어그라운드(Background to Foreground)로 올 때

이 경우에도 Inactive 상태를 거침.

Background -> foreground-Inactive -> foreground-Active 처럼.

이 과정에서는 다음 메서드가 호출된다:

  • applicationWillEnterForeground(_:)

    앱이 Background에서 Inactive로 전환될 때

  • applicationDidBecomeActive(_:)

    앱이 Inactive에서 Active 상태로 전환될 때


앱 종료시

앱이 어떤 과정을 거쳐서 종료가 되든 다음의 메서드를 호출한다:

  • applicationWillTerminate(_:)

    시스템이 오류로 종료될 때에는 호출되지 않는다!


앱 라이프 사이클에 따른 Scene 기준 메서드들은 아래 블로그 참조

https://velog.io/@rnfxl92/%EC%95%B1-%EC%83%9D%EB%AA%85%EC%A3%BC%EA%B8%B0-Application-Life-Cycle


푸시 알림(Push Notification)

푸시 알람은 위키백과 에 따르면 어떤 전송 요청이 중앙 서버에서 시작되는 정보 전달 방식 이다.

예를 들어 실시간 주식시장 정보나 뉴스기사 소식을 푸시해주는 것이 이에 해당한다.

반대되는 개념으로는 풀(pull)이 있는데, 이것은 클라이언트가 먼저 데이터를 얻기 위해 서버에 요청하여 데이터를 받는 것이다.

정보 취득을 통제하는 주체가 서버(광고주)이냐 클라이언트(사용자)이냐에 따라 푸시 풀이 나뉜다.


푸시를 보내는 원리

처음에는 푸시를 보내는 원리를 파악하지 못하고 단순히 어떻게 보내는지에 대해서만 파악을 했다가, 아~무리 구글링을 해도 내가 원하는 상황에 자동으로 특정 타겟에게 푸시를 보낼 방법을 모르겠어서 그 원리부터 다시 조사했다. 그 원리를 파악하자 내가 무엇이 문제였는지 바로 확인할 수 있었다.


image


사진은 푸시를 보내는 원리에 대해 간략히 순서도를 나타내고 있다. 하나씩 설명하자면

\1. 먼저 앱을 설치하고 실행하면 이 app은 푸시알림을 보내기 위한 device token을 포함해 해당 user의 정보를 서버로 보낸다.

\2. 서버에서는 받아온 데이터를 저장하고, FCM에 해당 기기에 대한 토큰을 저장한다.

\3. 당근마켓에서 어떤 상품을 등록하는 것처럼 사용자의 이벤트가 발생하여 서버로 보내진다.

\4. 해당 서버는 그 이벤트를 가지고 어떤 디바이스들에게 푸시 알림을 보낼지 필터링해서 FCM에 넘긴다.

\5. FCM은 미리 저장해놓은 기기 토큰 중에서 요청받은 토큰이 무엇인지 판단하고 APNs를 통해 각각의 기기에 푸시 알림을 보낸다.


그렇다면 우리에게 필요한 것은 APN과 FCM에 계정 등록이 되어야 하고,

푸시알림을 보낼 서버가 따로 있어야 하며,

앱에서는 device token을 뽑아 서버로 넘겨야 한다는 것.

(APN과 FCM 등록하는 것부터 오래 걸려서 전체적으로 짧진 않을 것이다. 등록이 오래 걸리는어떻게 알았냐구요? 저도 알고 싶지 않았습니다..)


APNs와 FCM이 어떻게 푸시알림을 처리하는지에 대해 좀더 자세히 알고 싶다면 다음 블로그와 사이트를 추천한다:

원리에 대해 자세히 설명한 블로그

원리에 대해 자세히 설명한 3p짜리 논문

android는 어떻게 보내는지에 대한 신문기사

Firebase->APNs로 넘기는 것에 대한 Firebase 공식 문서


문제점과 해결 방안(제목이 대학교 레포트같네)

여기까지 공부하고 내가 발견한 큰 문제점은 당근마켓처럼 사용자의 디바이스에서 발생하는 이벤트에 따라 푸시 알림을 보내는 경우 자체적으로 푸시 알림을 요청하는 푸시 알림 요청 서버가 있어야 한다는 것이었는데, 직접 서버를 따로 세워서 테스트를 진행하기에는 내가 구현할 수 있는 서버 기술에 한계가 있을 뿐더러 배보다 배꼽이 더 큰 상황이 벌어질 것 같다는 점이었다.

그래서 어차피 어떤 게시글이 등록되었을 때 위치 기반으로 그것이 내 주변에서 발생한 일이면 푸시 알림을 보내는 게 목적이었으니,

예를 들어 지점 A, B의 위도, 경도를 설정해서 지금 내 위치로부터 1km 안으로 하나를 설정(A)하고 1km 밖으로 하나를 설정(B)해서,

1km 를 기준으로 그 안에서 게시글을 등록했을 때에는 알림이 오게 만들고 해당 범위 밖에서 게시글을 등록했을 때에는 알림이 오지 않게 만들어보는 쪽으로 생각을 바꾸었다.


아이디어

아이디어는 이렇다. 아래 사진처럼 현재 내 위치로부터 1 km 안에 있는 Location1에서 게시글 등록이 이루어졌다고 한다면 푸시 알림이 오고, 반면에 1km 밖에 있는 Location2 의 경우 푸시 알림이 오지 않도록 해보려 한다.


image


이를 위해서 구체적으로 어떤 로직에 따라 구현을 할지 정해야 했는데, 나는 다음과 같은 그림처럼 한번 로직을 구성해보았다.


push_alrarm001


그리고 위 로직에 따라 최대한 구현하고, 여기에 더해 현재 내 위치로부터 몇 meter 안에 있는 게시물 등록 글만 받을건지 추가해보았다.

여기에 쓰인 기술은

  • 위치 정보 받아오기
  • iOS 에서 제공하는 Map Kit 지도 위에 내 위치 띄우기
  • 특정 위, 경도 좌표에 대한 마크 띄우기
  • 로컬 푸시 알림 보내기

정도였다.

그리고 그것을 구현한 결과는 다음과 같다.


먼저 다음 테스트의 경우 개포초등학교는 내 위치로부터 1km 안에 있어서 게시글이 등록되었을 때 알림이 뜨고,

반대로 구룡초등학교의 경우 1km 밖에 있기 때문에 알림이 뜨지 않는다.

pushAlarmTestWithMapKit


만약 범위를 2km로 늘린다면?

예상대로 두 지점 모두 게시글을 등록했을 때 내게 푸시 알림이 뜬다.


pushalramTestWithMapKit2


후기

오랜시간 자료들을 찾아보면서 푸시 서버를 따로 만들어야 한다는 결론에 도달했을 때에는 그동안 자료조사를 하고 쓸데없는 것들을 구현하느라 시간을 날린 것 같아서 스트레스였지만, 막상 테스트를 모두 끝내고 나니 왜 그렇게까지 스트레스를 받았나 싶다.

그리고 미들웨어(푸시 서버)를 따로 구현해서 실험하지 못해 아쉬움이 남았지만, 그래도 만약 당근마켓 푸시알림과 같이 유저들로부터 특정 이벤트가 일어났을 때 또다른 유저들에게 푸시 알림을 보내야 하는 경우 어떤 것들이 필요한지 감을 잡을 수 있게 되어서 개발 지식이 늘어난 느낌이 들었다 !


혹시나 기술적으로 로컬 푸시 알람이 궁금하신 분들은 여기 를 참고하시고,

iOS 에서 제공하는 Map Kit 지도 위에 내 위치 띄우기 및 특정 위, 경도 좌표에 대한 마크 띄우기 기술이 궁금하신 분들은 여기,

마지막으로 위치 정보를 얻어오고 관련 설정을 위한 정보를 얻고 싶은 분들은 여기 를 참고하시길 바라요오우우우


refs

  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기
// custom