[TIL] 네트워크

박주하·2025년 6월 4일

1. RESTful API


REST(REpresentational State Transfer)는 웹에서 데이터를 다루기 위한 아키텍처 스타일이다. HTTP 프로토콜을 기반으로 클라이언트와 서버가 데이터를 주고받는 방식을 따른다. 보통 JSON 포맷으로 데이터를 주고받는다.

동작HTTP 메서드데이터 요청 방법
데이터 조회GET쿼리스트링으로 데이터 전달
데이터 생성POSTBody에 데이터를 담아 요청
데이터 수정PUT or PATCHBody에 데이터를 담아 요청
데이터 삭제DELETE-

2. URLSession으로 요청


1. URLSession

URLSession은 네트워크 요청을 관리하는 클래스다. HTTP 요청(GET, POST 등), 파일 다운로드, 업로드, 백그라운드 처리 등을 담당한다.


2. URLSessionTask

URLSession이 네트워크 작업을 수행할 때는 내부적으로 URLSessionTask를 사용한다.

Task 타입역할예시
URLSessionDataTaskJSON, 텍스트 등 데이터를 서버와 주고받음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() 호출!

3. URLSessionConfiguration

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: 택배회사 설정 (운영 정책, 배송 방식 등)

3. Codable로 응답 파싱(JSON ↔ Swift 구조체 변환)


서버가 보내는 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)

4. Result 타입으로 성공/실패 처리


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)")
    }
}

5. 비동기 방식(async/await)


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서버와 데이터 교환 방식 설계 모델
URLSessionHTTP 요청 전송
CodableJSON ↔ Swift 변환
Result 타입성공/실패 깔끔하게 표현
async/await비동기 코드로 가독성 향상

0개의 댓글