Skip to content

[NO-ISSUE] SwiftLint strict 적용 및 lint/format CI 정비#72

Merged
gnoes-ios merged 3 commits into
developfrom
chore/lint-format-strict
Jun 7, 2026
Merged

[NO-ISSUE] SwiftLint strict 적용 및 lint/format CI 정비#72
gnoes-ios merged 3 commits into
developfrom
chore/lint-format-strict

Conversation

@gnoes-ios

@gnoes-ios gnoes-ios commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

📌 변경 요약

SwiftLint·SwiftFormat 설정을 정렬하고, PR CI에서 strict lint를 강제합니다.
기존 위반을 전부 해소했으며, 로컬 Xcode 빌드는 lint 실패로 막지 않습니다.

📌 변경 내용

인프라·CI

  • .swiftlint.yml: trailing_comma 비활성(SwiftFormat과 충돌 해소), file_length 비활성
  • .github/workflows/verify.yml 신규: lint(SwiftFormat + SwiftLint strict) → buildtest
  • docs/harness/scripts/swiftlint-strict.sh 신규, verify.sh 연동
  • docs/harness/scripts/prepare-ci-config.sh 신규 (CI secrets → Config 복원)
  • docs/harness/verification.md, CLAUDE.md(mise trust/install) 업데이트
  • souzip-commit 스킬: 로컬 전용 파일 커밋 제외 규칙 추가

Lint 정책

  • PR CI: warning/error 모두 fail (--strict)
  • 로컬 Xcode: SwiftLint 미실행, 빌드는 그대로
  • SwiftFormat: 기존 Tuist pre-build 유지 + CI에서 전체 lint

코드 정리

  • SwiftLint error/warning 전부 해소 (identifier_name, complexity, body length 등)
  • DiscoveryView / RecommendView: CellRegistration struct + helper 분리
  • SouvenirFormViewModel: extension 4파일로 분리, type_body_length disable 제거
  • RecommendSectionInput 제거
  • DSNavigationBar.Style.trailingSettings (탭 루트 마이페이지용 settings only)

CI secrets (build/test job)

  • SOUZIP_DEBUG_XCCONFIG, SOUZIP_RELEASE_XCCONFIG
  • SOUZIP_GOOGLE_SERVICE_DEBUG, SOUZIP_GOOGLE_SERVICE_RELEASE
  • 미설정 시 lint는 통과, build job만 실패

📌 기타 참고 사항

검증

  • docs/harness/scripts/verify.sh plan 통과
  • SwiftLint strict 0 violations
  • SwiftFormat lint 통과
  • Tuist generate + build 수집-Debug 통과

의도적 예외 (후속 optional)

  • SouvenirDetailView, LocationSearchResultView: type_body_length disable 유지
  • LayoutConstants: nesting disable 2곳

develop 겹침

  • base commit: fb552f4
  • base_commit..origin/develop: 변경 없음
  • 겹치는 파일: 없음

Jira

  • [NO-ISSUE]

Note

Medium Risk
CI가 develop PR에서 strict lint·Tuist build/test를 강제하며, Config secrets 미설정 시 build job만 실패합니다. SouvenirFormViewModel 분리는 구조 변경이 크지만 동작은 extension으로 이전한 수준입니다.

Overview
SwiftLint strictPR CI 검증을 맞추기 위한 인프라·설정 변경이 핵심입니다. .swiftlint.yml에서 trailing_comma·file_length를 끄고, verify.yml로 develop PR/push 시 lint → build → test 파이프라인을 추가합니다. swiftlint-strict.sh, prepare-ci-config.sh(secrets로 Config 복원), verify.sh·harness 문서·souzip-commit 스킬(로컬 전용 경로 커밋 금지)도 같이 갱신됩니다.

기존 위반을 없애기 위한 리팩터·스타일 정리가 넓게 들어갑니다. DiscoveryView/RecommendView는 셀 등록·dequeue·섹션 레이아웃을 helper로 쪼갰고, SouvenirFormViewModel은 Actions·Submit·ImageProcessing·Supporting extension으로 나눴습니다. 폼 UI는 튜플 대신 SouvenirFormPhotoRenderLine 등 명시 타입을 쓰고, DSNavigationBar.Style.Settings.trailingSettings로 바뀌며 마이페이지 탭 루트에 맞춥니다. RecommendStatemakeSectionModels는 인자 없이 state를 읽도록 단순화됩니다.

동작 변경은 거의 없고 로그인 메서드명(performLogin), GlobeViewModel 업로드 탭 추출, 로딩 인디케이터 ternary 제거 등은 lint·가독성용입니다. SouvenirDetailView·LocationSearchResultViewtype_body_length disable을 유지합니다.

Reviewed by Cursor Bugbot for commit d546aea. Configure here.

@coderabbitai

coderabbitai Bot commented Jun 6, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

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

More reviews will be available in 36 minutes and 54 seconds. 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f8a73805-c024-4809-acf1-3a5eaba1b1c9

📥 Commits

Reviewing files that changed from the base of the PR and between d546aea and f579193.

📒 Files selected for processing (1)
  • docs/harness/skills.md
📝 Walkthrough

워크스루

이 PR은 소유물 폼 생성/편집 기능 구현과 CI/CD 검증 인프라 통합, 그리고 화면 리팩토링을 결합한 대규모 변경입니다. 뷰모델 액션 처리를 헬퍼 메서드로 분해하고, 이미지 처리와 제출 흐름을 확장 모듈로 추가했으며, GitHub Actions strict 린트 검증을 도입했습니다.

변경 사항

소유물 폼 VM 분해 및 이미지 처리

계층 / 파일(들) 요약
뷰모델 구조 리팩토링 및 액션 처리
Projects/Presentation/Sources/Scene/Souvernir/Form/Base/SouvenirFormViewModel.swift, SouvenirFormViewModel+Actions.swift
private 제어를 모듈 내부 접근으로 완화하고, 닫기·사진·이름·위치·주소·가격·통화·목적·카테고리·설명·제출 등 각 액션을 전용 헬퍼 메서드로 위임했습니다. handleAction switch가 각 케이스를 해당 핸들러 호출로 정리됨.
이미지 변환 및 리사이징
SouvenirFormViewModel+ImageProcessing.swift
convertPhotosToData로 LocalPhoto 배열의 파일 유효성을 검사하고, resizeImageFromFile로 픽셀 크기 기반 리사이징 후 JPEG 변환을 수행합니다. ImageProcessingError로 각 실패 경로를 타입화했습니다.
제출 흐름 및 보조 메서드
SouvenirFormViewModel+Submit.swift, SouvenirFormViewModel+Supporting.swift
handleSubmit → makeSubmitInput → handleCreate/Update 경로로 생성/수정을 처리하고, 국가 통화 조회, 주소 해석(세대 기반), 사진/가격/설명 입력 및 업로드 이벤트 중복 방지를 구현했습니다.

소유물 폼 뷰 및 컨트롤러 렌더 데이터 구조화

계층 / 파일(들) 요약
렌더 라인 구조체 및 뷰 메서드 업데이트
Projects/Presentation/Sources/Scene/Souvernir/Form/Base/SouvenirFormView.swift
SouvenirFormPhotoRenderLine(모드, 사진, 파일)과 SouvenirFormAddressRenderLine(주소, 상세, 정밀 위치 표시)을 정의하고, renderPhotos/renderAddress 시그니처를 튜플에서 구조체로 변경했습니다.
컨트롤러 상태 매핑
SouvenirFormViewController.swift
observeState 결과를 렌더 라인 구조체로 매핑하고, showsPreciseLocationEntry를 coordinate 존재 여부로 설정합니다.

화면 리팩토링 및 협업 로직 분리

계층 / 파일(들) 요약
Discovery/Recommend 셀 등록 및 레이아웃 리팩토링
Projects/Presentation/Sources/Scene/Discovery/Discovery/Base/DiscoveryView.swift, RecommendView.swift, RecommendIntent.swift
DiscoveryCellRegistrations/RecommendCellRegistrations 구조체로 셀·헤더 등록을 중앙화하고, dequeueConfiguredCell/isSectionEmpty/sectionLayout 헬퍼를 추가해 셀 제공과 레이아웃 선택 로직을 단순화했습니다. RecommendIntent의 makeSectionModels는 무인자로 변경됨.
GlobeViewModel 업로드 탭 처리
Projects/Presentation/Sources/Scene/Home/Globe/Base/GlobeViewModel.swift
wantToUploadSouvenir 처리를 handleUploadSouvenirTap으로 분리해 세션 갱신 후 인증 여부에 따라 생성 화면 또는 로그인으로 네비게이션합니다.

CI/CD 검증 인프라

계층 / 파일(들) 요약
GitHub Actions 워크플로우 및 스크립트
.github/workflows/verify.yml, docs/harness/scripts/swiftlint-strict.sh, prepare-ci-config.sh
develop 브랜치의 PR/push에서 실행되는 verify.yml을 추가하고, lint 잡(swiftformat + swiftlint-strict)과 build 잡(tuist 빌드·테스트)을 정의했습니다. prepare-ci-config.sh는 GitHub Secrets의 base64 설정을 복원합니다.
SwiftLint 규칙 및 검증 문서
.swiftlint.yml, docs/harness/scripts/verify.sh, docs/harness/verification.md, docs/harness/skills.md, CLAUDE.md
trailing_comma, file_length를 disabled_rules에 추가하고, verify.sh를 swiftlint-strict.sh로 연결했습니다. 검증과 스킬 문서에 Never Commit 규칙과 strict lint 기준을 명시했습니다.

코드 정리 및 마이너 UI 조정

계층 / 파일(들) 요약
변수명 표준화 및 조건문 명시화
Projects/Presentation/Sources/Scene/Auth/Login/LoginViewModel.swift, Home/Globe/SubView/View/SouvenirSheetView.swift, MyPage/*, Souvernir/Search/*
h→height, w→width, n→rowCount, r→radius, e→event로 재명명하고, NoticeListViewController/NoticeDetailViewController의 로딩 처리를 삼항 연산자에서 if/else로 변경했습니다.
네비게이션 스타일 및 SwiftLint 주석
Projects/Shared/DesignSystem/Sources/Component/Navigation/DSNavigationBar.swift, MyPageView.swift, SouvenirDetailView.swift, LocationSearchResultView.swift, LayoutConstants.swift
DSNavigationBar.Style의 Settings를 trailingSettings로 변경하고, type_body_length/nesting SwiftLint 경고를 선택적으로 비활성화하는 주석을 추가했습니다.
문서 및 설정 마이너 변경
CLAUDE.md, Workspace.swift
mise trust/install 단계를 초기 설정 문서에 추가하고, Workspace.swift의 projects 배열 항목 사이에 콤마를 추가했습니다.
⚠️ 주의: 초기화 제거 이슈
Projects/Data/Sources/Souvenir/DTO/SouvenirDTOMapper.swift
localPrice, currencySymbol, krwPrice 변수의 = nil 초기화를 제거해, dto.price가 없을 때 명시적 초기화 없이 SouvenirPrice 생성에 사용될 수 있는 경로가 생겼습니다. (High risk: 옵셔널 변수 미초기화)

평가 코드 리뷰 난이도

🎯 4 (Complex) | ⏱️ ~60분

관련 가능성 있는 PR

  • souzip/souzip-iOS#45: SwiftLint strict 검증 인프라(verify.yml, swiftlint-strict.sh)와 .swiftlint.yml 설정 변경이 SwiftLint 완전 제거와 직접 충돌 관계
  • souzip/souzip-iOS#26: GlobeViewModel의 업로드 탭 처리와 SouvenirCoordinator 라우팅이 직접 연동

제안된 라벨

chore, refactor, docs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 31.25% 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 제목이 주요 변경 사항(SwiftLint strict 적용 및 CI 정비)을 명확하고 간결하게 설명하고 있습니다.
Description check ✅ Passed PR 설명이 변경 요약, 상세 변경 내용, 기타 참고 사항으로 구성되어 있으나, 저장소의 필수 템플릿 구조를 완벽하게 준수하지 않았습니다.
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.


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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
.github/workflows/verify.yml (1)

1-71: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

GitHub Actions 보안 설정 보완이 필요합니다.

정적 분석 도구가 다음 보안 이슈를 플래그했습니다:

  1. Unpinned actions: actions를 태그(v4, v2) 대신 커밋 해시로 고정하면 공급망 공격 리스크를 줄일 수 있습니다.
  2. Credential persistence: actions/checkoutpersist-credentials: false 설정이 없어 GITHUB_TOKEN이 워크플로우 실행 중 남아있을 수 있습니다.
  3. Excessive permissions: permissions: 블록이 없어 GITHUB_TOKEN이 기본(과도한) 권한을 갖습니다.
🔒 보안 강화 제안
 name: Verify
+
+permissions:
+  contents: read

 on:
   pull_request:
@@ -18,10 +21,12 @@
     runs-on: macos-15
     steps:
       - name: Checkout
-        uses: actions/checkout@v4
+        uses: actions/checkout@<COMMIT_HASH>  # v4의 최신 커밋 해시로 교체
+        with:
+          persist-credentials: false

       - name: Setup mise
-        uses: jdx/mise-action@v2
+        uses: jdx/mise-action@<COMMIT_HASH>  # v2의 최신 커밋 해시로 교체

 # build 잡에도 동일하게 적용

커밋 해시 확인 방법:

# actions/checkout v4
gh api repos/actions/checkout/commits/v4 --jq .sha

# jdx/mise-action v2
gh api repos/jdx/mise-action/commits/v2 --jq .sha
🤖 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 @.github/workflows/verify.yml around lines 1 - 71, The workflow uses unpinned
action tags and leaves GITHUB_TOKEN and permissions too permissive; update all
action references (e.g., the uses entries for actions/checkout@v4 and
jdx/mise-action@v2) to fixed commit SHAs, add persist-credentials: false to the
actions/checkout step(s) in both the lint and build jobs, and add an explicit
permissions: block (least-privilege) at the top level of the workflow to
restrict GITHUB_TOKEN scopes required for the jobs.
🤖 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 `@docs/harness/skills.md`:
- Line 15: Update the broken relative link for the `souzip-commit` entry: in
docs/harness/skills.md locate the `souzip-commit` row (the link text `[로컬 전용
파일]`) and change the target from
../.codex/skills/souzip-commit/SKILL.md#never-commit-로컬-전용 to
../../.codex/skills/souzip-commit/SKILL.md#never-commit-로컬-전용 so the path
correctly climbs out of docs/ before referencing .codex.

In `@Projects/Presentation/Sources/Scene/Auth/Login/LoginViewModel.swift`:
- Around line 36-45: The loading state may not be cleared if performLogin throws
or the Task fails; wrap the async call in a Task that guarantees
emit(.loading(false)) using a defer (or a do { defer { emit(.loading(false)) };
await performLogin(...) } pattern). Update the .tapLogin branch (where you
currently call emit(.loading(true)) then await performLogin(provider) then
emit(.loading(false))) to set loading true, then defer clearing it, and do the
same for the .tapGuest branch (currently Task { await performLogin(nil) }) so
both branches always call emit(.loading(false)) even on errors; reference
performLogin, emit(.loading(true)), emit(.loading(false)), .tapLogin and
.tapGuest.

---

Outside diff comments:
In @.github/workflows/verify.yml:
- Around line 1-71: The workflow uses unpinned action tags and leaves
GITHUB_TOKEN and permissions too permissive; update all action references (e.g.,
the uses entries for actions/checkout@v4 and jdx/mise-action@v2) to fixed commit
SHAs, add persist-credentials: false to the actions/checkout step(s) in both the
lint and build jobs, and add an explicit permissions: block (least-privilege) at
the top level of the workflow to restrict GITHUB_TOKEN scopes required for the
jobs.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 81583de9-d952-49ed-bf2e-1b5fdbfa6e83

📥 Commits

Reviewing files that changed from the base of the PR and between fb552f4 and d546aea.

📒 Files selected for processing (36)
  • .codex/skills/souzip-commit/SKILL.md
  • .github/workflows/verify.yml
  • .swiftlint.yml
  • CLAUDE.md
  • Projects/Core/Analytics/Sources/AnalyticsEvent.swift
  • Projects/Data/Sources/Souvenir/DTO/SouvenirDTOMapper.swift
  • Projects/Presentation/Sources/Scene/Auth/Login/LoginViewModel.swift
  • Projects/Presentation/Sources/Scene/Discovery/Discovery/Base/DiscoveryView.swift
  • Projects/Presentation/Sources/Scene/Discovery/Recommend/Base/RecommendIntent.swift
  • Projects/Presentation/Sources/Scene/Discovery/Recommend/Base/RecommendView.swift
  • Projects/Presentation/Sources/Scene/Home/Globe/Base/GlobeViewModel.swift
  • Projects/Presentation/Sources/Scene/Home/Globe/SubView/View/SouvenirSheetView.swift
  • Projects/Presentation/Sources/Scene/MyPage/MyPage/Base/MyPageView.swift
  • Projects/Presentation/Sources/Scene/MyPage/MyPage/SubView/MyCollectionView.swift
  • Projects/Presentation/Sources/Scene/MyPage/Notice/Base/NoticeListViewController.swift
  • Projects/Presentation/Sources/Scene/MyPage/NoticeDetail/NoticeDetailViewController.swift
  • Projects/Presentation/Sources/Scene/Souvernir/Detail/Detail/Base/SouvenirDetailView.swift
  • Projects/Presentation/Sources/Scene/Souvernir/Form/Base/SouvenirFormView.swift
  • Projects/Presentation/Sources/Scene/Souvernir/Form/Base/SouvenirFormViewController.swift
  • Projects/Presentation/Sources/Scene/Souvernir/Form/Base/SouvenirFormViewModel+Actions.swift
  • Projects/Presentation/Sources/Scene/Souvernir/Form/Base/SouvenirFormViewModel+ImageProcessing.swift
  • Projects/Presentation/Sources/Scene/Souvernir/Form/Base/SouvenirFormViewModel+Submit.swift
  • Projects/Presentation/Sources/Scene/Souvernir/Form/Base/SouvenirFormViewModel+Supporting.swift
  • Projects/Presentation/Sources/Scene/Souvernir/Form/Base/SouvenirFormViewModel.swift
  • Projects/Presentation/Sources/Scene/Souvernir/Search/Country/SubView/SearchCountryNoResultsView.swift
  • Projects/Presentation/Sources/Scene/Souvernir/Search/Country/SubView/SearchCountryOnboardingView.swift
  • Projects/Presentation/Sources/Scene/Souvernir/Search/Result/Base/LocationSearchResultView.swift
  • Projects/Shared/DesignSystem/Sources/Component/Navigation/DSNavigationBar.swift
  • Projects/Shared/DesignSystem/Sources/Component/View/DSSpeechBubbleView.swift
  • Projects/Shared/DesignSystem/Sources/Layout/LayoutConstants.swift
  • Workspace.swift
  • docs/harness/scripts/prepare-ci-config.sh
  • docs/harness/scripts/swiftlint-strict.sh
  • docs/harness/scripts/verify.sh
  • docs/harness/skills.md
  • docs/harness/verification.md

Comment thread docs/harness/skills.md Outdated
Comment thread Projects/Presentation/Sources/Scene/Auth/Login/LoginViewModel.swift
@gnoes-ios gnoes-ios self-assigned this Jun 6, 2026
@gnoes-ios gnoes-ios added the chore label Jun 6, 2026
@gnoes-ios gnoes-ios merged commit 36a6cda into develop Jun 7, 2026
3 checks passed
@gnoes-ios gnoes-ios deleted the chore/lint-format-strict branch June 7, 2026 05:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant