XE 코어 백업과 복원하기

XE의 백업과 복원은 포장이사로...

XE 코어로 만든 웹사이트를 업그레이드하거나 다른 서버로 이사를 가야 할 때 백업과 복원은 필수입니다. 그런데 FTP 프로그램을 이용해 파일을 하나하나 내려받고 다시 서버에 올린다면 XE 코어는 정상적으로 작동하지 않습니다. 왜냐하면 여러분이 호스팅 서비스를 이용해 계정을 할당 받고 사용하게 되면 아래 그림과 같이 html이라는 사용자 폴더(root, 서버환경에 따라 public_html 또는 www라는 폴더를 사용)를 이용하게 되는데 이 폴더 안에는 여러분이 사용중인 폴더와 XE 코어가 만들어서 사용하는 files 디렉터리가 함께 있기 때문입니다. files 디렉터리는 xe 디렉터리 안에 있고 웹사이트의 운영내용 및 설정 파일들을 XE 코어가 수시로 점검하고 수정하고 첨부 파일들을 저장하는데 사용하고 있습니다. 이 files 디렉터리는 FTP를 이용해 다운로드 하거나 이동, 복사 할 수 없습니다.

웹사이트 운영시 함께 할당 받는 또 하나는 데이터베이스(DB)입니다. DB는 쉽게 말해 엑셀문서를 닮았다고 설명드린 적이 있습니다. 데이터 하나하나를 쪼개어 저장하는 아주 강력한 친구입니다. 하지만 무척 까탈스러운 친구라 말붙이기도 어렵고 접근하기 조차 쉽지 않습니다. 하지만 XE 코어를 도와 항상 열심히 일하는 아주 근면성실한 친구이기 때문에 믿음직스럽습니다.

image

웹사이트를 백업한다는 것은 보통 html 폴더(root) 전체를 하나의 파일로 묶는 것을 말하는데 반드시 잊지말아야 하는 것은 DB에 저장되어 있는 자료 역시 압축파일로 받아 두어야 합니다. 만약 DB를 백업 받아두고 다시 복원해 주지 않으면 XE 코어는 정상적으로 작동하지 않습니다. 따라서 계정의 백업은 디렉터리의 백업과 DB 백업으로 나누어 작업하게 됩니다. XE 코어와 DB의 상호작용은 관리자 페이지에 있는 캐시파일 재생성 버튼으로 연동을 유지하게 됩니다. 이렇게 XE 코어를 백업 받거나 복원하기 위해서는 반드시 포장을 잘 해 두어야하며 또는 부득이하게 다른 서버 계정으로 이사를 가야한다면 반드시 포장이사를 맡겨야 합니다...^^ (필요에 따라서는 XE 디렉터리만 포장해도 상관 없으며 index.html 문서는 별도로 작성하고 필요한 파일만 가져가도 됩니다.)


1. 파일 및 디렉터리의 백업과 복원

계정의 백업 작업은 FTP를 이용해서 할 수 없습니다. 오직 SSH 또는 Putty 프로그램을 이용해 서버에 접속하고 텔넷 명령어를 사용해서 백업 및 복원 명령어를 입력해야 합니다. SSH를 이용해 서버에 접속하게 되면 최상위 디렉터리 바깥에서 디렉터리 전체를 하나의 파일로 묶어주는 명령어를 입력합니다. 최상위 디렉터리(root)는 서버 환경에 따라서 html, www 또는 public_html로 사용할 수 있습니다. ls 명령어를 입력해 보고 html, www, public_html 디렉터리만 보인다면 루트 디렉터리 바깥에 있는 것이 맞습니다.

tar -cvfpz backuphtml.tar.gz html

html은 압축하고자 하는 디렉터리의 이름이고 backuphtml.tar.gz는 백업 작업의 결과로 만들어질 압축파일 이름입니다. tar의 옵션의 c는 파일 및 디렉터리를 하나로 묶어 새 저장 파일을 만들라는 뜻입니다.

이렇게 만들어진 backuphtml.tar.gz 압축파일은 루트 디렉터리 바깥에 있고 서버에 그대로 두어도 상관은 없지만(서버용량이 가능하다면) 추후에 작업을 위해서는 FTP 프로그램을 이용해 다운로드 받아 두는 것이 좋습니다. 그리고 압축파일 네이밍을 할 때 백업일자를 같이 써주면 언제 백업 받아둔 것이지 쉽게 확인할 수도 있겠지요. 예) backuphtml_20110630.tar.gz

디렉터리의 백업파일을 복원하는 것은 마찬가지로 루트 디렉터리 바깥에서 아래의 명령어를 입력하는 것으로 쉽게 작업하실 수 있습니다. 이번에는 옵션에 c가 아니라 압축을 해제하는 옵션 x를 주게 됩니다. 만약 다른 계정에서 해제하려면 FTP를 이용해 백업 받은 압축 파일을 미리 업로드 해 두어야 합니다.

tar -xvfpz backuphtml.tar.gz

압축이 풀리게 되면 자동으로 html 디렉터리 안에 모든 파일이 원상태로 복구됩니다. 만약 다른 계정에서 html 디렉터리가 없다면 html 디렉터리를 만들고 압축된 파일을 그 안에 풀어 놓게 되는데 이때 html 디렉터리 안의 모든 내용을 새로운 계정 환경의 루트(root) 디렉터리 안으로 옮겨 주어야 합니다.(※ 아래 "다른 서버 계정으로 이사가기" 참고) 옵션 -p는 모든 퍼미션(권한) 정보를 포함하여 압축을 하기도 하고 해제하기도 하지만 만약을 위해 chmod 707 xe 명령을 실행하여 xe 디렉터리의 권한설정을 다시한번 실행해 주어도 좋습니다.

chmod 707 xe

TIP - 간혹 xe 디렉터리 안에 xe가 만들고 사용하는 files 디렉터리의 권한 문제로 오류가 발생하기도 합니다.
이런 경우 chmod -R 707 xe/files 명령어로 files 디렉터리를 포함하여 하위 폴더까지 권한을 재설정해 줍니다. 기타 오류에 대해서는 이용중인 호스팅 웹서버의 root 권한이 필요한 경우가 있습니다. 이런 경우 서비스 제공 회사와 상의하는 것이 바람직합니다.


2. 데이터베이스(DB)의 백업과 복원

DB의 백업은 파일을 압축하는 방법이 아니라 DB에서 사용할 수 있는 sql 문서를 한장 만들어 받아 두는 것입니다. DB는 까탈스러운 친구라고 했죠? 자료를 좀 백업해 달라고 요청을 하면 달랑 서류 한장만 넘겨 줍니다...^^ 그런데 이것을 압축 파일이라고도 부르는 이유는 모든 내용을 텍스트로만 작성하기 때문에 압축한다는 의미로 표현하는 것입니다. CD를 굽는다고 표현하는 것과 같습니다. 이 문서는 나중에 복원을 할때도 DB에게 보여주기만 하면 된답니다. DB를 백업하는 명령어는 아래와 같습니다.

mysqldump -u 아이디 -p 디비네임 > backupdb.sql

DB의 본래 이름은 데이터베이스 관리 시스템(Database Management System, DBMS)인데 이 친구가 쓰는 말은 SQL(Structured Query Language, 구조화 질의어)이라는 언어를 씁니다. 좀 유별납니다...^^ 그래서 정중하게 mysql님 DB를 좀 출력(dump)해 주시죠!(dump is a Unix program used to backup file systems.) 라고 해야 합니다. 명령어가 아닌 정중한 부탁을 해야 합니다. 그러면 backupdb.sql 문서를 내놓습니다. 이 문서 안에는 XE 코어에서 사용하는 테이블의 종류와 갯수 및 내용(스키마), 그리고 그동안 누가 로그인해서 어떤 글들을 썼는지, 그리고 어떤 첨부파일이 어느 디렉터리에 보관되고 있었고, 레이아웃은 어떤 것을 자주 쓰는지, 메뉴는 어떤 것들이 있는지 하는 아주 소소한 것들까지 적어 놓은 가계부와 같습니다.

DB를 복원하려면 위에서 받아둔 sql 문서를 다시 DB에게 보여주기만 하면 됩니다. 이때는 화살표를 반대로 꺽어주면 되죠!

mysql -u 아이디 -p 디비네임 < backupdb.sql

출력(dump) 해 달라는 부탁은 할 필요없습니다. 화살표만 mysql 쪽으로 꺽어서 sql 문서를 보여주기만 하면 됩니다. -p 다음에 비밀번호가 없는 것은 나중에 password: 라고 입력을 기다리기 때문에 그때 입력하면 됩니다. 아이디와 디비네임은 여러분의 계정 아이디와 DB의 네임을 입력하시면 됩니다.(서버에 따라 계정의 아이디와 DB네임, 비밀번호가 다를 수 있습니다.)

TIP - 아이디와 옵션 -u는 붙여 쓰기도 합니다. 즉 옵션 -u 다음의 문자열은 DB의 아이디로 인식합니다.


캐시파일 재생성 하기

루트 디렉터리와 그 안의 모든 파일들을 백업하고 DB 역시 백업한 후에 다시 복원 작업을 거치게 되면 반드시 XE 관리자로 로그인 한 후에 캐시파일을 재생성 해 주어야 합니다. 만약 관리자로 로그인이 되지 않는 경우, 하얀 백지로 웹사이트가 표시 된다면 xe/files/cache 디렉터리를 삭제(rm -rf cache)한 후에 아래 관리자 주소를 웹브라우저 주소 입력칸에 직접 입력하여 관리자로 로그인 합니다. /xe/는 코어 설치폴더 이름입니다.

  • http://웹사이트 주소/xe/?module=admin
  • http://웹사이트 주소/xe/?module=admin&act=dispAdminConfig

관리자 로그인 후 캐시파일을 재생성하게 되면 XE 코어가 정상적으로 작동하게 됩니다.

TIP - 텔넷 명령어 rm -rf cache 로도 xe/files/cache 디렉터리가 삭제되지 않으면 nobody 권한문제 때문입니다. 호스팅 회사에 문의하여 삭제를 요청하는 것이 좋습니다. 간혹 php 문서를 활용한 권한수정도 통하지 않는 경우가 있습니다. 이런 경우 SuperUser 권한으로 nobody권한을 다시 조정해 줘야 합니다.


다른 서버 계정으로 이사가기

위의 과정은 같은 서버의 계정에서 필요에 따라 계정을 백업하거나 복원할 때 사용하는 방법입니다. 그럼 다른 서버의 계정으로 이사를 가야 한다면 어떻게 할까요?

image

백업된 디렉터리 압축파일과 DB에게서 받아 두었던 sql 문서를 복원하는 방법은 위와 동일합니다. 다만, 새로운 서버의 설정값들이 변경되기 때문에 이에 따른 수정할 부분이 추가됩니다. 우선 최상위 디렉터리(root)의 이름이 html이 아니라고 한다면 FTP로 업로드한 후 압축을 풀었을 때 html 디렉터리 안에 모든 파일과 폴더가 풀어져 있습니다. 이것을 새로 이사 간 서버의 루트 디렉터리(public_html 또는 www) 안으로 옮겨 주어야 합니다. 이때 사용하는 명령어는 아래와 같습니다.

mv html/* public_html

html 안의 모든(*) 파일과 폴더를 public_html 디렉터리 안으로 이동하라!(move) 는 뜻입니다. 완료가 되면 FTP를 이용해서 xe 디렉터리 안에 .htaccess 파일이 제대로 있는지도 확인해 보시고 재 확인차 chmod 707 xe 명령을 이용해 권한설정을 한번 더 확인해 줍니다.

TIP 1 - 리눅스 명령어 mv 에서 와일드카드(*)를 사용하면 도트(.)로 시작되는 파일이름(숨김파일)을 포함하도록 확장되지 않습니다. 이런경우 [mv 디렉터리/* 이동할 디렉터리] 와 [mv 디렉터리/.htaccess 이동할 디렉터리] 이렇게 2번 나누어서 실행하거나 또는 [mv 디렉터리/{*,.htaccess} 이동할 디렉터리/]처럼 여러 대상을 포함시켜 이동할 수 있습니다. 아래 예제와 같이 명령어를 실행하면 .htaccess 파일도 함께 이동할 수 있습니다.
예제) mv html/{*,.htaccess} public_html/
또는 mv html/{*,.*} public_html/ 도 같은 역할이지만 서버환경 옵션설정에 따라 허용되지 않을 수 있습니다.
숨김파일은 ls -a 옵션을 사용하여 확인할 수 있습니다.

TIP 2 - 숨김파일까지 한꺼번에 이동하려면 shopt -s dotglob 명령어를 우선 실행한후 mv 명령어를 이용해 와일드카드(*)를 사용하면 한번에 이동이 가능합니다.
shopt -s dotglob
mv html/* public_html/

그리고 다른 서버로 이사를 간 경우에는 XE 코어가 이전 서버에서 사용했던 xe/files/cache 디렉터리가 더이상 필요없습니다. 새로운 설정값을 다시 만들어야 하기 때문에 rm -rf cache 명령을 이용해 cache 디렉터리를 완전히 삭제해야 합니다.

★중요★
새로운 서버의 아이디와 DB네임으로 DB를 복원한 후에는 반드시 /xe/files/config/db.config.php 문서를 FTP를 이용해 서버에서 내려받고 그 안에 적힌 이전 서버의 내용을 새로운 서버의 아이디와 비밀번호, DB네임, 사이트 주소 등을 수정하여 XE 코어에게 이곳은 새로운 서버라는 것을 알려 주세요. 만약 아래 내용처럼 수정을 하여 업로드 한 후에도 문제가 발생하게 되면 설정 중에 localhost 등과 같이 기타 서버에서 사용하는 설정 방법을 호스팅사에 문의하여 수정해야 합니다.

db.config.php 파일 수정 :

<?php if(!defined("__ZBXE__")) exit();
$db_info->master_db = array('db_type' => 'mysql','db_port' => '3306','db_hostname' => 'localhost','db_userid' => 'DB아이디','db_password' => 'DB비밀번호','db_database' => 'DB이름','db_table_prefix' => 'xe_');
$db_info->slave_db = array(array('db_type' => 'mysql','db_port' => '3306','db_hostname' => 'localhost','db_userid' => 'DB아이디','db_password' => 'DB비밀번호','db_database' => 'DB이름','db_table_prefix' => 'xe_'));
$db_info->default_url = 'http://홈페이지 URL/xe/';
$db_info->lang_type = 'ko';
$db_info->use_rewrite = 'Y';
$db_info->time_zone = '+0900';
?>
  • 'db_hostname' => 'localhost'
  • 'db_userid' => 'DB아이디'
  • 'db_password' => 'DB비밀번호'
  • 'db_database' => 'DB이름'

db.config.php 파일을 수정한 후 다시 업로드하여 원본 파일을 덮어씌운 후에 관리자로 로그인하게 되면 반드시 캐시파일을 재생성하여 변경된 서버 계정의 환경 설정값들을 새로운 캐시파일로 생성하도록 하고 XE 코어와 DB가 연동하게 되면 XE 포장이사는 무사히 마치게 됩니다. 만약 /xe/files/cache 디렉터리가 rm -rf 명령어로도 삭제되지 않는다면 호스팅 회사에 문의하여 삭제를 요청하시면 곧바로 삭제를 해 줍니다.


포장이사 도움말

XE 코어의 백업과 복원, 서버 계정의 이전은 그리 쉬운 작업은 분명히 아닙니다. 왜냐하면 같은 계정 안에서의 백업 및 복원작업은 상대적으로 문제가 적은 반면에 다른 서버의 계정으로 이사를 가는 것은, 이사라는 일이 늘 그렇듯 그릇이 깨지기도 하고 가구에 스크레치가 나기도 하는 등 새로운 문제점들이 늘 발생할 수 있습니다. 새로운 서버의 설정과 운영에 따라 문제가 발생된 경우에는 호스팅사의 도움을 요청하는 것이 바람직합니다.

★ 무작정 XE를 백업 받고 새로운 서버로 이사를 가기보다는 호스팅 회사에 문의를 하여 백업 받은 XE 코어를 복원할 수 있는지에 대한 여부와 환경 설정에 필요한 자문을 구하는 것이 바람직합니다. 일반적인 경우 호스팅 서비스 회사는 이에 대한 안내를 자세히 해주며 백업파일이 있는 경우 호스팅 회사가 무료로 직접 압축파일을 해제하고 복원 해 주기도 합니다.

일반적인 문제발생의 원인은 이전 서버에서 파일 및 폴더을 압축할 때 nobody 권한에 따른 설정들이 새로운 서버에서 제대로 적용되지 않기 때문에 작은 문제들이 발생 되곤 한답니다.(호스팅 회사의 안내) 따라서 XE 코어 운영에 최적화된 좋은 호스팅 서비스 회사를 선택하고 XE 코어의 백업 파일을 제대로 포장한 후에 서버 이전을 진행하는 것이 가장 확실한 방법임을 추천합니다.
(※ 위와 같은 기본적인 텔넷 명령어들이 받아들여지지 않는 서버 계정은 추천하지 않습니다.)



출처 : http://www.xeschool.com/xe/step1_52

사용자를 추가하기 위해서는 반드시 루트 계정이 있어야 한다.

1. grant all privileges on *.* to 'testuser'@'localhost'

   identified by '설정패스워스' with grant option;

2. grant all privileges on *.* to 'testuser'@'%'

   identified by '설정패스워스' with grant option;

3. grant reload,process on *.* to 'testuser'@'locahost';

4. grant usage on *.* to 'testuser'@'locahost';

1번과 2번의 경우는 슈퍼 계정!! root와 같은 모든 권한을 가지는 계정을 가지게 된다.

1번 계정은 로컬호스트에서 접속을 할 경우에만 사용되는것이고,

2번 계정은 다른 호스트에서 접속을 하기 위해 사용된다.

testuser란 계정으로 어디에서든지 접속을 하려면 testuser이름으로 1,2번 계정을 모두 가지고 있는 것이 필요하다.

3번 계정은 패스워드가 정의되어있지 않다.

이것은 로컬호스트에서 reload와 process관리 권한을 가지고있다.

mysqladmin reload,mysqladmin refresh, mysqladmin flush-xxx, mysqladmin processlist 도 실행할수 있다고 한다. 하지만 어떤 데이터 베이스에도 접급할수있는 권한은 없다!

나중에 grant명령문을 입력하새ㅓ 권한을 추가할수도 있다.

4번 계정은 3번가 마찬가지 이지만 아무런 권한이 없는 계정을 만든것이다.

USAGE가 그런 뜻을 의미하는데, 이것은 모든 글로번 권한을  'N'으로 설정한 것이라고 한다.

이계정에 특정 권한을 나중에 승인할 것이라는 가정을 한다.

계정을 생성하는 또 한가지 방법은 INSERT 문을 사용하는 것이다.

1. insert into user values('locahost','계정명',password('패스워드'),

   'y','y','y','y','y','y','y','y','y','y','y','y','y','y','y','y');

2. insert into user values('%','계정명',password('패스워드'),

   'y','y','y','y','y','y','y','y','y','y','y','y','y','y','y','y');

3. insert into user set Host='locahost',User='계정명',Reload_priv='y',Process_priv='y';

4. insert into user(Host,User,Password) values('locahost','계정명','');

5. flush privileges;

1,2,3,4번 계정의 의미는 위에서 grant를 이용하여 만든 계정과 의미가 같다.

5번의 flush privileges; 는 서버가 grant 테이블을 다시 읽어 오도록 만들기 위해서 이다.

그렇지 않으면 서버를 재 구동 시키기 전에는 변경 사항이 적용되지 않는다.

하지만 grant를 사용하는 경우는 flush pricileges가 필요 없다.

1,2번의 insert문의 패스워드 입력에서 password('패스워드')라고 사용한것은 패스워드를 암호화 하기 위해서 이다. grant명령문은 알아서 암호화가 된다!

이번에는 데이터베이스 접근 권한을 설정해보자.

1. grant select, insert, update, delete, create, drop

   on bankaccount.*

   to 'custom'@'localhost'

   identified by 'obscure';

2. grant select, insert, update, delete, create, drop

   on expenses.*

   to 'custom'@'whitehouse.gov'

   identified by 'obscure';

3. grant select, insert, update, delete, create, drop

   on customer.*

   to 'custom'@'server.domain'

   identified by 'obscure';

1번 계정은 bankaccount 데이터 베이스에 접근할 수는 있으나, 로컬 호스트에서만 가능하다.

2번 계정은 expenses 데이터 베이스에 접근할 수 있으나, 호스트 whitehouse.gov에서만 가능하다.

3번 계정은 customer 데이터 베이스에 접근할 수 있으나, server.domain에서만 가능하다.

insert를 사용한 방법.

1-1. insert into user(Host,User,Password) values('locahost','custom',password('obscure'));

1-2. insert into user(Host,User,Password) values('whitehouse.gov','custom',password('obscure'));

1-3. insert into user(Host,User,Password) values('server.domain','custom',password('obscure'));

2-1. insert into db

     (Host,Db,User,Select_priv,Insert_priv,Update_priv,Delete_pric,Create_pric,Drop_pric)

     values('localhost','bankaccount','custom','y','y','y','y','y','y');

2-2. insert into db

     (Host,Db,User,Select_priv,Insert_priv,Update_priv,Delete_pric,Create_pric,Drop_pric)

     values('whitehouse.goc','expenses','custom','y','y','y','y','y','y');

2-3. insert into db

     (Host,Db,User,Select_priv,Insert_priv,Update_priv,Delete_pric,Create_pric,Drop_pric)

     values('server.domain','customer','custom','y','y','y','y','y','y');

flush privileges;

이것도 마찬가지로 flush pricileges; 를 반드시 해줘야 적용이 된다~

한가지 팁이라면... % 와일드 카드 문자를 사용하여 grant명령문을 입력하면 모든 곳에서 허용이 된다.

예) grant ... on *.* to 'user'@'%.mydomain.com' identified by 'myboss';

   (이렇게 하면 .mydomain.com앞에  뭐가 붙어서 오든지 허용이 된다는 뜻.)

    grant ... on test_%.* to ... identified by ...;

   (이건 데이터베이스 이름이 test_ 로 시작하는 모든것에 사용권한을 부여한다는 뜻이다.)

 원본 레퍼런스 참조.

 

mysql 실행

>mysql -u 계정명 -p 

>비밀번호


>show databases;

>use 데이터베이스명;

>show tables;

>select 속성1, 속성2, ...., 속성n from 테이블명;

>select * from 테이블명;


사용자 계정 추가

>use mysql;

>insert into user(host,user,password) values('localhost','계정명',password('비밀번호'));로컬접근 허용

>insert into user(host,user,password) values('%','계정명',password('비밀번호'));외부접근 허용

>flush privileges;


db 생성후 db에 계정연결

>grant all privileges on DB명.* to 계정명@localhost identified by '비밀번호' with grant option;

>flush privileges;



- 특정 사용자의 외부접근을 허용 (이미 만들어진 계정에서)

>update user set host='%' where host='localhost' and user='계정명';

>flush privileges;

 작성일 : 12-02-16
 조회 : 1,201  
1. FTP 환경설정파일 수정

~# vi /etc/vsftpd/vsftpd.conf

# 문서하단에 추가

chroot_local_user=YES
chroot_list_enable=YES
chroot_list_file=/etc/vsftpd/chroot_list


2. chroot_list 파일안에, root 추가 (이파일안에 등록된 USER는 상위디렉토리 접근제한을 받지 않는다.)

~# vi /etc/vsftpd/chroot_list

root


vsftp, ftp 사용자 폴더를 제외한 다른 폴더 접근제한

1)파일을 편집
/etc/vsftpd/vsftpd.conf

chroot_local_user=YES 추가

2)ftp demon restart
/etc/init.d/vsftpd restart


*다음의 파일에 등록된 사용자들은 FTP 접속이 불가능
/etc/vsftpd.ftpusers

/etc/vsftpd.user_list

-------------------------------------------------------------------------------

1. ftpusers와 vsftpd.chroot_list 차이 

/etc/ftpusers 과 chroot_list_file=/etc/vsftpd.chroot_list 파일은 용도가 다릅니다. 

* /etc/ftpusers : 이건 접속을 제한하는 것입니다
. 즉, 여기에 등록된 ID는 ftp 접속 자체를 할 수 없습니다. 
* /etc/vsftpd.chroot_list : 여기에 써진 ID는 접속할 수 있습니다. 
다만, 자신의 홈디렉토리를 벗어날 수(상위디렉토리로 갈 수) 없습니다.
 
이를테면 홈이 /home/truefeel/ 일 때 /home/truefeel/ 를 벗어난 /etc, /home, /usr 등을 갈 수 없다는 것입니다. 
이해되시나요? 
보안상 홈보다 상위 디렉토리로 이동하는 것을 제한하는 경우가 많습니다. 

2. chroot_local_user 와 chroot_list_enable 설정을 함께 사용할 때 

제가 쓴 글을 다시 인용하여 설명하겠습니다. 

인용:

3) 사용자가 홈디렉토리를 못 벗어나게 하고 싶는데? 

 /etc/vsftpd.conf에 다음을 추가하면, 모든 사용자는 자신의 홈디렉토리만 접근할 수 있다. 

 chroot_local_user=YES 

 또한 특정 사용자로만 제한을 하고 싶다면 다음과 같이 한다. /etc/vsftpd.chroot_list에는 제한할 
 사용자 ID를 한줄에 하나씩 나열하면 된다. 

 chroot_list_enable=YES 
 chroot_list_file=/etc/vsftpd.chroot_list 

 주의할 것은 chroot_local_user=YES와 chroot_list_enable=YES를 함께 사용할 경우에는 
 /etc/vsftpd.chroot_list에 포함된 사용자 ID만 제한없이 홈디렉토리를 벗어날 수 있다. 
 즉, 반대로 작용한다. 



위의 내용을 설정부분만 뽑으면 이렇습니다. 

1) 조건 1 
코드:

chroot_local_user=NO 
chroot_list_enable=YES 
chroot_list_file=/etc/vsftpd/chroot_list 



이런 경우는 /etc/vsftpd/chroot_list 파일에 설정한 ID는 홈디렉토리를 벗어날 수(즉, 상위로 갈 수) 없습니다. 

2) 조건 2 
코드:

chroot_local_user=YES 
chroot_list_enable=YES 
chroot_list_file=/etc/vsftpd/chroot_list 



이런 경우는 
/etc/vsftpd/chroot_list 파일에 설정한 ID만 홈디렉토리를 벗어날 수(즉, 상위로 갈 수) 있습니다. 
즉, 조건1과는 반대로 되는 것입니다. 


3. 특정 IP를 막을 때 

레드햇의 자체 vsftpd rpm은 tcp wrapper를 통해 IP제한을 할 수 있습니다. 
님의 말씀대로 이걸 이용하거나 iptables를 통해서 막으면 되겠네요.


sudo 권한이 있어야한다.


사용자추가


$ adduser NewId


하면 알아서 비밀번호 설정 가능


그룹생성


$ groupadd NewGroup


새사용자 그룹지정


$ vi /etc/group


NewGroup:x:1002:

이있는데 뒤에다 사용자 아이디를 써준다

NewGroup:x:1002:NewId

그리고 저장.

NewGroupDir이란 디렉토리의 소유권을 NewGroup으로 줌


$chown -R root:NewGroup NewGroupDir/


그리고 폴더 접근권한을 설정한다


$chmod -R 700 NewGroupDir/



접근권한 참고

r : Read  = 4
w : Write = 2
x : eXcute = 1

-rwxrwxrwx   ( 777 )
-r--r--r--  ( 444 )
-rwx--x--x ( 711 )

2~4필드 : 소유주 ( User ) 권한
5~7필드 : 그룹 ( Group )  권한
8~10필드 : 나머지 ( Others ) 권한


 


프로젝트 폴더에서 마우스오른쪽 클릭 -> Refactor -> Rename

오늘은 JNI를 이용한 C모듈을 안드로이드에 적용하는 방법을 설명하겠습니다.

이 부분은 좀 방대한 부분이고, 저 또한 짧은 시간에 글을 적어야 하므로 충분한 캡처를 동반하지 못함을 아쉽게 생각합니다.

 

우선 기본환경은

이클립스  +  NDK r5b + 우분투 11.04 이며, 모듈 컴파일은 Cygwin에서 사용해도 상관 없습니다.

(참고로 저는 윈도우즈 환경에서 우분투를 VM 으로 사용합니다.)

 

작업순서는 다음과 같이 하려 합니다.

 

가. 모듈 컴파일을 위한 NDK  다운로드 및 환경설정

나. *.java 에서 *.h, *.c의 JNI 파일 만드는 방법

다. 안드로이드 프로젝트 내에 JNI 쓰는 방법

 

가. 리눅스에서 NDK  설정

 

1. 우선 NDK 부터 사이트에서 받도록 합니다. 윈도우즈 환경에서 MS cl.exe 컴파일러를 이용해 *.dll 모듈을 만들어도 되지만

 저는 리눅스의 *.so 모듈을 만들기 위해서 리눅스용 버전을 다운로드 받습니다.

http://developer.android.com/sdk/ndk/index.html  

 

2. 다운로드를 받았으면 압축을 해제하고 자신의 홈 계정 디렉토리 밑으로 옮깁니다.

 예) home/maluchi/android-ndk-r5b


maluchi@ubuntu:~/android-ndk-r5b$ ls
GNUmakefile  build               ndk-build  projects  tests
README.TXT   docs                ndk-gdb    samples   toolchains
RELEASE.TXT  documentation.html  platforms  sources
maluchi@ubuntu:~/android-ndk-r5b$ pwd
/home/maluchi/android-ndk-r5b

3. 폴더 위치에 상관없이 ndk-build 명령이 수행되도록 PATH를 다음처럼 추가합니다.

   자신의 홈으로 가서 .bash_profile 파일을 만들어서 다음처럼 추가하고 저장합니다.

   PATH=$PATH:$HOME/android-ndk-r5b

maluchi@ubuntu:~/android-ndk-r5b$ cd
maluchi@ubuntu:~$ ls
android-ndk-r5b          androiddev        workspace  문서      사진
android_gingerbread      examples.desktop  공개       바탕화면  음악
android_gingerbread.tgz  froyo2.2.tgz      다운로드   비디오    템플릿
maluchi@ubuntu:~$ vi .bash_profile 

4. 콘솔에서 source ~/.bash_profile 을 해서 바로 업데이트 하고, 잘 입력이 되었는지 env 명령을 넣어 확인합니다.

maluchi@ubuntu:~$ source ~/.bash_profile 
maluchi@ubuntu:~$ env
......

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/home/maluchi/android-ndk-r5b
....

 

5. 마지막으로 NDK 폴더 내 samples->hello-jni->jni->Android.mk 를 열어 보면 다음과 같은 부분이 있습니다.

LOCAL_MODULE := hello-jni
LOCAL_SRC_FILES := hello-jni.c

 

LOCAL_MODULE 은 모듈명이며, LOCAL_SRC_FILES는 C의 구현파일입니다. 여러개 존재한다면 '\ ' 추가합니다.

차후에 Android.mk를 복사해서 수정할 것입니다.

예)

LOCAL_SRC_FILES := hello-jni.c \

                                    test.c

 

6. Android.mk 파일 내용을 봐 보겠습니다. 리눅스에서 해당 파일을 받기 힘드시면 아래 내용으로 파일을 만들어도 됩니다. 

maluchi@ubuntu:~/android-ndk-r5b/samples/hello-jni/jni$ more Android.mk 
# Copyright (C) 2009 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := hello-jni
LOCAL_SRC_FILES := hello-jni.c

include $(BUILD_SHARED_LIBRARY)

maluchi@ubuntu:~/android-ndk-r5b/samples/hello-jni/jni$

 

이상으로 리눅스에서 모듈을 컴파일을 위한 환경은 완료가 되었습니다.

 

-------------------------------------------------------------------------------------------------

 

나. *.java 에서 *.h, *.c의 JNI 파일 만드는 방법

 

1. 이제 C모듈(*.so)를 java로 가져오는 부분이 필요합니다.  CalcJni.java 라는 파일을 만듭니다.

그리고 다음처럼 추가합니다. 사칙연산을 수행하는 네이티브 메서드를 정의하고 "CalcJni" 라는 C 모듈을 로드하는 역할을 합니다.

01.//package com.maluchi.CalcJNITest;
02.public class CalcJni
03.{
04.public CalcJni(){};
05.public native int Sum(int a, int b);
06.public native int Sub(int a, int b);
07.public native int Mul(int a, int b);
08.public native int Div(int a, int b);
09.public native String message();
10. 
11.static{
12.System.loadLibrary("CalcJni");
13.};}


2. 위의 Java 파일로부터 JNI의 C헤더(*.h) 파일을 만드는 것을 수작업으로 하기에는 번거로운 면이 있습니다.

하지만, Java에서 친절하게도 C헤더 파일을 만들어주는 명령이 있습니다. cmd 창을 하나 띄웁니다.

 

3. 자바 SDK가 설치되었다는 전제하에 해당 경로로 이동하여 *.class 파일을 만듭니다.

 >javac CalcJni.java

 

4. *.h 헤더 파일을 만듭니다.

>javah CalcJni

 

5. 다음처럼 CalcJni.h 헤더가 만들어진 것을 확인할 수 있습니다.

C:\tmp\jni>dir
 C 드라이브의 볼륨: Win7
 볼륨 일련 번호: D037-8597

 C:\tmp\jni 디렉터리

2011-06-16  오후 04:23    <DIR>          .
2011-06-16  오후 04:23    <DIR>          ..
2011-06-16  오후 04:22               425 CalcJni.class
2011-06-16  오후 04:23             1,011 CalcJni.h
2011-06-16  오후 04:22               391 CalcJni.java
               3개 파일               1,827 바이트
               2개 디렉터리  20,543,619,072 바이트 남음

6. CalcJni.h 에 맞춰 CalcJni.c 파일을 만듭니다. 이 때 가장 중요한 메서드 명명 규칙이 있습니다.

javah로 *.h를 만들면 아래처럼 패키지명이 적용되지 않고 만들어집니다.

 JNIEXPORT jint JNICALL Java_CalcJni_Sum(JNIEnv *, jobject, jint, jint);

 

중요한 것은 JNICALL Java_패키지명_클래스명_메서드(...)  규칙으로 적용해야 한다는 것입니다.

실제 안드로이드 패키지를 만드고 그곳에서 C 모듈을 로드한다면 반드시 패키지명_클래스명 을 확인해야 합니다.

그렇지 않으면 에러의 원인이 됩니다. 더불어 JNI에서의 구현부는 반드시 파라미터명을 주도록 되어 있으므로 생략되어 있는 파라미터 변수명들을 넣습니다.

 

 다시 1번에 가서 보시면 //package com.maluchi.CalcJNITest; 주석 처리된 것을 볼수 있습니다.

이는 후에 저 패키지에 자바파일이 들어가게 되고  메서드명들을 아래처럼 모두 바꿀 필요가 있기에 임시 주석처리를 한 것입니다. 

예) JNIEXPORT jint JNICALL Java_com.maluchi.CalcJNITest_CalcJni_Sum(JNIEnv *, jobject, jint, jint);

 

CalcJni.h

01./* DO NOT EDIT THIS FILE - it is machine generated */
02.#include <jni.h>
03./* Header for class CalcJni */
04. 
05.#ifndef _Included_CalcJni
06.#define _Included_CalcJni
07.#ifdef __cplusplus
08.extern "C" {
09.#endif
10./*
11.* Class:     CalcJni
12.* Method:    Sum
13.* Signature: (II)I
14.*/
15.JNIEXPORT jint JNICALL Java_com.maluchi.CalcJNITest_CalcJni_Sum
16.(JNIEnv *, jobject, jint, jint);
17. 
18./*
19.* Class:     CalcJni
20.* Method:    Sub
21.* Signature: (II)I
22.*/
23.JNIEXPORT jint JNICALL Java_com.maluchi.CalcJNITest_CalcJni_Sub
24.(JNIEnv *, jobject, jint, jint);
25. 
26./*
27.* Class:     CalcJni
28.* Method:    Mul
29.* Signature: (II)I
30.*/
31.JNIEXPORT jint JNICALL Java_com.maluchi.CalcJNITest_CalcJni_Mul
32.(JNIEnv *, jobject, jint, jint);
33. 
34./*
35.* Class:     CalcJni
36.* Method:    Div
37.* Signature: (II)I
38.*/
39.JNIEXPORT jint JNICALL Java_com.maluchi.CalcJNITest_CalcJni_Div
40.(JNIEnv *, jobject, jint, jint);
41. 
42./*
43.* Class:     CalcJni
44.* Method:    message
45.* Signature: ()Ljava/lang/String;
46.*/
47.JNIEXPORT jstring JNICALL Java_com.maluchi.CalcJNITest_CalcJni_message
48.(JNIEnv *, jobject);
49. 
50.#ifdef __cplusplus
51.}
52.#endif
53.#endif


CalcJni.c

01.#include <stdio.h>
02.#include "CalcJni.h"
03. 
04.JNIEXPORT jint JNICALL Java_com_maluchi_CalcJNITest_CalcJni_Sum
05.(JNIEnv *env, jobject obj, jint a, jint b)
06.{
07.return a+b;
08.}
09./*
10.* Class:     CalcJni
11.* Method:    Sub
12.* Signature: (II)I
13.*/
14.JNIEXPORT jint JNICALL Java_com_maluchi_CalcJNITest_CalcJni_Sub
15.(JNIEnv *env, jobject obj, jint a, jint b)
16.{
17.return a-b;
18.}
19./*
20.* Class:     CalcJni
21.* Method:    Mul
22.* Signature: (II)I
23.*/
24.JNIEXPORT jint JNICALL Java_com_maluchi_CalcJNITest_CalcJni_Mul
25.(JNIEnv *env, jobject obj, jint a, jint b)
26.{
27.return a*b;
28.}
29./*
30.* Class:     CalcJni
31.* Method:    Div
32.* Signature: (II)I
33.*/
34.JNIEXPORT jint JNICALL Java_com_maluchi_CalcJNITest_CalcJni_Div
35.(JNIEnv *env, jobject obj, jint a, jint b)
36.{
37.if(a == 0 || b ==0)
38.return 0;
39.return a/b;
40.}
41./*
42.* Class:     CalcJni
43.* Method:    message
44.* Signature: ()Ljava/lang/String;
45.*/
46.JNIEXPORT jstring JNICALL Java_com_maluchi_CalcJNITest_CalcJni_message
47.(JNIEnv *env, jobject obj)
48.{
49.return (*env)->NewStringUTF(env, "Hello world!! Welcome to maluchi's world");
50.}


7. 이제 JNI 파일까지 모두 준비가 됐습니다.

 

------------------------------------------------------------------------------------------------------

 

다) 안드로이트 프로젝트에서 JNI 사용하는 방법

 가)에서 NDK를 설정하여 *.so 파일을 만드는 환경 설정을 했고, 

 나)에서 *.so 의 구현 C모듈에서 Java로 읽어들오도록 인터페이스를 만들고 c 파일 구현까지 했습니다.

 

이제 실질적으로 구현한 C 파일을 리눅스에서 컴파일을 하고 *.so 파일을 얻은 후 안드로이트 프로젝트에 넣어서 실제 동작하는지 테스트 해보는 일만 남았습니다.

이 작업을 설명하겠습니다.

 

우선 안드로이드 프로젝트를 하나 만듭니다.

 

1. CalcJNITest 라는 프로젝트를 만들고 패키지경로를 com.maluchi.CalcJNITest 로 합니다.

 

2. 이 프로젝트에 jni 라는 폴더를 만들고 아까 만들어둔 CalcJni.h, CalcJni.c, CalcJni.java 를 넣고, 가) 에서 봤던 Android.mk도 복사해서 넣습니다.

반드시 jni 라는 폴더에 넣어야 합니다. 그리고 Android.mk 의 LOCAL_MODULE 과  LOCAL_SRC_FILES을 다음처럼 수정합니다.

 

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := CalcJni
LOCAL_SRC_FILES := CalcJni.c

include $(BUILD_SHARED_LIBRARY)

 

3. 이제 CalcJNITest라는 프로젝트를 리눅스 NDK홈에 projects라는 폴더를 만들고 projects 폴더로 옮깁니다.

maluchi@ubuntu:~/android-ndk-r5b/projects$ pwd
/home/maluchi/android-ndk-r5b/projects

maluchi@ubuntu:~/android-ndk-r5b/projects$ ls
CalcJNITest

4. CalcJNITest에 프로젝트에 들어가서 ndk-build 명령을 넣습니다.

해당 프로젝트에 jni 폴더 밑에 c 파일이 있고, ndk-build 는 이를 바탕으로 Libs 폴더를 만들어 *.so 파일을 생성합니다.

진행은 다음처럼 하시면 됩니다..

 

maluchi@ubuntu:~/android-ndk-r5b/projects/CalcJNITest$ ls
AndroidManifest.xml  bin                 gen  proguard.cfg  src
assets               default.properties  jni  res
maluchi@ubuntu:~/android-ndk-r5b/projects/CalcJNITest$ ndk-build
Compile thumb  : CalcJni <= CalcJni.c
SharedLibrary  : libCalcJni.so
Install        : libCalcJni.so => libs/armeabi/libCalcJni.so
maluchi@ubuntu:~/android-ndk-r5b/projects/CalcJNITest$ 

5. 결국 프로젝트에 Libs/armeabi/libCalcJni.so 파일이 만들어짐을 알게 됩니다.

Libs를 통째로 복사해서 윈도우의 해당 안드로이드 프로젝트에 복사합니다.

 

6. 이제 이 C구현부를 java로 불러들이는 일만 남았습니다.

 아까 만들어둔 jni/CalcJni.java를 com.maluchi.CalcJNITest 패키지로 복사를 합니다.

 그리고 CalcJni.java 파일 내에 주석 처리한 package package com.maluchi.CalcJNITest; 주석해제 시킵니다.

 

7. 구현을 다음과 같습니다.

01.package com.maluchi.CalcJNITest;
02. 
03.import android.app.Activity;
04.import android.os.Bundle;
05.import android.os.Handler;
06.import android.util.Log;
07.import android.widget.TextView;
08.public class CalcJNITest extends Activity {
09./** Called when the activity is first created. */
10.@Override
11.public void onCreate(Bundle savedInstanceState) {
12.super.onCreate(savedInstanceState);
13.setContentView(R.layout.main);
14. 
15.CalcJni jni = new CalcJni();
16. 
17.String string ="";
18.string = "Sum :"+jni.Sum(1010)+
19.", Sub: "+jni.Sub(2010)+
20.", Mul: "+jni.Mul(10010)+
21.", Div: "+jni.Div(10010)+
22.", Message: "+jni.message();
23. 
24.Log.d("maluchi", string);
25.TextView v = (TextView) findViewById(R.id.txt);
26.v.setText(string);
27. 
28.// mHandler.sendEmptyMessageDelayed(0, 300);
29. 
30.}
31. 
32.Handler mHandler = new Handler()
33.{
34.public void handleMessage(android.os.Message msg) {
35. 
36.CalcJni jni = new CalcJni();
37.String string = "";
38.string = "Sum :" + jni.Sum(1010) + ", Sub: " + jni.Sub(2010)
39.", Mul: " + jni.Mul(10010) + ", Div: "
40.+ jni.Div(10010) + ", Message: " + jni.message();
41.TextView v = (TextView) findViewById(R.id.txt);
42.v.setText(string);
43.}; 
44.};
45.}


마지막으로 에러 관련 상황입니다.

06-16 06:33:34.304: ERROR/AndroidRuntime(602): Uncaught handler: thread main exiting due to uncaught exception
06-16 06:33:34.354: ERROR/AndroidRuntime(602): java.lang.UnsatisfiedLinkError: Sum
06-16 06:33:34.354: ERROR/AndroidRuntime(602):     at com.maluchi.CalcJNITest.CalcJni.Sum(Native Method)
 

위처럼 나타나는 에러는 JNI의 메서드명 규칙의 JNICALL Java_ 패키지명_클래스명_메서드명() 규칙이 잘못됐을 때 나타는 현상입니다.

 

만약 다음과 같은 예외가 발생한다면 라이브러리 패스가 올바르게 설정되어 있는지 확인합니다.

java.lang.UnsatisfiedLinkError: no hello in shared library path at java.lang.Runtime.loadLibrary(Runtime.java)
        at java.lang.System.loadLibrary(System.java)
       at java.lang.Thread.init(Thread.java)


http://falseisnotnull.wordpress.com/2012/10/31/did-you-lose-your-mariadb-root-password-gnulinux/


Did you lose your MariaDB root password? (GNU/Linux)

Don’t even think to drastical solutions. If you can log into GNU/Linux as root, you can always recover MariaDB root password.

Did you never know the password?

Maybe you installed MariaDB, or you bought a new server, but you don’t know the root password. Don’t panic! It’s ok!

Probably there is no password. Well, this is false; MariaDB asks for a password, and you won’t be able to logon if the password is incorrect; but the password is an empty string. On the CLI, just press enter to access.

Change it: it is insecure. If I had to break into a MariaDB/MySQL installation as root, I would first try an empty password. Don’t let me break into your system so easily!

Ok, you lost the password

Ok, you lost it. This is not the simplest case, but… it’s simple!

1) Log into your GNU/Linux system as the user used by MySQL (usually ‘mysql’) or root.

2) Restart MariaDB with the grant tables disabled:
mysqld_safe --skip-grant-tables --skip-networking

mysqld_safe will shut down mysqld for you.
With --skip-grant-tables, no password is needed to logon.
This is unsafe, so until the password is reset MariaDB should not accept network connections (--skip-networking).

3) Logon with no password:
mysql -u root

4) Set your new password.

Exec these 2 SQL statements and exit the client:

-- change pwd
UPDATE `mysql`.`user`
	SET `Password` = PASSWORD('new_password')
	WHERE `User` = 'root';
-- tell the server to read the grant tables
FLUSH PRIVILEGES;
\quit

(replace ‘new_password’ with the new password)

5) Stop mysqld_safe and restart mysqld:

mysqladmin shutdown
/etc/init.d/mysql start

(depending from your system, you may need to replace ‘/etc/init.d‘ with the correct MySQL path)

6) Logoff from you system (because you are now root or someone very powerful).


<목표> [안드로이드] 터치화면, 제스처 기능을 이용한 터치 인식

       

 

   오늘은  터치화면을 이용한 제스처 기능에 대해서 알아보겠습니다. 제스처 기능은 말그래로 "동작, 움직임" 을 감지합니다. 하지만 핸드폰 자체의 센서를 이용한 움직임은 아닙니다. 즉, 핸드폰을 흔들거나, 기울이거나를 해서 얻는 이벤트를 이용해서 어떠한 기능을 수행하는 기능이 아닙니다. 제스처 기능은 화면 내의 터치를 감지하여, 어떠한 이벤트가 들어왔는지를 분석하여 어떤 기능을 수행할 수 있도록 합니다. 간단한 예로는 화면을 넘기는 것을 예로 들 수 있습니다. 흔히 메인 화면은 여러 개의 화면으로 구성되어 있습니다. 왼쪽에서 오른쪽으로 화면을 넘기기 위해서 손가락을 이용해서 왼쪽에서 오른쪽으로 드래그하는 동작을 취합니다. 이런 방식으로 손가락 모션을 인식하는 것이 제스처 기능입니다.

 

   아래의 예제 코드는 왼쪽에서 오른쪽으로 드래그했을 때, 오른쪽에서 왼쪽으로 드래그를 했을 때, 아래에서 위로, 위에서 아래로 손가락으로 드래그했을 시에 이벤트를 발생시킬 수 있도록 하는 기능을 담고 있습니다. 이벤트가 발생하면 간단하게 토스트를 띄워서 제대로 동작하고 있는지를 확인하도록 되어있습니다. 그리고 부가적으로, 한번 클릭했을 때, 길게 클릭했을 때 등의 기능도 포함되어 있습니다.

 

   Android & Amir 홈페이지에서 가져왔으며, 한 두가지 오류를 찾아 수정한 것입니다.

       

[ 제스처 결과 화면 ]

(오른쪽 방향으로 Swipe하였을 때)

       

STEP 1  Java Source Code

       

   예제는 간단한 한 화면만 있습니다. 상위에는 현재 마우스 클릭(손가락 터치)이 어떠한 방식으로 되고 있는지 출력하는 텍스트 박스가 있으며, 하위에는 마우스 제스처 (손가락 터치 모션) 를 테스트해볼 수 있는 화면이 구성되어 있습니다. 레이아웃은 Xml을 통해 구현하지 않았고, 바로 코드를 이용해서 추가하도록 하였습니다. 눈여겨 봐야할 것은, 제스처를 인식하는 방법입니다.

   

   

  [[ 예제 코드 ]]   Touch UI Gesture (제스처 기능)

   

 

package pulsebeat.example.gesture;

 

import android.app.Activity;

import android.graphics.Color;

import android.os.Bundle;

import android.view.GestureDetector;

import android.view.Gravity;

import android.view.MotionEvent;

import android.view.GestureDetector.OnGestureListener;

import android.widget.LinearLayout;

import android.widget.TextView;

import android.widget.Toast;

 

public class GestureActivity extends Activity implements OnGestureListener{

 

    private LinearLayout main;

    private TextView viewA;

 

    private static final int SWIPE_MIN_DISTANCE = 120;

    private static final int SWIPE_MAX_OFF_PATH = 250;

    private static final int SWIPE_THRESHOLD_VELOCITY = 200;

 

    private GestureDetector gestureScanner;

 

    @Override

    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        gestureScanner = new GestureDetector(this);

        main = new LinearLayout(this);

        main.setBackgroundColor(Color.GRAY);

        main.setLayoutParams(new LinearLayout.LayoutParams(320, 480));

        viewA = new TextView(this);

        viewA.setBackgroundColor(Color.WHITE);

        viewA.setTextColor(Color.BLACK);

        viewA.setTextSize(30);

        viewA.setGravity(Gravity.CENTER);

        viewA.setLayoutParams(new LinearLayout.LayoutParams(320, 80));

        main.addView(viewA);

        setContentView(main);

    }

 

    @Override

    public boolean onTouchEvent(MotionEvent me) {

        return gestureScanner.onTouchEvent(me);

    }

 

    public boolean onDown(MotionEvent e) {

        viewA.setText("-" + "DOWN" + "-");

        return true;

    }

    

    public boolean onFling(MotionEvent e1, MotionEvent e2, floatvelocityX, float velocityY) {

        try {

            if (Math.abs(e1.getY() - e2.getY()) > SWIPE_MAX_OFF_PATH)

                return false;

            

            // right to left swipe

            if (e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE

                    && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {

                Toast.makeText(getApplicationContext(), "Left Swipe",

                        Toast.LENGTH_SHORT).show();

            }

            // left to right swipe

            else if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE

                    && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {

                Toast.makeText(getApplicationContext(), "Right Swipe",

                        Toast.LENGTH_SHORT).show();

            }

            // down to up swipe

            else if (e1.getY() - e2.getY() > SWIPE_MIN_DISTANCE

                    && Math.abs(velocityY) > SWIPE_THRESHOLD_VELOCITY) {

                Toast.makeText(getApplicationContext(), "Swipe up",

                        Toast.LENGTH_SHORT).show();

            }

            // up to down swipe

            else if (e2.getY() - e1.getY() > SWIPE_MIN_DISTANCE

                    && Math.abs(velocityY) > SWIPE_THRESHOLD_VELOCITY) {

                Toast.makeText(getApplicationContext(), "Swipe down",

                        Toast.LENGTH_SHORT).show();

            }

        } catch (Exception e) {

            

        }

 

        return true;

    }

 

    public void onLongPress(MotionEvent e) {

        Toast mToast = Toast.makeText(getApplicationContext(), "Long Press", Toast.LENGTH_SHORT);

        mToast.show();

    }

 

    public boolean onScroll(MotionEvent e1, MotionEvent e2, floatdistanceX, float distanceY) {

        viewA.setText("-" + "SCROLL" + "-");

        return true;

    }

 

    public void onShowPress(MotionEvent e) {

        viewA.setText("-" + "SHOW PRESS" + "-");

    }

 

    public boolean onSingleTapUp(MotionEvent e) {

        Toast mToast = Toast.makeText(getApplicationContext(), "Single Tap", Toast.LENGTH_SHORT);

        mToast.show();

        return true;

    }

}

   

 

   Swipe(휘두르다, 강타)라는 용어를 썼는데, 한국어로 어떻게 변경을 해야할지 몰라 그대로 두었습니다. 손가락을 이용하여 여러 개로 구성되어 있는 메인 화면을 넘기는 방법을 생각하시면 가장 간단할 것 같습니다.

 

   코드의 핵심은 GestureDetector 에 있습니다. 엑티비티에서 OnGestureListener 를 상속받으면, GestureDetector를 사용할 수 있습니다. GestureDetector를 엑티비티에 등록시켜면서 생성합니다. 그런 다음 onTouchEvent를 통해 GestureDetector 객체의 onTouchEvent로 등록시켜줍니다. 이렇게 하면 기본 세팅은 마무리가 됩니다. 그런 다음에 자신이 하고 싶은 기능을 상속받아 사용할 수 있습니다. 위에서 보시면, onDown, onLongPress, onScroll, onShowPress, onSingleTapUp, onFling라는 것을 상속받아 구현되어 있는 것을 확인할 수 있습니다. 함수명에서 보시듯 어떠한 터치 동작을 하고 있는 중인지에 따라 각각이 호출되고 있습니다. 다른 것은 직관적으로 아실 수 있을 거라 생각이 듭니다만, 복잡한 듯 보이는 onFling은 조금 생각을 해보아야 합니다. 아래의 세가지 변수를 먼저 알아봅시다. 이것만 이해되신다면 아주 간단한 구조로 되어 있다는 것을 알게 되실 겁니다.

 

 

static final int SWIPE_MIN_DISTANCE = 120;

static final int SWIPE_MAX_OFF_PATH = 250;

static final int SWIPE_THRESHOLD_VELOCITY = 200;

    

 

   터치 이벤트를 통해서 onFling이벤트로 들어옵니다. onFling으로 들어오는 인자를 보시면, 모션 이벤트가 2개가 들어오고, float 형의 velocity(속도)가 들어옵니다. 벌써 이미 느낌이 오실 겁니다. 첫 번째 인자는 터치를 시작한 그 순간의 이벤트이며, 두 번째 인자는 터치를 땐 그 시점의 이벤트 입니다. 세 번째 인자는 X축으로의 속도이며, 네 번째 인자는 Y 축으로의 인자입니다. 즉 이 네 가지의 이벤트를 가지고, 어떤 방향으로의 Swipe동작인지 구분할 수가 있습니다.

 

   네 가지 방향으로의 Swipe동작을 어떻게 구분하는가 하는 것은 거리 측정과, 속도를 통해서 이루어집니다. SWIPE_MIN_DISTANSE 인자는 Swipe를 인식하는 가장 작은 거리를 뜻합니다. 설정된 거리 이상이 되어야 Swipe동작으로 인식한다는 것이죠. SWIPE_MAX_OFF_PATH는 인식하는 최대 거리라고 보시면 됩니다. 그 이상의 거리를 Scrolling하면 제외하겠다는 의미를 가지고 있습니다. 마지막 SWIPE_THRESHOLD_VELOCITY는 속도입니다. 각 방향으로 임의의 속도 이상으로 터치동작이 이루어져야 Swipe로 인식하겠다는 것이지요.

 

  이제 위의 코드를 살펴보시면, 어떤 구조로 되어 있는지 한 눈에 들어오실 겁니다. 첫 좌표와 끝 좌표를 비교하여 거리를 비교하여 적정 수준의 거리와 속도를 가지면 Swipe로 인식하며 토스트를 띄웁니다. 이러한 방식을 이용하여 화면 전환 등의 자신이 하고 싶은 동작을 넣으시면 됩니다.

 

    

STEP 2  Xml Code

         

    Xml 코드를 제외하고 코드로 바로 레이아웃을 설정하였습니다. onCreate에 있는 추가동작을 xml로 구현하셔도 무방합니다.

             

STEP 3  AndroidManifest.xml Code

       

   안드로이드 자체에서 제공되는 서비스기 때문에  메니페스트는 손댈 필요가 없습니다.

 



 마무리 >   터치화면, 제스처 기능을 이용한 터치 인식

       

   

   일반적으로 다이나믹한 UI를 사용자에게 제공하기 위해서는 터치 기능을 이용하여 다양한 서비스를 제공해야합니다. 제스처(Gesture)를 인식하기 위해서는 이벤트 핸들러를 통해서 현재 어떠한 모션을 취하고 있는지를 확인하는 절차가 필요합니다. onGestureListener를 Activity로 상속받고, GestureDetector를 생성하여, 터치이벤트로 등록시키며, 자신이 원하는 동작을 가져올 수 있도록, 터치 이벤트의 동작 지점, 거리, 터치 속도 등을 고려하여 설계를 하시면 됩니다. 간단한 Swipe기능에 대한 예제를 알아보고, 제스처가 어떠한 방식으로 등록되고 사용되는지에 대해서 알아보았습니다. 이것 외에도 안드로이드에서 제공하는 기본 제스처, GestureLibrary 등이 있는 것으로 생각됩니다만, 아직는 그것을 쓸 필요가 없었던 관계로 추후에 기회가 된다면 다시 포스팅 하겠습니다.    

     

출처 : http://pulsebeat.tistory.com/27

xml파일내에서 

<LinearLayout

android:layout_width="240dip"

android:layout_height="320dip" />


설정해줘도 LayoutParams 사용할 경우 안되는 경우가 있었었었었었다



LayoutParams params = new LayoutParams( 240, 320 ) 해줘도 단위가 달라져서 찾아보던 중


final int width = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 240, getResources().getDisplayMetrics());

final int height = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 320, getResources().getDisplayMetrics());


LinearLayout.LayoutParams paramlinear = new LinearLayout.LayoutParams(width,height);


하니까 됨ㅋ





+ Recent posts