Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
1197bb0
del: #28 - ๋ ˆ๊ฑฐ์‹œ ์ฝ”๋“œ ์‚ญ์ œ
ChoiAnYong Feb 14, 2026
7b672fd
refactor: #28 - api ๊ทธ๋ฃน์— ๋”ฐ๋ฅธ TargetType ๋ถ„๋ฅ˜
ChoiAnYong Feb 14, 2026
3f1815f
refactor: #28 - api ๊ทธ๋ฃน์— ๋”ฐ๋ฅธ Service ๋ถ„๋ฅ˜
ChoiAnYong Feb 14, 2026
bb7bf31
refactor: #28 - api ๊ทธ๋ฃน์— ๋”ฐ๋ฅธ RepositoryProtocol ๊ตฌํ˜„
ChoiAnYong Feb 14, 2026
361a0d8
refactor: #29 - api ๊ทธ๋ฃน์— ๋”ฐ๋ฅธ Repository ๊ตฌํ˜„
ChoiAnYong Feb 14, 2026
f437229
fix: #29 - api ์ŠคํŽ™ ๋ณ€๊ฒฝ ๋Œ€์‘
ChoiAnYong Feb 14, 2026
dfca50e
del: #29 - ๋ ˆ๊ฑฐ์‹œ ์ฝ”๋“œ ์‚ญ์ œ
ChoiAnYong Feb 15, 2026
e13e0a6
feat: #29 - ServiceFactory ๊ตฌํ˜„
ChoiAnYong Feb 15, 2026
f96335f
feat: #29 - Tranform ๊ตฌํ˜„
ChoiAnYong Feb 15, 2026
a54151c
chore: #29 - ๋„ค์ด๋ฐ ๋ณ€๊ฒฝ
ChoiAnYong Feb 15, 2026
ca84a7e
refactor: #29 - HomeUsecase ๋ฆฌํŽ™ํ† ๋ง
ChoiAnYong Feb 15, 2026
081c9bb
chore: #29 - ์˜์กด์„ฑ ๋ณ€๊ฒฝ
ChoiAnYong Feb 15, 2026
d2ce738
fix: #28 - api ์ŠคํŽ™ ๋ณ€๊ฒฝ ๋Œ€์‘
ChoiAnYong Feb 16, 2026
c22cc8e
fix: #28 - ํƒ€๊ฒŸ ํƒ€์ž… ์˜คํƒ€ ์ˆ˜์ •
ChoiAnYong Feb 16, 2026
db1cb1f
del: #28 - ๋ ˆ๊ฑฐ์‹œ ์ฝ”๋“œ ์‚ญ์ œ
ChoiAnYong Feb 16, 2026
5711c76
feat: #28 - FollowDetailUsecase ๊ตฌํ˜„
ChoiAnYong Feb 16, 2026
7336680
refactor: #28 - ์˜์กด์„ฑ ์ฃผ์ž… ๋ฆฌํŽ™ํ† ๋ง
ChoiAnYong Feb 16, 2026
b6155df
chore: #28 - import ์ •๋ ฌ
ChoiAnYong Feb 16, 2026
9a0b330
chore: #28 - ์ž„์‹œ accessToken ๋ณ€๊ฒฝ
ChoiAnYong Feb 16, 2026
48487b8
fix: #28 - ์ž„์‹œ ์˜์กด์„ฑ ์ฃผ์ž…
ChoiAnYong Feb 16, 2026
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
7 changes: 3 additions & 4 deletions Projects/App/Project.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@
// Created by ์ตœ์•ˆ์šฉ on 1/13/26.
//

import ConfigPlugin
import DependencyPlugin
import EnvPlugin
import ProjectDescription
import ProjectDescriptionHelpers
import EnvPlugin
import DependencyPlugin
import ConfigPlugin


let project = Project.makeModule(
name: "App",
Expand Down
47 changes: 45 additions & 2 deletions Projects/App/Sources/Application/AppComponent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,52 @@ import RootFeature
import RIBs

final class AppComponent: Component<EmptyDependency>, RootDependency {
private var travelTemplateRepository: TravelTemplateRepositoryInterface {
shared {
let service = makeTravelTemplateService(tokenProvider: tokenProvider)
return TravelTemplateRepository(service: service)
}
}

private var travelProgramRepository: TravelProgramRepositoryInterface {
shared {
let service = makeTravelProgramService()
return TravelProgramRepository(service: service)
}
}

private var userTravelRepository: UserTravelRepositoryInterface {
shared {
let service = makeUserTravelService(tokenProvider: tokenProvider)
return UserTravelRepository(service: service)
}
}

private var placeRepository: PlaceRepositoryInterface {
shared {
let service = makePlaceService()
return PlaceRepository(service: service)
}
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

var homeUsecase: HomeUsecaseProtocol {
let homeRepository = HomeRepository(homeService: makeHomeService(tokenProvider: tokenProvider))
return HomeUsecase(repository: homeRepository)
shared {
HomeUsecase(
travelTemplateRepository: travelTemplateRepository,
travelRepository: travelProgramRepository,
userTravelRepository: userTravelRepository
)
}
}

var followDetailUsecase: FollowDetailUsecaseProtocol {
shared {
FollowDetailUsecase(
travelTemplateRepository: travelTemplateRepository,
userTravelRepository: userTravelRepository,
placeRepository: placeRepository
)
}
}

var tokenProvider: TokenProviding {
Expand Down
2 changes: 1 addition & 1 deletion Projects/Data/Sources/Adapter/TokenProviderAdapter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@ public final class TokenProviderAdapter: TokenProviding, @unchecked Sendable {

public func accessToken() -> String? {
// tokenRepository.get(.accessToken)
"eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJiYmE3ODIwYS0wMDUzLTQxZDctODdhYi00Zjk2ZWM3ZDI1MTMiLCJpYXQiOjE3NzA5NjQzMDUsImV4cCI6MTc3MTA1MDcwNX0.Sn8wNhZ1Ac-ETZDsOiSMMHHaALJNXxNKrbN_-4xD5REcVa2tJ0NiafhTKlbIuYafL1Acd9dDMIHjx3H33c5w8w"
"eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJiYmE3ODIwYS0wMDUzLTQxZDctODdhYi00Zjk2ZWM3ZDI1MTMiLCJpYXQiOjE3NzEyMzU1MDUsImV4cCI6MTc3MTMyMTkwNX0.SfuVfF9FFpnFcUkGM7zC7mlE7-f8zo3NgG5mm86xekLnurFhGgnTIhwpew7FinguOex0smsnx--EHsMaED8D5A"
}
Comment on lines 20 to 23
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 | ๐Ÿ”ด Critical

ํ•˜๋“œ์ฝ”๋”ฉ๋œ JWT ํ† ํฐ์ด ์†Œ์Šค ์ฝ”๋“œ์— ์ปค๋ฐ‹๋˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

tokenRepository.get(.accessToken) ํ˜ธ์ถœ์ด ์ฃผ์„ ์ฒ˜๋ฆฌ๋œ ์ƒํƒœ์—์„œ, ์‹ค์ œ JWT ๋ฌธ์ž์—ด์ด ์†Œ์Šค ์ฝ”๋“œ์— ์ง์ ‘ ํ•˜๋“œ์ฝ”๋”ฉ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ํ† ํฐ์ด git ํžˆ์Šคํ† ๋ฆฌ์— ๋‚จ๊ฒŒ ๋˜๋ฉด, ๋ฆฌํฌ์ง€ํ† ๋ฆฌ์— ์ ‘๊ทผ ๊ฐ€๋Šฅํ•œ ๋ˆ„๊ตฌ๋‚˜ ์ด ํ† ํฐ์„ ์ถ”์ถœํ•˜์—ฌ ๋ฌด๋‹จ ์ ‘๊ทผ์— ์•…์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Gitleaks ์ •์  ๋ถ„์„์—์„œ๋„ ์ด ๋ผ์ธ์„ high ์‹ฌ๊ฐ๋„๋กœ ํ”Œ๋ž˜๊ทธํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

์ž„์‹œ ํ…Œ์ŠคํŠธ ๋ชฉ์ ์ด๋ผ๋ฉด, ์ตœ์†Œํ•œ #if DEBUG ์ปดํŒŒ์ผ ์กฐ๊ฑด์œผ๋กœ ๊ฐ์‹ธ๊ณ , ๊ฐ€๋Šฅํ•˜๋ฉด gitignore๋œ xcconfig ๋˜๋Š” ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋ฅผ ํ†ตํ•ด ์ฃผ์ž…ํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๋ณ€๊ฒฝํ•ด ์ฃผ์„ธ์š”.

๐Ÿ”’ ์ œ์•ˆ: DEBUG ์กฐ๊ฑด๋ถ€ ์ปดํŒŒ์ผ ๋ฐ ์ฃผ์„ ํ•ด์ œ
     public func accessToken() -> String? {
-//        tokenRepository.get(.accessToken)
-        "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJiYmE3ODIwYS0wMDUzLTQxZDctODdhYi00Zjk2ZWM3ZDI1MTMiLCJpYXQiOjE3NzEyMzU1MDUsImV4cCI6MTc3MTMyMTkwNX0.SfuVfF9FFpnFcUkGM7zC7mlE7-f8zo3NgG5mm86xekLnurFhGgnTIhwpew7FinguOex0smsnx--EHsMaED8D5A"
+        `#if` DEBUG
+        // TODO: xcconfig ๋˜๋Š” ํ™˜๊ฒฝ ๋ณ€์ˆ˜์—์„œ ํ…Œ์ŠคํŠธ ํ† ํฐ์„ ์ฃผ์ž…ํ•˜๋„๋ก ๋ณ€๊ฒฝ
+        return ProcessInfo.processInfo.environment["DEBUG_ACCESS_TOKEN"]
+        `#else`
+        return tokenRepository.get(.accessToken)
+        `#endif`
     }

Based on learnings: "Do not hardcode temporary/local test secrets in code. Inject secrets via a gitignored xcconfig and wire them into Info.plist, then read only in DEBUG builds."

๐Ÿ“ Committable suggestion

โ€ผ๏ธ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public func accessToken() -> String? {
// tokenRepository.get(.accessToken)
"eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJiYmE3ODIwYS0wMDUzLTQxZDctODdhYi00Zjk2ZWM3ZDI1MTMiLCJpYXQiOjE3NzA5NjQzMDUsImV4cCI6MTc3MTA1MDcwNX0.Sn8wNhZ1Ac-ETZDsOiSMMHHaALJNXxNKrbN_-4xD5REcVa2tJ0NiafhTKlbIuYafL1Acd9dDMIHjx3H33c5w8w"
"eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJiYmE3ODIwYS0wMDUzLTQxZDctODdhYi00Zjk2ZWM3ZDI1MTMiLCJpYXQiOjE3NzEyMzU1MDUsImV4cCI6MTc3MTMyMTkwNX0.SfuVfF9FFpnFcUkGM7zC7mlE7-f8zo3NgG5mm86xekLnurFhGgnTIhwpew7FinguOex0smsnx--EHsMaED8D5A"
}
public func accessToken() -> String? {
`#if` DEBUG
// TODO: xcconfig ๋˜๋Š” ํ™˜๊ฒฝ ๋ณ€์ˆ˜์—์„œ ํ…Œ์ŠคํŠธ ํ† ํฐ์„ ์ฃผ์ž…ํ•˜๋„๋ก ๋ณ€๊ฒฝ
return ProcessInfo.processInfo.environment["DEBUG_ACCESS_TOKEN"]
`#else`
return tokenRepository.get(.accessToken)
`#endif`
}
๐Ÿงฐ Tools
๐Ÿช› Gitleaks (8.30.0)

[high] 22-22: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)

๐Ÿค– Prompt for AI Agents
In `@Projects/Data/Sources/Adapter/TokenProviderAdapter.swift` around lines 20 -
23, accessToken() currently returns a hardcoded JWT; uncomment and restore the
tokenRepository.get(.accessToken) call and remove the literal token, and ensure
test-only injection by wrapping a fallback debug-only provider in `#if` DEBUG
(read token from a git-ignored xcconfig or environment var) so production builds
never include the secret; specifically update the accessToken() implementation
to call tokenRepository.get(.accessToken) and add a debug-only code path that
reads a config/env value for local testing.

}
15 changes: 0 additions & 15 deletions Projects/Data/Sources/DI/FollowServiceFactory.swift

This file was deleted.

17 changes: 0 additions & 17 deletions Projects/Data/Sources/DI/HomeServiceFactory.swift

This file was deleted.

16 changes: 16 additions & 0 deletions Projects/Data/Sources/DI/PlaceServiceFactory.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// PlaceServiceFactory.swift
// Data
//
// Created by ์ตœ์•ˆ์šฉ on 2/15/26.
// Copyright ยฉ 2026 NDGL-iOS. All rights reserved.
//

import Foundation

import Domain
import Networks

public func makePlaceService() -> PlaceServiceProtocol {
return PlaceService()
}
16 changes: 16 additions & 0 deletions Projects/Data/Sources/DI/TravelProgramServiceFactory.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// TravelProgramServiceFactory.swift
// Data
//
// Created by ์ตœ์•ˆ์šฉ on 2/15/26.
// Copyright ยฉ 2026 NDGL-iOS. All rights reserved.
//

import Foundation

import Domain
import Networks

public func makeTravelProgramService() -> TravelProgramServiceProtocol {
return TravelProgramService()
}
19 changes: 0 additions & 19 deletions Projects/Data/Sources/DI/TravelServiceFactory.swift

This file was deleted.

20 changes: 20 additions & 0 deletions Projects/Data/Sources/DI/TravelTemplateServiceFactory.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//
// TravelTemplateServiceFactory.swift
// Data
//
// Created by ์ตœ์•ˆ์šฉ on 2/15/26.
// Copyright ยฉ 2026 NDGL-iOS. All rights reserved.
//

import Foundation

import Domain
import Networks

import Moya

public func makeTravelTemplateService(tokenProvider: TokenProviding) -> TravelTemplateServiceProtocol {
let provider: MoyaProvider<TravelTemplateAPI> = NetworkProviderFactory.makeAuthenticatedProvider(tokenProvider: tokenProvider)

return TravelTemplateService(provider: provider)
}
20 changes: 20 additions & 0 deletions Projects/Data/Sources/DI/UserTravelServiceFactory.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//
// UserTravelServiceFactory.swift
// Data
//
// Created by ์ตœ์•ˆ์šฉ on 2/15/26.
// Copyright ยฉ 2026 NDGL-iOS. All rights reserved.
//

import Foundation

import Domain
import Networks

import Moya

public func makeUserTravelService(tokenProvider: TokenProviding) -> UserTravelServiceProtocol {
let provider: MoyaProvider<UserTravelAPI> = NetworkProviderFactory.makeAuthenticatedProvider(tokenProvider: tokenProvider)

return UserTravelService(provider: provider)
}
52 changes: 0 additions & 52 deletions Projects/Data/Sources/Repository/Home/HomeRepository.swift

This file was deleted.

44 changes: 44 additions & 0 deletions Projects/Data/Sources/Repository/Place/PlaceRepository.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//
// PlaceRepository.swift
// Data
//
// Created by ์ตœ์•ˆ์šฉ on 2/14/26.
// Copyright ยฉ 2026 NDGL-iOS. All rights reserved.
//

import Foundation

import Domain
import Networks

public final class PlaceRepository: PlaceRepositoryInterface {
private let service: PlaceServiceProtocol

public init(service: PlaceServiceProtocol) {
self.service = service
}

public func searchPlaces() async throws -> Int {
do {
return try await service.searchPlaces()
} catch {
throw error.toNDGLError()
}
}

public func fetchPlacePhotos(googlePlaceId: String) async throws -> [PlacePhoto] {
do {
return try await service.getPlacePhotos(googlePlaceId: googlePlaceId).toDomain()
} catch {
throw error.toNDGLError()
}
}

public func fetchPlaceDetail(googlePlaceId: String) async throws -> PlaceDetail {
do {
return try await service.getPlaceDetails(googlePlaceId: googlePlaceId).toDomain()
} catch {
throw error.toNDGLError()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
// TravelProgramRepository.swift
// Data
//
// Created by ์ตœ์•ˆ์šฉ on 2/14/26.
// Copyright ยฉ 2026 NDGL-iOS. All rights reserved.
//

import Foundation

import Domain
import Networks

public final class TravelProgramRepository: TravelProgramRepositoryInterface {
private let service: TravelProgramServiceProtocol

public init(service: TravelProgramServiceProtocol) {
self.service = service
}

public func fetchCategoryList() async throws -> [TripCategory] {
do {
return try await service.getTravelPrograms().map { $0.toDomain() }
} catch {
throw error.toNDGLError()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//
// TravelTemplateRepository.swift
// Data
//
// Created by ์ตœ์•ˆ์šฉ on 2/14/26.
// Copyright ยฉ 2026 NDGL-iOS. All rights reserved.
//

import Foundation

import Domain
import Networks

public final class TravelTemplateRepository: TravelTemplateRepositoryInterface {
private let service: TravelTemplateServiceProtocol

public init(service: TravelTemplateServiceProtocol) {
self.service = service
}

public func fetchPlaces(travelId: Int, day: Int) async throws -> [TravelPlace] {
do {
return try await service.getItinerary(travelId: travelId, day: day).toDomain()
} catch {
throw error.toNDGLError()
}
}

public func fetchTravelDetail(id: Int) async throws -> TravelDetail {
do {
return try await service.getContentCard(id: id).toDomain()
} catch {
throw error.toNDGLError()
}
}

public func searchTemplate() async throws -> Int {
do {
return try await service.searchTemplate()
} catch {
throw error.toNDGLError()
}
}

public func fetchPopularTripList(id: Int?, page: Int?, size: Int?) async throws -> [TripInfo] {
do {
return try await service.getPopularTripList(id: id, page: page, size: size).toDomain()
} catch {
throw error.toNDGLError()
}
}

public func fetchRecommendTripList(page: Int?, size: Int?) async throws -> [TripInfo] {
do {
return try await service.getRecommendTripList(page: page, size: size).toDomain()
} catch {
throw error.toNDGLError()
}
}
}
Loading