Skip to content

Most visited

Recently visited

navigation

주석으로 코드 검사 개선

Lint와 같은 코드 검사 도구를 사용하면 문제점을 찾아내고 코드를 개선하는 데는 도움이 될 수 있지만 검사 도구가 유추할 수 있는 범위도 한정되어 있습니다. 예를 들어, Android 리소스 ID는 int를 사용해 문자열, 그래픽, 색상 및 기타 리소스 유형을 식별하므로, 색을 지정했어야 하는 곳에서 문자열 리소스를 지정했더라도 검사 도구가 이 사실을 알려주지는 못합니다. 즉, 코드 검사 기능을 사용하더라도 앱이 잘못 렌더링되거나 아예 실행조차 되지 않을 수도 있다는 얘기입니다.

주석을 사용하여 Lint와 같은 코드 검사 도구에 힌트를 줌으로써 이처럼 미묘한 코드 문제를 더 잘 탐지하도록 할 수 있습니다. 주석은 변수, 매개변수 및 반환 값에 추가하여 메서드 반환 값, 전달된 매개변수, 지역 변수 및 필드를 검사하는 메타데이터 태그로 추가되었습니다. 주석을 코드 검사 도구와 함께 사용하면 null 포인터 예외 및 리소스 유형 충돌과 같은 문제를 탐지하는 데 도움이 될 수 있습니다.

Android는 Annotations 지원 라이브러리를 통해 다양한 주석을 지원합니다. android.support.annotation 패키지를 통해 라이브러리에 액세스할 수 있습니다.

프로젝트에 주석 추가

프로젝트에서 주석을 사용하려면 라이브러리나 앱에 support-annotations 종속성을 추가하세요. 그러면 추가하는 주석에 대해 코드 검사 또는 lint 작업을 실행할 때 검사가 이루어집니다.

지원 주석 라이브러리 종속성 추가

Support Annotations 라이브러리는 Android 지원 저장소의 일부입니다. 프로젝트에 주석을 추가하려면 지원 리포지토리를 다운로드하고 build.gradle 파일에 support-annotations 종속성을 추가해야 합니다.

  1. 툴바에서 SDK Manager를 클릭하여 SDK Manager를 엽니다. 또는 Tools > Android > SDK Manager를 선택해서 열어도 됩니다.
  2. SDK Tools 탭을 클릭합니다.
  3. Support Repository를 펼치고 Android Support Repository 확인란을 선택합니다.
  4. OK를 클릭합니다.
  5. 마법사를 계속 진행하여 패키지를 설치합니다.
  6. build.gradle 파일의 dependencies 블록에 다음 줄을 넣어 프로젝트에 support-annotations 종속성을 추가합니다.
     dependencies { compile 'com.android.support:support-annotations:24.2.0' } 
    자신이 다운로드하는 라이브러리 버전이 더 높은 버전일 수도 있으므로 여기서 지정하는 값이 3단계의 버전과 일치하도록 하세요.
  7. 툴바나 화면에 나타나는 동기화 알림 메시지에서 Sync Now를 클릭합니다.

자체 라이브러리 모듈에서 주석을 사용하는 경우에는 주석이 XML 형식으로 된 AAR(Android Archive) 아티팩트의 일부로 annotations.zip 파일에 포함됩니다. support-annotations 종속성을 추가하더라도 라이브러리의 다운스트림 사용자에 대해서는 종속성이 적용되지 않습니다.

Gradle용 Android 플러그인(com.android.application 또는 com.android.library)을 사용하지 않지만 대신에 Gradle 자바 플러그인을 사용하는 Gradle 모듈에서 주석을 사용하려는 경우, JCenter 자바 리포지토리에서 Android 지원 라이브러리를 사용할 수 없으므로 다음과 같이 SDK 리포지토리를 명시적으로 포함해야 합니다.

repositories {
   jcenter()
   maven { url '<your-SDK-path>/extras/android/m2repository' }
}

참고: appcompat 라이브러리를 사용 중이라면 support-annotations 종속성을 추가할 필요가 없습니다. appcompat 라이브러리는 이미 주석 라이브러리에 의존하므로 주석에 액세스할 수 있습니다.

지원 리포지토리에 포함되어 있는 주석의 전체 목록을 보려면 Support Annotations 라이브러리 참조를 확인하거나 자동 완성 기능을 사용해 import android.support.annotation. 명령문에 사용 가능한 옵션을 표시하면 됩니다.

코드 검사 실행

주석 유효성 검사 기능과 자동 Lint 검사 기능이 있는 Android Studio에서 코드 검사를 시작하려면 메뉴 모음에서 Analyze > Inspect Code를 선택하세요. Android Studio에서는 개발자가 작성한 코드가 주석과 충돌할 가능성이 있는 문제를 플래그로 지정해 표시하고 가능한 해결책을 제안할 목적으로 충돌 메시지를 표시해 줍니다.

명령줄을 사용해 lint 작업을 실행함으로써 주석을 강제 적용할 수도 있습니다. 이는 연속 통합 서버와의 플래그 지정 문제에 유용할 수도 있지만, lint 작업은 nullness 주석을 강제 적용하지 못한다는 점에 유의하세요(Android Studio만이 강제 적용함). Lint 검사의 사용과 실행에 대한 자세한 정보는 Lint로 코드 개선을 참조하세요.

참고로, 주석 충돌로 인해 경고가 생성되더라도 앱 컴파일을 막지는 않습니다.

Nullness 주석

주어진 변수, 매개변수 또는 반환 값의 nullness를 확인하려면 @Nullable@NonNull 주석을 추가하세요. @Nullable 주석은 null이 될 수 있는 변수, 매개변수 또는 반환 값을 나타내는 반면, @NonNull은 null이 될 수 없는 변수, 매개변수 또는 반환 값을 나타냅니다.

예를 들어, null 값을 포함하는 지역 변수가 어떤 메서드에 매개변수로서 전달되고 이 메서드의 @NonNull 주석이 이 매개변수에 연결되어 있는 경우 코드를 빌드하면 null이 아닌 충돌을 나타내는 경고가 생성됩니다. 한편, 결과가 null인지 먼저 확인하지 않고 @Nullable로 표시된 메서드의 결과를 참조하려고 하면 nullness 경고가 생성됩니다. 메서드를 사용할 때마다 명시적으로 null 여부를 확인해야 할 경우에는 메서드의 반환 값에 대해 @Nullable만 사용해야 합니다.

다음 예에서는 @NonNull 주석을 contextattrs 매개변수에 연결하여 전달된 매개변수 값이 null이 아님을 확인합니다. 또한, 다음과 같이 onCreateView() 메서드 자체가 null을 반환하지 않는다는 점도 확인합니다.

import android.support.annotation.NonNull;
...

    /** Add support for inflating the <fragment> tag. **/
    @NonNull
    @Override
    public View onCreateView(String name, @NonNull Context context,
      @NonNull AttributeSet attrs) {
      ...
      }
...

Null 허용 여부 분석

Android Studio는 Null 허용 여부 분석을 실행하여 코드에서 nullness 주석을 자동으로 유추하고 삽입하는 기능을 지원합니다. Null 허용 여부 분석에서는 다음 항목들을 찾아내기 위해 코드의 메서드 계층 구조 전반에 걸쳐 계약을 검색합니다.

이 분석에서는 발견된 위치에 적절한 null 주석을 자동으로 삽입합니다.

Android Studio에서 null 허용 여부 분석을 실행하려면 Analyze > Infer Nullity를 선택하세요. Android Studio가 코드에서 검색된 위치에 Android @Nullable@NonNull 주석을 삽입합니다. Null 분석을 실행한 후에는 삽입된 주석을 확인하는 것이 좋습니다.

참고: Nullness 주석을 추가할 때 자동 완성 기능을 통해 Android null 주석 대신에 IntelliJ @Nullable@NotNull 주석이 추천될 수 있으며 그에 상응하는 라이브러리를 자동으로 가져올 수 있습니다. 하지만 Android Studio Lint 검사기는 Android null 주석만 찾습니다. 주석을 확인할 때는 Lint 검사기가 코드 검사 중에 올바로 알려줄 수 있도록 프로젝트에서 Android null 주석을 사용하는지 확인하세요.

리소스 주석

드로어블문자열 리소스와 같은 리소스에 대한 Android 참조는 정수로 전달되기 때문에 리소스 유형의 유효성 검사가 유용할 수 있습니다. 매개변수가 특정 유형의 리소스를 참조할 것으로 예상하는 코드(예: 드로어블)는 예상되는 참조 형식인 int로 전달될 수 있지만, 실제로는 R.string 리소스 등의 다른 리소스 유형을 참조할 수 있습니다.

예를 들어, 아래에 표시된 것처럼 리소스 매개변수에 R.string 참조가 포함되어 있는지 검사하려면 @StringRes 주석을 추가하세요.

public abstract void setTitle(@StringRes int resId) { … }

코드 검사 중에 매개변수에서 R.string 참조가 전달되지 않으면 주석에서 경고가 생성됩니다.

같은 주석 형식을 사용하여 @DrawableRes, @DimenRes, @ColorRes@InterpolatorRes처럼 다른 리소스 유형에 대한 주석을 추가하고 코드 검사 중에 실행할 수 있습니다. 매개변수가 여러 리소스 유형을 지원할 경우 주어진 매개변수에 이들 주석 중 두 개 이상을 배치할 수 있습니다. 주석이 지정된 매개변수가 어떤 유형의 R 리소스든 될 수 있음을 나타내려면 @AnyRes를 사용하세요.

@ColorRes를 사용하여 매개변수가 색상 리소스여야 한다는 점을 지정할 수 있더라도, (RRGGBB 또는 AARRGGBB 형식으로 된) 색상 정수는 색상 리소스로 인식되지 않습니다. 대신에 @ColorInt 주석을 사용하여 매개변수가 색상 정수여야 함을 나타냅니다. 빌드 도구는 색상 정수가 아니라 android.R.color.black과 같은 색상 리소스 ID를 주석이 지정된 메서드로 전달하는 잘못된 코드를 플래그 지정합니다.

스레드 주석

스레드 주석은 메서드가 특정 유형의 스레드로부터 호출되는지 확인합니다. 다음과 같은 스레드 주석이 지원됩니다.

참고: 빌드 도구는 @MainThread@UiThread 주석을 교환 가능한 것으로 취급하므로, @MainThread 메서드에서 @UiThread 메서드를 호출할 수 있고 그 반대도 마찬가지입니다. 하지만 여러 가지 다른 스레드에 여러 가지 뷰가 있는 시스템 앱의 경우 UI 스레드가 주 스레드와 다를 가능성이 있습니다. 따라서 @UiThread가 있는 앱의 뷰 계층 구조와 연결된 메서드에 주석을 달고 @MainThread가 있는 앱의 수명 주기와 연결된 메서드에만 주석을 달아야 합니다.

클래스에 있는 모든 메서드의 스레딩 요구사항이 동일할 경우, 클래스에 단일 스레드 주석을 추가하여 클래스의 모든 메서드가 같은 유형의 스레드로부터 호출되는지 확인할 수 있습니다.

스레드 주석의 공통적인 용도는 AsyncTask 클래스에서 메서드의 재정의를 확인하는 것입니다. 이 클래스가 백그라운드 작업을 수행하고 UI 스레드에 대한 결과만 게시하기 때문입니다.

값 제약 조건 주석

전달된 매개변수의 값에 대한 유효성 검사를 수행하려면 @IntRange, @FloatRange@Size 주석을 사용하세요. @IntRange@FloatRange는 둘 다 사용자가 잘못된 범위를 지정할 가능성이 있는 매개변수에 적용할 때 가장 유용합니다.

@IntRange 주석은 integer 또는 long 형식의 매개변수 값이 지정 범위 내에 있는지 확인합니다. 다음은 alpha 매개변수가 0~255의 정수 값을 포함하도록 하는 예시입니다.

public void setAlpha(@IntRange(from=0,to=255) int alpha) { … }

@FloatRange 주석은 float 또는 double 형식의 매개변수 값이 부동 소수점 값의 지정 범위 내에 있는지 검사합니다. 다음은 alpha 매개변수가 0.0~1.0의 부동 소수점 값을 포함하도록 하는 예시입니다.

public void setAlpha(@FloatRange(from=0.0, to=1.0) float alpha) {...}

@Size 주석은 컬렉션 또는 배열의 크기는 물론이고, 문자열의 길이도 검사합니다. @Size 주석을 사용하여 다음과 같은 특성을 확인할 수 있습니다.

예를 들어, @Size(min=1)은 컬렉션이 비어 있지 않은지 검사하고 @Size(3)은 배열에 정확히 3개의 값이 있는지 확인합니다. 다음은 location 배열에 1개 이상의 요소가 있도록 하는 예시입니다.

int[] location = new int[3];
button.getLocationOnScreen(@Size(min=1) location);

권한 주석

메서드 호출자의 권한에 대한 유효성을 검사하려면 @RequiresPermission 주석을 사용하세요. 유효한 권한의 목록에서 어떤 한 가지 권한이 있는지 검사하려면 anyOf 속성을 사용하세요. 권한 집합이 있는지 검사하려면 allOf 속성을 사용하세요. 다음 예시에서는 setWallpaper() 메서드에 주석을 달아 메서드 호출자에게 permission.SET_WALLPAPERS 권한이 있는지 확인합니다.

@RequiresPermission(Manifest.permission.SET_WALLPAPER)
public abstract void setWallpaper(Bitmap bitmap) throws IOException;

다음 예시에서는 copyFile() 메서드의 호출자에게 외부 리포지토리에 대한 읽기 권한과 쓰기 권한이 모두 있어야 합니다.

@RequiresPermission(allOf = {
    Manifest.permission.READ_EXTERNAL_STORAGE,
    Manifest.permission.WRITE_EXTERNAL_STORAGE})
public static final void copyFile(String dest, String source) {
    ...
}

인텐트에 대한 권한의 경우 인텐트 작업 이름을 정의하는 문자열 필드에 권한 요건을 지정합니다.

@RequiresPermission(android.Manifest.permission.BLUETOOTH)
public static final String ACTION_REQUEST_DISCOVERABLE =
            "android.bluetooth.adapter.action.REQUEST_DISCOVERABLE";

읽기 및 쓰기 액세스를 위해 별개의 권한이 필요한 콘텐츠 공급자에 대한 권한의 경우 다음과 같이 @RequiresPermission.Read 또는 @RequiresPermission.Write 주석에 각각의 권한 요구사항을 래핑하세요.

@RequiresPermission.Read(@RequiresPermission(READ_HISTORY_BOOKMARKS))
@RequiresPermission.Write(@RequiresPermission(WRITE_HISTORY_BOOKMARKS))
public static final Uri BOOKMARKS_URI = Uri.parse("content://browser/bookmarks");

간접 권한

권한이 메서드의 매개변수에 제공되는 특정 값에 종속될 때는 특정 권한을 나열하지 않고 매개변수 자체에서 @RequiresPermission을 사용하세요. 예를 들어, startActivity(Intent) 메서드는 메서드로 전달되는 인텐트에 대해 다음과 같이 간접 권한을 사용합니다.

public abstract void startActivity(@RequiresPermission Intent intent, @Nullable Bundle) {...}

간접 권한을 사용할 때는 빌드 도구가 데이터 흐름 분석을 수행하여 메서드로 전달되는 인수에 @RequiresPermission 주석이 있는지 확인합니다. 그런 다음, 매개변수에서 기존 주석을 메서드 자체에 강제 적용합니다. 그림 1에 나오는 것처럼, startActivity(Intent) 예시에서는 적절한 권한이 없는 인텐트가 메서드로 전달될 때 Intent 클래스의 주석 때문에 startActivity(Intent)의 잘못된 사용에 대한 경고가 발생하게 됩니다.

그림 1. startActivity(Intent) 메서드에 대한 간접 권한 주석에서 생성되는 경고

빌드 도구는 다음과 같이 Intent 클래스의 해당 인텐트 작업 이름에 대한 주석에서 startActivity(Intent)에 대한 경고를 생성합니다.

@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
@RequiresPermission(Manifest.permission.CALL_PHONE)
public static final String ACTION_CALL = "android.intent.action.CALL";

필요하다면 메서드의 매개변수에 주석을 달 때 @RequiresPermission.Read 및/또는 @RequiresPermission.Write 대신 @RequiresPermission을 사용할 수 있습니다. 하지만 간접 권한의 경우 읽기 또는 쓰기 권한 주석 중 어느 하나와 함께 @RequiresPermission을 사용하면 안 됩니다.

반환 값 주석

메서드의 결과 또는 반환 값이 실제로 사용되는지 확인하려면 @CheckResult 주석을 사용하세요. 무효가 아닌 모든 메서드에 @CheckResult로 주석을 다는 대신, 혼동을 일으킬 가능성이 있는 메서드의 결과를 분명히 밝히는 주석을 추가하세요. 예를 들어, 초보 자바 개발자는 <String>.trim()이 원래 문자열에서 공백을 제거한다고 잘못 생각할 때가 많습니다. @CheckResult를 포함한 메서드에 주석을 달면 <String>.trim() 사용 플래그가 지정되며, 이때는 호출자가 메서드의 반환 값으로 아무 것도 하지 않습니다.

다음은 메서드의 반환 값이 실제로 참조되도록 하기 위해 checkPermissions() 메서드에 주석을 다는 예시입니다. 또한, 이 예시에서는 enforcePermission() 메서드의 이름을 개발자에게 대체 메서드로 추천할 메서드로 지정합니다.

@CheckResult(suggest="#enforcePermission(String,int,int,String)")
public abstract int checkPermission(@NonNull String permission, int pid, int uid);

CallSuper 주석

재정의하는 메서드가 메서드의 슈퍼 구현(super implementation)을 호출하는지 확인하려면 @CallSuper 주석을 사용하세요. 다음은 재정의하는 모든 메서드 구현이 super.onCreate()를 호출하도록 하기 위해 onCreate() 메서드에 주석을 다는 예시입니다.

@CallSuper
protected void onCreate(Bundle savedInstanceState) {
}

Typedef 주석

@IntDef@StringDef 주석을 사용하면 정수 및 문자열 집합으로 구성된 열거형 주석을 생성하여 다른 유형의 코드 참조에 대한 유효성을 검사할 수 있습니다. Typedef 주석은 특정 매개변수, 반환 값 또는 필드가 특정한 상수 집합을 참조하도록 해줍니다. 또한, 코드 완성을 사용하여 허용되는 상수를 자동으로 제공합니다.

Typedef 주석은 @interface를 사용하여 새로운 열거형 주석 형식을 선언합니다. @IntDef@StringDef 주석은 @Retention과 함께 새 주석을 생성하는데, 이러한 주석은 열거 형식을 정의하기 위해 필요한 것입니다. @Retention(RetentionPolicy.SOURCE) 주석은 컴파일러에게 .class 파일에 열거형 주석 데이터를 저장하지 않도록 지시합니다.

다음은 메서드 매개변수가 정의된 상수 중 하나를 참조할 때 값이 전달되도록 하는 주석을 생성하기 위한 단계를 보여주는 예시입니다.

import android.support.annotation.IntDef;
...
public abstract class ActionBar {
    ...
    // Define the list of accepted constants and declare the NavigationMode annotation
    @Retention(RetentionPolicy.SOURCE)
    @IntDef({NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS})
    public @interface NavigationMode {}

    // Declare the constants
    public static final int NAVIGATION_MODE_STANDARD = 0;
    public static final int NAVIGATION_MODE_LIST = 1;
    public static final int NAVIGATION_MODE_TABS = 2;

    // Decorate the target methods with the annotation
    @NavigationMode
    public abstract int getNavigationMode();

    // Attach the annotation
    public abstract void setNavigationMode(@NavigationMode int mode);

이 코드를 빌드할 때 mode 매개변수가 정의된 상수(NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST 또는 NAVIGATION_MODE_TABS) 중 하나를 참조하지 않을 경우에 경고가 생성됩니다.

@IntDef@IntRange를 결합하여 정수가 주어진 상수 집합이나 어떤 범위 내의 값 중 하나일 수 있음을 나타낼 수도 있습니다.

상수와 플래그 결합 사용

사용자가 허용되는 상수를 플래그(예: |, &, ^ 등)와 결합할 수 있는 경우, 매개변수 또는 반환 값이 유효한 패턴을 참조하는지 검사하는 flag 속성을 가진 주석을 정의할 수 있습니다. 다음은 유효한 DISPLAY_ 상수의 목록을 포함한 DisplayOptions 주석을 생성하는 예시입니다.

import android.support.annotation.IntDef;
...

@IntDef(flag=true, value={
        DISPLAY_USE_LOGO,
        DISPLAY_SHOW_HOME,
        DISPLAY_HOME_AS_UP,
        DISPLAY_SHOW_TITLE,
        DISPLAY_SHOW_CUSTOM
})
@Retention(RetentionPolicy.SOURCE)
public @interface DisplayOptions {}

...

주석 플래그가 있는 코드를 빌드할 때 데코레이트된 매개변수 또는 반환 값이 유효한 패턴을 참조하지 않을 경우 경고가 생성됩니다.

코드 접근성 주석

메서드, 클래스 또는 필드의 접근성을 나타내려면 @VisibleForTesting@Keep 주석을 사용하세요.

@VisibleForTesting 주석은 어떤 코드 블록이 코드를 테스트할 수 있도록 하기 위해 필요한 것보다 더 많이 표시된다는 점을 나타냅니다.

@Keep 주석은 빌드 시점에 코드가 축소될 때 주석이 지정된 요소가 삭제되지 않도록 해줍니다. 일반적으로, 컴파일러가 해당 코드는 사용되지 않는다고 인식하지 않도록 반사를 통해 액세스되는 메서드와 클래스에 이 주석이 추가됩니다.

This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Take a one-minute survey?
Help us improve Android tools and documentation.