그래프 라이브러리 1. 커스텀 뷰 만들기
Last updated: Dec 12, 2023
이번에 부스트캠프 그룹 프로젝트를 하면서 그래프 라이브러리를 구현한 과정을 정리해보았다. 이번 포스팅에서는 프로젝트 초기 설정 및 커스텀 뷰 만든 과정에 대해 설명하고자 한다. (project repo, library repo)
프로젝트 초기 설정
프로젝트 구조
Project Root
│
├───app
│ ├───src
│ └───build.gradle.kts
│
├───materialchart
│ ├───src
│ └───build.gradle.kts
│
├───build.gradle.kts
├───gradlew
├───gradle.properties
└───등등 여러 파일들...
여러 안드로이드 오픈소스 라이브러리를 참고한 결과, 대부분의 라이브러리들이 위와 같은 구조를 가지고 있었다. 라이브러리를 구현하는 프로젝트는 라이브러리 이름
폴더에, 라이브러리를 사용하는 샘플 앱 소스코드는 app
폴더에 위치시키는 것이 일반적인 것 같다. 라이브러리 개발 후 테스트할때도 샘플 앱으로 손쉽게 테스트 할 수 있고, 오픈소스 라이브러리 배포가 목표라는 이 두가지 이유로 위 구조를 따라서 프로젝트를 구성하였다.
배포 전 사용하고 싶을때
위와 같이 프로젝트를 구성하면, 한가지 문제가 생긴다. 라이브러리를 Maven과 같은 원격 레포지토리에 배포를 하면 문제가 없지만, 현재 상태에서 그래프 라이브러리를 사용하려고 하는 원래 프로젝트에서 사용하려고 하면 1. 라이브러리 소스만 따로 복사 붙여넣기 해서 테스트, 2. 서브모듈로 추가하기 두가지 방법이 있다.
1번 방법은 라이브러리를 수정할때마다 매번 복사 붙여넣기 해야하는 불편함이 있고, 2번 방법은 서브모듈을 사용하는 방법이기 때문에 레포 전체의 코드가 다 모듈 폴더 안으로 들어가게 되는데, 이렇게 되면 모듈 안 구조도 맞지 않을 뿐더러 샘플 앱 소스까지 들어가기 때문에 문제가 발생한다.

subtree를 활용한 레포 구조
이 문제를 해결하기 위해 subtree를 활용하였다. Git Subtree는 레포의 subdirectory를 복사할 수 있는 기능이다. 이 기능을 이용하여 라이브러리 소스코드만 담긴 라이브러리 폴더를 subtree로 복사하여 다른 브랜치에 넣어두었다. Subtree로 복사된 브랜치를 확인해보면 라이브러리 소스코드가 root 폴더에 있는 파일 구조가 나오게 된다. 이렇게 설정한 후 라이브러리를 사용하고 싶은 원래 프로젝트에는 해당 브랜치를 따르는 서브모듈을 추가하면 끝이다.
git submodule add -b <subtree_branch> https://github.com/Taewan-P/material-android-chart <your_module_folder_location>
완벽해 보이지만, 아직 부족하다. 라이브러리를 수정한다고 subtree로 만들어놓은 브랜치의 파일들이 바뀌는게 아니다. 따라서 라이브러리를 수정하고 해당 변경 사항을 subtree로 복사된 브랜치에 반영해주어야 한다. 이를 위해 git subtree push
명령어를 사용하면 된다.
git subtree push --prefix materialchart origin <subtree_branch>
매번 push하는 이 과정이 귀찮아서 커밋이 들어오면 자동으로 푸시해주는 워크플로우를 GitHub Actions를 통해 구축해주었다.
name: 🚜 Auto update subtree
on:
push:
branches:
- main
paths:
- <library_folder>/**
workflow_dispatch: # 수동으로 할 경우도 고려
jobs:
run:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Sync branch using subtree
run: |
git subtree push --prefix materialchart origin <subtree_branch>
커스텀 뷰 만들기
class Chart @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
dataset ?: return
// TODO: 그래프 그리기
// TODO: 축, 라벨 그리기
// TODO: 가이드라인 그리기
}
}
커스텀 뷰 클래스는 View
를 상속받았다. View
를 상속받은 후 onDraw
메서드를 override하여 그래프를 그리는 로직을 구현하면 된다. onDraw
메서드는 View
가 화면에 그려질 때마다 호출되는 메서드이다. onDraw
안에 들어가는 Canvas 오브젝트는 View
가 자기자신?한테 그릴 수 있는 오브젝트이기 때문에 그래프를 그리는 로직은 해당 오브젝트를 이용하면 된다.
<your.library.package.name.Chart
android:id="@+id/example_chart_1"
android:layout_width="0dp"
android:layout_height="400dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
커스텀 뷰를 만들었으면, Android에서는 레이아웃 파일에 위와 같이 추가할 수 있다.
다음 게시글에서는 그래프 축을 어떻게 그렸는지 설명하고자 한다.