샘성의 iOS 개발 일지

네이버 검색 API 사용하기 (feat. RxSwift, Alamofire) 본문

iOS/ReactiveX

네이버 검색 API 사용하기 (feat. RxSwift, Alamofire)

SamusesApple 2023. 6. 23. 23:36
728x90

1. 네이버 개발자 앱 등록하기

   네이버 로그인 후, 하단의 링크에서 애플리케이션 등록을 진행한다.

 

 

애플리케이션 - NAVER Developers

 

developers.naver.com

 

 

 

 

     휴대폰 인증을 한 후, 회사이름은 없기에 공란으로 두었다.

 

 

 

 

      원하는 '애플리케이션 이름' 을 입력 후, 사용 API의 최상단에 있는 '검색'을 선택한다.

     (필자는 RxSwift를 활용하여 검색 기능을 구현할 것..)

 

   그리고 'iOS 설정'을 선택 후, 각자 본인의 프로젝트의 번들 ID를 입력한다.

 

 

 

 

   그러면 상단처럼 Client ID와 Client Secret을 발급 받을 수 있다!

 

 

 

 

 

 

 

2. 검색 API 살펴보기

 

이제 검색 API를 어떻게 사용하면 되는지 살펴볼 차례다!

 

검색 > 블로그 - Search API

검색 > 블로그 블로그 검색 개요 개요 검색 API와 블로그 검색 개요 검색 API는 네이버 검색 결과를 뉴스, 백과사전, 블로그, 쇼핑, 영화, 웹 문서, 전문정보, 지식iN, 책, 카페글 등 분야별로 볼 수

developers.naver.com

 

 

 

 

   이 중 어떠한 검색 결과를 조회할 것인지 선택 하면 된다.

   필자는 간단하게 블로그 검색을 요청할 것이다.

 

 

 

  필수 파라미터는 역시나 검색어이고 1번에서 받았던 Client ID와 Client Key가 요청할 때 필요하다.

 

 


- GET /v1/search/blog.xml?query=%EB%A6%AC%EB%B7%B0&display=10&start=1&sort=sim HTTP/1.1
- Host: openapi.naver.com
- User-Agent: curl/7.49.1
- X-Naver-Client-Id: {애플리케이션 등록 시 발급받은 클라이언트 아이디 값}
- X-Naver-Client-Secret: {애플리케이션 등록 시 발급받은 클라이언트 시크릿 값}


* 요청 예시 *
curl  "https://openapi.naver.com/v1/search/blog.json?query=안녕utf-8변환된값&display=10&start=1&sort=date" \
    헤더 "X-Naver-Client-Id: {애플리케이션 등록 시 발급받은 클라이언트 아이디 값}" 
    헤더 "X-Naver-Client-Secret: {애플리케이션 등록 시 발급받은 클라이언트 시크릿 값}"

    요청 예시대로 작성한다면 서버에 하단과 같은 요청을 보내게 된다.

  • 요청 응답 데이터 형태 : JSON
  • 검색 범위 : 블로그
  • 검색어 : '안녕'
  • 요청 검색 결과 갯수 : 10개 
  • 검색 시작 위치 : 1
  • 정렬 : 날짜순으로 내림차순 정렬 (기본값)

 

 

 

 

3. Postman으로 테스트 JSON 데이터 받기

  하단의 HTTPS 요청서를 Postman에 요청한다.

https://openapi.naver.com/v1/search/blog.json?query=%EB%A6%AC%EB%B7%B0&display=10&start=1&sort=date

 

 

  꼭!!! 1번에서 발급받은 Client ID와 Client Key도 꼭 같이 넣어서 요청을 넣어야 응답을 정상적으로 받을 수 있다.

 

 

 

   이제 서버로부터 받은 응답을 복사하여 하단의 JSON을 Swift 형태로 파싱해주는 사이트에 붙여넣는다.

 

 

Instantly parse JSON in any language | quicktype

 

app.quicktype.io

 

그럼 이렇게 응답을 받을 수 있다.

이제 이 응답을 원하는 데이터 모델 형태로 다듬어서 사용하면 된다!

 

 

 

 

4. 실제 요청 코드

 

   필자는 NetworkManager라는 싱글톤을 하나 만들어서 네트워크 요청을 담당하도록 만들었다.

Alamofire + RxSwift 조합으로 네트워킹 요청하는 코드를 만들어보았다.

* 네트워킹 *

import Foundation
import Alamofire
import RxSwift

struct NetworkManager {
    
    static let shared = NetworkManager()
    
    private var header: HTTPHeaders = [
        "X-Naver-Client-Id": 개인 클라이언트 ID,
        "X-Naver-Client-Secret": 개인 클라이언트 Secret
    ]
    
    private init() { }
    
    
    // MARK: - Parameters
    
    private func searchParameters(_ keyword: String) -> [String: Any] {
        [
            "query" : keyword.utf8, // utf8로 변환해서 요청 넣어야함!!!!
            "display" : "20"  // 검색 결과 20개 요청
        ]
    }
    
    // MARK: - Get

    func getSearchResult(_ keyword: String) -> Observable<[Item]?> {
        return Observable.create { emitter in
        let request = AF.request(URL(string: "https://openapi.naver.com/v1/search/blog.json")!,
                                              method: .get,
                                              parameters: searchParameters(keyword),
                                              headers: header)
        .responseDecodable(of: Search.self) { response in
            switch response.result {
            case .success(let search):
                emitter.onNext(search.items)
            case .failure(let error):
                emitter.onError(error)
            }
        }
            return Disposables.create {
                request.cancel()
            }
        }
    }
}

 

* SearchVC - Input Bind *
private func bindInput() {
        searchController.searchBar.rx.text // searchBar의 텍스트 구독하기
            .orEmpty
            .bind(to: searchText) // 외부 변수에 저장
            .disposed(by: disposeBag)
        
        searchText
            .filter({ $0 != nil })
            .flatMap({ NetworkManager.shared.getSearchResult($0!) }) // 외부 변수 값 변경 될 때마다 네트워킹 요청
            .bind(onNext: { [weak self] item in
                self?.data = item // data 배열에 담기
                self?.tableView.reloadData() // 결과값 받을 때마다 테이블뷰 reload
            })
            .disposed(by: disposeBag)
    }

 

* 전체 VC 코드 *

import UIKit
import RxSwift
import RxCocoa
import SnapKit
import Then

class MainViewController: UIViewController {
    
    private var searchText: BehaviorSubject<String?> = BehaviorSubject(value: "")
    
    private var data: [Item]? = []
    
    private let disposeBag = DisposeBag()
    
// MARK: - Components
    
    private let searchController = UISearchController(searchResultsController: nil)
    
    private lazy var tableView = UITableView().then {
        $0.backgroundColor = .white
        $0.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
        $0.dataSource = self
        $0.delegate = self
    }
    
// MARK: - Lifecycle
    override func viewDidLoad() {
        super.viewDidLoad()

        self.navigationItem.searchController = searchController
        
        view.addSubview(tableView)
        tableView.snp.makeConstraints { make in
            make.height.equalTo(view.frame.height)
            make.width.equalTo(view.frame.width)
        }
        
        bindInput()
    }

// MARK: - Bind
    
    private func bindInput() {
        searchController.searchBar.rx.text
            .orEmpty
            .bind(to: searchText)
            .disposed(by: disposeBag)
        
        searchText
            .filter({ $0 != nil })
            .flatMap({ NetworkManager.shared.getSearchResult($0!) })
            .bind(onNext: { [weak self] item in
                self?.data = item
                self?.tableView.reloadData()
            })
            .disposed(by: disposeBag)
    }

    
}

// MARK: - UITableViewDataSource

extension MainViewController: UITableViewDelegate, UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return data?.count ?? 0
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell")!
        cell.textLabel?.text = data?[indexPath.row].title?.htmlToString
        return cell
    }
}

 

결과 :

 

SearchBar에 검색어를 입력 할 때마다 데이터를 요청해서 테이블뷰에 세팅하는 것을 확인할 수 있다.

728x90

'iOS > ReactiveX' 카테고리의 다른 글

ReactorKit Unit Test하기  (0) 2023.07.04
ReactorKit을 MVVM에 넣어보기  (0) 2023.07.02
[XCTest] Unit Test작성하기 (feat. RxSwift)  (0) 2023.07.01