소개
기존 Next.js 웹 애플리케이션이 있으신가요? 이 가이드에서는 웹 앱을 iOS 및 Android 네이티브 모바일 앱으로 전환하는 방법을 알아봅니다. 사용: Capacitor 8 — 성능이 개선되고 새로운 기능이 추가된 최신 버전입니다.
Capacitor는 기존 React codebase를 그대로 유지하면서 웹 앱을 native container로 감싸 카메라, filesystem, push notifications 같은 device API에 접근할 수 있게 해줍니다. React Native와 달리 코드를 다시 작성할 필요가 없습니다. 여러분의 Next.js code가 그대로 실행됩니다.
학습 내용:
- 기존 Next.js 앱을 static export로 설정하기
- 필수 native plugins와 함께 Capacitor 8 추가하기
- iOS와 Android 시뮬레이터에서 빌드하고 테스트하기
- 빠른 개발을 위한 live reload 활성화하기
- 자주 겪는 iOS 레이아웃 문제 해결하기 (viewport, safe area, horizontal overflow)
- Capgo Native Navigation and Transitions로 네이티브 느낌의 UI 추가하기
새 프로젝트를 처음부터 시작하려고요? 다음 가이드를 확인해 보세요: Next.js 모바일 앱을 처음부터 만들기.
Next.js와 Capacitor 사용 시 장점
- Code 재사용성: Next.js를 사용하면 재사용 가능한 컴포넌트를 작성하고 웹 및 모바일 앱 간에 code를 공유할 수 있어 개발 시간과 노력을 절약할 수 있습니다.
- 성능: Next.js는 서버 사이드 렌더링과 code 스플리팅과 같은 내장 성능 최적화를 제공하여 빠른 로딩 시간과 매끄러운 사용자 경험을 보장합니다.
- 네이티브 기능: Capacitor를 사용하면 카메라, 위치 정보 등 네이티브 기기 기능에 접근할 수 있어 기능적인 모바일 앱을 구축할 수 있습니다.
- 간소화된 개발: Capacitor를 사용하면 익숙한 웹 기술로 모바일 앱을 개발하고 테스트할 수 있어 학습 곡선을 낮추고 개발 프로세스를 간소화할 수 있습니다.
사전 요구사항
시작하기 전에 다음이 준비되어 있는지 확인하세요:
- Node.js 18+ 설치되어 있는지
- 기존의 Next.js 15+ 애플리케이션
- iOS 프로파일링 (iOS 개발의 경우, macOS 전용)
- 디버깅하기 (Android 개발용)
모바일용 Next.js 앱 설정하기
첫 단계는 Next.js 앱을 정적 내보내기(static export)로 설정하는 것입니다. Capacitor는 네이티브 앱에 번들링할 정적 HTML/JS/CSS 파일이 필요합니다.
다음 next.config.js (또는 next.config.ts) 파일에 내보내기 설정을 추가하세요:
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'export',
images: {
unoptimized: true,
},
};
module.exports = nextConfig;
이 output: 'export' 설정은 Next.js가 정적 HTML 파일을 생성하도록 하고, images: { unoptimized: true } 서버가 필요한 Next.js 이미지 최적화를 우회합니다.
중요: 서버가 필요한 기능(API 라우트, 데이터 페칭이 있는 서버 컴포넌트 등)을 사용 중이라면, 클라이언트 측 대안이나 외부 API를 사용하도록 리팩토링해야 합니다.
에 모바일 전용 스크립트를 추가하세요 package.json:
{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"mobile": "bun run build && bunx cap sync",
"mobile:ios": "bun run mobile && bunx cap open ios",
"mobile:android": "bun run mobile && bunx cap open android"
}
}
다음 명령을 실행하여 정적 내보내기를 테스트하세요:
bun run build
다음이 표시되어야 합니다: out 프로젝트 루트에 있는 폴더입니다. Capacitor가 네이티브 앱에 번들링할 모든 정적 파일이 여기에 포함됩니다.
프로젝트에 Capacitor 8 추가하기
Next.js 앱을 네이티브 모바일 컨테이너로 패키징하려면 다음 단계를 따르세요:
- Capacitor core와 CLI를 설치하세요:
bun add @capacitor/core
bun add -D @capacitor/cli
- 자주 필요할 Capacitor 플러그인을 설치하세요:
bun add @capacitor/app @capacitor/keyboard @capacitor/splash-screen @capacitor/preferences
이 플러그인들은 다음과 같은 필수 기능을 제공합니다:
- @capacitor/app: 앱 라이프사이클 이벤트(전경/배경, URL) 처리
- @capacitor/keyboard: 모바일 키보드 동작 제어
- @capacitor/splash-screen: 네이티브 스플래시 화면 관리
- @capacitor/preferences: 키-값 데이터를 영구적으로 저장
- 프로젝트 정보로 Capacitor를 초기화하세요:
bunx cap init my-app com.example.myapp --web-dir out
교체 my-app 에 앱 이름과 com.example.myapp 에 앱 ID(역도메인 표기법)를 입력하세요.
- 을(를) 생성하거나 업데이트하세요
capacitor.config.ts파일에 올바른 구성을 추가하세요:
import type { CapacitorConfig } from '@capacitor/cli';
const config: CapacitorConfig = {
appId: 'com.example.myapp',
appName: 'my-app',
webDir: 'out',
plugins: {
SplashScreen: {
launchShowDuration: 2000,
launchAutoHide: true,
androidScaleType: 'CENTER_CROP',
showSpinner: false,
splashFullScreen: true,
splashImmersive: true,
},
},
};
export default config;
- 네이티브 플랫폼 설치:
bun add @capacitor/ios @capacitor/android
- 네이티브 플랫폼 폴더 추가:
bunx cap add ios
bunx cap add android
Capacitor 이(가) 생성합니다 ios 및 android 프로젝트 루트에 있으며 네이티브 프로젝트를 포함하는 폴더입니다.
Android 프로젝트를 빌드하려면 다음이 필요합니다: 디버깅하기. iOS의 경우 다음이 설치된 Mac이 필요합니다: iOS 프로파일링.
- 프로젝트 빌드 및 동기화:
bun run mobile
이 명령은 Next.js 프로젝트를 빌드하고 정적 파일을 네이티브 플랫폼과 동기화하는 커스텀 스크립트를 실행합니다.
네이티브 앱 빌드 및 배포
네이티브 모바일 앱을 빌드하고 배포하려면 다음 단계를 따르세요: iOS 앱을 개발하려면 다음이 필요합니다: iOS 프로파일링 가 설치되어 있어야 하며, Android 앱의 경우 디버깅하기 가 설치되어 있어야 합니다. 또한 앱을 스토어에 배포하려면 iOS는 Apple Developer Program에, Android는 Google Play Console에 등록해야 합니다.
- 네이티브 프로젝트를 여세요:
iOS의 경우:
bun run mobile:ios
Android의 경우:
bun run mobile:android
또는 Capacitor CLI로 직접 실행하세요:
bunx cap open ios
bunx cap open android
- 앱 빌드 및 실행:

-
Android Studio에서 프로젝트가 준비될 때까지 기다린 다음, “Run” 버튼을 클릭해 연결된 기기나 에뮬레이터에 앱을 배포하세요.

-
Xcode에서 실제 기기에 앱을 배포하려면 signing account를 설정해야 합니다. 처음이라면 Xcode의 안내에 따라 설정을 진행하세요 (Apple Developer Program 등록이 필요합니다). 설정이 완료되면 “Play” 버튼을 클릭해 연결된 기기에서 앱을 실행하세요.
축하합니다! Next.js 웹 앱을 모바일 기기에 성공적으로 배포했습니다.
Capacitor Live Reload
개발 중에는 live reloading을 활용해 변경 사항을 모바일 기기에서 즉시 확인할 수 있습니다. 이 기능을 사용하려면 다음 단계를 따르세요:
- 로컬 IP 주소를 확인하세요:
-
macOS에서는 터미널에서 다음 명령어를 실행하세요:
ipconfig getifaddr en0 -
Windows에서는 다음을 실행하세요:
ipconfig출력에서 IPv4 주소를 확인하세요.
- 다음 파일을
capacitor.config.ts개발 서버를 가리키도록 하려면:
import type { CapacitorConfig } from '@capacitor/cli';
const config: CapacitorConfig = {
appId: 'com.example.app',
appName: 'my-app',
webDir: 'out',
server: {
url: 'http://YOUR_IP_ADDRESS:3000',
cleartext: true,
},
};
export default config;
교체 YOUR_IP_ADDRESS 로컬 IP 주소로 (예: 192.168.1.100).
- 변경 사항을 네이티브 프로젝트에 적용하세요:
bunx cap copy
이 copy 이 명령은 전체 프로젝트를 업데이트하지 않고 web 폴더와 설정 변경 사항만 네이티브 프로젝트로 복사합니다.
- Android Studio 또는 Xcode를 사용하여 기기에서 앱을 다시 빌드하고 실행하세요.
이제 Next.js 앱을 수정할 때마다 모바일 앱이 자동으로 새로 고침되어 변경 사항이 반영됩니다.
참고: 새 플러그인을 설치하거나 네이티브 파일을 수정하면 네이티브 프로젝트를 다시 빌드해야 합니다. 라이브 리로드는 web code 변경 사항에만 적용되기 때문입니다.
Capacitor 플러그인 사용법
Capacitor 플러그인을 사용하면 Next.js 앱에서 네이티브 디바이스 기능에 접근할 수 있습니다. 사용 방법을 살펴보겠습니다 플러그인 공유하기 예시:
- Share 플러그인 설치:
bun add @capacitor/share
- 다음 파일을 업데이트하세요:
pages/index.jsShare 플러그인을 사용할 파일:
import Head from 'next/head';
import styles from '../styles/Home.module.css';
import { Share } from '@capacitor/share';
export default function Home() {
const share = async () => {
await Share.share({
title: 'Open Youtube',
text: 'Check new video on youtube',
url: 'https://www.youtube.com',
dialogTitle: 'Share with friends',
});
};
return (
<div className={styles.container}>
<Head>
<title>Create Next App</title>
<meta name="description" content="Generated by create next app" />
<link rel="icon" href="/favicon.ico" />
</Head>
<main className={styles.main}>
<h1 className={styles.title}>
Welcome to <a href="https://nextjs.org">Capgo!</a>
</h1>
<p className={styles.description}>
<h2>Cool channel</h2>
<button onClick={() => share()}>Share now!</button>
</p>
</main>
</div>
);
}
- 변경 사항을 네이티브 프로젝트와 동기화:
앞서 말했듯이, 새 플러그인을 설치할 때는 sync 작업을 수행한 뒤 기기에 앱을 다시 배포해야 합니다. 이렇게 하려면 다음 명령어를 실행하세요:
bun run mobile
또는 재빌드 없이 동기화만:
bunx cap sync
- 앱을 재빌드하고 디바이스에서 실행하세요.
이제 “Share now!” 버튼을 클릭하면 네이티브 공유 다이얼로그가 나타나며, 다른 앱과 콘텐츠를 공유할 수 있습니다.
저는 수년간 Ionic 크로스 플랫폼 애플리케이션을 빌드할 수 있지만, Next.js와 통합하는 방식은 hacky하고 이미 사용 중인 게 있다면 그만한 가치가 거의 없습니다. Tailwind CSS 4.
Next.js + Capacitor 앱에서 네이티브 모바일 느낌을 내려면 Konsta UI 같은 웹 전용 UI 키트 대신 Capgo 플러그인을 사용하세요:
- @capgo/capacitor-native-navigation — 네이티브 navbar, iOS의 Liquid Glass tab bar, Android의 블러 처리된 tab bar 스타일. Next.js router는 라우트 상태를 유지하고, 네이티브 chrome은 플러그인이 담당합니다.
- @capgo/capacitor-transitions — Ionic UI 없이 WebView 레이어에서 Ionic 스타일 페이지 전환과 iOS edge swipe-back을 제공합니다.
둘 다 설치하세요:
bun add @capgo/capacitor-native-navigation @capgo/capacitor-transitions
bunx cap sync
웹 콘텐츠가 native bar를 침범하지 않도록 CSS inset 모드로 native navigation을 설정하세요:
import { NativeNavigation } from '@capgo/capacitor-native-navigation';
await NativeNavigation.configure({
contentInsetMode: 'css',
animationDuration: 360,
glass: {
effect: 'liquidGlass',
},
});
Liquid Glass tab bar를 렌더링합니다 (iOS는 시스템이 렌더링하고, Android는 블러 처리된 WebView 배경을 사용합니다):
await NativeNavigation.setTabbar({
selectedId: 'home',
labelVisibilityMode: 'labeled',
icons: true,
colors: { dynamic: true },
tabs: [
{ id: 'home', title: 'Home', icon: { svg: '...' } },
{ id: 'settings', title: 'Settings', icon: { svg: '...' } },
],
});
await NativeNavigation.addListener('tabSelect', ({ id }) => {
router.push(`/${id}`);
});
앱 shell에 native page transitions를 추가하세요:
import '@capgo/capacitor-transitions';
import { initTransitions, setDirection, setupRouterOutlet } from '@capgo/capacitor-transitions/react';
initTransitions({ platform: 'auto' });
라우팅된 페이지를 cap-router-outlet, cap-page, 그리고 cap-content로 감싸고, 호출하세요 setDirection('forward') 또는 setDirection('back') 첫 번째 제출이 승인되기 router.push() 또는 router.back(). 네이티브 네비게이션이 해당 영역을 담당하는 경우 웹 헤더나 푸터를 중복 표시하지 마세요.
전체 가이드 보기: @capgo/capacitor-native-navigation 사용하기 및 @capgo/capacitor-transitions 사용하기.
Tailwind로 safe area 처리하기
Tailwind CSS에서 디바이스 safe area에는 다음을 사용하세요: @capgo/tailwind-capacitor (다음으로 게시됨: tailwind-capacitor npm). 다음을 제공합니다: safe-areas 유틸리티와 기타 Capacitor 친화적인 Tailwind 플러그인:
bun add -D tailwind-capacitor
다음에서 styles/globals.css:
@import 'tailwindcss';
@plugin "@capgo/tailwind-capacitor/platform";
@plugin "@capgo/tailwind-capacitor/safe-areas";
다음과 같은 유틸리티를 사용하세요: pt-safe, pb-safe, 그리고 px-safe 여기저기 흩뿌리는 대신 env(safe-area-inset-*) 수동으로. 이 프로젝트는 활발히 개발 중이므로 — Next.js 설정에 필요한 게 빠져 있다면, GitHub에 PR을 올려주세요..
iOS 레이아웃 이슈 해결 (Viewport, Safe Area, 가로 오버플로우)
iOS에서 콘텐츠가 잘리거나, 밀리거나, 가로 스크롤이 생긴다면, 더 추가하거나 overflow-x: hidden viewport 태그를 조정하는 것만으로는 보통 해결되지 않습니다. 다음 점검을 순서대로 진행하세요.
viewport meta 태그가 올바르게 적용되었는지 확인하세요
App Router (app/): export viewport 과 app/layout.tsx:
import type { Viewport } from 'next';
export const viewport: Viewport = {
width: 'device-width',
initialScale: 1,
viewportFit: 'cover',
};
Pages Router (pages/): viewport meta 태그를 다음 위치에 넣으세요. pages/_app.tsx, 아니라 _document.tsx (Next.js가 태그를 적용하지 않을 수 있습니다 _document.tsx viewport 동작에 대해 기대하는 방식대로).
iOS safe area는 루트 래퍼 한 곳에서만 처리하세요
단일 앱 셸을 생성하고 그곳에 safe area 패딩을 적용하세요. 여러 중첩된 컴포넌트에 각각 적용하지 마세요:
html,
body,
#__next {
width: 100%;
min-height: 100%;
margin: 0;
padding: 0;
overflow-x: hidden;
}
* {
box-sizing: border-box;
}
.app-shell {
min-height: 100dvh;
width: 100%;
padding-top: env(safe-area-inset-top);
padding-right: env(safe-area-inset-right);
padding-bottom: env(safe-area-inset-bottom);
padding-left: env(safe-area-inset-left);
}
모든 페이지 콘텐츠를 다음 안에 감싸세요: .app-shell. 헤더, 모달, 레이아웃 래퍼에 safe-area 패딩이 중복되면 UI가 잘리거나 너무 커 보일 수 있습니다.
사용하면 @capgo/tailwind-capacitor, 다음과 같은 유틸리티로 동일한 패딩을 표현할 수 있습니다. pt-safe pb-safe px-safe 해당 단일 셸에서.
Capacitor iOS를 설정하세요 contentInset 대상 never 먼저
다음에서 capacitor.config.ts, native inset을 비활성화하는 편이 좋고 CSS(또는 Native Navigation의 contentInsetMode: 'css')가 safe area를 제어하도록 하세요:
const config: CapacitorConfig = {
appId: 'com.example.myapp',
appName: 'my-app',
webDir: 'out',
ios: {
contentInset: 'never',
},
};
Capacitor의 자동 content inset과 CSS env(safe-area-inset-*) padding을 함께 쓰면 이중 여백이 생기는 흔한 원인입니다.
실제로 overflow되는 요소를 찾으세요
흔한 원인은 다음을 사용하는 요소입니다: 100vw, Tailwind w-screen, 고정 픽셀 너비 또는 큰 min-width.
Safari Web Inspector에서 다음을 실행하세요:
[...document.querySelectorAll('*')]
.filter(el => el.scrollWidth > document.documentElement.clientWidth)
.map(el => ({
el,
tag: el.tagName,
class: el.className,
scrollWidth: el.scrollWidth,
clientWidth: document.documentElement.clientWidth,
}));
Tailwind에서는 다음을 교체하세요: w-screen 포함 w-full 가능하면 사용하세요. 많은 가로 오버플로 문제는 100vw / w-screen중복된 safe-area 패딩이나 고정 너비 컨테이너 때문이며, viewport meta tag 자체 때문은 아닙니다.
성능 최적화
Next.js와 Capacitor 앱의 최적의 성능을 보장하려면 다음 모범 사례를 고려하세요:
- 사용하지 않는 의존성과 자산을 제거하여 앱 크기를 최소화하세요.
- 이미지 및 기타 미디어 파일을 최적화하여 로딩 시간을 줄이세요.
- 컴포넌트와 페이지에 지연 로딩을 구현하여 초기 로딩 성능을 개선하세요.
- Next.js의 서버 사이드 렌더링(SSR)을 활용하여 앱 로딩 속도와 검색 엔진 최적화(SEO)를 강화하세요.
- 웹뷰 캐싱 및 앱 번들링과 같은 Capacitor의 내장 최적화 기능을 활용하세요.
마치며
Capacitor 8을 사용하여 기존 Next.js 웹 애플리케이션을 네이티브 iOS 및 Android 앱으로 성공적으로 변환했습니다. 이제 웹 코드베이스가 모바일 기기에서 네이티브로 실행되며 디바이스 API에 접근할 수 있습니다.
달성한 내용:
- Next.js를 정적 내보내기로 설정
- 필수 플러그인과 함께 Capacitor 8 추가
- iOS 및 Android 시뮬레이터에 빌드 및 배포
- 개발용 라이브 리로드 활성화
- iOS 레이아웃 문제(뷰포트, 안전 영역, 오버플로) 수정
- Capgo 네이티브 내비게이션 및 전환으로 네이티브 느낌의 UI 추가
다음 단계:
- 설정하기 Capgo 앱 스토어 재제출 없이 OTA 업데이트를 하려면
- Camera, Geolocation, Push Notifications 같은 네이티브 플러그인을 더 추가하세요
- 프로덕션용 앱 아이콘 및 스플래시 스크린 설정
- App Store 및 Google Play 제출 준비
새 프로젝트를 시작하시나요? 확인해 보세요 Next.js 모바일 앱을 처음부터 만들기 단계별 가이드를 확인하세요.
리소스
- Next.js 문서
- @capgo/capacitor-native-navigation — Liquid Glass 탭 바 및 네이티브 크롬
- Capacitor 8 문서
- @capgo/capacitor-transitions — 네이티브 느낌의 페이지 전환
- @capgo/tailwind-capacitor — Capacitor용 Tailwind safe-area 유틸리티
- Capgo - Capacitor 앱용 Live Updates
Capgo로 더 나은 앱을 더 빠르게 빌드하는 방법을 알아보세요. 무료 계정 가입하기 만들어 보세요.
자주 묻는 질문
Next.js 앱을 모바일 앱으로 변환하는 방법은 무엇인가요?
next.config에서 output을 export로 설정하여 Next.js가 정적 사이트를 빌드하도록 하고, Capacitor와 iOS, Android 플랫폼을 추가한 뒤 webDir을 out 폴더로 지정하세요. 그런 다음 npx cap sync를 실행하고 네이티브 프로젝트를 엽니다. API 라우트와 같은 서버 전용 기능은 별도의 백엔드로 이동해야 합니다. 릴리스 후, Capgo Live Update 사용하기 스토어 재심사 없이 웹 변경 사항을 배포하세요.
Capacitor 8로 Next.js 앱을 iOS & Android로 변환하기 계속하기
사용 중이라면 Capacitor로 Next.js 앱을 iOS 및 Android로 전환하기 8 네이티브 플러그인 작업을 계획하려면, 이를 다음 항목과 연결하세요: Capgo 플러그인 디렉토리 Capgo Plugin Directory의 제품 워크플로우에 맞춰, Capgo의 Capacitor 플러그인 Capgo의 Capacitor 플러그인 구현 상세 내용에 대해서는, 플러그인 추가 또는 업데이트 플러그인 추가 또는 업데이트의 구현 세부 사항은, Ionic Enterprise 플러그인 대안 Ionic Enterprise 플러그인 대안의 제품 워크플로우와, Capgo Native Build Capgo Native Builds의 제품 워크플로우를 위해.