네트워크 통신 이해

동그라미·2024년 12월 27일
3
post-thumbnail

네트워크 기본 개념


💻 네트워크란 둘 이상의 컴퓨터가 연결되고 소통하는 것을 말합니다.

  • 아이폰도 하나의 컴퓨터, 서버도 하나의 컴퓨터로 생각할 수 있기 때문에, 서버와 아이폰과의 통신도 네트워크 통신입니다.
  • 인터넷이란 전 세계 컴퓨터를 연결하는 거대한 네트워크를 말합니다.
  • 인터넷 연결을 위해서는 와이파이 연결이 되있거나 데이터가 켜져있어야 합니다.
  • Swift 로 서버와 통신하는 코드를 작성할 수 있습니다.
  • 네트워크 통신 코드를 공부하기 전에, 알아야할 기본 개념들을 먼저 공부해봅시다.

JSON 이란


💻 JSON (JavaScript Object Notation) 은 데이터를 표현하는 형식 중 하나입니다.

  • 이름은 Adam, 전화번호는 010-1111-2222 라는 전화번호 정보를 알고 있습니다. 이 정보를 다른 누군가에게 전달할 때 어떤 포맷으로 전달하는게 좋을까요?
  • 그냥 쉽게 문자열로 “이름Adam,전화번호010-1111-2222” 로 보낸다면..?
  • 아니면.. “이름:Adam,전화번호:010-1111-2222” 이렇게? 🤔
  • 일반적으로 데이터를 표현하는 형식이 있다면, 그걸 따르는 게 좋을 것 같습니다.
  • 이렇게 네트워크에서 데이터를 주고받으려면, 아무렇게나 주고 받는 것이 아니라 정해진 형식을 지켜서 데이터를 교환하는 것이 좋습니다.
  • 이 중 서버와 클라이언트가 가장 많이 사용하는 데이터 형식이 JSON 형식입니다.
  • JSON 은 key-value 형태를 가집니다.
  • 예를 들어, 앞선 강의의 전화번호 구조체를 JSON 으로 표현하면 다음과 같습니다.
[
    {
        "name": "Adam",
        "phoneNumber": "010-1111-2222"
    },
    {
        "name": "Eve",
        "phoneNumber": "010-3333-4444"
    },
    {
        "name": "Abel",
        "phoneNumber": "010-5555-6666"
    }
]
  • 이 JSON 데이터는 리스트[ ] 안에 3개의 전화번호부 데이터를 표현합니다.
  • JSON 은 특정한 프로그래밍 언어 안에 속하지 않으며, 대부분의 프로그래밍 언어에서는 JSON 포맷의 데이터를 다룰 수 있는 기능을 제공합니다. Swift 역시 마찬가지입니다.

API 란


💻 API (Application Programming Interface) 란?

  • API 를 이해하기 위해서는 먼저 API 의 I(Interface) 가 뭔지 먼저 이해해야 합니다.

  • 개발 용어에서 인터페이스(Interface)는 항상 창구를 의미합니다.

    너는 나의 내부가 어떻게 생겨먹었는지는 정확히 알 필요가 없어. 그저 내가 뚫어준 창구를 통해 나와 소통하면 돼.

    ? 예를 들어, TV 를 컨트롤 하기 위한 리모콘을 생각해봅시다.

  • 리모콘의 전원 버튼, 채널 버튼, 음량 버튼 은 TV 를 컨트롤 하기 위한 창구 = API입니다.

  • 여러분들이 실제로 음량 버튼 내부 회로 및 하드웨어가 어떻게 동작하는지 알 필요가 있을까요?

  • 그저 음량 버튼을 눌렀을 때TV 음량이 조절된다. 의 결과가 잘 도출되는지가 중요합니다.

    • 음량 버튼을 눌렀다 = API 에게 내가 원하는 요청을 했다 = API Request
    • 음량이 조절 되었다 = API 로 부터 요청의 결과를 받았다 = API Response

    조금 더 실제 개발 상황을 예로 들어볼까요?

  • 서버의 데이터베이스에 모든 유저의 정보를 담고 있습니다. 전화번호까지요.

  • 클라이언트 (= iOS 네이티브 앱) 에서 “아담” 이라는 유저의 정보를 알고 싶습니다.

  • 서버는 API 로 UserInfo 라는 API 를 뚫어 놓았고, 이 API 를 사용하면 유저의 정보를 알 수 있습니다. API 명세는 다음과 같습니다.

  • API Request 는 이렇게 보내주세요.

{
		name: "Adam"
}
  • API Response 는 이렇게 보내주겠습니다.
{
		"name": "Adam",
		"phoneNumber": "010-1111-2222",
		"Mbti": "ENTJ"
}
  • iOS 개발자인 여러분은 이제 이 API 명세를 보고, Request JSON 과 Response JSON 형식을 지켜서 서버와 소통하는 코드를 작성하면 됩니다.
  • 서버가 이 데이터를 돌려주기 위해서 내부적으로 어떤 로직을 수행했고, 서버 데이터베이스 내부가 어떻게 생겨먹었고를 알 필요가 없습니다.
  • 그저 서버가 뚫어놓은 API 라는 창구 를 통해서 서버와 소통을 하고, 원하는 결과를 얻으면 iOS 개발자의 책임은 끝입니다.

👨🏻‍🍳 다시, 이번엔 레스토랑에 비유해보겠습니다.

  • 클라이언트 앱 = 레스토랑의 손님
  • API = 레스토랑의 메뉴판, 웨이터
  • 서버 = 레스토랑의 요리사
  • 손님이라는 클라이언트메뉴판, 웨이터 라는 창구를 통해서 요리사라는 서버의 음식을 받습니다.
    • 손님이 메뉴판을 보고 웨이터를 부른다 → API 명세를 파악한다
    • “봉골레 파스타 주세요” → API Request
    • 요리사가 파스타를 손님에게 바친다 → API Response

마지막으로 API 가 뭔지 정리해보면,

  • API 는 직역 그대로 Application Programming 에 필요한 Interface 입니다.
  • 즉, 어떤 프로그램을 개발할 때 원하는 기능들을 제공해주는 창구, 설명서, 도구 입니다.

Swift Codable


🧑🏻‍💻 Swift 의 인코딩과 디코딩

  • 인코딩: 데이터를 특정 형식으로 변환하는 것.
  • 디코딩: 인코딩 된 데이터를 다시 원본으로 변환하는 것.
  • Swift 의 Codable 프로토콜을 채택한다는 것은 인코딩과 디코딩이 될 수 있음을 의미.
    • Codable 안을 열어보면 Decodable & Encodable 로 구현되어있음.
  • 서버와 통신하기 위해서, JSON 형식으로 인코딩을 많이 한다.
struct PhoneBook: Codable {
    
    let name: String
    let phoneNumber: String
}

Codable 을 채택함으로써 인코딩 디코딩이 가능한 객체가 됨.

🧑🏻‍💻 JSON 형식의 데이터에서 Swift 로 데이터를 디코딩해서 추출하는 과정

import Foundation

struct PhoneBook: Codable {
    
    let name: String
    let phoneNumber: String
}

// string 으로 json 모양의 데이터를 생성.
let jsonString = """
[
    {
        "name": "Adam",
        "phoneNumber": "010-1111-2222"
    },
    {
        "name": "Eve",
        "phoneNumber": "010-3333-4444"
    },
    {
        "name": "Abel",
        "phoneNumber": "010-5555-6666"
    }
]
"""

// jsonString 으로 jsonData 를 생성.
let jsonData = jsonString.data(using: .utf8)!

// Swift 가 제공하는 JSON 디코더.
let jsonDecoder = JSONDecoder()

// JSON -> Codable 디코딩 진행.
do {
    let phoneBooks = try jsonDecoder.decode([PhoneBook].self, from: jsonData)
    for phoneBook in phoneBooks {
        print("name: \(phoneBook.name), phoneNumber: \(phoneBook.phoneNumber)")
    }
} catch {
    print("JSON 디코딩 실패")
}


URL 구조


URL 의 구조에 대해 자세히 공부해봅니다.

  • URL (Uniform Resource Locators): 웹에서 특정 위치를 나타내는 주소.
  • Protocol: http, https → 인터넷 통신 규약을 의미.
  • Domain: 자원이 위치한 서버(컴퓨터)의 이름. 예를 들어 google, naver . url 의 정체성을 나타낸다.
  • Port: 구체적으로 어떤 서버를 이용할지 번호로 결정. HTTP 의 경우 80. HTTPS 는 443.
  • Path: 서버에서 제공하는 자원의 경로를 나타냄.
  • Query: 자원에 대한 추가적인 매개변수를 전달하는 데 사용됨. 주로 key=value 형식으로 표현되며, 여러 개의 매개변수는 &로 구분.
  • Fragment: 자원 내에서 특정 부분을 가리킬 때 사용. ex) https://ko.wikipedia.org/wiki/대한민국#문화

REST API


REST API (Representational State Transfer) 란 무엇인지 공부합니다.

  • 전세계에서 대표적으로 널리 쓰이는 API 형식 중 하나.
  • 상태 (State) 를 표현해서 정보를 주고 받는 API 이다.
  • HTTP URL 을 통해서 자원을 명시한다.
  • HTTP Method (GET, POST, PUT, DELETE 등) 를 통해 해당 자원을 어떻게 할 것인지 CRUD 를 결정한다.
    • GET: 자원을 조회합니다.
    • POST: 자원을 생성합니다.
    • PUT: 자원을 업데이트합니다.
    • DELETE: 자원을 삭제합니다.
  • REST API 도 결국 API 이기 때문에, 데이터를 주고 받는 형식, 창구라고 생각 할 수 있음.

🧐 예를들어, https://spartacodingclub.com 서버와 REST API 통신을 한다고 가정해봅시다.

스파르타 코딩클럽의 유저 정보 데이터들은 https://spartacodingclub.com/users 에 저장되어있다고 가정합니다. (실제로 그렇지 않습니다.)

유저 데이터를 조회하고 싶으면 GET 메소드와 해당 URL 을 사용해서 네트워크 통신을 하면 됩니다.

요청 방식: GET https://spartacodingclub.com/users

[
    {
        "id": 1,
        "name": "Adam",
        "email": "adam.doe@example.com"
    },
    {
        "id": 2,
        "name": "Eve",
        "email": "eve.smith@example.com"
    }
]

위처럼 URL 을 가지고 네트워크 통신을 할 수 있게 하는 Swift 의 클래스가 바로 URLSession 입니다.


URLSession


URLSession 은 Swift 에서 서버와 통신하기 위해 제공되는 클래스.

  • URLSession 을 다루기 위해서는 크게 아래 2가지 개념을 알아야 합니다.
    1. URLSessionConfiguration
    2. URLSessionTask
  1. URLSessionConfiguration

    Configuration 이란 환경 설정을 의미.

    URLSession 으로 네트워크 통신을 하되, 여러가지 커스텀한 설정들을 할 때 URLSessionConfiguration 을 이용.

    예를들어 네트워크 통신의 타임아웃 시간 설정, 네트워크 통신 캐시 정책 설정 등을 세팅할 수 있음.

    URLSession 객체를 생성하려면 URLSessionConfiguration 을 넣어줘야 함.

    다음과 같이 default configuration 을 활용해 URLSession 생성 가능.

let defaultUrlSession = URLSession(configuration .default)
  1. URLSessionTask

    URLSessionTask 으로 네트워크 통신을 할 때 어떤 태스크를 수행할 것 인지 결정 가능.

    • URLSessionDataTask: GET 요청. 서버로부터 데이터를 가져오거나 서버에 데이터를 전송할 때 사용.
    • URLSessionDownloadTask 파일 다운로드를 처리할 때 사용. 백그라운드 다운로드 지원.
    • URLSessionUploadTask: 파일 업로드를 처리할 때 사용. 백그라운드 업로드 지원.

URLSession 을 통해, 서버의 데이터를 GET 해오는 예제 코드를 작성해봅시다.

  • https://reqres.in/api/users/1
  • 위 URL 은 테스트를 위한 데이터를 내려주는 사이트입니다.
  • GET 메소드를 사용해서 REST API 통신을 수행해봅시다.
import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        fetchData()
    }
    
    // 서버 데이터를 불러오는 메서드 선언
    private func fetchData() {
        
        let defaultUrlSession = URLSession(configuration: .default)
        
        guard let url: URL = URL(string: "https://reqres.in/api/users/1") else {
            print("URL is not correct")
            return
        }
        
        // URLRequest 설정
        var request: URLRequest = URLRequest(url: url)
        
        // GET 메소드 사용
        request.httpMethod = "GET"
        
        // json 데이터 형식임을 나타냄
        request.addValue("application/json", forHTTPHeaderField: "Content-Type")

        // URLSession 생성 (기본 default 세션)
        let session: URLSession = URLSession(configuration: .default)

        // dataTask
        session.dataTask(with: request) { (data, response, error) in
		        // http 통신 response 에는 status code 가 함께오는데, 200번대가 성공을 의미.
            let successRange: Range = (200..<300)
            
            // 통신 성공
            guard let data, error == nil else { return }
            
            if let response: HTTPURLResponse = response as? HTTPURLResponse{
                print("status code: \(response.statusCode)")
                
                // 요청 성공 (StatusCode가 200번대)
                if successRange.contains(response.statusCode){
                    
                    // decode
                    guard let userInfo: ResponseData = try? JSONDecoder().decode(ResponseData.self, from: data) else { return }
                    print(userInfo)
                    
                } else { // 요청 실패 (Status code가 200대 아님)
                    print("요청 실패")
                }
            }
            
        }.resume()
    }
}

// 데이터 구조체 정의
struct UserData: Codable {
    let id: Int
    let email: String
    let firstName: String
    let lastName: String
    let avatar: URL
    
    // JSON 키와 구조체 프로퍼티 간의 매핑을 위해 CodingKeys 열거형 정의
    enum CodingKeys: String, CodingKey {
        case id
        case email
        case firstName = "first_name"
        case lastName = "last_name"
        case avatar
    }
}

// Support 구조체 정의
struct SupportData: Codable {
    let url: URL
    let text: String
}

// 최상위 구조체 정의
struct ResponseData: Codable {
    let data: UserData
    let support: SupportData
}


→ 서버에서 받아온 값이 잘 print 되는 것 확인.


profile
맨날 최선을 다하지는 마러라. 피곤해서 못산다.

2개의 댓글

comment-user-thumbnail
2024년 12월 27일

지나칠 수 없는 썸네일이네요

답글 달기
comment-user-thumbnail
2025년 1월 2일

예시가 넘 찰떡같아서 이해가 잘되었어요!! 감사합니당

답글 달기