Events/Tech Onboarding

[2022 신입 공채] Kagong - 공유좌석 예약 시스템 구축

Tech HR 2022. 2. 10. 10:02
프로젝트명 : 공유 좌석 예약 시스템 구축
팀명 : Kagong
팀원 :  Tony, River, Nayla, Levi
멘토 : Justin, Jun

 

안녕하세요, 

저희는 2022 신입 개발자 공채 Kagong의 토니, 리버, 나일라, 리바이입니다. 

 

테크온보딩 기간 동안 진행했던 토이프로젝트 '공유 좌석 예약 시스템 구축'를 소개하려고 합니다.

 

먼저 'Kagong'이라는 팀명은

카카오엔터테인먼트(KakaoEntertainment) + 공유 좌석(GongYuJwaSeok)을 합쳐 카공(?)이라고... 지었습니다.

 

뭐.. 대충 이런 과정을 거쳐서 나왔다고 한다….. 리버가 지었음을 알 수 있다..

 

 

팀명은 이정도로 설명하고, 서비스 소개로 넘어가겠습니다 ^_^;

 

 

 

 

서비스 소개

 

카공은 공유 좌석 예약 시스템입니다.

카카오엔터테인먼트의 공유 좌석 공간을 예약할 때 사용하는 시스템으로, 기본 기능은 좌석 예약, 좌석 후기 작성, 좌석 즐겨찾기가 있습니다.

 

 

화면 설계

 

◾️시작화면

 


◾️예약화면

 

 
 
 

 

◾️예약현황 화면

 

 

◾️후기 작성 화면 / 전체 좌석 후기 화면 

 

 

 

◾️좌석 즐겨찾기 

 

 

 

백엔드

 

◽️About Kotilin Spring Boot

 

자바와 고민하던 중, 새로운 도전으로 코틀린을 사용해보기로 했는데요!

아무래도 최근 핫한 언어고, 다른 회사들도 많이들 넘어간다는 점에서 결정하게 되었습니다.

문법만 약간 바뀌고 스프링의 구조는 그대로 가져가는지라 크게 혼란은 없게 개발을 진행할 수 있던 것 같아요.

그러나 최대한 마감 시한을 맞추기 위해서 기존 구조에서 크게 다르지 않게 진행했습니다.

 

문제는 Spring Boot와 Kotlin의 약간의 궁합 미스로 인해 Entity 처리에 살짝 시간이 많이 들었다는 점입니다.

기존 자바처럼 만들면 돌아가지 않는 것들이 많아서, 문서를 찾아보는 과정이 많이 필요했습니다.

역시나 날로 먹을 생각은 하지 않는 게 좋았나 봅니다…

 

다행히도 필요한 기능을 구현하는 데에 큰 문제가 발생하지는 않았지만,

이후에 더 견고한 서비스를 위해서라면 단단히 준비를 하고 도입을 하는 게 맞는 것 같아요.

 

 

◽️About MSA

 

모놀로틱 환경과 MSA 환경에서 어떻게 구축할지 고민했습니다. 계정 인증 로직을 MSA환경으로 분산하면 편리할 것 같아 MSA 구조를 채택했습니다.

서버는 크게 4가지로 나눌 수 있을 것 같습니다.

  1. Gateway(Spring Cloud Gateway) - 요청을 분기 처리하고 인증 루틴을 거칠지 말지 판단합니다.
  2. Service Discovery - Eureka를 이용하여 각 서버들의 상태를 체크했습니다.
  3. Auth Server - JWT 인증 토큰을 발행 및 검증하고 구글(소셜 로그인)로부터 유저 검증을 진행합니다.
  4. API Server - 핵심 기능 로직들을 수행합니다.

 

 

◽️ 서비스 사용 시 2가지 시나리오 

서버 관점에서 요청을 2가지 시나리오로 나누어봤습니다.

 

 📍login/sign-up

 

 

❶ 클라이언트에서 로그인/회원가입을(소셜 로그인을 통해 받은 구글 코드를 포함) 요청한다.

❷ 게이트웨이가 auth 서버에게 요청을 전달한다.

❸ 클라이언트가 보낸 구글 코드를 가지고 구글 api와 통신하여 유저 정보를 검증한다.

❹ 검증된 이메일을 가지고 api 서버에게 로그인/회원가입을 요청합니다.
(해당 이메일은 검증되었으므로 api 서버는 어뷰징 여부를 판단을 안 해도 됩니다.)

❺ api 서버로부터 로그인/회원가입 응답이 오면 auth 서버에서 해당 유저 정보가 담긴 토큰을 발행합니다.

이때 핵심은 auth 서버는 jwt 토큰을 발행하고 구글로부터 유저 정보를 검증합니다. api 서버는 검증된 이메일을 가지고 회원가입을 진행합니다. 각각의 서버는 관심사 분리를 통해 핵심 기능에 집중을 할 수 있었습니다.

 

 

 📍인증이 필요한 api 기능을 사용

 

 

❶ auth 서버로부터 발행받은 토큰과 함께 api 기능을 요청합니다.

❷ 게이트웨이에서 api 서버에 요청을 전달하기 전에, 해당 토큰을 auth 서버에게 검증 요청을 합니다.

❸ auth 서버는 해당 토큰을 디코딩하여 검증합니다. 토큰 안에 있는 유저 정보를 gateway에게 응답합니다.

❹ auth 서버가 응답한 유저 정보를 header에 포함하여 api 서버에게 요청을 전달하고 수행합니다.

이 때도 마찬가지로 api 서버는 header에 포함되어 있는 유저 정보를 바로 활용할 수 있습니다.

api 서버 개발을 맡으신 Levi(리바이)가 엄청 엄청 편하다고 꼭 강조해달라고 했었습니다ㅋㅋㅋ 😄

 

 

◽️ DB 구조 및 설명

데이터베이스는 다음과 같이 구성했습니다.

기능별로 구성했는데 크게 다음의 데이터들로 나눌 수 있을 것 같습니다.

 

* 자리 정보 관련(layout, cell, seat): 공간별로 종류별로 각 자리에 대한 값을 보관

* 예약 관련(reservation): 실제 예약 내역이 저장

* 계정 관련(account): 회원 가입된 유저 정보가 저장

* 리뷰 관련(review, file): 사용자들의 각 자리에 대한 리뷰 정보 저장

* 즐겨찾기 관련(favorite): 사용자들의 각 자리에 대한 즐겨찾기 여부 저장

 

사내에서 가장 많이 사용하는 MySQL을 이용하여 DB를 만들었으며, 테이블의 초기 생성과 변경에 대해서는 flyway를 스프링에 도입하여 사용하였습니다.

 

 

◽️인프라 구성 

저희는 Kakao 내부 인프라를 이용하여 배포를 진행하였습니다. 깃헙과, docker registry인 d2hub, 배포 툴인 kargo, 배포 환경인 dkos를 사용했습니다. 확실히 내부적으로 굉장히 잘 구성이 되어 있어서, aws 같은 서비스보다도 더욱 손쉽게 배포할 수 있다는 점이 장점이었던 것 같아요! 쿠버네티스만 이해하고 있다면 크게 배포에서 신경 쓸 것이 없는 좋은 환경이었습니다.(그게 어렵지만)

 

 

Spring Cloud를 이용하였기 때문에 그에 해당하는 리소스들을 배포했습니다.

gateway, eureka, auth, api 이렇게 네 개의 서비스를 배포했습니다.

그리고 Database는 helm chart로 배포했습니다.

 

 

kubernetes 설정 파일을 만드는 것이 쉽지 않아서 하나를 만들어 놓고, 나머지는 비슷하게 작성했는데요,

주어진 시간상 완벽하기보다는 성공적인 배포를 목적으로 진행했기 때문에 전반적으로 상세 설정은 진행하지 않았습니다.

그래서 보통이라면 로드밸런싱도 들어가고, 고가용성도 염두해야 했지만, 간단하게 하나씩만 만들어서 진행했습니다.

 

 

클라이언트 (안드로이드/FE)

 

◽️프로젝트 목표 

저희는 프로젝트를 시작할 때, 3가지의 목표를 정했습니다. 그리고 프로젝트를 수행하는 과정에서 문제를 만날 때마다 의사 결정의 기준으로 이 목표를 사용했습니다. 저희가 정한 목표는 다음과 같습니다.

첫째, 왜 그렇게 해야만 하는지 확실히 이유를 남기려고 했습니다.

둘째, 기간이 짧은 만큼 한 가지의 기술적인 도전을 진행하려고 했습니다.하지만 프로젝트 진행 과정에서 저희의 성장 욕구로 인해 목표가 3가지로 늘어났습니다.

셋째, 카카오엔터테인먼트의 개발 프로세스 컨벤션에 익숙해지기를 목표로 삼았습니다. 컨플루언스나 지라, 그리고 코딩 컨벤션에 대해 공유받고 지키려고 노력했습니다.

 

 

◽️기술 도전 사항 

 

1. 아키텍처 & 폴더링

 

앱의 확장성을 위해 아키텍처와 폴더링 구조에 신경을 썼습니다. 먼저, 패키지를 프레젠테이션, 도메인, 데이터로 분리하여 클린 아키텍처의 구조를 가져가려고 노력했고, 이와 함께 MVVM과 Repository Pattern을 함께 적용했습니다.

 

 

2. Hilt

 

 

두 번째 도전사항은 힐트입니다. 의존성을 주입해주는 힐트를 사용하여 제어의 역전을 이용해 변경에 유연한 코드를 만들어줬습니다.

 

 

3. WebView

 

 

세 번째는 웹뷰입니다. 좌석배치도가 쉽게 바뀔 수 있어야 한다는 점에서 안드로이드는 제약이 있어 예약하는 화면을 웹뷰를 통해 구현했습니다. 웹뷰를 구현 방법으로 Javascript Interface로 데이터를 수신하는 것과 웹 url을 통해 수신하는 것이 존재했습니다.

웹 url을 통해 수신할 경우, url에 존재하는 query로 가져오게 됩니다. 반면에 Javascript Interface는 json 문자열로 데이터를 얻어와 파싱을 하면, 바로 객체로 얻어올 수 있다는 장점이 있었습니다. 이런 장점을 살려 저희는 Javascript Interface로 데이터를 수신했습니다.

 

 

◽️도전하는 과정에서 기술적으로 느낀 점

 

1. Clean Architecture 적용

 

 

DTO를 분리함으로써 서버 API의 구조가 바뀌더라도, 안드로이드 앱의 비즈니스 로직이 영향을 받는 것을 줄일 수 있었습니다.

 

 

2. Architecture에서 Repository Pattern을 이용한 Data Layer 분리

 

 

두 번째로, Repository 패턴을 통해 데이터 계층을 분리하여 Repository 구현체를 빠르게 전환했습니다.

서버와 동시에 개발하는 과정에서 아직 API가 나오지 않았어도 Dummy Data로 빠르게 기능 개발이 가능했고, 실제 API를 사용할 때는 Build Type을 변경하여 레포지터리 구현체를 빠르게 전환했습니다.

 

 

3. Hilt를 사용하여 데이터 의존성 주입

 

힐트를 사용하여 뷰 모델에서 레포지토리를 빠르게 전환 가능했습니다.

또한, 의존성 주입할 인스턴스의 생명주기를 관리할 때 보일러 플레이트 코드를 줄일 수 있었습니다.

 

 

 

◽️개발 과정에서 발생한 문제 상황 및 해결 방안

 

1. RecyclerView의 Paging에서 Footer 관리 로직을 ViewModel이 담당하는 것이 옳은가?
-> 해결방안: Footer 관리 로직을 역할로 갖는 클래스 구현

 

 

문제 상황 설명에 앞서, RecyclerView의 Paging 로직에 대해 공유드리겠습니다.

데이터 목록이 나타나는 화면에서 가장 마지막의 아이템 뷰로 푸터 View가 존재합니다.

Footer View는 자신이 생성됨과 동시에 ViewModel에 다음 페이지 데이터를 요청하고, ViewModel은 Repository에게 이 요청을 전달합니다.

Repository를 통해 다음 페이지 데이터를 가져오고, 이때 해당 페이지가 마지막 페이지인지를 나타내는 IsEnd값을 함께 가지고 옵니다.

isEnd값이 false라면, Repository에서 가져온 페이지 데이터에 Footer 데이터가 추가되고, (클릭) 이것이 뷰로 컨버팅되어 나타나게 됩니다.

 

 

그런데 Footer 데이터를 붙여주는 로직을 'ViewModel에서' 구현하면, 화면의 노란 박스 안의 if문 코드처럼 isEnd 값에 대한 검증 로직이 추가됩니다.

이와 같이 구현이 된다면, 저희 프로젝트에 대한 배경지식이 없는 개발자는 이 if문 검증 로직이 언제 필요한지 알 수 없다는 문제가 발생했습니다.

 

 

그래서 페이지 데이터 리스트를 ItemReviewStatusList라는 이름의 '일급 컬렉션' 클래스로 구현했습니다.

이 클래스에게, 요청한 페이지 데이터가 마지막 페이지에 해당할 경우 Footer를 갖도록 책임을 부여했습니다.

RecyclerView의 페이징에서 Footer에 관한 로직을 담당하는 클래스를 따로 만들어주어, RecyclerView 페이징 비즈니스를 관리할 수 있었습니다.

 

2. Fragment History 관리

 

 

저희 앱의 구조는 Main Activity를 두고 있고 그 위에 4개의 Fragment들이 동작하는 방식입니다.

 

 

Fragment의 History를 제한된 크기만큼 기억해서 뒤로 가기를 관리해주려고 했습니다.

그런데 안드로이드에서 제공하는 addToBackStack 함수는 후입 선출로 쌓여 History의 크기를 관리하기 어렵다는 단점이 있었습니다.

이를 해결하기 위해 FragmentManager를 만들어서 Fragment를 Deque로 관리했습니다.

 

 

 
시연 영상

 

◽️시연 영상 상세 설명 보기

더보기

[시작 화면]

  • 구글 로그인을 하여 앱에 접속할 수 있습니다.
  • 구글로그인이 성공하면 예약하기 화면에 진입합니다.

[예약 화면]

  • 좌석배치도를 보면 투썬빌딩 5층을 참고했습니다.
  • 회색은 이미 사용 중인 좌석이고 파란색은 사용 가능한 좌석입니다.
  • 회색 좌석을 클릭하면 다음과 같이 사용 중인 사람에 대한 정보가 뜹니다.

[예약하기]

  • 앱의 상단에서 층수, 예약의 시작시간과 종료시간을 선택할 수 있습니다.
  • 그 후에 파란 좌석을 클릭하면 예약하기 창이 뜹니다.
  • 변경된 시간이 입력되었고, 선택한 좌석에 관한 후기 또한 볼 수 있습니다.
  • 여기서 신청 버튼을 누르면 좌석이 신청되고 화면이 전환됩니다.

[예약 현황 화면]

  • 예약된 좌석을 최신순으로 확인할 수 있습니다.
  • 좌석 옆에 점 세 개 버튼을 클릭하면 예약한 자리를 취소할 수 있습니다.
  • 후기를 작성하지 않은 좌석에 대해서 후기 작성하기 버튼이 뜨고 클릭하면 후기 작성 화면으로 이동합니다.

[후기 작성 화면]

  • 후기 내용을 작성하고 이미지를 첨부할 수 있습니다.

[전체 좌석 후기 화면]

  • 그리고 후기를 등록하면 좌석 후기 페이지에서 모든 좌석에 대한 후기를 볼 수 있습니다.

[좌석 즐겨찾기]

  • 예약 화면에서 좌석을 클릭한 후 오른쪽 상단의 별 모양 버튼을 클릭하면 선호하는 좌석으로 등록됩니다.
  • 선호하는 좌석 페이지로 이동하면 선택한 좌석이 제대로 등록이 된 것을 볼 수 있습니다.
  • 이 페이지에서 선호하는 좌석 정보를 클릭하면 예약 화면의 좌석으로 이동합니다.
  • 통통튀는 좌석을 확인할 수 있습니다.
  • 좌석을 눌러서 별 버튼을 다시 누르면 선호하는 좌석이 해제됩니다.

 

 

프로젝트 리뷰

 

Nylah 🙇🏻‍♀️

  • 동기들과 함께하는 프로젝트라는 점에서 기억에 많이 남을 것 같습니다. 프로젝트를 통해 서로가 이제까지 쌓아온 개발 지식도 공유할 수 있어서 도움이 많이 되었습니다.
  • 개발은 문제 해결의 과정인 만큼, 해결하기 어려운 문제들도 여러 번 만났지만, 동기들과 멘토님들과 함께 어떤 방법이 더 좋은 해결책인지 고민하며 성장할 수 있었습니다!
  • 특히, River와 함께 클린 아키텍처를 적용하며 더 나은 안드로이드 앱 구조에 대해 많이 공부할 수 있었습니다.
  • 멘토님들께서 코드 리뷰도 꼼꼼히 해주시고, 항상 문제가 발생할 때마다 오프라인으로 안되면, 슬랙이나 구글 미트를 통해서 빠르게 도와주신 덕분에 프로젝트를 성공적으로 마무리할 수 있었다고 생각합니다. 다시 한번 저희 멘토님이신 Justin, Jun에게 감사 인사를 드립니다!~

 

River 💁🏻‍♀️

  • 이번 프로젝트를 통해 평소 사용해보고 싶은 기술과 아키텍처를 적용해서 좋았습니다.
  • 동기들과 협업하면서 빠르게 친해질 수 있었던 것 같습니다. 어려운 점도 많았지만, 동기들과 함께 해결해가는 과정이 보람찼습니다.
  • 또한, 멘토님께 질문하면 항상 빠르게 답장해주시고 생각해보지 못한 부분까지 짚어주셔서 감사했습니다.
  • 프로젝트를 하면서 기본을 다지는 게 중요하다는 생각을 많이 했습니다. 안드로이드와 Kotlin에 대해 이론적으로 더 잘 알았다면 더 좋은 구조를 만들고, 오래 헤맸던 구간도 빨리 해결할 수 있었을 것입니다. 성공적으로 프로젝트를 마무리할 수 있어서 기쁩니다! ㅎㅎ

 

Tony 🙋🏻‍♂️

  • 난생처음 kotlin, webflux, msa 환경을 도전해보았습니다. 저는 프로젝트를 진행하면서 처음 설정부터 도전이었는데요. 혼자 진행했으면 목적지까지 도착하지 못했을 겁니다.
  • 하지만 백엔드 파트이신 levi(리바이)의 도움 덕분에 1인분을 하게 되었네요. 책도 빌려주시고 제가 귀찮게 자주 질문하는데도 늘 친절하고 상냥하게 답변해주셨어요. ㅎㅎ (매우 매우 감사했습니다. )짧은 시간 동안 귀중한 경험을 했던 것 같습니다! ㅎㅎ 스스로 성장을 많이 할 수 있었던 시간이었습니다.
  • 좋은 파트 구성해주신 기술기획팀과 소중한 실전 경험들을 아낌없이 전달해주신 멘토님들 Justin, Jun 매우 감사합니다!
  • 다들 새해 복 많이 받으세요! 🎉

 

Levi 🙆🏻‍♂️

  • 웹과의 협업은 많이 해봤지만, 안드로이드 같은 모바일 개발과의 협업은 처음 해 봤어요.
  • 여러모로 모르는 게 많았지만 하도 다들 능력자분들이 시라, 결과물은 잘 나온 것 같아서 뿌듯합니다. 다들 머리도 빨리 돌아가시고, 일 진행도 척척 하시는 걸 보면서 많이 배웠어요. 사실 실제로 사용한다 그래서 중간에 엄청나게 쫄아있던 기억이 있네요…ㅎ
  • 3, 4주 정도 동안 많은 걸 팀원들과 했더라고요. 밥도 많이 먹고, 이야기도 하고, BD도 써보고, 재택도 해보고 참 많은 것들이 기억에 남는 프로젝트 기간이었습니다!

 

토니, 나일라, 리버, 리바이