Skip to content

[FEAT] WTH-377: capacitor 앱 초기세팅#123

Open
woneeeee wants to merge 3 commits into
developfrom
feat/WTH-377-capacitor
Open

[FEAT] WTH-377: capacitor 앱 초기세팅#123
woneeeee wants to merge 3 commits into
developfrom
feat/WTH-377-capacitor

Conversation

@woneeeee
Copy link
Copy Markdown
Member

@woneeeee woneeeee commented May 27, 2026

✅ PR 유형

어떤 변경 사항이 있었나요?

  • 새로운 기능 추가
  • 버그 수정
  • 코드에 영향을 주지 않는 변경사항(오타 수정, 탭 사이즈 변경, 변수명 변경)
  • 코드 리팩토링
  • 주석 추가 및 수정
  • 문서 수정
  • 빌드 부분 혹은 패키지 매니저 수정
  • 파일 혹은 폴더명 수정
  • 파일 혹은 폴더 삭제

📌 관련 이슈번호

  • Closed #377

✅ Key Changes

Capacitor 앱 초기 세팅

  • @capacitor/core, @capacitor/cli, @capacitor/android, @capacitor/ios v8 설치
  • capacitor.config.ts 추가: appId: 'kr.weeth.client', appName: 'Weeth'
    • 기본 서버 URL: https://weeth.kr (production hosted 방식)
    • CAP_SERVER_URL 환경변수로 서버 URL 오버라이드 가능
    • 로컬 서버(localhost / 127.0.0.1 / 10.0.2.2) 감지 시 cleartext: true 자동 적용
  • Android / iOS 네이티브 프로젝트 생성 및 초기 설정 (스플래시, 아이콘, manifest 포함)

pnpm 스크립트 추가 (package.json)

스크립트 설명
pnpm cap:sync 웹 빌드 → 네이티브 동기화
pnpm cap:open:ios Xcode 열기
pnpm cap:open:android Android Studio 열기
pnpm cap:dev:ios localhost:3000 기준으로 iOS 실행
pnpm cap:dev:android 10.0.2.2:3000 기준으로 Android 에뮬레이터 실행
pnpm cap:prod:sync production URL 기준 동기화

개발용 로그인 페이지 (/dev-login)

  • WebView 환경에서 카카오 OAuth를 우회해 토큰을 수동으로 쿠키에 심는 서버 페이지
  • NODE_ENV === 'development' 또는 NEXT_PUBLIC_ENABLE_DEV_LOGIN=true 일 때만 활성화
  • 비활성 상태에서 접근 시 notFound() 반환 (production 노출 없음)
  • 입력 필드: accessToken, refreshToken, clubId, clubName, redirectPath

README 업데이트

  • Capacitor 로컬/프로덕션 개발 플로우 사용법 문서 추가

📸 스크린샷 or 실행영상

2026-05-27.9.31.04.mov

🎸 기타 사항 or 추가 코멘트

로컬 3000으로 개발하려면 토큰이랑 clubid, clubName이 필요한데 시뮬레이터에서는 넣을 수 없어서 따로 dev-login.page를 만들었습니당!! 그래서 실행하시면 위 화면이 뜨고 거기에 쿠키에서 가져와서 복붙하면 바로 넘어가집니당

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • Android 및 iOS 모바일 네이티브 앱 지원 추가
    • 개발 환경용 로그인 페이지 추가
  • 개선사항

    • 크로스플랫폼 모바일 애플리케이션 빌드 및 배포 환경 구성
    • 모바일 플랫폼별 개발 및 프로덕션 실행 워크플로우 추가

Review Change Stack

@woneeeee woneeeee requested review from JIN921, dalzzy and nabbang6 May 27, 2026 12:40
@woneeeee woneeeee self-assigned this May 27, 2026
@woneeeee woneeeee added the ⚙ Setting 개발 환경 세팅 label May 27, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 27, 2026

Warning

Review limit reached

@woneeeee, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 48 minutes and 1 second. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 32b471b3-e61d-4fa8-851c-6a3aa1284c9c

📥 Commits

Reviewing files that changed from the base of the PR and between 785e004 and 4879b0c.

📒 Files selected for processing (1)
  • src/hooks/attendance/useAttendanceSSE.ts
📝 Walkthrough

전체 요약

이 PR은 Capacitor 프레임워크를 사용하여 웹 기반 Next.js 프로젝트를 iOS 및 Android 네이티브 앱으로 변환하기 위한 전체 플랫폼 설정을 추가합니다. Android Gradle 빌드 시스템, iOS Xcode 프로젝트 구조, 공유 Capacitor 설정, 그리고 개발 환경용 토큰 기반 로그인 페이지를 포함합니다.

변경 사항

Capacitor 모바일 플랫폼 및 개발 기능

레이어 / 파일(들) 요약
Capacitor 공통 설정 및 문서
README.md, capacitor.config.ts, package.json
Capacitor 기본 설정, 환경별 서버 URL 구성, iOS/Android 개발 및 배포 스크립트, 프로젝트 문서를 통합합니다.
Android Gradle 빌드 설정
android/build.gradle, android/app/build.gradle, android/settings.gradle, android/gradle*, android/variables.gradle
Android 플러그인, 저장소, Capacitor/AndroidX 의존성, 빌드 버전 설정, Gradle 래퍼 및 JVM 옵션을 정의합니다.
Android 매니페스트 및 액티비티
android/app/src/main/AndroidManifest.xml, android/app/src/main/java/kr/weeth/client/MainActivity.java
런처 액티비티, 단일 작업 모드, FileProvider 등록, 파일 URI 권한, 인터넷 접근 권한을 선언합니다.
Android 리소스 및 테스트
android/app/src/main/res/*, android/app/src/*/java/*/ExampleTest*.java
아이콘, 드로어블, 레이아웃, 스타일, 문자열 리소스, 파일 경로 설정, 단위 및 계측 테스트를 구성합니다.
iOS Xcode 프로젝트 구성
ios/App/App.xcodeproj/project.pbxproj, ios/App/App.xcodeproj/project.xcworkspace/...
Swift 패키지 의존성, 빌드 단계, 타깃 설정, Debug/Release 빌드 구성을 정의합니다.
iOS 앱 델리게이트 및 설정
ios/App/App/AppDelegate.swift, ios/App/App/Info.plist
앱 라이프사이클 콜백, URL 스킴 및 Universal Links 처리, 번들 메타데이터를 구성합니다.
iOS 리소스 및 Swift 패키지
ios/App/App/Assets.xcassets/*, ios/App/App/Base.lproj/*.storyboard, ios/App/CapApp-SPM/*, ios/debug.xcconfig
앱 아이콘, 스플래시 이미지, 스토리보드, 에셋 카탈로그, Capacitor SPM 패키지, 디버그 플래그를 정의합니다.
개발용 로그인 페이지
src/app/dev-login/page.tsx
개발 환경 전용 로그인 폼으로, 액세스/리프레시 토큰을 쿠키에 저장하고 조건부 리다이렉트를 수행합니다.

예상 코드 리뷰 노력

🎯 4 (Complex) | ⏱️ ~60 분

제안 라벨

✨ Feature

제안 리뷰어

  • nabbang6
  • dalzzy
  • JIN921

시 (한 마리 토끼의 축하시)

🐰 모바일 앱이 드디어 태어났네요,
Capacitor가 다리를 만들고 gradle이 성을 지었고,
iOS 성벽 위에 storyboard 망루가 서있네.
개발 로그인으로 마지막 문을 열면,
안드로이드와 iOS 두 나라가 하나의 왕국이 되는군요! 🎉

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목이 주요 변경사항인 Capacitor 앱 초기 세팅을 명확하게 요약하고 있으며, 이슈 번호(WTH-377)도 포함되어 있습니다.
Description check ✅ Passed PR 설명이 필수 섹션(PR 유형, 이슈번호, Key Changes)을 모두 포함하고 있으며, 변경사항이 상세하고 명확하게 문서화되어 있습니다.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/WTH-377-capacitor

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown

PR 테스트 결과

Jest: 통과

🎉 모든 테스트를 통과했습니다!

@github-actions
Copy link
Copy Markdown

PR 검증 결과

TypeScript: 통과
ESLint: 통과
Prettier: 실패
Build: 통과

⚠️ 일부 검증에 실패했습니다. 확인 후 수정해주세요.

@github-actions
Copy link
Copy Markdown

구현한 기능 Preview: https://weeth-a7rbs7heb-weethsite-4975s-projects.vercel.app

@github-actions
Copy link
Copy Markdown

PR 테스트 결과

Jest: 통과

🎉 모든 테스트를 통과했습니다!

@github-actions
Copy link
Copy Markdown

PR 검증 결과

TypeScript: 통과
ESLint: 통과
Prettier: 통과
Build: 통과

🎉 모든 검증을 통과했습니다!

@github-actions
Copy link
Copy Markdown

구현한 기능 Preview: https://weeth-8jcge4h6w-weethsite-4975s-projects.vercel.app

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@android/app/src/androidTest/java/com/getcapacitor/myapp/ExampleInstrumentedTest.java`:
- Line 24: The test assertion in ExampleInstrumentedTest (assertEquals call)
uses the old expected package string "com.getcapacitor.app" and should be
updated to match the current app ID; change the expected value in the
assertEquals inside the test method (the assertEquals("com.getcapacitor.app",
appContext.getPackageName()) call) to "kr.weeth.client" so the assertion
compares the correct package name.

In `@android/app/src/main/AndroidManifest.xml`:
- Line 5: AndroidManifest.xml currently enables app data backup via the
manifest's android:allowBackup="true"; change this attribute in the <manifest>
element to android:allowBackup="false" (or remove it to rely on platform
default) so the app does not allow automatic backup/restore of sensitive data in
production; locate the android:allowBackup attribute in the manifest tag and
update its value accordingly.

In `@android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml`:
- Line 4: ic_launcher_round.xml의 foreground 속성이 현재
`@mipmap/ic_launcher_foreground로` 되어 있어 벡터/foreground 리소스 타입과 불일치할 수 있으니
ic_launcher_round.xml의 foreground android:drawable 참조를
`@drawable/ic_launcher_foreground로` 변경해 동일한 drawable 리소스 타입을 사용하도록 수정하세요.

In `@android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml`:
- Line 4: The foreground element currently references the mipmap resource
"`@mipmap/ic_launcher_foreground`", but this PR uses a drawable-based foreground
which causes resource resolution failures; update the foreground's
android:drawable reference from "`@mipmap/ic_launcher_foreground`" to
"`@drawable/ic_launcher_foreground`" in the ic_launcher XML (look for the
foreground element and its android:drawable attribute) so the launcher XML
points to the drawable resource type used by the project.

In `@android/app/src/main/res/xml/file_paths.xml`:
- Line 3: The FileProvider config currently exposes the whole external storage
via the external-path entry (external-path name="my_images" path="."), which is
too broad; change that entry to use external-files-path (keep the same name
"my_images") and narrow the path to the app-specific directory (e.g., remove "."
and rely on external-files-path default or specify a subfolder) so FileProvider
only grants access to app external files rather than all external storage.

In `@android/app/src/test/java/com/getcapacitor/myapp/ExampleUnitTest.java`:
- Around line 1-18: The test file's package declaration does not match the app's
actual package (class ExampleUnitTest / method addition_isCorrect), causing
package mismatch errors; fix by either changing the package declaration at the
top of this file from com.getcapacitor.myapp to kr.weeth.client so the test
class resides in the correct package, or if the template test is not needed,
remove the ExampleUnitTest.java file entirely to avoid the mismatch.

In `@src/app/dev-login/page.tsx`:
- Around line 14-25: The current isDevLoginEnabled() uses
NEXT_PUBLIC_ENABLE_DEV_LOGIN which can enable dev-login in production; change it
to only allow development or a server-only flag by replacing
NEXT_PUBLIC_ENABLE_DEV_LOGIN with process.env.ENABLE_DEV_LOGIN and keep the
NODE_ENV==='development' check, i.e., update isDevLoginEnabled() to check
process.env.NODE_ENV === 'development' || process.env.ENABLE_DEV_LOGIN ===
'true'; remove any usage of NEXT_PUBLIC_ENABLE_DEV_LOGIN so the flag is not
exposed to the client and ensure devLoginAction still calls isDevLoginEnabled()
to gate the server action.
- Around line 68-136: The form controls in the Dev Login form (the <form
action={devLoginAction}>, the textarea elements named "accessToken" and
"refreshToken", the input elements "clubId", "clubName", "redirectPath", and the
submit <button>) use hardcoded Tailwind classes and nonstandard typography
tokens (e.g., typo-heading1) and must be refactored to use cva-based shared
variants and design tokens; extract a shared cva factory (e.g., createInputClass
/ createTextareaClass / createButtonClass or a single formControl cva) that
exposes variants for size/state and apply design token classes (typo-h*,
bg-button-primary, p-100~500, gap-100~400, etc.) instead of inline classes, then
replace the className strings on those controls with the cva-generated
classNames and update any spacing/typography tokens in the surrounding elements
to the prescribed token names.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 88b43cd2-a106-41aa-9297-ca0115e17863

📥 Commits

Reviewing files that changed from the base of the PR and between 7d82f78 and 785e004.

⛔ Files ignored due to path filters (32)
  • android/app/src/main/res/drawable-land-hdpi/splash.png is excluded by !**/*.png
  • android/app/src/main/res/drawable-land-mdpi/splash.png is excluded by !**/*.png
  • android/app/src/main/res/drawable-land-xhdpi/splash.png is excluded by !**/*.png
  • android/app/src/main/res/drawable-land-xxhdpi/splash.png is excluded by !**/*.png
  • android/app/src/main/res/drawable-land-xxxhdpi/splash.png is excluded by !**/*.png
  • android/app/src/main/res/drawable-port-hdpi/splash.png is excluded by !**/*.png
  • android/app/src/main/res/drawable-port-mdpi/splash.png is excluded by !**/*.png
  • android/app/src/main/res/drawable-port-xhdpi/splash.png is excluded by !**/*.png
  • android/app/src/main/res/drawable-port-xxhdpi/splash.png is excluded by !**/*.png
  • android/app/src/main/res/drawable-port-xxxhdpi/splash.png is excluded by !**/*.png
  • android/app/src/main/res/drawable/splash.png is excluded by !**/*.png
  • android/app/src/main/res/mipmap-hdpi/ic_launcher.png is excluded by !**/*.png
  • android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png is excluded by !**/*.png
  • android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png is excluded by !**/*.png
  • android/app/src/main/res/mipmap-mdpi/ic_launcher.png is excluded by !**/*.png
  • android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png is excluded by !**/*.png
  • android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png is excluded by !**/*.png
  • android/app/src/main/res/mipmap-xhdpi/ic_launcher.png is excluded by !**/*.png
  • android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png is excluded by !**/*.png
  • android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png is excluded by !**/*.png
  • android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png is excluded by !**/*.png
  • android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png is excluded by !**/*.png
  • android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png is excluded by !**/*.png
  • android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png is excluded by !**/*.png
  • android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png is excluded by !**/*.png
  • android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png is excluded by !**/*.png
  • android/gradle/wrapper/gradle-wrapper.jar is excluded by !**/*.jar
  • ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png is excluded by !**/*.png
  • ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-1.png is excluded by !**/*.png
  • ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-2.png is excluded by !**/*.png
  • ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732.png is excluded by !**/*.png
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (45)
  • README.md
  • android/.gitignore
  • android/app/.gitignore
  • android/app/build.gradle
  • android/app/capacitor.build.gradle
  • android/app/proguard-rules.pro
  • android/app/src/androidTest/java/com/getcapacitor/myapp/ExampleInstrumentedTest.java
  • android/app/src/main/AndroidManifest.xml
  • android/app/src/main/java/kr/weeth/client/MainActivity.java
  • android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
  • android/app/src/main/res/drawable/ic_launcher_background.xml
  • android/app/src/main/res/layout/activity_main.xml
  • android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
  • android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
  • android/app/src/main/res/values/ic_launcher_background.xml
  • android/app/src/main/res/values/strings.xml
  • android/app/src/main/res/values/styles.xml
  • android/app/src/main/res/xml/file_paths.xml
  • android/app/src/test/java/com/getcapacitor/myapp/ExampleUnitTest.java
  • android/build.gradle
  • android/capacitor.settings.gradle
  • android/gradle.properties
  • android/gradle/wrapper/gradle-wrapper.properties
  • android/gradlew
  • android/gradlew.bat
  • android/settings.gradle
  • android/variables.gradle
  • capacitor.config.ts
  • ios/.gitignore
  • ios/App/App.xcodeproj/project.pbxproj
  • ios/App/App.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
  • ios/App/App/AppDelegate.swift
  • ios/App/App/Assets.xcassets/AppIcon.appiconset/Contents.json
  • ios/App/App/Assets.xcassets/Contents.json
  • ios/App/App/Assets.xcassets/Splash.imageset/Contents.json
  • ios/App/App/Base.lproj/LaunchScreen.storyboard
  • ios/App/App/Base.lproj/Main.storyboard
  • ios/App/App/Info.plist
  • ios/App/CapApp-SPM/.gitignore
  • ios/App/CapApp-SPM/Package.swift
  • ios/App/CapApp-SPM/README.md
  • ios/App/CapApp-SPM/Sources/CapApp-SPM/CapApp-SPM.swift
  • ios/debug.xcconfig
  • package.json
  • src/app/dev-login/page.tsx

// Context of the app under test.
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();

assertEquals("com.getcapacitor.app", appContext.getPackageName());
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

패키지명 assertion 값을 현재 앱 식별자와 맞춰주세요.

Line 24의 기대값(com.getcapacitor.app)이 현재 설정된 앱 식별자(kr.weeth.client)와 달라 테스트가 실패합니다.

수정 제안
-        assertEquals("com.getcapacitor.app", appContext.getPackageName());
+        assertEquals("kr.weeth.client", appContext.getPackageName());
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
assertEquals("com.getcapacitor.app", appContext.getPackageName());
assertEquals("kr.weeth.client", appContext.getPackageName());
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@android/app/src/androidTest/java/com/getcapacitor/myapp/ExampleInstrumentedTest.java`
at line 24, The test assertion in ExampleInstrumentedTest (assertEquals call)
uses the old expected package string "com.getcapacitor.app" and should be
updated to match the current app ID; change the expected value in the
assertEquals inside the test method (the assertEquals("com.getcapacitor.app",
appContext.getPackageName()) call) to "kr.weeth.client" so the assertion
compares the correct package name.

<manifest xmlns:android="http://schemas.android.com/apk/res/android">

<application
android:allowBackup="true"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

인증 데이터 앱에서 자동 백업 허용 설정은 재검토가 필요합니다.

android:allowBackup="true"는 앱 데이터(예: WebView 저장 데이터)의 백업/복원 경로를 열 수 있어 보안/프라이버시 위험을 키울 수 있습니다. 프로덕션 기본값을 false로 두는 쪽이 안전합니다.

🔧 제안 수정안
-        android:allowBackup="true"
+        android:allowBackup="false"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
android:allowBackup="true"
android:allowBackup="false"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@android/app/src/main/AndroidManifest.xml` at line 5, AndroidManifest.xml
currently enables app data backup via the manifest's android:allowBackup="true";
change this attribute in the <manifest> element to android:allowBackup="false"
(or remove it to rely on platform default) so the app does not allow automatic
backup/restore of sensitive data in production; locate the android:allowBackup
attribute in the manifest tag and update its value accordingly.

<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

라운드 아이콘도 foreground 리소스 타입을 맞춰주세요.

Line 4의 @mipmap/ic_launcher_foreground는 foreground 벡터 리소스 타입과 불일치할 가능성이 큽니다. @drawable 참조로 통일하는 게 안전합니다.

수정 제안
-    <foreground android:drawable="`@mipmap/ic_launcher_foreground`"/>
+    <foreground android:drawable="`@drawable/ic_launcher_foreground`"/>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
<foreground android:drawable="`@drawable/ic_launcher_foreground`"/>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml` at line 4,
ic_launcher_round.xml의 foreground 속성이 현재 `@mipmap/ic_launcher_foreground로` 되어 있어
벡터/foreground 리소스 타입과 불일치할 수 있으니 ic_launcher_round.xml의 foreground
android:drawable 참조를 `@drawable/ic_launcher_foreground로` 변경해 동일한 drawable 리소스 타입을
사용하도록 수정하세요.

<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

ic_launcher_foreground 리소스 타입 참조를 수정해주세요.

Line 4에서 @mipmap/ic_launcher_foreground를 참조하고 있는데, 이 PR의 리소스 구성은 drawable 기반 foreground를 사용합니다. 빌드 시 리소스 해석 실패로 이어질 수 있습니다.

수정 제안
-    <foreground android:drawable="`@mipmap/ic_launcher_foreground`"/>
+    <foreground android:drawable="`@drawable/ic_launcher_foreground`"/>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml` at line 4, The
foreground element currently references the mipmap resource
"`@mipmap/ic_launcher_foreground`", but this PR uses a drawable-based foreground
which causes resource resolution failures; update the foreground's
android:drawable reference from "`@mipmap/ic_launcher_foreground`" to
"`@drawable/ic_launcher_foreground`" in the ic_launcher XML (look for the
foreground element and its android:drawable attribute) so the launcher XML
points to the drawable resource type used by the project.

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="my_images" path="." />
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

FileProvider 외부 경로 범위를 축소해주세요.

Line 3의 external-path path="."는 외부 저장소 전체를 포괄할 수 있어 권한 위임 시 노출 범위가 과도합니다. 앱 전용 경로(external-files-path)로 제한하는 구성이 더 안전합니다.

수정 예시
-    <external-path name="my_images" path="." />
+    <external-files-path name="my_images" path="Pictures/" />
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<external-path name="my_images" path="." />
<external-files-path name="my_images" path="Pictures/" />
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@android/app/src/main/res/xml/file_paths.xml` at line 3, The FileProvider
config currently exposes the whole external storage via the external-path entry
(external-path name="my_images" path="."), which is too broad; change that entry
to use external-files-path (keep the same name "my_images") and narrow the path
to the app-specific directory (e.g., remove "." and rely on external-files-path
default or specify a subfolder) so FileProvider only grants access to app
external files rather than all external storage.

Comment on lines +1 to +18
package com.getcapacitor.myapp;

import static org.junit.Assert.*;

import org.junit.Test;

/**
* Example local unit test, which will execute on the development machine (host).
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {

@Test
public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

패키지 이름이 실제 앱 패키지와 불일치합니다.

테스트 파일의 패키지 이름이 com.getcapacitor.myapp로 되어 있으나, 실제 앱의 패키지는 kr.weeth.client입니다 (capacitor.config.ts의 appId 참조). 이는 Capacitor 템플릿 기본 코드가 업데이트되지 않은 것으로 보입니다.

다음 중 하나를 선택해주세요:

  1. 실제 앱 코드를 테스트할 예정이라면 패키지 이름을 kr.weeth.client로 수정
  2. 템플릿 예제가 필요 없다면 이 파일 삭제
🔧 제안: 패키지 이름 수정
-package com.getcapacitor.myapp;
+package kr.weeth.client;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
package com.getcapacitor.myapp;
import static org.junit.Assert.*;
import org.junit.Test;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
}
}
package kr.weeth.client;
import static org.junit.Assert.*;
import org.junit.Test;
/**
* Example local unit test, which will execute on the development machine (host).
*
* `@see` <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
`@Test`
public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@android/app/src/test/java/com/getcapacitor/myapp/ExampleUnitTest.java` around
lines 1 - 18, The test file's package declaration does not match the app's
actual package (class ExampleUnitTest / method addition_isCorrect), causing
package mismatch errors; fix by either changing the package declaration at the
top of this file from com.getcapacitor.myapp to kr.weeth.client so the test
class resides in the correct package, or if the template test is not needed,
remove the ExampleUnitTest.java file entirely to avoid the mismatch.

Comment on lines +14 to +25
function isDevLoginEnabled() {
return (
process.env.NODE_ENV === 'development' || process.env.NEXT_PUBLIC_ENABLE_DEV_LOGIN === 'true'
);
}

async function devLoginAction(formData: FormData) {
'use server';

if (!isDevLoginEnabled()) {
notFound();
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

운영 환경에서 dev-login이 활성화될 수 있는 조건을 제거하세요.

Line 16 조건 때문에 NEXT_PUBLIC_ENABLE_DEV_LOGIN=true 오설정 시 production에서도 토큰 주입 페이지/액션이 열립니다. 운영에서는 무조건 비활성화하고, 필요 시 서버 전용 env(ENABLE_DEV_LOGIN)만 허용하는 게 안전합니다.

🔧 제안 수정안
 function isDevLoginEnabled() {
-  return (
-    process.env.NODE_ENV === 'development' || process.env.NEXT_PUBLIC_ENABLE_DEV_LOGIN === 'true'
-  );
+  if (process.env.NODE_ENV === 'production') return false;
+  return (
+    process.env.NODE_ENV === 'development' ||
+    process.env.ENABLE_DEV_LOGIN === 'true'
+  );
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function isDevLoginEnabled() {
return (
process.env.NODE_ENV === 'development' || process.env.NEXT_PUBLIC_ENABLE_DEV_LOGIN === 'true'
);
}
async function devLoginAction(formData: FormData) {
'use server';
if (!isDevLoginEnabled()) {
notFound();
}
function isDevLoginEnabled() {
if (process.env.NODE_ENV === 'production') return false;
return (
process.env.NODE_ENV === 'development' ||
process.env.ENABLE_DEV_LOGIN === 'true'
);
}
async function devLoginAction(formData: FormData) {
'use server';
if (!isDevLoginEnabled()) {
notFound();
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/dev-login/page.tsx` around lines 14 - 25, The current
isDevLoginEnabled() uses NEXT_PUBLIC_ENABLE_DEV_LOGIN which can enable dev-login
in production; change it to only allow development or a server-only flag by
replacing NEXT_PUBLIC_ENABLE_DEV_LOGIN with process.env.ENABLE_DEV_LOGIN and
keep the NODE_ENV==='development' check, i.e., update isDevLoginEnabled() to
check process.env.NODE_ENV === 'development' || process.env.ENABLE_DEV_LOGIN ===
'true'; remove any usage of NEXT_PUBLIC_ENABLE_DEV_LOGIN so the flag is not
exposed to the client and ensure devLoginAction still calls isDevLoginEnabled()
to gate the server action.

Comment on lines +68 to +136
<main className="mx-auto flex min-h-dvh w-full max-w-[520px] flex-col justify-center px-6 py-10">
<div className="space-y-3">
<p className="typo-body2 text-text-alternative">Local app development</p>
<h1 className="typo-heading1 text-text-normal">Dev Login</h1>
<p className="typo-body2 text-text-alternative">
카카오 로그인을 우회해서 로컬 WebView에 개발용 토큰을 심습니다.
</p>
</div>

<form action={devLoginAction} className="mt-8 space-y-5">
<label className="block space-y-2">
<span className="typo-body2 text-text-normal">Access token</span>
<textarea
name="accessToken"
required
rows={4}
className="border-line-normal bg-background-normal text-text-normal focus:border-primary-normal w-full resize-y rounded-lg border px-4 py-3 text-sm outline-none"
placeholder="accessToken"
/>
</label>

<label className="block space-y-2">
<span className="typo-body2 text-text-normal">Refresh token</span>
<textarea
name="refreshToken"
required
rows={4}
className="border-line-normal bg-background-normal text-text-normal focus:border-primary-normal w-full resize-y rounded-lg border px-4 py-3 text-sm outline-none"
placeholder="refreshToken"
/>
</label>

<label className="block space-y-2">
<span className="typo-body2 text-text-normal">Club ID</span>
<input
name="clubId"
className="border-line-normal bg-background-normal text-text-normal focus:border-primary-normal w-full rounded-lg border px-4 py-3 text-sm outline-none"
placeholder="예: club id"
/>
</label>

<label className="block space-y-2">
<span className="typo-body2 text-text-normal">Club name</span>
<input
name="clubName"
className="border-line-normal bg-background-normal text-text-normal focus:border-primary-normal w-full rounded-lg border px-4 py-3 text-sm outline-none"
placeholder="예: Weeth"
/>
</label>

<label className="block space-y-2">
<span className="typo-body2 text-text-normal">Redirect path</span>
<input
name="redirectPath"
className="border-line-normal bg-background-normal text-text-normal focus:border-primary-normal w-full rounded-lg border px-4 py-3 text-sm outline-none"
placeholder="비워두면 /{clubId}/home 또는 /hub"
/>
</label>

{error === 'missing-token' && (
<p className="typo-caption1 text-status-negative">
access token과 refresh token을 모두 입력해야 합니다.
</p>
)}

<button
type="submit"
className="bg-primary-normal text-static-white h-12 w-full rounded-lg text-base font-semibold"
>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

폼 스타일을 토큰 기반 + cva로 정리해 주세요.

현재 구간은 하드코딩 유틸/비표준 타이포 토큰(typo-heading1)과 중복 class가 섞여 있어 가이드와 어긋납니다. input/textarea/button 공통 스타일을 cva로 추출하고, typo-h*, bg-button-primary, p-100~500, gap-100~400 등 지정 토큰으로 맞춰주세요.

♻️ 적용 예시(요지)
+import { cva } from 'class-variance-authority';
+import { cn } from '`@/lib/cn`';
+
+const fieldClass = cva(
+  'w-full rounded-lg border border-line-normal bg-container-neutral text-text-normal p-300 typo-body2 outline-hidden focus:border-primary-normal'
+);
+
+const submitButtonClass = cva(
+  'w-full bg-button-primary text-text-inverse typo-button1 p-300'
+);

-<h1 className="typo-heading1 text-text-normal">Dev Login</h1>
+<h1 className="typo-h1 text-text-strong">Dev Login</h1>

-<input className="border-line-normal bg-background-normal ... px-4 py-3 text-sm ..."/>
+<input className={cn(fieldClass())} />

-<button className="bg-primary-normal text-static-white h-12 w-full rounded-lg text-base font-semibold">
+<button className={cn(submitButtonClass())}>

As per coding guidelines **/*.{ts,tsx,css}: Use Tailwind CSS v4 with class-variance-authority (cva) for styling, Always use design token classes first; no hardcoded values, Use typography token classes: typo-h1~h3 ..., Use spacing token classes: p-100~500, gap-100~400.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/dev-login/page.tsx` around lines 68 - 136, The form controls in the
Dev Login form (the <form action={devLoginAction}>, the textarea elements named
"accessToken" and "refreshToken", the input elements "clubId", "clubName",
"redirectPath", and the submit <button>) use hardcoded Tailwind classes and
nonstandard typography tokens (e.g., typo-heading1) and must be refactored to
use cva-based shared variants and design tokens; extract a shared cva factory
(e.g., createInputClass / createTextareaClass / createButtonClass or a single
formControl cva) that exposes variants for size/state and apply design token
classes (typo-h*, bg-button-primary, p-100~500, gap-100~400, etc.) instead of
inline classes, then replace the className strings on those controls with the
cva-generated classNames and update any spacing/typography tokens in the
surrounding elements to the prescribed token names.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

⚙ Setting 개발 환경 세팅

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant