VesselWheel

API key 값 사용시, 외부 노출 방지 방법(with 날씨앱) 본문

Xcode Study

API key 값 사용시, 외부 노출 방지 방법(with 날씨앱)

JasonYang 2024. 2. 9. 05:23

1. OpenWeatherMap에서 API 키 값을 받고나서, 

https://openweathermap.org/api

 

Weather API - OpenWeatherMap

 

openweathermap.org

2. 내가 필요로 하는 API를 선택 후 : 좌측 상단의 Current Weather Datas의 API doc에서 API call 주소를 획득한다. 

트래픽 제한으로 유료결제를 해야하는 API도 있지만, 3시간 단위 5일치를 무료로 제공하는 Current Weather Data로도 충분하다.

- API doc를 클릭하면 아래의 화면이 나오게 되는데,

획득한 API call를 활용하여 API를 호출할 수 있다. 

https://api.openweathermap.org/data/2.5/weather?lat={lat}&lon={lon}&appid={API key}

 

3. WeatherManager 클래스에서 매소드를 통해 API 를 호출하게 된다.

- 하지만 키 값을 코드에 직접 넣게 되면, github에서 공유될 경우 노출되는 위험이 생겼다. 그렇다면 어떻게 해야할까?

class WeatherManager {
    // 싱글톤 정적상수
    static let shared = WeatherManager()
    
    private init() {}
    
    // MARK: - public Methods
    // 서울지역 : URL(string: "https://api.openweathermap.org/data/2.5/weather?q=seoul&appid=\(apiKey)")
    public func getWeather(completion: @escaping(Result<WeatherData, NetworkError>) -> Void) {
        let url = URL(string: "https://api.openweathermap.org/data/2.5/weather?q=seoul&appid=\(YourKey)")
        guard let url = url else {
            return completion(.failure(.badUrl))
        }
        
        URLSession.shared.dataTask(with: url) { data, response, error in
            guard let data = data, error == nil else {
                return completion(.failure(.noData))
            }
            
            // Data 타입으로 받은 리턴을 디코드
            let weatherData = try? JSONDecoder().decode(WeatherData.self, from: data)
            
            // 성공 시 성공한 데이터 저장
            if let weatherData = weatherData {
                completion(.success(weatherData))
            } else {
                completion(.failure(.decodingError))
            }
        }.resume()  // dataTask 시작
        print("Success")
    }

->즉, url 프로퍼티를 선언할 때 YourKey에 API 키값을 넣게 된다는 것은,

API Key를 공개된 곳에 노출하면 다른 사람들이 악의적인 목적으로 이용할 수도 있기 때문에, 깃허브같은 저장소나 드라이브에 노출하면 안되는 것은 물론, 컴파일 후 바이너리 파일로 바뀌더라도 충분히 디컴파일 하려는 시도가 있을 수 있기 때문에 Key보안에 신경을 써야합니다.

 

이를 방지하기 위해서,

먼저 생성해둔 Xcode 프로젝트를 열고, Key를 저장할 .plist파일을 하나 만들겠습니다.

String 타입의 값을 하나 만들고, Key에 원하는 값, Value에 API Key 값을 넣음

4. WeatherManager.swift에 Key에 접근하는 코드 추가

// MARK: - Extensions
extension WeatherManager {
    //plist를 통해 숨긴 API 호출을 위한 프로퍼티
    private var apiKey: String {
        get {
            // 생성한 .plist 파일 경로 불러오기
            guard let filePath = Bundle.main.path(forResource: "WeatherKey", ofType: "plist") else {
                fatalError("Couldn't find file 'WeatherKey.plist'.")
            }
            
            // .plist를 딕셔너리로 받아오기
            let plist = NSDictionary(contentsOfFile: filePath)
            
            // 딕셔너리에서 값 찾기
            guard let value = plist?.object(forKey: "OPENWEATHERMAP_KEY") as? String else {
                fatalError("Couldn't find key 'OPENWEATHERMAP_KEY' in 'KeyList.plist'.")
            }
            return value
        }
    }
}

코드 해석

더보기

이 코드는 `WeatherManager` 클래스의 확장(extension)으로, OpenWeatherMap API를 호출하는 데 필요한 API 키를 가져오는 부분입니다.

`apiKey`라는 private 프로퍼티를 통해 plist 파일의 API 키 값을 가져옵니다. 이는 API 키를 코드 내에 직접 작성하지 않고 별도의 plist 파일에 저장함으로써 보안을 향상시킵니다.

 

1. `guard let filePath = Bundle.main.path(forResource: "WeatherKey", ofType: "plist")`

- Bundle.main.path 메소드를 사용하여 "WeatherKey.plist" 파일의 경로를 가져옵니다. 만약 파일을 찾지 못하면 fatalError를 발생시켜 앱을 종료합니다.

 

2. `let plist = NSDictionary(contentsOfFile: filePath)`

- filePath에서 가져온 경로의 파일을 NSDictionary로 변환합니다. NSDictionary는 plist 파일의 구조를 잘 나타내는 타입입니다.

 

3. `guard let value = plist?.object(forKey: "OPENWEATHERMAP_KEY") as? String`

- plist에서 "OPENWEATHERMAP_KEY"라는 키의 값을 String으로 변환합니다. 만약 키를 찾지 못하면 fatalError를 발생시켜 앱을 종료합니다.

요약하면, 이 코드는 "WeatherKey.plist" 파일에서 API 키 값을 가져와서 `apiKey` 프로퍼티에 저장하는 역할을 합니다. 이렇게 하면 plist 파일 외부에서는 API 키를 직접 볼 수 없으므로 보안을 유지할 수 있습니다.

\(YourKey)에 private var apikey를 넣어줌으로써 개인의 apikey는 github에 공유되지 않게 되었다. 

let url = URL(string: "https://api.openweathermap.org/data/2.5/weather?q=seoul&appid=\(YourKey)")