[Dalendar DevLog 7] 달력 앱의 핵심 구조: 중첩 RecyclerView로 월간 뷰 만들기
월 단위 스크롤과 날짜 그리드를 효율적으로 구현하기 위해 이중 RecyclerView 구조를 채택한 배경과 상세 구현 내용을 소개합니다.
![[Dalendar DevLog 7] 달력 앱의 핵심 구조: 중첩 RecyclerView로 월간 뷰 만들기](/images/blog/dalendar_dev_7_recyclerview.png)
7편: 달력 앱의 핵심 구조: 중첩 RecyclerView로 월간 뷰 만들기
1. 들어가며: 스크롤 가능한 월간 달력의 목표 설정
이번 글에서는 제가 기본적인 형태의 달력 앱을 어떻게 구현했는지 공유해 보려고 합니다. 핵심 요구사항은 ‘한 화면에 한 달씩 표시’하고, ‘좌우 스크롤을 통해 이동’하는 것이었습니다.
2. 핵심 아키텍처: 이중 RecyclerView 구조
제가 구현한 앱의 기본 구조는 두 개의 RecyclerView를 중첩시키는 것입니다. 참고 코드의 핵심이기도 한 이 구조를 통해 월 단위의 좌우 스크롤과 월별 날짜 그리드 표시를 효율적으로 처리할 수 있었습니다.
- 메인 화면 (MainActivity): 앱이 처음 실행될 때 나타나는 화면입니다.
- 월(Month)을 위한 RecyclerView (A): 이 RecyclerView는 각 아이템이 한 달 전체를 나타냅니다. 좌우로 스크롤하여 월을 넘길 수 있습니다.
- 일(Day)을 위한 RecyclerView (B): 이 RecyclerView는 A의 각 아이템 내부에 중첩된 형태로 존재합니다. 6행 7열의 그리드(Grid) 레이아웃을 가지며, 총 42개의 아이템이 각각 하루를 나타내는 방식입니다.
RecyclerView는 틀(viewholder)을 재활용하여 시스템 자원을 효율적으로 사용합니다. 레이아웃 매니저를 통해 재활용 방식을 자유롭게 설정할 수 있다는 것이 큰 장점입니다.
3. 구현 상세: 월(月) 생성 로직 (RecyclerView A)
3.1. ‘무한’ 스크롤을 위한 초기 위치 설정
사용자가 과거와 미래, 양방향으로 계속 스크롤할 수 있는 것처럼 보이게 하기 위해, 저는 월 RecyclerView(A)의 초기 위치를 아주 큰 값으로 설정했습니다.
// MainActivity.kt
scrollToPosition(Int.MAX_VALUE/2)
Int.MAX_VALUE의 절반을 시작 위치로 설정함으로써, 사용자가 왼쪽(과거)이나 오른쪽(미래)으로 스크롤할 때 사실상 끝없이 이동하는 듯한 ‘무한 스크롤’ 효과를 구현할 수 있습니다.
3.2. 스크롤에 따른 월 계산
실제 월(月)을 계산하는 로직은 현재 position을 기준으로 합니다.
calendar.add(Calendar.MONTH, position - center)
현재 아이템의 position과 중앙 위치(center)의 차이를 계산하여 월을 조정합니다. 이 덕분에 좌우 스크롤을 통해 월이 자연스럽게 변경됩니다.
4. 구현 상세: 일(日) 생성 로직 (RecyclerView B)
각 월에 해당하는 6x7 그리드(총 42개 셀)의 날짜를 생성하는 로직은 월 RecyclerView(A)에서 계산된 특정 월을 기준으로 이루어집니다.
- 리스트 초기화: 42개의 날짜 객체를 담을 리스트를 생성합니다.
- 날짜 생성 루프: 해당 월의 1일이 포함된 주의 일요일부터 시작하여 42개의 날짜를 순차적으로 생성합니다.
- 데이터 전달: 날짜가 모두 채워진 dayList는 최종적으로 일 RecyclerView(B)를 담당하는 어댑터로 전달되어 화면에 표시됩니다.
5. 마무리
참고 코드를 제 프로젝트에 맞게 적용하면서 뷰 바인딩(view binding)을 사용하도록 코드를 수정하는 작은 작업이 필요했습니다. 이렇게 중첩 RecyclerView를 활용해 월 단위 스크롤이 가능한 달력의 기본 골격을 성공적으로 완성할 수 있었습니다.
![[Dalendar DevLog 1] 프로젝트의 시작과 기술 스택 선정](/images/blog/dalendar_dev_1_ideation.png)
![[Dalendar DevLog 2] 견고한 앱의 뼈대 - 아키텍처와 데이터 구조 설계](/images/blog/dalendar_dev_2_architecture.png)
![[Dalendar DevLog 3] 핵심 기능 구현 1 (백엔드 & 로직)](/images/blog/dalendar_dev_3_math_logic.png)