URL 주소를 이용하여 ImageView에 이미지 채우기 (Glide 활용)

|

URL 주소를 이용한 이미지 로딩

이미지의 URL 주소를 알고 있을 때, 그 이미지를 ImageView에 그리는 방법은 다음과 같습니다.

  • 이미지를 다운로드한다.
  • 다운로드한 이미지를 Bitmap 로딩하여 ImageView에 채워 넣는다.

이 경우, 구현해야 할 코드의 양도 꽤 많지만, 성능 문제를 위해 여러 가지 기법을 적용해야 합니다. 예를 들면, 다음과 같습니다.

  • ListView 등과 같이 여러 장의 이미지를 동시에 다운로드 할 수 있는 Thread 환경 구현
  • 한 번 다운로드 받은 이미지는 다시 받지 않도록, 그리고 성능을 위해 Image Cache 구현
  • Thread로 다운로드하는 동안 화면에는 ProgressBar를 표시


기본적으로 위의 요소들이 있지만, 하나 하나를 파고들면 고려해볼 것이 상당히 많습니다. 예를 들면 Image Cache에 무한대의 이미지를 저장할 수 없기 때문에 Cache의 리소스 관리도 필요하고, 이미지 다운로드 및 로딩이 끝났을 때 어느 ImageView에 이미지를 그릴 것인지 관리하는 ImageView의 레퍼런스 저장도 필요합니다. 또한 이러한 것들은 WeakReference로 관리하여 해당 ImageView가 메모리에 여전히 남아 있는지 GC(Garbage Collector)로 청소가 끝났는지도 체크해야 합니다.

예전에 프로젝트를 진행하면서 직접 구현한 적이 있었는데, 신경쓸게 너무 많아서 고생을 한 적이 있습니다. 물론, 그만큼 공부는 많이 되긴 했습니다.


Glide

이러한 것들을 한 꺼번에 해소해주기 위해서 오픈 소스 활용을 추천합니다. 오픈 소스는 유명한 것들이 많이 있는데, 5년 정도 전만 하더라도 저는 Android Universal Image Loader를 애용하였습니다. 지금도 무난히 사용하기에 충분히 괜찮은 라이브러리입니다. 하지만, 지금은 Glide 라이브러리를 사용해보려고 합니다. Glide는 구글이 인수한 Bump라는 회사에서 사용한 이미지 로딩 라이브러리입니다.


사용 방법

Gradle에 다음 코드를 추가해줍니다.

dependencies{
    compile'com.github.bumptech.glide:glide:3.7.0'
    ...
}


그리고 별도로 XML 코드를 건드릴 필요 없고, 다음과 같은 Java 코드로 ImageView에 이미지를 불러오게 할 수 있습니다.

Glide
    .with(mContext)
    .load(item.image)
    .centerCrop()
    //.placeholder(R.drawable.loading_spinner)
    .crossFade()
    .into(holder.image);

이렇게만 하면 끝입니다. 더 자세한 사용법은 여기에서 확인할 수 있습니다.

OkHttp를 활용한 GET, POST

|

OkHttp는 HTTP 및 HTTP/2 통신을 보다 쉽게 할 수 있도록 다양한 기능을 제공해주는 Android 및 Java 용 라이브러리입니다. 오픈 소스로 되어 있으며 GitHub에서 소스 확인 및 다운로드할 수 있습니다.


Gradle 설정

build.gradle에 다음 라인을 추가해줍니다.

compile 'com.squareup.okhttp3:okhttp:3.6.0'


GET 예제

public boolean getUserInfo(final Context context) {

  try {
    OkHttpClient client = new OkHttpClient();

    String url = SERVER_CONFIGURATION.ADDRESS + ":" +
        SERVER_CONFIGURATION.PORT + "/v1/userinfo";

    Request request = new Request.Builder()
        .addHeader("Authorization", "TEST AUTH")
        .url(url)
        .build();
    Response response = client.newCall(request)
        .execute();

    String result = response.body().string();

    Gson gson = new Gson();
    UserInfo info = gson.fromJson(result, UserInfo.class);

    Log.i("id: " + info.id);
    Log.i("name: " + info.name);

    return true;
  } catch(Exception e) {
    e.printStackTrace();
  }

  return false;
}


POST 예제

private boolean updatetMetaInfo(JsonItemMetaInfo metaInfo) {

  try {
    OkHttpClient client = new OkHttpClient();

    String url = SERVER_CONFIGURATION.ADDRESS + ":" +
        SERVER_CONFIGURATION.PORT + "/v1/updateMetaInfo";

    Gson gson = new Gson();
    String json = gson.toJson(metaInfo);

    Request request = new Request.Builder()
        .url(url)
        .post(RequestBody.create(MediaType.parse("application/json"), json))
        .build();

    Response response = client.newCall(request).execute();

    Log.i("request : " + request.toString());
    Log.i("Response : " + response.toString());

    return true;
  } catch(Exception e) {
    e.printStackTrace();
  }

  return false;
}


PUT 예제

public boolean registerAppToken(JsonToken token) {
  try {
    OkHttpClient client = new OkHttpClient();

    String url = SERVER_CONFIGURATION.ADDRESS + ":" +
        SERVER_CONFIGURATION.PORT + "/v1/registerAppToken";

    Gson gson = new Gson();
    String json = gson.toJson(token);

    Request request = new Request.Builder()
        .addHeader("key", "Content-Type")
        .addHeader("value", "application/json")
        .addHeader("description", "")
        .url(url)
        .put(RequestBody.create(MediaType.parse("application/json"), json))
        .build();

    Response response = client.newCall(request).execute();

    Log.i("request : " + request.toString());
    Log.i("Response : " + response.toString());

    return true;
  } catch(Exception e) {
    e.printStackTrace();
  }

  return false;
}

프로그램 강제 종료시키기

|

프로그램 강제 종료시키기

MMac에서 어플리케이션들이 응답 없음 상태(바람개비가 빙글빙글 돌아가면서)가 되는 경우가 종종 있습니다. 이런 경우 ‘닫기’ 버튼도 먹통이 되고 종료 단축키인 Command + Q 도 먹통이 됩니다.

이 경우는 응용 프로그램 강제 종료 기능을 호출해서 해당 어플을 종료시킬 수 있습니다.

image


단축키

단축키는 다음과 같습니다.

Command + Option + ESC

ExpandableListView 예제

|

Android에서 다음 그림과 같이 Depth가 존재하는 ListView를 만들고 싶을 때는 어떻게 해야 할까요? 여러 가지 방법이 있긴 한데, 가장 간편하게 만들 수 있는 방법으로는 ExpandableListView를 사용하는 방법이 있습니다. 이 UI 컴포넌트는 Android SDK에 기본으로 포함되어 있습니다.

image

위와 같은 화면을 구현하려면 크게 다음과 같은 요소들을 구현해주면 됩니다.

  • 첫 번째 Depth의 ItemView
  • 두 번째 Depth의 ItemView
  • ExpandableListView의 Adapter


item_view_setting_parent.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:id="@+id/layout_background"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:padding="15dp"
  android:orientation="horizontal">

  <ImageView
    android:id="@+id/icon"
    android:layout_width="32dp"
    android:layout_height="32dp"
    android:layout_marginLeft="16dp"
    android:src="@mipmap/ic_launcher" />

  <TextView
    android:id="@+id/name"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginLeft="16dp"
    android:layout_gravity="center"
    android:text="name"
    android:textColor="@color/black"
    android:textSize="22sp" />

</LinearLayout>


item_view_setting_child.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:id="@+id/layout_background"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:padding="15dp"
  android:orientation="horizontal">

  <TextView
    android:id="@+id/name"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginLeft="80dp"
    android:layout_gravity="center"
    android:text="name"
    android:textColor="@color/black"
    android:textSize="18sp" />

  <Switch
    android:id="@+id/data_switch"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginRight="18dp"
    android:layout_gravity="right"
    android:checked="true" />

</LinearLayout>


fragment_device_setting.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:app="http://schemas.android.com/apk/res-auto"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:fitsSystemWindows="false">

  <android.support.design.widget.AppBarLayout
    android:id="@+id/app_bar_layout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@color/background"
    android:fitsSystemWindows="true">

    <RelativeLayout
      android:layout_width="match_parent"
      android:layout_height="wrap_content">

      <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="12dp"
        android:layout_centerInParent="true"
        android:text="Device Setting"
        android:textColor="@color/white"
        android:textSize="24sp" />

    </RelativeLayout>

  </android.support.design.widget.AppBarLayout>

  <ExpandableListView
    android:id="@+id/expandable_listview"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:groupIndicator="@null"
    app:layout_behavior="@string/appbar_scrolling_view_behavior" />

</android.support.design.widget.CoordinatorLayout>


DeviceSettingExpandableAdapter.java

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.ImageView;
import android.widget.Switch;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.HashMap;

public class DeviceSettingExpandableAdapter extends BaseExpandableListAdapter {

  private Context mContext;
  private ArrayList mParentList;
  private HashMap<String, ArrayList> mChildHashMap;

  public DeviceSettingExpandableAdapter(Context context) {
    this.mContext = context;

    initList();
  }

  private void initList() {
    mParentList = new ArrayList<>();
    mParentList.add(
        new SettingParentItem(SettingParentItem.KEY_MYPHONE,
            R.drawable.icon_setting_myphone, "My Phone"));
    mParentList.add(
        new SettingParentItem(SettingParentItem.KEY_SHEALTH,
            R.drawable.icon_setting_shealth, "SHealth"));

    mChildHashMap = new HashMap<>();

    ArrayList childList = new ArrayList<>();
    childList.add(new SettingChildItem(DEVICE_DATA_TYPE.TYPE_MYPHONE_APP_USAGE,
        "앱 사용 시간", ""));
    childList.add(new SettingChildItem(DEVICE_DATA_TYPE.TYPE_MYPHONE_LOCATION,
        "위치 정보", ""));
    childList.add(new SettingChildItem(DEVICE_DATA_TYPE.TYPE_MYPHONE_ACTIVITY,
        "액티비티 정보", ""));
    childList.add(new SettingChildItem(DEVICE_DATA_TYPE.TYPE_MYPHONE_WEATHER,
        "날씨", ""));
    childList.add(new SettingChildItem(DEVICE_DATA_TYPE.TYPE_MYPHONE_EVENT,
        "스마트폰 이벤트", ""));
    mChildHashMap.put(mParentList.get(0).key + "", childList);

    childList = new ArrayList<>();
    childList.add(new SettingChildItem(DEVICE_DATA_TYPE.TYPE_SHEALTH_STEP_COUNT,
        "걸음 수", ""));
    childList.add(new SettingChildItem(DEVICE_DATA_TYPE.TYPE_SHEALTH_SLEEP,
        "수면 정보", ""));
    childList.add(new SettingChildItem(DEVICE_DATA_TYPE.TYPE_SHEALTH_HEART_RATE,
        "심박수", ""));
    mChildHashMap.put(mParentList.get(1).key + "", childList);
  }

  @Override
  public String getGroup(int groupPosition) {
    return mParentList.get(groupPosition).key + "";
  }

  @Override
  public int getGroupCount() {
    return mParentList.size();
  }

  @Override
  public long getGroupId(int groupPosition) {
    return groupPosition;
  }

  // ParentListView
  @Override
  public View getGroupView(int groupPosition, boolean isExpanded, View convertView,
      ViewGroup parent) {
    ParentItemHolder holder = null;
    View row = convertView;

    if(row == null) {
      LayoutInflater inflator = (LayoutInflater) mContext
          .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
      row = inflator.inflate(R.layout.item_view_setting_parent, null);

      holder = new ParentItemHolder();

      holder.icon = (ImageView) row.findViewById(R.id.icon);
      holder.name = (TextView) row.findViewById(R.id.name);

      row.setTag(holder);
    } else {
      holder = (ParentItemHolder) row.getTag();
    }

    final SettingParentItem item = mParentList.get(groupPosition);
    if(item != null) {
      holder.icon.setImageResource(item.resId);
      holder.name.setText(item.name);
    }

    return row;
  }

  //ChildListView
  @Override
  public SettingChildItem getChild(int groupPosition, int childPosition) {
    return this.mChildHashMap.get(this.mParentList.get(groupPosition).key + "")
        .get(childPosition);

  }

  @Override
  public int getChildrenCount(int groupPosition) {
    return this.mChildHashMap.get(this.mParentList.get(groupPosition).key + "")
        .size();

  }

  @Override
  public long getChildId(int groupPosition, int childPosition) {
    return childPosition;
  }

  @Override
  public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
      View convertView, ViewGroup parent) {
    ChildItemHolder holder = null;
    View row = convertView;

    if(row == null) {
      LayoutInflater inflator = (LayoutInflater) mContext
          .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
      row = inflator.inflate(R.layout.item_view_setting_child, null);

      holder = new ChildItemHolder();

      holder.name = (TextView) row.findViewById(R.id.name);

      row.setTag(holder);
    } else {
      holder = (ChildItemHolder) row.getTag();
    }

    SettingChildItem item = getChild(groupPosition, childPosition);

    if(item != null) {
      holder.name.setText(item.name);
    }

    return row;
  }

  @Override
  public boolean hasStableIds() { return true; }

  @Override
  public boolean isChildSelectable(int groupPosition, int childPosition) { return true; }


  class ParentItemHolder {

    ImageView icon;
    TextView name;
  }

  ;

  class ChildItemHolder {

    TextView name;
    Switch _switch;
  }

  ;
}
</pre>

블록체인의 등장과 기업 금융에 미치는 영향

|

블록체인의 등장과 기업 금융에 미치는 영향

이글은 2016.12.08 버전의 POSRI 보고서에 대한 내용의 후기 및 정리입니다. 특별히 새로울만한 내용은 없고, 그동안 많이 알려진 내용들을 다시 한 번 정리해놓은 수준의 보고서입니다. 간단히 현황 파악하기엔 나쁘지 않은 듯 싶네요.


목차

  1. 블록체인의 등장과 이해
  2. 블록체인 기술 개발 및 국내 도입 사례
  3. 기업 금융에 미치는 영향

1. 블록체인의 등장과 이해

  • 금융 시장의 새로운 패러다임 변화가 예상됨
    • 2016년 초 스위스 다보스에서 개최된 세계경제포럼(WEF, World Economic Forum)에서 산업 패러다임 변화를 예측함. 4차 산업혁명으로 촉발되는 기술, 정보, 제조, 서비스 등의 연결과 융복합을 기반으로 할 것이라고 예상.
    • 대표적인 기술로 빅데이터, 인공지능, IoT, 3D 프린터등 거론됨
  • 블록체인은 소비자와 생산자를 실질적으로 연결해주는 네트워크이자 실행 플랫폼
    • 세계경제포럼 설문 조사 결과 응답자의 50% 이상이 2025년까지 세계 GDP의 10% 이상이 블록체인 기반일 거라 답함
    • UN 미래보고서의 '미래를 바꿀 신기술 10가지'에 블록체인이 포함됨
  • 블록체인은 분산원장(Distributed Ledger)를 지칭함 - "정보는 모든 사람이 공유한다."
    • 블록체인 기반으로 비트코인이 개발되었으며, 위변조(Forgery)의 보안성이 아주 우수함
    • 블록은 거래들이 정보가 저장되는 단위이며, 이런 블록들이 연결되어 체인을 형성함
  • 블록체인을 활용하면 은행, 거래소 등의 중간 매개자가 없어지고 당사자간 거래가 가능함. 비용을 크게 절감하고 거래의 효율성, 신뢰성을 제고할 수 있음.
  • 블록체인은 지급결제, 송금, 무역금융, 금융거래 기록 관리, 보험, 부동산 등 다양한 분야에 활용 가능
  • 블록체인의 관심은 계속 확대되며, 각국 중앙은행, 금융당국, 정부 차원에서도 관심을 가지고 있음


2. 블록체인 기술 개발 및 국내 도입 사례

  • 해외 글로벌 기업, 대형 은행을 중심으로 블록체인 기반 표준 플랫폼 개발  컨소시엄이 구성되고 있음
    • 금융업계에서는 블록체인을 수용하여 'R3CEV 컨소시엄'을 구축. 블록체인 기반의 거래 플랫폼을 개발하고 은행간 거래 간소화를 위해 노력 중
    • R3CEV 컨소시엄에서 개발 중인 블록체인 기반 거래 플랫폼은 지급결제, 부동산, 회사채, 주식 등 8개 분야에 적용될 예정.
    • R3CEV 컨소시움은 씨티그룹, 모건스탠리, 골드만삭스 등 47개 이상의 글로벌 은행으로 구성됨
  • 마이크로소프트(MS)나 IBM 등도 블록체인에 관심을 갖고 자사 비즈니스 연계 및 상용화에 투자중
    • MS는 블록체인 기반 'Smart Contract(스마트 계약) 기능'을 상용화 진행 중. 해당 기능은 개발 선도 업체와 파트너십을 맺고 조건 완료시 거래가 자동으로 수행되는 기능임
    • Smart Contract은 부동산 거래, 법률 계약 등에 적용고 있으며, 거래의 실행 조건과 내용을 등록하면 그 절차가 자동으로 적용되어 거래 당사자들에게 통보되는 시스템. 기존 거래대비 절차가 간소화되고 비용도 감소할 것으로 예상
    • IBM은 인텔, 웰스파고 등 48개사가 참여한 '하이퍼 레저(Hyper Ledger)' 프로젝트를 주도적으로 진행 중. 이와 별개로 'IBM 블록체인 연구소' 개설함
  • 국내에서는 블록체인을 정부 공공 서비스 및 기업 비즈니스 쪽에 적용 시도
    • 한국예탁결제원은 2016년 7월부터 Hyper Ledger 회원으로 참여하여 활동
    • 한국거래소는 블록체인 기반 KSM(Korea Startup Market) 프로젝트를 진행 중
    • 한국금융연구원은 미래창조과학부, 금융위원회 및 산업, 학계, 연구기관 등이 함께 참여하는 공동 대응조직 구축이 필요하다고 언급
  • 국내 기업들도 블록체인 연구개발 및 투자 중
    • 삼성은 IoT와 블록체인을 접목하는 연구 진행 중
    • LG그룹은 LG CNS를 통해 2015년 국내 최초로 비상장주식 유통 플랫폼 개발 성공


3. 기업 금융에 미치는 영향

  • 블록체인은 금융 전반에 영향을 미칠 것으로 예상됨. 하지만, 개인, 기업, 공공 영역의 금융 분야에 보편화 적용 시기 예측은 어려움
  • 직접적인 효과가 예상되는 금융 업계와 공공 서비스 분야에 우선 적용되고, 그 효과가 입증되면 다른 분야에도 적용될 예정
  • 현재 블록체인의 파급력을 보면, 향후 기업들의 무역 금융, 유동성 관리, 자금 모니터링 등에 활용될 가능성 존재
  • 블록체인은 국가간 송금 및 기업 유동성 관리에도 활용할 수 있음
  • 궁극적으로 블록체인의 원천 기술은 뛰어난 보안성에 기반한 거래기록 관리이며, 이는 기업 관점에서 자금 관리 및 모니터링에 연계할 수 있음
  • 블록체인 기술은 아직 상용화 단계는 아니지만 현재 진행형이며 혁신 기술 분야로서 잠재력이 있음