Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 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
Copy link
Member

Choose a reason for hiding this comment

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

Completable 사용이 좋을 것 같습니다

Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import Foundation

struct EmptyResponse: Decodable {}

struct AdminAPIEndpoint {

// MARK: - Store List
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,3 @@ struct GetAdminPopUpStoreDetailResponseDTO: Decodable {
let imageUrl: String
}
}

// MARK: - Empty Response
struct EmptyResponse: Decodable {}
Copy link
Member

Choose a reason for hiding this comment

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

해당 부분이 삭제되고 컨버터가 생긴것일까요? 프로젝트의 일반적인 구조와 다르게 사용한 이유가 궁금합니다

Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,6 @@ struct MapPopUpStoreDTO: Codable {
let markerSnippet: String
let mainImageUrl: String?
let bookmarkYn: Bool?

func toDomain() -> MapPopUpStore {
return MapPopUpStore(
id: id,
category: categoryName,
name: name,
address: address,
startDate: startDate,
endDate: endDate,
latitude: latitude,
longitude: longitude,
markerId: markerId,
markerTitle: markerTitle,
markerSnippet: markerSnippet,
mainImageUrl: mainImageUrl

)
}
}

struct GetViewBoundPopUpStoreListResponse: Decodable {
Expand Down
191 changes: 97 additions & 94 deletions Poppool/Poppool/DataLayer/RepositoryImpl/AdminRepositoryImpl.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import Foundation

import Alamofire
import Foundation
Copy link
Member

Choose a reason for hiding this comment

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

임포트 순서가 퍼스트 파티, 서드 파티 순서로 배치되어 있으면 좋을 것 같습니다.

import RxSwift

final class AdminRepositoryImpl: AdminRepository {
Expand All @@ -15,7 +14,7 @@ final class AdminRepositoryImpl: AdminRepository {
}

// MARK: - Store Methods
func fetchStoreList(query: String?, page: Int, size: Int) -> Observable<GetAdminPopUpStoreListResponseDTO> {
func fetchStoreList(query: String?, page: Int, size: Int) -> Observable<[AdminStore]> {
let endpoint = AdminAPIEndpoint.fetchStoreList(
query: query,
page: page,
Expand All @@ -25,131 +24,135 @@ final class AdminRepositoryImpl: AdminRepository {
with: endpoint,
interceptor: tokenInterceptor
)
.map { response in
response.popUpStoreList?.map {
AdminStore(id: $0.id, name: $0.name, categoryName: $0.categoryName, mainImageUrl: $0.mainImageUrl)
} ?? []
}
}

func fetchStoreDetail(id: Int64) -> Observable<GetAdminPopUpStoreDetailResponseDTO> {
func fetchStoreDetail(id: Int64) -> Observable<AdminStoreDetail> {
let endpoint = AdminAPIEndpoint.fetchStoreDetail(id: id)
return provider.requestData(
with: endpoint,
interceptor: tokenInterceptor
)
.map { dto in
AdminStoreDetail(
id: dto.id,
name: dto.name,
categoryId: dto.categoryId,
categoryName: dto.categoryName,
description: dto.desc,
address: dto.address,
startDate: dto.startDate,
endDate: dto.endDate,
createUserId: dto.createUserId,
createDateTime: dto.createDateTime,
mainImageUrl: dto.mainImageUrl,
bannerYn: dto.bannerYn,
images: dto.imageList.map {
AdminStoreDetail.StoreImage(
id: $0.id,
imageUrl: $0.imageUrl
)
},
latitude: dto.latitude,
longitude: dto.longitude,
markerTitle: dto.markerTitle,
markerSnippet: dto.markerSnippet
)
}
.catch { error in
if case .responseSerializationFailed = error as? AFError {
// 빈 데이터 응답시 기본값 반환
return Observable.just(GetAdminPopUpStoreDetailResponseDTO.empty)
return Observable.empty()
}
throw error
}
}

func createStore(request: CreatePopUpStoreRequestDTO) -> Observable<EmptyResponse> {
Logger.log(message: "createStore API 호출 시작", category: .info)
let endpoint = AdminAPIEndpoint.createStore(request: request)
Logger.log(message: "Request URL: \(endpoint.baseURL + endpoint.path)", category: .info)
Logger.log(message: "Request Body: \(request)", category: .info)

return provider.requestData(
with: endpoint,
interceptor: tokenInterceptor
)
.catch { error -> Observable<EmptyResponse> in
if case .responseSerializationFailed(let reason) = error as? AFError,
case .inputDataNilOrZeroLength = reason {
// 빈 응답 데이터일 경우 성공으로 간주
Logger.log(message: "빈 응답 데이터 처리: 성공으로 간주", category: .info)
return Observable.just(EmptyResponse())
}
throw error
}
.do(
onNext: { _ in
Logger.log(message: "createStore API 호출 성공", category: .info)
},
onError: { error in
Logger.log(message: "createStore API 호출 실패: \(error)", category: .error)
}
func createStore(params: CreateStoreParams) -> Completable {
let dto = CreatePopUpStoreRequestDTO(
name: params.name,
categoryId: params.categoryId,
desc: params.desc,
address: params.address,
startDate: params.startDate,
endDate: params.endDate,
mainImageUrl: params.mainImageUrl,
imageUrlList: params.imageUrlList,
latitude: params.latitude,
longitude: params.longitude,
markerTitle: params.markerTitle,
markerSnippet: params.markerSnippet,
startDateBeforeEndDate: params.startDateBeforeEndDate
)
let endpoint = AdminAPIEndpoint.createStore(request: dto)
return provider.request(with: endpoint, interceptor: tokenInterceptor)
}

func updateStore(request: UpdatePopUpStoreRequestDTO) -> Observable<EmptyResponse> {
let endpoint = AdminAPIEndpoint.updateStore(request: request)

Logger.log(message: """
Store Update 요청:
URL: \(endpoint.baseURL + endpoint.path)
Method: PUT
Request: \(request)
""", category: .debug)

return provider.requestData(
with: endpoint,
interceptor: tokenInterceptor
func updateStore(params: UpdateStoreParams) -> Completable {
let dto = UpdatePopUpStoreRequestDTO(
popUpStore: UpdatePopUpStoreRequestDTO.PopUpStore(
id: params.id,
name: params.name,
categoryId: params.categoryId,
desc: params.desc,
address: params.address,
startDate: params.startDate,
endDate: params.endDate,
mainImageUrl: params.mainImageUrl,
bannerYn: !params.mainImageUrl.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty,
imageUrl: params.imageUrlList.compactMap { $0 },
startDateBeforeEndDate: params.startDateBeforeEndDate
),
location: UpdatePopUpStoreRequestDTO.Location(
latitude: params.latitude,
longitude: params.longitude,
markerTitle: params.markerTitle,
markerSnippet: params.markerSnippet
),
imagesToAdd: params.imageUrlList.compactMap { $0 },
imagesToDelete: params.imagesToDelete
)
.catch { error -> Observable<EmptyResponse> in
Logger.log(message: "Update Store Error 발생: \(error)", category: .error)

if let afError = error as? AFError {
switch afError {
case .responseSerializationFailed(let reason):
Logger.log(message: "Serialization 실패 reason: \(reason)", category: .error)
if case .inputDataNilOrZeroLength = reason {
Logger.log(message: "빈 응답 데이터 - 성공으로 처리", category: .info)
return Observable.just(EmptyResponse())
}
default:
Logger.log(message: "기타 AFError: \(afError)", category: .error)
}
}

throw error
}
.do(onNext: { _ in
Logger.log(message: "Store Update 성공", category: .info)
}, onError: { error in
Logger.log(message: "Store Update 최종 실패: \(error)", category: .error)
})
let endpoint = AdminAPIEndpoint.updateStore(request: dto)
return provider.request(with: endpoint, interceptor: tokenInterceptor)
}

func deleteStore(id: Int64) -> Observable<EmptyResponse> {
Logger.log(message: "deleteStore API 호출 시작", category: .info)
func deleteStore(id: Int64) -> Completable {
let endpoint = AdminAPIEndpoint.deleteStore(id: id)
return provider.request(with: endpoint, interceptor: tokenInterceptor)
.andThen(Observable.just(EmptyResponse()))
.do(
onNext: { _ in
Logger.log(message: "deleteStore API 호출 성공", category: .info)
},
onError: { error in
Logger.log(message: "deleteStore API 호출 실패: \(error)", category: .error)
}
)
}

// MARK: - Notice Methods
func createNotice(request: CreateNoticeRequestDTO) -> Observable<EmptyResponse> {
let endpoint = AdminAPIEndpoint.createNotice(request: request)
return provider.requestData(
with: endpoint,
interceptor: tokenInterceptor
func createNotice(params: CreateNoticeParams) -> Completable {
let dto = CreateNoticeRequestDTO(
title: params.title,
content: params.content,
imageUrlList: params.imageUrlList
)
let endpoint = AdminAPIEndpoint.createNotice(request: dto)
return provider.request(with: endpoint, interceptor: tokenInterceptor)
}

func updateNotice(id: Int64, request: UpdateNoticeRequestDTO) -> Observable<EmptyResponse> {
let endpoint = AdminAPIEndpoint.updateNotice(id: id, request: request)
return provider.requestData(
with: endpoint,
interceptor: tokenInterceptor
func updateNotice(params: UpdateNoticeParams) -> Completable {
let dto = UpdateNoticeRequestDTO(
title: params.title,
content: params.content,
imageUrlList: params.imageUrlList,
imagesToDelete: params.imagesToDelete
)
let endpoint = AdminAPIEndpoint.updateNotice(id: params.id, request: dto)
return provider.request(with: endpoint, interceptor: tokenInterceptor)
}

func deleteNotice(id: Int64) -> Observable<EmptyResponse> {
func deleteNotice(id: Int64) -> Completable {
let endpoint = AdminAPIEndpoint.deleteNotice(id: id)
return provider.requestData(
with: endpoint,
interceptor: tokenInterceptor
)
return provider.request(with: endpoint, interceptor: tokenInterceptor)
}
}

// Helper extension - keeping this for utility purposes
extension GetAdminPopUpStoreDetailResponseDTO {
static var empty: GetAdminPopUpStoreDetailResponseDTO {
return GetAdminPopUpStoreDetailResponseDTO(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import Foundation

import RxSwift

final class AuthAPIRepositoryImpl: AuthAPIRepository {
Expand All @@ -20,8 +19,11 @@ final class AuthAPIRepositoryImpl: AuthAPIRepository {
}
}

func postTokenReissue() -> Observable<PostTokenReissueResponseDTO> {
func postTokenReissue() -> Observable<PostTokenReissueResponse> {
let endPoint = AuthAPIEndPoint.postTokenReissue()
return provider.requestData(with: endPoint, interceptor: tokenInterceptor)
.map { responseDTO in
return responseDTO.toDomain()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,21 @@ final class CommentAPIRepositoryImpl: CommentAPIRepository {
self.provider = provider
}

func postCommentAdd(request: PostCommentRequestDTO) -> Completable {
let endPoint = CommentAPIEndPoint.postCommentAdd(request: request)
func postCommentAdd(popUpStoreId: Int64, content: String?, commentType: String?, imageUrlList: [String?]) -> Completable {
let requestDTO = PostCommentRequestDTO(popUpStoreId: popUpStoreId, content: content, commentType: commentType, imageUrlList: imageUrlList)
let endPoint = CommentAPIEndPoint.postCommentAdd(request: requestDTO)
return provider.request(with: endPoint, interceptor: tokenInterceptor)
}

func deleteComment(request: DeleteCommentRequestDTO) -> Completable {
let endPoint = CommentAPIEndPoint.deleteComment(request: request)
func deleteComment(popUpStoreId: Int64, commentId: Int64) -> Completable {
let requestDTO = DeleteCommentRequestDTO(popUpStoreId: popUpStoreId, commentId: commentId)
let endPoint = CommentAPIEndPoint.deleteComment(request: requestDTO)
return provider.request(with: endPoint, interceptor: tokenInterceptor)
}

func editComment(request: PutCommentRequestDTO) -> Completable {
let endPoint = CommentAPIEndPoint.editComment(request: request)
func editComment(popUpStoreId: Int64, commentId: Int64, content: String?, imageUrlList: [PutCommentImageDataRequestDTO]?) -> Completable {
let requestDTO = PutCommentRequestDTO(popUpStoreId: popUpStoreId, commentId: commentId, content: content, imageUrlList: imageUrlList)
let endPoint = CommentAPIEndPoint.editComment(request: requestDTO)
return provider.request(with: endPoint, interceptor: tokenInterceptor)
}
}
Copy link
Member

Choose a reason for hiding this comment

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

MapDomainModelConverter가 필요한 이유가 있을까요? MapPopUpStoreDTO에 메서드를 추가하는 방식 말고 왜 이방식으로 Entity로 변환하는지 궁금합니다.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

대부분의 DTO에서 toDoMain으로 매핑하여 처리하는것에 대한 의문에서 시작했습니다 .. 이부분만 우선적으로 진행해뒀습니다만
네트워크레이어에서 매핑까지 하는것보다 도메인 모델이 DTO에 조금의 의존성도 가지지 않으려면 분리하는게 좋지않을까 ? 란생각으로 분리하게되었습니다 DTO 마다 ToDoMain을 제각각 구현하는것보다 각기 컨버터 를 추가하는것도 나쁘지않을것 같아서요!

Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import Foundation

struct MapDomainModelConverter {
/// MapPopUpStoreDTO
/// → MapPopUpStore 도메인 모델로 변환
static func convert(_ dto: MapPopUpStoreDTO) -> MapPopUpStore {
return MapPopUpStore(
id: dto.id,
category: dto.categoryName,
name: dto.name,
address: dto.address,
startDate: dto.startDate,
endDate: dto.endDate,
latitude: dto.latitude,
longitude: dto.longitude,
markerId: dto.markerId,
markerTitle: dto.markerTitle,
markerSnippet: dto.markerSnippet,
mainImageUrl: dto.mainImageUrl
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Foundation
import RxSwift

final class MapDirectionRepositoryImpl: MapDirectionRepository {

private let provider: Provider
private let tokenInterceptor = TokenInterceptor()

Expand Down
Loading