버터나이프(ButterKnife)를 사용한 효율적인 추상화

대부분 프로젝트에서 소스의 양은 복잡도와 밀접한 관계가 있다. 그래서 OOP 언어에서는 공통 로직의 추상화와 모듈화로 소스의 복잡도를 줄이는 방향으로 가이드하고 있다는 것을 알 수 있다. 자바언어는 도메인에 특화된 여러 프레임웍이 제공하는 의존성 주입(DI)을 사용해서 코드의 복잡도를 줄이고 있다.

안드로이드 앱 프로젝트 또한 프레임웍 기반에서 개발한다. 그래서 안드로이드 프로젝트를 지원하는 각종 라이브러리가 존재하지만 프레임웍은 존재하지 않는 이유이기도 하다. 그래서 대부분의 의존성을 주입하는 라이브러리들은 어노테이션으로 컴파일 타임에 코드를 생성하는 형태이다. 이 형태는 런타임에 의존성 주입을 하는 형태보다 성능상의 이점이 있어서 표준처럼 사용하고 있다.

의존성 주입을 지원하는 대표 라이브러리로 버터나이프(ButterKnife)와 대거(Dagger)가 있다. 개인적으로도 버터나이프를 사용하고 있고, 안드로이드 프로젝트를 시작할 때에 처음 사용하는 라이브러리이다. 그래서 이 라이브러리를 사용해서, 비교적 좋은 형태의 추상화 예제를 살펴보겠다.

아래 예제는 안드로이드 프로젝트에서의 가장 많은 템플릿(Boilerplate) 코드로 뷰(View)에서 위젯(Widget)을 로드하는 예제이다. 아래 예제에서 액티비티(Activity)와 프레그먼트(Fragment)에서 어떻게 사용하고, 개선하는지 살펴보자.

1. 안드로이드 기본 예제

아래는 안드로이드 앱에서 많이 사용하는 액티비티와 프레그먼트의 예제이다.

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;

/**
 * Created by mcsong@gmail.com on 2016-11-14.
 */
public class MainActivity extends AppCompatActivity {

	private TextView tv01;
	private TextView tv02;
	private TextView tv03;
	private TextView tv04;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		tv01 = (TextView) findViewById(R.id.activity_main_tv_01);
		tv02 = (TextView) findViewById(R.id.activity_main_tv_02);
		tv03 = (TextView) findViewById(R.id.activity_main_tv_03);
		tv04 = (TextView) findViewById(R.id.activity_main_tv_04);
	}
}

액티비티 예제

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

/**
 * Created by mcsong@gmail.com on 2016-11-14.
 */

public class MainFragment extends Fragment {

	private TextView tv01;
	private TextView tv02;
	private TextView tv03;
	private TextView tv04;

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
		View view = inflater.inflate(R.layout.fragment_main, parent, false);

		tv01 = (TextView)view.findViewById(R.id.fragment_exview_tv_01);
		tv02 = (TextView)view.findViewById(R.id.fragment_exview_tv_02);
		tv03 = (TextView)view.findViewById(R.id.fragment_exview_tv_03);
		tv04 = (TextView)view.findViewById(R.id.fragment_exview_tv_04);

		return view;
	}

}

프레그먼트 예제

위 예제들은 findViewId() 메서드를 사용해서 화면에 배치한 위젯을 로드하는 예제이다. 이 형태로 화면에서 많은 위젯을 로드하면, 코드의 양도 그만큼 늘어난다.

2. 버터나이프(Butterknife) 예제

버터나이프를 사용해서 액티비티나 프레그먼트에서 화면을 로드하는 예제이다.

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;

import net.sjava.example.abstraction.R;

import butterknife.BindView;
import butterknife.ButterKnife;

/**
 * Created by mcsong@gmail.com on 2016-11-14.
 */

public class ButterknifeActivity extends AppCompatActivity {

	@BindView(R.id.activity_main_tv_01) TextView tv01;
	@BindView(R.id.activity_main_tv_02) TextView tv02;
	@BindView(R.id.activity_main_tv_03) TextView tv03;
	@BindView(R.id.activity_main_tv_04) TextView tv04;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		ButterKnife.bind(this);
	}
}

액티비티 예제

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import net.sjava.example.abstraction.R;

import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.Unbinder;

/**
 * Created by mcsong@gmail.com on 2016-11-14.
 */

public class ButterknifeFragment extends Fragment {
	@BindView(R.id.fragment_exview_tv_01) TextView tv01;
	@BindView(R.id.fragment_exview_tv_02) TextView tv02;
	@BindView(R.id.fragment_exview_tv_03) TextView tv03;
	@BindView(R.id.fragment_exview_tv_04) TextView tv04;

	private Unbinder unbinder;

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
		View view = inflater.inflate(R.layout.fragment_main, parent, false);
		unbinder = ButterKnife.bind(this, view);
		return view;
	}

	@Override
	public void onDestroyView() {
		super.onDestroyView();
		unbinder.unbind();
	}
}

프레그먼트 예제

버터나이프 예제들은 @BindView 어노테이션을 사용해서 화면의 위젯을 로드한다. 이 예로 위의 안드로이드 기본 예제보다 약간의 수고를 덜 수 있다.

3. 버터나이프 추상화 예제

버터나이프를 사용해서 액티비티와 프레그먼트에서 추상화하는 예제와 추상화한 액티비티와 프레그먼트를 사용하는 예를 살펴보자.

3.1 추상화 예제

import android.os.Bundle;
import android.support.annotation.LayoutRes;
import android.support.v7.app.AppCompatActivity;

import butterknife.ButterKnife;

/**
 * Created by mcsong@gmail.com on 2016-11-14.
 */
public abstract class AbsBaseActivity extends AppCompatActivity {
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		super.setContentView(getLayoutId());
		ButterKnife.bind(this);
	}

	@LayoutRes
	public abstract int getLayoutId();
}

추상화 액티비티 예제

이 추상화한 클래스는 abstract 클래스로 이 클래스를 상속하는 클래스는 getLayoutId() 메서드를 구현하게 만든다. 이 메서드에서는 액티비티의 화면인 xml 파일의 id를 반환하도록 한다.


import android.os.Bundle;
import android.support.annotation.LayoutRes;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import butterknife.ButterKnife;
import butterknife.Unbinder;

/**
 * Created by mcsong@gmail.com on 2016-11-14.
 */

public abstract class AbsBaseFragment extends Fragment {

	Unbinder mUnbinder;

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
		View view = inflater.inflate(getLayoutId(), container, false);
		mUnbinder = ButterKnife.bind(this, view);
		return view;
	}

	@LayoutRes
	public abstract int getLayoutId();

	@Override
	public void onDestroyView() {
		super.onDestroyView();
		mUnbinder.unbind();
	}
}

추상화 프레그먼트 예제

이 추상화한 프레그먼트는 추상화한 액티비티의 getLayoutId()와 더불어, onDestroyView() 메서드에서 버터나이프로 읽어들인 뷰를 제거하는 메서드를 추가했다.

3.2 사용 예제

import android.os.Bundle;
import android.widget.TextView;
import net.sjava.example.abstraction.R;

import butterknife.BindView;

/**
 * Created by mcsong@gmail.com on 2016-11-14.
 */

public class ButterknifeMainActivity extends AbsBaseActivity {

	@BindView(R.id.activity_main_tv_01) TextView tv01;
	@BindView(R.id.activity_main_tv_02) TextView tv02;
	@BindView(R.id.activity_main_tv_03) TextView tv03;
	@BindView(R.id.activity_main_tv_04) TextView tv04;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
	}

	@Override
	public int getLayoutId() {
		return R.layout.activity_main;
	}
}

액티비티 예제

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import net.sjava.example.abstraction.R;

import butterknife.BindView;

/**
 * Created by mcsong@gmail.com on 2016-11-14.
 */

public class ButterknifeMainFragment extends Fragment {
	@BindView(R.id.fragment_exview_tv_01) TextView tv01;
	@BindView(R.id.fragment_exview_tv_02) TextView tv02;
	@BindView(R.id.fragment_exview_tv_03) TextView tv03;
	@BindView(R.id.fragment_exview_tv_04) TextView tv04;

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
		return super.onCreateView(inflater, parent, savedInstanceState);
	}
}

프레그먼트 예제

* 예제 코드 : https://github.com/mcsong/AbstractionExample
이 글에서 버터나이프를 사용해서 비교적 효율적으로 추상화해서 사용하는 방법을 살펴봤다.

안드로이드 프로젝트 빌드 유지(CI)를 위한 팀시티(TeamCity) 설정(2/2)

이 글은 안드로이드 프로젝트의 빌드를 유지하는 세 개의 글 중에 세 번째이다.

이 글은 팀시티가 저장소의 소스를 내려받고, 안드로이드 프로젝트 빌드를 완료하면, 등록된 계정에게 알려주도록 알람을 설정하는 내용이다. 이 글에서는 이메일과 메신저(Jabber 기반) 계정으로 알림을 송/수신 하는 설정, 빌드 파일(apk)을 다운로드 받게 설정, 그리고 수신하는 내용(템플릿)을 수정하는 방법에 대해서 살펴보자.

1. 알람 발송 계정 설정

구글의 서비스를 사용한다고 가정하고, 구글의 이메일과 메신저로 알림을 받도록 설정해보자.
구글 메일과 메신저 서비스(GTalk)는 아래 링크의 설정을 참고하면 된다.
https://confluence.jetbrains.com/display/TCD10/Setting+up+Google+Mail+and+Google+Talk+as+Notification+Servers
설정 메뉴는 Administration > Server Administration > Email, Jabber Notifier 메뉴에서 확인할 수 있다.
– 이메일 설정 화면
setting_noti_jabber

– 메신저(Jabber) 설정 화면
setting_noti_gmail

이제 알람을 발송할 계정 설정을 완료했다.

2. 알람 수신 계정 설정

다음으로, 알람을 받는 계정의 Email과 Jabber 계정을 설정해 보자. Administration > Users 에서 알람을 받을 계정을 선택한다. 우측의 “Watched Builds and Notifications” 에 Email과 Jabber Notifier를 선택한다. 그리고, Add New Rule을 선택하면 아래와 같은 화면을 볼 수 있다. 이 화면에서 알람을 받을 프로젝트의 빌드를 선택하고, 빌드의 상태인 “Build fails”과 “Build is successful” 을 선택하고 저장한다.
setting_jabber setting_email

이제 Email과 Jabber로 알람을 받을 준비가 완료됐다.

3. 빌드 파일 설정

빌드한 apk 파일을 다운로드 할 수 있게 산출물(Artifact)로 설정해야 한다. 아래 설정은 Projects > Build > General Settings에 아래와 같은 설정이 있고, Artifact paths에 apk 파일을 설정한다. 이제 빌드한 파일을 배포할 준비가 되었다.
apk_path_settings

4. 수신 내용 변경

이제 위에서 설정한 이메일과 메신저를 사용해서 소스 빌드에 대한 이벤트(성공/실패)를 수신 받게 된다. 기본적으로 수신받는 화면은 아래와 같다.
jabber_notification_alert email_notification_alert

위에서 보는 이메일이나 메신저의 알림 내용을 추가해서 조금 더 편리하게 알림을 사용할 수 있다. 아래에서 빌드된 apk 파일을 내려받기 쉽게 링크를 추가해 보자. 팀시티의 알림 템플릿은 아래의 위치에서 확인할 수 있고, 알림의 형태에 따라서 다음과 같이 나눠져 있다.

C:\TeamCityData\config\_notifications\email\build_successful.ftl
C:\TeamCityData\config\_notifications\jabber\build_successful.ftl

위에서 email 폴더에 있는 build_successful.ftl 파일을 수정해 보자. 우선 프로젝트의 빌드 파일의 위치를 확인해 보면 다음과 같다.

<#-- MODIFICATION START -->
  <#if buildType.externalId = "Nbarcode">    
    Click here to download APK.
      
  </#if>
<#-- MODIFICATION END -->

팀시티의 10.x 버전의 다운로드 위치는 위의 URI 형태를 띄고 있다. 이제 이 내용을 템플릿 파일의 적절한 위치에 복사를 하고 저장한다.

  <@common.build_comment build/>
  
  <#-- MODIFICATION START -->
  <#if buildType.externalId = "Nbarcode">
    
    Click here to download APK.
    
  </#if>
<#-- MODIFICATION END -->
  
  <@common.build_changes var.changesBean/>

이제 저장하고 소스를 커밋하면 아래와 같이 이메일과 메신저로 알람을 받게 된다.
email_notification_alert jabber_notification_alert

* Reference
– TeamCity에서 안드로이드앱 배포하기

안드로이드 프로젝트 빌드 유지(CI)를 위한 팀시티(TeamCity) 설정(1/2)

이 글은 안드로이드 프로젝트의 빌드를 유지하는 세 개의 글 중에 두 번째이다.

안드로이드 프로젝트를 팀시티에서 빌드하는데 필요한 설정을 살펴보자. 이 글에서는 프로젝트 연결, 빌드 설정, 트리거(Triggers) 설정, 그리고 빌드 확인의 과정으로 살펴보겠다.

1. 프로젝트 연결

프로젝트 연결은 3가지 형태를 지원한다. 저장소 연결(Repository Url), 깃헙(Github), 그리고 비트버킷(Bitbucket)을 지원한다. 여기에서는 저장소 연결을 사용해서 프로젝트에 연결한다.
project_connection_01 project_connection_02
위 우측 화면은 예제로 사용하는 프로젝트의 소스 저장소인 비트버킷을 연결하는 화면이다. 이곳에서 지원하는 연결 프로토콜로 http(s)://, svn://, 그리고 git:// 를 지원하는 것을 알 수 있다. 다음으로, 프로젝트의 “Version Control Settings”에서 소스의 “Check out”할 위치를 변경한다.
version_control_settings
여기에서 사용하는 프로젝트 이름은 Nbarcode이고, 프로젝트 소스의 Check out 위치는 C:\TeamCityProjects\Nbarcode 를 사용한다.

2. 빌드 설정

이제 소스를 빌드하는데 필요한 설정을 알아 보자. 빌드 설정은 “Build Steps” 메뉴를 선택해서 시작할 수 있다. “Auto-detect build steps” 버튼을 클릭해서 빌드 단계를 인식시켜 보자. 아래에서는 Clean, Release, 그리고 Rename 단계로 살펴보자.

2.1 Clean 단계

이 단계는 프로젝트 빌드 이전 단계로, 이전에 빌드에 사용한 각종 파일들을 삭제한다. 이 단계에 필요한 설정은 아래와 같다.

Runner Type : Gradle
Step Name : Clean
Gradle tasks: clean
Working directory: C:\TeamCityProjects\Nbarcode
Gradle Wrapper: 체크
JDK : 사용하는 자바 버전으로 설정한다.

build_step_02

2.2 Release 단계

이 단계에서 프로젝트를 빌드한다. 이전에 빌드에 사용한 각종 파일들을 삭제한다. “Build Configuration Settins” > “Build Step: Gradle” > “+ Add build step”을 클릭해서 빌드 단계를 추가한다. 이 단계에 필요한 설정은 아래와 같다.

Runner type : Gradle
Step Name : Release
Gradle tasks: assembleRelease
Working directory: C:\TeamCityProjects\Nbarcode
Gradle Wrapper: 체크
JDK : 사용하는 자바 버전으로 설정한다.

이제 우측 상단에 있는 “Run” 버튼을 클릭해서 위 단계를 시작해 보자. 이제 아래와 같이 apk 파일이 빌드된 것을 확인할 수 있다.
build_apk_result

2.3 Rename 단계

이 단계에서는 apk 파일 이름을 변경해서, 버전을 명확하게 알 수 있게 파일 이름을 변경하는 단계이다. 이 단계에 필요한 설정은 아래와 같다.

Runner type : Command Line
Step Name : Assemble Release
Working directory: C:\TeamCityProjects\Nbarcode\app\build\outputs\apk
Run : Executable with parameters
Command executable: rename
Command parameters: app-release.apk app-release-%build.counter%.apk

build_step_03

3. 트리거 설정

팀시티로 지속 빌드를 하는데 필요한 설정으로 VCS에 소스가 커밋되면, 빌드를 하는 과정이 필요하다. “Build Configuration Settings” > “Triggers” 메뉴에서 소스 커밋의 트리거 설정을 변경할 수 있다.
trigger
위 이미지는 기본 설정의 트리거 화면이다. 이 화면을 보면 소스코드 코밋이 확인되면 빌드를 시작한다는 것을 알 수 있다. 추가로 Quiet Period 설정에서 일정 시간 뒤에 빌드하도록 설정할 수 있다.

4. 빌드 확인

이제 소스를 커밋해서 빌드가 실행되는 것을 확인해 보자. 소스를 커밋하면 팀시티가 소스가 변경된 것을 확인하고, 소스를 받고, 빌드(위 2.1, 2.2, 2.3의 과정)를 한다. 이 과정이 완료되면 아래 화면에서 보는 apk 파일을 확인할 수 있다.
build_step_04

안드로이드 프로젝트를 지속적으로 빌드할 수 있는 환경을 구성해 봤다.

* Reference
TeamCity에서 안드로이드앱 배포하기

안드로이드 프로젝트 빌드 유지(CI)를 위한 팀시티(TeamCity) 설치

이 글은 안드로이드 프로젝트의 빌드를 유지하는 세 개의 글 중에 첫 번째이다.

팀시티(TeamCity)는 JetBrain에서 개발한 지속 통합(CI, Continuous Integration)을 지원하는 툴이다. 지속 통합 툴로 유명한 것으로는 Jenkins, Travis, 그리고 Cruise Control등이 있다. 지속 통합에 대한 개념은 아래의 링크에서 확인해 보자.
https://en.wikipedia.org/wiki/Continuous_integration
http://happystory.tistory.com/89

팀시티는 기본 무료로 사용할 수 있지만, 프로젝트 개수와 볼륨의 크기에 따라 상업용으로 구매해야 할 수도 있다. 라이선스에 대한 내용은 라이선스 페이지 에서 확인할 수 있고, 현재는 아래의 사용까지는 무료로 사용할 수 있다.

20 build configurations
3 build agents

이 정도면 작은 기업에서는 구입하지 않아도 충분히 사용할 수 있을 것이다. 이제 팀시티를 설치하고, 소스 저장소에 있는 안드로이드 앱을 연결해서 지속 통합을 하는데 필요한 환경을 설치하는 방법을 살펴보자. 아래 내용은 안드로이드 개발환경이 없는 윈도 서버(2012)에 설치하는 것을 가정한다.

1. 자바, 안드로이드 SDK 설치

팀시티로 안드로이드 앱을 빌드하는데는 자바와 안드로이드 SDK가 필요하고, 자바와 안드로이드 SDK를 설치하는 것을 살펴보자.

1.1 자바 설치

자바는 오라클 자바 사이트에서 내려받아 설치한다. 설치한 후에 “내 PC > 속성(R) > 고급 시스템 설정 > 고급 > 환경 변수 > 시스템 변수”에 JAVA_HOME을 추가한다.

1.2 안드로이드 SDK 설치

안드로이드 SDK는 안드로이드 앱을 개발/빌드/실행 하는데 필요한 라이브러리, 툴, 에뮬레이터 환경을 가지고 있다. 이 툴은 개발툴 다운로드 페이지에서 내려받을 수 있고, 이 페이지 아래에 “Get just the command line tools”의 하위에 SDK만을 별로도 다운로드 받을 수 있게 분리되어 있다.

여기에서는 윈도 2012 서버에 android-sdk_r24.4.1-windows.zip 파일을 다운로드 받아서 C:\Dev\android-sdk에 압축을 풀어서 저장하고 사용한다. SDK가 준비되면, “내 PC > 속성(R) > 고급 시스템 설정 > 고급 > 환경 변수 > 시스템 변수”에 ANDROID_HOME 변수를 추가하고 SDK 위치를 설정한다. 아래 이미지에 자바와 안드로이드 SDK가 설정된 것을 볼 수 있다.

system_variable_settings

SDK 설치를 완료하면, SDK 매니저(Manager)를 실행해서 필요한 툴, 플랫폼, 저장소 소스등을 내려받는다. 아래 이미지는 SDK 매니저를 실행해서 필요한 툴, 플랫폼(화면은 없지만, 앱에서 사용하는 플랫폼은 선택해야 한다), 그리고 라이브러리 및 툴등을 선택한 화면이다.

sdk_image_01

위 이미지는 필요한 빌드툴을 선택해서 설치하는 화면이다.

sdk_image_02

위 이미지는 필요한 라이브러리등을 선택한 화면이다.

SDK 매니저를 사용해서 필요한 것들의 설치를 완료하면 팀시티를 사용해서 빌드할 수 있는 기본 환경 구성을 완료한 것이다.

2. 팀시티(TeamCity) 설치

팀시티는 팀시티 웹 사이트에서 다운로드 메뉴를 클릭해서 내려받고 설치한다. 여기에서 사용하는 버전 정보는 다음과 같다.

Version : 10.0.2
Build: 42234
Released: September 19, 2016

이제 팀시티를 설치해 보자. 아래에 설치하면서 필요한 설정이 있는 부분들을 기준으로 살펴본다.

2.1 다운로드 받은 파일을 실행하고, 설치 위치를 확인한다.

아래의 왼쪽 이미지가 설치를 시작하는 화면이고, 오른쪽 화면이 설치 위치(C:\TeamCity)를 설정하는 화면이다.
teamcity_install_01 teamcity_install_02

2.2 웹 서비스를 위한 서버의 포트를 설정하고, 각종 설정을 확인한다.

왼쪽 화면이 팀시티가 실행하면서 접속하는 웹 서비스 포트를 설정하는 화면이고, 오른쪽은 설정된 값들을 확인하고 추가할 수 있는 화면이다.

teamcity_install_03 teamcity_install_04

2.3 팀시티의 시작 계정을 설정한다.

이 단계에서는 팀시티를 구성하는 서버/에이전트를 런칭하는 계정을 설정하고, 서비스를 시작하도록 설정한다. 아래 화면을 보면 시스템 계정으로 시작시키는 것을 알 수 있다. 시스템 계정으로 시작시키기에 위에서 설정한 JAVA_HOME과 ANDROID_HOME을 내 계정이 아닌 시스템으로 설정한 이유이기도 하다.

teamcity_install_05 teamcity_install_06 teamcity_install_07

2.4 팀시티 웹 서비스에 연결해서 필요한 설정을 추가한다.

teamcity_install_08 teamcity_install_09

위 화면은 팀시티 설치를 완료하고, 팀시티 웹서비스에 연결해서 필요한 설정을 시작하는 화면이다. 오른쪽 화면에서 팀시티의 데이터를 저장하는 위치를 설정한다. 기본으로 C:\ProgramData 폴더가 지정되어 있는데, 이 폴더는 윈도에서 앱들의 데이터 저장 위치로, 기본 설정이 숨김폴더이다. 그래서 저장하는 데이터를 쉽게 확인할 수 있게 저장 위치를 C:\TeamCityData로 변경한다.
teamcity_install_10

위 화면은 팀시티가 사용할 데이터베이스를 선택하는 화면이다. 데이터베이스로 Internal(HSQLDB), PostgreSQL, MySQL, Oracle, MS SQL Server를 지원한다. 여기에서는 프로젝트가 많지 않아서, Internal(HSQLDB)을 선택한다.

teamcity_install_11 teamcity_install_12

다음은 라이선스 동의 화면으로, “Accept license agreement”는 선택해야 하고, “Send anonymous usage statistics to TeamCity development team”은 옵션이라서, 선택을 해제한다. 그리고 우측의 화면에서 관리자 계정을 생성한다.

teamcity_install_13

이 화면은 관리자 계정을 생성한 후에, 로그인된 화면이다.

이제 팀시티 설치가 마무리 되었다. 다음 포스트에서 팀시티로 안드로이드 앱을 지속적으로 빌드하는데 필요한 설정을 살펴보겠다.

자바로 다중 작업(Multi Task)을 효과적으로 처리하는 방법들

자바로 다중 작업(Multi Task)을 효과적으로 처리하는 방법을 살펴보자. 자바에서 다중 작업을 처리하는데 기본으로 Thread 클래스와 Runnable 인터페이스를 사용한다. 하지만 이 Thread나 Runnable의 경우에는 병행처리를 하는 데 필요한 태스크 관리와 태스크의 분리(Divide)와 처리를 직접 구현해야 하는 단점이 있다. 그래서 자바 java.util.concurrent 패키지에는 다중 작업을 효과적으로 처리할 방법을 여러 가지 제공한다.

1. Executor 프레임웍

이 프레임웍은 처리해야 할 작업을 프로듀서-컨슈머 패턴의 큐에 저장하고, N개의 스레드를 사용해서 병행으로 처리하는 프레임웍이다. 일반적으로 프로듀스는 큐에 작업을 추가하고, 스레드 풀(컨슈머 스레드)에서 작업을 처리한다.

2. 카운트다운래치(CountDownLatch) & 사이클릭배리어(CyclicBarrier)

카운트다운래치(CountDownLatch)와 사이클릭배리어(CyclicBarrier)는 둘 다 큰 작업을 작은 작업으로 분리(개별 구현)하고, 이 분리한 작업의 실행을 완료한 뒤에 흐름을 진행하도록 흐름을 동기화시키는 클래스이다.
그림에서 보듯이, 카운트다운래치는 메인 스레드에서 작업을 처리하는 실행하는 스레드를 만들고 실행한다. 그리고 개별 스레드가 카운트다운래치의 값을 하나씩 제거하면, 메인 스레드가 시그널을 받아서 흐름을 진행하는 형태이다.

사이클릭배리어는 두 스레드 사이에 로직을 동기화하는 데 사용한다. 그래서 배리어를 사용해서 N개의 스레드 실행 흐름을 중단하다가, 배리어액션(단계를 지날 수 있는 과정)을 완료하면 N개의 스레드 실행 결과를 병합할 수 있는 형태이다. 그리고, 두 개의 스레드가 데이터를 교환하는데 사용하는 Exchanger는 사이클릭배리어의 특정 예이다.

3. ForkJoin 프레임웍

포크조인(ForkJoin) 프레임웍은 작업을 분리(분리하기 쉽게 선언)하고, 실행하고, 그리고 결과를 병합하기 쉽게 도와준다. 포크(Fork)는 작업을 분리하고, 조인(Join)은 분리한 작업이 완료한 뒤에 결과를 병합하는 과정이다. 이 프레임웍에 대한 자세한 내용은 http://www.oracle.com/technetwork/articles/java/fork-join-422606.html 에서 살펴보자.

이 그림에서 보듯이, 작업은 내부에서 fork()로 작업을 분리하고, join()으로 태스크를 병합한다. 그리고 이 작업은 재귀(Recursive)로 작업을 분리하는 형태라서 RecursiveTask 클래스를 상속해서 구현한다.

이제 어느 상황에서 이런 다중 작업을 처리하는 것이 좋은지 살펴보자.

1. 작업이 분리되지 않는 경우 “Executor”를 사용하자.

– 가장 많이 사용하는 형태이다.

2. 작업이 분리되는 경우에는 “카운트다운래치” 또는 “포크조인”을 사용하자.

– 많은 양의 데이터 정렬, 최대, 최소값 찾는 경우.
– 많은 양의 데이터를 합산하는 경우.
– 네트웍을 스캔하는 경우.
– 기타
개인적으로는 카운트다운래치를 많이 사용했지만, 작업 분리는 동일하지만 스레드 관리가 불편하다. 그래서 포크조인을 사용하는 것이 좋겠다.

3. 작업이 분리될 수도 있고 아닐 수도 있는 단계가 있는 작업은 “사이클릭배리어”를 사용하자.

– 예를 들어서 게임의 단계를 배리어도 두고 여러 스레드의 작업을 제어할 수 있다. 1단계를 지나면서, 랭킹정보, 개인정보, 게임정보등등을 가져오는 스레드를 실행하고, 필요한 정보를 다 가져온 뒤에 화면을 넘기는 예로 사용할 수 있겠다.