데보션앱 소개페이지 바로가기
로그인 선택

신고하기

CLOSE
신고사유 (대표 사유 1개)
상세내용 (선택)
0/200
  • 신고한 게시글은 더 이상 보이지 않습니다.
  • 이용약관과 운영정책에 따라 신고사유에 해당하는지 검토 후 조치됩니다.
  • 허위 신고인 경우, 신고자의 서비스 이용이 제한될 수 있으니 유의하시어 신중하게 신고해 주세요.
(이 회원이 작성한 모든 댓글과 커뮤니티 게시물이 보이지 않고, 알림도 오지 않습니다.)

미리보기

커뮤니티

      1,234

      badge 23.06.15

      글 등록

      카테고리를 선택해주세요.

      DEVOTEE를 활성화 시키면
      지금 작성한 커뮤니티 글에 대해 1개의 댓글을 달아줍니다.

      버튼을 누르면 글 수정 시 ChatGPT가 작성한 댓글이 수정됩니다.

      임시저장함에 저장되었습니다. 저장일시 : 2022.5.17 14:29:08

      임시저장함

      제목을 선택하시면 이어서 작성이 가능하며,
      최대 20건까지 저장합니다.
      컨텐츠 유형, 제목, 저장일시, 삭제로 이뤄진 임시저장 목록
      컨텐츠 유형 제목 저장일 삭제

      데보션 블로그 게재 요청

      CLOSE
      • *
      • *

      본인인증

      효율적인 데보션 서비스 이용 및
      고객님의 소중한 개인정보보호를 위해
      본인인증을 진행해주세요. 본인인증 미 진행 시 로그인이 제한됩니다.
      본인인증 실패

      본인인증 로그인에 실패하였습니다.
      회원이 아니시거나 본인인증 등록이
      완료되지 않은 사용자입니다.

      회원정보 연결

      Kakao OpenAPI 를 활용한 나만의 도서검색 앱 구현해보기 (ft. Volley Library)

      매직 23.10.23
      8,807 21 0
      DEVOTEE 요약
      이 블로그는 Android 네이티브 앱 개발에 좀 더 친숙해지기 위해 책 검색 기능을 가진 앱을 만드는 과정을 설명한다. 앱은 Kakao의 도서 검색 OpenAPI와 안드로이드의 Volley Library를 활용해 만들었다. 결국, Kakao OpenAPI를 이용해 도서를 검색하고 그 결과를 Volley를 통해 앱에 띄우는 간단한 앱을 성공적으로 완성하였다.
      DEVOTEE 추천 블로그

      블로그 목적

      Android native 개발과 더욱 친해지기(?) 위한 목적을 이루기 위해서 간단한 도서검색 기능을 갖춘 앱을 구현해본다.


      블로그 요약

      • Kakao 도서검색 Openapi 를 알아본다.

      • 안드로이드 Volley Library 에 대해서 알아본다.

      • Android native 기반의 간단한 앱을 직접 구현하고 그 결과를 공유한다.


      Kakao 도서검색 OpenAPI 소개

      Kakao 의 Daum 검색 OpenAPI

      https://developers.kakao.com/docs/latest/ko/daum-search/common

      image.png

      한마디로, Daum 검색 API는 포털 사이트 Daum에서 방대한 웹 문서, 동영상, 이미지, 블로그, 책, 카페를 검색하는 기능을 제공한다고 하네요.

      그중에서 저는 책를 검색하는 기능을 통해서 저만의 앱을 구현해 보고자 합니다.

      image.png

      책검색하기 URI

      image.png

      아래는 응답 Document 인데요..

      저는 title, authors, thumbnail, price, sale_price 를 추출해서 표현해보고자 합니다.

      image.png

      안드로이드 Volley Library 소개

      1. Volley 라이브러리란?

      Volley는 안드로이드 앱에서 네트워크 통신을 간단하고 효과적으로 처리하기 위한 라이브러리입니다. 

      이 라이브러리는 HTTP 요청을 보내고 응답을 처리하는 데 유용하며, 이미지 다운로드 및 캐시, JSON 파싱 등 다양한 네트워크 작업을 처리할 수 있습니다.


      Volley는 Google에서 개발하였으며, 대부분의 안드로이드 앱에서 사용 가능하며, 안정성과 성능이 뛰어나다는 장점이 있습니다.

      2. Volley 라이브러리 주요기능

      Volley 라이브러리의 주요 기능은 다음과 같습니다.

      • 네트워크 요청의 비동기 처리

      • 요청 데이터의 캐싱 및 관리

      • Multipart form 데이터 전송 지원

      • JSON, XML 등 다양한 데이터 형식의 파싱 지원

      • 오류 처리 및 로깅 기능 제공


      안드로이드 Volley Library를 활용한 간단한 예시

      그럼 차근차근 간단한 예시를 통해서 확인해 보겠습니다. 

      1.Gradle에 Volley 라이브러리 추가하기

      앱의 build.gradle 파일에 다음 의존성을 추가하여 Volley 라이브러리를 사용할 수 있습니다.

      dependencies {
          implementation 'com.android.volley:volley:1.2.0'
      }

      2.퍼미션 설정: AndroidManifest.xml 파일에 인터넷 권한을 추가합니다.

      <uses-permission android:name="android.permission.INTERNET" />

      3.네트워크 요청 및 응답 처리를 구현하면 됩니다. 

      아래는 Volley를 사용한 네트워크 요청 예시이고요. 참고하시면 될것 같습니다.

      이제 간단한 GET 요청을 보내고 응답을 처리하는 예시를 보겠습니다.


      네트워크 요청을 보낼 때는 다음과 같은 절차를 따릅니다.

      1) Request 객체 생성

      Request 객체는 네트워크 요청의 세부 정보를 담은 클래스입니다. 

      Volley에서는 다양한 종류의 Request 객체를 제공합니다.


      2) RequestQueue 객체 생성

      RequestQueue 객체는 Volley에서 제공하는 스레드 풀을 이용하여 네트워크 요청을 큐에 저장하고 순서대로 처리합니다.


      3) RequestQueue에 Request 객체 추가

      큐에 저장된 요청은 스레드 풀에서 순서대로 처리됩니다.


      4) 응답 처리

      서버로부터 받은 응답은 Response 객체를 통해 전달됩니다. Response 객체는 요청의 성공 여부와 응답 데이터를 담고 있습니다.


      다음은 실제로 Volley를 사용하여 네트워크 요청을 보내고 응답을 처리하는 예시입니다.

      import android.app.Activity;
      import android.os.Bundle;
      import android.widget.TextView;
      import com.android.volley.Request;
      import com.android.volley.RequestQueue;
      import com.android.volley.Response;
      import com.android.volley.VolleyError;
      import com.android.volley.toolbox.StringRequest;
      import com.android.volley.toolbox.Volley;
      
      public class MainActivity extends Activity {
          private TextView responseText;
      
          @Override
          protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.activity_main);
      
              responseText = findViewById(R.id.responseText);
      
              // Volley의 요청 큐 초기화
              RequestQueue queue = Volley.newRequestQueue(this);
      
              // 네트워크 요청 생성
              String url = "http://127.0.0.1/posts/1";
              StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
                  new Response.Listener<String>() {
                      @Override
                      public void onResponse(String response) {
                          // 요청 성공 시 응답 처리
                          responseText.setText("응답: " + response);
                      }
                  },
                  new Response.ErrorListener() {
                      @Override
                      public void onErrorResponse(VolleyError error) {
                          // 요청 실패 시 에러 처리
                          responseText.setText("에러: " + error.getMessage());
                      }
                  });
      
              // 요청 큐에 요청 추가
              queue.add(stringRequest);
          }
      }

      그리고, 아래는 XML 레이아웃 파일 (activity_main.xml) 예시 입니다.

      <?xml version="1.0" encoding="utf-8"?>
      <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
          xmlns:app="http://schemas.android.com/apk/res-auto"
          xmlns:tools="http://schemas.android.com/tools"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:orientation="vertical"
          android:padding="16dp"
          tools:context=".MainActivity">
      
          <TextView
              android:id="@+id/responseText"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:text="응답을 여기에 표시합니다."
              android:textSize="18sp" />
      
      </LinearLayout>

      위 코드를 사용하여 안드로이드 앱에서 Volley 라이브러리를 통해 네트워크 요청을 보내고 응답을 처리할 수 있게 되고요..

      또한 해당 예시를 사용하여 앱에서 다양한 네트워크 작업을 수행할 수 있습니다.


      Andorid Native 기반 나만의 도서검색기능 앱 구현

      그럼, Android Native 기반의 간단한 앱을 구현해볼께요~


      나만의 도서검색기능 앱을 구현하려면, 크게 아래와 같습니다.

      • UI 화면설계및 구현 (xml 파일)

      • 안드로이드 액티비티구현 (java 파일)

      우선, UI(webview_magic_menu_book_search.xml) 는 아래와같이 xml 로 구현하면 됩니다.

      <?xml version="1.0" encoding="utf-8"?>
      <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:orientation="vertical"
          android:padding="16dp">
      
          <EditText
              android:id="@+id/editText_search"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:hint="책 검색" />
      
          <Button
              android:id="@+id/button_search"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:text="검색" />
      
          <ProgressBar
              android:id="@+id/progressBar"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:layout_gravity="center"
              android:visibility="gone" />
      
          <ListView
              android:id="@+id/listView_books"
              android:layout_width="match_parent"
              android:layout_height="0dp"
              android:layout_weight="1"
              android:divider="@android:color/darker_gray"
              android:dividerHeight="1dp" />
      
          <Button
              android:id="@+id/button_load_more"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:text="더보기"
              android:layout_centerHorizontal="true"
              android:layout_alignParentBottom="true"
              android:layout_margin="16dp"
              android:visibility="gone" />
      
          <TextView
              android:id="@+id/textView_noData"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:layout_gravity="center"
              android:text="데이터가 없습니다."
              android:textSize="20sp"
              android:visibility="gone" />
      
      </LinearLayout>

      액티비티(MagicMenuBookSearchWebViewActivity.java)는 위에서 소개한 Kakao openAPI와 안드로이드의 Volley Library 를 사용해서 아래와 같이 구현해 봤습니다.

      (참고로, 하기의 API Key 는 생략처리하도록 할께요~ㅎㅎ)

      import android.os.Bundle;
      import android.text.TextUtils;
      import android.view.View;
      import android.widget.Button;
      import android.widget.EditText;
      import android.widget.ListView;
      import android.widget.ProgressBar;
      import android.widget.TextView;
      import androidx.annotation.Nullable;
      import androidx.appcompat.app.AppCompatActivity;
      import com.android.volley.Request;
      import com.android.volley.RequestQueue;
      import com.android.volley.Response;
      import com.android.volley.VolleyError;
      import com.android.volley.toolbox.JsonObjectRequest;
      import com.android.volley.toolbox.Volley;
      import com.book.Book;
      import com.book.BookAdapter;
      import org.json.JSONArray;
      import org.json.JSONException;
      import org.json.JSONObject;
      
      import java.util.ArrayList;
      import java.util.HashMap;
      import java.util.List;
      import java.util.Map;
      
      import uxpp.uc.R;
      
      public class MagicMenuBookSearchWebViewActivity extends AppCompatActivity {
      
          private static final String BASE_URL = "https://dapi.kakao.com/v3/search/book";
          private static final String API_KEY = "[자신이 발급한 kakao openapi key]";
      
          private EditText searchEditText;
          private Button searchButton;
          private ListView listView;
          private ProgressBar progressBar;
          private TextView noDataTextView;
          private Button loadMoreButton; // 추가 데이터를 불러오는 버튼
      
          private RequestQueue requestQueue;
          private BookAdapter adapter;
          private List<Book> bookList;
          private int page = 1;
          private boolean isLoading = false;
          private boolean hasMoreData = true;
          private String currentQuery = "";
      
          @Override
          protected void onCreate(@Nullable Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.webview_magic_menu_book_search);
      
              searchEditText = findViewById(R.id.editText_search);
              searchButton = findViewById(R.id.button_search);
              listView = findViewById(R.id.listView_books);
              progressBar = findViewById(R.id.progressBar);
              noDataTextView = findViewById(R.id.textView_noData);
              loadMoreButton = findViewById(R.id.button_load_more); // 더보기 버튼
      
              requestQueue = Volley.newRequestQueue(this);
              bookList = new ArrayList<>();
              adapter = new BookAdapter(this, R.layout.webview_magic_list_item_book, bookList);
              listView.setAdapter(adapter);
      
              searchButton.setOnClickListener(new View.OnClickListener() {
                  @Override
                  public void onClick(View v) {
                      if (!isLoading) {
                          currentQuery = searchEditText.getText().toString();
                          if (!TextUtils.isEmpty(currentQuery)) {
                              initData();
                              getJSONData(currentQuery);
                          }
                      }
                  }
              });
      
              loadMoreButton.setOnClickListener(new View.OnClickListener() {
                  @Override
                  public void onClick(View v) {
                      if (!isLoading) {
                          loadMoreData();
                      }
                  }
              });
          }
      
          private void initData() {
              page = 1;
              hasMoreData = true;
              bookList.clear();
              adapter.notifyDataSetChanged();
              isLoading = false;
              noDataTextView.setVisibility(View.GONE);
              loadMoreButton.setVisibility(View.GONE);
          }
      
          private void getJSONData(String query) {
              if (isLoading) {
                  return;
              }
              isLoading = true;
              progressBar.setVisibility(View.VISIBLE);
      
              String url = BASE_URL + "?query=" + query + "&size=10&page=" + page;
      
              JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET, url, null,
                      new Response.Listener<JSONObject>() {
                          @Override
                          public void onResponse(JSONObject response) {
                              try {
                                  JSONArray documents = response.getJSONArray("documents");
                                  if (documents.length() > 0) {
                                      for (int i = 0; i < documents.length(); i++) {
                                          JSONObject bookObject = documents.getJSONObject(i);
      
                                          String title = bookObject.getString("title");
                                          JSONArray authorsArray = bookObject.optJSONArray("authors");
                                          String author = (authorsArray != null && authorsArray.length() > 0) ? authorsArray.getString(0) : "";
                                          String thumbnail = bookObject.optString("thumbnail", "");
                                          int price = bookObject.optInt("price", 0);
                                          int sale_price = bookObject.optInt("sale_price", 0);
      
                                          Book book = new Book(title, author, thumbnail, price, sale_price);
                                          bookList.add(book);
                                      }
                                      adapter.notifyDataSetChanged();
                                      isLoading = false;
                                      progressBar.setVisibility(View.GONE);
                                      page++; // 페이지 증가
      
                                      // Check if it's the last page based on the 'is_end' field
                                      boolean isEnd = response.getJSONObject("meta").getBoolean("is_end");
                                      if (isEnd) {
                                          hasMoreData = false;
                                          if (page == 1) {
                                              noDataTextView.setText("데이터가 없습니다.");
                                          } else {
                                              noDataTextView.setText("마지막 데이터에 도달했습니다.");
                                          }
                                          noDataTextView.setVisibility(View.VISIBLE);
                                          loadMoreButton.setVisibility(View.GONE); // 더보기 버튼 숨김
                                      } else {
                                          // 추가 데이터가 있는 경우 더보기 버튼 표시
                                          loadMoreButton.setVisibility(View.VISIBLE);
                                      }
                                  } else {
                                      if (page == 1) {
                                          noDataTextView.setText("데이터가 없습니다.");
                                          noDataTextView.setVisibility(View.VISIBLE);
                                      } else {
                                          hasMoreData = false;
                                          noDataTextView.setText("마지막 데이터에 도달했습니다.");
                                          noDataTextView.setVisibility(View.VISIBLE);
                                      }
                                      progressBar.setVisibility(View.GONE);
                                  }
                              } catch (JSONException e) {
                                  e.printStackTrace();
                                  isLoading = false;
                                  progressBar.setVisibility(View.GONE);
                              }
                          }
                      },
                      new Response.ErrorListener() {
                          @Override
                          public void onErrorResponse(VolleyError error) {
                              isLoading = false;
                              progressBar.setVisibility(View.GONE);
                              String errorMessage = (error != null && error.getMessage() != null) ? error.getMessage() : "알 수 없는 오류";
                              noDataTextView.setText("요청 실패: " + errorMessage);
                              noDataTextView.setVisibility(View.VISIBLE);
                          }
                      }
              ) {
                  @Override
                  public Map<String, String> getHeaders() {
                      Map<String, String> headers = new HashMap<>();
                      headers.put("Authorization", "KakaoAK " + API_KEY);
                      return headers;
                  }
              };
      
              requestQueue.add(request);
          }
      
          private void loadMoreData() {
              if (!isLoading) {
                  getJSONData(currentQuery);
              }
          }
      }

      위의 소스코드의 주요내용을 간단하게 설명해 보자면요..

      1. 라이브러리 및 API 키 설정:

        • 소스 코드에서는 com.android.volley 라이브러리를 사용하여 네트워크 요청을 처리합니다. 또한 Kakao API를 사용하므로 개발자는 Kakao 개발자 포털에서 API 키를 발급받아서 API_KEY 변수에 설정해야 합니다.

      2. UI 요소 초기화:

        • EditText, Button, ListView, ProgressBar, TextView, 및 "더보기" 버튼을 XML 레이아웃에서 찾아서 초기화합니다.

      3. 이벤트 핸들러 등록:

        • 검색 버튼(searchButton) 및 "더보기" 버튼(loadMoreButton)에 클릭 리스너를 등록합니다.

      4. initData() 메서드:

        • 검색 초기화를 수행하는 메서드로, 페이지 번호, 데이터 유무 상태, 도서 목록을 초기화하고 UI 요소를 초기 상태로 돌립니다.

      5. getJSONData() 메서드:

        • 검색어를 사용하여 Kakao API를 호출하여 도서 정보를 가져오는 메서드입니다. 메서드 내부에서는 API 요청이 진행 중인지 여부를 확인하고, ProgressBar를 표시하며, API 요청을 수행합니다.

        • API 응답을 파싱하여 도서 정보를 추출하고, 도서 목록에 추가합니다.

        • 페이지 번호를 증가시키고, API의 'is_end' 값을 통해 추가 데이터가 있는지 여부를 확인합니다.

        • 오류 처리 및 UI 갱신을 수행합니다.

      6. loadMoreData() 메서드:

        • "더보기" 버튼을 클릭할 때 호출되는 메서드로, 현재 검색어를 사용하여 추가 데이터를 불러옵니다.

      7. 앱 실행 흐름:

        • 앱이 실행되면 XML 레이아웃을 화면에 표시하고 UI 요소를 초기화합니다.

        • 사용자가 검색어를 입력하고 검색 버튼을 클릭하면 onClick() 이벤트 핸들러가 호출됩니다.

        • initData()를 호출하여 검색 초기화를 수행하고, getJSONData()를 호출하여 Kakao API를 통해 도서 정보를 가져옵니다.

        • 사용자가 "더보기" 버튼을 클릭하면 loadMoreData()가 호출되어 추가 데이터를 불러옵니다.

        • API 응답을 파싱하여 도서 정보를 화면에 표시하고, 데이터가 없거나 마지막 페이지에 도달하면 UI에 알맞는 메시지를 표시합니다.

      아무튼, 위와 같이 구현 후 컴파일및 실행을 하여 아래와 같은 간단한 앱을 만들었습니다.


      실행 결과는....아래와 같이 최초 책 검색 창이 나오게되고요..

      image.png


      책검색에 제가 좋아라하는 "삼국지" 를 검색해 보겠습니다. (아래와 같이 잘 검색되어 리스트로 출력되네요.)

      image.png


      리스트를 더보고 싶으면 더보기 버튼을 클릭하시면 아래와 같이 추가적으로 리스트를 보실 수 있습니다.

      image.png

      썸네일, 책제목, 저자, 도서정가, 도서판매가가 잘나오고 있음을 확인 할 수 있었습니다.


      간단한 Android native 프로그래밍을 하면서 좀더 앱개발과 친해진(?) 느낌을 가질 수 있었습니다.

      아무튼 오늘하루도 감사한 하루였고요.. 더욱 열심히 사는 "매직" 이 되겠습니다.


      오늘의 블로그는 여기까지고요.

      항상 믿고봐주셔서 감사합니다.


      나의 목표 및 다짐을 항상 내곁에 두기

      • 목표

        • 나의 강점을 바탕으로 나의 일을 잘해냄으로써 타인과 사회를 아릅답게 만든다.

        • 현재 내가 가진 능력으로 누군가에 도움이 될 수 있을까? 에 대해서 항상 생각하기

        • 나는 블로그 생태계에서 IT 테크관련 파워블로거가 반드시 된다!

        • 유튜브 시장(?)을 호령하는 100만 유튜버가 반드시 된다!! (feat. 100만 유튜버가 반드시 될 매직!!)

        • 나는 꾸준히 공부하고 공부한것을 실천하는 창의적인 개발자가 된다.! (feat. devocean 개발&운영자가될 매직!!)

      • 목표를 이루기 위한 실천방안

        • 꾸준한 블로깅/기록법/독서법으로 넘버원이 아닌 온리원이 되보자.

        • 천사불여일행(千思不如一行)을 항상생각하며 체화 및 각인시키자.

          • "천번 생각하는것보다 한번 행동하는 것이 더 중요하다."

        • 기기일약 불능십보(騏驥一躍 不能十步) / 노마십가 공재불사(駑馬十駕 功在不舍)

          • 천리마도 한번에 열걸음을 뛸 수 없고, 느리고 둔한말이라도 열흘이면 하룻길을 간다.

        • 모든 실수에는 마술이 숨어 있다. 따라서 나는 실수하면 실수할수록, 그런 실수에서 더 많이 배울수록 삶에서 더 많은 마술을 갖는다.


      Advertising ...

      조금이나마 제 글이 도움이 되셨다면, 아래 배너 링크를 통해서 네이버인플루언서 팬이되주시면 제가 컨텐츠를 생산(?)하는데 큰 도움이 될것 같습니다. ^^;


      댓글 0

      DEVOTEE를 활성화 시키면
      지금 작성한 댓글에 AI가 댓글을 달아줍니다.

      매직 님의 최신 블로그

      더보기

      DEVOTEE 추천 블로그

      동영상 기고하기