Android - Conxtext란?
프로그래밍에서 컨텍스트란?
컨텍스트(context)란 프로그래밍을 하다보면 자주 접하게 되는 단어이다.
영어뜻 context(명사) : 1. (어떤일의)맥락, 전후 사정 2.(글의)맥락, 문맥
일반적인 의미로서 컨텍스트는 어떤 일,글 따위에 대한 전체적인 내용을 포괄적으로 말하는 것이다. 즉 어떤것의 모든 것을 컨텍스트라고 볼 수 있다.
프로그래밍에 context라는 말도 비슷한 의미이다. 다른 것이 있다면 주어가 항상 프로그램 그 차제로 고정되어 있다는 것이다. 프로그래밍에서 컨텍스트를 아주 일반화해서 말하면 프로그램 그 자체라고 볼수있다. 조금 더 세세하게 표현하자면 프로그램 전체를 아우르며 접근 및 제어(컨트롤) 할 수 있는 코어라고 볼수 있다.
안드로이드의 컨텍스트란?
안드로이드에서도 안드로이드 어플리케이션 전체를 아우르며 컨트롤 할수 있는 컨텍스트가 존재하며, 그것을 추상클래스로 구현해 놓았다. 다음은 안드로이드의 context의 사전적 의미이다.
어플리케이션 환경에 관한 글로벌 정보를 접근하기 위한 인터페이스. Abstract 클래스이며 실재 구현은 안드로이드 시스템에 의해 제공된다. Context 를 통해, 어플리케이션에 특화된 리소스나 클래스에 접근할 수 있을 뿐만 아니라, 추가적으로, 어플리케이션 레벨의 작업 - Activity 실행, Intent 브로드캐스팅, Intent 수신 등, 을 수행하기 위한 API 를 호출 할 수도 있다.
예를 들어 대저택(안드로이드 앱)이 있고 그 집의 주인(개발자) 가 있다면, 안드로이드 컨텍스트는 대저택의 집사쯤 된다고 생각 할수 있겠다. 주인(개발자)은 본인 대신 대저택(개발중인 앱)의 관리를 집사에게 맞기고 필요한 것들을 집사(컨텍스트)에게 명령 하기만 하면된다.
실제 코딩에서 개발자는 안드로이드 앱의 리소스에 접근하기 위해 특정 리소스의 파일 경로등에 직접 접근할 필요 없이, 아래처럼 컨텍스트에게 리소스 정보를 달라고 요청만 하면 그만이다.
1
this.getResources();
컨텍스트의 종류
Application Context – 이 컨텍스트는 실행중인 안드로이드 어플리케이션 프로세스의 Singletone instances 이다. 즉, 안드로이드 Application 프로세스가 생성될 때 Application Context 생성되고, 안드로이드 Application 프로세스가 종료될 때 Application Context가 같이 사라진다. Activity이나 Service에서는 getApplication() 메소드를 통해 getApplicationContext()로 이 Application Context를 얻어 올 수 있다. 이 컨텍스트는 안드로이드 어플리케이션 프로세스의 Singletone instances이므로 어디서나 동일한 인스턴스를 받게 된다.
(Activity / Service) Context – Activity 와 Service는 본인 그 자체가 context이다. Activity와 Service 모두 ContextWrapper를 상속받기 때문이다. (Activity / Service) Context는 Application Context를 확장한 개념이며, Activity또는 Service자신의 라이프사이클을 따라간다. Activity또는 Service는 자신이 그 자체가 Context이니 당연한 말일 것이다. Activity Context인경우 자신의 뷰 컴포넌트를 관리하기 위해서는 Application Context가 아니라 자신의 Activity Context에 접근해야 한다. Activity java코드에서 this키워드가 바로 이것이다.
BroadcastReceiver와 ContentProvider에서의 Context
BroadcastReceiver – 위의 2가지와 다르게 자기자신이 Context자체는 아니다. 하지만 onRecevie()시 Context를 가져올 수 있는데, 이때의 Context는 ReveiverRestrictedContext이며 두가지 기능 registerReceiver()와 bindService()를 사용 할 수 없다. 리시버가 브로드캐스트를 처리 할때마다 새로운 Context가 생성 된다.
ContentProvider – 브로드캐스트와 마찬가지로 자기자신이 Context를 상속 받은것은 아니다. 하지만 액세스후 getContext()를 통해 Application Context를 가져 올 수 있다. ContentProvider 동일한 응용프로그램에 대해 호출시, 동일한 응용프로그램의 Singletone instances를 반환하게 된다. 하지만 별도의 프로세스에 있는 경우(서로다른 다른 앱), 프로바이더가 실행되는 응용프로그램의 instances가 반환된다
컨텍스트 사용시 주의점
Application Context와 (Activity / Service) Context는 라이프 사이클이 다르다. Application Context는 프로그램 프로세스가 종료되지 않는한 메모리에서 사라지지 않는반면, (Activity / Service) Context는 수업이 생성과 소멸을 반복한다. Context를 참조할 때, (Activity / Service) Context를 참조하는 경우 이미 메모리에서 삭제된 Context를 참조하는 경우가 발생할 수 있다. 경우에 따라 다르겠지만, Context를 참조할 때에는 Application Context 참조해야 메모리누수를 피할수 있다.
컨텍스트 종류에 따라 다른 기능구분 표
참조한 사이트목록
http://sunphiz.me/wp/archives/483 http://arabiannight.tistory.com/entry/272 http://dev.youngkyu.kr/36 http://wengdiiiy.tistory.com/1 http://www.kmshack.kr/2013/10/android-context-what-context/ https://blog.mindorks.com/understanding-context-in-android-application-330913e32514
안드로이드스튜디오 한글깨짐 해결방법
안드로이드스튜디오에서 뷰의 디자인화면에서 한글로 입력된 텍스트가 경우 깨지는 경우가 있다.
해당 문제는 안드로이드스튜디오의 한글 폰트설정을 수정하면 해결 할 수 있다.
가. 안드로이드스튜디오 설치폴더로 이동한다. C:\Program Files\Android\Android Studio\plugins\android\lib\layoutlib\data\fonts 폴더로 이동한 뒤, fonts.xml파일을 에디터로 연다.(에디터를 관리자 모드로 실행하라.)
나. 텍스트 검색으로 아래의 태그를 찾는다.
1
<family lang="ko">
검색결과 NotoSansCJK-Regular.ttc 라는것으로 한글 폰트가 설정되어 있다.
NotoSansCJK-Regular.ttc를 NanumGothic.ttf로 바꿔서 fonts.xml 파일을 저장한다.
다. 안드로이드스튜디오를 재부팅 시키면 디자인에서 정상적으로 한글이 보인다.
안드로이드스튜디오 자동임폴트 설정(AndroidStudio Auto Import)
안드로이드스튜디오는 이클립스기반이 아니라 인텔리J기반 IDE 툴이다. 따라서 상당히 햇갈리는 부분이 많이 있다. 그중 하나가 자동임폴트이다.
안드로이드스튜디오의 초기 셋팅은 자동임폴트 기능이 enabled되어 있다. 위 이미지처럼 import 되어있지 않은 클래스를 코딩할 때 마다 Alt+Enter 를 눌러 달라고 귀찮게 한다.
안드로이드스튜디오에서 자동임폴트 기능을 활성화 하면 위 같은 귀찮은 것들을 줄일 수 있다.
안드로이드스튜디오의 좌측 상단의 file - Setting 화면에서 Editor - General - Auto Import 화면에서 Optimize imports on the fly, Addunambiguos imports on the fly 두개 항목을 체크하면 된다.
셋팅이 완료되면 타이핑 하는 그 즉시 자동으로 import가 되며, 번거롭게 Alt+Enter 를 누를 필요가 없어진다.
androidstudio - instant run requires 'tools android enable adb integration' to be enabled 해결
안드로이드스튜디오에서 개발중인 앱을 가상에뮬레이터에 동작시킬 때 간혹 Error running app: instant run requires 'tools android enable adb integration' to be enabled 라는 메세지와 함께 동작을 안하는 경우가 있다. 오류메세지 말 그대로 enable adb integration 라는 Android tools 가 enabled(비활성화) 되었다는 것이다. 이것을 활성화 해주면 문제가 해결된다. 잘 쓰다가 갑작이 왜 비활성화가 되었는지는 아직 의문이다.
일단 안드로이드스튜디오의 상단 메뉴 Tools -> Android -> Enable ADB Integration 항목을 확인해 보면 비활성화 된 것은 확인 할수있다.
해당 항목을 클릭하여 활성화 시키면, 정상적으로 안드로이드앱이 가상에뮬레이터에서 동작 한다.
java nested interface(중첩인터페이스)
중첩인터페이스(Nested Interface)는 사실 중첩클래스(Nested Class)와 같은 맥락을 갖는다. 중첩클래스가 클래스 내부에 선언된 클래스라면 중첩인터페이스는 클래스 내부에 선언된 인터페이스이다. 특정 클래스와 아주 긴밀하 연관된 인터페이스일 경우 중첩인터페이스를 사용하며, UI프로그래밍에서 이벤트 처리용으로 주로 사용된다.
중첩클래스의 경우 인스턴스멤버클래스, 정적멤버클래스, 로컬클래스 3가지의 선언 방법이 있지만, 중첩인터페이스는 클래스의 멤버로만 선언 할 수 있다.
형태
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17class ButtonA{
OnClickListener listener;
void setOnClickListener(OnClickListener listener){
this.listener = listener
}
void touch(){
this.listener.onClick();
}
/* 내부인터페이스 */
interface OnClickListener{
void onClick();
}
}
ButtonA클래스는 listener를 소유하고 사용한다.(has-a) 만약 ButtonA, ButtonB, ButtonC클래스가 존재하며 각기 다른 타입의 listener를 사용할 경우 즉 자신만이 사용하는 타입의 인터페이스를 선언할 경우 외부가 아니라, 위 처럼 자신 내부에 중첩인터페이스로 선언하여 인터페이스의 외부적변경을 경계할 수 있다.
1
2
3
4
5
6
7//내부인터페이스 구현 클래스
public class ListenerA implements ButtonA.OnClickListener{
@Override
public void onClick(){
System.out.println("난 ButtonA를 위해서만 일한다.");
}
}
위처럼 내부 인터페이스를 구현 할 수 있다.
Related Posts
java nested class(중첩클래스)
중첩클래스란(Nested Class)?
클래스 내부에 선언한 클래스를 말한다. 인터넷을 찾아보니, 중첩클래스(Nested Class)와 내부클래스(Inner Class) 이름을 혼용해서 사용 하는듯 하다. 본 글에서는 중첩클래스(Nested Class)로 표기한다. 중첩클래스는 크게 중첩클래스와 중첩인터페이스 크게 두개로 나뉜다. 이번글은 중접클래스만 다루도록 하겠다. 중첩클래스(Nested Class)를 사용하면 두 클래스의 멤버들을 서로 쉽게 접근할 수 있다는 장점과 외부에서 불필요한 관계 클래스를 감춤으로써 코드의 복잡성을 줄일 수 있다.
중첩클래스(Nested Class)의 활용에 대한 개인적 고민
처음 중첩클래스(Nested Class)를 접했을 때, 대체 이것을 어디에 써먹을 수 있을까 고민을 했었다. 당시 고민을 하다가 애라 모르겠다 하고 그냥 책의 페이지를 넘긴 기억이 난다. 그 고민을 지금 다시 정리해 본다. (개인적인 생각 정리라 참고만 하길...)
자바라는 언어는 객체지향 언어이고 클래스 단위로 프로그래밍을 해야 한다. 내가 만약 어떤 프로그래밍 소스가 필요하다면 그것을 클래스로 만들고 그걸 구현해야 한다.
로직 -> 클래스 선언(클래스 파일생성) -> 클래스를 객체화
자바에서는 소스코드의 단위를 클래스 단위로 강제하고 있다. 이로 인해 클래스의 다양한 장점을 이용해서 코딩을 할 수있다. 다들 들어본 재사용성, 다형성, 캡슐화등이 그 대표적인 것들이다. 하지만 만약 재사용이 필요하지 않고 일회성 소스코드를 만들어 사용할 경우에 그 것들을 클래스 파일로 만들어서 사용해야 할까? 재사용이 필요하지 않는 클래스파일의 생성은 소스코드관리의 관점에서 좋지 못한 선택이다.
중첩클래스(Nested Class)는 이런 문제를 해소하기 위해 사용되는 것이라고 생각된다. 물론 익명클래스도(anonymouse class) 같은 고민을 해결해 준다. 중첩클래스(Nested Class)와 익명클래스는 둘 다 일회성 코드를 만드는 방법이지만 재사용성 노출범위에서, 그 정도에 차이가 있어보인다. 중첩클래스는 클래스를 파일 단위로 만들지는 않지만, 협소하게 재사용이 가능하게 클래스 선언을 한다. 따라서 그만큼 더 클래스의 장점을 사용 할 수 있다. 그에 반해 익명클래스는 클래스를 선언조차 하지 않는다.
중첩클래스와 익명클래스는 코딩시에 상황과 필요에 따라 선택되어 사용될 뿐이다.
참고로 디폴트클래스(defualt class)도 하나의 방법이 되겠지만, 디폴트클래스(defualt class)는 프로그래밍의 구조를 너무 느슨하게 만드는 단점을 가지기 때문에 개인적으로 지양한다. 따라서 굳이 언급하지 않았다.
중첩클래스의 종류
인스턴스 멤버클래스
1
2
3class A{
class B{ ... }
}
특징 : 외부 클래스 A의 인스턴스 영역에 선언된 중첩클래스를 인스턴스 멤버클래스 라고 부른다. 인스턴스 멤버클래스(B)에서는 정적필드, 정적메소드를 선언할수 없으며, 외부 A클래스의 객체를 통해 내부 중첩클래스B에 접근하여 B의 구현객체를 생성할 수 있다. B로 구현된 객체는 외부객체의 인스턴스 필드와 외부객체의 클래스의 static영역에 접근 할 수 있다.
정적 멤버클래스
1
2
3class A{
static class B{ ... }
}
특징 : 인스턴스 멤버클래스와 다르게 static(정적)으로 선언된 정적 멤버클래스는 모든 종류의 필드, 메소드를 선언 할 수 있으며, 바깥 A클래스를 통해 내부클래스 B에 접근하여 객체를 생성 할 수 있다. 당연히 내부클래스의 객체는 외부클래스로 구현된 객체의 인스턴스 영역에는 접근 할수 없으며 외부 클래스 A의 static(정적)영역에만 접근 할 수있다.
로컬클래스
1
2
3
4
5class A{
void method1(){
class B{ ... }
}
}
특징 : method1가 실행할 때만 B중첩클래스 사용가능 하다. 외부 클래스의 모든 영역에 접근 가능하다. 심지어 method1의 메개변수와 로컬변수에도 접근이 가능한데, method1함수가 종료된 이후에도 로컬클래스의 객체는 힙 메모리에 존재해서 계속 사용 될 수 있다.
본래 매개변수나 로컬변수는 메소드실행이 종료되면 메모리에서 제거되어야 할 대상이다. 하지만 그 매개변수와 로컬변수를 로컬클래스로 생성된 객체가 참조한 경우가 문제가 될 수 있다. 자바는 이런 겅우를 위해 컴파일 시 로컬클래스에서 사용하는 매개변수나 로컬변수의 기억장소를 로컬클래스 내부에 복사해 두고 사용한다. (자바스크립트의 클로저를 연상시킨다.) 중요한 점은 참조만 가능하다는 것이다, 로컬클래스에서 자신을 감싸고 있는 함수의 매개변수와 로컬변수를 접근해서 수정을 할 수 없다. 아니 없어야 한다. (자바스크립트와는 다르다.) 따라서 컴파일러는 로컬클레스애서 자신을 감싸고 있는 외부함수의 로컬변수, 매개변수를 수정할 수 없게 강제한다. java7 버전에서는 로컬클래스에서 참조하는 함수의 매개변수와 로컬변수는 반드시 명시적으로 final 선언 했어야 했다. java8에서는 자동으로 final키워드를 내부적으로 붙여준다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37public class Outter {
//자바7 이전
public void methodJava7(final int arg) {
final int localVariable = 1;
class Inner {
public void method() {
int result = arg + localVariable;
System.out.println("java7 로컬클래스:" + result);
}
}// Inner
Inner java7Inner = new Inner();
java7Inner.method();
}//methodJava7
//자바8 이후
public void methodJava8(int arg) {
int localVariable = 2;
class Inner {
public void method() {
int result = arg + localVariable;
System.out.println("java8 로컬클래스:" + result);
}
}// Inner
Inner java8Inner = new Inner();
java8Inner.method();
}//methodJava8
public static void main(String [] args) {
Outter o1 = new Outter();
o1.methodJava7(7);
o1.methodJava8(8);
System.out.println("----");
}
}
결과
1
2
3java7 로컬클래스:8
java8 로컬클래스:10
----
이 중첩클래스라는 것을 잘 쓸지 안쓸지는 모르겠으나, 문법적 구조는 눈에 익혀둘 필요가 있어보여 정리해본다.
Related Posts
java8 interface의 변화(default method, static method)
java8에서 interface의 문법이 약간 변경되어 그 내용을 정리하고자 한다. java7 까지는 인터페이스에 상수, 실행블록이 없는 추상 메소드 선언만 가능했다. 하지만 java8부터 인터페이스에 디폴트메소드와 정적메소드도 추가로 선언이 가능하다. 이로인해 java의 인터페이스는 더욱 유연해진 코딩을 할 수 있다.
java8의 인터페이스 형태
1
2
3
4
5
6
7
8
9
10interface 인터페이스명{
//상수
타입 상수명 = 값;
//추상 메소드
타입 메소드명(매개변수, ...);
//디폴트 메소드
default 타입 메소드명(매개변수, ...){ ... }
//정적 메소드
static 타입 메소드명(매개변수){ ...}
}
자바8에서 인터페이스의 디폴트메소드와 추상메소드가 추가된 이유가 있다. 어떤 프로그램에서 인터페이스 A가 있고 이 인터페이스를 구현한 A1, A2, A3..., A10 총 10개의 구현 클래스가 있다고 가정하자. 5년정도 사융하다가 인터페이스 A에 추가기능이 필요해서 추상메소드를 한개 추가하고 새로이 B1이라는 클래스로 인터페이스 A를 구현하게 된다. 문제는 기존에 사용하던 A1 ~ A10의 구현객체에서 B1클래스를 위해 추가된 추상메소드를 추가로 구현해야 한다는 점이다. java8에서는 인터페이스의 디폴트 메소드와, 정적메소드를 추가하여 프로그래밍의 유연성을 높여주고 있다.
인터페이스의 디폴트메소드(default method)
디폴트 메소드는 인터페이스에 선언되지만 사실은 객체(구현객체)가 가지고 있는 인스턴스 메소드라고 생각해야 된다. 따라서 인터페이스를 구현한 객체를 통해서 호출 해야만 한다. 자바8에서 디폴트 메소드를 허용한 이유는 기존 인터페이스를 확장해서 새로운 기능을 추가하기 위해서이다. 만약 구현객체에서 인터페이스의 디폴트메소드가 적절하지 못하다면, 오버라이딩 하여 수정해서 사용하면 된다.
형태 : [public] default 리턴타입 메소드명(매개변수, ...){ ... }
인터페이스의 정적메소드(static method)
정적 메소드도 역시 자바8 부터 작성할 수 있는데, 디폴트 메소드와는 달리 객체가 없어도 인터페이스만으로 호출이 가능하다.
형태 : [public] static 리턴타입 메소드명(매개변수, ...){ ... }
java8의 인터페이스의 디폴트메소드, 추상메소드 예제
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47interface Java8InterfaceTest{
int talkCnt = 0;
void run();
//디폴트 메소드
default void talk(String msg){
if(msg != null && msg.equals("")){
System.out.println(msg);
}else{
System.out.println("난 디폴트 메소드다!");
}
}// talk
//정적 메소드
static void talk2(String msg){
if(msg != null && msg.equals("")){
System.out.println(msg);
}else{
System.out.println("난 정적 메소드다!");
}
}// talk2
}
public class InterfaceTest1 {
public static void main(String[] args) {
Java8InterfaceTest.talk2(null); // 정적메소드 호출
Java8InterfaceTest test1 = new Java8InterfaceTest(){
@Override
public void run() {
System.out.println("달려라!");
}
};
test1.run();
test1.talk(null); // 디폴트 메소드 호출
}
}
실행결과
1
2
3난 정적 메소드다!
달려라!
난 디폴트 메소드다!
인터페이스의 상속에서 디폴트메소드의 변화
인터페이스는 인터페이스를 상속 할 수 있다. 자식 인터페이스에서 부모의 인터페이스를 상속할 때, 부모의 디폴트 메소드를 어떻게 할지 선택 할 수있다.
-
부모 인터페이스의 디폴트 메소드를 그냥 상속한다.
-
부모 인터페이스의 디폴트 메소드를 재정의(Override)한다.
-
부모 인터페이스의 디폴트 메소드를 추상메소드로 재 선언한다.
인터페이스의 디폴트메소드는 인터페이스의 강제성을 조금 유연하게 해주는 기능이다. 하지만 좋게 말하면 유연이고 나쁘게 말한다면 느슨한 것이다. 만약 이 느슨한것을 다시 엄격하게 수정할 필요가 있다면 인터페이스를 상속하여 다시 추상메소드로 재 선언 하는것도 방법이다. 개인적으로 java8의 인터페이스의 변화는 개발자의 자유도를 높여주는 기능처럼 보이며, 상당히 마음에 든다.
인터페이스 상수 선언(public static final)할 때 주의할점
java8의 특징은 아니지만, 자주 햇갈리는 부분이라 추가로 기록한다. 일반적인 클래스에서 상수(static final)의 초기화는 선언과 동시 그리고 정적영역(static block) 두 곳 모두에서 가능하다. 하지만 인터페이스의 상수는 정적영역(static block)에서 초기화 할수 없기 때문에 반드시 선언과 동시에 초기화 해야 한다.
java Anonymouse class(익명구현객체)
익명구현객체(Anonymouse class)란 말 그래도 이름이 없는 객체이다.일반적으로 인터페이스를 사용하기 위해서는 해당 인터페이스를 구현한 클래스를 만들어야 한다. 예를 들어 하나의 인터페이스를 구현하는 클래스 3개를 만든다면, 3개의 java파일을 미리 만들어야 한다. 하지만 만약 재사용이 불필요한 구현클래스가 필요할땐 어떻게 해야 할까? 한번만 사용할 소스코드를 XXX.java 라는 파일로 만들고 그 것을 파일로 관리 해야만 할까? 프로그래밍에서 관리의 대상이 많아지는 것은 그 만큼 유지보수의 비용이 증가한다는 의미이다.
이렇게 일회성이며, 재사용할 필요 없고 파일단위의 관리 대상이 아닌 객체를 만들때 이 익명구현객체라는 것을 활용 할 수 있다. 문법 형태는 다음과 같다.
1
2
3
4
5
6인터페이스명 변수 = new 인터페이스명(){
//인터페이스에 선언된 추상메소드의 구현 메소드
//해당 인터페이스에 선언된 모든 추상메소드를 구현해야 한다. 없을시 컴파일 에러 발생
//추가적으로 필드와 메소드를 선언 할 수 있지만, 익명객체 안에서만 사용할수있다.
//익명구현객체도 컴파일시 클래스파일이 만들어 지긴한다. 인터페이스명$1(순번)
}; //익명 클래스의 선언은 하나의 실행문이므로 끝에 세미콜론(;)이 무조건 붙는다.
간단한 예제
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25//구현할 인터페이스
interface Car{
public void run();
public void stop();
}
public class AnonymouseTest1 {
public static void main(String[] args) {
//익명구현 객체 fooCar
Car fooCar = new Car(){
public void run(){
System.out.println("달려라!");
}
public void stop(){
System.out.println("머.. 멈춰!!!");
}
};
fooCar.run();
fooCar.stop();
}
}
실행결과
1
2달려라!
머.. 멈춰!!!
즉 한번 쓰고 버릴 객체가 필요할때 이 익명구현객체란 것을 이용할 수 있다. 익명구현객체는 java8의 람다식이나 UI 프로그래밍에서 이벤트의 처리용으로 많이 사용되기 때문에 그 형태를 알아둘 필요가 있다.
Related Posts
java Annotation(어노테이션)이란?
java5부터 어노테이션이라는 문법이 제공된다. 어노테이션이란 java의 소스코드를 설명하는 메타데이터다. 형태는 @어노테이션명 이다. 즉 소스코드의 특정부분(클래스, 인터페이스, 메소드, 필드 ...)에 @어쩌구저쩌구를 붙여서 이 소스코드를 설명한다는 것인데... 누구에게 무엇을 설명한다는 것일까?
- 컴파일러에게 코드 문법 에러를 체크하도록 정보를 제공
- 소프트웨어 개발 툴이 빌드나 배치 시 코드를 자동으로 생성할 수 있도록 정보를 제공
- 실행 시(런타임 시) 특정 기능을 실행하도록 정보를제공
컴파일러
어노테이션의 설명의 대상 중 하나는 컴파일러다. 컴파일러에게 이 소스코드를 어떻게 처리해야 되는 것인지 표시를 해준다. 예를들어 내장 어노테이션인 @override경우 해당 메소드가 부모클래스를 오버라이딩 한 메소드라고 컴파일러에게 미리 일러주는 것이다. 따라서 컴파일러는 런타임 이전에 이 메소드가 문제없이 오버라이딩 되었는지 검사한다. 개발툴, 빌드툴 등도 이 컴파일러와 비슷한 맥락으로 어노테이션을 사용한다고 생각된다.
개발자
어노테이션이 만들어진 목적은 프로그래밍에서 사용하던 xml 파일들을 줄이기 위해서라고 한다. 즉 프로그램에서 사용하는 xml 형태의 다양한 설정정보들, 특히 소스코드를 설명하는 내용의 설정 파일들을 대체하기 위해서이다. 예를 들어 n개의 클래스와 그 n개의 클래스의 설정정보가 xml로 되어있다면 관리의 대상이 두배가 된다. 어노테이션은 이런 xml들을 대신 설명이 필요한 대상과 그 설명을 합치게 되었고, 그 결과 어노테이션이란 것이 만들어 졌다.
예를들어 개발자는 소스코드에 있는 @overide를 보고, 해당 메소드는 상속 받은 것 임을 확인할수 있다.
컨테이너
어노테이션의 설명의 대상 세번째는 컨테이너 이다. 스프링 프레임워크를 만져본 개발자라면 수많은 스프링 어노테이션을 본적이 있을 것이다. 프리임워크는 자신 스스로 자바의 클래스를 객체화 하고 조립하고 사용한다. 즉 자신이 주체가 되어 자바 인스턴스의 라이프사이클을 관리한다. 이때 이 어린아이와 같은 프레임워크가 가지고 놀 클래스에 어노테이션으로 라벨을 붙여, 이 부품(클래스)는 용도가 무엇이고 어떻게 사용하고, 문제가 있을시에 어떻게 해야 하며, 테스트는 어떻게 하는지 기타 등등의 정보를 알려줄수 있다.
정리
자바책에서 어노테이션을 설명하면서, 커스텀어노테이션도 설명 한다. 하지만 공부를 하면서 내가 이 어노테이션을 공부해서 어디에 활용 할 수 있지? 라는 고민이 들었다. 어노테이션은 메타데이터이다. 즉 프로그램 그 자체에서 동작하는 코드가 아니다. 만약 컴파일러, 개발툴, 빌드툴, 테스트툴 또는 스프링프레임워크와 같은 컨테이너를 개발하지 않는 이상 직접 어노테이션을 만들어서 사용해 볼 일이 거의 없을 것 같다.