티스토리 뷰
서비스 운영할 때는 대부분 develope, production (or Staging) 서버를 함께 운영한다.
dev 에서 테스트하고 추가한 기능들을 릴리즈에 맞춰서 prod로 옮기는 방식일텐데,
서버의 URL이 달라지므로 클라이언트에서는 이 부분에 대한 처리가 필요하다.
방법은 여러가지가 있겠지만 이번에 사용한 방법은 Target을 분리하고,
Schem에 따라 처리가 되도록 구현했다.
진행한 프로젝트는 Tuist를 사용했기 때문에 Tuist Manifest에서 설정을 시작한다.
1. Tuist Manifest - Build Setting
public enum AppEnviroment: String {
case dev
case prod
}
public func setEnviroment(to env: AppEnviroment) -> SettingsDictionary {
return .init(dictionaryLiteral: ("AppEnviroment", .string(env.rawValue)))
}
먼저 AppEnviroment Enum을 만들어준 뒤에 다음과 같이 Setting 을 추가해주는 함수를 만들었다.
let prodTarget = Target(
name: "weave-ios-prod",
platform: .iOS,
product: .app,
bundleId: "-",
deploymentTarget: .iOS(targetVersion: "17.0",
devices: .iphone,
supportsMacDesignedForIOS: false),
infoPlist: .file(path: "Support/weave-ios-Info.plist"),
sources: ["Sources/**"],
resources: ["Resources/**"],
entitlements: .file(path: .relativeToCurrentFile("weave-ios.entitlements")),
dependencies: [
.project(target: "Services",
path: .relativeToRoot("Projects/Core")),
.project(target: "DesignSystem",
path: .relativeToRoot("Projects/DesignSystem")),
.package(product: "ComposableArchitecture", type: .macro),
.package(product: "KakaoSDKCommon", type: .macro),
.package(product: "KakaoSDKAuth", type: .macro),
.package(product: "KakaoSDKUser", type: .macro),
.package(product: "KakaoSDKShare", type: .macro),
.package(product: "KakaoSDKTemplate", type: .macro),
],
settings: .settings(
base: setEnviroment(to: .prod)
)
)
let devTarget = Target(
name: "weave-ios-dev",
platform: .iOS,
product: .app,
bundleId: "-",
deploymentTarget: .iOS(targetVersion: "17.0",
devices: .iphone,
supportsMacDesignedForIOS: false),
infoPlist: .file(path: "Support/weave-ios-Info.plist"),
sources: ["Sources/**"],
resources: ["Resources/**"],
entitlements: .file(path: .relativeToCurrentFile("weave-ios.entitlements")),
dependencies: [
.project(target: "Services",
path: .relativeToRoot("Projects/Core")),
.project(target: "DesignSystem",
path: .relativeToRoot("Projects/DesignSystem")),
.package(product: "ComposableArchitecture", type: .macro),
.package(product: "KakaoSDKCommon", type: .macro),
.package(product: "KakaoSDKAuth", type: .macro),
.package(product: "KakaoSDKUser", type: .macro),
.package(product: "KakaoSDKShare", type: .macro),
.package(product: "KakaoSDKTemplate", type: .macro),
],
settings: .settings(
base: setEnviroment(to: .dev)
)
)
기존에 있었던 하나의 App Target 을 두개의 타겟으로 분리해주었고,
project setting 에 아까 만들었던 setEnviroment 를 넣어준다.
프로젝트에 따라 빌드세팅이 복잡할수도, xcconfig를 사용할 수도 있지만
build setting에 값이 들어가도록 하는 것은 동일하다.
let project = Project(
name: "Weave-ios",
organizationName: nil,
options: .options(),
packages: [
.remote(
url: "https://github.com/pointfreeco/swift-composable-architecture.git",
requirement: .exact("1.7.2")),
.remote(url: "https://github.com/kakao/kakao-ios-sdk", requirement: .branch("master"))
],
settings: nil,
targets: [prodTarget, devTarget],
schemes: [],
fileHeaderTemplate: nil,
additionalFiles: [],
resourceSynthesizers: []
)
이렇게 타겟만 분리해줘도 Scheme이 나눠지게 되는데,
Scheme의 세팅은 기본 세팅으로 설정된다. 추가적인 설정이 필요하거나 사용중이라면 Scheme 설정의 수정이 필요하다.
프로젝트를 확인해 보면 target과 Schem이 나눠진 것을 확인할 수 있다.
그리고, 각 타겟의 빌드 세팅에 가보면 아까 입력한 값이 User-Defined 로 잘 들어가는 것을 볼 수있다.
이제 이 값을 가지고 prod와 dev를 분기 할 수 있다.
2. info.plist
위에서 입력한 buildSetting 값을 런타임에서 읽기 위해서 info.plist 에서 설정이 필요하다.
key를 입력한 뒤에 value는 앞서 입력한 BuildSetting을 가르키도록 한다.
<key>App Enviroment</key>
<string>${AppEnviroment}</string>
3. 코드
그럼 이제 준비는 완료가 되었고, 코드에서 info.plist의 값을 읽어야 한다.
진행한 프로젝트에서는 다른 팀원이 네트워크 모듈에 ServerType 분기를 미리 만들어주셨으므로,
App Enviroment 를 가져와서 설정해주는 작업만 진행했다.
enum ServerType: String {
case dev // db 개발, api 개발
case prod // db 상용, api 상용
var baseURL: String {
switch self {
case .dev:
return SecretKey.developURL
case .prod:
return SecretKey.releaseURL
}
}
}
public class APIProvider {
static private(set) var serverType: ServerType = {
if let appEnviroment = Bundle.main.infoDictionary?["App Enviroment"] as? String,
let serverType = ServerType(rawValue: appEnviroment) {
return serverType
}
assert(false, "App Enviroment가 설정되지 않았습니다")
return .prod
}()
...
}
구현한 코드는 다음과 같다.
Provider 내부의 변수로 serverType 을 가지고 있도록 하고,
네트워크 요청시 baseURL 은 serverType이 가지고 있는 baseURL로 설정되도록 했고, 정상적으로 작동 된다.
4. 마무리
본 기능에서 내가 의도한건 코드를 변경하지 않고 prod와 dev를 전환하는 것 이였다.
아무래도 코드에서 변경하면 사람의 실수가 있을 수 있을 수 있다고 생각했기 때문이다.
이렇게 구현 하면 매번 prod와 dev를 바꿔줄 때 코드에서 변경해줄 필요 없이 scheme 을 변경함으로 간편하게 대응이 가능하다.
또한 배포를 위해 빌드할 때 prod scheme 이 빌드되도록 신경만 써주면 코드를 통한 변경보다 안전하다고 생각한다.
(fastlane 등에서 특정 scheme이 빌드되도록 사용하면 더 안전할 것 같다.)
휴먼 에러를 방지하기 위한 더 좋은 방법은 BundleId 를 분리하는 방법도 있을 것 같다.
BundleId 까지 분리한다면 아예 별도의 앱이 되기 때문에 실수로 Dev를 빌드하는 경우에도 대응이 가능할 것이다.
다만, 앱 내의 설정을 prod, dev 에 동일하게 적용해야 하므로 관리 포인트가 늘어나고, provisioning 또한 별도 관리가 필요할 것이다.
개발환경 분리는 이외에도 여러가지 방법이 있으니,
프로젝트의 상황에 맞는 방법을 사용하는 것이 좋을 것 같다.
'iOS' 카테고리의 다른 글
[iOS] SwiftUI에서 커스텀 Alert 만들기(Animation도) (0) | 2024.05.01 |
---|---|
[iOS] SwiftUI 섹션이 있는 메뉴 리스트 만들기 (0) | 2024.04.22 |
[iOS] SwiftUI 그라데이션 응용하기 (Stepper) (0) | 2024.04.17 |
[iOS] 오픈소스 기여를 해보았다. (2) | 2024.01.04 |
[iOS] DateFormatter 연도가 잘못 나올 때(주 기반 연도, YYYY) (0) | 2024.01.02 |
- Total
- Today
- Yesterday
- KVO
- TextField
- watch connectivity
- 애플워치 데이터 전송
- auth
- keyboardtype
- SwiftUI
- avplayer
- Swift
- 애플워치
- retry
- Xcode
- 소수점
- DateFormatter
- open-api-generator
- swift날짜
- watchOS
- demical
- 2024년
- 회고
- easy cue
- locale
- IOS
- flo
- openapi-generator
- 토큰
- AVFoundation
- OAS
- Xcode15
- musicplayer
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |