Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Projects/BKData/Sources/API/AuthAPI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public enum AuthAPI {

extension AuthAPI: RequestTarget {
public var baseURL: String {
return "\(APIConfig.baseURL)/auth"
return "\(APIConfig.baseURLv1)/auth"
}

public var path: String {
Expand Down
2 changes: 1 addition & 1 deletion src/Projects/BKData/Sources/API/BookAPI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ enum BookAPI {

extension BookAPI: RequestTarget {
var baseURL: String {
return "\(APIConfig.baseURL)/books"
return "\(APIConfig.baseURLv1)/books"
}

var path: String {
Expand Down
41 changes: 41 additions & 0 deletions src/Projects/BKData/Sources/API/EmotionAPI.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright © 2025 Booket. All rights reserved

import Foundation

enum EmotionAPI {
case fetchEmotions
}

extension EmotionAPI: RequestTarget {
var baseURL: String {
return "\(APIConfig.baseURLv2)/emotions"
}

var path: String {
switch self {
case .fetchEmotions:
return ""
}
}

var method: HTTPMethod {
switch self {
case .fetchEmotions:
return .get
}
}

var headers: [String: String] {
return [
"Content-Type": "application/json"
]
}

var body: Encodable? {
return nil
}

var query: [String: Any] {
return [:]
}
}
2 changes: 1 addition & 1 deletion src/Projects/BKData/Sources/API/HomeAPI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ enum HomeAPI {

extension HomeAPI: RequestTarget {
var baseURL: String {
return "\(APIConfig.baseURL)/home"
return "\(APIConfig.baseURLv1)/home"
}

var path: String {
Expand Down
9 changes: 2 additions & 7 deletions src/Projects/BKData/Sources/API/RecordAPI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,7 @@ enum RecordAPI {

extension RecordAPI: RequestTarget {
var baseURL: String {
switch self {
case .fetch, .seed:
return "\(APIConfig.baseV2URL)/reading-records"
default:
return "\(APIConfig.baseURL)/reading-records"
}
return "\(APIConfig.baseURLv2)/reading-records"
}

var path: String {
Expand All @@ -46,7 +41,7 @@ extension RecordAPI: RequestTarget {
case .fetch, .detail, .seed:
return .get
case .patch:
return .patch
return .put // V2 API uses PUT instead of PATCH
case .delete:
return .delete
}
Expand Down
2 changes: 1 addition & 1 deletion src/Projects/BKData/Sources/API/UserAPI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ enum UserAPI {

extension UserAPI: RequestTarget {
var baseURL: String {
return "\(APIConfig.baseURL)/users/me"
return "\(APIConfig.baseURLv1)/users/me"
}

var path: String {
Expand Down
20 changes: 12 additions & 8 deletions src/Projects/BKData/Sources/Constant/APIConfig.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,22 @@ private final class BKDataBundleToken {}

enum APIConfig {
private static let bundle = Bundle(for: BKDataBundleToken.self)

static let baseURL: String = {

/// API Base URL (xcconfig에서 /api까지만 포함)
private static let baseURL: String = {
guard let value = bundle.object(forInfoDictionaryKey: "BASE_API_URL") as? String else {
fatalError("Can't load environment: BKData.BASE_API_URL")
}
return value
}()

static let baseV2URL: String = {
guard let value = bundle.object(forInfoDictionaryKey: "BASE_API_V2_URL") as? String else {
fatalError("Can't load environment: BKData.BASE_API_V2_URL")
}
return value

/// V1 API Base URL (auth, books, users, home)
static let baseURLv1: String = {
return baseURL + "/v1"
}()

/// V2 API Base URL (emotions, reading-records)
static let baseURLv2: String = {
return baseURL + "/v2"
}()
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,33 @@ import BKDomain
import Foundation

struct InsertRecordRequestDTO: Encodable {
let pageNumber: Int
let pageNumber: Int?
let quote: String
let review: String?
let emotionTags: [String]

let primaryEmotion: String
let detailEmotionTagIds: [String]

init(
pageNumber: Int,
pageNumber: Int?,
quote: String,
review: String?,
emotionTags: [String]
primaryEmotion: String,
detailEmotionTagIds: [String]
) {
self.pageNumber = pageNumber
self.quote = quote
self.review = review
self.emotionTags = emotionTags
self.primaryEmotion = primaryEmotion
self.detailEmotionTagIds = detailEmotionTagIds
}

init(data: RecordVO) {
self.init(
pageNumber: data.pageNumber,
quote: data.quote,
review: data.review,
emotionTags: data.emotionTags
review: data.memo,
primaryEmotion: data.primaryEmotion.rawValue,
detailEmotionTagIds: data.detailEmotionIds
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,20 @@
import BKDomain
import Foundation

public struct DetailRecordResponseDTO: Decodable {
public let id: String
public let userBookId: String
public let pageNumber: Int
public let quote: String
public let review: String?
public let emotionTags: [Emotion]
public let createdAt: String
public let updatedAt: String
public let bookTitle: String
public let bookPublisher: String
public let bookCoverImageUrl: URL
public let author: String
struct DetailRecordResponseDTO: Decodable {
let id: String
let userBookId: String
let pageNumber: Int?
let quote: String
let review: String?
let primaryEmotion: PrimaryEmotionDTO
let detailEmotions: [DetailEmotionDTO]
let createdAt: String
let updatedAt: String
let bookTitle: String
let bookPublisher: String
let bookCoverImageUrl: URL
let author: String
}

extension DetailRecordResponseDTO {
Expand All @@ -26,7 +27,8 @@ extension DetailRecordResponseDTO {
pageNumber: pageNumber,
quote: quote,
review: review,
emotionTags: emotionTags,
primaryEmotion: primaryEmotion.toDomain() ?? .other,
detailEmotions: detailEmotions.map { $0.toDomain() },
createdAt: DateParser.parseISO8601(createdAt) ?? .distantPast,
updatedAt: DateParser.parseISO8601(updatedAt),
bookTitle: bookTitle,
Expand All @@ -41,11 +43,11 @@ extension DetailRecordResponseDTO {
public struct DetailRecordV2ResponseDTO: Decodable {
public let id: String
public let userBookId: String
public let pageNumber: Int
public let pageNumber: Int?
public let quote: String
public let review: String?
public let primaryEmotion: PrimaryEmotionResponseDTO
public let detailEmotions: [DetailEmotionResponseDTO?]
public let detailEmotions: [DetailEmotionResponseDTO]
public let createdAt: String
public let updatedAt: String
public let bookTitle: String
Expand All @@ -62,7 +64,8 @@ extension DetailRecordV2ResponseDTO {
pageNumber: pageNumber,
quote: quote,
review: review,
emotionTags: [primaryEmotion.displayName],
primaryEmotion: primaryEmotion.toDomain(),
detailEmotions: detailEmotions.map { $0.toDomain() },
createdAt: DateParser.parseISO8601(createdAt) ?? .distantPast,
updatedAt: DateParser.parseISO8601(updatedAt),
bookTitle: bookTitle,
Expand Down
53 changes: 53 additions & 0 deletions src/Projects/BKData/Sources/DTO/Response/EmotionResponseDTO.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Copyright © 2025 Booket. All rights reserved

import BKDomain
import Foundation

// MARK: - API Response DTO

struct EmotionListResponseDTO: Decodable {
let emotions: [EmotionGroupDTO]
}

struct EmotionGroupDTO: Decodable {
let code: String
let displayName: String
let detailEmotions: [DetailEmotionDTO]
}

struct DetailEmotionDTO: Decodable {
let id: String
let name: String
}

// MARK: - Mapping to Domain

extension EmotionGroupDTO {
func toDomain() -> EmotionGroup? {
guard let primaryEmotion = PrimaryEmotion(rawValue: code) else {
return nil
}
return EmotionGroup(
primaryEmotion: primaryEmotion,
displayName: displayName,
detailEmotions: detailEmotions.map { $0.toDomain() }
)
}
}

extension DetailEmotionDTO {
func toDomain() -> DetailEmotion {
return DetailEmotion(id: id, name: name)
}
}

// MARK: - Response에서 사용하는 DTO (기록 조회 시)

struct PrimaryEmotionDTO: Decodable {
let code: String
let displayName: String

func toDomain() -> PrimaryEmotion? {
return PrimaryEmotion(rawValue: code)
}
}
Comment on lines +46 to +53
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

PrimaryEmotionDTO.toDomain()의 fallback 처리 불일치

PrimaryEmotionResponseDTO.swifttoDomain()PrimaryEmotion(rawValue: code) ?? .other로 fallback을 제공하지만, 이 파일의 PrimaryEmotionDTO.toDomain()nil을 반환합니다. 같은 도메인 타입을 변환하는데 fallback 전략이 다르면 호출부에서 혼란이 생길 수 있습니다. 의도된 차이인지 확인해 주세요.

🤖 Prompt for AI Agents
In `@src/Projects/BKData/Sources/DTO/Response/EmotionResponseDTO.swift` around
lines 46 - 53, PrimaryEmotionDTO.toDomain() currently returns nil on unknown
codes which is inconsistent with PrimaryEmotionResponseDTO.toDomain() that uses
PrimaryEmotion(rawValue: code) ?? .other; update PrimaryEmotionDTO.toDomain() to
use the same fallback by returning PrimaryEmotion(rawValue: code) ?? .other so
both DTOs map unknown/unsupported codes to .other consistently (refer to
PrimaryEmotionDTO, toDomain(), PrimaryEmotionResponseDTO, and PrimaryEmotion).

Original file line number Diff line number Diff line change
Expand Up @@ -3,45 +3,32 @@
import BKDomain
import Foundation

public struct InsertRecordResponseDTO: Decodable {
struct InsertRecordResponseDTO: Decodable {
let id: String
let userBookId: String
let pageNumber: Int
let pageNumber: Int?
let quote: String
let review: String?
let emotionTags: [Emotion]
let primaryEmotion: PrimaryEmotionDTO
let detailEmotions: [DetailEmotionDTO]
let createdAt: String
let updatedAt: String
let bookTitle: String
let bookPublisher: String
let bookCoverImageUrl: URL
let author: String

enum CodingKeys: String, CodingKey {
case id
case userBookId
case pageNumber
case quote
case review
case emotionTags
case createdAt
case updatedAt
case bookTitle
case bookPublisher
case bookCoverImageUrl
case author
}
}

public extension InsertRecordResponseDTO {
extension InsertRecordResponseDTO {
func toRecordInfo() -> RecordInfo {
return RecordInfo(
recordId: id,
bookId: userBookId,
pageNumber: pageNumber,
quote: quote,
review: review,
emotionTags: emotionTags,
primaryEmotion: primaryEmotion.toDomain() ?? .other,
detailEmotions: detailEmotions.map { $0.toDomain() },
createdAt: DateParser.parseISO8601(createdAt) ?? .distantPast,
updatedAt: DateParser.parseISO8601(updatedAt),
bookTitle: bookTitle,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,18 @@ import BKDomain

public struct PrimaryEmotionResponseDTO: Decodable {
let code: String
let displayName: Emotion
let displayName: String

func toDomain() -> PrimaryEmotion {
return PrimaryEmotion(rawValue: code) ?? .other
}
}

public struct DetailEmotionResponseDTO: Decodable {
let id: String
let name: String

func toDomain() -> DetailEmotion {
return DetailEmotion(id: id, name: name)
}
}
10 changes: 9 additions & 1 deletion src/Projects/BKData/Sources/DataAssembly.swift
Original file line number Diff line number Diff line change
Expand Up @@ -165,9 +165,17 @@ public struct DataAssembly: Assembly {
pushTokenStore: pushTokenStore
)
}

container.register(type: ExternalLinkRepository.self) { _ in
return DefaultExternalLinkRepository()
}

container.register(
type: EmotionRepository.self,
scope: .singleton
) { _ in
@Autowired(name: "OAuth") var networkProvider: NetworkProvider
return DefaultEmotionRepository(networkProvider: networkProvider)
}
}
}
Loading