• Jan
  • Feb
  • Mar
  • Apr
  • May
  • Jun
  • Jul
  • Aug
  • Sep
  • Oct
  • Nov
  • Dec
  • Sun
  • Mon
  • Tue
  • Wed
  • Thu
  • Fri
  • Sat
  • 27
  • 28
  • 29
  • 30
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

Alamofire 기초

APIService

APIConstants.swift

struct APIConstants {
    static let baseURL = "http://3.36.79.14:3000"
    // 로그인 url
    static let usersSignInURL = baseURL + "/users/signin"
    // 회원가입 url
    static let usersSignUpURL = baseURL + "/users/signup"
}

APIConstants에 통신 URL들을 잘 나눠서 작성!

GenericResponse.swift

데이터를 JSON 데이터 포맷으로 자유롭게 Decoding, Encoding 할 수 있도록 해주는 protocol

// 받아온 data를 swift type으로 decoding하기 위한 파일
// 응답에 대한 처리를 해줘서 성공과 실패에 대해 다른 decoding을 해줘야 함.
// 그걸 위해 만든 모델 파일 - GenericResponse.swift
struct GenericResponse<T: Codable>: Codable {
    // Codable: 데이터를 JSON 데이터 포맷으로 자유롭게 Decoding, Encoding 할 수 있도록 해주는 protocol
    var message: String
    var data: T? //요 타입도 임의로 만들어줘야 함
    
    enum CodingKeys: String, CodingKey {
        //json은 key, data값을 가지고있는데
        //json의 key값을 swift 타입으로 디코딩할때 이름이 똑같아야해서
        //CodingKeys를 통해 data변수의 key랑 struct를 이어주는 역할
        case message = "message"
        case data = "data"
    }
    
    init(from decoder: Decoder) throws {
        //데이터로 들어오는 값이 없을수도 있고 있을수도 있어서 먼저 처리해주는 것
        //데이터가 없을 때 nil로 처리
        let values = try decoder.container(keyedBy: CodingKeys.self)
        message = (try? values.decode(String.self, forKey: .message)) ?? ""
        data = (try? values.decode(T.self, forKey: .data)) ?? nil
    }
}

Data Model 만들기

quicktype.io

SignInData.swift

// MARK: - SignInData
struct SignInData: Codable {
    var email, password, userName: String
}

통신에 성공했을 때에 받아올 타입 생성

서버 통신에 따른 결과

NetworkResult.swift

//서버 통신과의 성공, 실패 등을 처리해주기 위한 열거형(enum) 타입
// 서버 통신에 대한 결과(성공, 요청에러, 경로에러, 서버내부에러, 네트워크 연결 실패)
enum NetworkResult<T> {
    case success(T) //임의로 만든 데이터 T 를 담아서 보낼 수 있음
    case requestErr(T)
    case pathErr
    case serverErr
    case networkFail
}

통신 구현하기

AuthService.swift

로그인 서버 통신을 위한 구조체

//로그인 서버 통신 구현을 위한 구조체
import Foundation
import Alamofire // 라이브러리 사용을 위해 import

struct AuthService { //파일 이름과 동일하게
    //싱글톤 디자인패턴 이용
    //싱글톤 객체 - 앱 어디서든 접근 가능
    static let shared = AuthService()
    
  	// 로그인 통신에 대한 함수 정의	
    	//closure를 함수의 파라미터로 받음
    	//@escaping - 탈출 클로저
    func signIn(email: String,
                password: String,
                completion: @escaping (NetworkResult<Any>) -> (Void)){
        
        let url = APIConstants.usersSignInURL // 통신 url
        let header: HTTPHeaders = [ // 요청 헤더: swift 딕셔너리형
            "Content-Type":"application/json",
          	"Authorization": UserDefaults.standard.string(forKey: "token") ?? ""
        ]
        let body: Parameters = [ // 요청 바디
            "email": email,
            "password":password
        ]
        
        // 원하는 형식의 HTTP Request 생성
        let dataRequest = AF.request(url,
                                     method: .post,
                                     parameters: body,
                                     encoding: URLEncoding.default, headers: header)
        
        // 데이터 통신 시작
        dataRequest.responseData{ (response) in // response = 통신의 결과
            // 통신 결과에 대한 분기 처리
            switch response.result {
            case .success:
              // 통신의 결과에 따라 statusCode와 value값을 가짐
                guard let statusCode = response.response?.statusCode else {
                    return
                }
                guard let data = response.value else {
                    return
                }
              // completion이란 클로져에게 전달할 데이터를 judgeSignInData라는 함수를 통해 결정
                completion(judgeSignInData(status: statusCode, data: data))
                
            case .failure(let err): print(err)
                completion(.networkFail) }
        }
    }
    
  // statusCode랑 decode 결과에 따라 NetworkResult를 반환시켜 줌
    private func judgeSignInData(status: Int, data: Data) -> NetworkResult<Any> {
      // 통신을 통해 전달받은 데이터를 decode
        let decoder = JSONDecoder()
        guard let decodedData = try? decoder.decode(GenericResponse<SignInData>.self, from: data) else {
            return .pathErr
        }
        // statusCode를 통해 통신 결과를 알 수 있음
        switch status {
          // 성공적으로 통신에 성공했다는 결과와 함께 decode한 data값도 전달
            case 200:
                return .success(decodedData.data)
          // 통신에는 성공했지만, 요청값에 대한 오류 처리.
          // 오류 결과와 함께 오류 메세지 전달
            case 400..<500:
                return .requestErr(decodedData.message)
          // server상의 에러 코드 (server 개발자가 지정)
          // 에러 결과만 보내 줌
            case 500:
                return .serverErr
            default:
                return .networkFail }
    }
    
}

싱글톤?

특정 용도로 객체를 하나 생성하여 공용으로 사용하고 싶을 때 사용하는 방법
여러 객체에서 접근 가능하도록 데이터를 사용하는 것
프로그램 내에서 단 하나의 인스턴스로만 클래스를 관리하고 사용할 수 있음
그러나 생성되고 나면 프로그램 종료 시까지 항상 메모리에 올라가 있으므로 적절하게 사용해야 함

@escaping 탈출 클로저

non-escaping인 경우에는 해당 함수 내에서의 호출만 가능
이러한 제약을 무시하기 위해 Escaping Closure를 사용함

VC에 연결