블로그 목록

Capacitor로 웹앱을 iOS/Android 앱으로 만들기

이미 만들어진 웹 앱을 앱스토어에 올려야 할 일이 생겼다. React Native로 다시 짜는 건 시간이 너무 걸린다. PWA로 가자니 iOS Safari에서 push 알림이나 파일 시스템 접근에 제약이 많다.

Capacitor는 Ionic 팀이 만든 런타임이다. 웹 앱을 네이티브 WebView 안에서 실행하고, 카메라·파일·푸시 알림 같은 네이티브 기능을 JavaScript API로 노출한다. Cordova의 후속 프로젝트라고 보면 된다.

어떤 구조인가

React Native처럼 네이티브 컴포넌트를 렌더링하는 방식이 아니다. 기존 웹 앱 그대로 WebView 안에서 실행하면서, 필요한 네이티브 기능만 플러그인 레이어를 통해 연결한다.

코드베이스는 하나다. 브라우저에서도 돌아가고, iOS/Android 앱으로 패키징해도 같은 코드가 실행된다.

세팅

Next.js 기반 프로젝트 기준이다.

npm install @capacitor/core @capacitor/cli
npx cap init

cap init을 실행하면 앱 이름과 패키지 ID(예: com.example.myapp)를 입력한다. capacitor.config.ts가 생성된다.

// capacitor.config.ts
import type { CapacitorConfig } from '@capacitor/cli';

const config: CapacitorConfig = {
  appId: 'com.example.myapp',
  appName: 'My App',
  webDir: 'out', // Next.js 정적 빌드 출력 디렉토리
};

export default config;

Capacitor는 정적 파일을 WebView에 올리는 구조라서, Next.js는 output: 'export'로 정적 빌드가 필요하다.

// next.config.js
const nextConfig = {
  output: 'export',
};

module.exports = nextConfig;

SSR이 필요한 API는 별도 서버를 두고 fetch로 연결하는 방식으로 분리해야 한다.

iOS/Android 플랫폼 추가

npm install @capacitor/ios @capacitor/android
npx cap add ios
npx cap add android

ios/, android/ 디렉토리가 생긴다. 이 안에 Xcode 프로젝트와 Gradle 프로젝트가 들어간다.

빌드 후 네이티브 프로젝트로 복사하는 명령어가 cap sync다.

next build
npx cap sync

코드를 수정할 때마다 next build && npx cap sync를 실행해야 네이티브 앱에 반영된다. 개발 중에는 npx cap run ios --livereload --external처럼 라이브리로드 옵션을 쓰면 편하다.

네이티브 기능 붙이기

공식 플러그인 패키지로 설치한다.

npm install @capacitor/camera @capacitor/filesystem @capacitor/push-notifications
npx cap sync

카메라 예시다.

import { Camera, CameraResultType, CameraSource } from '@capacitor/camera';

async function takePhoto(): Promise<string | undefined> {
  const photo = await Camera.getPhoto({
    resultType: CameraResultType.DataUrl,
    source: CameraSource.Camera,
    quality: 80,
  });
  return photo.dataUrl;
}

웹에서 실행하면 파일 선택 다이얼로그가 열리고, 네이티브 앱에서는 카메라 앱이 열린다. 같은 코드로 두 환경을 다 처리한다.

iOS는 Info.plist에 카메라 권한 설명 문자열을 추가해야 심사를 통과한다.

<!-- ios/App/App/Info.plist -->
<key>NSCameraUsageDescription</key>
<string>사진 촬영은 위해 카메라 접근 권한이 필요합니다.</string>

최종 빌드

npx cap open ios     # Xcode에서 열기
npx cap open android # Android Studio에서 열기

Xcode에서는 Product → Archive 후 App Store Connect로 업로드한다. Android Studio에서는 Build → Generate Signed Bundle/APK로 배포 파일을 만든다.

EAS Build 같은 클라우드 빌드 서비스 없이 로컬에서 처리하는 방식이라, 맥이 있어야 iOS 빌드가 가능하다는 점은 동일하다.


네이티브 UI 컴포넌트가 많이 필요하거나 성능이 중요한 앱이라면 React Native가 맞다. 하지만 기존 웹 앱을 빠르게 앱스토어에 올리는 게 목표라면 Capacitor가 훨씬 현실적이다. 웹 코드를 그대로 재사용하면서 네이티브 기능만 얹는 접근이라 학습 비용도 낮다.