feat(component): 오디오 콘텐츠 카드를 추가한다
This commit is contained in:
154
SodaLive/Sources/V2/Component/AudioContentCard.swift
Normal file
154
SodaLive/Sources/V2/Component/AudioContentCard.swift
Normal file
@@ -0,0 +1,154 @@
|
||||
//
|
||||
// AudioContentCard.swift
|
||||
// SodaLive
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
enum AudioContentCardSize {
|
||||
case large
|
||||
case medium
|
||||
case small
|
||||
|
||||
var width: CGFloat {
|
||||
switch self {
|
||||
case .large:
|
||||
return 185
|
||||
case .medium:
|
||||
return 163
|
||||
case .small:
|
||||
return 122
|
||||
}
|
||||
}
|
||||
|
||||
var labelWidth: CGFloat {
|
||||
width - (labelHorizontalPadding * 2)
|
||||
}
|
||||
|
||||
var labelHorizontalPadding: CGFloat {
|
||||
switch self {
|
||||
case .large:
|
||||
return 0
|
||||
case .medium:
|
||||
return SodaSpacing.s6
|
||||
case .small:
|
||||
return SodaSpacing.s4
|
||||
}
|
||||
}
|
||||
|
||||
var titleTypography: SodaTypography {
|
||||
switch self {
|
||||
case .large, .medium:
|
||||
return .heading4
|
||||
case .small:
|
||||
return .body1
|
||||
}
|
||||
}
|
||||
|
||||
var subtitleTypography: SodaTypography {
|
||||
switch self {
|
||||
case .large, .medium:
|
||||
return .body5
|
||||
case .small:
|
||||
return .caption2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct AudioContentCard<Thumbnail: View>: View {
|
||||
let size: AudioContentCardSize
|
||||
let title: String
|
||||
let subtitle: String
|
||||
private let thumbnail: Thumbnail
|
||||
|
||||
init(
|
||||
size: AudioContentCardSize,
|
||||
title: String,
|
||||
subtitle: String,
|
||||
@ViewBuilder thumbnail: () -> Thumbnail
|
||||
) {
|
||||
self.size = size
|
||||
self.title = title
|
||||
self.subtitle = subtitle
|
||||
self.thumbnail = thumbnail()
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: SodaSpacing.s8) {
|
||||
thumbnailContent
|
||||
|
||||
labelContent
|
||||
}
|
||||
.frame(width: size.width, alignment: .leading)
|
||||
}
|
||||
|
||||
private var thumbnailContent: some View {
|
||||
thumbnail
|
||||
.frame(width: size.width, height: size.width)
|
||||
.clipped()
|
||||
.frame(width: size.width, height: size.width)
|
||||
.clipShape(RoundedRectangle(cornerRadius: SodaSpacing.s14, style: .continuous))
|
||||
}
|
||||
|
||||
private var labelContent: some View {
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text(title)
|
||||
.appFont(size.titleTypography)
|
||||
.foregroundColor(.white)
|
||||
.lineLimit(1)
|
||||
.truncationMode(.tail)
|
||||
|
||||
Text(subtitle)
|
||||
.appFont(size.subtitleTypography)
|
||||
.foregroundColor(Color.gray500)
|
||||
.lineLimit(1)
|
||||
.truncationMode(.tail)
|
||||
}
|
||||
.frame(width: size.labelWidth, alignment: .leading)
|
||||
.padding(.horizontal, size.labelHorizontalPadding)
|
||||
.frame(width: size.width, alignment: .leading)
|
||||
}
|
||||
}
|
||||
|
||||
struct AudioContentCard_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
HStack(alignment: .top, spacing: SodaSpacing.s20) {
|
||||
AudioContentCard(
|
||||
size: .large,
|
||||
title: "오디오 콘텐츠 제목입니다",
|
||||
subtitle: "크리에이터명"
|
||||
) {
|
||||
previewThumbnail(Color.gray800)
|
||||
}
|
||||
|
||||
AudioContentCard(
|
||||
size: .medium,
|
||||
title: "긴 제목은 한 줄로 말줄임 처리됩니다",
|
||||
subtitle: "긴 부제목도 한 줄로 말줄임 처리됩니다"
|
||||
) {
|
||||
previewThumbnail(Color.gray700)
|
||||
}
|
||||
|
||||
AudioContentCard(
|
||||
size: .small,
|
||||
title: "스몰 카드",
|
||||
subtitle: "오디오"
|
||||
) {
|
||||
previewThumbnail(Color.gray900)
|
||||
}
|
||||
}
|
||||
.padding(SodaSpacing.s20)
|
||||
.background(Color.black)
|
||||
.previewLayout(.sizeThatFits)
|
||||
}
|
||||
|
||||
private static func previewThumbnail(_ color: Color) -> some View {
|
||||
ZStack {
|
||||
color
|
||||
|
||||
Image(systemName: "waveform")
|
||||
.font(.system(size: 32, weight: .bold))
|
||||
.foregroundColor(Color.gray500)
|
||||
}
|
||||
}
|
||||
}
|
||||
162
docs/plan-task/20260519_오디오콘텐츠카드컴포넌트.md
Normal file
162
docs/plan-task/20260519_오디오콘텐츠카드컴포넌트.md
Normal file
@@ -0,0 +1,162 @@
|
||||
# 20260519 오디오 콘텐츠 카드 컴포넌트 구현 계획
|
||||
|
||||
## 기준 문서
|
||||
|
||||
- PRD: `docs/prd/20260519_오디오콘텐츠카드컴포넌트_PRD.md`
|
||||
- Figma source nodes: `20:3800`, `20:3818`, `20:3829`
|
||||
- 코드 스타일: `docs/agent-guides/code-style.md`
|
||||
- 빌드/검증: `docs/agent-guides/build-test-verification.md`
|
||||
|
||||
## 작업 원칙
|
||||
|
||||
- PRD의 성공 기준과 제외 범위를 벗어나지 않는다.
|
||||
- 기존 구현이 있더라도 PRD 기준으로 재검토하고, 맞지 않는 부분만 최소 수정한다.
|
||||
- 신규 화면 적용, API 연동, asset 추가, dependency 추가는 하지 않는다.
|
||||
- `series`, `ORIGINAL`, `FIRST`, `무료` 관련 구현은 이번 범위에서 제거한다.
|
||||
- 수정 후 검증 기록은 이 문서 하단에 누적한다.
|
||||
|
||||
## 구현 대상 파일
|
||||
|
||||
- 생성 또는 수정: `SodaLive/Sources/V2/Component/AudioContentCard.swift`
|
||||
- `AudioContentCardSize`: Figma 세 크기와 typography/spacing 값을 캡슐화한다.
|
||||
- `AudioContentCard`: 썸네일과 라벨을 조합하는 public 재사용 View다.
|
||||
- Preview: 세 크기를 한 번에 비교한다.
|
||||
- 필요 시 수정: `SodaLive.xcodeproj/project.pbxproj`
|
||||
- 신규 Swift 파일이 Xcode 빌드 대상에 포함되지 않는 경우에만 수정한다.
|
||||
- 수정: `docs/plan-task/20260519_오디오콘텐츠카드컴포넌트.md`
|
||||
- 구현 계획과 검증 기록을 유지한다.
|
||||
|
||||
## TASK 체크리스트
|
||||
|
||||
- [x] **Task 1: PRD 기준으로 기존 구현 상태 점검**
|
||||
- `AudioContentCard.swift`가 존재하는지 확인한다.
|
||||
- `AudioContentCardSize`가 `large`, `medium`, `small`만 제공하는지 확인한다.
|
||||
- 각 크기 값이 PRD와 일치하는지 확인한다.
|
||||
- `large`: width 185, thumbnail 185x185, label width 185, label horizontal padding 0, title `.heading4`, subtitle `.body5`
|
||||
- `medium`: width 163, thumbnail 163x163, label width 151, label horizontal padding 6, title `.heading4`, subtitle `.body5`
|
||||
- `small`: width 122, thumbnail 122x122, label width 114, label horizontal padding 4, title `.body1`, subtitle `.caption2`
|
||||
- 전체 공통: thumbnail-title gap 8, title-subtitle gap 2
|
||||
- `series`, `ORIGINAL`, `FIRST`, `무료` 관련 API나 UI가 포함되어 있으면 제거 대상으로 표시한다.
|
||||
|
||||
- [x] **Task 2: 컴포넌트 API 정리**
|
||||
- 호출부 API를 아래 형태로 유지한다.
|
||||
|
||||
```swift
|
||||
AudioContentCard(
|
||||
size: .large,
|
||||
title: "오디오 콘텐츠 제목",
|
||||
subtitle: "크리에이터명"
|
||||
) {
|
||||
Image("content_thumbnail")
|
||||
.resizable()
|
||||
.scaledToFill()
|
||||
}
|
||||
```
|
||||
|
||||
- `thumbnail`은 `@ViewBuilder`로 주입한다.
|
||||
- production code에 Figma localhost URL을 넣지 않는다.
|
||||
- 실제 이미지 로딩 방식은 호출부 책임으로 둔다.
|
||||
|
||||
- [x] **Task 3: 시각 스펙 구현 또는 보정**
|
||||
- 썸네일 영역은 `size.width x size.width`로 고정한다.
|
||||
- 썸네일 corner radius는 `SodaSpacing.s14`를 사용한다.
|
||||
- 썸네일과 제목 사이 간격은 모든 크기에서 8pt로 설정한다.
|
||||
- 제목과 부제 사이 간격은 모든 크기에서 2pt로 설정한다.
|
||||
- 라벨 horizontal padding은 `large` 0pt, `medium` 6pt, `small` 4pt로 설정한다.
|
||||
- 제목/부제는 각각 `.lineLimit(1)`과 `.truncationMode(.tail)`을 적용한다.
|
||||
- 카드 자체는 배경색을 강제하지 않는다.
|
||||
- `series`, `ORIGINAL`, `FIRST`, `무료` 관련 View, enum case, parameter, Preview 상태를 추가하지 않는다.
|
||||
|
||||
- [x] **Task 4: Preview 작성 또는 보정**
|
||||
- Preview는 `large`, `medium`, `small` 세 크기를 모두 포함한다.
|
||||
- Preview 썸네일은 외부 네트워크나 Figma localhost asset에 의존하지 않는다.
|
||||
- Preview에는 태그 노출 조합을 포함하지 않는다.
|
||||
|
||||
- [x] **Task 5: Xcode 빌드 대상 포함 여부 확인**
|
||||
- 신규 파일이 Xcode 프로젝트에 포함되어 있지 않으면 `SodaLive.xcodeproj/project.pbxproj`에 추가한다.
|
||||
- 이미 포함되어 있거나 파일 시스템 동기화 방식으로 빌드되는 경우 프로젝트 파일을 수정하지 않는다.
|
||||
- 프로젝트 파일을 수정한 경우 `plutil -lint SodaLive.xcodeproj/project.pbxproj`를 실행한다.
|
||||
|
||||
- [x] **Task 6: 검증 실행**
|
||||
- 변경 Swift 파일에 대해 `lsp_diagnostics`를 실행한다.
|
||||
- 단일 파일 문맥에서 프로젝트 공통 확장을 찾지 못하는 SourceKit 오류가 나오면 실제 빌드 결과로 최종 판단하되, 오류 내용을 검증 기록에 남긴다.
|
||||
- 다음 빌드 명령을 실행한다.
|
||||
|
||||
```bash
|
||||
xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build
|
||||
```
|
||||
|
||||
- test action 확인이 필요하면 다음 명령을 실행하고, 스킴 미구성 시 그 사실을 검증 기록에 남긴다.
|
||||
|
||||
```bash
|
||||
xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" test
|
||||
```
|
||||
|
||||
- [x] **Task 7: 문서와 코드 일치 여부 최종 점검**
|
||||
- PRD의 Success Criteria 각 항목을 코드와 대조한다.
|
||||
- 구현 계획의 체크리스트가 실제 상태와 맞는지 갱신한다.
|
||||
- 검증 기록을 하단에 누적한다.
|
||||
|
||||
## 검증 기록
|
||||
|
||||
### 2026-05-19 문서 재정리
|
||||
|
||||
- 목적: 구현보다 PRD/계획 문서를 먼저 정리하라는 사용자 피드백 반영
|
||||
- 수행 내용:
|
||||
- `docs/prd/20260519_오디오콘텐츠카드컴포넌트_PRD.md` 신규 작성
|
||||
- `docs/plan-task/20260519_오디오콘텐츠카드컴포넌트.md`를 구현 결과 요약에서 구현 계획 문서로 재작성
|
||||
- 아직 수행하지 않은 작업:
|
||||
- PRD 기준 코드 재수정
|
||||
- TASK 체크리스트 완료 처리
|
||||
- 신규 빌드 검증
|
||||
|
||||
### 2026-05-19 범위 축소 반영
|
||||
|
||||
- 목적: 이번 범위에서 제외할 항목 명시
|
||||
- 반영 내용:
|
||||
- `series` 타입 콘텐츠 카드 제외
|
||||
- `ORIGINAL` 태그 제외
|
||||
- `FIRST` 태그 제외
|
||||
- `무료` 태그 제외
|
||||
- 계획 문서에서 태그 구현/Preview 항목 제거
|
||||
|
||||
### 2026-05-19 라벨 패딩 및 간격 스펙 반영
|
||||
|
||||
- 목적: Figma 컴포넌트의 라벨 위치와 텍스트 간격을 구현 전에 문서 기준으로 고정
|
||||
- 반영 내용:
|
||||
- label horizontal padding: `large` 0pt, `medium` 6pt, `small` 4pt
|
||||
- thumbnail-title gap: 모든 크기 8pt
|
||||
- title-subtitle gap: 모든 크기 2pt
|
||||
|
||||
### 2026-05-19 PRD 기준 구현
|
||||
|
||||
- 목적: PRD와 구현 계획 기준으로 오디오 콘텐츠 카드 컴포넌트 보정
|
||||
- 수행 내용:
|
||||
- `AudioContentCard` API에서 `showsFirstTag`, `showsFreeTag` 제거
|
||||
- `AudioContentTag` 및 태그 관련 UI 제거
|
||||
- label horizontal padding 적용: `large` 0pt, `medium` 6pt, `small` 4pt
|
||||
- thumbnail-title gap 8pt, title-subtitle gap 2pt 적용
|
||||
- Preview에서 태그 노출 상태 제거
|
||||
- 확인 내용:
|
||||
- `SodaLive.xcodeproj/project.pbxproj`에 `AudioContentCard.swift`가 두 앱 타깃 Sources에 포함되어 있음
|
||||
- 아직 수행하지 않은 작업:
|
||||
- 해당 없음
|
||||
|
||||
### 2026-05-19 PRD 기준 구현 검증
|
||||
|
||||
- `lsp_diagnostics`:
|
||||
- 결과: SourceKit 단일 파일 문맥에서 `SodaSpacing`, `SodaTypography`, `appFont`, `Color.gray800`, `Color.gray500` 등 프로젝트 공통 확장을 찾지 못하는 오류 표시
|
||||
- 판단: 기존 V2 컴포넌트에서도 동일한 단일 파일 문맥 한계가 있어 실제 Xcode 빌드로 최종 판단
|
||||
- `plutil -lint "SodaLive.xcodeproj/project.pbxproj"`:
|
||||
- 결과: `SodaLive.xcodeproj/project.pbxproj: OK`
|
||||
- `git diff --check`:
|
||||
- 결과: 통과
|
||||
- `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build`:
|
||||
- 결과: `BUILD SUCCEEDED`
|
||||
- `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" test`:
|
||||
- 결과: `Scheme SodaLive-dev is not currently configured for the test action.`
|
||||
- 문서/코드 대조:
|
||||
- `large`, `medium`, `small` 크기 지원 확인
|
||||
- label horizontal padding `large` 0pt, `medium` 6pt, `small` 4pt 반영 확인
|
||||
- thumbnail-title gap 8pt, title-subtitle gap 2pt 반영 확인
|
||||
- `series`, `ORIGINAL`, `FIRST`, `무료` 관련 API/UI 제거 확인
|
||||
125
docs/prd/20260519_오디오콘텐츠카드컴포넌트_PRD.md
Normal file
125
docs/prd/20260519_오디오콘텐츠카드컴포넌트_PRD.md
Normal file
@@ -0,0 +1,125 @@
|
||||
# PRD: 오디오 콘텐츠 카드 컴포넌트
|
||||
|
||||
## 1. Overview
|
||||
|
||||
Figma의 오디오 콘텐츠 카드 3종(`large`, `medium`, `small`)을 iOS SwiftUI에서 재사용 가능한 V2 컴포넌트로 제공한다.
|
||||
|
||||
---
|
||||
|
||||
## 2. Problem
|
||||
|
||||
- Figma에 정의된 3개 컴포넌트는 형태가 동일하고 크기만 다르지만, 구현 전에 요구사항 문서가 충분히 정리되지 않아 구현 기준이 불명확했다.
|
||||
- 크기, 라벨, 제외 범위, 에셋 처리 기준이 문서화되지 않으면 구현자가 임의로 API나 시각 요소를 해석할 수 있다.
|
||||
- 향후 메인/콘텐츠 영역에서 동일한 오디오 카드가 반복 사용될 가능성이 있어 단일 기준 컴포넌트가 필요하다.
|
||||
|
||||
---
|
||||
|
||||
## 3. Goals
|
||||
|
||||
- Figma 노드 `20:3800`, `20:3818`, `20:3829`의 오디오 카드 공통 구조를 하나의 SwiftUI 컴포넌트로 표현한다.
|
||||
- 호출부는 크기, 제목, 부제, 썸네일 표시 방식을 명시적으로 주입할 수 있다.
|
||||
- 크기별 썸네일/라벨/폰트/간격 스펙을 문서와 코드에서 일관되게 유지한다.
|
||||
- 외부 Figma localhost asset URL을 production code에 하드코딩하지 않는다.
|
||||
- 신규 컴포넌트는 `SodaLive/Sources/V2/Component/**`의 기존 SwiftUI 컴포넌트 스타일을 따른다.
|
||||
|
||||
---
|
||||
|
||||
## 4. Non-Goals
|
||||
|
||||
- 이번 작업에서 실제 콘텐츠 API 연동, 네트워크 로딩 정책, 캐싱 정책은 구현하지 않는다.
|
||||
- Figma의 임시 썸네일 이미지를 앱 asset으로 추가하지 않는다.
|
||||
- `series` 타입 콘텐츠 카드는 구현하지 않는다.
|
||||
- `ORIGINAL` 태그는 구현하지 않는다.
|
||||
- `FIRST` 태그는 구현하지 않는다.
|
||||
- `무료` 태그는 구현하지 않는다.
|
||||
- 태그 아이콘 에셋은 이번 범위에서 추가하지 않는다.
|
||||
- 시리즈 카드(`type = series`) 또는 세로형 콘텐츠 카드 전체를 함께 일반화하지 않는다.
|
||||
- 기존 화면에 컴포넌트를 배치하거나 사용자 플로우를 변경하지 않는다.
|
||||
|
||||
---
|
||||
|
||||
## 5. Target Users
|
||||
|
||||
- V2 SwiftUI 화면을 구현하는 iOS 개발자
|
||||
- 콘텐츠 카드 UI를 재사용해야 하는 홈/콘텐츠/오디오 영역 구현자
|
||||
- Figma와 앱 구현 사이의 시각 기준을 확인해야 하는 리뷰어
|
||||
|
||||
---
|
||||
|
||||
## 6. User Stories
|
||||
|
||||
- 개발자는 `large`, `medium`, `small` 중 하나를 선택해 동일한 구조의 오디오 카드를 표시할 수 있다.
|
||||
- 개발자는 실제 이미지, placeholder, `AsyncImage`, Kingfisher 기반 이미지 View 등 썸네일 표시 방식을 호출부에서 결정할 수 있다.
|
||||
- 리뷰어는 PRD와 계획 문서만 보고 구현 범위와 제외 범위를 확인할 수 있다.
|
||||
|
||||
---
|
||||
|
||||
## 7. Core Features
|
||||
|
||||
### 7.1 단일 오디오 콘텐츠 카드 컴포넌트
|
||||
|
||||
#### Requirements
|
||||
- 컴포넌트 파일은 `SodaLive/Sources/V2/Component/AudioContentCard.swift`에 둔다.
|
||||
- 크기는 `AudioContentCardSize` enum으로 표현한다.
|
||||
- 카드 구조는 썸네일 영역과 라벨 영역으로 분리한다.
|
||||
- 제목과 부제는 각각 한 줄로 표시하고 초과 시 tail truncation 처리한다.
|
||||
- 썸네일은 호출부가 `@ViewBuilder`로 주입한다.
|
||||
|
||||
#### Edge Cases
|
||||
- 제목/부제가 긴 경우 한 줄 말줄임 처리한다.
|
||||
- 태그는 이번 범위에서 표시하지 않는다.
|
||||
|
||||
### 7.2 크기별 스펙
|
||||
|
||||
| Size | Figma node | Card width | Thumbnail | Label width | Label horizontal padding | Title | Subtitle |
|
||||
| --- | --- | ---: | ---: | ---: | ---: | --- | --- |
|
||||
| `large` | `20:3800` | 185 | 185x185 | 185 | 0 | 18 bold | 14 medium |
|
||||
| `medium` | `20:3818` | 163 | 163x163 | 151 | 6 | 18 bold | 14 medium |
|
||||
| `small` | `20:3829` | 122 | 122x122 | 114 | 4 | 16 bold | 12 medium |
|
||||
|
||||
#### Requirements
|
||||
- 썸네일은 모든 크기에서 정사각형이다.
|
||||
- 썸네일 corner radius는 14pt다.
|
||||
- `large`, `medium` 제목은 기존 typography 기준 `.heading4`를 사용한다.
|
||||
- `small` 제목은 기존 typography 기준 `.body1`을 사용한다.
|
||||
- `large`, `medium` 부제는 기존 typography 기준 `.body5`를 사용한다.
|
||||
- `small` 부제는 기존 typography 기준 `.caption2`를 사용한다.
|
||||
- 썸네일과 제목 사이 간격은 모든 크기에서 8pt다.
|
||||
- 제목과 부제 사이 간격은 모든 크기에서 2pt다.
|
||||
- 라벨 영역의 horizontal padding은 `large` 0pt, `medium` 6pt, `small` 4pt다.
|
||||
|
||||
## 8. UX / UI Expectations
|
||||
|
||||
- Figma와 동일한 카드 외곽 크기, 썸네일 크기, 라벨 폭을 유지한다.
|
||||
- 썸네일은 호출부에서 전달한 View가 카드 영역을 채우도록 렌더링할 수 있어야 한다.
|
||||
- 카드 자체는 배경색을 강제하지 않는다. 배경은 사용하는 화면이 결정한다.
|
||||
- SwiftUI Preview는 세 크기를 한 화면에서 비교할 수 있게 제공한다.
|
||||
|
||||
---
|
||||
|
||||
## 9. Technical Constraints
|
||||
|
||||
- SwiftUI 기반으로 구현한다.
|
||||
- 기존 디자인 토큰과 확장(`SodaSpacing`, `Color`, `SodaTypography`, `appFont`)을 우선 사용한다.
|
||||
- 신규 dependency를 추가하지 않는다.
|
||||
- `Pods/**`, `generated/**`, `build/**`는 수정하지 않는다.
|
||||
- production code에 Figma localhost asset URL을 넣지 않는다.
|
||||
- 프로젝트 파일 수정은 신규 Swift 파일이 Xcode 빌드 대상에 포함되어야 할 때만 수행한다.
|
||||
|
||||
---
|
||||
|
||||
## 10. Success Criteria
|
||||
|
||||
- `AudioContentCard`가 `large`, `medium`, `small` 세 크기를 모두 지원한다.
|
||||
- 각 크기별 수치와 라벨 간격/패딩이 PRD의 size spec과 일치한다.
|
||||
- 컴포넌트 API가 썸네일 View 주입, 제목, 부제를 명시적으로 제공한다.
|
||||
- `series`, `ORIGINAL`, `FIRST`, `무료` 관련 API나 UI가 포함되지 않는다.
|
||||
- `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build`가 성공한다.
|
||||
- `docs/plan-task/20260519_오디오콘텐츠카드컴포넌트.md`에 구현 작업 순서와 검증 기록이 남는다.
|
||||
|
||||
---
|
||||
|
||||
## 11. Open Questions
|
||||
|
||||
- 컴포넌트를 어느 실제 화면에 처음 적용할지는 이번 PRD 범위 밖이다.
|
||||
- 카드 접근성 label 조합 규칙은 실제 화면 적용 시 데이터 정책에 맞춰 확정한다.
|
||||
Reference in New Issue
Block a user