질문/답변 페이지 많이 이용해 주세요.

Archive for the ‘Development’ Category.

북마크 프로그램 만들려 하면서, …

일전 포스트에서 적었듯, SiteBar( http://sitebar.org/ ) 대신할 프로그램을 만들어볼까 고민중인데, …

기술적인 구현도 문제지만 ( RESTful Server, 인증 )

개념적인 구현도, 막상 닥쳐보니 고민할 것이 많다.

우선, Tag 개념은 넣을려고 하는데, 이건 그리 복잡하지 않을 수 있겠다 싶기도 한데, …
( 실제 데이터 모델을 생각하니 복잡할 수도. … )

먼저, Category 혹은 Directory 개념은 있어야 할 듯 하고, 바로 막막하네, …
그래서, 잠시 ownCloud 같은 클라우드를 백으로 활용할까 생각하고, …

Directory 개념은 Tree 구조로 가져가는 것이 복잡할 것 같지만, 향후를 생각해도 기본적인 것 같아 복잡하다.

추가로, … public, private 개념을 두려고 하니, … 또 하나, ACL 넣기가 필요하다.

역시나 기존에 구현된 filesystem의 권한 및 tree 구조를 바로 활용하면 편한데, …
이번 프로젝트는 직접 구현해 보는 것이 목표라서 re-inventing wheel은 하려고 한다.

firefox의 bookmark API는 우선 고려 대상에서 제외하고, ( bookmark를 사용할 분들은 잘 사용하시고, 나는 따로 서버에 두고, bookmark와는 별개의 sidebar와 web interface, 앱 인터페이스로 가려고 한다 … )

그래서 생각하는 것이, sidebar를 사용하고, 거기에 SiteBar와 유사하게 디렉토리 구조를 표현하면서, drag and drop을 편하게 해 보고자 한다.

drag and drop은 내 블로그에 전에 테스트하던 것을 참조하면서도, 오늘 추가로 아래 자료도 보고,
https://developer.mozilla.org/en-US/docs/Web/Events/drop
https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API/Drag_operations
https://24ways.org/examples/marking-up-a-tag-cloud/example.html

게시판도 하나 직접 만들어보지 않은 dgkim이라, directory, acl, tag, 인증을 적절하게 만들어보면, 다른 업무용 프로그램 만드는데도 쉽게 재활용할 수 있지 않을까 기대하는데, …
기존 구현된 tree나 relation, rdb 구조를 보면 어렵지는 않을 것 같은데, 로직 코드를 그다지 꺼내쓰듯 하지 못하니 어려움으로 다가오는듯 하다.

ps. 그리고, 만들고 나서는 현재의 2900여개의 북마크를 이전해야 하는데, …
여전히, 내 자료 저장은 좀 다양한 도구에 의존하는데, 개선해야 하나 싶기도 하고, … 메일, 블로그, 클라우드, 파일서버, 북마크, 노트, …

그러고 보니, 북마크를 만들고 나면, 자신감을 가지고 노트도 대신할 것을 만들까 싶지만, 거기에는 문서 표현(wiki? or html?)이라는 어려운 벽이 또 하나 있다.

firefox sidebar를 준비해보다…

아래 삽질 기록이 좀 있습니다만.

우선, 기본적인 클라이언트측 컨셉은 가능하겠다는 판단까지 왔습니다.

우선, webextension 예제에서 2가지를 테스트하였고, 조합하면 동작할 것 같습니다.

https://github.com/mdn/webextensions-examples/tree/master/annotate-page
위 예제를 통해서, sidebar를 띄우는 방법이 나왔습니다.
단, sidebar 내용이 서버측 내용이 아닌 로컬 내용인데, 로컬에서 커버할지 웹을 띄울지 고민이 필요한 것 같습니다.

https://github.com/mdn/webextensions-examples/tree/master/bookmark-it
bookmark에 바로 접근하는 것이 가능한 것으로 나오는데,
이걸 활용하고, 로컬 북마크와 서버측 북마크를 연동할 것인지는 고민이 필요합니다.

annotate-page는 sidebar에 뭔가 표시가 가능하다는 것을 의미하며,
bookmark-it은 currentTab.url에 접근 가능하고, XHR 요청이 가능하다는? 것이 테스트되었습니다.

이제 이걸 쓸만하도록 클라이언트 앱을 만드는 것이 필요한데, …
먼저 해결해야할 산이 남은 것이 아래 적은 것과 같이. codeigniter restful 서버 만드는 것, 그 전에 인증 문제 해결하는 것 등이 남았네요. 웹에 표현하는 tag cloud 같은 건 논외로 하더라도, …

어쨌든, firefox extension 완성되면, codeigniter 버전도 만들어 보고, django 버전도 만들어 보면 좋겠다는 계획입니다. … 이 계획이 언제 실현되어 제품으로 나올지는 모르겠지만 … spring에서는 restful API나, spring-security가 경험이 있어 model만 만들면 바로 동작하는 것은 만들 수 있겠다고 생각하지만,, …. 무엇보다 큰 문제는, 사용자 웹 인터페이스가 …


예전에는 SiteBar라는 프로그램을 사용하여 북마크를 관리?했었습니다.

그러던 중, SiteBar가 PHP7과 호환성 문제가 있어 사용하지 못하게 되었습니다.

그래서 이번에는 한번 만들어 볼까? 고민하고 있었지요.

간편한 시작을 위해서 웹서버쪽을 PHP + CodeIgniter사용하고,
클라이언트는 Firefox Sidebar를 사용해 보고자 했습니다.

Firefox Sidebar를 만드는 방법이 역사가 있고, 바뀌어 가기도 하는데, 그중 최신인,
web extension을 사용해서 틀을 잡아보기로 하였습니다.

https://github.com/mdn/webextensions-examples/

그런데, …

XHR을 통해서 링크를 서버로 보내는 것을 준비하다가, …

심각한 상황에 빠졌느데, … CI에서 log_message 걸다보니, httpd가 hang 걸리고, …

그러다가, gdb, lsof, debuginfo-install httpd-2.4.6-45.el7.centos.x86_64
하는 상황까지 오게된. …

그나 저나, 해야 할 일들은 아래와 같이 시작도 못했는데, …

1. firefox web extension 사용하여 sidebar 활용 등의 클라이언트 프로그램 준비
2. 서버측 bookmark 모델 설계
3. 서버측 codeigniter LDAP 및 인증 모듈 개발
4. 서버측 codeigniter bookmark RESTful 인터페이스 개발
5. 서버측 codeigniter web 인터페이스 개발 ( bootstrap 사용할까? )

참조 URL
https://developer.mozilla.org/en-US/docs/Mozilla/Creating_a_Firefox_sidebar
https://github.com/kyoshino/simple-sidebar
https://developer.mozilla.org/en-US/Add-ons/WebExtensions
https://developer.mozilla.org/en-US/Add-ons/WebExtensions/user_interface/Sidebars
https://github.com/mdn/webextensions-examples/

ps. 헐, 위에 적은 debuginfo-install은 시작이었고, 아래와 같은 엄청난 메시지가 나오는데, 포기할까 싶은 생각이 바로 …
debuginfo-install bzip2-libs-1.0.6-13.el7.x86_64 cyrus-sasl-lib-2.1.26-20.el7_2.x86_64 elfutils-libelf-0.166-2.el7.x86_64 elfutils-libs-0.166-2.el7.x86_64 file-libs-5.11-33.el7.x86_64 freetype-2.4.11-12.el7.x86_64 gmp-6.0.0-12.el7_1.x86_64 keyutils-libs-1.5.8-3.el7.x86_64 krb5-libs-1.14.1-27.el7_3.x86_64 libX11-1.6.3-3.el7.x86_64 libXau-1.0.8-2.1.el7.x86_64 libXpm-3.5.11-3.el7.x86_64 libattr-2.4.46-12.el7.x86_64 libcap-2.22-8.el7.x86_64 libcom_err-1.42.9-9.el7.x86_64 libcurl-7.29.0-35.el7.centos.x86_64 libgcc-4.8.5-11.el7.x86_64 libgcrypt-1.5.3-13.el7_3.1.x86_64 libgpg-error-1.12-3.el7.x86_64 libidn-1.28-4.el7.x86_64 libjpeg-turbo-1.2.90-5.el7.x86_64 libpng-1.5.13-7.el7_2.x86_64 libssh2-1.4.3-10.el7_2.1.x86_64 libstdc++-4.8.5-11.el7.x86_64 libuuid-2.23.2-33.el7.x86_64 libxcb-1.11-4.el7.x86_64 libxml2-2.9.1-6.el7_2.3.x86_64 libxslt-1.1.28-5.el7.x86_64 libzip-0.10.1-8.el7.x86_64 mariadb-libs-5.5.52-1.el7.x86_64 mod_dav_svn-1.7.14-10.el7.x86_64 mod_wsgi-3.4-12.el7_0.x86_64 nspr-4.11.0-1.el7_2.x86_64 nss-3.21.3-2.el7_3.x86_64 nss-softokn-freebl-3.16.2.3-14.4.el7.x86_64 nss-util-3.21.3-1.1.el7_3.x86_64 openldap-2.4.40-13.el7.x86_64 openssl-libs-1.0.1e-60.el7.x86_64 php-5.4.16-42.el7.x86_64 php-common-5.4.16-42.el7.x86_64 php-gd-5.4.16-42.el7.x86_64 php-ldap-5.4.16-42.el7.x86_64 php-mbstring-5.4.16-42.el7.x86_64 php-mysql-5.4.16-42.el7.x86_64 php-pdo-5.4.16-42.el7.x86_64 php-process-5.4.16-42.el7.x86_64 php-xml-5.4.16-42.el7.x86_64 python-libs-2.7.5-48.el7.x86_64 sqlite-3.7.17-8.el7.x86_64 subversion-libs-1.7.14-10.el7.x86_64 t1lib-5.1.2-14.el7.x86_64 xz-libs-5.2.2-1.el7.x86_64
지금 서버가 테스트 개발 서버라면 몰라도, 준 운용 서버인데, 위와 같은 짓을 하기는 싫은데, …

ps. 환장할 일일쎼… Java는 보통 hang상황에서 thread dump라는 편리한 도구를 사용했었고, apache는 server-status 활용하고, tcpdump나 lsof 등으로 대충 찍으면, 뭔짓을 하는지 나왔었는데, … 지금 버그인지, 뭔지는, gdb로 찾아야 하다니 하면서, …

ps. 정확한 원인은 모르겠으나, 우선 보이는 것부터 잡아보려고, …
#9 0x00007f01cba6a344 in php_verror (docref=, params=params@entry=0x7f01cbb81ce5 “”, type=type@entry=2,
format=format@entry=0x7f01cbb7bb08 “It is not safe to rely on the system’s timezone settings. You are *required* to use the date.timezone setting or the date_default_timezone_set() function. In case you used any of those methods and you”…,
args=args@entry=0x7fff62819750) at /usr/src/debug/php-5.4.16/main/main.c:862

오늘은 가볍게, vuejs 시작해보기. … 그러다가, bootstrap으로 …

어제, vuejs, reactjs, angularjs 관련 글을 보고, …

angular 같은 경우, typescript 압박에 … 시작하다 말았고, … react도 나중에 보기로 하면서, …

그나마 가볍게 시작하기 좋지 않을까 생각에 vuejs를 시작해 본다. … test 서버를 빨리 만들면 좋은데, 돈이 없어서, …

시작 글을 적어 두고, 차근 차근 시작 …

{{ }} 구문으로 데이터를 전달하는 것이 쉽게 이해되긴하는데, …
v-bind 까지도 쉬운데, … v-if v-for 넘어가니 뭔가 새로운 세상이 열리는 듯 하다. …
일부러 오류를 일으켜 보아야지…
구문 오류나 이름 오류도 잘 잡아내긴 하네, … 역시나 스크립트 영역이다 보니, 이런 특징 때문에 싫어할 사람도 있을 듯.

component까지 오니, 다른 느낌이 있다.
jquery 같은 경우, DOM은 그대로 두고, 거기에 붙여서 쓸 수 있게 해 뒀다고 한다면,
vuejs 같은 경우, DOM에 대한 통제를 완전히 가져가 버린 듯.

참 좋은데, 개념도 알겠고, 코드와 뷰를 엮어 준다는 것까지는 알겠는데, …
extjs 같은 실제 화면 디자인을 품고 있는 것은 아닌 듯 하다,
dgkim은 바로 제품으로(?) 쓸 수 있는 디자인 및 CSS가 포함된 것이 필요한데, …

bootstrap을 파 보아야 할까?

그래서, 바로 bootstrap으로 전환하여 이어 나가본다.

bootstrap 시작하고, navigation bar 따라해 보았다. …

그런데, .. 머리속에 아직 만들고 싶은 것의 스케치가 없어서 이어나가지 못한다.

NodeJS, MongoDB 볼까?

javascript가 참 끌리는 언어이다.

nodejs의 한계도 있는데…

MongoDB document db 개발자에게 유리한 것은 많은데, ….

성능이 문제가 안 될까 싶은데, 아래 인용구가 답

The RDBMS optimizes data for storage efficiency (as it was conceived at a time when storage was the most expensive component of the system).

MongoDB’s document model is optimized for how the application accesses data (as developer time and speed to market are now more expensive than storage).

조직도 JavaScript 실험. recursive function. 연습.

커뮤니티에 javascript를 사용하여, 조직도를 구현하는 것에 대한 질문이 올라왔다.
그래서, 나는 프로그래밍 연습을 하기 위해서, 한 번 만들어보기로 결심하고, 답변을 올렸다.

https://okky.kr/article/389606

올리자마자 다시 봐도, 코드 상에 잘못된 부분은 보이지만, 그런 것들은 나중에 시간날지 고칠지 하고, 우선 코드를 내 블로그에 복사해 놓는다.

var depth1 = [];
var depth2 = [];
var depth3 = [];
var depth4 = [];

depth1.push(
    {
        GROUP_ID: 1,
        GROUPNAME: '본사',
	children: []
    },
    {
        GROUP_ID: 2,
        GROUPNAME: '지사',
	children: []
    }
);

depth2.push(
    {
        GROUP_ID: 10,
        GROUP_ID_P: 1,
        GROUPNAME: '기술지원본부',
	children: []
    },
    {
        GROUP_ID: 20,
        GROUP_ID_P: 1,
        GROUPNAME: '연구소',
	children: []
    },
    {
        GROUP_ID: 30,
        GROUP_ID_P: 2,
        GROUPNAME: '영업부',
	children: []
    }
);

console.log(depth1);
console.log("========================================");
console.log(depth2);
console.log("========================================");

/*
var datasource = { children: [] };

for(var i=0; i < depth1.length; i++){
	datasource['children'].push({'name': depth1[i]['GROUPNAME'], 'gid': depth1[i]['GROUP_ID'],'children': []});
	for(var j=0; j < depth2.length; j++){
		if(depth1[i]['GROUP_ID'] == depth2[j]['GROUP_ID_P']){
			datasource['children'][i]['children'].push({'name': depth2[j]['GROUPNAME'], 'gid': depth2[j]['GROUP_ID'], 'children':[]});
			for(var k=0; k < depth3.length; k++){
				if(depth2[j]['GROUP_ID'] == depth3[k]['GROUP_ID_P']){
					for(var l=0; l < datasource['children'][i]['children'].length; l++){
						if(depth2[j]['GROUP_ID'] == datasource['children'][i]['children'][l]['gid']){
							datasource['children'][i]['children'][l]['children'].push({'name': depth3[k]['GROUPNAME'], 'gid': depth3[k]['GROUP_ID'], 'children':[]});
							for(var m=0; m < depth4.length; m++){
								if(depth3[k]['GROUP_ID'] == depth4[m]['GROUP_ID_P']){
									for(var n=0; n < datasource['children'][i]['children'][l]['children'].length; n++){
										if(depth3[k]['GROUP_ID'] == datasource['children'][i]['children'][l]['children'][n]['gid']){
											datasource['children'][i]['children'][l]['children'][n]['children'].push({'name': depth4[m]['GROUPNAME'], 'gid': depth4[m]['GROUP_ID'], 'children':[]});
										}
									}
								}
							}
						}
					}
				}
			}
		}
	}
}

console.log("===== FINAL =====");
console.log(JSON.stringify(datasource));
*/

/**
 * tree에서 gid를 가지는 객체를 반환한다.
 * 못 찾으면 undefined를 반환한다.
 */
function findById(tree, gid) {

	//console.log("========================================");
	//console.log(new Error().stack);
	//console.log("========================================");

	if ( tree['GROUP_ID'] == gid ) {
		return tree;
	} else if ( tree.children.length == 0 ) {
		return undefined;
	} else {
		for ( var i = 0 ; i < tree.children.length ; i ++ ) {
			var found = findById(tree.children[i], gid);
			if ( found !== undefined ) {
				return found;
			}
		}
		return undefined;
	}
}

var ROOT = {
	'GROUP_ID': 0,
	'GROUPNAME': 'ROOT',
	'children': []
}

depth1 = depth1.concat(depth2);
for ( var i = 0 ; i < depth1.length ; i ++ ) {
	if ( depth1[i]['GROUP_ID_P'] === undefined ) {
		ROOT.children.push(depth1[i]);
	} else {
		var parent = findById(ROOT, depth1[i]['GROUP_ID_P']);
		if ( parent !== undefined ) {
			parent.children.push(depth1[i]);
		}
	}
}

console.log("FINAL");

console.log(JSON.stringify(ROOT));

tomcat parallel deployment

톰캣이 이미 8.0까지 나온 시점에 인터넷에서 흥미를 끄는 내용을 하나 접했습니다.

parallel deployment라는 제목에서는

전략을 얘기하는 것인 줄 알았는데, 기능이었습니다.

tomcat 7.0 부터 지원하는 기능으로 무중단 배포를 할 수 있는 기능입니다.

즉, app v1 운용중에 app v2를 배포할 수 있고,
기존 세션 사용자는 v1을 사용하고 신규 접속자는 v2로 접근하게 됩니다.

저는 weblogic에서 production redeployment로 접했던 기능입니다.

상세내용은 위 링크에서 참조하십시오.

‘Objective-C for absolute Beginners’를 봅니다.

2013-002 – 2013/04/30 – Objective-C for absolute Beginners – Gary Bennett 외 2 – 공학 – 전자책 – 링크 – 읽는중

gcc로 몇가지 연습 -c -S -E

gcc로 c언어 컴파일을 몇 가지 실습해 보았습니다.

-c 옵션

gcc 명령에 -c 옵션을 주면, .c파일을 컴파일만하고 링크를 하지 않습니다. 결과물로는 .o 파일이 생성됩니다.

test.c 파일

int test(int a, int b) {
    return a + b;
}

int main(int argc, char* argv[]) {
    test(3,4);
    return 0;
}
[dgkim@dgkim asm]$ ls -al
합계 12
drwxr-xr-x   2 dgkim users 4096 2013-01-03 19:37 .
drwxrwxrwt. 18 root  root  4096 2013-01-03 19:34 ..
-rw-r--r--   1 dgkim users  112 2013-01-03 18:12 test.c
[dgkim@dgkim asm]$ gcc -c test.c
[dgkim@dgkim asm]$ ls -l
합계 8
-rw-r--r-- 1 dgkim users  112 2013-01-03 18:12 test.c
-rw-r--r-- 1 dgkim users 1464 2013-01-03 19:37 test.o
[dgkim@dgkim asm]$

-c 옵션으로 생성된 .o 파일을 gcc를 통해 링크하려면 아래와 같이 할 수 있습니다.
여러 개의 .c 파일을 가지는 프로그램 및 링크 과정 및 순서에 따라, -c 옵션으로 컴파일만하고, 마지막에 .o 파일들을 묶는 일은 자주 있을 것입니다.

[dgkim@dgkim asm]$ gcc -o test test.o
[dgkim@dgkim asm]$ ls -l
합계 16
-rwxr-xr-x 1 dgkim users 6379 2013-01-03 19:39 test
-rw-r--r-- 1 dgkim users  112 2013-01-03 18:12 test.c
-rw-r--r-- 1 dgkim users 1464 2013-01-03 19:37 test.o
[dgkim@dgkim asm]$

-S 옵션

gcc에서 -S 옵션을 주고 실행하면, .c파일에서 .s파일, 즉 어셈블러 소스가 생성됩니다.

[dgkim@dgkim asm]$ ls -al
합계 28
drwxr-xr-x   2 dgkim users 4096 2013-01-03 19:41 .
drwxrwxrwt. 18 root  root  4096 2013-01-03 19:39 ..
-rwxr-xr-x   1 dgkim users 6379 2013-01-03 19:39 test
-rw-r--r--   1 dgkim users  112 2013-01-03 18:12 test.c
-rw-r--r--   1 dgkim users 1464 2013-01-03 19:37 test.o
-rw-r--r--   1 dgkim users  807 2013-01-03 19:41 test.s
[dgkim@dgkim asm]$

위의 간단하고 짧던 .c 파일은 아래와 같은 복잡한 .s 파일을 만들었습니다.

        .file   "test.c"
        .text
.globl test
        .type   test, @function
test:
.LFB0:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        movl    %edi, -4(%rbp)
        movl    %esi, -8(%rbp)
        movl    -8(%rbp), %eax
        movl    -4(%rbp), %edx
        leal    (%rdx,%rax), %eax
        leave
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
.LFE0:
        .size   test, .-test
.globl main
        .type   main, @function
main:
.LFB1:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        subq    $16, %rsp
        movl    %edi, -4(%rbp)
        movq    %rsi, -16(%rbp)
        movl    $4, %esi
        movl    $3, %edi
        call    test
        movl    $0, %eax
        leave
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
.LFE1:
        .size   main, .-main
        .ident  "GCC: (GNU) 4.4.6 20120305 (Red Hat 4.4.6-4)"
        .section        .note.GNU-stack,"",@progbits

-E 옵션

gcc에서 -E 옵션을 주면, preprocessor 처리가 된 c파일의 내용을 볼 수 있습니다.

preprocessor 처리를 위해서 test.c 파일에서 #define 사용하여 작은 수정을 한 결과입니다.

[dgkim@dgkim asm]$ cat test.c
#define a alpha
#define b bravo

int test(int a, int b) {
    return a + b;
}

int main(int argc, char* argv[]) {
    test(3,4);
    return 0;
}
[dgkim@dgkim asm]$ gcc -E test.c
# 1 "test.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "test.c"



int test(int alpha, int bravo) {
    return alpha + bravo;
}

int main(int argc, char* argv[]) {
    test(3,4);
    return 0;
}
[dgkim@dgkim asm]$

ps. 다음에는 -m32 옵션을 통해서 x86_64 머신에서 x86 i686용 빌드를 테스트한 글을 올려볼까요? glibc-devel.i686
ps2. 그리고, gdb를 통해서 위 파일을 디버깅하였는데, 그 글도 올려볼까요?

파이어폭스에서 드래그앤드롭으로 파일업로드 구현하기

파이어폭스에서는 파일업로드를 드래그앤드롭으로 할 수 있습니다.

3.6 버전에서부터 지원했던 것 같습니다.

자바스크립트와 Spring 프레임워크를 사용해서 간단하게 파일 업로드 프로그램을 작성해 보았습니다.

HTML + JavaScript 업로드 구현부분 UI

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>파일업로드</title>
</head>
<body>
    <form name="form0" action="file/upload" method="POST" enctype="multipart/form-data">
        <ul>
            <li>파일 하나 업로드 : <input id="file1" type="file" name="file" /></li>
            <li>여러 파일 업로드 : <input id="file2" type="file" name="multifile" multiple /></li>
            <li>파일을 선택하고 '전송'을 눌러 업로드하시거나, 탐색기에서 파일을 드래그앤드롭하여 업로드할 수 있습니다. 드래그앤드롭은 '전송'을 누르는 것이 아니라 드롭시점에 바로 업로드됩니다.</li>
        </ul>
        <input type="submit" value="전송"/>
    </form>
    <script type="text/javascript">
    var dropzone1 = document.getElementById('file1');
    var dropzone2 = document.getElementById('file2');
    setDnDhandler(dropzone1);
    setDnDhandler(dropzone2);
    
    function setDnDhandler(obj) {
        obj.addEventListener("dragover", function(event) {
            event.preventDefault();
        }, true);
        obj.addEventListener("drop", function(event) {
            event.preventDefault();
            var allTheFiles = event.dataTransfer.files;
            for (var i=0; i<allTheFiles.length; i++) {
                var element = document.createElement('div');
                element.id = 'f' + i;
                document.body.appendChild(element);
                sendFile(allTheFiles[i], element.id);
            }
        }, true);
    }
    function sendFile(file, fileId) {
        var xhr = new XMLHttpRequest();
        
        xhr.upload.addEventListener("progress", function(e) {
                if (e.lengthComputable) {
                    var percentage = Math.round((e.loaded * 100) / e.total);
                    document.getElementById(fileId).innerHTML = file.name + ' - ' + percentage + '%';
                }
            }, false);
        xhr.onreadystatechange = function() {  
            if (xhr.readyState == 4 && xhr.status == 200) {
                alert(xhr.responseText);
            }
        };
        xhr.upload.addEventListener("load", function(e){
                document.getElementById(fileId).innerHTML = file.name + ' - uploaded';
            }, false);
        xhr.open("POST", "file/upload");
        var fd = new FormData();
        fd.append("file", file);
        xhr.send(fd);
    }
    </script>
</body>
</html>

드래그앤드롭을 사용할 때, 드롭될 영역의 경우는 input형이 아니라도 가능하지만, 일반 HTML전송도 가능하게 예제를 작성하고자 input을 사용하였습니다.

드래그앤드롭을 지원하는 부분은 addEventListener 부분입니다. “dragover”, “drop” 이벤트에 대하여 기본동작을 막고, 자바스크립트처리를 할 수 있도록 하였습니다.

파이어폭스에서 input에 type이 file이면, 기본동작이 파일이 업로드를 위해 준비됩니다. IE의 경우는 그 동작조차 없으며, 파일 열기가 수행되지요.

drop이벤트에서 XMLHttpRequest를 통해 AJAX업로드를 합니다.

“xhr.upload.addEventListener(“progress”, …);” 부분은 업로드 처리 경과를 확인할 수 있는 기능입니다. 업로드된 양을 퍼센트로 출력해 줍니다.

서버에서 파일 받기

package test.control;

import java.io.File;
import java.io.IOException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

@Controller
@RequestMapping("/file")
public class FileController {
    
    @RequestMapping("upload")
    public @ResponseBody String upload(HttpServletRequest request,
            HttpServletResponse response) throws IOException {
        if (! (request instanceof MultipartHttpServletRequest)) {
            response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Expected multipart request");
            return null;
        }
        MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
        for ( String fileName : multipartRequest.getFileMap().keySet() ) {
            for ( MultipartFile file : multipartRequest.getFiles(fileName) ) {
                String originalFileName = file.getOriginalFilename();
                File destination = File.createTempFile("file", originalFileName, new File("/var/dgkim"));
            }
        }
        return "success";
    }
}

스프링 MVC 프레임워크를 통해, 저수준의 파일 처리부분은 빠지고 실제 파일명과 File객체 처리를 바로 할 수 있습니다.

위 예에서는 의미없이 임시 파일만 생성했으나, 필요에 따라 처리를 작성할 수 있겠습니다.

서버측 라이브러리는 Maven으로 관리하였으며, Spring 3.0외에 추가로 아래 의존성이 있습니다.

        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.2</version>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>1.3</version>
        </dependency>

소스 다운로드 : http://www.dgkim.net/dav/users/dgkim/fileuptest.zip

참조

웹로직의 보편적인 웹개발상의 디버그

웹로직을 사용하면서, 에러의 원인을 쉽게 찾을 수 없을 때, 원인을 추적하는 방법을 나열해 봅니다.

dispatcher forward 사용시 해당 리소스가 없을 경우 추적하기.( 알 수 없는 404 에러 )

웹개발 프레임워크를 사용할 때 주로 개발하는 형태가, controller를 작성하여 비즈니스 로직을 수행한 후, 사용자에게 보여질 페이지(JSP)로 RequestDispatcher.forward 하는 형태이리라 생각됩니다. ( 물론 다른 형태도 많겠지만, RequestDispatcher.forward 하는 형태를 가정한 상황으로 글을 씁니다. )

아래는 그렇게 사용한 java servlet 예제입니다.

RequestDispatcher rd = null;
rd = request.getRequestDispatcher("/main.jsp");
rd.forward(request, response);

이 경우 만약 main.jsp 파일이 존재하지 않는다면, 웹로직은 404 에러 페이지만 출력할 뿐입니다. 그러면 사용자는 호출한 URL이 존재하지 않는 것인지 forward 대상 페이지가 없는 것인지 알 수 없습니다.

tomcat 이나 다른 WAS의 경우는 주로 위 처리에 대한 에러가 로그파일 혹은 웹브라우저에 추적이 가능한 정보를 제공하는데, 웹로직은 404 에러코드만 줄 뿐 추가정보를 제공하지 않았습니다. ( 제 경험 )

이에 추적정보를 찾는 방법을 알았기에 공유합니다.

  1. 해당 서버를 선택합니다.
  2. 디버그 탭을 선택합니다.
  3. weblogic 항목을 펼칩니다.
  4. servlet 항목을 펼칩니다.
  5. DebugURLResolution 항목에 체크표합니다.
  6. 사용 버튼을 눌러 활성화 합니다.

위 과정을 거치면 아래와 같은 로그를 발견할 수 있습니다. 서버의 logs/서버명.log 파일에 기록됩니다.

####<2011. 6. 17 오후 4시 41분 10초 KST> <Debug> <URLResolution> <whity> <AdminServer> <[ACTIVE] ExecuteThread: '1' for queue: 'weblogic.kernel.Default (self-tuning)'> <<anonymous>> <> <> <1308296470912> <BEA-000000> <ServletContext@465470[app:test module:test path:/test spec-version:null]: resolving request with relUri: /main.jsp>
####<2011. 6. 17 오후 4시 41분 10초 KST> <Debug> <URLResolution> <whity> <AdminServer> <[ACTIVE] ExecuteThread: '1' for queue: 'weblogic.kernel.Default (self-tuning)'> <<anonymous>> <> <> <1308296470912> <BEA-000000> <ServletContext@465470[app:test module:test path:/test spec-version:null]: getResourceAsSource() couldn't find source for : /main.jsp>

위 로그 마지막 줄을 통해서 main.jsp 파일을 찾을 수 없다는 내용을 확인할 수 있습니다.