스파르타 코딩 클럽 - iOS 스타터 6기/본 캠프

53. 스파르타 코딩 클럽 - 봄여어름갈겨어울 #3 (날씨앱)

seongpil Heo 2025. 5. 22. 22:19
 

52. 스파르타 코딩 클럽 - 봄여어름갈겨어울 #2 (날씨앱)

51. 스파르타 코딩 클럽 - 봄여어름갈겨어울 #1 (날씨앱)🌤️ 봄여어름갈겨어울 (심화 팀 프로젝트)최종 프로젝트 전 마지막 팀 프로젝트인 심화 주차 팀 프로젝트가 오늘 시작되었다.기간은 5월

coding-pill.tistory.com

  🌤️  봄여어름갈겨어울 (심화 팀 프로젝트)

[ 목차 ]

1. 오늘 작업 내용

2. Trouble Shooting

3. TIL


  👨🏻‍💻  오늘 작업 내용

[ 1. NetworkManager 코드 수정 - 입력받은 위도, 경도를 사용하기 ]

[ 1-1. 외부에서 위도와 경도를 파라미터로 받아서 urlQueryItems를 리턴하는 함수를 추가

// urlQueryItems을 리턴하는 함수
func makeUrlQueryItems(lat: Double, lon: Double) -> [URLQueryItem] {
    return [
        URLQueryItem(name: "lat", value: String(lat)), // 위도
        URLQueryItem(name: "lon", value: String(lon)), // 경도
        URLQueryItem(name: "appid", value: apiKey), // apiKey 추가
        URLQueryItem(name: "units", value: "metric") // 섭씨로 데이터 받기
    ]
}

 

[ 1-2. fetchCurrentWeatherData 메서드를 파라미터가 2개 있는 메서드로 변경 ]

// 서버에서 현재 날씨를 받아오는 메서드
func fetchCurrentWeatherData(lat: Double, lon: Double) -> Single<(WeatherResponse, String)> {
    return Single.create { single in
        var urlComponents = URLComponents(string: "https://api.openweathermap.org/data/2.5/weather")
        urlComponents?.queryItems = self.makeUrlQueryItems(lat: lat, lon: lon)

        guard let url = urlComponents?.url else {
            print("잘못된 URL")
            single(.failure(AFError.invalidURL(url: "")))
            return Disposables.create()
        }

        self.fetchDataByAlamofire(url: url) { (result: Result<WeatherResponse, AFError>) in

            switch result {

                // 네트워크 통신 성공시
            case .success(let weatherResponse):
                let imageUrl = "https://openweathermap.org/img/wn/\(weatherResponse.weather[0].icon)@2x.png"
                single(.success((weatherResponse, imageUrl))) // 성공시 날씨 정보와 아이콘 이미지 url을 방출

                // 네트워크 통신 실패시
            case .failure(let error):
                print("데이터 로드 실패: \(error)")
                single(.failure(error)) // 실패시 에러 방출
            }
        }
        return Disposables.create() // Single 종료
    }
}

fetchCurrentWeatherData 메서드 내부에서 urlQueryItems를 추가하기 위해 

self.makeUrlQueryItems(lat: Double, lon: Double) 메서드를 사용

 

[ 1-3. fetchForeCastData 메서드를 파라미터가 2개 있는 메서드로 변경 ]

// 서버에서 5일 간 날씨 예보 데이터를 불러오는 메서드
func fetchForeCastData(lat: Double, lon: Double) -> Single<WeatherForecast> {
    return Single.create { single in
        var urlComponents = URLComponents(string: "https://api.openweathermap.org/data/2.5/forecast")
        urlComponents?.queryItems = self.makeUrlQueryItems(lat: lat, lon: lon)

        guard let url = urlComponents?.url else {
            print("잘못된 URL")
            single(.failure(AFError.invalidURL(url: "")))
            return Disposables.create() // error를 방출하고 종료
        }

        self.fetchDataByAlamofire(url: url) { (result: Result<WeatherForecast, AFError>) in

            switch result {

                // 네트워크 통신 성공시
            case .success(let weatherForecast):
                single(.success(weatherForecast)) // 결과를 방출

                // 네트워크 통신 실패시
            case .failure(let error):
                print("데이터 로드 실패: \(error)")
                single(.failure(error)) // 에러 방출
            }
        }
        return Disposables.create() // Single 종료
    }
}

fetchForeCastData 메서드 내부에서 urlQueryItems를 추가하기 위해

self.makeUrlQueryItems(lat: Double, lon: Double) 메서드를 사용

 

 

[ 2. NetworkManager PR 및 Merge ]

Github PR

 

 

[ 3. NetworkTestViewController 에서 Network Test ]

import UIKit
import RxSwift
import SnapKit
import Alamofire

class NetworkTestViewController: UIViewController {

    var disposeBag = DisposeBag()
    let network = NetworkManager.shared
    
    private let currentWeatherLabel: UILabel = {
        let label = UILabel()
        label.text = ""
        label.numberOfLines = 0
        label.textAlignment = .center
        return label
    }()
    
    private let imageView: UIImageView = {
        let imageView = UIImageView()
        imageView.contentMode = .scaleAspectFit
        imageView.backgroundColor = .lightGray
        return imageView
    }()
    
    private let forecastWeatherLabel: UILabel = {
        let label = UILabel()
        label.text = ""
        label.numberOfLines = 0
        label.textAlignment = .center
        return label
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.backgroundColor = .white
        
        // 현재 날씨 네트워크 통신 테스트
        fetchCurrentWeatherResponse()
        
        // 날씨 예보 네트워크 통신 테스트
        fetchForeCastResponse()

        // 화면에 표시 테스트
        configureUI()
    }
    
    func fetchCurrentWeatherResponse() {
        network.fetchCurrentWeatherData(lat: 37.5, lon: 126.9)
            .subscribe { (weather, imageUrl) in
                print("날씨 정보 : \(weather)")
                self.currentWeatherLabel.text = "현재 기온 : \(Int(weather.main.temp))°C"
                print("날씨 아이콘 주소 : \(imageUrl)")
                AF.request(imageUrl).responseData { response in
                    if let data = response.data, let image = UIImage(data: data) {
                        DispatchQueue.main.async {
                            self.imageView.image = image
                        }
                    }
                }
            } onFailure: { error in
                print("에러 발생 : \(error)")
            }.disposed(by: disposeBag)
    }
    
    
    func fetchForeCastResponse() {
        network.fetchForeCastData(lat: 37.5, lon: 126.9)
            .subscribe { weather in
                print("5일간 날씨 예보 : \(Int(weather.list[0].main.temp))°C")
                self.forecastWeatherLabel.text = "5일간 날씨 예보 기온 : \(weather.list[0].main.temp)"
            } onFailure: { error in
                print("에러 발생 : \(error)")
            }.disposed(by: disposeBag)

    }
}

시뮬레이터 테스트
콘솔 Print 테스트


  🎯  Trouble Shooting

[ 1. NetworkManager 작성을 완료하고, 테스트를 위해 NetworkTestViewController를 만들어서 빌드를 했는데 apiKey를 찾지 못하는 에러가 발생 ] 

 

- 이유 : 왜 인지 모르겠지만 (아마 dev 브랜치를 pull 한 뒤로 예상함) ApiKey를 넣어뒀던 Secret.xcconfig 파일이 내 프로젝트 폴더 내에 보이지 않음

 

- 해결 방법 : 1. 파인더로 봄여어름갈겨어울 프로젝트 폴더에 들어간 뒤 안에 있는 Secret.xcconfig 파일을 Xcode 내 프로젝트 폴더 안으로 드래그하여 넣어줌. 2. 다시 타겟 설정해 줌


  ✓  TIL

한 줄 TIL : RxSwift 이해에 한 발 다가선 오늘

RxSwift가 궁금한 명수
올해 벌써 473 contributions

 

내일은 팀원분과 함께 ViewModel 연결하는 작업을 해보려고 한다.