React Native 독서기록 앱 개발일지 - 외부 API 정보 가져오기

thoho·2022년 12월 9일
0
post-thumbnail

오늘 개발해보려는 내용은 알라딘 오픈 API를 이용해, 특정 문자열을 입력하면 해당 문자열 관련 책 정보를 가져오는 것. 브라우저에 GET 요청을 보내야하고, 이를 위해 Axios를 사용한다.

체계적으로 계획을 세우는 게 아니라 필요한 기능을 하나하나 제작해보며 연습해보는 목적이 더 큰 프로젝트이기에, 우선 알라딘 오픈 API에서 필요한 정보를 받아오는 것부터 작업.

Axios

Axios는 node.js 환경에서 동작하는 HTTP 클라이언트이다.
Node.js에서 HTTP 메소드(GET, POST, PUT 등)를 처리할 수 있게 해준다.
예제가 있는 참고 사이트

다음과 같이 설치할 수 있다.

$ npm install axios

import는 다음과 같이.

import axios from 'axios';

자세한 사용은 개발을 진행하며 차근차근 진행하는 것으로.

다른 js 파일에 있는 Component 가져오기

우선 파일 관리가 용이하도록 파일 분리부터 해줬다.

앱을 시작하면 기본적으로 App.js에 저장된 정보를 불러온다. Search.js에 있는 <Search>라는 component를 App.js에서 import하여 사용하도록 구현.

// App.js
import React from 'react';
import Search from './Search';

const App = () => {
  return (
    <Search></Search>
  )
}

export default App;
// Search.js
import React from 'react';
import {View, Text} from 'react-native';

const Search = () => {
    return (
        <View>
            <Text>hello</Text>
        </View>
    )
}

export default Search;

위의 코드를 작성하고 npm run android를 실행하면 화면에 hello가 나타나는 것을 확인할 수 있다.

Search.js에서는 <Search>라는 Component만을 내보낼 것이므로 export default Search;라고 했지만, 만일 한 파일에서 여러개의 Component를 내보낼 것이라면 다음과 같이 사용할 수 있다.

// Search.js
export {Search};

// App.js
import {Search} from './Search';

옛날에 이걸 몰라서 한참 헤맸던 기억이.



Axios GET 응답 받기

시험용으로 Dog API에서 이미지를 받아왔다.

// Search Component
  const [imageSrc, setImageSrc] = useState("");
  
  useEffect(()=>{
    axios.get("https://dog.ceo/api/breeds/image/random")
      .then((response) => {
        setImageSrc(response.data.message);
        console.log("imageSrc : " + imageSrc);
      })
      .catch((error) => {
        console.log(error)
      });
  });

useEffect는 component가 렌더링될 때마다 실행되는 Hook
useState는 변수와 함수의 쌍으로, 함수를 실행할 때 마다 입력된 parameter의 값으로 변수를 수정한다.

axios의 get 함수는 다음과 같이 작동한다.

axios.get(URL, {header: ...}).then().catch()

URL에 GET을 요청하고(경우에 따라 header를 추가로 보낼 수 있다. 로그인 된 유저를 위한 정보를 가져오는 기능을 구현할 경우, 유저 고유의 token을 header를 통해 보낼 수 있다), 만일 응답을 받아오는 데에 성공하면 then을 수행하고 에러가 발생하면 catch를 수행한다.

//Search Component
...

  return (
    <View>
      <Image
        source={{ uri: imageSrc }}
        style={{ width: 400, height: 400 }}
      />
    </View>
  )

네트워크 요청을 통해 이미지를 가져오려면 source에 source={{uri : imageSrc}}의 형식으로 입력한다. 여기서 imageSrc에는 https://images.dog.ceo/breeds/schnauzer-giant/n02097130_5609.jpg이런 형식의 주소가 들어간다.




알라딘 Open API에서 정보 가져오기

요청 방식 확인

우선 결과의 모양은 크게 신경쓰지 않은 채 기능적인 부분부터 구현하기로 했다.
알라딘 Open API 블로그에 들어가 관련 내용을 확인해보았다. 요청 URL 샘플 및 Request Parameter 관련 정보가 나와있다.
알라딘 Open API 블로그

샘플 URL의 형식은 다음과 같다.

http://www.aladin.co.kr/ttb/api/ItemSearch.aspx?ttbkey=TTBKey&Query=aladdin&QueryType=Title&MaxResults=10&start=1&SearchTarget=Book&output=xml&Version=20070901

요청 URL에 Parameter를 덧붙혀서 전송하는 방식(Parameter를 하나라도 빼먹으면 오류가 발생한다). Parameter를 보면 ttbkey라는 항목이 있는데, 이 란에는 Request를 보내는 사람에 따라 별도의 API Key를 발급받아 입력해야한다.
알라딘 Open API Key 관리 페이지


API Key 코드 상에서 숨기기

Github에 코드를 업로드할 것이기 때문에, API Key가 노출되지 않게 처리했다.
참고 블로그

사용자 입력 받아 Request 보내기

	<TextInput
      onChangeText={setKeyword}
      placeholder="검색어를 입력해주세요."
    />
    <Button
      title="검색"
      onPress={()=>{
        axios.get(setURL(keyword))
          .then((response) => {
            processBookData(response.data);
          })
          .catch((error) => {
            console.log(error);
          })
      }}
    />

TextInput Component에서 keyword를 입력받고, Button을 누르면 onPress함수가 callback되며 해당 함수를 실행한다. 이 때 onPress 함수에 Request를 보내는 부분을 구현하였다.

Obejct is not a function

이 오류가 계속 나와서 대체 무슨 오류인가 했더니, 화살표 함수에 해당하는 ()=>{} 형식으로 함수를 입력한 게 아니라, {}만 입력해서 Object로 분류되는 바람에 생긴 어이없는 실수였다.

계속해서 코드를 보자면, setURL함수는 keyword를 바탕으로 Request URL을 생성해주는 함수이다.

const setURL = (keyword) => {
  var URL = config.aladinBasicURL + "?ttbkey=" + config.aladinKey + "&Query=" + keyword + "&QueryType=keyword&MaxResults=10&start=1&SearchTarget=Book&output=js&Version=20070901";
  return URL;
}

이렇게 생성한 URL을 axios.get()에 넣어주어 함수를 호출했다. 정상적인 응답이 오면 .then((response)=>{})이 실행되는데, 응답 받은 데이터가 Javascript Object의 String 형식이기 때문에, 이를 처리해주어야했다.

const [bookData, setBookData] = useState(""); // 받아온 데이터를 object의 형식으로 저장

const processBookData = (data) => {
  data = data.replaceAll('\\', ''); 	// 역슬래시(\)를 제거.
  data = data.substr(0, data.length-1); // 문자열 맨 끝의 세미콜론(;)을 제거.
  setBookData(JSON.parse(data).item); 	// JSON 형식으로 문자열을 parse해, 그 중 item만 저장
}

escape 문자 관련 오류가 계속 발생해서 받아온 데이터를 확인해보았더니, JS파일을 받아오다보니 문자열에 역슬래시()가 사용되어서 발생한 문제였다. 모든 역슬래시를 제거해주었다. 문자열 끝에 세미콜론도 붙어있길래 삭제.

위에서 저장한 데이터의 item은 가져온 책들의 배열이다. 해당 정보만이 필요하므로 따로 저장하였다.

저장한 책들의 정보를 화면에 출력

우선 간단하게 책들의 제목만 출력해보았다. 지금 bookData에는 책들의 정보가 Object의 배열로 저장된 형태이다. 각각의 정보는 index로 접근할 수 있으며, 책의 제목이 알고 싶으면 bookData[0].title로 접근하면 된다.

반복되는 데이터를 같은 형식으로 화면에 출력하고 싶다면 Object.values().map()을 사용하면 된다.

<View>
  {Object.values(bookData).map((book, index)=>(
	<View key={index}>
	  <Text>
        {book.title}
	  </Text>
	</View>
  ))}
</View>

map함수는 입력받은 Array를, map의 인자로 들어온 callback 함수의 처리를 거쳐 새로운 배열로 반환해준다. 여기서 callback함수의 첫번째 parameter에는 배열의 개별 요소가 들어가게된다.

이렇게 map을 이용해 여러개의 View를 만들려할 때, 각 View를 구분하는 고유한 key가 필요하다. 이를 map에서 생성해주는 index를 통해 구분해준다.

component 내에서 inline 코드를 실행하려고 할 때, 중괄호로 감싸주어야한다. {book.title}을 입력하면 현재 book의 title 값이 <Text> component의 Child로 들어가게된다.

화면에 map 결과물이 제대로 출력되지 않는 오류

뜻밖의 오류는 대체로… 말도 안 되는 실수로 발생한다. map의 인자로 주어지는 callback 함수의 결과로 component의 배열을 만들고 싶으면, 해당 내용을 중괄호가 아니라 소괄호로 감싸주어야한다.


여기까지 수행하면 위와 같은 결과가 나온다.

profile
어떻게든 굴러가고 있는

0개의 댓글