[iOS/Swift] Alamofire을 이용하여 서버와 통신해 보자 - POST

Nakyung Lee·2023년 5월 30일
0

iOS

목록 보기
1/14

졸업 프로젝트를 진행하면서 사용자의 위도, 경도를 서버에 넘겨주고 서버에서 보내는 온도, 날씨 응답을 받아와야 하는 일이 생겼다.

서버와 통신할 때 Alamofire을 많이 이용한다 하여 나도 이 라이브러리를 사용하기로 했다.

Alamofire 을 이용하기 위해서는 먼저 프로젝트에 라이브러리를 추가해 주어야 하는데, 나는 해당 블로그의 글을 참고하였다. 검색하면 수많은 글들이 나오니 참고하면서 하면 된다.


1. APIConstants.swift 파일 생성

struct APIConstants {
    static let baseURL = "http://54.180.220.34:8080"
    
    // 회원가입 url
    static let userSignUpURL = baseURL + "/users/new-user"
    // 로그인 url
    static let userSignInURL = baseURL + "/users/login"
    // 장르 저장 url
    static let genreURL = baseURL + "/create/genre"
    // 날씨 불러오기 url
    static let weatherURL = baseURL + "/read/weather"
}

통신 URL 작성하기

2. NetworkResult.swift 파일 생성

enum NetworkResult<T> {
    case success(T)
    case requestErr(T)
    case pathErr
    case serverErr
    case networkFail
}

서버 통신에 대한 결과를 담는 enum 만들기

3. GenericResponse.swift 파일 생성

struct GenericResponse<T: Codable>: Codable {
    var message: String
    var data: T?
    
    enum CodingKeys: String, CodingKey {
        // json은 key, data 값을 갖고 있는데 json의 key값을 swift 타입으로 디코딩할 때 이름이 같아야 해서
        // CodingKeys를 통해 data 변수를 key랑 struct를 이어주는 역할
        case message = "message"
        case data = "data"
    }
    
    init(from decoder: Decoder) throws {
        // 데이터로 들어오는 값이 없을 수도 있을 수도 있기 때문에 먼저 처리
        // 데이터가 없을 때 nil로 처리
        let values = try decoder.container(keyedBy: CodingKeys.self)
        message = (try? values.decode(String.self, forKey: .message)) ?? ""
        data = (try? values.decode(T.self, forKey: .data)) ?? nil
    }
}

4. 데이터 모델 구조체 파일 생성 (Codable)

struct WeatherData: Codable {
    let temp: Int
    let icon, main: String
}

통신 구현

5. WeatherService.swift 파일 작성

import Foundation
import Alamofire

struct WeatherService {
    static let shared = WeatherService()
    
    func getWeather(lat: Double, lon: Double, completion: @escaping (NetworkResult<Any>) -> (Void)) {
        let url = APIConstants.weatherURL // 날씨 불러오기 url
        let header: HTTPHeaders = [ "Content-Type" : "application/json" ]
        let params: Parameters = [
            "lat": lat,
            "lon": lon
        ]
        
        let dataRequest = AF.request(url, method: .post, parameters: params, encoding: JSONEncoding.default, headers: header)

        dataRequest.responseData { (response) in
            switch response.result {
            case .success:
                guard let statusCode = response.response?.statusCode else { return }
                guard let data = response.value else { return }
                print(data)
                completion(judgeWeatherData(status: statusCode, data: data))

            case .failure(let err):
                print(err)
                completion(.networkFail)
            }
        }
    }
    
    // statusCode와 decode 결과에 따라 NetworkResult 반환
    private func judgeWeatherData(status: Int, data: Data) -> NetworkResult<Any> {
        // 통신을 통해 전달받은 데이터를 decode
        let decoder = JSONDecoder()
        guard let decodedData = try? decoder.decode(
            WeatherData.self, from: data) else {
            return .pathErr
        }
        // statusCode를 통해 통신 결과를 알 수 있음
        switch status {
        case 200:
            print(decodedData)
            return .success(decodedData)
        case 400..<500:
            return .requestErr
        case 500:
            return .serverErr
        default:
            return .networkFail
        }
    }
}

6. ViewController에서 사용

WeatherService.shared.getWeather(lat: LocationService.shared.latitude ?? 0, lon: LocationService.shared.longitude ?? 0) { response in
    switch response {
    case .success(let data):
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy년 MM월 dd일 (E)"
        let current_date_string = formatter.string(from: Date())
                
        if let data = data as? WeatherData {
            self.dateLabel.text = "\(current_date_string) \(data.temp)º \(data.main)"
        }
                
    case .pathErr:
        print("결과 :: Path Err")
    case .requestErr:
        print("결과 :: Request Err")
    case .serverErr:
        print("결과 :: Server Err")
    case .networkFail:
        print("결과 :: Network Fail")
    }
}

반환값인 data를 print 해 보면 아래와 같이 출력되고, 만들어 둔 label에도 잘 적용되는 모습을 확인할 수 있었다.

WeatherData(temp: 13, icon: "04n", main: "Clouds")

profile
앱 개발자를 꿈꾸는 ✨

0개의 댓글