[두리번] 프로젝트 16~21일차 회고
🛫서버 연동 공부
서버 연동 경험이 전무한 것은 아니지만, 단순히 GET을 통해 데이터를 가져오는 것에만 익숙해 걱정이 좀 있었습니다.
심지어 배포받은 API가 30개 가량 되기 때문에, 빨리 익혀서 앱잼 기간 내에 작업을 완료해야했습니다.
다행히 저희 리드개발자 태현이형이 하나하나 다 완벽하게 설명해준 덕분에 금방 이해할 수 있었습니다! 팟짱 못지않은 YB 클라스...
이번 프로젝트를 통해 처음 알게 된 서버관련 몇 가지를 기록해보겠습니다.
🛬Request-Header
두리번은 로그인 과정을 통해 유저를 구분하여 서비스를 제공하기 때문에, 대부분의 API에서 Request-Header에 토큰을 입력했어야 했습니다.
따라서 토큰을 싱글톤 변수로 저장해두고 활용했습니다.
struct APIConstants {
static let jwtToken = "~~"
}
struct NetworkInfo {
static let token = APIConstants.jwtToken
static var header: HTTPHeaders {
[NetworkHeaderKey.contentType.rawValue: APIConstants.applicationJSON]
}
static var headerWithToken: HTTPHeaders {
[
NetworkHeaderKey.contentType.rawValue: APIConstants.applicationJSON,
NetworkHeaderKey.auth.rawValue: token
]
}
}
🛬Parameters
여행 그룹에 따라 다른 정보들을 지니고 있고, 사용자별로 다른 여행 그룹을 갖기 때문에 API송수신 과정에서 해당 그룹의 고유ID를 주고 받아야했습니다.
API주소에서 ":groudID"에 해당하는 부분은 replacingOccureences 메소드를 활용했습니다.
struct EditTripService{
static let shared = EditTripService()
private func makeURL(groupID: String) -> String {
let url = APIConstants.editTripURL.replacingOccurrences(of: ":groupId", with: groupID)
return url
}
🛬pathErr
POST API를 사용하는 과정에서 네트워킹 결과가 PATH ERROR가 나오는 경우가 지속됐었습니다.
처음엔 오타가 있거나 문법적으로 오류가 있는 줄 알고 한참 확인해보았지만 잘못된 것은 없었습니다.
결국 태현이형에게 확인요청을 한 결과, 데이터 모델을 잘못 설계한 탓이었습니다.
데이터 모델은 항상 Respone-Body를 바탕으로 구성해야하는데, POST API를 사용하며 Request-Body를 바탕으로 구성한 것이 문제였습니다.
import Foundation
struct EditTripResponse: Codable {
let status: Int
let success: Bool
let message: String
let data: EditTripData
}
// MARK: - DataClass
struct EditTripData: Codable {
let travelName, destination, startDate, endDate: String
let image: String
}
데이터 모델은 항상 Respone-Body를 바탕으로 구성해야한다는 것을 깨닫게 되었습니다.
🛬PATCH API
GET&POST 이외의 API는 접해본 적이 없어 PATCH API를 처음 보곤 많이 당혹스러웠습니다.
어떤 기능을 하는 지도 모르고, 사용법도 몰랐기 때문입니다.
구글링을 해보니 다행히(?) POST API와 사용법이 동일하여 별 탈 없이 사용할 수 있었습니다.
PATCH는 이전에 POST했던 데이터를 갱신해주는 역할을 하는 API라고 합니다.
func patchData(groupID : String,
travelName : String,
destination : String,
startDate : String,
endDate : String,
imageIndex : Int,
completion : @escaping (NetworkResult<Any>) -> Void)
{
let url: String = makeURL(groupID: groupID)
let header : HTTPHeaders = NetworkInfo.headerWithToken
let dataRequest = AF.request(url,
method: .patch,
parameters: makeParameter(travelName: travelName, destination: destination, startDate: startDate, endDate: endDate, imageIndex: imageIndex),
encoding: JSONEncoding.default,
headers: header)
dataRequest.responseData { dataResponse in
switch dataResponse.result {
case .success:
guard let statusCode = dataResponse.response?.statusCode else {return}
guard let value = dataResponse.value else {return}
let networkResult = self.judgeStatus(by: statusCode, value)
print(statusCode)
print(networkResult)
completion(networkResult)
case .failure: completion(.pathErr)
}
}
}
Alamofire.request 메소드를 사용할 때 method를 post에서 patch로 변경만 해주면 되었습니다.
이러한 내용들이 서버 연동을 위해 새로 공부해야 했던 내용들이었으며, 생각보다 그렇게 어렵진 않았습니다.
🛫날짜 파싱
두리번은 여행 관련 어플이라 시간과 날짜를 다룰 일이 많았습니다.
이전에 캘린더 작업을 할 때에 잠깐 다뤄보긴 했지만, 더미데이터가 아닌 실제 서버와 데이터를 송수신하려고 하니 형태가 맞지 않아 오류도 많이 나고 출력해줄 때 원하는 형태로 변환해주는 과정도 너무 어려웠습니다.
{
"travelName": "두리번 강릉 여행",
"destination": "강릉",
"startDate": "2021-07-17",
"endDate": "2021-07-18",
"imageIndex": 1
}
API 사용시 날짜를 위와 같은 형태와 String형식으로 주고 받아야했는데, 데이터를 사용할 땐 아래와 같은 구조로 사용하기 때문에 파싱이 필요했지만 처음엔 그 과정이 어려웠습니다.
요일같은 경우에는 Calendar를 활용해 추출했으며, 날짜 형태는 separatedBy를 활용해 변형했습니다.
func dateSet() {
let f = DateFormatter()
f.locale = Locale(identifier: "ko_KR")
f.dateFormat = "yyyy.MM.dd"
let today = f.date(from: self.startDate)
var cal = Calendar(identifier: .gregorian) // 그레고리 캘린더 선언
cal.locale = Locale(identifier: "ko_KR")
let dateComponents = cal.dateComponents([.weekday], from: today!)
guard let weekIndex = dateComponents.weekday else { return }
let dayOfWeek = cal.weekdaySymbols[weekIndex-1]
let strList = startDate.components(separatedBy: "-")
day = "\(strList[0]).\(strList[1]).\(strList[2])(\(dayOfWeek.first!))"
startDateLabel.text = day
endDateLabel.text = day
}
혹은 Service 파일에서 JSONDecoder 자체를 포메팅할 수 있었습니다.
JSONDecoder 자체를 포메팅하면, 데이터 모델의 형태도 수정해주어야합니다.
private func judgeStatus(by statusCode: Int, _ data: Data) -> NetworkResult<Any> {
let f = DateFormatter()
f.dateFormat = "yyyy-MM-dd-HH:mm"
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(f)
guard let decodedData = try? decoder.decode(MainDataModel.self, from: data)
else { return .pathErr }
switch statusCode {
case 200: return .success(decodedData)
case 400: return .pathErr
case 500: return .serverErr
default: return .networkFail
}
}
struct Group: Codable {
let _id: String
var startDate: Date
var endDate: Date
var travelName: String
var image: String
var destination: String
let members: [String]
}
🛫서버 로딩
서버에서 이미지 혹은 큰 용량의 데이터를 불러오게 되면 로딩이 되는 동안의 시간이 소요되게 됩니다.
데이터가 로딩이 되는 동안에는 데이터가 들어갈 자리들이 빈 자리로 보이게 되어 이 부분은 어떻게 해야할지 팀원들과 상의해보았습니다.
- Placeholder를 사용해 데이터가 불러와지는 동안의 빈자리를 커버한다.
- 메인 뷰가 로딩되는 과정에서 모든 데이터들을 한 꺼번에 로딩해버린다.
- 로딩 인디케이터를 사용한다.
등의 의견이 나왔고, 결국엔 SkeletonView 라이브러리와 로딩 인디케이터를 함께 사용하기로 결정했습니다.
private func getPlanData(date: String) {
guard let groupId = tripData?._id else { return }
startLoading()
TripPlanDataService.shared.getTripPlan(groupId: groupId,
date: date) { [weak self] (response) in
switch response {
case .success(let data):
if let schedule = data as? [Schedule] {
self?.endLoading()
self!.planData = schedule
}
case .requestErr(_):
print("requestErr")
self?.endLoading()
case .serverErr:
print("serverErr")
self?.endLoading()
case .networkFail:
print("networkFail")
self?.endLoading()
case .pathErr:
print("pathErr")
self?.endLoading()
}
}
}
서버로부터 데이터를 불러오는 과정에서 인디케이터를 활용해 뷰를 멈춰두고, 스켈레톤뷰를 활용해 Placeholder를 남기며 로딩이 끝나게 되면 인디케이터를 종료시켜 뷰가 활성화되게 하는 방식입니다.
이렇게 구성하니 깔끔하고 자연스럽게 뷰 전환이 가능했습니다.
🛫마무리
16일차부터 마지막21일차 까지 서버연동 작업을 진행하며 나름 성공적인 데모데이를 치룰 수 있었습니다.
서버연동하는 작업은 개념적으로는 딱히 어려운 내용이 없었지만, 알 수 없는 이런저런 오류들이 너무 많이 나는게 고생이었습니다 ㅠㅠ
특히 메인뷰에서는 "when"에 따라 데이터를 분류해주어야했고, 분류된 데이터들을 각각 컬렉션뷰와 테이블뷰에 넣어주는 과정이 많이 복잡했습니다.
이번 프로젝트를 통해 서버 데이터가 많아지고 복잡해지는 경우에도 잘 분류하는 법을 배울 수 있었고, 당시에는 많이 힘들었지만 지금 되돌아보면 한 번쯤 꼭 겪어봐야할 성장통이었다는 생각이 듭니다.
지금까지의 회고는 프로젝트를 진행하며 기술적으로 배웠던 내용들을 기록했지만, 다음 회고에서는 프로젝트의 경험, 추억들을 전체적으로 기록하는 글을 작성할 예정입니다!
Author And Source
이 문제에 관하여([두리번] 프로젝트 16~21일차 회고), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@sangjin98/두리번-프로젝트-1621일차-회고저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)