이번 프로젝트에서는 로컬에서 저장된 데이터를 서버에 전달하는 작업을 진행함!
Android와 동일한 데이터를 공유하기 위해 gRPC를 사용해 데이터 모델을 맞춘기로 했다고..
gRPC란?
Proto 변환 과정
처음 인수인계를 받을 때, 프로젝트의 .proto 파일이 작성되어 있어, 기존 내용을 유지하며 변환 과정을 진행
그러나 경로 문제로 변환에 어려움을 겪었고, 다른 개발자들이 최상위 폴더에 proto를 지정한 뒤 변환 작업을 진행하는 것을 참고해, 최상위 폴더에 protoGenerated 폴더를 추가해 변환 파일을 저장하도록 설정
폴더 구조
기존 쉘 스크립트
기존에는 Proto 폴더 안에서 바로 변환을 진행
arr=$(find ./proto -name '*.proto')
for file in $arr
do
protoc -I=. --swift_out=. --grpc-swift_out=. $file
echo "$file to Swift..."
done
echo "ProtoToSwift Complete"
수정된 쉘 스크립트
arr=$(find ./proto -name '*.proto')
for file in $arr
do
protoc -I=./proto --swift_out=./protoGenerated --grpc-swift_out=./protoGenerated $file
echo "$file to Swift..."
done
echo "ProtoToSwift Complete"
주의사항: Swift Prefix
gRPC 서비스 구성
이번 프로젝트에서는 gRPC 호출을 효율적으로 처리하기 위해 GrpcServiceHelper, GrpcRepository, GrpcRepositoryInterface, GrpcServiceUseCase와 같은 구조를 도입
GrpcServiceHelper
import GRPC
import NIO
import RxSwift
import SwiftProtobuf
final class GrpcServiceHelper {
private let group: EventLoopGroup
init(group: EventLoopGroup) {
self.group = group
}
func performGrpcCall<Request: SwiftProtobuf.Message, Response: SwiftProtobuf.Message>(
_ request: Request,
callMethod: @escaping (Request) -> UnaryCall<Request, Response>
) -> Single<Response> {
return Single.create { single in
let call = callMethod(request)
call.response.whenComplete { result in
switch result {
case .success(let response):
single(.success(response))
case .failure(let error):
single(.failure(error))
}
}
return Disposables.create {
try? self.group.syncShutdownGracefully()
}
}
}
GrpcRepository
import GRPC
import NIO
import RxSwift
final class GrpcRepository: GrpcRepositoryInterface {
typealias ServiceNIOClient = AAA_ServiceNIOClient
private let serviceClient: ServiceNIOClient
private let helper: GrpcServiceHelper
override init() {
let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
self.helper = GrpcServiceHelper(group: group)
let channel = ClientConnection.insecure(group: group)
.connect(host: "host", port: 1111)
self.serviceClient = AAA_ServiceNIOClient(channel: channel)
}
func createServic(aa: String) -> Single<CreateResponse> {
var request = CreatRequest()
request.aa = aa
return helper.performGrpcCall(request) { self.serviceClient.create($0) }
}
}
GrpcRepositoryInterface
protocol GrpcServiceRepositoryInterface {
func createServic(aa: String) -> Single<CreateResponse>
}
GrpcServiceUseCase
import RxSwift
final class GrpcNoteServiceUseCase {
private let grpcServiceRepositoryInterface: GrpcServiceRepositoryInterface!
init(grpcServiceRepositoryInterface: GrpcServiceRepositoryInterface!) {
self.grpcServiceRepositoryInterface = grpcServiceRepositoryInterface
}
func createExecute(aa: String) -> Single<CreateResponse> {
return grpcServiceRepositoryInterface.createServic(aa: aa)
}
}
이제 ViewModel에서 GrpcServiceUseCase를 생성하여 사용할 수 있음!
정보가 많이 없어 gRPC와 Proto 변환 및 구현 과정을 정리해봄!