2010년 11월 8일 월요일
개발자용 폰트 Bitstream Vera Sans Mono + 맑은 고딕
2009년 11월 8일 일요일
java 에서의 call by value 와 call by reference
자바에서는 진정한 call by reference는 없고 reference 값을 넘기는 것만 있다고 들었는데,
그 말을 이해하지 못하다가 이제서야 이해하게 되었다.
그 이해를 도와준 포스트가 아래 포스트이다.
---------------------------------------------------------------------------------------------
테스트 없이 눈으로만 코딩할 때 가장 틀리기 쉬운 부분이 call by value 와 call by refence 를 정확히 이해하지 못할 때 생기는 오류가 아닐까 싶다.
기본형과 객체가 있는데 기본형은 call by value 이고 객체는 call by refence 라는 것은 대부분의 java 개발자들이 알고 있을꺼라 생각한다.
그 예로 swap 함수를 가장 많이 설명하는데..
class Test {
private static void swap(int a, int b) {
int temp = a;
a = b;
b = temp;
}
public static void main(String args[]) {
int a = 1;
int b = 2;
System.out.println("a => " + a);
System.out.println("b => " + b);
swap(a, b);
System.out.println("------- swap 후 -------");
System.out.println("a => " + a);
System.out.println("b => " + b);
}
}
예제 1 의 경우 원하던 결과가 아닌 것을 바로 알아 낼 수 있을 것이다.
swap 메소드에 넘기는 것은 reference 가 아닌 value 이기 때문에...
쉽다. 넘어가자.
다음 예제 2 를 보자
class Test {
private static void swap(Integer a, Integer b) {
Integer temp = a;
a = b;
b = temp;
}
public static void main(String args[]) {
Integer a = new Integer(1);
Integer b = new Integer(2);
System.out.println("a => " + a.intValue());
System.out.println("b => " + b.intValue());
swap(a, b);
System.out.println("------- swap 후 -------");
System.out.println("a => " + a.intValue());
System.out.println("b => " + b.intValue());
}
}
예제 2 의 경우 Integer 는 Object 이다. Object 는 call by reference 다.
따라서 위의 예제는 원하는 결과를 가져올 것이다.
그러나 실행을 하면 예제 1과 전혀 다르지 않다는 것을 알 수 있다.
왜? 객체는 call by reference 라며 사기친거야?
결론부터 말하면 객체는 call by reference 맞다
그러나 해당 객체를 보는 새로운 reference 를 참조해서 넘기는 것이다.
따라서 동일한 객체를 가르키고 있지만
main 에서의 reference 값과 swap 함수에서의 reference 값은 다르다.
따라서 위의 예제의 경우 원하는 결과가 나오지 않는다.
그렇다면 어떻게 해야 해?
예제 3 을 보자.
class Test {
int value;
Test(int value) {
this.value = value;
}
private static void swap(Test a, Test b) {
int temp = a.value;
a.value = b.value;
b.value = temp;
}
public static void main(String args[]) {
Test a = new Test(1);
Test b = new Test(2);
System.out.println("a => " + a.value);
System.out.println("b => " + b.value);
swap(a, b);
System.out.println("------- swap 후 -------");
System.out.println("a => " + a.value);
System.out.println("b => " + b.value);
}
}
예제 2와 같이 객체의 reference 를 넘긴다.
reference 를 직접 변경하는 게 아니라.
reference 가 참조하는 객체의 value 를 변경하고 있다.
따라서 같은 객체를 보고 있는 main 에서도 값이 바뀌는 것을 알 수 있다.
해당 객체의 주소값을 직접 넘기는 게 아닌 객체를 보는 또 다른 주소값을 만들어서 넘기다는 사실을 잊지 말자~
2009년 11월 4일 수요일
자바 추상클래스와 인터페이스 (abstract class VS interface)
다중상속
클래스는 하나의 클래스만을 상속(extends)받을 수 있으므로 추상클래스를 상속받고자 하는 클래스는 한개의 추상클래스만이 상속가능하다. 이에 반해 클래스는 여러개의 인터페이스는 구현(implements)할 수 있으므로 하나의 클래스에 여러개의 인터페이스 구현이 가능하다는 뜻에서 다중상속이 가능하다. (엄격히 말한다면 인터페이는 상속이 아닌 구현이다.)
필드 정의
추상클래스에서는 클래스 멤버 변수의 정의가 가능하다. 하지만 인터페이스에서는 멤버변수의 정의를 할 수 없다. 인터페이스에서 멤버변수 선언이 가능하다고 생각해보자. 하나의 클래스에서는 여러 개의 인터페이스를 구현해야하는데 서로 다른 인터페이스에 같은 이름의 멤버변수가 있다면 인터페이스들을 구현한 클래스에서는 그 멤버변수가 어느곳에서 선언한 것인지 구별할 수 있겠는가?! 구별이 안된다.
메소드 구현
추상 클래스는 구현메소드와 추상메소드 모두 가능하지만, 인터페이스에서는 추상메소드만이 가능하다. 추상 클래스에서는 메소드에 abstract 키워드를 사용하여 추상 메소드를 표시하고, 인터페이스는 abstract 키워드가 없이 추상메소드를 만든다. 추상 메소드는 접근제한자, 리턴타입,메소드명,전달인자 리스트 로만 구성이 되며, 메소드의 몸체 즉 메소드의 내용을 정의하지 않는다. 추상 메소드를 가지고 있는 추상클래스나 인터페이스를 상속받거나 구현하는 하위 클래스는 상위에 있는 추상 메소드를 모두 구현해야만 한다.
상수 정의
상수란 클래스 멤버 변수에 final 키워드를 붙인 것으로 변하지 않는 값을 나타낸다. 하지만 인터페이스에서는 final 키워드를 붙이지 않아도 상수가 된다. 인터페이스에서는 클래스 멤버변수를 가질 수는 없지만 클래스 멤버 상수는 가질 수 있다.
2009년 9월 16일 수요일
방사형 Flex SpringGraph
Flex로 방사형 그래프를 만들어야하는 일이 생겼는데
어떻게 해야할지 전혀 갈피를 못 잡고 있던 중,
Adobe의 FlexBuilder 엔지니어인 Mark Shepherd가 개발하여 오픈 소스화한 것을 발견했다.
게다가 각종 demo까지!
소스와 demo는 http://mark-shepherd.com에서 찾을 수 있다.
가장 마음에 들었던 demo는 여기!
그리고 적용 예제는 "예제로 배우는 플렉스"의 저자인 "옥상훈"님의 사이트 okgosu.net에서
찾을 수 있었다. 항상 지식을 다른 사람들에게 베풀려고 하시는 옥상훈 님께 감사드린다.
2009년 9월 10일 목요일
[Shell] 날짜로 파일 필터링 하기
쉘에서 작업을 하다보면 파일들을 날짜로 필터링 해야하는 경우가 생긴다.
예를 들면, 어떤 날짜와 다른 어떤 날짜 사이에 수정한 파일들만 가지고 와서 따로 백업을 해준다든지,
공동으로 작업하는 사람이 오늘 어떤 파일을 수정하였는지 보고 싶다든지 하는 경우가
파일을 날짜로 필터링해야 하는 경우이다.
그럴 때는 편법으로 이런 방법이 있다.
$ touch -t 200901010000 file1
$ touch -t 200902010000 file2
$ find / -newer file1 -a ! -newer file2 -ls > result
여기서 이 명령어들을 이해하려면 몇 가지 알고 있어야 하는 것들이 있다.
1) 용도 : 파일의 시간을 원하는 시간으로 생성하거나 수정
2) -t 옵션 : 수정하고자 하는 시간을 지정하는 옵션. -t 를 쓴 후 뒤에 원하는 시간을 적으면 됨.
2. find 명령어
1) 용도 : 원하는 조건의 파일을 찾음
2) -newer 옵션 : 옵션 뒤의 파일보다 최근에(이후에) 내용이 수정된 파일을 검색
3) -a 옵션 : 'and' 연산자와 같은 의미
4) ! : 'not' 연산자와 같은 의미
자 그렇다면, 이제 명령어를 이해해보자.
1. 첫 번째 줄을 보면 'file1'이라는 파일을 2009년 1월 1일 00시 00분 00초로 생성한다.
2. 두 번째 줄을 보면 1번과 마찬가지 방법으로 'file2'이라는 파일을 2009년 2월 1일 00시 00분 00초로 생성한다.
3. 세 번째 줄에 대해서 설명하면, find 명령어 다음의 '/'는 전체 범위에서 찾으라는 의미이고, file1이 수정된 시간보다 최근에, file2가 수정된 시간보다는 이전의 파일을 찾으라는 의미의 명령어 문장이다.
그러므로, 저 세 명령어 문장으로 수정된 시간이 원하는 범위 내에 있는 파일을 찾을 수 있다.
2009년 9월 3일 목요일
jsp/servlet scope
forward는 request를 발생시키지 않으므로 request scope 내의 모든 변수(빈)들을 공유할 수 있다. 반면 redirect는 request를 발생시키므로 공유가 불가능하다. 이때는 session scope를 사용한다.
2009년 8월 21일 금요일
[Linux-CVS-4] CVS 클라이언트 작업2 - Eclipse
많은 사람들이 CVS로 프로젝트 작업 시 Eclipse를 이용한다.
실제로 많이 편리하다. ^^;
CVS 서버 등록 및 접속
이클립스 화면에서 'CVS Repositaries Perspective'를 연다.
이미지에 보이는 'Other ..' 을 클릭하면 다음 화면이 뜬다.
거기서 'CVS Repository Exploring'을 클릭한 후 'OK'를 클릭한다.
그러면 좌측의 Perspective들이 있는 부분에 'CVS Repository Perspective' 패널이 추가된다.
그 패널 위에서 오른쪽 마우스를 클릭하고 'New'에 마우스 오버하면 'Repository Location..'이라는
메뉴가 있다. 클릭한다.
클릭하면 다음과 같은 화면이 뜨는데 빨간 네모가 되어 있는 곳에 기입해야 할 것이
CVS 서버의 정보이다. 모두 입력 후, 'Finish'를 클릭한다.
이제 'CVS Repository Perspective' 패널에 추가되어 있는 CVS 서버 정보가 보일 것이다.
checkout 명령어
'CVS Repository Perspective' 패널에 추가되어 있는 CVS 서버 정보를 아래로 펼치면
'HEAD', 'Branches'.. 등등의 메뉴가 보일 것인데 'HEAD' 메뉴를 아래로 펼친다.
아래로 펼치면 CVS 서버에 있는 프로젝트들을 볼 수 있는 원하는 프로젝트를 클릭한 후
마우스 오른쪽 클릭한 다음 'Check Out'을 클릭한다.
그러면 자신의 Eclipse의 'Workspace'에 해당 프로젝트가 복사된다.
만약에 'Workspace'가 아닌 다른 장소에 복사하고 싶으면 'Check Out As'를 클릭하면 된다.
CVS로 프로젝트 작업하기
이제 'Navigator View'를 열어서 확인해보면 프로젝트가 추가되어 있는 것을 볼 수 있다.
Eclipse에서는 굳이 status 명령어 없이도 파일 우측에 파일 및 디렉토리의 버전 정보가 표시된다.
게다가 아직 update되지 않은 정보는 파일이나 디렉토리명 앞에 '>'라는 표시가 되어 있어서
구분하기가 용이하다.
update 및 commit 실행 시 실행시키고 싶은 파일이나 디렉토리에, 혹은 프로젝트 이름에서
마우스 오른쪽 버튼 클릭 후 'team' 메뉴에 마우스 오버하게 되면 update 및 commit이 보인다.
이상으로 Eclipse에서 간단히 CVS로 작업해보았다.
CVS를 처음 다루기를 어렵지만 한번 익혀놓으면 협업 작업 시 아주 편리하다.
물론 관리 차원에서의 효율성도 증대될 것이다.
더 나은 개발자가 되기 위해서..!
[Linux-CVS-3] CVS 클라이언트 작업1 - 쉘
이제 서버에서 할 수 있는 대부분의 설정은 끝났다고 보면 된다.
클라이언트 작업이 필요한데, 익히 알고 있는 checkout, update, commit 등이 그것이다.
많은 사람들이 eclipse에서 작업하기도 하고 쉘에서 작업하기도 하는데,
먼저 쉘에서 하는 것에 대해서 알아보겠다.
CVS 계정 로그인
첫 번째에서 cvs 계정을 생성했었는데, 예를 들어서 user1 이라고 해보자.
제일 먼저 해야하는 것은 당연히 cvs 로그인이다.
물론 로컬 디스크에서 작업할 때는 로그인이 필요없다. root가 모든 권한을 가지고 있기 때문이다.
차례대로 설명해보면,
1. -d : 저장소의 위치를 나타낼 때 쓰는 옵션
2. pserver : pserver 프로토콜
3. user1 : 클라이언트의 cvs 계정
4. myhost.com : cvs 서버의 host
5. /home/cvs : cvs 서버의 repository 경로
6. login : 로그인 명령어
로그인할 때마다 저 긴 명령어를 타이핑하려면 힘들 것이다. 그래서 환경 변수로 등록해두면 편하다.
$ cvs login
checkout 명령어
checkout 명령어는 cvs로 작업 중인 프로젝트의 복사본을 클라이언트에게 가지고 오는 것이다.
클라이언트에서 원하는 경로에 checkout 명령어를 실행시킨다.
$ cvs checkout project_name
$ ls /home/client/
project_name
cvs checkout 뒤에는 원하는 프로젝트 이름을 적는다.
ls 명령어로 디렉토리 내용을 보면 해당 프로젝트 이름으로 디렉토리가 생성되어 있는 것을
확인할 수 있다. 그리고 그 디렉토리 아래에는 프로젝트 파일들이 모두 복사되어 있을 것이다.
checkout 명령어는 프로젝트 작업 시작 시 한 번만 실행시켜 주면 된다.
CVS로 프로젝트 작업하기
1. update
update 명령어는 실제 프로젝트 진행 시 다른 팀원이 변경한 데이터를 자신의 프로젝트에 적용시키는
작업이다.
클라이언트에서 진행 중인 프로젝트 내의 update를 원하는 경로에서 명령어를 실행시키면 된다.
예컨대, 클라이언트 프로젝트에서 갱신하고 싶은 디렉토리가 project_name/my_directory1이라고
하면,
$ cvs update my_directory1
이렇게 실행시키면 된다.
물론 그냥
라고만 해도 실행된다. 이것은 현재 디렉토리 내의 하위 경로까지 모두 update 하는 명령어이다.
클라이언트의 프로젝트 정보가 서버 상의 정보로 완전히 갱신이 되지 않았을 경우, commit 명령이
실행되지 않으므로 소스 수정 전에 반드시 update 명령을 수행하는 것이 좋다.
2. add & remove
파일이나 디렉토리를 추가할 때도 cvs 명령어를 사용해야 한다.
예를 들면 project_name 디렉토리 아래에 my_directory2 디렉토리를 추가하고자 한다면,
$ cvs add project_name/my_directory2
이렇게 하면 된다. 파일도 마찬가지이다.
파일을 삭제하는 경우도 추가 시와 유사하다.
가령, project_name 디렉토리 아래의 my.jsp 파일을 삭제한다고 하자.
$ cvs remove project_name/my.jsp
그러나 디렉토리를 삭제하는 경우는 좀 다른데, 아까 생성했던 project_name/my_directory2를
삭제한다고 했을 때, 클라이언트의 프로젝트에서 해당 디렉토리를 삭제 후 직접 CVS 서버로
가서 해당 디렉토리를 삭제해줘야 한다.
3. status
staus 명령어는 현재 버전을 확인하는 명령어이다.
===================================================================
File: index.jsp Status: Up-to-date
Working revision: 1.3 Fri Aug 21 05:14:03 2009
Repository revision: 1.3 /home/cvs/project_name/index.jsp,v
Sticky Tag: (none)
Sticky Date: (none)
Sticky Options: (none)
이런 식으로 출력된다.
뒤에 파일 이름이나 디렉토리 이름을 적지 않으면 프로젝트 내의 모든 파일에 대한 정보가
출력된다.
4. commit
commit 명령어는 클라이언트에서 수정한 정보를 서버에 등록시키는 것이다.
클라이어트에서 project_name/index.jsp 파일을 수정했을 경우,
이렇게 실행시키면 된다. 위의 명령어들과 마찬가지로 뒤에 파일 이름이나 디렉토리 경로를
쓰지 않으면 프로젝트 내의 모든 파일의 수정된 정보가 업로드된다.
이상으로 가장 많이 쓰이는 명령어들을 살펴 보았다.
사용해보니 역시 쉘보다는 이클립스가 편한 것 같다.
대부분의 개발자들이 그렇게 생각하겠지만 ^^;
참고1 : CVS User Guide
참고2 : cvs 설정 및 사용법
2009년 8월 20일 목요일
[Linux-CVS-2] CVS 프로젝트 등록 및 시작
난생 처음으로 CVS 세팅은 맞아 무수한 삽질과 성공을 거듭하고 있다.
끝나게 되면 정말 뿌듯하겠지?
막상 하고 나면 정말 별거 아닌 작업들이다.
CVS 프로젝트 초기화
프로젝트를 초기화하는 import 명령어를 실행하기 위해서는 반드시 환경 변수 CVSROOT가
설정되어 있어야 한다.
CVSROOT 설정 방법은 로컬 디스크에 있을 때와 리모트에 있을 때에 따라서 방법이 나뉜다.
먼저 로컬 디스크에 있는 경우,
다음은 리모트에 있을 경우,
이렇게 하면 CVSROOT가 설정된다.
이제 프로젝트를 초기화할 수 있다.
새 프로젝트를 초기화시키기 위해서는 뭔가 선행되어 있는 작업이 있어야 한다.
만약, 완전히 새롭게 시작하는 프로젝트 일 경우, 빈 디렉토리를 하나 생성한 후,
import 명령어를 실행시키면 될 것이고, 이미 작업되어 있는 프로젝트를 수정하거나
프레임워크가 구성된 프로젝트를 시작할 경우는 해당 디렉토리 내에서 import 명령어를
실행시키면 된다.
가령, 생성된 빈 디렉토리거나 구축된 프로젝트가 있는 디렉토리가 /home/my/myproj 라고 하자.
그러면 해당 디렉토리로 옮겨간 후 명령어를 실행한다.
$ cvs import -m "설명" project_name my_name release_0
두 번째 줄을 차례대로 설명해보면,
1. cvs import : 프로젝트 추가 명령어
2. -m : import 시 기록할 수 있도록 문자를 넣을 수 있는 옵션
3. "설명" : 프로젝트에 관련된 간단한 설명글
4. project_name : 프로젝트 이름, 이 이름으로 프로젝트 디렉토리가 생성된다.
5. my_name : vendor_tag라고 하며, 프로젝트는 만드는 사람 이름이나 팀 이름을 적을 수 있다.
6. release_0 : release_tag라고 한다.
5, 6번인 vendor_tag와 release_tag는 크게 중요하지 않다고 한다.
명령어를 실행시키고 나면 /home/cvs 아래에 4번 project_name으로 설정했던 이름으로
생성된 디렉토리를 볼 수 있을 것이다.
CVSROOT project_name
자, 이제 프로젝트 추가가 완료되었다. 이젠 공동으로 사용가능한 모든 작업이 완료되었으며
각 팀원들이 클라이언트 환경에서 세팅해 줄 일만 남았다.
참고 : CVS User Guide
2009년 8월 19일 수요일
[Linux-CVS-1] CVS 서버 설정
개발 프로젝트 진행 시, 웬만한 프로젝트는 대부분 팀을 이뤄서 하게 된다.
그럴 때, 버전 관리가 필수적인데, 필요한 프로그램이 CVS이다.
CVS의 정의
CVS(Current Versions System, 동시 버전 시스템)은 소프트웨어 프로젝트를 진행할 때, 파일로 이뤄진 모든 작업과 변화를 추적해서 여러 개발자가 협력적이고 효율적으로 작업을 할 수 있게 한다. CVS는 GPL(GNU General Public License, GNU 일반 공중 허가서) 하에서 배포되는 오픈 소스 프로그램이다.
CVS 설치
1. CVS 설치 여부 확인
$ cvs -version
위 방법으로 확인이 가능하다. CVS가 설치되어 있다면 버전 정보가 보일 것이다.
설치되어 있지 않다면 CVS를 먼저 설치해야한다.
2. CVS 설치
CVS는 http://ftp.gnu.org/non-gnu/cvs/binary/stable/x86-linux/RPMS/i386/에서 패키지를 다운받을 수 있다.
CVS 리눅스 계정 추가
CVS를 관리할 계정을 생성해야 한다. 계정을 따로 생성하지 않고 관리해도 되지만 따로 계정을 두고 관리하는 것이 관리 차원에서 효율적이다.
추가되는 사용자 계정의 홈 디렉토리가 CVS의 저장소가 될 것이다.
$ useradd -g cvs cvs - cvs 그룹에 계정 추가
$ passwd cvs - cvs 계정 패스워드 설정
CVS 저장소(repository) 설정
여러 명이 동시에 작업해야 하므로 공통으로 저장할 장소가 필요하다. 그 장소를 저장소(repository)라고 부르며 CVS의 전반적인 설정 파일, 버전 관리에 관련된 정보, 파일 별 작업 기록 등이 저장된다. 설정 파일 및 작업 기록은 CVS 프로그램이 자동으로 관리하므로 사용자가 건드릴 필요는 전혀 없다.
/home/cvs 디렉토리를 CVS 저장소로 초기화한다. -d 옵션은 저장소가 될 디렉토리 경로를 지정하기 위한 옵션이고, 'init'은 저장소를 초기화하기 위한 cvs 명령어이다.
명령어 실행 후 /home/cvs 디렉토리를 확인해 보면 'CVSROOT'라는 이름의 디렉토리가 생성되어 있으며 각종 설정 파일이 그 디렉토리 내에 들어있다는 것을 알 수 있다.
CVS 디렉토리 사용 권한 설정
설정했던 디렉토리를 cvs 계정이 사용 가능하도록 지정해 준다.
$ chmod 770 /home/cvs /home/cvs/CVSROOT
CVS 서비스 설정
1. 2041 포트 확인
CVS 전용 계정으로 관리하는 경우 클라이언트는 CVS 서버에 pserver 프로토콜을 사용해 접근한다. 이 프로토콜을 사용하려면 몇 가지 준비 사항이 필요하다.
우선 pserver 프로토콜이 사용하는 포트가 사용 가능하게 설정되어 있는지 확인한다.
이 명령어의 결과가 출력이 된다면 pserver 프로토콜을 사용할 준비가 되어 있는 것이다.
2. pserver 환경 파일 작성
pserver 프로토콜을 사용하기 위해서는 환경 파일을 작성해야 한다. 슈퍼 데몬인 xinetd를 사용하는 경우 우선 /etc/xinetd.d 디렉토리에 cvspserver 파일이 있는지 확인하고 없으면 파일을 만든다.
새 파일로 vi 에디터가 뜨면 다음 내용을 입력한다.
{
disable = no
flag = REUSE
socket_type = stream
wait = no
user = root
server = /usr/bin/cvs
server_args = -f --allow-root=/home/cvs pserver
log_on_failure += USERID
}
--allow-root는 cvs 저장소가 /home/cvs 임을 명시하기 위해서 사용된다.
만약에 새로운 프로젝트 project1을 생성하였을 경우, 이 프로젝트는 /home/cvs/project1 디렉토리에 저장된다.
cvspserver 파일 편집을 완료했으면 저장 후 xinetd 서비스를 재시작한다.
CVS 전용 사용자 계정 추가
CVS 사용자 계정은 실제로 프로젝트를 진행하는 팀원 개개인의 계정이다. 팀원들은 이 계정을 통해 프로젝트 파일에 접근해서 작업을 수행할 것이다.
CVS 계정을 더 쉽게 추가하기 위해 /usr/local/bin 디렉토리에 cvspasswdgen 파일을 생성하고 다음의 내용을 스크립트를 기록한다.
#!/usr/bin/perl
($u, $p)=@ARGV;
@d=(A..Z,a..z);
$s=$d[rand(52)].$d[rand(52)];
print $u.":".crypt($p, $s).":cvs\n"
이어서 cvspasswdgen 파일에 대해 다음과 같이 실행 권한을 부여한다.
이제 다음과 같은 명령어로 CVS 전용 사용자 계정을 추가할 수 있다. cvspasswdgen 명령어의 첫 번째 파라미터는 사용자 계정 이름이고, 두 번째 파라미터는 해당 계정의 패스워드이다.
$ cvspasswdgen cvsuser1 cvsuser1_passwd >> passwd
위 명령어를 입력하면 passwd 파일에 계정 정보가 입력될 것이다. 이제 이클립스와 같은 IDE에서 CVS 전용 계정을 통해 접속하면 된다.
다음 번에는 프로젝트 초기화에 대해서 정리해 봐야지.
2009년 8월 13일 목요일
UL 태크 삽입 시 상단의 여백 생기는 문제
html 태그 및 css로 UI 작업을 하다보면 도저히 이해할 수 없는
버그들이 종종 발생한다.
이번의 UL 태그 관련 버그도 그런 부류 중 하나였는데,
갖은 삽질 끝에 해결할 수 있었다.
상황은 이러하다.
큰 div 개체 안에 다른 div 개체를 넣고 그 밑에 ul 개체를 삽입하였을 경우,
즉, div A 개체 안에 div B 개체와 ul 개체가 삽입되어 있는 상황인 것이다.
그때 예기치 않게 div B 개체와 ul 개체 사이에 공백이 생겨버렸다.
비슷한 경우로, li 개체 안에 ul 개체를 삽입하였을 경우도 이런 문제가 발생하였다.
의외로 이 문제는 간단하게 해결되었는데,
ul 개체의 css에 float: left 속성만 추가시켜주니 해결이 되었다.
내 생각에는 ul 개체는 원래 어떤 개체(이하 A개체) 밑에 속할 때
자동으로 여백이 생성이 되는데 float: left 속성을 줌으로써 A개체 좌측에
강제로 배치되게 되고, width가 지정되어 있으면서 A개체 좌측에 공간이 없는
경우에는 A개체 아래로 흐르게 되는 것이다.
( float 속성 사용 시 흐른다는 말이 어울리는 듯해서 사용해봅니다 ^^)
그러나 이건 어디까지나 내 생각일 뿐, 근거는 없다.
역시 개발은 삽질을 동반할 수 밖에 없는 것인가.. ㅠ_ㅠ
2009년 7월 31일 금요일
mysql rownum 구현하기
mysql을 쓰다보면 oracle 처럼 rownum을 써야하는 경우가 생긴다.
혹은, 자동으로 1씩 증가하는 for 문의 변수(주로 integer i)와 같은 형태의
컬럼의 쿼리에서 구현하는 것이 편할 때가 있다.
주로 ordering이나 grouping 후 순위를 매길 때가 그런 경우인데,
그럴 때 쓸 수 있는 rownum이 mysql에도 있다.
ex 1 )
SELECT @RNUM := @RNUM + 1 AS ROWNUM
FROM ( SELECT @RNUM := 0 ) R
ex 2 )
SELECT @RNUM := @RNUM + 1 AS ROWNUM, t.*
FROM
(
SELECT *
FROM table
ORDER BY column1
) t,
( SELECT @RNUM := 0 ) R
ex 2 처럼 구현하게 되면 ordering 된 상태에서 rownum이
0부터 순서대로 부여된다.
은근히 자주 쓰이는 편리한 방법이다. ^^
2009년 7월 3일 금요일
[자바스크립트] 현재 스크롤 위치 알아내기
var b = document.body;
var now = {};
now.X = document.all ? (!de.scrollLeft ? b.scrollLeft : de.scrollLeft) : (window.pageXOffset ? window.pageXOffset : window.scrollX);
now.Y = document.all ? (!de.scrollTop ? b.scrollTop : de.scrollTop) : (window.pageYOffset ? window.pageYOffset : window.scrollY);
return now;
소스는 어렵지 않아서 별다른 설명이 필요 없을 것 같네요.
IE7, FF2 에서 테스트 해보았으며,
사용법은 간단합니다
<script type="text/javascript">
var CurrentScroll = function(){
alert(nowScroll.X + "," + nowScroll.Y);
</script>
http://jdsharp.us/ 에 스크롤 위치구하는 코드가 잘 짜여져 있어, 이 코드를 사용하였습니다.
출처 : [js] 현재 스크롤 알아내기
2009년 6월 22일 월요일
유용한 자바스크립트 정리
가끔 웹에서 자바스크립트 오픈 소스를 찾다 보면
한 줄로 쭉 이어진 매너 없는 소스가 있다..
그런 소스를 한 번에 정리해 주는 페이지를 구글링으로 찾을 수 있었다.
나와 같은 불편을 겪는 분들은, 편히 이용하시길..
2009년 6월 16일 화요일
[jQuery] each 메소드 사용시 break, continue 사용
jQuery.each ( callback )
jQuery에서 for, while 문과 비슷한 역할을 하는 each 메소드 사용 시
break, continue와 같은 프로세스를 수행해야 할 때가 있다.
그럴 때 return true;는 continue와 같은 역할을, return false;는 break와 같은
역할을 수행한다.
예제 >>
$('#loop').each ( function() {
if ( i==0 ) return true; // continue;
else return false; // break;
} );
출처 : 짱프로그래머 카페
2009년 6월 10일 수요일
DIV 객체에서 valign='middle' 구현하기
Ajax 프로그래밍을 하다보면 DIV를 수시로 사용해야 하는데 DIV는 테이블과 다르게 valign="middle" 속성이 존재하지 않는다. 여러가지 방법을 고려하던 중 몇몇사이트에서 힌트가 될만한 내용을 얻게 되었다.
첫번째 방식의 경우
첫번째 방식은 다음과 같은 소스로 구성되어 있다.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=iso-8859-2">
<title>Universal vertical center solution</title>
<style>
.greenBorder {border: 1px solid green;}
</style>
</head>
<body>
<h1>Vertical Centering in CSS - Example</h1>
<div class="greenBorder" style="display: table; height: 400px; #position: relative; overflow: hidden;">
<div style=" #position: absolute; #top: 50%;display: table-cell; vertical-align: middle;">
<div class="greenBorder" style="#position: relative; #top: -50%; ">
any text<br>
any height<br>
any content, for example generated from DB<br>
everything is vertically centered
</div>
</div>
</div>
<p>Please view source for details or read the
<a href="../css-vertical-center-solution.html">CSS
vertical centering article</a>.
<p>See also the <a href="vertical-align-valid-solution-en.html">structural and
valid example</a>.
</body>
</html>
IE의 경우 Layer를 3중으로 감싸서 첫번째 Layer에서 전체적인 위치를 잡고 두번째 Layer에서는 첫번째 Layer기준으로 가운데(상대 좌표 기준 50%로 top 설정)하고 세번째 Layer에서 두번째 Layer기준으로 -50%로 top을 설정하는 방식이다. 이 방식의 경우 IE의 거의 모든 버전에서 동작하는 장점이 있지만 데이터가 많아지는 경우 스크롤바가 생기도록 할 수 없다.
Firefox와 Opera의 경우 IE에서 사용하는 방식을 이용하는 것이 아니라 display 라는 CSS 속성을 table로 설정하는 방식으로 처리가 가능하다. 이 방식의 경우 가장 밖에 있는 Layer에 display: table를 설정하고 두번째 Layer에는 display: table-cell; vertical-align: middle;를 설정하면 되는데 데이터가 많은 경우 Layer이 커져서 Layout이 변경되는 문제가 있다.
두번째 방식의 경우
두번째 방식은 좀더 복잡한데, 분리된 CSS와 JavaScript파일을 통합하면 다음과 같은 소스로 구성되어 있다.
<!-- Centered Layout Code by Christian Fecteau www.metaw3.ca 29/09/05 Do not remove this comment -->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Centered Layout</title>
<!-- Centered Layout Code Start -->
<style type="text/css" id="layout">
#outer { width: 50%; } /* customize width of the page: 700px, 90% etc. */
</style>
<script type="text/javascript">
/* Coded by Christian Fecteau 26/09/05 www.metaw3.ca */
var layout_ie5 = (window.ActiveXObject && document.getElementById);
var layout_ie5Mac = (!window.showModelessDialog && window.ActiveXObject && document.getElementById);
if (layout_ie5)
{
var layout_old_onload = null;
if (typeof window.onload == "function")
{
layout_old_onload = window.onload;
}
window.onload = layout_fix;
var layout_old_onresize = null;
if (typeof window.onresize == "function")
{
layout_old_onresize = window.onresize;
}
window.onresize = layout_fix;
if (!layout_ie5Mac)
{
var layout_ow = null;
var layout_ss = document.styleSheets('layout').rules;
for (var i = 0; i < layout_ss.length; i++)
{
if (layout_ss[i].selectorText == '#outer')
{
layout_ow = layout_ss[i].style.width;
}
}
}
}
function layout_fix()
{
if (layout_old_onload)
{
layout_old_onload();
layout_old_onload = null;
}
if (layout_old_onresize)
{
layout_old_onresize();
}
if (layout_ie5Mac)
{
document.body.style.height = '0px';
document.body.style.width = '0px';
}
else
{
if (layout_ow) middle.style.width = layout_ow;
outer.style.width = '100%';
}
outer.style.height = 'auto';
outer.align = 'center';
middle.align = 'left';
inner.style.styleFloat = 'left';
var layout_b = Number(document.body.clientHeight);
var layout_c = Number(inner.offsetHeight);
if (layout_b > layout_c)
{
outer.style.marginTop = (layout_b-layout_c)/2 + 'px';
}
else
{
outer.style.marginTop = '0px';
}
}
</script>
<!-- Centered Layout Code End -->
<style type="text/css">
#container {
color: black;
background-color: silver;
}
p {
font: 0.7em verdana, serif;
text-align: justify;
margin: 0;
}
body, html {
height: 100%;
margin: 0;
padding: 0;
border: 0;
}
#outer {
display: table;
height: 100%;
margin: 0 auto;
}
#middle {
display: table-row;
}
#inner {
display: table-cell;
vertical-align: middle;
}
</style>
</head>
<body>
<!-- Centered Layout Code Start -->
<div id="outer"><div id="middle"><div id="inner">
<!-- Centered Layout Code End -->
<!-- your page starts here -->
<div id="container">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
</div>
<!-- your page ends here -->
<!-- Centered Layout Code Start -->
</div></div></div>
<!-- Centered Layout Code End -->
</body>
</html>
IE의 경우 windows의 onresize 이벤트를 받아서 위치를 계산해 준다.
Firefox와 Opera의 경우 처음 방법과 비슷하게 display: table를 사용하는데 두번째 Layer에 display:table-row라는 속성을 지정한 부분만 차이가 있다.
이 방식의 문제점은 IE의 경우 onresize라는 event를 이용하는데 이 event가 DIV객체에서는 발생하지 않는다는 점이다.
웹스퀘어를 위한 개선책
이 두가지 방식 모두 2% 부족하므로 웹스퀘어를 위한 새로운 해결 방법을 고민하게 되었다. 먼저 웹스퀘어의 경우 별도의 Layout엔진을 사용하므로 하나의 HTML소스에서 여러 브라우저를 모두 지원하지 않아도 된다. 그래서 IE를 지원하기 위한 소스와 Firefox, Opera를 지원하기 위한 소스로 분리하였다.
IE의 경우
IE의 경우 첫번째 방식이 좋은 해결책이지만 데이터의 양이 많아지게 되면 스크롤바를 이용해서 데이터를 처리할 수 없다. 만일 첫번째 Layer의 overflow를 auto로 하게 되면 다음과 같은 일이 발생한다.
데이터의 양이 많아 져서 세번째 Layer의 높이가 첫번째 Layer의 50% 이상인 경우 스크롤바가 생기게 된다!!!
그래서 두번째 Layer의 margin-top을 첫번째 Layer와 두번째 Layer의 높이의 절반으로 설정하는 두번째 방식과 expression이라는 IE전용 CSS함수를 이용해서 다음과 같이 수정하였다.
<div id="layer1" style="background-color:#9999FF;">
<div id="layer2" style="background-color:#99FF99; margin-top:expression( ( eval(this.offsetParent.offsetHeight) - eval(this.offsetHeight) )/2 );">
ajax 테스트<br>
valign=middle<br>
데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>
</div>
</div>
expression은 계산 결과를 CSS에 적용할 수 있도록 지원하는 함수이다. 화면은 아래 처럼 나타나는데 데이터 양에 상관없이 정상적으로 동작하는 것을 확인할 수 있다.
전체 소스는 다음과 같다.
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=EUC-KR">
<title>DIV valign=middle</title>
<style>
#layer1 {position: absolute; top:100px; left:100px; height: 400px; width:700px; overflow: auto;}
</style>
<script>
function short() {
layer2.innerHTML = "ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플";
}
function middle() {
layer2.innerHTML = "ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>";
}
function long() {
layer2.innerHTML = "ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>";
}
</script>
</head>
<body>
모든 경우 정상적으로 동작하지만 expression의 overhead가 얼마나 될지 모른다.<br>
<a href="javascript:short()">짧은글</a>
<a href="javascript:middle()">중간글</a>
<a href="javascript:long()">긴글</a>
<div id="layer1" style="background-color:#9999FF;">
<div id="layer2" style="background-color:#99FF99; margin-top:expression( ( eval(this.offsetParent.offsetHeight) - eval(this.offsetHeight) )/2 );">
ajax 테스트<br>
valign=middle<br>
데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>
</div>
</div>
</html>
Firefox와 Opera의 경우
Firefox와 Opera의 경우에는 display:table 속성이 있는 Layer를 다시 display 속성이 없는 Layer로 감싸는 방식으로 해결할 수있다.
전체 소스는 다음과 같다.
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=EUC-KR">
<title>DIV valign=middle</title>
<style>
#layer1 {position: absolute; top:100px; left:100px; height: 400px; width:700px; overflow: auto;}
#layer2 {display: table; width:100%; height:100% }
#layer3 {display: table-cell; vertical-align: middle;}
</style>
<script>
function short() {
layer3.innerHTML = "ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플";
}
function middle() {
layer3.innerHTML = "ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>";
}
function long() {
layer3.innerHTML = "ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>";
}
</script>
</head>
<body>
ff.html의 문제를 해결하기 위해서 display 속성을 별도로 지정하지 안은 layer로 한번 더 감싸는 형태로 구성되어있다.<br>
<a href="javascript:short()">짧은글</a>
<a href="javascript:middle()">중간글</a>
<a href="javascript:long()">긴글</a>
<div id="layer1" style="background-color:#9999FF;">
<div id="layer2" style="background-color:#FF9999;">
<div id="layer3" style="background-color:#99FF99;">
ajax 테스트<br>
valign=middle<br>
데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>
</div>
</div>
</div>
</html>
2009년 6월 4일 목요일
javascript 클래스
class_name = function ( parameter, ... ) {
....
property declaration...
...
}
또는
function class_name ( parameter, ... ) {
....
property declaration...
...
}
function 이 함수를 의미하는 것이 아니라 여기서는 클래스 선언을 위해서 사용하는 키워드임. 단어 자체가 주는 사전적 의미에 함몰되어서 자꾸 딴지 걸면 안됨.(내가 ... 그랬었음..). 클래스나 함수나 어차피 프로세스로 존재할때 메모리를 차지하는 모듈로서 본다면 객체와 메소드 따위의 구분이 의미가 없다.
public class Student {
String name;
int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
}
위와같은 자바 클래스에 대응하는 자바스크립트의 클래스는 다음과 같음.
Student = function(name, age) {
this.name = name;
this.age = age;
} // 초간단. -_-;
다음과 같이 인스턴스를 생성하고 사용할 수 있음.
Student student = new Student("전지현", 26);
document.write("이름 : " + student.name + ", 나이 : " + student.age );
2. 메소드 정의하기.
메소드도 정의할 수 있는데 두가지 방법이 있음. prototype에 정의하는 방법과 클래스 자체에 정의하는 방법.
2-1. prototype 프로퍼티에 메소드 정의
위에서 만든 Student 클래스에 getter/setter 메소드를 정의하면 다음과 같다.
Student.prototype.getName = function() {
return this.name;
}
Studoent.prototype.setName = function (name) {
this.name = name;
}
이제 다음은 동일한 결과를 보여준다.
student.name;| student.getName();
student.name = "왕지현"; | student.setName("왕지현");
위에서 주의할 점은 Student.prototype.getName() 이 아니라 그냥 getName임. 괄호 넣으면 작동하지 않음. -_-a
여기서 잠깐 prototype 에대해서 짚고 넘어가보자.
자바에서 일반적인 객체 상속구조를 떠올려보자.
student.getAddress();
라고 호출하면 Student 클래스에서 아직 정의되어 있지 않기 때문에 부모 클래스에서 getAddress() 메소드가 있는지 찾는다. 존재 하지 않으면 그 부모클래스의 부모 클래스를 조회하고 마지막으로 Object 객체까지 거슬러 올라가서 최종적으로 찾지 못하면 예외가 발생한다.
javascript에서 정의되는 class는 모두 prototype이라는 프로퍼티를 가지는데 객체에 존재하지 않는 메소드나 클래스 필드를 호출하면 prototype 프로퍼티에서 메소드나 클래스 필드를 찾는다. prototype에서 찾지 못하면 prototype의 prototype 프로퍼티까지 계속해서 탐색을 하고 최종적으로 찾지 못하면
"undefined"(클래스 필드인 경우) , ""(메소드인 경우)
를 출력한다. 위에서 메소드를 정의한 방식이 바로 이 prototype 프로퍼티에 메소드를 정의한 것인데 엄밀히 말하면 위에서 정의한 메소드는 Student 클래스의 것이 아닌, prototype 프로퍼티의 메소드인 셈이다. 하지만 사용하는 입장에서는 중요한 내용은 아닌 듯.
기억해야 할 점은 prototype 프로퍼티는 인스턴스당 할당되는 것이 아니라, 클래스당 하나씩 할당된다는 것. 마치 자바에서 Object.class, ArrayList.class 와 같은 Class 를 떠올리면 좋을듯 하다.
student.getEmail() 을 호출하면 정의되지 않은 메소드이므로 TypeError가 발생하고 진행중인 메소드가 종료된다. 아무것도 출력이 안되는 것처럼 보이지만 예외가 던져져서 진행중이던 메소드를 벗어나버리는 것임.
Student jenny = new Student(....);
try {
jenny.getEmail(); // NO!!!
} catch(e) {
alert(e); // firefox에서는 예외가 던져짐.
}
Student.prototype.getEmail = function() { return googler@gmail.com; };
jenny.getEmail() ; // OK!!!!!!!!!!!
위처럼 prototype에 메소드를 정의하면 메소드를 정의하기 이전에 생성되어 사용되던 인스턴스라도 getEmail() 메소드를 사용할 수 있게 된다. 왜냐하면 위에서 말했듯이 prototype은 클래스당 하나이므로 하나의 클래스에서 생성된 인스턴스들은 prototype을 공유한다.
이때문에 언제든지 메소드나 클래스 필드를 마음대로 만들어낼 수 있으니 조낸! 탄력적이다!!
2-2. 클래스 자체에 메소드 정의
prototype 이 아닌 class 자체에 메소드를 생성하는 방법도 있다.
Student = function(name, age) {
this.name = name;
this.age = age;
this.getName = function() { return this.name ; }
this.setName = function(name) { this.name = name;}
} // 초간단. -_-;
그렇다면 메소드를 prototype에 정의하는 것과 클래스 자체에 정의하는게 어떻게 다를까?
클래스 자체에 정의할 경우, 클래스의 인스턴스를 여러개 생성할 때 메소드 코드를 인스턴스들마다 따로 갖게 된다. 하지만 prototype에 메소드를 정의하면 동일한 클래스로부터 생성된 인스턴스들은 하나의 prototype 프로퍼티를 공유하므로 인스턴스들마다 중복해서 메소드 코드를 가질 필요가 없게된다.
따라서 자바스크립트에서 클래스를 생성할 때 가능하면 prototype에 메소드를 정의하는게 메모리를 아낄 수 있는 길이다...라고 일단 정리하게 넘어가겠다. (그런데 메소드를 클래스 자체에 정의해야 하는 상황도 있지 않을까? 잠깐 고민해봤는데, 아직은 없는 것 같다. 인스턴스의 상태는 프로퍼티에 좌우되기 때문에 메소드를 인스턴스마다 따로 가져야할 상황은 없는 듯.)
하지만 다음과 같이 오버하면 안된다.
Student = function(name, age) {
Student.prototype.name = name;
Student.prototype.age = age;
}
이렇게 하면 클래스의 인스턴스들이 모두 동일한 이름과 나이를 갖게 된다. 즉,
Student jane = new Student("jane", 21);
을 생성한 후
Student jack = new Student("jack", 34);
로 jack을 생성하면 jane의 이름이 "jack", 나이가 34살로 둔갑한다.(뜨아~).
정리하면 상황에 맞게 잘 사용해야 한다는 것이다.
2-3 효율적인 클래스 정의 방법
2-1 방식의 문제점은 클래스를 정의할 때 코드가 난잡해져서 가독성이 떨어진다는 점이다. 클래스를 여러개 정의하는 상황이라면 메소드를 정의한 블록들이 산재해 있어서 클래스의 모습이 눈에 딱 들어오지는 않는다.
반면에 2-2는 코드가 깔끔해져서 가독성이 높아지지만 문제는 위에서 말했듯이 인스턴스들이 중복된 메소드 코드를 갖는다는 점이다.(간단한 클래스라면 상관없겠지만...)
그래서 생각해낸 방법은 다음과 같다.
function Student(name, age)
{
var strName = name;
var intAge = age;
Student.prototype.getName() { return this.strName; }
Student.prototype.setName(name) { this.strName = name; }
Student.prototype.getAge() { return this.strAge; }
Student.prototype.setAge(age) { this.intAge = age; }
}
메소드 정의부를 클래스 내로 옮기면서
fucntion class_name ( parameter, ... )
{
class_name.prototype.method_name ( parameter, ... ) { ... };
.....
}
로 바꿔주는 것. IE와 FF 에서 테스트해봤는데 아무 문제없이 잘 돌아간다. 흐화화~
3. JSON 표기법을 이용한 클래스 정의
아, 이런것도 있다. JSON에 대한 자세한 내용은 http://www.json.org/ 에서 보면 될 것 같다.
위에서 메소드 정의하는 부분을 아래의 코드로 표현했다.
Student.prototype.getName = function() {
return this.name;
}
Studoent.prototype.setName = function (name) {
this.name = name;
}
위의 코드를 JSON 을 이용해서 나타내면 다음과 같다.
Student.prototype = {
getName : function() {
return this.name;
}
setName : function (name) {
this.name = name;
}
}
4. 정보은닉(encapsulation)
위에서 설명한 클래스 정의 방법은 정보은닉이 안된다는 문제점이 있다. student.name 으로 프로퍼티에 접속이 가능한데 OOP 에서는 이런 접근을 꺼리기 때문에 자바스크립트에서도 이걸 흉내내려는 시도가 있지 않았나 추측해본다.
여자들의 몸무게 평균을 내는 저울이 있다고 치자. 몇 명의 여성의 정보를 가져와서 전체 몸무게을 내는데 여자들은 자신의 몸무게가 드러나는 것을 반대한다. 저울을 통해서 특정 여성의 몸무게를 알아서는 안되는 경우를 생각해보자.
이런 상황을 모델링하면 다음과 같다.
function Scale ( ) {
this.womans = [new Woman(...), new Woman(...), ...];
this.prototype.getTotalWeights() {
var totalWeight = 0;
for( i = 0 ; i < womans.length ; i++) {
totlaWeight += this.womans[i].getWeight();
}
return totalWeight;
}
}
.....
Scale scale = new Scale() ;
scale.getTotalWeight();
위코드에서는
scale.womans[0].getWeight();
로 특정 여성의 몸무게에 접근할 수 있다.
정보은닉이란 외부에 노출되어서는 안되는 데이터를 꼭꼭 감추는 것을 의미하는데 여기서 여성들의 몸무게 노출을 막기 위해서는 다음과 같이 프로퍼티 선언을 변경해준다.
function Scale ( ) {
var womans = [new Woman(....), new Woman(....), new Woman(...)];
this.prototype.getTotalWeights() {
var totalWeight = 0;
for( i = 0 ; i < womans.length ; i++) {
totlaWeight += womans[i].getWeight();
}
return totalWeight;
}
}
이제 scale.womans 로 접근하면 "undefined"가 출력되기 때문에 여성들 개개인의 몸무게를 보여주는 메소드 호출을 할 수 없다.
5. 정리하면.
자바스크립트에서 클래스를 정의해서 사용하는게 불필요하고 이상해 보일 수도 있다. 왜냐하면 지금까지 이렇게 하지 않아도 자바스크립트를 잘만 써왔기 때문이다. 이런 편견은 예전에 자바스크립트가 화면을 동적으로 구성하는 도구로서 사용되어왔기 때문에 생긴 것이 아닌가 싶다. 또한 언어가 매우 탄력성이 높아서 초기에 자바스크립트를 어떠한 체계에 맞춰서 사용할지 뚜렷한 가이드라인이 없었기 때문이기도 하다.
하지만 ajax의 도입으로 자바스크립트가 데이터 처리를 위한 수단으로서 많이 사용되고 있다. 서버쪽에서는 데이터 처리만 해서 클라이언트에 전달해주고 클라이언트는 ajax 를 이용해서 데이터를 2차 가공한 후 display하는 ui 과정을 모두 떠안게 되는 것이다.
이러한 변화에 대응하기 위해서 자바스크립트도 class 를 도입해서 좀 더 체계적으로 코드를 만들어어지 향후 유지 보수하는데 어려움이 없을 것으로 생각된다. 예전에는 자바스크립트를 아주 우스운 언어, 웹 디자이너들이나 다루는 수준낮은 언어로 생각해왔지만 이제는 생각을 바꿀 때가 된 것 같다.
OOP의 개념을 자바스크립트에 이식해서 언어의 질을 한층 높일 때가 되지 않았나 싶다.
출처 : 플밍
2009년 4월 2일 목요일
오라클 페이징
오라클 페이징 쿼리..
-----------------------------------
SELECT *
FROM (SELECT T0.*, FLOOR((ROWNUM - 1) / 20 + 1) PAGE
FROM (
-- 쿼리를 할 실제 부분 SS
SELECT BBS_ID
,DEPTH_CD
,BBS_SEQ
,TTL
,CNTS
,COUNT(*) OVER() AS TOT_CNT
FROM MTI_BBS
-- 쿼리를 할 실제 부분 EE
) T0
)
WHERE PAGE = 1 --변수로 page 값을 넘겨받음