
Swift에서 API요청을 보낼 일이 생겼다.
따라서 그에 관한 정리된 코드를 작성한다.
Request,Response 모델을 정의 & 에러 핸들링값을 enum으로 정의한다.
import Foundation
enum HTTP_ERROR_RESPONSE: Identifiable {
case NetworkError, TimeOut, JsonDecodeErrorm, BASE64DecodeError, InvalidURL, ETC, NoneData
var id: Self { self }
}
struct AiModel: Decodable {
let openai: String
}
struct ChatGPTResponse: Codable {
let choices: [Choice]
}
struct Choice: Codable {
let message: GPTMessage
}
struct GPTMessage: Codable {
let content: String
}
struct Message: Identifiable, Equatable {
let id = UUID()
let content: String
let isUser: Bool
static func == (lhs: Message, rhs: Message) -> Bool {
return lhs.content == rhs.content &&
lhs.isUser == rhs.isUser
}
}
URL session으로 요청 및 응답을 받는다.
// URL이 유효한지 확인
guard let url = URL(string: urlString) else { // OpenAI URL
// updateError("Invalid URL")
self.errorMessage = .InvalidURL
self.isLoading = false
return
}
// HTTP 요청 설정
var request = URLRequest(url: url)
request.httpMethod = "POST" // HTTP 메소드를 POST로 설정
request.addValue("Bearer \(apiKey)", forHTTPHeaderField: "Authorization") // 인증 헤더 추가 및 API키 추가
request.addValue("application/json", forHTTPHeaderField: "Content-Type") // Content-Type 헤더 설정
request.timeoutInterval = 10 // 타임아웃 시간을 10초로 설정
// 모델 이름 설정
var modelName = ""
if gptmodel {
modelName = "gpt-4o-2024-11-20" // GPT-4 모델 사용
} else {
modelName = "gpt-4o-mini" // GPT-4 미니 모델 사용
}
// API 요청 본문 구성
let requestBody: [String: Any] = [
"model": modelName,
"messages": [
["role": "system", "content": systemPrompt], // 시스템 프롬프트
["role": "user", "content": userPrompt] // 사용자 프롬프트
]
]
// 타임아웃 작업 아이템 선언
var timeoutWorkItem: DispatchWorkItem?
do {
// 요청 본문을 JSON 데이터로 변환
let jsonData = try JSONSerialization.data(withJSONObject: requestBody)
request.httpBody = jsonData
// API 요청 태스크 생성
let task = Task {
do {
// API 요청 실행
let (data, response) = try await URLSession.shared.data(for: request)
// 타임아웃 작업 취소
timeoutWorkItem?.cancel()
// HTTP 응답 확인
guard let httpResponse = response as? HTTPURLResponse else {
timeoutWorkItem?.cancel()
DispatchQueue.main.async { [weak self] in
self?.isLoading = false
self?.errorMessage = .NetworkError
}
return
}
// 응답 상태 코드가 200(성공)인 경우
if httpResponse.statusCode == 200 {
// JSON 응답 디코딩 및 처리
if let jsonResult = try? JSONDecoder().decode(ChatGPTResponse.self, from: data),
let responseContent = jsonResult.choices.first?.message.content {
DispatchQueue.main.async { [weak self] in
let botMessage = Message(content: responseContent, isUser: false)
self?.isLoading = false
self?.receimessages = botMessage.content
}
} else {
// JSON 디코딩 실패 처리
timeoutWorkItem?.cancel()
DispatchQueue.main.async { [weak self] in
self?.isLoading = false
self?.errorMessage = .JsonDecodeErrorm
}
}
} else {
// HTTP 오류 응답 처리
timeoutWorkItem?.cancel()
DispatchQueue.main.async { [weak self] in
self?.isLoading = false
self?.errorMessage = .NetworkError
}
}
} catch URLError.timedOut {
// 타임아웃 오류 처리
timeoutWorkItem?.cancel()
DispatchQueue.main.async { [weak self] in
self?.isLoading = false
self?.errorMessage = .TimeOut
}
} catch {
// 기타 오류 처리
timeoutWorkItem?.cancel()
DispatchQueue.main.async { [weak self] in
self?.isLoading = false
self?.errorMessage = .ETC
}
}
}
// 타임아웃 처리를 위한 DispatchWorkItem 설정
timeoutWorkItem = DispatchWorkItem { [weak self] in
task.cancel() // 태스크 취소
DispatchQueue.main.async {
self?.isLoading = false
self?.errorMessage = .TimeOut
}
}
// 10초 후에 타임아웃 작업 실행
if let timeoutWorkItem = timeoutWorkItem {
DispatchQueue.main.asyncAfter(deadline: .now() + 10, execute: timeoutWorkItem)
}
} catch {
// JSON 직렬화 오류 처리
timeoutWorkItem?.cancel()
DispatchQueue.main.async { [weak self] in
self?.isLoading = false
self?.errorMessage = .JsonDecodeErrorm
}
}