본문 바로가기
IOS App Programming/IOS 연습

[IOS] URLSessrion을 통해 CLOVA Sentiment API 사용기

by B_Tori 2024. 2. 27.

프로젝트 당시 API를 불러오기 위해 URL Session을 사용했었다.

작성했던 코드를 예제 삼아 URL Session 내용을 다시 한번 공부하고 정리해볼까 한다.

URL Session이란?

네트워크 데이터 전달 작업에 관련된 일을 하는 개체

URLSessionConfiguration

  • shared : 기본 요청을 하기 위한 세션으로 싱글톤으로 사용 ( 맞춤 설정 불가 ) "
    URLSession.shared
  • default : 가장 기본적인 통신 방법으로 shared랑 비슷하지만 맞춤 설정 가능
    URLSession(configuration:. default)
  • ephemeral : 쿠키나 캐시, 인증을 저장하지 않을 때 사용 (사파리 개인정보보호 모드)
    URLSession(configuration:. ephemeral)
  • background : 앱이 백그라운드에 있는 상황에서 http(s) 방식의 콘텐츠를 다운로드하거나 업로드할 때 사용
    URLSession(configuration:. background)

shared의 경우만 싱글톤 객체를 사용하여 접근하고 나머지의 경우 URLSession init을 통해 객체 생성 후 접근한다.

Task 유형

주로 API 통신에 사용하는 것은 Data Task

  • Data Task : NSData 개체를 사용하여 데이터를 주고받음.
    func dataTask(with:) 사용
  • Upload Task : 데이터 작업과 유사하지만 파일 형태로 데이터를 보내고, 백그라운드 업로드 지원
    func uploadTask(with:) 사용
  • Download Task : 파일 형태의 데이터를 다운로드하고, 백그라운드 다운로드 지원
    func downloadTask(with) 사용

task 함수들은 여러 오버로딩 함수 존재한다. (상황에 맞게 사용)

작업을 생성한 후에는 해당 resume() 메서드를 호출하여 작업을 시작한다.

[작업 상태 제어 함수]

  • resume() : 작업 시작
  • cancel() : 작업 취소
  • suspend() : 작업 일시 중단

Task 생성하기

  • URL 혹은 URLRequest를 통해 만들어줄 수 있음
  • URLRequest를 이용하면 HttpMethod와 Http헤더를 설정해 줄 수 있음
    HttpMethod의 기본값은 GET 형태이기 때문에 단순히 GET 요청을 할 경우 URL로 Task를 생성하여 사용해도 되지만
    POST 요청의 경우 URLRequest를 이용하여 Task를 생성해야 한다.

CLOVA Sentiment API 사용해 보기

API 요청 문서를 보면 다음과 같이 적혀있다.

  • POST 요청 및 헤더 설정을 위해 URLRequest로 Task를 생성하고
  • 바디에는 필수 요청인 content만 보내기로 하였다.

Request 얻기

해당 함수는 request를 만들어 반환하는 함수이다.

API KEY 값들은 따로 plist 파일로 만들어 Bundle에 extension 해놓은 상태이다.

private func requestApi(content: String) -> URLRequest? {
        guard let url = URL(string: "<https://naveropenapi.apigw.ntruss.com/sentiment-analysis/v1/analyze>") else { return nil }
        guard let body = try? JSONSerialization.data(withJSONObject: ["content" : content]) else { return nil }

        var urlRequest = URLRequest(url: url)
        urlRequest.httpMethod = "POST"
        urlRequest.setValue(Bundle.main.clientSecret, forHTTPHeaderField: "X-NCP-APIGW-API-KEY")
        urlRequest.setValue(Bundle.main.clientID, forHTTPHeaderField: "X-NCP-APIGW-API-KEY-ID")
        urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
        urlRequest.httpBody = body

        return urlRequest
    }
  1. 요청 url String을 통해 URL 객체를 얻는다.
  2. body 구성을 위해 JSONSerialization를 이용해서 Data 형식으로 변환해 주었다. (urlRequest.httpBody 가 Data? 형이기 때문) ["content" : content]를 보면 content라는 키값을 가지고 value로는 매개변수로 넘겨받은 String 값 변수 content를 넣어주었다.
  3. URLRequest 생성자에 1에서 얻은 URL 객체를 넣어 request를 생성해 준다.
  4. 메서드 형식을 POST로 지정하고 헤더 값을 설정해 준다.
    • "X-NCP-APIGW-API-KEY" 필드 → Bundle.main.clientSecret 변수 저장
    • "X-NCP-APIGW-API-KEY-ID" 필드 → Bundle.main.clientID 변수 저장
    • “Content-Type” 필드 → "application/json"
  5. requset의 바디에도 아까 얻어둔 body Data 변수를 대입해 준다.

Task 만들어 실행하기

enum ApiError: Error {
    case apiError
    case dataReciveError
    case jsonDecodeError
    case httpRequestError
}

func fetchData(text: String, completion: @escaping (Result<SentimentModel, Error>) -> Void) {
        guard let request = requestApi(content: text) else { return }
        URLSession.shared.dataTask(with: request) { (data, response, error) in
            if let error = error {
                print("api Error :\\n\\(error.localizedDescription)")
                completion(.failure(ApiError.apiError))
                return
            }
            guard let data = data else {
                print("Error: 데이터를 받지 못함")
                completion(.failure(ApiError.dataReciveError))
                return
            }
            guard let response = response as? HTTPURLResponse, (200 ..< 300) ~= response.statusCode else {
                print("Error: HTTP 요청 실패")
                completion(.failure(ApiError.httpRequestError))
                return
            }
            guard let result = try? JSONDecoder().decode(SentimentModel.self, from: data) else {
                print("Error: Json Decode 실패")
                completion(.failure(ApiError.jsonDecodeError))
                return
            }
            completion(.success(result))
        }.resume()
    }

아까 작성한 request 함수로 requset를 얻고 URLSession.shared.dataTask를 호출한다.

dataTask의 컴플리션 핸들러에는 data, response, error를 받을 수 있으며

각 에러에 대응할 수 있게 여러 에러 상황들을 enum으로 정의하여 fetchData의 failure 컴플리션에 에러를 같이 전달하였다.

 

dataTask 컴플리션으로 받은 데이터 변수를 미리 정의해 둔 SentimentModel 구조체로 디코딩한다.

디코딩에 성공하면 success 컴플리션을 통해 결과를 전달한다.

 

task 함수를 실행하면 새로운 task가 만들어지면서 일시정지 상태이기 때문에 resume()을 통해 task를 실행해 준다.

댓글