
- 서비스 소개
- iOS Developer
- Tech stack & Architecture
- 시연영상
- Library
- Coding Convention
- Tag & Commit Convention
- Git Flow
- Foldering
- Trouble Shooting
Spoony는 리뷰 작성자의 신뢰도와 영향력에 기반해,
유저가 믿을 수 있는 장소 정보를 탐색하고 공유하며 나만의 찐 리스트 지도를 만들어가는 앱 서비스입니다.
🌟Spoony와 함께 새로운 장소를 발견하고 나만의 지도를 완성해보세요!🌟
1️⃣ 장소 등록하고 수저 획득하기
나만 알고 싶은 맛집, 아늑한 카페, 분위기 좋은 펍 등 찐 장소를 등록하고 수저를 획득하세요!
2️⃣신뢰도 높은 찐 리스트 떠먹기
획득한 수저로 다른 사람의 찐 리스트를 떠먹어 보세요! 원하는 유저를 팔로우하고 로컬 사용자와 지역별 랭킹을 통해 신뢰도 높은 리뷰를 확인해보세요.
3️⃣ 나만의 지도 완성하기
떠먹은 리스트 중 마음에 드는 장소를 추가해 나만의 찐 리스트 지도를 만들어보세요!

이지훈 @hooni0918 |
최안용 @ChoiAnYong |
최주리 @juri123123 |
이명진 @thingineeer |
---|---|---|---|
![]() |
![]() |
![]() |
![]() |
메인 지도 ,장소검색 |
등록하기 |
등록장소 리스트 , 신고하기 |
등록장소 디테일뷰 |
library | description | version |
---|---|---|
Moya | 추상화된 네트워크 레이어를 보다 간편하게 사용 | 15.0.3 |
Kingfisher | 이미지 캐싱 처리 | 8.1.3 |
NMFMaps | 지도 구현 | 3.20.0 |
FlexSheet | 바텀시트 라이브러리 커스텀 구현 | 1.2.27 |
- 소문자로 작성
- 한글 사용
- 제목은 50자 이내, 명령조로 작성
- 상세 내용은 본문에 작성
태그 | 설명 |
---|---|
feat | 기능 구현 |
fix | 버그/오류 수정 |
docs | 문서 수정 |
setting | 프로젝트 설정 변경 |
add | 에셋/라이브러리 추가 |
refactor | 코드 리팩토링 (생산적) |
chore | 경미한 수정 (비생산적) |
style | UI 작업 |
juri | 주리야 도와줘 |
feat: #1 로그인 기능 구현
add: #2 이미지 에셋 추가
juri: #3 주리야 도와줘
- 1 issue = 1 PR
- Merge= 리드 승인 + 2인 승인
- Squash and Merge, Rebase and Merge는 사용하지 않습니다.
📁 Project
├── App.swift
├── 📁 Source
│ ├── 🗂️ Features
│ │ ├── 🗂️ Home
│ │ │ ├── 🗂️ View
│ │ │ │ ├── MovieView.swift
│ │ │ │ └── 🗂️ Components
│ │ │ │ └── MovieCell.swift
│ │ │ ├── 🗂️ Intent
│ │ │ │ └── MovieIntent.swift
│ │ │ ├── 🗂️ State
│ │ │ │ ├── MovieState.swift
│ │ │ │ └── MovieStore.swift
│ │ │ ├── 🗂️ Model
│ │ │ │ └── MovieModel.swift
│ │ │ └── 🗂️ Service
│ │ │ └── MovieAPIService.swift
│ │ ├── 🗂️ Detail
│ │ ├── 🗂️ Quest
│ │ ├── 🗂️ Register
│ │
├── 📁 Network
│ ├── 🗂️ Network
│ │ ├── NetworkManager.swift
│ │ ├── Endpoints.swift
│ │ ├── APIError.swift
│ │
│ ├── 🗂️ Services
│ │ ├── AuthService.swift
│ │ ├── StorageService.swift
│ │
├── 📁 Resources
│ ├── 🗂️ Extensions
│ │ ├── View+.swift
│ │ ├── Color+.swift
│ │ ├── Date+.swift
│ │
│ ├── 🗂️ Helpers
│ │ ├── Constants.swift
│ │ ├── Utilities.swift
│ │
│ ├── 🗂️ Theme
│ │ ├── Colors.swift
│ │ ├── Typography.swift
│ │ ├── Spacing.swift
│ │
│ ├── 🗂️ Fonts
│ │ ├── CustomFont.ttf
│ │
│ ├── Assets.xcassets
│ ├── Info.plist
문제상황
- SwiftUI 내장
.sheet
modifier 사용 시 탭바를 항상 덮는 UI 문제 - 스크롤과 드래그 제스처 간의 충돌
- 커스텀 높이 지정의 어려움
해결방안
- 모듈화와 접근제어
public struct FlexibleBottomSheet<Content: View>: View {
private let content: Content
private let sheetStyle: FlexSheetStyle
@Binding private var currentStyle: BottomSheetStyle
public init(
currentStyle: Binding<BottomSheetStyle>,
style: FlexSheetStyle = .defaultFlex,
@ViewBuilder content: () -> Content
) {
self._currentStyle = currentStyle
self.sheetStyle = style
self.content = content()
}
}
- public 접근제어자를 통한 모듈간 접근 제어
- initializer도 public으로 명시하여 외부 모듈에서의 인스턴스 생성 가능하게 구현
- 제스처 처리 시스템
private func handleDragEnd(translation: CGFloat, velocity: CGFloat, in geometry: GeometryProxy) {
if abs(velocity) > sheetStyle.dragSensitivity {
handleVelocityBasedSnap(velocity: velocity)
} else {
let screenHeight = geometry.size.height
let currentOffset = screenHeight - currentStyle.height(for: screenHeight) + translation
currentStyle = getClosestSnapPoint(to: currentOffset, in: geometry)
}
}
- 드래그 제스처의 속도 기반 스냅 기능
- 사용자 경험을 고려한 자연스러운 애니메이션
- 스타일 커스터마이징
public struct FlexSheetStyle {
let animation: Animation
let dragSensitivity: CGFloat
let allowHide: Bool
let sheetSize: BottomSheetStyle
let fixedHeight: CGFloat
let handleBarVisible: Bool
public static let defaultFlex = FlexSheetStyle(
animation: .spring(response: 0.3, dampingFraction: 0.7),
dragSensitivity: 500,
allowHide: false,
sheetSize: .minimal,
fixedHeight: 0,
handleBarVisible: false
)
}
- 애니메이션, 드래그 감도, 시트 크기 등 다양한 커스터마이징 옵션
- 미리 정의된 스타일 제공으로 사용 편의성 향상
결과 및 느낀점
-
모듈화와 은닉화
- 모듈 간 접근 제어의 중요성 이해
-
확장성 있는 설계
- 다양한 사용 케이스 대응
- 커스터마이징 옵션 제공