REST(REpresentational State Transfer)는 웹에서 데이터를 다루기 위한 아키텍처 스타일이다. HTTP 프로토콜을 기반으로 클라이언트와 서버가 데이터를 주고받는 방식을 따른다. 보통 JSON 포맷으로 데이터를 주고받는다.
| 동작 | HTTP 메서드 | 데이터 요청 방법 |
|---|---|---|
| 데이터 조회 | GET | 쿼리스트링으로 데이터 전달 |
| 데이터 생성 | POST | Body에 데이터를 담아 요청 |
| 데이터 수정 | PUT or PATCH | Body에 데이터를 담아 요청 |
| 데이터 삭제 | DELETE | - |
URLSession은 네트워크 요청을 관리하는 클래스다. HTTP 요청(GET, POST 등), 파일 다운로드, 업로드, 백그라운드 처리 등을 담당한다.
URLSession이 네트워크 작업을 수행할 때는 내부적으로 URLSessionTask를 사용한다.
| Task 타입 | 역할 | 예시 |
|---|---|---|
URLSessionDataTask | JSON, 텍스트 등 데이터를 서버와 주고받음 | GET |
URLSessionUploadTask | 파일 업로드 | 이미지 업로드 |
URLSessionDownloadTask | 파일 다운로드 | 앱 업데이트 다운로드 등 |
URLSessionStreamTask | 스트리밍 (비동기 전송) | 실시간 통신, WebSocket 등 |
let url = URL(string: "url 주소")!
let task = URLSession.shared.dataTask(with: url) { data, response, error in
// 응답 처리
}
task.resume()
data: 서버가 보낸 데이터response: HTTP 상태 코드 등의 메타 정보error: 네트워크 오류 (없으면 nil)dataTask는 즉시 실행되지 않음 → 반드시 .resume() 호출!URLSessionConfiguration은 세션의 기본 동작 설정을 정의한다. URLSession 인스턴스를 커스터마이징하고 싶을 때 사용한다.
| 주요 타입 | 설명 |
|---|---|
.default | 쿠키, 캐시, 인증 등을 포함한 일반 요청 |
.ephemeral | 캐시나 쿠키를 저장하지 않음(시크릿 모드 느낌) |
.background(withIdentifier:) | 앱이 백그라운드에 있어도 작업 계속(파일 다운로드 등) |
// 커스텀 세션
let config = URLSessionConfiguration.default
config.timeoutIntervalForRequest = 15 // 요청 시간 제한
config.httpAdditionalHeaders = [
"Authorization": "Bearer abc123"
]
let session = URLSession(configuration: config)
let url = URL(string: "url 주소")!
let task = session.dataTask(with: url) { data, response, error in
// 응답 처리
}
task.resume()
💡 비유로 쉽게 정리!
URLSession: 택배회사URLSessionTask: 택배 배송차 (요청)URLSessionConfiguration: 택배회사 설정 (운영 정책, 배송 방식 등)
서버가 보내는 JSON을 Swift 객체로 바꾸려면 Codable을 사용한다.
Encodable: Swift 객체 → JSON (보낼 때)Decodable: JSON → Swift 객체 (받을 때)// Swift 구조체
struct User: Codable {
let id: Int
let name: String
let email: String
}
//인코딩(Swift 객체 → JSON)
let user = User(id: 1, name: "luna", email: "luna@example.com")
let encoded = try? JSONEncoder().encode(user)
if let jsonString = String(data: encoded!, encoding: .utf8) {
print(jsonString) // {"id":1,"name":"luna", "email": "luna@example.com"}
}
// 디코딩(JSON → Swift 객체)
let decoder = JSONDecoder()
let user = try decoder.decode(User.self, from: data)
Result는 성공(Success) 또는 실패(Failure)를 하나의 타입으로 표현할 수 있는 열거형(enum)이다. 함수의 결과가 정상적인 값이 될 수도 있고, 에러가 될 수도 있는 상황에서 유용하다.
💡 왜 사용할까?
- 예외처리를 명시적으로 관리
do-catch없이 클로저에서 에러를 깔끔하게 전달- 네트워크, 파일 I/O, 비동기 작업에서 에러 핸들링 표준화 가능
import Foundation
// 에러 정의
enum APIError: Error {
case invalidURL
case requestFailed(Error)
case invalidResponse
case decodingFailed(Error)
}
// 사용자 정보 로드 함수
func fetchUser(completion: @escaping (Result<User, APIError>) -> Void) {
let urlString = "url 주소"
guard let url = URL(string: urlString) else {
completion(.failure(.invalidURL))
return
}
URLSession.shared.dataTask(with: url) { data, _, error in
if let error = error {
completion(.failure(.requestFailed(error)))
return
}
guard let data = data else {
completion(.failure(.invalidURL))
return
}
do { // User 모델로 파싱, Result<User, Error> 형태로 반환
let user = try JSONDecoder().decode(User.self, from: data)
completion(.success(user))
} catch {
completion(.failure(.decodingFailed(error)))
}
}.resume()
}
// Result 타입으로 에러 처리
fetchUser { result in
switch result {
case .success(let user):
print("👤 사용자 이름: \(user.name)")
print("📧 이메일: \(user.email)")
case .failure(let error):
print("⚠️ 에러 발생: \(error)")
}
}
async/await를 활용하면 코드가 훨씬 간결해진다. throw 키워드로 오류를 던지고 처리한다.
💡
async/await이 왜 더 좋을까?
- 코드 흐름이 직관적임
throw로 오류를 명확하게 전달- 동기 코드처럼 깔끔
// async 네트워크 함수
func fetchUser() async throws -> User {
let urlString = "url 주소"
guard let url = URL(string: urlString) else {
throw APIError.invalidURL
}
do {
let (data, response) = try await URLSession.shared.data(from: url)
guard let httpResponse = response as? HTTPURLResponse,
200..<300 ~= httpResponse.statusCode else {
throw APIError.invalidResponse
}
do {
let user = try JSONDecoder().decode(User.self, from: data)
return user
} catch {
throw APIError.decodingFailed(error)
}
} catch {
throw APIError.requestFailed(error)
}
}
// 함수 호출 예
Task {
do {
let user = try await fetchUser()
print("✅ 사용자 이름: \(user.name)")
} catch {
print("❌ 네트워크 에러: \(error)")
}
}
| 항목 | 역할 |
|---|---|
| RESTful API | 서버와 데이터 교환 방식 설계 모델 |
URLSession | HTTP 요청 전송 |
Codable | JSON ↔ Swift 변환 |
Result 타입 | 성공/실패 깔끔하게 표현 |
async/await | 비동기 코드로 가독성 향상 |