안녕하세요. 드림어스컴퍼니 IOS개발팀 Kats입니다.
FLO에서 로그를 생성하기 위해 어떤 고민을 하고, 어떤 방식을 채택했는지에 대해 소개하겠습니다.
Chapter 1 : 개념 정의
FLO의 로그를 정의할때 가장 신경 쓴 부분은 모두에게 “쉬운” 로그를 만드는 것이었습니다.
쉬운 로그가 어떤 것인지 정의하기 위해 다음과 같이 구분을 했습니다.
| Action Name : 동일한 기능을 가진 항목에는 동일한 명칭을 부여한다.
화면에서 보는 것과 같이 “재생”기능을 하는 모든 버튼들에 대해서 “play”라는 명칭을 부여합니다.
어떤 화면에서든 “재생”기능을 하는 버튼은 “play”라는 명칭으로 로그를 생성합니다.
| Category Name : Section 혹은 작은 단위에 대한 구분이 가능해야한다.
사용자가 재생버튼을 눌러 이벤트를 발생했을 때 “좋아할만한 최신앨범“과 “오늘 발매 음악“ 중
어디서 재생버튼을 눌렀는지 알 수 없기 때문에 어느 위치에서 이벤트가 발생되었는지 구분이
가능해야 합니다. 따라서 영역을 구분할 수 있는 Section 단위의 명칭을 부여하고, Category
Name이라고 정의했습니다.
| Screen Name : 각 화면을 특정할 수 있는 명칭을 부여한다.
마지막으로 어떤 화면에서 이벤트가 발행되었는지를 구분할 수 있는 값도 필요합니다. 애초에
Category Name을 화면의 성격도 포함해서 생성하는 방법도 있지만, 이 경우 동일한 Section을 “홈”과 “둘러보기”에 동시에 노출하게 된다면, Code로 관리되는 하나의 Section에 두개의 Category Name을
등록하는 일이 발생합니다. 그래서 FLO에서는 화면 단위의 이름도 부여했습니다.
| Screen / Category / Action을 조합하여 하나의 Event를 생성한다.
각 단위의 명칭을 조합하면 “어떤 화면”, “어떤 영역“, “어떤 버튼”을 눌렀을 때 “어떤 로그”가 생성될 지
명확하게 예상할 수 있습니다. 아래와 같이 홈화면에서 ‘오늘 발매 음악’의 재생버튼을 눌렀을 때는
“HOME, NEW_ALBUM, PLAY“라는 로그가 생성됩니다.
로그를 생성하는 기준을 정했을 뿐이지만, 로그를 정의하는 생성자와, 적용하는 개발자, 분석하는 분석자 모두가 적재된 로그만으로 “어느 화면, 어느 영역, 어느 버튼”을 통해 발생된 로그인지를 별도의 Mapping Table 없이 유추할 수 있다는 것이 가장 큰 장점이 됩니다.
“로그를 사용하는 모두가 이해하기 쉬운 로그가 가장 좋은 로그다”
오글거리지만, 위와 같이 정의를 했습니다. 그리고 위의 설명에는 빠져있는 항목이 있습니다.
| 무엇을 재생했을까?
위에서 발생된 로그를 통해 “화면상에 재생버튼을 눌렀다.” 는 사실은 알 수 있지만, “무엇을
재생했는지?”에 대한 내용은 없습니다. 로그 생성 단계에서 Section안의 Cell의 Index를 포함하는
방법도 있겠지만, 데이터가 주기적으로 변경되는 항목에 대해서 Index를 표현해도 얻을 수 있는 건
없습니다. (노출대비 클릭률 정도는 알 수 있습니다.)
무엇을 재생했는지에 대한 답을 얻기위해선 쌓아야할 데이터가 꽤 많습니다.
TYPE : 앨범 재생인지?, 특정 음원을 재생했는지?, 아티스트의 인기곡을 재생했는지?
NAME : 재생된 아이템의 이름은 무엇인지?
ID : 재생된 아이템의 ID는 무엇인지?
ORDER : 재생된 아이템은 몇번째에 노출되었는지?
위와 같은 아이템의 정보도 담을 수 있고,
어떤 키워드로 검색을 해서 노출된 아이템인지?
에 대한 좀 더 상위의 정보도 담을 수 있어야 합니다.
그래서 각 로그를 발행할 때 함께 적재되는 Body를 추가했습니다. 좀 더 자세한 설명은 아래에서 이야기하겠습니다.
Chapter 2 : 개발 방법
정의된 개념을 적용하기 위해 설계를 하다보면 아래와 같은 상황을 마주합니다.
| 모든 화면은 Hierarchy로 이루어져있다.
빠른 이해를 위해 그림으로 보면 FLO의 홈의 구조는 다음과 같습니다.
UIViewController와 Collection Section에서 Screen Name, Category Name을 설정하면 그 하위 계층인 Cell에서 이 정보를 확인할 수 있는 방법을 제공해야 했습니다.
코드로 보면,
이렇게 계층 구조의 하위 뷰를 생성할 때 상위 정보를 내려주는 방법으로 제공을 해야합니다.
“그리고, 우리는 이런 방식을 노가다라고 합니다.“
| 개발과 유지보수 모두 쉬워야한다.
Screen, Category의 정보를 전달하기위해 Initializer에 값을 전달하는 건 너무 많은 작업량이 추가될 뿐
아니라, 이후 유지보수 단계에서도 지속적인 수정이 발생하고 유연하게 대처할 수 없습니다. (로그변경에 대한 요청이 온다면..) 이 부분에 대한 해결방법으로 이것을 사용하기로 했습니다.
“UIResponder”
화면을 구성하는 모든 UI는 UIResponder를 가지고 있습니다. 그리고 UIResponder는 Responder chain으로 연결되어 상위 UIResponder를 획득할 수 있습니다.
이 방법을 활용하여, 이벤트가 발생할 때 하위 뷰에서 상위 뷰로 역탐색하는 방식으로 정보를 획득합니다.
이 방식대로 코드를 생성하면 다음과 같습니다.
더이상 하위 뷰는 상위 뷰의 정보를 알 필요가 없어졌습니다.
이 방식의 또 다른 장점은 A화면의 CollectionViewCell을 B화면에서 재활용해도 로그를 위한 수정을 하지 않아도 된다는 점이 있습니다. 로그를 생성할 때 상위 뷰를 탐색하여 Screen Name이 B로 발송됩니다.
| UIResponder.next로 무엇을 찾아야할까?
상위 뷰를 탐색하기 위해 UIResponder를 사용하는 것까진 좋았으나, 정보를 획득할 수 있는 기준점이
필요합니다.
“AssociatedObject - Swift“
AssociatedObject를 통해 모든 UIView에 다음과 같은 변수를 주입하고
상황에 따라 ScreenName, categoryName, bodyItem을 등록하면, 상위뷰로 탐색하며 데이터를 획득하는 구조로 만듭니다.
bodyItem에는 “검색 키워드”와 같은 공통 데이터를 입력해놓고, 탐색과정에서 획득할 수 있도록 합니다.
Chapter 3 : 로그 확인
발송한 로그에 대한 확인을 위해서는 한 가지 과제가 선행되어야 합니다.
| 뷰어 제공은 선택이 아니라 의무!
물론 Client에서 발송한 로그는 Server 어딘가에 Data형태로 적재가 되고, DB에 등록되어 Query를 통해서 조회가 가능합니다. 이 Data를 토대로 로그를 분석할 수 있는 있습니다.
하지만 로그 생성자 / 검수자의 경우 어떤 Action을 통해 발행된 로그가 정상적으로 서버로 보내졌는지, 발행된 순서대로 전송이 되었는지, 오탈자는 없는지 등의 “검수”를 진행하는 단계에서 일일이 DB를 조회하여 보는 것은 매우 비생산적인 일이 됩니다. 어떤 로그가 생성되었는지에 대해 예상을 해야 하고, 발행된 로그에 대한 모든 순서를 기억해서 DB와 비교를 해야 검증이 가능합니다.
그래서 FLO에서는 지금 한 행위에 대해 발행된 로그를 확인할 수 있도록 앱 내에서 뷰어를 제공합니다.
이렇게 앱 내에서 뷰어를 제공하여 로그 생성자가 이벤트 발행 즉시 확인이 가능하도록 합니다.
그리고, 이 과정에서 로그의 퀄리티가 상승하게 됩니다. (오기입된 로그가 어느 정도 체크됩니다.)
마치며,
FLO에 적용된 로그 방식은 현재의 트랜드인 MVVM Architecture에는 적합하지 않습니다. UIResponder를 사용하면서 많은 부분이 View Level에서 처리가 되기 때문입니다. ViewModel에서 상위 Model의 정보를 획득하는 것도 가능하지만, 로그 제작에서 가장 중점을 둔 부분이 “노가다 코드에 대한 지양”이었기 때문에, 이렇게 만들어봤습니다.
감사합니다.
로그에 대한 설계가 쉽지않았다는게 생생하게 느껴집니다.
좋은 아이디어네요 ^^