[Vi] Vi 사용하기

개발도구로그 2011. 4. 25. 11:58



vi(Visual display editor) 는 유닉스 환경에서 emacs 와 함께 많이 사용되어지는 편집기 중 하나이다.  
vi의 개선된 버전인 vim(vi Improved) 또한 존재한다.  vi는 사용자 홈폴더의 ~/.exrc 파일에  vi의 환경설정을 셋팅할 수 있다. 
vim은 window 환경에서는 vim 설치폴더에, 유닉스/리눅스 환경에서는 홈폴더에 .vimrc 파일이 존재하여 환경설정을 할수 있다.


IDE 환경이 없을 경우, vi를 잘 다룰수 있다면 상당히 편리하다. 커서이동, 화면분할, 화면이동, 문자열 검색, 대치, 정규식 적용
등을 단축키로만 사용하여 처리하면 엄청난 작업효율을 가져올 수 있다는...emacs를 깊이 있게 사용해보지 못해서 
아직까지는 vi 가 좀더 편하다

.exrc / .vimrc 설정 예
다음은 내가 사용하고 있는 vi 설정이다.  vim 은 플러그인 기능을 제공해서 좀더 다양한 기능을 사용할 수도 있다. 
 





단축키 설정
map 명령을 사용하여 특정 명령에 대한 단축키를 설정할 수 있다.  현재  편집중인 파일을 컴파일하고, 실행하는 명령을
F2 와 F3 단축키로 설정하는 방법은 다음과 같다.  이렇게 해놓으면 매번 vi 빠져나가서 컴파일하고 실행하는 번거로운 과정을
피할 수 있다.  

map  :!gcc -o %< %            
map  :!./%<

%는 현재 편집중인 파일이름을 , %< 확장자를 제외한 현재파일 이름, <CR>은 엔터의 의미로 해석된다.





약어(abbreviation) 설정 
키를 맵핑하여 사용할 수도 있지만,  문장을 치환하는 것은 약어를 사용하는 방법이 더 간편하다.  IDE 에서 지원하는 코드 snippet
기능을 흉내낼 수 있어서 유용한 기능이다.  약어를 지정 방식은 다음과같다.
ab 약어 대체할문장

사용 예를 들어보자.   C 언어에서 매번 stdio.h 헤더파일과 main 함수를 작성하는 것은 상당히 번거로운데
약어로 지정해 놓으면 약어 + Tap 키 조합으로 한번에 코드들을 삽입할 수 있다. 

ab Inc #include <stdio.h>
ab inc #include "custom.h"
ab main int main(int argc, char *argv[]) {<LF><LF>return 0;<LF>}



방법론(methodology)

방법론로그/OOAD 2011. 4. 12. 15:17



소프트웨어 방법론의 개발 프로세스이자 일반적인 문제 해결 과정을 알아보자.

아래 내용은 박병준 선생님의 가르침을 바탕으로 정리한 내용들이다!! 감솨합니다 샘!!
이것을 이해하고 자기의 것으로 어떻게 만드느냐는 개인의 몫!

아래 그림은 일반적인 개발 프로세스들과 그 프로세스를 지원하기 위해  작성하는 문서들이다. 
물론 이런 문서들이 항상 고정되어 있는 것은 아니다. 개발하고자는 업무영역에 따라 도움이 된다면 더 많은 문서들을
작성할 수도 있고, 간소화 할수도 있다. 중요한 것은 대상을 얼마나 "다각도에서 이해하는냐" 이지 "무엇을 작성하느냐" 가
아니다.   
프로그램이란 해야할 일을 다른 사람에게 대신하게 하는 것이라는 느낌으로 , 단계별로  "주인과 종"을 예로 들어 설명했다.








1. 문제인식(Problem Recognition)
개발 프로세스의 가장 첫번째 과정이다.  "누가, 무엇을 원하는가" 를 정의하는 단계이다. 
"누구" 는 반드시 한 사람이라고 할 수 없다. 문제에 연관되어 있는 다양한 사람들을 도출하는 하는 것이 첫번째 단계이고, 
이들이 각각 무엇을 원하는지 도출하는 것이 두 번째 단계이다. 






누구를 만족시킬 것인가? 그 누구가들이 바라는 것들은 무엇인가? 를 도출하는 과정 

그렇다면 문제는 어떻게 인식하는가? 앞서 문제는 왜 생기는가? 그것은 사람이기 때문이다. 
사람이기 때문에 필연적으로 문제는 생기기 마련이다. 즉 사람이 모든 문제의 시작점이 되야 한다. 
하지만 누군가는 모든 사람들을 의미하는 것은 아니다. 수많은 사람들의 범주 가운데서, 좁혀지고 좁혀져
특정 집합에 속하는 사람들만이 우리가 만족시켜야 하는 사람군에 속한다. 이 집한군은 여러 군이 존재할 수 있으며
이렇게 이익과 해를 보는 사람들을 Stakeholder (이해당사자)라고 한다. 

ex) twitter를 예로 들어보자. 
사람들은 커뮤니케이션을 할 수 있는 수단을 필요로 했다. 가장먼저 대화를 하기 시작했으며, 공간의 제약을 극복하기위해
편지라는 것이 등장했다. 하지만 더빠른 전달을 위해 전보가 등장했다. 송수신을 더 잘하기 위해 전화가 등장했다.  나아가 
언제 어디서든 가능하게 하기 위해 핸드폰 또는  SMS가 등장하고 결국 twitter 에 이르게 된것이다. 

요약하면 Stakeholder를 도출하고,  그들이 원하는 기능을 도출하는 과정이 문제인식이라 정의할 수 있다.  







2. 정보수집(Requirement Gathering)
기존에 (오프라인에서) 업무가 어떤 절차에 의해 진행되어 왔는지를 파악하는 단계이다.  문제인식에서 도출한 기능별로
실제업무가 어떤 절차로 진행되는지를 시나리오 방식으로 기술하되 잠재적으로 데이터가 될수 있는 항목들을 명시한다. 
절차와 데이터를 함께 고려하면 혼선을 가져올 수 있기 때문에, 절차를 기준으로 먼저 기술하고 난후 데이터를 도출하는 것이
도움이 된다.




 
기존에 행해져 왔던 일의 전체과정을 보는 단계이다.  
즉 절차에 집중하는 단계이며, 데이터 또한 그 절차안에서만 의미를 갖는다. 
(Stakeholder 가 기존에 해당 목적을 달성해왔던 과정을 훑어 보는 과정)

ex) 인사관리시스템을 만들기 위해서는 인사관리 업무가 무엇인자 알아야 하지 않겠는가?





3. 분석(Analysis)
오프라인 업무에 시스템이 적용되기 위한 접합점이다.  정보수집 단계에서 작성한 시나리오를 바탕으로  사용자가
수행할 업무와 시스템이 수행할 업무를 분리하여 기술한다.  사용자의 절차, 시스템의 절차가 분리되어 기술하는 것이
좋으며 그 절차와 더불어 데이터 항목들을 도출해야 한다.  정보수집 단계에서는 없었던 데이터들이 시스템이 적용되면서
추가되거나,  데이터들에 대한  조건(유효성 검사)들이 추가된다. 




 
절차에서 어떤부분을 대신 시스템화 할 수 있는지 고민하는 과정이다. 
어디서부터 어디까지 대신 시킬 것인지에 따라 결과가 달라진다. 
종과의 약속 => 인터페이스
종에게 물어본후 답에 따라 그 일을 다르게 행하게 할 수 있다. => 입력
(Stackholder 에 따라 물어보는 부분이 달라 질 수 있다. )

종에게 대신 시킬 과정을 찾는 단계이며, 전체 절차는 변하지 않는다것에 주의한다. 





4. 아키텍처(Architecture)
종에게 대신 시키기 위해서 주인이 준비물을 지정하는 단계이다. 준비물에 무엇이냐에 따라서  종이 받아들이는 것과 
대신 수행한 일의 결과가 달라질 것이다. 

아키텍처는 Infra 와 Technology로 구성된다. 인프라는  하드웨어적인 준비물을 의미한다.
ex) 대리운전을 호출했다면, 차와 키, 톨비가 인프라

인프라가 있더라도 이를 사용할 수 있는 기술이 있어야 한다.
ex) 대리 운전기사가 차를 운적할 수 있어야 함


 





5. 설계(Design)
종에게 준비물을 사용하게 했을 때의 절차이다. 
전체 절차는 동일하지만, 준비물이 바뀜에 따라 절차가 조금씩 바뀌게 된다. 
=> 모든 생각이 끝나야 하는 단계. 생각을 안해도 되게끔 설계 단계까지 모든 것이 이루어져야 한다. 



 



6. 구현(Implementation)


7. 테스트(Test)
미리 예측한 결과를 확인하는 단계이다.  그렇다면 어느단계에서 예측하는가? => 바로 분석단계!
작은 단위로 하나하나 테스트 하는 것을 단위 테스트(Unit test) 라고 한다. 
전체시스템을 놓고 테스트 하는 것을 통합 (Integretaion test) 라고 한다. 
Close test는 개발사 자체조직 내에서의 테스트 
Open test는 서비스 이전에 일정한 사용자에 의한 테스트 


8. 배포(Deployment)
유기적인 장소에 하드웨어/소프트웨어들을 설치하는 단계


9. 서비스(Service)


10. 유지보수(Maintenance)

 

영어를 빨리 배우는 방법

학습로그/영어 2011. 4. 12. 01:15



관심있게 보고 있는 Effortless English 사이트에 "영어를 빨리 배우는 방법"에 기사글이 올라와 있어 번역해보았다. 
일부 중복된 내용은 번역하지 않았다. 원문은 다음 사이트를 참고하자.   Effortless English


영어를 빨리 배우는 방법은 무엇일까? 2~3달 안에 영어를 아주 빨리 배우는게 가능할까?

그렇다. 하지만 그에 상응하는 강한 시간투자와 노력이 필요하다.

다음과 같은 사항들이 필요하다.


 


1. 몰입 (Obsession)

목적을 달성하기 위해 가장 필요한 것은 강한 열정이다. 하루에 8~14시간동안 영어를

공부하기 위해서는 매시간 마다 스스로 자각해야하며,  흥분되고, 열정적인 상태를 유지해야한다.

빨리 성장하기 위한 열쇠는 감정이다. 영어에 몰입해야하고, 믿을 수 없을 정도로 열정적이어야 한다. 


열정을 만들기 위해서는 영어를 공부해야하는 흥미를 유발하는 이유가 필요하다.

단지 시험을 잘 보기위한 것 또는 단지 새로운 직업을 얻기 위한 것으로는 부족하다.

그렇게 해야하는 확고한 이유가 필요하다. 


유창한 영어 스피커로서 얻게될 믿을 수 없을 만큼의 많은 혜택을 상상해보자. 

지금부터 변화될 5년, 10년, 그리고 20년 후를 상상해보자.

돈이 동기가 된다면, 영어가 얼마나 나를 부유하게 해줄지 상상해보자. 꿈의 집, 꿈의 차, 꿈 같은 삶을 상상해보자.  

반대로 영어를 유창하게 못했을 경우에 발생할 끔찍한 일들을 과장해서 상상해보자. 

잃게 되는 모든 직업들, 결코 만날 수 없게 되버린 사람들, 영어를 못해서 얼마나  많은 기회를 잃게 되는지를 상상해보자. 


더 큰 이유를 만들자. 큰 이유일 수록 더 큰 열정을 가져온다. 그리고 더 큰 성공을 가져온다.

감정이 열쇠이다. 감정을 더 강하게 하자. 영어에 몰입하자. 




2. 대량입력 (Massive Input)

영어를 빨리 공부하기 위한 두번째 열쇠는 영어 "입력"에 집중하는 것이다. 문법과 단어를

공부하기 위해 많은 시간을 낭비하자 마라. 말하기 위해 시간을 낭비하지 마라.

많은 시간을 리스닝과 리딩을 위해 투자해라. 이것이 영어를 유창하게 하기 위한 

가장 빠르고 가장 효율적이 방법이다.  iPod 또는 영어 책을 항상 가지고 다녀라. 

특히,  Mini-Story 레슨과 Point of View 레슨, Main Audio 아티클을 많이 들어라. 

이것들은 가장 강력한 교육자료이고, 영어를 빨리 늘도록 도울 것이다. 

어린이들을 위한 영어 소설부터 시작하자.  결코 교과서를 읽기 위해 시간을 낭비하자 마라.




3. 공부의 강도 (Massive Intensity)

2~3 달안에 영어를 유창하게 하고 싶으면, 영어에 대한 공부의 강도를 강화해야한다.

매일 하루에 8~14 동안 듣고, 읽어야 한다. 영어를 끊임없이 듣고, 끊임없이 읽어야한다.

두 활동을 번갈아 가면서 하는게 좋다. 한시간은 듣고 한시간은 영어소설을 읽고…

스피킹에 중점을 둔다면 좀더 많이 들어라. 

이것들이 영어를 빨리 배우기 위한 전부이다.  이렇게 빠른 시간안에 공부하지 않아도 

된다면 하루에 2시간의 리스닝과 리딩으로도 충분하다.


빨리 향상되고 싶다면, 이 계획을 따르라. 




'학습로그 > 영어' 카테고리의 다른 글

[마인드맵] My House  (0) 2012.03.14
[마인드맵] Wedding  (0) 2012.03.06
영어를 빨리 배우는 방법  (0) 2011.04.12
ESL(cafe #274)  (0) 2011.03.02
[EE]Hitch1  (0) 2011.02.19
학습방법  (0) 2011.02.14

Inner Class(내부 클래스)

언어로그/Java 2011. 4. 12. 00:16



 오래전에 Inner Class  개념을 공부하면서  "Thinking In Java 4/e"  을 바탕으로 정리한 내용이다. 

1. 외곽클래스의 멤버메서드(static이 아닌)에서는 내부클래스의 객체를 다른 클래스 객체처럼 참조할 수 있다.

2. 외곽클래스의 static메서드에서는 반드시 '외곽클래스명.' 통해 내부클래스 객체를 참조할 수 있다

3. 내부 클래스 내부에서는 외곽클래스의 멤버를 자유롭게 참조할 수 있는데, 이것은 내부클래스가 자신을 생성한
    외곽클래스 객체의 참조를 은밀히 가지고 있기 때문이다. 내부클래스 객체 내부에서 '외곽클래스명.this'를 사용하여
    접근할 수 있다.

4.  내부클래스 객체 생성시, 특정 외곽클래스 객체와 연결하고자 할때(앞서 외곽클래스 this참조를 은밀히 갖느다고 했다)에는,
    해당 외곽클래스 객체에 대한 참조를 '.new'키워드에 지정해야 한다

5. 지역내부 클래스(local inner class)는 외곽클래스 멤버메서드 내부 또는 코드블록({}로 둘러싸인)에서 정의되고,  
   객체가 생성되는 것이다.  지역내부 클래스를 사용하는 이유는,
   (1) 특정인터페이스를 구현하는 내부 클래스의 객체를 생성후, 그 참조를 반환시거나,
   (2) 클래스의 접근을 일정범위 내로 제한하고자 할때

 6. 내부 클래스 왜 필요할까?
   내부클래스는 다중상속의 문제점을 해결하는 하나의 방안이다. 여러개의 인터페이스를 구현해서, 다중상속을 구현하지만,
    여전히 하나의 추상클래스 밖에 상속하지 못하기 때문에 제한이 있다. 이를 해결하는 것이 내부클래스로, 특정 클래스를
    내부클래스가 상속하게 하여 보다쉽게 다중상속의 기능을 제공하게 된다.

7. 이벤트 중심 시스템(Event Driven System)과 같은 제어 프레임워크의 구현에서, 내부클래스가 많이 사용된다.
   내부클래스를 사용한 제어 프레임워크의 한가지 예제 코드는 다음과 같다.


이벤트 기반 제어프레임워크 소스 예


이벤트를 정의하는 추상클래스 (어떠한 액션을 캡슐화한다)




이벤트를 큐에 쌓고, 순차적으로 실행시키는 컨트롤러 (이벤트들의 행위를 순차적으로 실행한다)




이벤트 추상클래스를 상속하는 구현 이벤트 클래스들 (구체적인 액션 대한 정의)
(불을 켜고, 끄고 벨을 누르고, 프로그램을 종료하는 이벤트들을 내부클래스를 사용해서 정의하였다.)




프로그램을 실행시키는  entry-point  클래스 



컬렉션(Collection)

언어로그/Java 2011. 4. 11. 23:26



자바에서는 효율적인 데이터 처리를 위해 일련의 컨테이너 자료구조 라이브러리를 제공하고 있다.  데이터를 담는다는
의미에서 컨테이너라고 하며,  최상위 추상클래스 Collection의 이름을 따서 컬렉션이라고 부르기도 한다. 
데이터를 다루는 방법과 데이터에 대한 조작 연산(CRUD)의 빈도에 따라 최적화되어 있는 다양한 자료구조들을 제공하고 있다. 
이 컬렉션의 클래스들에 대한 모두 기억할 필요는 없다.  데이터 추가/조회/수정/삭제 중 어떤 연산이 많은 빈도를 가지고 있는지에 따라 어떤 컬렉션 클래스가 적합한지 알고 사용하면 되겠다.  각 컬렉션들에 대한 구체적인 설명은 생략하겠다. 
컬렉션류 클래스들의 전체 상속도는 다음과 같다







컬렉션 클래스들 중 Set 과 HashMap 대한 특성을 알아보자. 



1. Set
기본형 타입 외 사용자 정의타입에 대해 Set을 사용하면 데이터들의 중복이 없으며 element  들의 순서를 유지하지 않는다.
(집합과 같이 데이터들의 순서가 중요하지 않으며,  데이터들이 중복없이 하나만 고유하게 존재할 경우 사용한다)
 Set (인터페이스)  중복요소 저장 안됨.
 추가되는 요소는 객체의 동일성 판별을 위해 equals() 메소드를 정의해야함.
  Collection 과 인터페이스가 동일하며, 요소(element)들의 순서유지가 보장되지 않음.
 HashSet  빠른 검색이 가능한 Set.
 요소는 equals(), hashCode()를 정의해야함. 
 TreeSet  트리형태의 순서를 갖는 Set.
 정렬된 요소를 얻을 수 있으며, Comparable 인터페이스를 구현해야 함.
 LinkedHashSet  HashSet의 빠른 검색속도를 갖으며,  내부적으로 LinkedList의 순서를 유지함.
 추가된 순서대로 요소를 얻을 수 있음.
 hashCode() 메소드 또한 정의해야함. 

Set을 사용자 정의 타입에서(클래스에) 사용하려면, 중복성 회피를 위해 equals() 메소드를 재정의 해주어야한다. 
(Set는 내부적으로 element 들의  equals() 메소드를 호출하여 서로간의 중복 여부를 판단하기 때문이다)
hashCode()는 hashSet,  LinkedHashSet 를 사용할 때만 재정의 해주면 된다.  재정의를 하지 않으면,  Object의  hashCode()를
사용하는데,  equals()를 구현했어도 객체가 중복되어 Set 인터페이스 규약이 깨지게 된다.   (서로 동일한 데이터를 가지고 있어도hashCode()를 구현하지 않으면 다른 버킷으로 해싱되기 때문  데이터가 중복될수 있다. 이것은 Set의 규약을 위반한 것임)
hashCode(), equals() 을 모두 재정의 하는 것이 바람직한 습관이다. 




2. Map
key-value  관계를 저장하여 key를 사용한 value의 검색을 가능하게 함.
 HashMap   해시 테이블에 기초한 구현 클래스.
 객체 추가 / 검색어에 동일한 시간을 보장함.
 LinkedHashMap  HashMap과 동일한 저장방식을 사용함.
 이터레이터를 사용함. key-value 쌍이 추가된 순서, 최근 최소 사용한 빈도에 따른 검색이 가능
 HashMap 에 비해 다소 느리고, 순차검색시 내부적으로 연결리스트를 사용해 더빠른 성능을 발휨. 
 TreeMap  이진 트리에 기초한 맵 구현클래스.
 key-value 쌍은 Comparable, Comparator에 의해 정렬순서가 저장됨.
하위 트리를 반환하는 subMap() 메소드를 보유함. 
 WeakHashMap  더 이상 참조되지 않은 key에 대한 가비지 컬렉션을 허용함.
 ConcurrentHashMap   동기화에서 락을 발생시키지 않는 Thread-Safe한 Map.
 IdentityHashMap   key 비교시 equals()가 아닌 == 사용함. 범용사용은 불가능함. 





예제
import java.util.*;

// 모든 Set 컬렉션은
// 중복 회피를 위해 equals()를 재정의 해야함
class SetType {
        int i;
        public SetType(int n) {
                i = n;
        }
        public boolean equals(Object o) {
                return o instanceof SetType && (i == ((SetType)o).i);
        }
        public String toString() {
                return Integer.toString(i);
        }
}

// HashSet 계열은 equals() 외에 hasCode()를 재정의 해야함
// 그렇지 않을 경우 equals()를 정의했어도, 데이터 중복이 발생
class HashType extends SetType {
        public HashType(int n) {
                super(n);
        }
        public int hashCode() {
                return i;
        }
}

// TreeSet 계열은 equals() 외에 compareTo()를 재정의 해야함
// compareTo()를 재정의 하지않으면 실행시 ClassCastException 발생
class TreeType extends SetType implements Comparable<treetype>
 {
        public TreeType(int n) {
                super(n);
        }
        public int compareTo(TreeType arg) {
                return (i > arg.i ? 1 : (i == arg.i) ? 0 : -1);
        }
}


public class TypesForSets {
        
        static <t> Set<t> fill(Set<t> set, Class<t> type) {
                try {
                        for (int i = 0; i < 10; i++) {
                                // int형 인자를 갖는 생성자를 얻어서, 객체 생성
                                set.add(type.getConstructor(int.class).newInstance(i));
                        }
                } catch(Exception e) {
                        throw new RuntimeException(e);
                }
                return set;
        }
        
        static <t> void test(Set<t> set, Class<t> type) {
                // 객체 중복저장 시도 - 셋의 특성을 알아보기 위함
                fill(set, type);
                fill(set, type);
                fill(set, type);
                System.out.println(set);
        }
        
        public static void main(String[] args) {
                // HashSet 계열은 equals() 와 hashCode()를 재정의 해야함
                test(new HashSet(), HashType.class);
                test(new LinkedHashSet(), HashType.class);
                // TreeSet 계열은 equals() 와 compareTo()를 재정의 해야함
                test(new TreeSet(), TreeType.class);
                
                // 아래메소드들은 각 Set 컬렉션의 특성이 파괴됨
                test(new HashSet(), SetType.class);
                test(new HashSet(), TreeType.class);
                test(new LinkedHashSet(), SetType.class);
                test(new LinkedHashSet(), TreeType.class);
                
                try {
                        test(new TreeSet(), SetType.class);
                } catch (Exception e) {
                        System.out.println(e.getMessage());
                }
                try {
                        test(new TreeSet(), HashType.class);
                } catch (Exception e) {
                        System.out.println(e.getMessage());
                }
        }
}



실행결과


리눅스 자격증

운영체제로그/Linux 2011. 4. 9. 00:29

자격증 취득을 목표로 하는 것을 별로 좋아하지 않지만,  공부를 해야하는 하나의 동기로서 자격증을 고려하는 것은 
괜찮은 방법이라 생각 한다. 리눅스 공부를 좀더 심도깊게 해볼 생각으로 관련 자격증에 대해서 알아보았다.
가장 잘 알려진 자격증으로 RHCE , LPIC, 리눅스 마스터 자격증이 있었다. 
RHCE 는 레드햇 리눅스 관련 가장 업계에서 인정해주는 자격증이고, 상당한 지식과 경험을 필요로 한다.
LPIC 는 국제인증 리눅스 자격증이고, 습득 난이도에 따라 단계별로 세분화 되어 있었다.
리눅스 마스터는 국내에서만 인정
되는 자격증이었지만, 덤프만 외우면 합격할 수 있는 자격증은 아닌듯 했다.
RHCE와 리눅스 마스터는 한국어로 출제되고, LPIC 은 영어로 출제되나 영어 난이도는 그리 어렵지 않다고 한다. 
자격증 습득 난이도는 RHCE 가 가장 어려운 듯 하고, LPIC 레벨 2가 리눅스 마스터 1급 정도에 해당하지 않을까...(신뢰성 없음) 

리눅스 자격증에 대한 간단한 소개 및 평가요소들을 나름 정리해 보았다. 



1. RHCE

Red Hat Certified Engineer(RHCE)는 레드햇사가 인증하는 국제적으로 가장 인정받는  리눅스 자격증이다. 
시험은 실습방식으로 2시간 동안 치뤄지고, 비용은 약 40만원으로 상당히 비싼편이다. RHCE는 사전에 RHCSA
(Red Hat Certified System Administrator)를 취득하지 않으면 자격증이 발급되지 않는다.  레드햇 커뮤니티를 바탕으로
수집된 실무와 연관된 작업들이 반영되어 출제된다고 한다. 

자격증 관련 자세한 정보는 다음 링크를 참고하자 :  RedHat Korea


시험 출제 목표는 다음과 같다. 

System Configuration and Management
 
Route IP traffic and create static routes
Use iptables to implement packet filtering and configure network address translation (NAT)
Use /proc/sys and sysctl to modify and set kernel run-time parameters
Configure system to authenticate using Kerberos
Build a simple RPM that packages a single file
Configure a system as an iSCSI initiator that persistently mounts an iSCSI target
Produce and deliver reports on system utilization (processor, memory, disk, and network)
Use shell scripting to automate system maintenance tasks
Configure a system to log to a remote system
Configure a system to accept logging from a remote system
 
Network Services
Install the packages needed to provide the service
Configure SELinux to support the service
Configure the service to start when the system is booted
Configure the service for basic operation
Configure host-based and user-based security for the service

HTTP/HTTPS
Configure a virtual host
Configure private directories
Deploy a basic CGI application
Configure group-managed content
 
DNS
Configure a caching-only name server
Configure a caching-only name server to forward DNS queries
Note: Candidates are not expected to configure master or slave name servers
 
FTP
Configure anonymous-only download
 
NFS
Provide network shares to specific clients
Provide network shares suitable for group collaboration
 
SMB
Provide network shares to specific clients
Provide network shares suitable for group collaboration
 
SMTP
Configure a mail transfer agent (MTA) to accept inbound email from other systems
Configure an MTA to forward (relay) email through a smart host
 
SSH
Configure key-based authentication
Configure additional options described in documentation
 
NTP
Synchronize time using other NTP peers








2. LPIC 
LPIC(Linux Professional Institute Certification)는 칼데라시스템, 레드햇, 데비안 등 리눅스 선도 기업들이 주축이 되어 설립한
비영리 단체 LPI에서 운영하는 리눅스 전문가 인증 제도이다. 수검비용은 CBT 경우 약 15만원 정도이고, PBT 방식은 조금 더
저렵하다.  시험이 레벨1(초급), 레벨2(중급), 레벨3(고급)으로 세분화되어 있으며,  실기가 없고 필기로만 시험이 진행되는 것도 
특징이다. 

LPIC에 대한 자세한 정보는 다음 사이트틀 참고 : LPIC Korea
시험 응시는 Prometric 또는 VUE 를 통해 할 수 있다. 

시험 출제 목표는 다음과 같다. 
 

 
 

 




3. 리눅스 마스터
리눅스 마스터는 한국 정보통신 진행협회가 인증하는 리눅스 한국 공인 자격증이다.  1급과 2급이 있으며,  시험을 
위한 특별한 자격요건은 없다.  수검비용은  1차(필기), 2차(실기) 합산하여 2급은 4만 5천원, 1급은 10만원이다.
시험 출제 목표는 아래 표를 참고하자. 

리눅스 마스터 시험 참고 사이트 : 한국 정보통신 진흥협회

 

 
더 자세한 시험 출제범위는 위 사이트에서 확인하자! 


 

'운영체제로그 > Linux' 카테고리의 다른 글

[Ubuntu] DNS 설정  (0) 2011.04.25
Wget  (0) 2011.04.25
리눅스 자격증  (0) 2011.04.09
리눅스 파일시스템 계층 표준(Filesystem Hierarchy Standard)  (0) 2011.03.09
su, sudo : 특정 사용자 권한으로 쉘/명령 실행  (0) 2011.03.08
cURL  (0) 2011.02.18

푸시 메시지 포맷

아이폰 2011. 4. 7. 23:39



 Provider와 APNS 간에 SSL(Secure Socket Layer)를 통해 통신을 하며, 서로 간에 올바른 통신을 위한 몇 가지
요구사항이 존재한다.   다음은  Local, Remote Notification Programming Guide 문서에서 일부 발췌한 내용이다.
자세한 내용은 실제 문서를 참고하자. 


1. Provider가 APNS와 통신하기 위한 요구사항
 〮 Provider는 Apple Push Notification Service와 바이너리 인터페이스를 사용해 통신함
 〮 TCP Socket을 사용하며, 안전한 통신채널을 위해 TLS(또는 SSL)을 사용함
 〮 APNS와의  잦은 연결과 종료는 DOS 공격으로 오인받을 수 있음 
 〮 에러가 발생하면 APNS는 해당연결을 종료함 
 〮 서버에 접속해서 반복적으로 푸시전송에 실패한 클라이언트 디바이스 리스트를 조회할 수 있음 => Feedback service



2. Binary Interface and Notification Formats
〮순수 TCP 소켓을 사용하며, 최적의 성능을 위해서는 단일 연결을 맺고, 다중 통지를 보내야함 
〮인터페이스는 simple 포맷, enhanced 포맷 2가지의 notification format을 지원함 
〮Simple 포맷에서는 payload 데이터가 제한크기를 초과하면 통지를 거부하지만, enhanced 포맷에서는
    notification 에 임의의 식별자를 할당하여, 에러가 발생하면 식별자와 연관된 에러코드를 확인할 수 있음


위에서 언급된 것처럼 2가지 종류의 메시지 형태가 존재한다. 대부분의 앱에서는 Simple 포맷으로도 충분하다. 하지만    
Simple 포맷은 실제로 푸시 메시지가 전송됐는지를 보장하지 않기 때문에,   푸시 메시지에 대한 전송 신뢰성이 보장되야 할경우엔는
Enhanced 포맷을 사용하자. Enhanced 포맷에서는 메시지 전송 실패시 에러를 반환하기 때문에,  Provider가 주기적으로 APNS에서 
이 값을 체크하여 실패시 다시 전송을 요청할 수 있다. 



3. Simple Format
APNS와  SSL 연결과, peer-exchage 인증을 가정한다. (SSL을 사용한 1:1 통신을 한다는 의미같다.)
메시지 포맷은 다음과 같다. 




Command : Simple 포맷인지 Enhanced  포맷인지 구별하기 위한 값. simple 포맷은 0을 갖는다.
Token length     : DeviceToken의 길이
Device Token    : 바이너리 포맷으로 인코딩 되야함
Payload length : 푸시로 전달할 메시지의 길이로 256 바이트를 초과할 수 없으며,
                           널(null)문자로 종료해서는 안됨
* token length와 payload length 는 네트워크 오더(big endian)이어야 함




3.1 SimpleFormat을 사용하는 예제코드 
Local, Remote Notification Programming Guide 문서에 나와있는 샘플 코드이다. 실제 메시지 전송을 구현해야할 경우 참고하자

static bool sendPayload(SSL *sslPtr, char *deviceTokenBinary, char *payloadBuff, size_t payloadLength)
{
        bool rtn = false;
        if (sslPtr && deviceTokenBinary && payloadBuff && payloadLength) {
        uint8_t command = 1; /* command number */ 
        char binaryMessageBuff[sizeof(uint8_t) + sizeof(uint32_t) + sizeof(uint32_t)
                + sizeof(uint16_t) + DEVICE_BINARY_SIZE + sizeof(uint16_t) + MAXPAYLOAD_SIZE];
        
        /* message format is, |COMMAND|ID|EXPIRY|TOKENLEN|TOKEN|PAYLOADLEN|PAYLOAD|  */
        char *binaryMessagePt = binaryMessageBuff; 
        uint32_t whicheverOrderIWantToGetBackInAErrorResponse_ID = 1234; 
        uint32_t networkOrderExpiryEpochUTC = htonl(time(NULL)+86400); 
        // expire message if not delivered in 1 day 

        uint16_t networkOrderTokenLength = htons(DEVICE_BINARY_SIZE); 
        uint16_t networkOrderPayloadLength = htons(payloadLength);
        
        /* command */ 
        *binaryMessagePt++ = command;
        
        /* provider preference ordered ID */
        memcpy(binaryMessagePt, &whicheverOrderIWantToGetBackInAErrorResponse_ID, sizeof
                                                                                                                (uint32_t));
        binaryMessagePt += sizeof(uint32_t);

        /* expiry date network order */ 
        memcpy(binaryMessagePt, &networkOrderExpiryEpochUTC, sizeof(uint32_t)); 
        binaryMessagePt += sizeof(uint32_t);
        
        /* token length network order */ 
        memcpy(binaryMessagePt, &networkOrderTokenLength, sizeof(uint16_t)); 
        binaryMessagePt += sizeof(uint16_t);

        /* device token */ 
        memcpy(binaryMessagePt, deviceTokenBinary, DEVICE_BINARY_SIZE); 
        binaryMessagePt += DEVICE_BINARY_SIZE;

        /* payload length network order */ 
        memcpy(binaryMessagePt, &networkOrderPayloadLength, sizeof(uint16_t)); 
        binaryMessagePt += sizeof(uint16_t);

        /* payload */ 
        memcpy(binaryMessagePt, payloadBuff, payloadLength); 
        binaryMessagePt += payloadLength; 
        
        if (SSL_write(sslPtr, binaryMessageBuff, (binaryMessagePt -binaryMessageBuff)) > 0) 
                rtn = true;
        } 
        return rtn;
}



4. Enhanced Format

Enhanced Format 를 사용하여 메시지를 전송하면, APNS가 인지 불가능한 명령을 만났을 경우 연결을 종료하기 전에 에러 응답을
반환해주어 에러의 원인을 확인할 수 있다. Provider 에서는 주기적으로 APNS의 에러응답에 접근하여 결과를 확인할 수 있다. 



first byte: 1
identifier: notification을 식별하기 위한 임의의 값. 에러가 발생하면 APNS는 이값을 반환함
expiry : notification이 더이상 유효하지 않는 때를 나타내는 초로 표현된 시간.  fixed UNIX epoch  date(UTC)
...나머지 필드는 Simple Format과 동일하다.




4.1 Enhanced Format을 사용하는 예제코드 
역시 실제 구현시에 참고하자. 
 
static bool sendPayload(SSL *sslPtr, char *deviceTokenBinary, char *payloadBuff, size_t payloadLength)
{
        bool rtn = false;
        if (sslPtr && deviceTokenBinary && payloadBuff && payloadLength) {
        uint8_t command = 1; /* command number */ 
        char binaryMessageBuff[sizeof(uint8_t) + sizeof(uint32_t) + sizeof(uint32_t)
                + sizeof(uint16_t) + DEVICE_BINARY_SIZE + sizeof(uint16_t) + MAXPAYLOAD_SIZE];
        
        /* message format is, |COMMAND|ID|EXPIRY|TOKENLEN|TOKEN|PAYLOADLEN|PAYLOAD|  */
        char *binaryMessagePt = binaryMessageBuff; 
        uint32_t whicheverOrderIWantToGetBackInAErrorResponse_ID = 1234; 
        uint32_t networkOrderExpiryEpochUTC = htonl(time(NULL)+86400); 
        // expire message if not delivered in 1 day 

        uint16_t networkOrderTokenLength = htons(DEVICE_BINARY_SIZE); 
        uint16_t networkOrderPayloadLength = htons(payloadLength);
        
        /* command */ 
        *binaryMessagePt++ = command;
        
        /* provider preference ordered ID */
        memcpy(binaryMessagePt, &whicheverOrderIWantToGetBackInAErrorResponse_ID, sizeof(uint32_t));
        binaryMessagePt += sizeof(uint32_t);

        /* expiry date network order */ 
        memcpy(binaryMessagePt, &networkOrderExpiryEpochUTC, sizeof(uint32_t)); 
        binaryMessagePt += sizeof(uint32_t);
        
        /* token length network order */ 
        memcpy(binaryMessagePt, &networkOrderTokenLength, sizeof(uint16_t)); 
        binaryMessagePt += sizeof(uint16_t);

        /* device token */ 
        memcpy(binaryMessagePt, deviceTokenBinary, DEVICE_BINARY_SIZE); 
        binaryMessagePt += DEVICE_BINARY_SIZE;

        /* payload length network order */ 
        memcpy(binaryMessagePt, &networkOrderPayloadLength, sizeof(uint16_t)); 
        binaryMessagePt += sizeof(uint16_t);

        /* payload */ 
        memcpy(binaryMessagePt, payloadBuff, payloadLength); 
        binaryMessagePt += payloadLength; 
        
        if (SSL_write(sslPtr, binaryMessageBuff, (binaryMessagePt -binaryMessageBuff)) > 0) 
                rtn = true;
        } 
        return rtn;
}



5. Error 응답 포맷
notification 포맷을 인식할 수 없을 경우 APNS는 연결을 종료하기 전에 error 응답을 전송해준다.  error가 없다면 어떠한 값도 
반환하지 않는다.  에러 응답의 포맷은 다음과 같다. 



first byte: 8
status : 상태코드
identifier : notification을 구성할 때 사용한 식별자 

상태코드
0 : 에러없음
1 :  에러 처리중
2 : device token 이 없음
3 : topic이 없음
4 : payload 없음
5 : 잘못된 token 크기
6 : 잘못된 topic 크기
7 : 잘못된 payload 크기
8 : 잘못된 token
255 : 알수없음




'아이폰' 카테고리의 다른 글

수동으로 UI 컨트롤에 이벤트 전달  (0) 2011.11.13
NSNotification  (1) 2011.04.29
푸시 메시지 포맷  (0) 2011.04.07
애플 푸시 서비스  (0) 2011.04.07
Sprite Sheet 제작툴 Zwoptex  (0) 2011.03.16
애플리케이션 응답성 향상을 위해 동시성 사용하기  (0) 2011.03.15

애플 푸시 서비스

아이폰 2011. 4. 7. 23:01



이 포스팅에서는 애플 푸시 서비스를 사용하는 방법에 대해서 알아보자. 작년 겨울 앱공모전을 준비하면서 "분실물 다나와"
라는 앱을 개발했다.  서울 분실물 센터에 수거되어 있는 분실물 목록을 조회할 수 있고, 등록된 분실물과 유사한 분신물 목록을
사용자 아이폰으로 알리는 앱이었다. 이때 유사 분실물 발견유무를 푸시 기능을 통해 구현하였다. 당시에 푸시와 관련된 
자료가 별로 없어서(내가 못찾았겠지만...) 애플 개발자 가이드 문서를 참고하여 구현하였다. 그 때 정리한 내용을 올려본다.
(사진 캡처를 안해서 이해가 잘 안될 수도....-_-;; )




1. Push Service 란?
서버에서 아이폰 애플리케이션으로 데이터를 역으로 전송할 수 있는 서비스이다. 
Remote  Notification을 등록하고 수신하는 애플리케이션과 푸시 서비스를 제공하는 APNS(Apple Push Notification Service),
APNS에 데이터 푸시요청을 수행할 수 있는 권한을 획득한  Provider(서버) 로 구성된다.



"분실물 다나와" 프로젝트로 예를 들면...Provider는 "분실물 다나와" 서버가 되고, Client App 은 "분실물 다나와" 아이폰 앱,
APNS는 애플에서 제공하는 푸시서버가 된다.  iPhone에 전송된 푸시메시지는 iOS 를 거쳐 실제 앱에 전달된다. 




2. Push 서비스 사용절차 
APNS는 실제 아이폰 기기로 푸시를 전송해주는 애플측에서 제공하는 서비스이다. 푸시서비스를 이용하는 서버인 Provider는
푸시할 데이터만을 가공하여 APNS에 푸시를 요청한다.  Provider가 APNS에 접속하여 푸시데이터를 전송하려 할 때, APNS는
푸시요청을  할수 있는 권한을 SSL 인증을 통해 검사하게 된다. 그래서 Provider는 사전에 이 인증을 위한 암호화 파일을 사전에
가지고 있어야 한다. 


① 개발자 포탈에서 애플리케이션에 대한 Push Serivce 사용권한 받기 
개발자 포탈에서 App Id에 대한  Development 또는 Product 푸시 서비스 옵션을 enable 시키고,  다운받은 인증파일을 KeyCain에
등록시킨다.  이 파일을 p12 파일로  export 하여, 파일, 비밀번호와 함께  Provider 에게 사전에 전달해야 한다. 


② 아이폰 애플리케이션 실행시 APNS에 RemoteNotification 등록 
아이폰 애플리케이션이 처음 구동시 APNS에 RemoteNotification 을 등록하고, 애플리케이션 DeviceToken을 수신받는다. 


③ Provider에게 DeviceToken 전송  
DeviceToken을 수신하면, 서버에 아이폰 UUID와 함께 전달한다. 이 토큰은 APNS에서 아이폰의 애플리케이션을 식별하는 키
되기 때문에 Provider가 보유하고 있어야 하는 데이터이다.  DeviceToken은 거의 변하지 않지만, 애플리케이션이 삭제되고 다시
설치될 때 변할 수도 있기 때문에 애플리케이션이 실행될 때마다,  Provider에게 전송하여 갱신여부를 확인하는 것이 좋다. Development용과 Product용의 DeviceToken 값은 다르다는 것에 주의한다. 
 




④ Provider 에서 푸시 메시지를 생성하여  APNS에 푸시 요청 
Provider 에서는 ①단계에서 전달받은 인증파일을 보유하고 있고, 인증파일 비밀번호를 알고 있어야 한다.
DeviceToken와 함께 다음의 주소로 push를 요청할 수 있다. 개발환경과 실제 서비스를 위한 APNS 의 주소가 다르다.
 
개발환경 푸시서버 주소: gateway.sandbox.push.apple.com / 2195
제품환경 푸시서버 주소: gateway.push.apple.com / 2195



⑤ APNS 에 전송하는 메시지의 포맷 
메시지는 JSON 포맷을 따르며 256바이트 이내야한다. 메시지를 구성하는 키는 다음과 같다. 


단순한 메시지 포맷은 위와 같으며, 좀더 세부적으로 메시지를 지정할 수도 있다. 더 자세한 사항은
Local, Remote Notification Programming Guide 를 참고하자. 

JSON 형태로 생성된 메시지 형태는 다음과 같다. 
{"aps":{"sound":"default","alert":"My alert message","badge":45}}

 
 
⑥ APNS에 푸시 요청하기 
Provider는 APNS가 요구하는 프로토콜을 따라 데이터를 전송해야 한다. 프로토콜은 APNS에 전송하는 Push 메시지 포맷을
참고하자.  이 메시지 포맷과 APNS 의 요구사항에 맞게 구현한 javapns라는 오픈소스 라이브러리를 사용하여 APNS에 푸시를
요청해보자 

javapns를 사용하기 위해서 아래의 의존 라이브러리를 가지고 있어야한다.
commons-lang-2.4.jar   // String클래스 등의 유틸리티 기능을 제
commons-io-1.4.ja
bcprov-jdk16-145.jar    // Bouncy Castle 단체의 암호화 관련 api
log4j-1.2.15.ja

 javapns 오픈소스 라이브러리 :  http://code.google.com/p/javapns/ 
Legion of the Bouncy Castle 사이트 : http://www.bouncycastle.org/java.html


다음은 javapns 라이브러리를 사용하여 Provider 에서 APNS 에 푸시를 요청하는  예이다. 
String password = "1q2w3e4r";
String certicatePath = "/home/CertificateSR/Interesting_Dev_Cert_pass.p12";
String apnsAddress = "gateway.sandbox.push.apple.com";
String apnsPort = "2195";

try {
        PayLoad payload = new PayLoad();
                                
        payload.addAlert(pushMessage.getAlert());
        payload.addBadge(pushMessage.getBadge());
        payload.addSound(pushMessage.getSound());
                                
        HashMap customMap =  (HashMap) pushMessage.getCustomData(); 
        Iterator iterator = customMap.keySet().iterator();
        
        while (iterator.hasNext()) {
                String key = iterator.next();
                payload.addCustomDictionary(key, customMap.get(key));
        }
                                
        PushNotificationManager manager = PushNotificationManager.getInstance();
                        
        manager.addDevice("myIphone", pushMessage.getDeviceToken());
        
        Device device = PushNotificationManager.getInstance().getDevice("myIphone");
        manager.initializeConnection( apnsAddress, 
                                          Integer.parseInt(apnsPort), 
                                          certicatePath, 
                                          password,                                                                            
                                          SSLConnectionHelper.KEYSTORE_TYPE_PKCS12);
        manager.sendNotification(device, payload);

        manager.stopConnection();
        manager.removeDevice("myIphone");
                                
} catch (Exception e) {
        e.printStackTrace();
}

 


⑦  애플리케이션측에서 Push를 수신하기 위해서  UIApplicationDelegate 프로토콜의 메소드를 구현 

(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        // 배지넘버 초기화
        application.applicationIconBadgeNumber = 0;

        NSDictionary *aps = 
          [launchOptionsobjectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
        
        // 애플리케이션을 처음실행 : RemoteNotification을 등록함
        if (aps == nil) {       
                [[UIApplication sharedApplication]              
                                                registerForRemoteNotificationTypes:
                                                        (UIRemoteNotificationTypeAlert | 
                                                         UIRemoteNotificationTypeSound | 
                                                         UIRemoteNotificationTypeBadge)

                ];
                        
        } else {
                // 애플리케이션이 원격 통보에 의해 실행됐음
                // alert 추출 
                NSString *alert = [aps objectForKey:@"alert"];
                // custom 데이터 추출 
                NSString *pushSeq = [userInfo objectForKey:@"pushSeq"];
        }

        return YES;

}

// RemoteNotification 등록 성공. deviceToken을 수신
(void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
        // Provider에게 DeviceToken 전송
        //[service registDeviceToken:[deviceToken description]];
}


// APNS 에 RemoteNotification 등록 실패
(void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
        NSLog(@"fail RemoteNotification Registration: %@", [error description]);
}


// 애플리케이션 실행 중에 RemoteNotification 을 수신
(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
        // push 메시지 추출
        NSDictionary *aps = [userInfo objectForKey:@"aps"];
        // alert 추출 
        NSString *alert = [aps objectForKey:@"alert"];
        // custom 데이터 추출 
        NSString *pushSeq = [userInfo objectForKey:@"pushSeq"]; 
}


RemoteNotification이 성공적으로 등록되면, 응답으로 호출되는 didRegistForRemoteNotificationWithDeviceToken 메시지의 파라미터에
DeviceToken 값이 담겨져 온다. 이  Token의 값은 다음과 같은 형태를 갖는다.  

<12345678 12345678 12345678 12345678 50cfb1 3f5febbe497df>

APNS에 전송하기 하기 위해서는  애플리케이션 또는 Provider 측에서 <, >, 공백문자를 제거해야 한다는 것을 잊지 말자!! 

 지금까지 애플 푸시서비스에 대해 알아보았다.  사실 푸시 서비스를 이용하는 과정은 그렇게 어렵지 않다. 오히려 개발자 포탈에서
푸시 서비스 권한을  설정하고, 키체인에서 인증파일을 생성하는 과정이 번거롭고... 푸시를 받을 앱이 실행중일 때와 실행중이지 
않을 때 푸시 수신 시 동작방식을 달리 처리해줘야 하는 것이 조금 번거로울 뿐이다.  다음 포스팅에서는 실제 푸시메시지 포맷에 
대해서 좀더 자세히 알아보자.
 



'아이폰' 카테고리의 다른 글

NSNotification  (1) 2011.04.29
푸시 메시지 포맷  (0) 2011.04.07
애플 푸시 서비스  (0) 2011.04.07
Sprite Sheet 제작툴 Zwoptex  (0) 2011.03.16
애플리케이션 응답성 향상을 위해 동시성 사용하기  (0) 2011.03.15
OpenGL ES 게임 프레임웍  (0) 2011.02.14

리플렉션으로 Getter 와 Setter 검사하기

언어로그/Java 2011. 4. 7. 20:35


리플렉션을 사용하여 해당 클래스가 어떤 getter와 setter를 갖는지 검사하는 예를 알아보자.


클래스에 getter와 setter 메서드만을 검사할 수는 없으며,  모든 메서드들을  스캔하여 getter인지 setter인지를 확인해야 한다.
getter와 setter 메서드가 나타내는 특성은 다음과 같다. 

Getter 는  “get” 이라는 이름으로 시작하며, 파라미터가 없고 하나의 값을 반환한다.
Setter 는  “set” 이라는 이름으로 시작하며, 하나의 파라미터를 취한다. 

몇몇 setter는 값을 반환하지 않을 수도 있고 또 어떤 것은 집합값을 반환하거나 어떤 것들은 메서드 chaining을 위해
값을 반환할 수 있기 때문에  setter의 반환타입에 대한 가정을 해서는 안된다.
다음은 getter와 setter를 찾아 출력하는 예이다.  

package com.tistory.hiddenviewer.reflection.executor; import java.lang.reflect.Method; import com.tistory.hiddenviewer.reflection.Board; public class GetterSetterPrinter { public static void main(String[] args) { printGettersSetters(Board.class); } public static void printGettersSetters(Class aClass){ Method[] methods = aClass.getMethods(); for(Method method : methods){ if(isGetter(method)) { System.out.println("getter: " + method); } if(isSetter(method)) { System.out.println("setter: " + method); } } // for } public static boolean isGetter(Method method){ // get 으로 시작하지 않으면 반환 if(!method.getName().startsWith("get")) { return false; } // 파라미터가 있으면 반환 if(method.getParameterTypes().length != 0) { return false; } // 반환값이 없으면 반환 if(void.class.equals(method.getReturnType())) { return false; } return true; }

public static boolean isSetter(Method method){ // set 으로 시작하지 않으면 반환 if(!method.getName().startsWith("set")) { return false; } // 파라미터가 개수가 1이 아니면 반환 if(method.getParameterTypes().length != 1) { return false; } return true; } }



출력결과


 


'언어로그 > Java' 카테고리의 다른 글

Inner Class(내부 클래스)  (0) 2011.04.12
컬렉션(Collection)  (0) 2011.04.11
리플렉션으로 Getter 와 Setter 검사하기  (0) 2011.04.07
(2) 리플렉션(Reflection) 사용하기  (0) 2011.04.07
(1) 리플렉션(Reflection)  (3) 2011.04.07
예외처리 (Exception Handling)  (0) 2011.03.24

(2) 리플렉션(Reflection) 사용하기

언어로그/Java 2011. 4. 7. 19:53



지난 포스팅에 이어 리플렉션을 사용하여 객체를 생성하고, private 필드에 접근하는 방법 그리고  메서드를 호출하는
방법에 대해 알아보자.



1. 리플렉션을 사용하여 객체생성
리플렉션을 사용하여 런타임에 클래스에 생성자들을 검사하고,  생성자 객체를 통해 객체를 생성하는 과정을 알아보자.
아래와 같이 3가지 단계를 거치게 되며 가장 먼저 클래스의 생성자 객체  java.lang.reflect.Consturctor 를 얻어야 한다. 


1.1  Constructor 객체 획득하기
다음과 같이 Class 객체로부터 Constructor 클래스를 얻는다. 
// 생성자 목록 얻기 
Class aClass = ... // 이전에 얻은 클래스 객체
Constructor[] constructors = aClass.getConstructors();


Constructor[] 배열은 클래스에 선언된 모든 public 생성자의 Constructor 인스턴스를 가집니다.  특정한 파라미터를 갖는
특정한 생성자는 다음과 같이 얻을 수 있다. 
// 특정 파라미터를 갖는 생성자 얻기 
Class aClass = ... // 이전에 얻은 클래스 객체
Constructor constructor =
        aClass.getConstructor(new Class[]{String.class});


위 예는 하나의 String 타입 파라미터를 갖는 생성자를 반환하는데, 일치하는 파라미터를 갖는 생성자가 없으면 
NoSuchMethodException 예외가 발생한다.


1.2 Constructor 파라미터 얻기  
 다음과 같이 생성자에 포함된 파라미터 타입 목록을 얻을 수 있다.
// 생성자의 파라미터 타입목록 얻기
Class aClass = ... // 이전에 얻은 클래스 객체
Class[] parameterTypes = constructor.getParameterTypes();


1.3 Constructor 객체를 사용하여 객체 생성하기
다음과 같이 생성자 객체로부터 객체를 생성한다. 
// 하나의 String 파라미터를 갖는 생성자를 얻는다. 
Constructor constructor = MyObject.class.getConstructor(String.class);
// 생성 
MyObject myObject = (MyObject)constructor.newInstance("constructor-arg1");

Constructor.newInstance() 메서드는 선택적인 개수의 파라미터를 취한다.  하지만 반드시 해당 생성자에 맞는 개수와 타입의 
파라미터를 제공해야 한다는 것에 주의하자.
 


 

2. 리플렉션을 사용하여 Field 에 접근하기
리플렉션을 사용하여 클래스의 모든 멤버 변수를 검사할 수 있으며, 런타임에 값을 얻어오거나 설정할 수 있다.
이때 하나의 프로퍼티 당 하나의 java.lang.reflect.Field 클래스 객체를 사용하게 된다. 

2.1 Field  객체 얻기 
다음과 같이 Field 객체를 얻는다. 
// 클래스에 선언된 public 속성의 Field 객체얻기
Class aClass = ... // 이전에 얻은 클래스 객체
Field[] fields = aClass.getFields();


Field[] 배열은  클래스에 선언된 각 public field  당 하나의 Field 객체를 갖는다.(public field 만을 갖는다는 것에 주의)
접근하려는 필드의 이름을 안다면,  다음과 같이 접근할 수  있다. 
// Field에 접근하기 
Class  aClass = MyObject.class
Field field = aClass.getField("someField");


위 예제는 아래 MyObject 에 선언된 someField 에 대응하는 Field 인스턴스를 반환합니다. 
public class MyObject{
  public String someField = null;
}

 getField() 메서드의 파라미터에 해당하는 이름의 필드가 클래스에 존재하지 않으면  NoSuchFieldException 예외가 발생한다.


2.2 Field 이름 얻기 
Field 인스턴스를 획득하면, 다음과 같이 Field.getName()을 사용하여 이름을 얻을 수 있다.
// Field 이름 얻기
Field field = ... // Field 객체를 얻는다. 
String fieldName = field.getName();


2.3 Field  타입 얻기
Field.getType() 메서드를 사용하여 필드의 타입을 얻을 수 있다. 
// Field 타입 얻기 
Field field = aClass.getField("someField");
Object fieldType = field.getType();


2.4 Field 값 조회하고 설정하기 
Field 에 대한 참조를 얻게되면, Field.get(), Field.set() 메소드를 사용하여 값을 얻거나 설정할수 있다.
// Field 값 설정하고 조회하기
Class  aClass = MyObject.class
Field field = aClass.getField("someField");
MyObject objectInstance = new MyObject();
Object value = field.get(objectInstance);
field.set(objetInstance, value);

field.get(), set() 메서드에는 해당 필드를 소유하는 객체가 인자로 전달되야 하며 만일 static 필드라면 null을 전달한다.
 
  
2.5 Private Field 에 접근하기 
클래스의 Private 필드는 외부클래스에서 접근 할 수 없지만,  리플렉션을 사용하면 접근이 가능하다.  캡슐화를 깨는 동작일 
수 있지만, 단위테스트와 하이버네이트와 같은 프레임워크에서 유용하게 사용되기도 한다. 
private 필드에 접근하기 위해서는 Class.getDeclaredField(String name)와 Class.getDeclaredFields() 메서드를 사용한다. (Class.getField(String name) 와 Class.getFields() 메서드는 public 필드만을 반환한다.)

다음은 private field에 접근하는 예이다. 

Board board = new Board();
board.setContents("test contents...");
		
Field field = cls.getDeclaredField("contents");
field.setAccessible(true);
String contents = (String) field.get(board);
		
System.out.println("Private Contents Field: " + contents);

Field 객체의 setAccessible(true)를 호출하지 않고, private  필드값을 조회하려고 하면 IllegalAccessException 예외가 발생한다. 





3. 리플렉션을 사용하여 Method 호출하기
 java.lang.reflect.Method 클래스를 사용하여  메서드를 검사하고 호출 하는 방법을 알아보자.



3.1 Method 객체 얻기  
Method 클래스를 다음과 같이 획득한다.  
// Method 객체목록 얻기 
Class aClass = ... // 이전에 얻은 클래스 객체
Method[] methods = aClass.getMethods();


역시 클래스에 선언된 public 메서드당 하나의 Method 인스턴스를 갖으며, 메서드의 구체적인 파라미터 타입들을 알고 있으면
해당 메서드의 인스턴스를 얻을 수 있다. 다음은 String 파라미터 하나를 갖는 doSomething 메소드의 Method 객체를 얻는다.
// 파라미터를 갖는 메소드의 Method 객체 얻기 
Class aClass = ... // 이전에 얻은 클래스 객체
Method method = aClass.getMethod("doSomething", new Class[]{String.class});


만일 일치하는 메서드가 없으면, NoSuchMethodException이 발생한다. 파라미터가 없는 메서드를 얻기 위해서는 파라미터
배열 대신 null을 전달한다.
// 파라미터가 없는 메서드의 Method 객체 얻기 
Class aClass = ... // 이전에 얻은 클래스 객체
Method method = aClass.getMethod("doSomething", null);



3.2 Method 파라미터와 반환값 얻기
다음과 같이 해당 메서드의 파라미터들을 얻을 수 있다.
// 메서드의 파라미터 타입목록 얻기 
Method method = ... //
Class[] parameterTypes = method.getParameterTypes();

메서드의 반환타입은 다음과 같이 접근한다.
// 메서드의 반환값 타입 얻기 
Method method = ... //
Class returnType = method.getReturnType();



3.3  Method 객체를 사용하여 메서드 호출하기(Invoking)
다음과 같이 메서드를 호출할 수 있다.
// 메서드 호출 
Method method = MyObject.class.getMethod("doSomething", String.class);
Object returnValue = method.invoke(null, "parameter-value1");

invoke() 메소드에는 호출하기를 원하는 객체를 전달하며, static 메서드이면 null을 대신 전달한다.
Method.invoke(Object target, Object...parameters) 메서드는 선택적인 개수의 파라미터를 취하지만,  메서드가 필요로 하는
정확한 개수의 파라미터를 전달해야 한다. 


3.4 Private Method 에 접근하기 
Private Field 에 접근하는 것과 유사하게 Class.getDeclaredField(String name)와 Class.getDeclaredFields() 메서드를 사용한다. 
(Class.getField(String name) 와 Class.getFields() 메서드는 public 필드만을 반환한다.)


'언어로그 > Java' 카테고리의 다른 글

컬렉션(Collection)  (0) 2011.04.11
리플렉션으로 Getter 와 Setter 검사하기  (0) 2011.04.07
(2) 리플렉션(Reflection) 사용하기  (0) 2011.04.07
(1) 리플렉션(Reflection)  (3) 2011.04.07
예외처리 (Exception Handling)  (0) 2011.03.24
[Java] 어노테이션 사용하기  (1) 2011.03.19