Compose Multiplatform으로 Android, iOS, Web에서 UI를 공유하는 개인 포트폴리오를 만들기 시작했다.
이번 글에서는 Kotlin/Wasm으로 웹 결과물을 만들고, GitHub Actions와 GitHub Pages를 이용해 main 브랜치에 코드를 푸시할 때마다 자동으로 배포되도록 구성한 과정을 정리한다.
1. 전체 배포 흐름
전체 과정은 다음과 같다.
main 브랜치에 Push
→ GitHub Actions 실행
→ Kotlin/Wasm 프로덕션 빌드
→ 정적 배포 파일 생성
→ GitHub Pages에 업로드
→ 포트폴리오 주소에 반영
소스 코드는 GitHub 저장소가 관리하고, GitHub Actions는 빌드를 담당한다. GitHub Pages는 빌드 결과물인 HTML, JavaScript, Wasm 파일을 실제 웹사이트로 제공한다.
2. 프로젝트 구성
프로젝트는 Android, iOS, Web 타깃을 가지는 Compose Multiplatform 프로젝트로 구성했다.
portfolio/
├── composeApp/
│ └── src/
│ ├── commonMain/
│ ├── androidMain/
│ ├── iosMain/
│ └── wasmJsMain/
├── core/
│ ├── model/
│ └── designsystem/
├── domain/
├── data/
├── feature/
│ └── portfolio/
├── iosApp/
└── .github/
└── workflows/
commonMain에는 플랫폼이 공유하는 UI를 두고, androidMain, iosMain, wasmJsMain에는 각 플랫폼의 진입점만 배치했다.
프로젝트 규모는 크지 않지만 이후 콘텐츠와 기능이 늘어날 것을 고려해 멀티모듈과 클린 아키텍처를 적용했다.
composeApp → feature → domain → core:model
composeApp → data → domain
feature → core:designsystem
domain 모듈은 Compose나 Android API를 알지 못한다. data 모듈이 domain의 Repository 인터페이스를 구현하고, composeApp에서 Repository, UseCase, Presenter를 조립한다.
3. Kotlin/Wasm 타깃 설정
composeApp/build.gradle.kts에서 wasmJs 타깃을 설정했다.
@OptIn(ExperimentalWasmDsl::class)
wasmJs {
outputModuleName = "portfolio"
browser {
commonWebpackConfig {
outputFileName = "portfolio.js"
}
}
binaries.executable()
}
outputModuleName과 outputFileName을 지정해 생성되는 모듈과 JavaScript 파일의 이름을 명확하게 관리했다.
Web 진입점에서는 ComposeViewport를 이용해 공통 App Composable을 브라우저에 표시한다.
fun main() {
ComposeViewport {
App()
}
}
4. 로컬에서 Web 실행하기
개발 중에는 다음 명령어로 브라우저 개발 서버를 실행할 수 있다.
./gradlew :composeApp:wasmJsBrowserDevelopmentRun
기본적으로 localhost:8080에서 화면을 확인할 수 있다.
GitHub Pages에 올릴 프로덕션 결과물은 다음 명령으로 생성한다.
./gradlew :composeApp:wasmJsBrowserDistribution
빌드가 완료되면 결과물은 다음 경로에 생성된다.
composeApp/build/dist/wasmJs/productionExecutable
이 디렉터리에는 index.html, portfolio.js, Kotlin과 Skia 관련 Wasm 파일 등이 포함된다. GitHub Pages에는 소스 코드가 아니라 이 디렉터리의 내용이 배포되어야 한다.
5. GitHub Actions 설정
.github/workflows/deploy-pages.yml 파일을 만들고 다음과 같이 구성했다.
name: Deploy portfolio to GitHub Pages
on:
push:
branches: [main]
workflow_dispatch:
permissions:
contents: read
pages: write
id-token: write
concurrency:
group: pages
cancel-in-progress: true
jobs:
build-and-deploy:
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 17
- uses: gradle/actions/setup-gradle@v4
- run: ./gradlew :composeApp:wasmJsBrowserDistribution
- uses: actions/configure-pages@v5
- uses: actions/upload-pages-artifact@v3
with:
path: composeApp/build/dist/wasmJs/productionExecutable
- name: Deploy
id: deployment
uses: actions/deploy-pages@v4
main 브랜치에 push가 발생하면 워크플로가 실행된다.
actions/checkout은 저장소 코드를 가져오고, setup-java는 Gradle 빌드에 사용할 JDK 17을 준비한다. setup-gradle은 Gradle 캐시와 실행 환경을 구성한다.
wasmJsBrowserDistribution 작업이 끝나면 upload-pages-artifact가 productionExecutable 디렉터리를 Pages용 아티팩트로 업로드한다. 마지막으로 deploy-pages가 해당 파일을 실제 GitHub Pages 환경에 배포한다.
6. GitHub Pages 설정
워크플로 파일만 추가했다고 바로 배포되는 것은 아니다. GitHub 저장소에서 다음 메뉴로 이동한다.
Settings
→ Pages
→ Build and deployment
→ Source
→ GitHub Actions
Repository 이름이 사용자명.github.io 형식이라면 기본 주소는 다음과 같다.
https://사용자명.github.io
이번 프로젝트의 주소는 https://leeeunjeong1.github.io이다.
최초 배포 중에는 404가 표시될 수 있다. Actions 빌드와 Pages 배포가 완료된 후 다시 접속하면 된다.
7. 반응형 화면에서 발생한 문제
데스크톱 화면에서는 정상적으로 보였지만 모바일 크기로 확인했을 때 Compose 화면 아래쪽이 검게 잘리는 문제가 있었다.
브라우저 높이는 844px이었지만 Compose가 붙은 컨테이너 높이는 195px로 계산되고 있었다.
처음 적용한 CSS는 다음과 같았다.
html, body, #root {
width: 100%;
min-height: 100%;
}
퍼센트 기반 min-height만으로는 ComposeViewport가 사용할 명확한 높이가 만들어지지 않았다.
다음과 같이 html과 body 높이를 명시해서 해결했다.
html, body {
width: 100%;
height: 100%;
margin: 0;
}
body {
min-height: 100vh;
overflow: hidden;
}
수정 후 모바일 뷰포트에서도 Compose 캔버스가 화면 전체 높이를 정상적으로 사용했다.
8. Kotlin과 Compose 버전 호환성
처음에는 최신 Compose Multiplatform 버전을 적용했지만 Android 빌드에서 compileSdk와 Android Gradle Plugin 요구 버전이 맞지 않는 문제가 발생했다.
또한 최신 Kotlin 버전을 기존 AGP와 조합했을 때 D8/R8이 Kotlin 메타데이터를 처리하면서 많은 경고를 출력했다.
최종적으로 다음 조합을 사용했다.
Kotlin 2.2.21
Compose Multiplatform 1.9.3
Android Gradle Plugin 8.11.1
Gradle 8.14.4
compileSdk 36
JDK 17 이상
항상 가장 최신 버전을 선택하기보다 Kotlin, Compose, AGP, Gradle, compileSdk의 호환성을 함께 확인하는 것이 중요했다.
9. 배포 전 로컬 검증
GitHub에 push하기 전에 Web과 Android 빌드를 함께 확인했다.
./gradlew \
:composeApp:wasmJsBrowserDistribution \
:composeApp:assembleDebug
두 작업이 모두 성공하는 것을 확인한 후 main 브랜치에 push했다.
이렇게 하면 GitHub Actions에서 처음 발견하는 오류를 줄일 수 있고, 공통 코드 변경이 Web과 Android 양쪽에 문제를 만들지 않았는지도 함께 확인할 수 있다.
10. 마무리
Compose Multiplatform을 사용하면 Android에서 익숙한 Kotlin과 Compose 방식으로 Web UI까지 공유할 수 있다.
여기에 GitHub Actions와 GitHub Pages를 연결하면 별도의 서버를 직접 관리하지 않고도 다음과 같은 배포 흐름을 만들 수 있다.
코드 수정
→ main 브랜치 push
→ Wasm 자동 빌드
→ GitHub Pages 자동 배포
'Study > Compose' 카테고리의 다른 글
| compose / Column Scrollable 하게 만들기 (Column + verticalScroll) (0) | 2023.08.27 |
|---|---|
| Compose / HorizontalPager swipe/drag/scroll 막기 (0) | 2023.08.02 |
| Compose / TopAppBar Title 가운데 정렬 - CenterAlignedTopAppBar (0) | 2023.07.18 |
| Compose / 선언형 UI(Declarative UI)란 무엇인가 (명령형 UI와의 차이) (0) | 2023.07.17 |
| 스터디 / 함수형 UI 스터디 - Compose (0) | 2023.07.17 |