Conversation
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
Walkthrough이 변경은 DI(Dependency Injection) 구조를 전면적으로 리팩토링하여, 기존의 명시적 생성자 주입 방식에서 프로퍼티 래퍼(@injected)를 활용한 간결한 주입 방식으로 전환했습니다. DIContainer 및 Injected 래퍼의 구현도 개선되었으며, DI 사용 방식이 반복적인 타입 명시 없이 간단해졌습니다. Changes
Sequence Diagram(s)sequenceDiagram
participant App as AppDelegate
participant DI as DIContainer
participant Repo as Repository/UseCase/ViewModel
App->>DI: register(Type.self, DefaultImpl())
Repo->>DI: @Injected var dep: Type
Repo->>DI: DIContainer.resolve(Type.self)
DI-->>Repo: 반환된 인스턴스(등록된 구현체)
Assessment against linked issues
Assessment against linked issues: Out-of-scope changes해당 변경 내역에서 이슈 #324의 목표와 무관한 기능적 코드 변경은 발견되지 않았습니다. Poem
✨ Finishing Touches
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 5
♻️ Duplicate comments (4)
Projects/Data/Sources/Repository/DefaultRegularAlarmRepository.swift (1)
18-21: DI 등록 중복 코멘트위
DefaultFavoritesRepository와 동일하게,CoreDataService·NetworkService가 주입되기 전에 객체가 생성되면 크래시가 발생합니다. 앞서 남긴 코멘트를 참고해 주세요.Projects/Domain/Sources/UseCase/DefaultSearchUseCase.swift (1)
18-20: 의존성 주입 검증 – 중복 안내
StationListRepository,LocationService도 DIContainer 에서 누락 시 크래시하므로 앞선 스크립트 패턴을 활용해 등록을 확인해 주세요.Projects/Domain/Sources/UseCase/DefaultAddRegularAlarmUseCase.swift (1)
16-18: 의존성 주입 중복 안내
LocalNotificationService,RegularAlarmRepository의 등록 여부 역시 앞선 코멘트와 동일합니다.Projects/Domain/Sources/UseCase/DefaultBusStopUseCase.swift (1)
17-20: 의존성 주입 중복 안내세 개의 Repository/Service 등록 상황을 확인해 주세요. 중복 코멘트이므로 자세한 내용은 최초 코멘트를 참고 바랍니다.
🧹 Nitpick comments (11)
.swiftlint.yml (2)
3-3: 새 exclude 항목이 실제 경로와 일치하는지 확인
Projects/App/Sources/AppDelegate+Register.swift파일이 이동·리네임될 경우 린트 제외가 무용지물이 됩니다.
Tuist가 생성한 실제 소스 경로(Sources/Application/등)와 정확히 맞는지 double-check 부탁드립니다.
32-34: line_length 경고 한도 상향 → 점진적 확산 주의경고선을 80 → 90자로 늘리면 이후 PR에서 줄 길이 규칙이 느슨해질 수 있습니다.
장문 체이닝이 잦은 Rx 코드 특성을 고려해 예외 룰(swiftlint:disable:this line_length)을 국소 적용하는 방법도 검토해 주세요.
코드 리뷰 난이도에 영향이 있으니 팀 합의가 있었는지 확인 바랍니다.Projects/Feature/AlarmFeature/Sources/ViewModel/AddRegularAlarmViewModel.swift (1)
19-20: @injected 제네릭 생략: 미등록 시 런타임 크래시 위험제네릭 추론 덕분에 코드가 간결해졌지만, 컴파일 타임 검증이 사라져 등록 누락을 IDE에서 캐치할 수 없습니다.
실제 앱 구동 전 unit test 또는 UI test 단계에서DIContainer.resolve()실패를 자동 검출할 테스트 훅을 추가하는 것을 권장드립니다.Projects/Feature/NearMapFeature/Sources/ViewModel/NearMapViewModel.swift (1)
13-14: 의존성 주입 간소화는 긍정적이나, 순환 의존성 확인 필요
NearMapUseCase가 내부에서 다시NearMapViewModel을 참조하거나, 동일 스코프에@Injected로 들어오는 객체 간에 순환 참조가 없는지 확인해 주세요.
Rx 스트림이 강한 캡처를 만들면disposeBag이 해제되지 않아 메모리 릭으로 이어질 수 있습니다.Projects/Domain/Sources/UseCase/DefaultNearMapUseCase.swift (1)
18-20: 생성자 DI → 프로퍼티 DI 전환에 따른 테스트 코드 영향 점검단위 테스트에서 mock 객체 주입이 어려워질 수 있습니다. 테스트 대상이라면
init(container:)와 같은 테스트 전용 초기화 방법을 고려해주세요.Projects/Feature/BusStopFeature/Sources/ViewModel/BusStopViewModel.swift (1)
10-11: 런타임 DI 미등록 시 크래시 – ViewModel 초기화 단계에서 선제 검증을 고려하세요
@Injected프로퍼티가 실제 사용 시점(예:transform호출)까지 DIContainer 해석을 지연시키므로, 등록이 누락된 경우 앱이 화면 로드 이후에야 크래시가 발생합니다.
BusStopViewModel생성자 내부에서DIContainer.assertRegistered(BusStopUseCase.self)같은 경량 체크를 추가하거나, App 시작 단계에서 일괄 검증하는 방법을 고려하면 문제를 더 빠르게 발견-대응할 수 있습니다.
Projects/Domain/Sources/UseCase/DefaultRegularAlarmUseCase.swift (1)
16-18: 테스트 주입 경로 소실 – 가짜/모킹 객체를 넘길 수 있는 생성자 유지 권장모든 의존성을
@Injected로 전환하면서 외부에서 구체 구현을 주입할 수 있는 초기화 메서드가 사라졌습니다.
단위 테스트나 Preview 환경에서 Stub/Mock 객체를 주입하기 어려워지는 단점이 있습니다.간단한 보조 이니셜라이저 추가로 가용성을 유지할 수 있습니다.
public class DefaultRegularAlarmUseCase: RegularAlarmUseCase { @Injected private var localNotificationService: LocalNotificationService @Injected private var regularAlarmRepository: RegularAlarmRepository - public init() { } + // 기본 DIContainer 경로 + public init() { } + + /// 테스트/프리뷰 전용 주입 경로 + public init( + localNotificationService: LocalNotificationService, + regularAlarmRepository: RegularAlarmRepository + ) { + self._localNotificationService = Injected(wrappedValue: localNotificationService) + self._regularAlarmRepository = Injected(wrappedValue: regularAlarmRepository) + }추가 생성자는 실제 앱 코드에는 영향이 없으며, 테스트에서만 사용 가능합니다.
Also applies to: 22-22
Projects/Domain/Sources/UseCase/DefaultFavoritesUseCase.swift (1)
17-19: 테스트 편의를 위한 커스텀 이니셜라이저 추가 제안
DefaultFavoritesUseCase도 동일하게 모킹 경로가 제거되었습니다.
위 RegularAlarmUseCase 와 동일 패턴으로 보조 생성자를 제공하면 테스트 작성이 수월해집니다.public final class DefaultFavoritesUseCase: FavoritesUseCase { @Injected private var busStopArrivalInfoRepository: BusStopArrivalInfoRepository @Injected private var favoritesRepository: FavoritesRepository public init() { } + + // 테스트/프리뷰 주입용 + public init( + busStopArrivalInfoRepository: BusStopArrivalInfoRepository, + favoritesRepository: FavoritesRepository + ) { + self._busStopArrivalInfoRepository = Injected(wrappedValue: busStopArrivalInfoRepository) + self._favoritesRepository = Injected(wrappedValue: favoritesRepository) + }실제 코드 경로에는 영향 없이, 테스트에서 원하는 Stub 을 전달할 수 있습니다.
Also applies to: 25-25
Projects/Domain/Sources/UseCase/DefaultSearchUseCase.swift (1)
31-34: 초기 바인딩 호출 시 위치 권한 다이얼로그 트리거 가능성
init()에서bindLocationStatus()/bindRecentSearchList()를 즉시 호출하면, ViewModel 이 메모리에 올라오기만 해도 위치 서비스가 구동될 수 있습니다. UI 흐름에 따라 예상치 못한 권한 다이얼로그가 뜨지 않는지 UX 관점에서 한번 더 검토 바랍니다.Projects/App/Sources/AppDelegate+Register.swift (1)
19-27: 배열형 등록 로직 추상화 권장비슷한 패턴이 반복되고 있어 유지보수성이 떨어집니다. 튜플 배열로 선언 후
forEach로 등록하거나, DIContainer에register(_ instances: [(Any.Type, Any)])등의 헬퍼를 추가해 중복 코드를 제거하면 가독성이 개선됩니다.Projects/Core/Sources/DIContainer/DIContainer.swift (1)
31-32: Analytics 파라미터 키 통일
Analytics.logEvent("DependencyCrash", parameters: ["type": typeName])에서 키가 문자열 리터럴로 하드코딩돼 있습니다. 이후 이벤트 스키마 변경 시 실수 가능성이 높으므로, 상수로 분리하거나enum으로 관리해 스키마를 일관성 있게 유지하는 것을 권장합니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (23)
.swiftlint.yml(2 hunks)Plugins/DependencyPlugin/ProjectDescriptionHelpers/Dependency+ThirdPartyRemote.swift(1 hunks)Projects/App/Sources/AppDelegate+Register.swift(1 hunks)Projects/Core/Project.swift(1 hunks)Projects/Core/Sources/DIContainer/DIContainer.swift(1 hunks)Projects/Core/Sources/DIContainer/Injected.swift(1 hunks)Projects/CoreDataService/Sources/CoreDataService.swift(1 hunks)Projects/Data/Project.swift(0 hunks)Projects/Data/Sources/Repository/DefaultBusStopArrivalInfoRepository.swift(1 hunks)Projects/Data/Sources/Repository/DefaultFavoritesRepository.swift(1 hunks)Projects/Data/Sources/Repository/DefaultRegularAlarmRepository.swift(1 hunks)Projects/Domain/Sources/UseCase/DefaultAddRegularAlarmUseCase.swift(1 hunks)Projects/Domain/Sources/UseCase/DefaultBusStopUseCase.swift(1 hunks)Projects/Domain/Sources/UseCase/DefaultFavoritesUseCase.swift(1 hunks)Projects/Domain/Sources/UseCase/DefaultNearMapUseCase.swift(1 hunks)Projects/Domain/Sources/UseCase/DefaultRegularAlarmUseCase.swift(1 hunks)Projects/Domain/Sources/UseCase/DefaultSearchUseCase.swift(2 hunks)Projects/Feature/AlarmFeature/Sources/ViewModel/AddRegularAlarmViewModel.swift(1 hunks)Projects/Feature/AlarmFeature/Sources/ViewModel/RegularAlarmViewModel.swift(1 hunks)Projects/Feature/BusStopFeature/Sources/ViewModel/BusStopViewModel.swift(1 hunks)Projects/Feature/HomeFeature/Sources/ViewModel/FavoritesViewModel.swift(1 hunks)Projects/Feature/NearMapFeature/Sources/ViewModel/NearMapViewModel.swift(1 hunks)Projects/Feature/SearchFeature/Sources/ViewModel/SearchViewModel.swift(1 hunks)
💤 Files with no reviewable changes (1)
- Projects/Data/Project.swift
🔇 Additional comments (13)
Plugins/DependencyPlugin/ProjectDescriptionHelpers/Dependency+ThirdPartyRemote.swift (1)
26-30: RxSwift 버전 업 시 호환성 확인 필요
upToNextMajor를6.8.0으로 올렸지만,
- 기존 코드에서
RxAtomic,RxRelay등 서브모듈 의존성을 직접 명시하지 않았는지,- iOS Deployment Target 및 다른 서드파티(Swift Concurrency 지원 여부 등)와 충돌이 없는지
를 한 번 더 빌드 매트릭스로 점검해 주세요.
특히 6.5 → 6.6 구간에서DisposeBagdeallocation 타이밍이 달라진 이슈가 있었으므로 UI 바인딩 쪽 메모리 릭 테스트를 권장합니다.Projects/Core/Project.swift (1)
9-11: FirebaseAnalytics 의존성 추가: 링커 플래그 및 중복 포함 여부 체크Core 모듈에서 바로
FirebaseAnalytics를 끌어오면,
- 상위 앱 타깃(Tuist
App프로젝트)에서도 동일 패키지를 추가할 때 중복 링크 경고가 날 수 있습니다.-ObjC플래그가 누락되면 런타임에 크래시가 발생합니다.워크스페이스 전역
Package.resolved에서 한 번만 선언되도록 정리했는지,
또Firebase/CoreOnly대신Analytics전체를 불러오는 것이 의도된 것인지 점검해 주세요.Projects/Feature/AlarmFeature/Sources/ViewModel/RegularAlarmViewModel.swift (1)
10-11: @injected 구문 단순화는 👍, 하지만 런타임 등록 여부 다시 한번 확인하세요
@Injected매개변수를 제거해 제네릭 추론에 맡긴 점은 가독성이 크게 개선되었습니다. 다만 DIContainer에RegularAlarmUseCase가 누락돼 있으면 앱 실행 시점에 크래시가 발생할 수 있으므로,AppDelegate+Register.swift에 올바르게 등록되었는지 다시 한번 검증해 주세요.Projects/Feature/SearchFeature/Sources/ViewModel/SearchViewModel.swift (1)
11-12: 의존성 주입 간소화 확인
@Injected매개변수 제거로 코드가 간결해졌습니다.SearchUseCase역시 DIContainer 등록 여부만 재확인하면 문제 없어 보입니다.Projects/CoreDataService/Sources/CoreDataService.swift (1)
18-19: fetch 시그니처 단순화 OK동일 기능을 한 줄로 표현해 가독성이 좋아졌으며, 기존 비동기 Observable 흐름을 그대로 유지합니다. 별다른 부작용은 없어 보입니다.
Projects/Data/Sources/Repository/DefaultBusStopArrivalInfoRepository.swift (2)
13-14: Core 모듈 import 필요성 명확
@Injected사용을 위해import Core추가한 부분 문제 없습니다.
20-21: property-injection 전환으로 초기화 순서 주의생성자 제거 후
@Injected로 변경되면서networkService가 resolve되지 않은 상태로 접근될 가능성이 있습니다. 레지스트리 등록과 lifecycle(싱글톤/거의 싱글톤 여부)을 재확인 부탁드립니다.Projects/Domain/Sources/UseCase/DefaultNearMapUseCase.swift (2)
12-13: Core import 👍DI 래퍼 사용을 위한 import 추가, 이상 없습니다.
26-28: 기본 init 유지로 side-effect 최소화, 다만 bind 호출 시점 확인
bindLocationStatus()가 init 내부에서 호출되는데,@Injected해석 시점과의 race condition 가능성을 한 번 더 검증해 주세요(실제 구현이 lazy-resolve라면 안전).Projects/Feature/HomeFeature/Sources/ViewModel/FavoritesViewModel.swift (1)
11-14:@Injected구문 단순화 확인 완료프로퍼티 주입으로 변경된 부분이 정상적으로 적용되어 보이며, 가시적 문제는 없습니다.
Projects/Data/Sources/Repository/DefaultFavoritesRepository.swift (1)
18-21: 필수 의존성 등록 여부 확인 필요
@Injected로 주입되는CoreDataService·NetworkService가 DIContainer 에 미리register돼 있지 않으면, 이 프로퍼티에 접근하는 시점(= 객체 초기화 직후)에서 앱이 즉시 크래시 납니다.
레지스트리가 정상인지 한 번 더 확인해 주세요.다음 스크립트로 프로젝트 전역에서 해당 타입이 등록돼 있는지 빠르게 확인할 수 있습니다.
#!/bin/bash # DIContainer 에 CoreDataService / NetworkService 가 등록됐는지 검색 rg --line-number --context 2 $'\.register\(.*CoreDataService' || echo "⚠️ CoreDataService register not found" rg --line-number --context 2 $'\.register\(.*NetworkService' || echo "⚠️ NetworkService register not found"Projects/Data/Sources/Repository/DefaultRegularAlarmRepository.swift (1)
27-29:bindStoreStatus()초기 호출 시점 점검생성자에서 즉시 스토어 상태를 바인딩하는 구조는 의존성 준비 순서에 민감합니다. 초기 로딩 단계에서 불필요한 네트워크/DB 작업이 발생하지 않는지 확인이 필요합니다.
Projects/Core/Sources/DIContainer/DIContainer.swift (1)
26-34:resolve접근 범위 재검토 필요
register는public인데resolve는internal(디폴트)입니다. Core 모듈 외부에서 직접DIContainer.resolve가 필요하지 않다는 의도라면 문제없지만, 테스트 코드나 툴링에서 호출해야 할 가능성이 있다면public으로 노출하거나 별도unsafeResolve를 제공해 선택권을 주는 편이 좋습니다.
| public struct Injected<Dependency> { | ||
| public var wrappedValue: Dependency { | ||
| DIContainer.resolve(type: Dependency.self) | ||
| } | ||
|
|
||
| public init(_ type: T.Type) { | ||
| self.type = type | ||
| } | ||
| public init() { } | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Injected 래퍼가 매 접근마다 resolve 호출 → 캐싱으로 비용·락 횟수 최소화 제안
현재 구현은 프로퍼티 접근마다 DIContainer.resolve 를 재호출합니다.
락 횟수 및 딕셔너리 조회 오버헤드는 미미할 수 있으나, 다수의 Rx 체인에서 자주 접근될 경우 의미 있는 비용이 됩니다. 한 번만 해석-캐싱하도록 아래와 같이 개선을 제안합니다.
@propertyWrapper
public struct Injected<Dependency> {
- public var wrappedValue: Dependency {
- DIContainer.resolve(type: Dependency.self)
- }
+ // 최초 1회만 DIContainer 에서 해석
+ private lazy var cached: Dependency = {
+ DIContainer.resolve(type: Dependency.self)
+ }()
+
+ public var wrappedValue: Dependency { cached }
public init() { }
}장점
• 불필요한 락 경합 감소
• 의존성 인스턴스가 단일화되어 테스트 시 예측 가능성 ↑
• 기존 사용처 변경 없음
단점
• 의도적으로 매번 새로운 인스턴스를 받아야 하는 특수 케이스가 있으면 별도 래퍼가 필요
검토 부탁드립니다.
📝 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.
| public struct Injected<Dependency> { | |
| public var wrappedValue: Dependency { | |
| DIContainer.resolve(type: Dependency.self) | |
| } | |
| public init(_ type: T.Type) { | |
| self.type = type | |
| } | |
| public init() { } | |
| } | |
| @propertyWrapper | |
| public struct Injected<Dependency> { | |
| // 최초 1회만 DIContainer 에서 해석 | |
| private lazy var cached: Dependency = { | |
| DIContainer.resolve(type: Dependency.self) | |
| }() | |
| public var wrappedValue: Dependency { cached } | |
| public init() { } | |
| } |
🤖 Prompt for AI Agents
In Projects/Core/Sources/DIContainer/Injected.swift around lines 12 to 18, the
Injected property wrapper calls DIContainer.resolve on every property access,
causing unnecessary overhead. Modify the struct to cache the resolved dependency
instance in a private variable upon first access and return the cached instance
on subsequent accesses. This change reduces lock contention and dictionary
lookups while preserving the existing API and behavior for typical use cases.
| public init() { | ||
| bindStoreStatus() | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
초기화 시점에 bindStoreStatus() 호출 → 순환 의존·시점 오류 가능성
bindStoreStatus() 내부에서 즉시 coreDataService를 사용합니다.
DIContainer 가 아직 준비되지 않았거나, 동일한 CoreDataService 내부에서 다시 이 Repository 를 생성하는 경로가 있으면 순환 의존으로 이어질 수 있으니 한 번 더 살펴보세요.
🤖 Prompt for AI Agents
In Projects/Data/Sources/Repository/DefaultFavoritesRepository.swift at lines 26
to 28, calling bindStoreStatus() directly in the initializer may cause circular
dependency or timing issues because bindStoreStatus() uses coreDataService
immediately. To fix this, remove the bindStoreStatus() call from the init method
and instead call it after the DIContainer and coreDataService are fully
initialized, ensuring no circular creation paths exist.
| public init() { } | ||
|
|
There was a problem hiding this comment.
🛠️ Refactor suggestion
파라미터 없는 init() → ViewModel 단위 테스트 용이성 하락
기존 DI 주입방식에서는 테스트에서 Stub 객체를 직접 주입할 수 있었으나,
파라미터가 제거되면서 DIContainer 를 통해서만 주입해야 합니다.
테스트 격리가 어려워질 수 있으니 init(container: DIContainerType = DIContainer.shared) 식의 테스트 전용 구멍을 제공하는 것을 고려해 보세요.
🤖 Prompt for AI Agents
In Projects/Domain/Sources/UseCase/DefaultBusStopUseCase.swift around lines 26
to 27, the init() method has no parameters, which reduces testability by
preventing direct injection of stub objects in unit tests. Modify the
initializer to accept a parameter for DIContainerType with a default value of
DIContainer.shared, like init(container: DIContainerType = DIContainer.shared),
to allow easier injection of test doubles and improve test isolation.
| DIContainer.register(type: StationListRepository.self, DefaultStationListRepository()) | ||
| DIContainer.register(type: RegularAlarmRepository.self, DefaultRegularAlarmRepository()) | ||
| DIContainer.register(type: LocalNotificationService.self, DefaultLocalNotificationService()) | ||
| DIContainer.register(type: RegularAlarmEditingService.self, DefaultRegularAlarmEditingService()) |
There was a problem hiding this comment.
중복 등록으로 이전 인스턴스가 은폐됩니다.
RegularAlarmEditingService가 두 번 등록되고 있습니다. 두 번째 호출이 첫 번째 인스턴스를 덮어쓰므로, 불필요한 객체 생성 + 디버깅 난이도 상승 위험이 있습니다. 실제로 서로 다른 구현체를 주입하려는 의도가 아니라면 한 번만 등록하도록 수정하세요.
- DIContainer.register(type: RegularAlarmEditingService.self, DefaultRegularAlarmEditingService())
...
- DIContainer.register(type: RegularAlarmEditingService.self, DefaultRegularAlarmEditingService())Also applies to: 38-38
🤖 Prompt for AI Agents
In Projects/App/Sources/AppDelegate+Register.swift at lines 28 and 38, the
RegularAlarmEditingService is registered twice, causing the second registration
to overwrite the first and potentially creating unnecessary instances. To fix
this, remove one of the duplicate registrations so that
RegularAlarmEditingService is registered only once, ensuring no redundant object
creation and clearer dependency injection.
| lock.lock() | ||
| defer { lock.unlock() } | ||
| storage[String(describing: Dependency.self)] = instance |
There was a problem hiding this comment.
🛠️ Refactor suggestion
중복 등록 감지 로직이 없습니다.
동일한 type이 이미 존재할 때 무조건 덮어쓰고 있는데, 이는 실수로 잘못된 인스턴스를 주입해도 즉시 알 수 없습니다. 기존 값이 존재하면 assertionFailure나 로그 경고를 남겨 개발 단계에서 문제를 조기에 발견하도록 하는 방안을 고려해 주세요.
- storage[String(describing: Dependency.self)] = instance
+ let key = String(describing: Dependency.self)
+ #if DEBUG
+ if storage[key] != nil {
+ assertionFailure("DIContainer: '\(key)' 가 이미 등록되어 있습니다. 중복 등록을 확인하세요.")
+ }
+ #endif
+ storage[key] = instance🤖 Prompt for AI Agents
In Projects/Core/Sources/DIContainer/DIContainer.swift around lines 21 to 23,
the code overwrites existing instances in storage without checking for
duplicates. Add a check before assigning to storage to see if an instance for
the given type already exists. If it does, trigger an assertionFailure or log a
warning to alert developers during development that a duplicate registration is
occurring, preventing silent overwrites.
작업내용
의존성
Lint
Package Dependency
특이사항
리뷰요청
관련 이슈
close #324
Summary by CodeRabbit
신규 기능
버그 수정
리팩터
의존성
스타일
설정