[알아봅시다] PMO

풍부한 경험ㆍ노하우가 기본
내년 공공분야서 본격 적용
SW업계도 전문협의회 발족

기업ㆍ기관 프로젝트 성공 이끄는 전문조직

최근 프로젝트 관리 조직(PMO)이 업계의 새로운 화두로 떠오르고 있습니다.

정부는 지난해 10월 말 대기업의 공공 정보화 사업 참여 제한 등의 정책을 담은 공생발전형 소프트웨어(SW) 생태계 구축방안을 발표하면서 대기업이 공공 정보화 사업에서 빠져나감에 따라 정보 시스템의 품질이 낮아질 우려에 대해 PMO 제도를 도입해 보완하겠다고 설명했습니다.

이후 지식경제부, 행정안전부 등이 공공 정보화 사업의 PMO 적용 확대 방침을 발표하고 있습니다. 정부는 전자정부법 개정을 통해 PMO를 법제화하고 내년을 기점으로 공공 정보화 사업의 PMO 적용을 본격화할 예정입니다.

PMO는 무엇이고, 어떤 역할을 하는 것일까요?

최근 설립된 PMO전문기업협의회의 초대 의장인 이석주 고려대 교수에 따르면, PMO는 기업, 기관 등에서 수행하는 다양한 프로젝트를 추진하는 목적에 부합하도록 성공시키거나 성공할 수 있도록 지원, 감독, 통제 등 제반 활동을 수행하는 조직을 뜻합니다.

또 △요구사항 분석과 확인 △원가, 일정, 범위, 품질, 의사소통, 위기, 인적자원 등 계획 작성 △계획과 실행에 대한 감시와 통제 △각종 변경(범위, 일정, 원가, 품질, 조직 등)에 대한 통제와 관리 △이슈나 문제 추적과 해결 △결과물과 관련 문서 관리, 방법론, 도구 등 표준화 추진과 프로젝트 평가가 PMO의 주된 활동이라고 할 수 있습니다.

행안부는 PMO에 대해 사업관리 전문가 조직으로, 사업단계별 발주자 지원과 대행을 책임지고, 사업자 시정 권고와 같은 권한을 가지며, 품질, 일정 등 목표에 달성하지 못할 경우 계약금 일부 환수 등 계약서에 명시된 바에 따라 책임을 부담한다고 설명하고 있습니다.

이 같은 정의를 종합하면, PMO의 역할은 풍부한 경험과 노하우를 바탕으로 발주자를 대신하거나 도와 정보화 프로젝트의 제반사항을 통제하고 보다 나은 방향으로 이끌어 정보화 사업을 성공시키는 것이라고 할 수 있습니다.

이 때문에 앞서 언급한 것처럼 정부는 그동안 많은 공공 정보화 사업을 담당해온 대기업이 공공시장에서 제외됐을 때 정보화 프로젝트의 품질이 떨어질 것이라는 우려를 잠재울 대안으로 PMO를 말하고 있는 것입니다.

내년에 PMO가 공공분야에서 본격적으로 적용되기 위해 올해는 여러 가지 준비작업이 중점적으로 진행될 예정입니다.

지경부는 최근 한국전력공사, 한국가스공사, 강원랜드 등 60개의 유관기관이 올해 PMO 제도를 우선 적용한다고 발표했습니다. 행안부도 최근 올해 PMO 시범도입을 추진한다고 밝혔습니다.

이처럼 PMO가 부상함에 따라 SW 업계도 PMO 준비에 나서고 있습니다.

한국소프트웨어산업협회는 최근 PMO전문기업협의회를 발족시켰습니다. PMO전문기업협의회는 PMO 정의ㆍ방법론ㆍ프레임 연구, PMO 전문기업과 전문인력 육성 등의 사업을 추진할 계획입니다.

한국소프트웨어전문기업협회는 PMO 전문회사 설립을 추진하고 있습니다. SW전문기업협회는 이르면 상반기에 PMO 전문기업을 설립할 계획입니다.

이밖에 한국정보산업협동조합은 컨설팅 및 감리 전문 조합원사의 프로젝트 관리능력 보유자로 풀(Pool)을 만들어 PMO와 품질보증 활동을 지원할 계획이라고 밝혔습니다.

전문가들은 그동안 PMO가 활성화되지 않았던 공공기관에서 PMO 제도가 성공적으로 적용되기 위해서는 철저한 준비가 필요하다고 지적하고 있습니다. 자칫 준비가 부족한 가운데 제도가 시행된 후 PMO의 능력 부족, PMO와 발주자의 커뮤니케이션 부족 등으로 정보화 프로젝트가 실패할 경우 PMO 제도에 대한 근본적인 회의까지 제기될 수 있기 때문입니다.

IT 컨설팅 및 감리기업인 케이씨에이의 이상인 상무는 발주기관 및 참여 사업자와의 원활한 의사소통, 단계별 철저한 작업계획 수립, 작업 진행에 대한 이슈 관리를 PMO의 성공요소로 꼽았습니다. 구체적으로 PMO는 추진단계별 목표와 주기적인 진행과정 확인과 보고, 사업 산출물 품질 제고를 위한 점검활동, 업무적ㆍ기술적 전문성 확보가 요구되고, 발주기관의 경우 PMO 수행범위에 대한 명확한 정의, PMO 조직에 대한 신뢰 등이 필요하며, 참여 시스템 통합(SI) 사업자는 발주기관, 참여 사업자들과의 의견 조정, 주요 이슈 및 미결사항 해결을 위한 코디네이터 역할이 필요하다는 것입니다.

이상인 상무는 또 PMO가 성공하기 위해서는 역량 있는 전문인력이 많아야 하며, 이를 어떻게 양성할 지가 관건이라고 덧붙였습니다.

강동식기자 dskang@
▶강동식기자의 블로그 : http://blog.dt.co.kr/blog/?mb_id=dskang

아이스크림 샌드위치 (ICS) 빌드 에러시 대처 방법

에러 종류 1)

development/tools/emulator/opengl/host/libs/Translator/EGL/../include/EGL/eglplatform.h:98: fatal error: GL/glx.h: No such file or directory
compilation terminated.
make: *** [out/host/linux-x86/obj/SHARED_LIBRARIES/libEGL_translator_intermediates/EglX11Api.o] Error 1


대처 방법)

아래 명령어를 통해 우분투에 libgl1-mesa-dev를 설치해준다.
$ sudo apt-get install libgl1-mesa-dev

W3C의 오픈소스 HTML/CSS 에디터

Amaya에 오신 것을 환영합니다

감사: AsiaTranslate
감사: Cheap buys coupons

참고로이 원래 W3C는 문서의 번역 및 W3C의 속성입니다. 우리는이 사본의 실수에 대한 책임을지지 않습니다.

[출처] http://www.asiatranslate.org/w3/amaya-ko/

W3C의 에디터 / 브라우저

Amaya 는 웝편집기 입니다. 예: 문서를 웹상에서 직접 작성과 편집을 할 수 있는 도구 입니다. 브라우저 기능은 수정과 원격 엑세스 기능과 원활하게 한 환경에서 통합되어있습니다. 이것은 웹의 근본적 비젼을 따라 한쪽 방향으로만 출판되는 매체가 아닌 협력을 위한 공간을 제공합니다.

Amaya 는 1996년도W3C 에서 시작되었으며 완전한 기능을 가춘 웹 클라이언트로 선을 보였습니다. Amaya의 주요 동기는 가장많은 W3C기술들을 통합한 프레임 웨크를 제공하기 위하는 것 입니다. 이러한 기술을 활성함과 동시에 그것들의 조합의 해택을 일관성 있는 환경에서 선을 보이고 있습니다.

Amaya는 HTML +CSS 스타일 쉬트로 시작되었습니다. 그후로 XML과 점차 더많은 프로그렘, 즉 XHTML, MathML 과 SVG가 지원되었습니다. AMAYA는 이 모든어휘를 동시에 복합 문서에서 편집할 수 있습니다.

AMAYA 는 공동 해설 응용 프로그렘이 포함되어있는데 이것은 리소스 설명 프레임 워크 (RDF), XLink, and XPointer. ANNOTEA 프로젝트 홈페이지를 방문해 주세요.

Amaya – 오픈 소스

Amaya는 오픈소스 소프트웨어이며 W3C 프로젝트를 통해 호스트 되고있습니다. 다양한 면으로 다지원하시기를 초대합니다 (문서제작, 번역, 코드작성, 버그 고치기, 다른 플레트 폼으로 포팅함).

Amaya소프트웨어 는 C 로 작성되었고 윈도우, 유닉스 플랫폼 및 맥 OS X에서 사용할 수있습니다.

공공 IRC 채널 #amayairc.w3.org (포트 6665)에서 찾으실 수 있습니다.

Amaya 팀

본 프로그렘은 W3C와 WAM (Web, Adaptation and Multimedia)프로젝트 가 INRIA에서 공동으로 개발되었 습니다. 핵심팀은 다음과 같습니다: 아이린 배튼 Irène Vatton (프로젝트 리더, INRIA), 로란 칼콘Laurent Carcone (W3C), 빈쎈 퀸트Vincent Quint (INRIA).

Current Release

Amaya screenshot

warningAmaya 11.3.1 이 출시되었습니다. (2009 년 12월 9일)

본 소프트웨어는HTML 4.01, XHTML 1.0, XHTML 베이식, XHTML 1.1, HTTP 1.1, MathML 2.0, 와 많은 CSS 2 기능과SVG 를 지원하고 있습니다.

이제는 SVG에디터가 (여러가지 언어로) 포함이 되었습니다. XML 문서를 부분적으로 보고 편집 하실 수 있습니다. 본 프로그렘은 국제화 응용된 프로그렘 입니다. 본 프로그렘은 문맥 메뉴된 고급사용자 인터페이스 와 사용자 정의 설정이 가능한 메뉴와 도구들, 그리고 태마들을 제공합니다.

배포판은 리눅스, 윈도우와 지금은 맥 OS X PowerPC 및 Intel에 사용할 수있습니다.

본 버전은 템플렛 지원을 제공하며 이 템플렛 의 지원자금은 일부분 유럽위원회의 제 6차 프레임워크 프로그렘, 팔라트프로젝트로서 받고있습니다.

자세한 내용은 개요 페이지를 참조하십시오.

도로지도

  • 렌더링 작업 계속하기
  • SVG 편집기작업 계속하기
  • 세로운 https 지원작업
  • 템플릿 편집기를 개선하기

AmayaW3C의 공지 사항과 소프트웨어 라이선스에 의해 보호됩니다. 귀하의 웹 페이지를Amaya로 작성, 편집하실때 아마야 아이콘 W3C-Amaya 을 삽입할 수있습니다.

Valid xhtml W3C-Amaya

Android Porting On Real Target


1 Introduction


Google explains that Android is a software stack for mobile devices that includes an operating system, middleware and key applications. This  document explains the Android architecture by Google and porting procedure on the real hardware. The explanation is based on the m3 sdk version of the Android emulator.


If you have enough knowledge about patching the kernel, resolving rejections from a patch, making an ramdisk image, and the Linux kernel itself, reading this article will be easier.

2 Copyright and Acknowledgements


This  document is copyright (c) Kwangwoo Lee (kwangwoo.lee at gmail dot com). Permission is granted to copy, distribute and/or modify this  document under the terms of the GNU Free Documentation License.

AndroidPortingOnRealTarget/ko - Korean translation by dasomoli (dasomoli at gmail dot com).

3 The brief analysis of the Android architecture


3.1 Android Kernel


The most different things are the Android kernel uses ARM EABI(Embedded Application Binary Interface) and OpenBinder IPC(Inter Process Communication). If you want to compile the kernel supporting ARM EABI, you should rebuild toolchains to support ARM EABI.

The Android sdk emulates goldfish architecture using qemu. The alsa may be used for audio on Android. See the audio.c file in the goldfish architecture directory and the driver uses /dev/eac for audio on the Android system. RTC(Real Time Clock) device is also used through /dev/rtc0.

The following parts explain the main differences:

3.1.1 ARM EABI


EABI is the new "Embedded" ABI by ARM Ltd. The changes are listed on Debian wiki. (http://wiki.debian.org/ArmEabiPort)

Example with long ftruncate64(unsigned int fd, loff_t length):legacy ABI:- put fd into r0- put length into r1-r2- use "swi #(0x900000 + 194)" to call the kernelnew ARM EABI:- put fd into r0- put length into r2-r3 (skipping over r1)- put 194 into r7- use "swi 0" to call the kernel

The Android uses EABI kernel feature. Enable kernel options of the CONFIG_AEABI and CONFIG_OABI_COMPAT. You can see the differences of the executable binary as follows :

  • Legacy ABI
$ arm-softfloat-linux-gnu-objdump -x t7-demo | grep privateprivate flags = 202: [APCS-32] [FPA float format] [software FP] [has entry point]$ file t7-demot7-demo: ELF 32-bit LSB executable, ARM, version 1 (ARM), for GNU/Linux 2.4.3, dynamically linked (uses shared libs), for GNU/Linux 2.4.3, stripped

  • ARM EABI
$ arm-softfloat-linux-gnueabi-objdump -x t7-demo  | grep privateprivate flags = 4000002: [Version4 EABI] [has entry point]$ file t7-demot7-demo: ELF 32-bit LSB executable, ARM, version 1 (SYSV), for GNU/Linux 2.6.14, dynamically linked (uses shared libs), for GNU/Linux 2.6.14, stripped


What is the ABI for the ARM Architecture? Is it the same as the ARM EABI?

The ABI for the ARM Architecture is a standard developed by ARM and its partners (including CodeSourcery) that explains how compilers, assemblers, linkers, and other similar tools should generate object files and executable files. Tools that correctly implement the ABI for the ARM Architecture can interoperate; i.e., objects files built with one toolchain can be combined with object files built with another toolchain if both compilers use the ABI for the ARM Architecture. The "ARM EABI" is an informal name for the ABI for the ARM Architecture.

3.1.2 OpenBinder


The OpenBinder provides a object-oriented operating system environment. It is designed to be hosted by traditional kernels. This project is started at Be. Inc. as the part of the next generation BeOS, and finished implementing at PalmSource as a core part at the Cobalt system.

It is a system oriented component architecture rather than application oriented, and It provides IPC between processes, threadpool, memory management and clean up feature at the end of reference of an binder object.

The vanilla kernel do not have OpenBinder IPC mechanism you should patch the kernel. The OpenBinder offers thread management for the system through /dev/binder. It is the reason that Android system do not offer thread libraries.

After patching the kernel, you can see the files for binder at drivers/binder/.

3.1.3 Frame Buffer


The basic frame buffer driver should be implemented already. After that you need to implement the differences between your architecture driver and the goldfish driver.

The frame buffer driver of the goldfish architecture supports the fb_pan_display function of the struct fb_ops. It means you should allocate memory twice rather than the actual frame size.

  • Initialize frame buffer information
struct fb_info *fbinfo;...fbinfo->fix.ypanstep	= 1;fbinfo->var.yres_virtual    = gm->lcd.yres * 2;fbinfo->fix.smem_len        =	(gm->lcd.xres *                                gm->lcd.yres *                                gm->lcd.bpp / 8) * 2;

  • Allocate frame buffer memory
struct mvfb_info *fbi;...fbi->map_size = PAGE_ALIGN(fbi->fb->fix.smem_len + PAGE_SIZE);fbi->map_cpu  = dma_alloc_writecombine(fbi->dev, fbi->map_size,                                       &fbi->map_dma, GFP_KERNEL);

  • Implement fb_pan_display fuction hook
static int mvfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *fb){...}static struct fb_ops mvfb_ops = {        .owner		= THIS_MODULE,        .fb_check_var	= mvfb_check_var,        .fb_set_par	= mvfb_set_par,	        .fb_setcolreg	= mvfb_setcolreg,        .fb_blank	= mvfb_blank,        .fb_pan_display = mvfb_pan_display,        .fb_fillrect	= cfb_fillrect,        .fb_copyarea	= cfb_copyarea,        .fb_imageblit	= cfb_imageblit,        .fb_mmap	= mvfb_mmap,	};

The device file is located at /dev/graphics/fb0.

3.1.4 Input Devices


Android uses event device for user input. There are three devices such as keypad, qwerty2 keyboard and mouse. The qwerty2 keyboard and mouse are normal devices. So I just explain the keypad and touchscreen which mouse device is replaced with.

On the Android shell, Cat the /proc/bus/input/{devices,handlers} and then you will see the devices used for the Android.
$ adb shell# cat /proc/bus/input/devicesI: Bus=0000 Vendor=0000 Product=0000 Version=0000N: Name="goldfish-events-keyboard"P: Phys=S: Sysfs=/class/inut/input0U: Uniq=H: Handlers=kbd mouse0 event0...## cat /proc/bus/input/handlersN: Number=0 Name=kbdN: Number=1 Name=mousedev Minor=32N: Number=2 Name=evdev Minor=64#

  • Keypad

Qemu emulates goldfish-events-keyboard. It is a keypad using event device(/dev/input/event0). So you should know which key event and values come from the event device to activate Android applications. To do that, read event0 device with cat and redirect the output to a file. If you push and release the key button on emulator, the output values will be saved.

The output format is struct input_event. So the output on each event is 16 bytes like 8 bytes for time, 2 bytes for type, 2 bytes for code, 4 bytes for value. Read input.txt and input-programming.txt about input event devices in the Documentation/input directory of the Linux kernel source code.

struct input_event {        struct timeval time;        unsigned short type;        unsigned short code;        unsigned int value;};

The Tiger7 evaluation board has it's own scancode table. The following shows the key layout on evaluation board, scancode table, and Android keycodes:
/* *  Key Layout       Scancode Table * *   1  2  3        0x1  0x10  0x100 *   4  5  6        0x2  0x20  0x200 *   7  8  9        0x4  0x40  0x400 *   *  0  #        0x8  0x80  0x800 */static unsigned short android_keycode[] = {        /*         *  0x66 0x67 0x9e	Home  Up   Back         *  0x69 0xe8 0x6a	Left  Ok   Right         *  0xe7 0x6c 0x6b	Send  Down Hangup         *  0xe5		Menu       just_distinction_for_private         */        KEY_HOME,         KEY_UP,       KEY_BACK,        KEY_LEFT,         KEY_REPLY,    KEY_RIGHT,        KEY_SEND,         KEY_DOWN,     KEY_END,        KEY_KBDILLUMDOWN, KEY_RESERVED, KEY_PLAY};

There is a power button on emulator, but I skipped it to get output value.

If an interrupt of the keypad is caught, translate the scancode with the keycode of the Android on the above table and send event to user space application.
...keycode = translate_keycode(scancode);...input_event(keydev->input, EV_KEY, keycode, KEY_PRESSED);orinput_event(keydev->input, EV_KEY, keycode, KEY_RELEASED);...

The high resolution timer - hrtimer is used for reduce keypad debounce.

  • Touchscreen

If you have a touchscreen driver supporting the event interface for a pointing device, it'll work well. If you do not have it, you may implement it or use other pointing devices. Fortunately the evaluation board has already implemented touchscreen driver - drivers/input/touchscreen/tsc2007.c - which is made just before beginning to porting Android. Refer the drivers on drivers/input/touchscreen/ to implement your own driver and the text files on Documentation/input/.

Here is the output of the /proc/bus/input/{devices,handlers} on evaluation board.
# cat /proc/bus/input/devicesI: Bus=0000 Vendor=0000 Product=0000 Version=0000N: Name="MVT7 KEYPAD"P: Phys=S: Sysfs=/class/input/input0U: Uniq=H: Handlers=kbd event0 evbugB: EV=f...I: Bus=0000 Vendor=0000 Product=0000 Version=0000N: Name="TSC2007 Touchscreen"P: Phys=0-0090/input0S: Sysfs=/class/input/input1U: Uniq=H: Handlers=event1 evbugB: EV=bB: KEY=400 0 0 0 0 0 0 0 0 0 0B: ABS=1000003# cat /proc/bus/input/handlersN: Number=0 Name=kbdN: Number=1 Name=evdev Minor=64N: Number=2 Name=evbug

As a result, the keypad uses /dev/input/event0 and the touchscreen interface uses /dev/input/event1 on application layer.

3.1.5 Low Memory Killer


The Linux Kernel has an OOM(Out of Memory) killer for the situation that no memory is left to allocate for a request of a process. It examines all processes and keeps score with some restrictions. The process with highest score will be killed except init.

The Low Memory Killer of the Android behaves a bit different against OOM killer. It classifies processes according to the importance with groups and kills the process in the lowest group. It will make the system to be stable at the view of the end users. For example, the UI Process - foreground process - is the most important process for the end users. So to keep the process live looks more stable than keeping other background processes live.

Enable CONFIG_LOW_MEMORY_KILLER after patching the kernel.

3.1.6 Android Logger


If you enable this feature, you can see some useful information about Android through /dev/log/main. There are three device files on /dev/log such as main, events, radio. The /dev/log/radio file seems to be related with a modem device and ril daemon - rild - on Android system.

When this logger is enabled, the system performance is a bit slower on the system. To use this feature, enable CONFIG_ANDROID_LOGGER.

3.1.7 Android Power


The Android power is for the battery management on devices and some subsystem related with power management like inotify feature on file system. It is not necessary to start up Android through the init(ShellScript) of the Android system. But the runtime binary looks up some files regarding Android power - /sys/android_power/acruire_partial_wake_lock - on starting up Android manually and failed to start up. Enable CONFIG_ANDROID_POWER to use.
- 예전 버전의 문서에서 init은 바이너리로 되어있었는데 쉘스크립트로 바뀌어 있네요. 이전 문서에서 말한 init은 안드로이드 램디스크의 루트 디렉토리 밑에 있는 init 바이너리를 말씀하신 것 같은데, 그 것이 아니고 다른 init인건가요? 아니면 그 init이 쉘 스크립트인 것인가요? -- dasomoli

- 문서를 작성한 m3 버전에서는 binary 였습니다. 다른 분이 shell script로 바꾼 것 같네요. 번역해 주셔서 감사합니다. -- 이광우

- m5 버전에서도 바이너리인 것 같아서요. 그리고 별 말씀을요.^^; -- dasomoli

3.1.8 Panic Timeout


It is not necessary to start up Android on evaluation board. Set CONFIG_PANIC_TIMEOUT with a desired value.

3.2 Android Root File system


Android emulator has 3 basic images on tools/lib/images directory.

  • ramdisk.img
  • system.img
  • userdata.img

ramdisk.img is gziped cpio archive. ramdisk image is very small and contains configuration files, and some executable files such as init and recovery. The init file is not a regular system V init. It is made just for the Android and do special things to start up the Android system.

system.img and userdata.img are VMS Alpha executable. system.img and userdata.img have the contents of /system and /data directory on root file system. They are mapped on NAND devices with yaffs2 file system. /dev/block/mtdblock0 for /system and /dev/block/mtdblock1 for /data.

/system directory has libraries and default system packages (*.apk). /data directory has timezone, cache, and ApiDemos.apk package.

The main services are zygote(/system/bin/app_process), runtime(/system/bin/runtime), and dbus(/system/bin/dbus-daemon). You can see the /etc/init.rc file on the Android ramdisk image.

...zygote {    exec /system/bin/app_process    args {        0 -Xzygote        1 /system/bin        2 --zygote    }    autostart 1}runtime {    exec /system/bin/runtime    autostart 1}...dbus {    exec /system/bin/dbus-daemon    args.0 --system    args.1 --nofork    autostart 1}...

3.3 Licenses of the Android Packages


tools/lib/images/NOTICE contains package lists and licenses for each libraries. The table of the licenses is cited from the presentation by Lim,GeunSik at 2008 Korea Android Summit.

Open Source License
Linux Kernel GPL
NetBSD C Library BSD
DBUS GPL2
OpenBinder (core) GPL2
YAFFS2 GPL
SQLite GPL2
Webkit BSD (including LGPL)
WebCore LGPL
SDL LGPL
SGL Google(Skia)
OpenGL SGI OpenGL (BSD/MPL)


4 Toolchain supporting ARM EABI


The toolchain represents the tools to be used for the system development. It contains C/C++ compiler, linker, libraries, binutils, and etc. The Android kernel and system requires EABI support. So legacy toolchain is not compatible to make the Android system.

4.1 Building toolchain


To make life easier, I used the crosstool-0.43 script (http://www.kegel.com/crosstool/) by Dan Kegel. Unfortunately it is not support to build eabi toolchain, so I applied a glibc 2.5+ nptl build for arm softfloat eabi patch (http://sources.redhat.com/ml/crossgcc/2006-12/msg00076.html) by Khem Raj.

$./arm-softfloat-eabi.sh

If the network is connected, the script will download and build toolchain using gcc 4.1.1 and glibc 2.5.

4.2 Other toolchain


I did not use the codesourcery toolchain, but they said it will work for the building Android system.


5 Kernel


To port the Android on a real hardware is started by Benno (http://benno.id.au), you can see some useful information on his blog. On his blog some pre-compiled static binaries are linked. It is very helpful for debugging Android system. You can also build static build busybox and strace binaries, but it's better to get them and use.

You can get patch file including the differences between the Android kernel and the vanilla kernel with 2.6.23 version. It has all differences between them. So you need to extract parts of them, and make your own patch for your system architecture.

For example, the Android kernel has it's own yaffs file system patch. If you have your own yaffs or some other file systems like jffs2 on your architecture, then you need to remove the yaffs parts of the patch. The goldfish architecture which the Android kernel emulate an ARM architecture on qemu is not necessary part for your architecture. It can be removed.

The Android kernel emulates ARMv5 instructions. So ARM926EJ-S (ARMv5TEJ) will be good to work.

5.1 Patch kernel


Benno played with a NEO1973 device by openmoko. So he made patch files for it. Get the original patch file from http://benno.id.au/blog/2007/11/21/android-neo1973, I used android.diff. It has whole things about goldfish, qemu, yaffs, and Android specific parts.

You can edit and remove the patch file directly. After making patch including binder, android power, android logger, low memory killer except goldfish and qemu specific parts, get vanilla 2.6.23 version Linux kernel and patch it.

If you use a 2.6.24.1 version Linux kernel, some part regarding android power should be fixed accordingly or disabled to work.

5.2 .config


  • Necessary
...CONFIG_PANIC_TIMEOUT=0CONFIG_AEABI=yCONFIG_OABI_COMPAT=yCONFIG_BINDER=yCONFIG_LOW_MEMORY_KILLER=y...

  • Optional
...# CONFIG_ANDROID_GADGET is not set# CONFIG_ANDROID_RAM_CONSOLE is not set# CONFIG_ANDROID_POWER is not set# CONFIG_ANDROID_LOGGER is not set...

6 Root file system


The root file system is composed of three parts such as a primary ramdisk image on ram, a system image on nand dev0 (/dev/block/mtdblock0), and a data image on nand dev1 (/dev/block/mtdblock1). The mtd devices has a yaffs2 file system and each of them has 64 MiB capacity on the Android emulator.

The extracted system and data directories are copied to the real existing NAND device and they are mounted with --bind option to work on a real hardware.

6.1 Get ramdisk image from emulator


1. unpack ramdisk image from tools/lib/images/ramdisk.img
$ gzip -cd ramdisk.img > ramdisk$ cpio -iv -F ramdisk

cpio will extract files and directories on current working directory.

2. the contents list of the ramdisk
datadevetcetc/default.propetc/firmwareetc/firmware/brf6150.binetc/firmware/brf6300.binetc/hcid.confetc/hostsetc/init.gprs-pppdetc/init.rcetc/init.riletc/init.testmenuetc/pppetc/ppp/chap-secretsetc/ppp/ip-downetc/ppp/ip-upetc/qemu-init.shetc/system.confetc/system.detc/system.d/bluez-hcid.confetc/usbd.confinitprocsbinsbin/recoverysyssystemtmpvarvar/run

6.2 Get data and system directory from emulator


To get data and system directory you need a static compiled busybox binary. The compiled binary can be obtained from http://benno.id.au/blog/2007/11/14/android-busybox , or make your own binary.

1. launch the Android emulator

2. push static compiled busybox into emulator
# adb push busybox .

3. launch the Android shell
# adb shell

4. make tarball with busybox
# chmod +x /busybox# busybox tar -c /data.tar /data# busybox tar -c /system.tar /system# exit

5. extract tarball from the emulator
# adb pull /data.tar .# adb pull /system.tar .

Extract command often failed. So you may repeat it again until it has done successfully.

6.3 Integrate the Android system with a existing ramdisk image.


The ramdisk for your architecture can make your work a bit easier. Copy the contents of the Android ramdisk to your own ramdisk except system and data directory. And make just mount point for system and data directory. The mount points will be used later with a bind option. The init binary of the Android ramdisk image is the key binary to start the system and It read a configuration file on /etc/init.rc.

Edit /etc/init.rc and comment out qemu part.

...startup {        ...#       qemu-init {#           exec /etc/qemu-init.sh#       }}...

Make run.sh script. /dev/block/mtdblock5 is a mtd partition on a real NAND device, and it is mounted on /mnt. data and system directories are already copied on mtdblock5. So the script below just shows bind mounting each directory on /. Fix your script according to your board configuration.

#!/bin/shmount -t yaffs /dev/block/mtdblock5 /mntmount --bind /mnt/data   /datamount --bind /mnt/system /system# data folder is owned by system user on emulator. Fix 777 to other.chmod 777 /data#chmod 777 /systemexport PATH=/system/sbin:/system/bin:/sbin/usr/local/binexport LD_LIBRARY_PATH=/system/libexport ANDROID_BOOTLOGO=1export ANDROID_ROOT=/systemexport ANDROID_ASSETS=/system/appexport EXTERNAL_STORAGE=/sdcardexport ANDROID_DATA=/dataexport DRM_CONTENT=/data/drm/content/init &

An optional configuration for touchscreen - TSLib.

...export TSLIB_CONSOLEDEVICE=noneexport TSLIB_FBDEVICE=/dev/fb0export TSLIB_TSDEVICE=/dev/input/event1export TSLIB_CALIBFILE=/etc/pointercalexport TSLIB_CONFFILE=/etc/ts.confexport TSLIB_PLUGINDIR=/lib/tsexport LD_PRELOAD=/lib/libts.so:/lib/ts/pthres.so...

6.4 System and Data directories


The contents of the system and data directories are copied to mtdblock5 already. You should copy your own method. To use it, I chose bind mounting on root directory. Bind mounting is a technique to mount an existing directory with a new mount point.

6.5 Run and Debug


Now the kernel, ramdisk, and data directories - data and system - are ready. It's time to see the red cylon eye. After boot up your integrated system, run the run.sh on root directory.

# cd /# . /android/run.shyaffs: dev is 32505861 name is "mtdblock5"yaffs: passed flags ""yaffs: Attempting MTD mount on 31.5, "mtdblock5"yaffs: auto selecting yaffs2# init: HOW ARE YOU GENTLEMENinit: reading config fileinit: device initinit: mtd partition -1,init: mtd partition 0, "l1boot"init: mtd partition 1, "u-boot"init: mtd partition 2, "params"init: mtd partition 3, "kernel"init: mtd partition 4, "ramdisk"init: mtd partition 5, "rootfs"sh: can't access tty; job control turned off# binder_open(c394bcc8 c3c731a0) (pid 1577) got c3e48000binder_open(c394bcc8 c3cd8dc0) (pid 1616) got c319f000binder_open(c394bcc8 c3cd8ac0) (pid 1673) got c3d10000binder_open(c394bcc8 c3cd8940) (pid 1680) got c0e19000binder_open(c394bcc8 c3cd88c0) (pid 1691) got c2fa0000binder_open(c394bcc8 c3d174a0) (pid 1592) got c25b8000binder_release(c394bcc8 c3cd88c0) (pid 1691) pd c2fa0000#

  • Do not make eac device file on /dev. It is for the audio on qemu. If it exists, the start up sequence will wait forever to finish writing some data to the sound device.
  • Use the Android init binary instead of manual startup. The manual start up will require the android power patch. In that case the start up sequence will access /sys/android_power/acquire_partial_wake_lock and wait.

To debug the Android system, use static compiled strace binary from http://benno.id.au/blog/2007/11/18/android-runtime-strace and run the Android manually.

#!/bin/sh# set environment variables above example.../system/bin/app_process -Xzygote /system/bin --zygote &/system/bin/dbus-daemon --system &/system/bin/runtime

The above example shows manual startup sequence, use strace on run /system/bin/runtime binary.

./strace -ff -F -tt -s 200 -o /tmp/strace runtime

6.6 Screenshots


  • Skipped

7 Application Development


The Android applications use Java syntax and xml layouts, but it is not a Java. Because they use their own virtual machine - dalvik - and compiler for dex file format. And use package named apk such as Home.apk, Phone.apk, ApiDemos.apk and etc.

The apk file is a Zip archive and it has four parts.

  • AndroidManifest.xml
  • classes.dex
  • resources.arsc
  • res directory

The Dex file format is explained on http://www.retrodev.com/android/dexformat.html. And the contents of the files and directories are explained some day by Google. It is not explained currently. We can just guess about it.

The Android SDK will create an *.apk file.

7.1 Install Eclipse IDE



1. Eclipse IDE for Java developer (JDT and WST plugins are included) from http://www.eclipse.org/downloads/


3. ADT (Android Development Tools) with a eclipse plugin including Apache Ant

7.2 Build and Run Sample Applications


1. Open sample projects and build

2. Run sample applications on emulator

7.3 Screenshots


  • Android Platform on Nokia's N810 Product(arm1136jf-s)
    n810.kandroid200805.PNG

  • Android Platform on arm1136jf-S for another CE Product.
    fi.initial_display.jpg
  • invain님이 shot을 올려주셨군요. -- 이광우

8 Epilogue


The Android system seems to be a new kind of a Linux based distribution for a mobile environment like Debian, RedHat, SuSE, and etc. They just use the Linux kernel and a lot of different libraries in the open source world. They offer a software based OpenGL-ES library on a 3D acceleration currently, but they are developing on a hardware accelerated baseband processor for it. The hardware acceleration is necessary for fast UI rendering effects later.

The Android system on the sdk is not a completed one to port on a real hardware, because some device - for example, camera - related libraries and classes are not implemented yet and not opened for users. It seems to be under the development stage. So we would better to wait the Google announces the whole porting kit.

Until then, we should look for the business model with the Android system. It requires a bit high cpu performance, so the carrier vendors will require a cheap baseband processor (RF part) and a multimedia co-processor, because the baseband processor including multimedia features will be very expensive.

9 Links and References

JSTL 기초, Part 2: core 분석 (한글)

[출처] http://www.ibm.com/developerworks/kr/library/j-jstl0318/

커스텀 태그를 이용한 플로우 제어와 URL 관리

Mark Kolb, 소프트웨어 엔지니어

요약: SP Standard Tag Library (JSTL) core 라이브러리는 이름이 말해주듯이, 범위(scoped) 변수를 관리하고 URL과 인터랙팅하는 등의 기본 기능과, 반복과 조건화 같은 근본적인 작동에 필요한 커스텀 태그를 제공한다. 이러한 태그들은 페이지 작성자가 직접 사용하기도 하지만 다른 JSTL 라이브러리와 함께 복잡한 표현 로직에 대한 토대를 제공한다.


이 시리즈의 첫 번째 글에서 JSTL을 처음 보았을 것이다. 거기에서 데이터에 액세스 하고 작동하기 위해 expression language (EL)의 사용법을 설명했다. 여러분이 배운 것 처럼 EL은 JSTL 커스텀 태그의 애트리뷰트에 동적 값을 할당하는데 사용된다. 따라서 빌트인 액션과 기타 커스텀 태그 라이브러리용 요청 시간 애트리뷰트 값을 지정하는 JSP 식과 같은 역할을 한다.

EL의 사용법을 설명하기 위해, core 라이브러리에서 세 개의 태그 (<c:set>, <c:remove>, <c:out>)를 소개했었다. <c:set><c:remove>는 범위 변수를 관리하는데 사용된다. <c:out>은 특별히 EL을 사용하여 계산된 값인 데이터 디스플레이에 사용된다. 기초 학습을 토대로 이제는 core 라이브러리의 나머지 태그를 볼 것이다. 두 가지 주요 카테고리로 나뉜다: 플로우 제어(플로우 제어)와 URL 관리.

예제 애플리케이션

JSTL 태그를 설명하기 위해서 실제 애플리케이션 예제를 사용할 것이다. 대중성과 인지도가 높아졌기 때문에 간단한 자바 기반의 Weblog를 사용할 것이다. (참고자료에서 JSP 페이지와 소스 코드를 다운로드 한다.) Weblog (blog)은 웹 기반 저널로서 Weblog의 작성자가 흥미를 가질만한 주제들에 대한 짧은 주석이다. 일반적으로 웹 상의 아티클이나 토의가 있는 곳 어디든 연결된다. 그림 1은 실행 중인 애플리케이션의 모습이다.


그림 1. Weblog 애플리케이션
Screenshot of the Weblog 예제 애플리케이션

스무 개 정도의 자바 클래스가 전체 구현에 필요하지만 Weblog 애플리케이션 클래스에서는 단 두개(EntryUserBean)만이 프리젠테이션 레이어에 사용된다. JSTL 예제를 이해하려면 이들 두 개의 클래스가 필요하다. 그림 2는 EntryUserBean의 클래스 다이어그램이다.


그림 2. Weblog 애플리케이션(클래스 다이어그램)
Class diagram for Weblog 예제 애플리케이션

Entry 클래스는 Weblog 내의 날짜가 나와있는 엔트리를 나타낸다. 이것의 id 애트리뷰트는 데이터베이스 내의 엔트리를 저장하고 검색하는데 사용된다. 반면 titletext 애트리뷰트는 엔트리의 실제 콘텐트를 나타낸다. 자바 Date 클래스 중 두 개의 인스턴스는 createdlastModified 애트리뷰트에 의해 레퍼런스되며 엔트리가 처음으로 만들어지고 마지막으로 편집 될 때 나타난다. author 애트리뷰트는 UserBean 인스턴스를 참조하면서 엔트리를 만든 사람을 나타낸다.

The UserBean 클래스는 애플리케이션의 권한이 있는 사용자 정보(사용자 이름, 성명, 이메일 주소)를 저장한다. 이 클래스에는 관련 데이터베이스와 인터랙팅하기 위한 id 애트리뷰트도 포함되어 있다. 마지막 애트리뷰트인 rolesString 값 리스트를 참조하면서 애플리케이션 역할과 이에 상응하는 사용자를 구분한다. Weblog 애플리케이션의 경우 일반적인 역할은 "User" (모든 일반적인 애플리케이션 사용자 역할)와 "Author" (Weblog 엔트리를 만들고 편집할 수 있는 사용자를 지정하는 역할) 이다.

이 기사의 다른 시리즈

Part 1, "expression language" (2003년 2월)
Part 3, "보여지는 것도 중요하다!" (2007년 3월)
Part 4, "SQL과 XML 콘텐트에 액세스 하기" (2007년 4월)


플로우 제어(Flow control)

동적 애트리뷰트 값을 지정하는데 JSP 식 대용으로 EL이 사용될 수 있기 때문에 스크립팅 엘리먼트를 사용할 필요가 줄어들었다. 스크립팅 엘리먼트는 JSP 페이지에서 중요한 소스가 될 수 있기 때문에 간단한 대안을 제공한다는 것은 JSTL에 있어서 큰 이점이다.

EL은 JSP 컨테이너에서 데이터를 검색하고 객체 계층을 오가며 간단한 작동을 수행한다. 데이터에 접근하여 조작하는 것 외에도 JSP 스크립팅 엘리먼트의 또 다른 사용 측면은 플로우 제어이다. 특히, 페이지 작성자가 반복되거나 조건적인 콘텐트를 구현하기 위해서 스크립틀릿을 의존한다는 것은 일반적인 일이다. 하지만 그와 같은 작동은 EL의 기능을 넘어서기 때문에, core 라이브러리는 반복, 조건화, 예외 처리 등의 형태로 플로우 제어를 관리 할 다양한 사용자 액션을 제공한다.

반복

웹 애플리케이션의 측면에서, 반복(iteration)은 데이터의 모음을 가져다가 디스플레이 하는데 주로 사용된다. 주로 테이블에 리스트나 열(row) 시퀀스의 형태로 나타난다. 반복 콘텐트를 구현하는 JSTL의 기본 액션은 <c:forEach> 커스텀 태그이다. 이 태그는 두 개의 다른 유형의 반복을 지원한다: 정수 범위내의 반복(이를 테면, 자바의 for 문)과 컬렉션 내의 반복(자바의 IteratorEnumeration 클래스).

정수 범위 내에서 반복하려면 <c:forEach>(Listing 1)의 커스텀 태그의 신택스가 사용된다. beginend 애트리뷰트는 정적 정수 값 또는 정수 값을 계산하는 수식이 되어야한다. 이들은 각각 반복을 위한 인덱스의 초기 값과 반복이 멈추는 지점의 인덱스 값을 지정한다. <c:forEach>를 사용하여 정수 범위에서 반복할 때, 이 두개의 애트리뷰트가 필요하며 다른 모든 것들은 선택사항이다.


Listing 1. <c:forEach> 액션을 통한 반복 신택스
<c:forEach var="name" varStatus="name"begin="expression" end="expression" step="expression">body content</c:forEach>		

step 애트리뷰트 또한 정수 값을 갖고 있어야한다. 매번 반복한 후에 인덱스에 추가될 양(amount)을 정한다. 따라서 반복 인덱스는 begin 애트리뷰트 값에서 시작하고 step 애트리뷰트의 값에 의해 증가하며 end 애트리뷰트의 값을 초과할 때 정지한다. step 애트리뷰트가 생략되면 step 크기는 1로 초기화된다.

var 애트리뷰트가 지정되면 지정된 이름을 가진 범위 변수가 만들어지고 인덱스의 현재 값으로 할당된다. 이 범위 변수는 <c:forEach> 태그의 바디 내에서 액세스 될 수 있다. Listing 2는 <c:forEach> 액션의 예제이다.


Listing 2. <c:forEach> 태그
<table><tr><th>Value</th>    <th>Square</th></tr><c:forEach var="x" begin="0" end="10" step="2">  <tr><td><c:out value="${x}"/></td>      <td><c:out value="${x * x}"/></td></tr></c:forEach></table>		

이 예제 코드는 다섯 개 짝수의 제곱을 테이블로 만들었다. 그림 3이 그 결과이다 .


그림 3. Listing 2의 결과
Output of Listing 2

컬렉션의 멤버들 사이를 반복할 때 <c:forEach> 태그의 추가 애트리뷰트인 items 애트리뷰트가 사용된다. (Listing 3). 이러한 형식의 <c:forEach> 태그를 사용할 때, items 애트리뷰트는 유일하게 필요한 애트리뷰트이다.


>Listing 3. <c:forEach> 액션을 통한 반복 신택스
<c:forEach var="name" items="expression" varStatus="name" begin="expression" end="expression" step="expression">  body content</c:forEach>					

자바 플랫폼에서 제공되는 표준 컬렉션 타입은 <c:forEach> 태그에 의해 지원된다. 어레이 엘리먼트를 통해 반복할 때 이 액션을 사용할 수 있다. 표 1은 items 애트리뷰트에 의해 지원되는 값들의 리스트이다. 테이블의 마지막 열이 표시될 때, JSTL은 이것의 인터페이스(javax.servlet.jsp.jstl.sql.Result)를 정의한다.

표 1. <c:forEach> 태그의 items 애트리뷰트에서 지원되는 컬렉션

itemsitem 값의 결과
java.util.Collection호출에서 iterator()까지의 엘리먼트
java.util.Mapjava.util.Map.Entry의 인스턴스
java.util.IteratorIterator 엘리먼트
java.util.EnumerationEnumeration 엘리먼트
Object 인스턴스 어레이Array 엘리먼트
초기 값들의 어레이래핑된 어레이 엘리먼트
콤마로 나뉘어진 String서브스트링
javax.servlet.jsp.jstl.sql.ResultSQL 쿼리의 열(row)

Listing 4는 컬렉션을 통한 반복에 사용되는 <c:forEach> 태그이다. entryList 라는 범위 변수가 Entry 객체의 리스트로 설정되었다. <c:forEach> 태그가 이 리스트의 각 엘리먼트를 처리한다. blogEntry 라는 범위 변수로 이것을 할당하고 두 개의 테이블 열을 만든다. 하나는 Weblog 엔트리의 title 이고 다른 하나는 이것의 text이다. 이 속성들은 한 쌍의 <c:out> 액션과 이에 상응하는 EL 식을 통해 blogEntry 변수에서 검색된다. Weblog 엔트리의 타이틀과 텍스트에 HTML이 포함되어있기 때문에 <c:out>escapeXml 애트리뷰트는 false로 설정된다. (그림 4).


Listing 4. <c:forEach> 태그를 사용하여 Weblog 엔트리 디스플레이 하기
<table>  <c:forEach items="${entryList}" var="blogEntry">    <tr><td align="left" class="blogTitle">      <c:out value="${blogEntry.title}" escapeXml="false"/>    </td></tr>    <tr><td align="left" class="blogText">      <c:out value="${blogEntry.text}" escapeXml="false"/>    </td></tr>  </c:forEach></table>		


그림 4. Listing 4의 결과
Output of Listing 4

남아있는 <c:forEach> 애트리뷰트인 varStatus는 정수 범위의 반복이나 컬렉션 범위의 반복에서 똑같은 역할을 한다. var 애트리뷰트와 마찬가지로, varStatus는 범위 변수를 만드는데 사용된다. 현재 인덱스 값이나 현재 엘리먼트를 저장하는 대신에 이 변수는 javax.servlet.jsp.jstl.core.LoopTagStatus 의 인스턴스로 할당된다. 이 클래스는 일련의 속성을 정의한다. (표 2).

표 2. LoopTagStatus 객체의 속성

속성GetterDescription
currentgetCurrent()현재 반복 라운드 아이템
indexgetIndex()현재 반복 라운드의 제로 기반(zero-based) 인덱스
countgetCount()현재 반복 라운드의 1 기반(one-based) 인덱스
firstisFirst()현재 라운드가 반복을 통한 첫 번째 패스임을 나타내는 플래그
lastisLast()반복현재 라운드가 반복을 통한 마지막 패스임을 나타내는 플래그
begingetBegin()begin 애트리뷰트의 값
endgetEnd()end 애트리뷰트의 값
stepgetStep()step 애트리뷰트의 값

Listing 5는 varStatus 애트리뷰트가 사용되는 방법을 나타낸 예제이다. Listing 4의 코드를 수정하여 Weblog 엔트리의 숫자세기를 타이틀을 디스플레이하는 테이블 열에 추가한다. 이것은 varStatus 애트리뷰트의 값을 지정하고 결과 범위 변수의 카운트 속성에 액세스 하면 된다. 결과는 그림 5 이다.


Listing 5. varStatus 애트리뷰트를 사용하여 Weblog 엔트리의 카운트 디스플레이하기
<table>  <c:forEach items=    "${entryList}" var="blogEntry" varStatus="status">    <tr><td align="left" class="blogTitle">      <c:out value="${status.count}"/>.      <c:out value="${blogEntry.title}" escapeXml="false"/>    </td></tr>    <tr><td align="left" class="blogText">      <c:out value="${blogEntry.text}" escapeXml="false"/>    </td></tr>  </c:forEach></table>		


그림 5. Listing 5의 결과
Output of Listing 5

<c:forEach> 이외에도, core 라이브러리는 두 번째 반복 태그인 <c:forTokens>를 제공한다. 이것의 액션은 자바의 StringTokenizer 클래스의 JSTL 이다. <c:forTokens> 태그(Listing 6)는 컬렉션 지향 버전의 <c:forEach>와 같은 애트리뷰트를 갖고 있다. <c:forTokens>의 경우 토큰화 될 스트링은 items 애트리뷰트를 통해 지정되는 반면 토큰을 만드는데 사용되는 지정자(deliniter)는 delims 애트리뷰트를 통해 제공된다. <c:forEach> 경우와 마찬가지로, begin, end, step애트리뷰트를 사용하여 토큰이 상응하는 인덱스 값들과 매칭되는 것에 프로세스 되도록 제한 할 수 있다.


Listing 6. <c:forTokens> 액션
<c:forTokens var="name" items="expression"    delims="expression" varStatus="name"    begin="expression" end="expression" step="expression">  body content</c:forTokens>		

조건화

동적 콘텐트를 포함하고 있는 웹 페이지라면 다양한 형식의 콘텐트를 볼 수 있는 다양한 사용자 카테고리가 필요할 것이다. Weblog에서 방문자들은 엔트리를 읽고 피드백을 제출 할 뿐만 아니라 권한을 받은 사용자는 새로운 엔트리를 게시하거나 기존 콘텐트를 편집할 수 있어야 한다.

JSP 페이지 내에 그러한 기능을 구현하고 리퀘스트 기반으로 디스플레이 하고자하는 것을 제어하도록 조건 로직을 사용함으로서 가용성과 소프트웨어 관리는 향상된다. core 라이브러리는 두 개의 다른 조건화 태그인 <c:if><c:choose>를 제공하는데 다음의 기능들을 구현한다.

이 두 가지 액션 중 좀더 단순한 <c:if>는 간단한 테스트 식을 계산한 다음 식이 true로 되었을 때만 바디 콘텐트를 처리한다. 그렇지 않다면 태그의 바디 콘텐트는 무시된다. Listing 7에서 보듯, <c:if>는 테스트의 결과를 varscope 애트리뷰트를 통해 범위 변수로 할당할 수 있다. 이 기능은 테스트 비용이 비쌀 경우 유용하다. 결과는 범위 변수에 캐시되고 <c:if>나 다른 JSTL 태그로의 연속 호출시에 검색된다.


Listing 7. <c:if> 조건 액션 신택스
<c:if test="expression" var="name" scope="scope">  body content</c:if>		

Listing 8은 <c:forEach> 태그의 LoopTagStatus 객체의 first 속성으로 사용된 <c:if> 를 보여준다. 이 경우, 그림 6 에서 보듯, Weblog 엔트리의 구현 날짜는 첫 번째 엔트리 위에 디스플레이 된다. 하지만 다른 엔트리 앞에 반복되지 않는다.


Listing 8. <c:if>를 사용하여 Weblog 엔트리 날짜 디스플레이 하기
<table><c:forEach items="${entryList}" var="blogEntry" varStatus="status"><c:if test="${status.first}"><tr><td align="left" class="blogDate"><c:out value="${blogEntry.created}"/></td></tr></c:if><tr><td align="left" class="blogTitle"><c:out value="${blogEntry.title}" escapeXml="false"/> </td></tr><tr><td align="left" class="blogText"><c:out value="${blogEntry.text}" escapeXml="false"/></td></tr></c:forEach></table>		


그림 6. Listing 8의 결과
Output of Listing 8

Listing 8 처럼, <c:if> 태그는 조건화된 콘텐트에 대해 매우 간략한 노트를 제공한다. 디스플레이 되어야하는 콘텐트가 무엇인지를 결정해야하는 중립적인 테스트가 필요할 경우, JSTL core 라이브러리는 <c:choose> 액션을 제공한다. <c:choose> 신택스는 Listing 9와 같다.


Listing 9. <c:choose> 액션 신택스
<c:choose>  <c:when test="expression">    body content  </c:when>  ...  <c:otherwise>    body content  </c:otherwise></c:choose>		

테스트 되는 각 조건은 상응하는 <c:when> 태그에 의해 나타난다. testtrue로 평가된 첫 번째 <c:when> 태그의 콘텐트만 프로세스된다. 어떤 <c:when> 테스트도 true로 리턴되지 않으면 <c:otherwise> 태그의 바디 콘텐트가 프로세스 된다. <c:otherwise> 태그가 선택적이라는 것을 주목하라. <c:choose> 태그는 최대 한 개의 중첩 <c:otherwise> 태그를 가질 수 있다. 모든 <c:when> 테스트가 false가 되고 어떤 <c:otherwise> 액션도 나타나지 않으면 <c:choose> 바디 콘텐트는 프로세스 되지 않는다.

Listing 10은 <c:choose> 태그의 실행 예제이다. 여기에서 프로토콜 정보는 리퀘스트 객체에서 검색되고 간단한 스트링 비교를 사용하여 테스트된다. 테스트 결과에 따라 상응하는 텍스트 메시지가 디스플레이된다.


Listing 10. <c:choose>를 이용한 콘텐트 조건화
<c:choose>  <c:when test="${pageContext.request.scheme eq 'http'}">    This is an insecure Web session.  </c:when>  <c:when test="${pageContext.request.scheme eq 'https'}">    This is a secure Web session.  </c:when>  <c:otherwise>    You are using an unrecognized Web protocol. How did this happen?!  </c:otherwise></c:choose>		

예외 처리

마지막 플로우 제어 태그는 <c:catch>이다. 이것은 JSP 페이지 내에서 기본적인 예외처리를 담당한다. 좀더 구체적으로 말하면 이 태그의 바디 콘텐트 내에서 발생하는 모든 예외가 잡히면 무시된다. 하지만 예외가 발생하고 <c:catch> 태그의 선택적인 var 애트리뷰트가 지정되면 예외는 지정된 변수로 할당되어 페이지 자체 내에서 에러 처리를 할 수 있다. Listing 11은 <c:catch>의 신택스이다. (예제는 Listing 18이다)


Listing 11. <c:catch> 실행 신택스
<c:catch var="name">  body content</c:catch>		


URL 작동

JSTL core 라이브러리의 나머지 태그는 URL에 초점을 맞춘다. 이 중 첫 번째는 <c:url> 태그인데 URL 생성에 사용된다. 특히, <c:url>은 J2EE 웹 애플리케이션용 URL을 구현할 때 중요하게 쓰이는 세 가지 엘리먼트를 제공한다:

  • 현재 서블릿 콘텍스트 이름이 됨
  • 세션 관리를 위한 URL 재작성
  • 요청 매개변수 이름과 값의 URL 인코딩

value 애트리뷰트가 사용되었다. 기본 URL을 지정하기 위해서. 태그는 필요할 경우 변형한다. 이 기본 URL이 포워드 슬래시로 시작하면 서블릿 콘텍스트 이름이 만들어진다. 구체적인 콘텍스트 이름은 context 애트리뷰트를 사용하여 제공될 수 있다. 이 애트리뷰트가 생략되면 현재 서블릿 콘텍스트 이름이 사용된다. 서블릿 콘텍스트 이름이 개발 보다는 전개 시에 결정될 때 유용하다.


isting 12. <c:url> 작동 신택스
<c:url value="expression" context="expression"    var="name" scope="scope">  <c:param name="expression" value="expression"/>  ...</c:url>		

URL 재작성은 <c:url> 작동에 의해 자동적으로 수행된다. JSP 컨테이너가 사용자의 현재 세션 아이디를 저장하고 있는 쿠키를 검사하면 재작성은 필요없다. 쿠키가 존재하지 않으면 <c:url>로 만들어진 모든 URL은 재작성되어 세션 아이디를 인코딩한다. 계속되는 요청에도 적절한 쿠키가 존재하지 않으면 <c:url>은 이 아이디를 포함하기 위한 URL 재작성을 멈춘다.

var 애트리뷰트를 위해 값이 제공되면 생성된 URL은 특정 범위 변수의 값으로 할당된다. 그렇지 않다면 결과 URL은 현제 JspWriter를 사용하여 아웃풋이 된다. 결과를 직접 산출하는 기능은 <c:url> 태그가 값으로서 나타날 수 있도록 한다. 예를들어 HTML의 <a> 태그의 href 애트리뷰트와 같다.


Listing 13. HTML 태그용 애트리뷰트 값으로 URL 생성하기
<a href="<c:url value='/content/sitemap.jsp'/>">View sitemap</a>		

마지막으로 모든 요청 매개변수가 중첩된 <c:param> 태그를 통해 지정되면 그들의 이름과 값은 HTTP GET 요청용 표준 표기법을 사용하여 생성된 URL에 붙여진다. 또한 URL 인코딩이 수행된다. 유효 URL을 만들어내기 위해 변형되어야하는 매개변수의 이름 또는 값에 나타나는 모든 문자는 적절히 변환된다.


Listing 14. 요청 매개변수를 가진 URL 만들기
<c:url value="/content/search.jsp">  <c:param name="keyword" value="${searchTerm}"/>  <c:param name="month" value="02/2003"/></c:url>		

Listing 14의 JSP 코드는 blog이라는 이름의 서블릿 콘텍스트에 전개되었다. 범위 변수 searchTerm의 값은 "core library"로 설정되었다. 세션 쿠키가 탐지되면 Listing 14에서 만들어진 URL은 Listing 15와 같다.


Listing 15. 세션 쿠키가 있는 상태에서 만들어진 URL
/blog/content/search.jsp?keyword=foo+bar&month=02%2F2003		

어떤 세션 쿠키도 없으면 Listing 16의 URL이 그 결과이다. 서블릿 콘텍스트가 프리펜드 되었고 URL 인코딩이된 요청 매개변수가 붙었다.


Listing 16. 세션 쿠키 없이 만들어진 URL
/blog/content/search.jsp;jsessionid=233379C7CD2D0ED2E9F3963906DB4290  ?keyword=foo+bar&month=02%2F2003		


중요한 콘텐트

JSP는 두 개의 빌트인 메커니즘을 갖고 있어 다른 URL에서 온 콘텐트를 JSP 페이지로 만든다. 그것이 바로 include 지시문과 <jsp:include> 작동이다. 두 경우 모두, 포함되어야 하는 콘텐트가 페이지로서 같은 웹 애플리케이션의 부분이 되어야 한다. 두 가지 태그의 주요 차이점은 include 지시문은 페이지가 컴파일하는 동안 포함된 콘텐트를 결합하고 <jsp:include> 액션은 JSP 페이지의 요청 프로세스 동안 작동한다는 것이다.

core 라이브러리의 <c:import> 액션은 더욱 일반적이면서 강력한 버전의 <jsp:include>라 할 수 있다. <jsp:include> 처럼, <c:import>는 요청 시간 작동이고 기본 태스크는 다른 웹 리소스의 콘텐트를 JSP 페이지에 삽입하는 것이다.


>Listing 17. <c:import>작동 신택스
<c:import url="expression" context="expression"    charEncoding="expression" var="name" scope="scope">  <c:param name="expression" value="expression"/>  ...</c:import>		

임포트 되어야하는 콘텐트용 URL은 url 애트리뷰트를 통해 지정된다. 관련 URL이 허용되고 현재 페이지의 URL에 대비하여 분해된다. url 애트리뷰트의 값은 포워드 슬래시로 시작한다. 하지만 로컬 JSP 컨테이너 내에서는 절대 URL로서 인터프리팅된다. context 애트리뷰트 값 없이는 그와 같은 절대 URL은 현재 서블릿 콘텍스트에서 리소스를 참조하는 것으로 간주된다. 명확한 콘텍스트가 context 애트리뷰트를 통해 지정되면 절대(로컬) URL은 이름을 가진 서블릿 콘텍스트에 대해 분해된다.

<c:import> 액션은 로컬 콘텐트 접근에만 제한되지 않는다. 프로토콜과 호스트 이름을 포함한 전체 URI는 url 애트리뷰트의 값으로 지정될 수 있다. 사실 프로토콜은 HTTP로 제한되지 않는다. java.net.URL 클래스로 지원되는 모든 프로토콜은 <c:import>url 애트리뷰트용 값에서 사용된다. (Listing 18).

<c:import> 액션은 FTP 프로토콜을 통해 액세스 되는 문서의 콘텐트를 포함하는데 사용된다. <c:catch> 액션은 FTP 파일 전송 동안 발생하는 모든 에러를 처리하기 위해 사용된다. <c:catch>var 변수를 사용하여 예외용 범위 변수를 지정하고 <c:if>를 사용하여 값을 검사하면 된다. 예외가 발생하면 범위 변수로의 할당이 발생한다.


Listing 18. <c:import>와 <c:catch>의 결합 예제
<c:catch var="exception">  <c:import url="ftp://ftp.example.com/package/README"/></c:catch><c:if test="${not empty exception}">  Sorry, the remote content is not currently available.</c:if>		

<c:import> 액션의 마지막 두 개의 애트리뷰트는 varscope이다. var 애트리뷰트는 지정된 유알엘에서 가져온 콘텐트가 현재의 JSP 페이지에 포함되도록 하는 것이 아니라 변수에 저장되도록 한다. scope 애트리뷰트는 이 변수의 범위 지정을 제어하고 페이지 범위를 초기화한다.


요청 리다이렉션(redirection)

마지막 core 라이브러리 태그는 <c:redirect>이다. 이 액션은 HTTP 리다이렉트 응답을 사용자 브라우저로 보내는데 사용되며, JSTL의 javax.servlet.http.HttpServletResponsesendRedirect() 메소드와 같다. 이 태그의 urlcontext 애트리뷰트 (Listing 19) 작동은 <c:import>urlcontext 애트리뷰트와 같다.


Listing 19. <c:redirect>action
<c:redirect url="expression" context="expression">  <c:param name="expression" value="expression"/>  ...</c:redirect>		

Listing 20은 <c:redirect> 작동 모습이다. Listing 18의 에러 메시지를 지정된 에러 페이지 리다이렉트로 대체한다. 이 예제에서 <c:redirect> 태그는 표준 <jsp:forward> 작동과 비슷한 방식으로 사용된다. 요청 디스패쳐를 통한 포워딩은 서버쪽에서 구현되지만 리다이렉트는 브라우저에서 수행된다. 개발자의 관점에서 보면 포워딩은 리다이렉팅보다 효율적이다. 하지만 <c:redirect> 액션이 좀더 유연하다. <jsp:forward>는 현재 서블릿 콘텍스트 내에서 다른 JSP 페이지로만 디스패치 할 수 있기 때문이다.


Listing 20. 예외에 대한 응답으로 리다이렉팅
<c:catch var="exception">  <c:import url="ftp://ftp.example.com/package/README"/></c:catch><c:if test="${not empty exception}">  <c:redirect url="/errors/remote.jsp"/></c:if>		

사용자 관점과의 주요 차이점은 리다이렉트가 브라우저로 디스플레이된 URL을 업데이트하고 북마크 설정에 영향을 미친다는 것이다. 반면 포워딩은 엔드유저에게 투명하다. <c:redirect><jsp:forward> 중의 선택은 사용자 경험에 따라 달라진다.


참고자료

필자소개

Mark Kolb는소프트웨어 엔지니어이며 Web Development with JavaServer Pages, 2nd Edition의 공동 저자이다.

JSTL 기초, Part 1: Expression Language (한글)

[출처] http://www.ibm.com/developerworks/kr/library/j-jstl0211.html

JSP 애플리케이션용 MA 단순화하기

Mark A. Kolb, 소프트웨어 엔지니어

요약: JSP Standard Tag Library (JSTL)은 일반적인 웹 애플리케이션 기능(반복(iteration)과 조건, 데이터 관리 포맷, XML 조작, 데이터베이스 액세스)을 구현하는 커스텀 태그 라이브러리 모음이다. 소프트웨어 엔지니어인 Mark Kolb은 JSTL 태그의 사용방법을 설명한다. 표현층(presentation layer)에서 소스 코드를 제거하여 소프트웨어 관리를 단순화시키는 방법도 설명한다. 이외에도 JSTL의 단순화된 Expression Language에 대한 설명도 포함되어 있다.


JavaServer Pages (JSP)는 J2EE 플랫폼을 위한 표준 표현 레이어(presentation-layer) 이다. JSP는 페이지 콘텐트를 동적으로 생성할 수 있는 전산을 수행 할 수 있는 스크립팅 엘리먼트와 액션을 제공한다. 스크립팅 엘리먼트는 프로그램 소스 코드가 JSP 코드에 포함될 수 있도록 한다. 페이지가 사용자 요청에 대한 응답으로 렌더링 될 때 실행할 목적이다. 액션(actions)은 전산 작동을 JSP 페이지의 템플릿 텍스트를 구성하고 있는 HTML 이나 XML과 거의 흡사하게하는 태그로 인캡슐한다. JSP 스팩에 표준으로 정의된 몇 가지의 액션들이 있다. 하지만 JSP 1.1 부터 개발자들은 커스텀 태그 라이브러리 형태로 자신만의 액션들을 만들 수 있다.

JSP Standard Tag Library (JSTL)는 JSP 1.2 커스텀 태그 라이브러리 모음으로서 광범위한 서버측 자바 애플리케이션에 일반적으로 쓰이는 기본 기능들을 구현한다. JSTL은 데이터 포맷, 반복 콘텐트 또는 조건 콘텐트 같은 전형적인 표현 레이어를 위한 표준 구현을 제공하기 때문에, JSP 작성자들이 애플리케이션 개발에 집중하는데 도움이 된다.

물론, 스크립틀릿, 익스프레션, 선언 같은 JSP 스크립팅 엘리먼트를 사용하는 태스크를 구현할 수 있다. 예를 들어 조건 콘텐트(conditional content)는 세 개의 스크립틀릿(Listing 1의 하이라이트 부분)을 사용하여 구현될 수 있다. 페이지 내에 프로그램 소스 코드를 임베딩하는 것에 의존하기 때문에 스크립팅 엘리먼트가 소프트웨어 관리 태스크를 매우 복잡하게 하는 경향이있더라도 JSP 페이지는 그들을 사용한다. Listing 1의 스크립틀릿 예제는 브레이스들의 적절한 매칭에 매우 의존한다. 조건화된 콘텐트 내에 추가 스크립틀릿을 중첩하는 것은 신택스 에러가 갑자기 일어났다면 페이지가 JSP 콘테이너에 의해 컴파일 될 때 결과 에러 메시지를 합리화하는 것은 도전이 될 수 있다.


Listing 1. 스크립틀릿을 통해 조건 콘텐트 구현하기
  <% if (user.getRole() == "member")) { %>    <p>Welcome, member!</p><% } else { %>    <p>Welcome, guest!</p><% } %>

그와 같은 프로그램을 해결하는데에는 프로그래밍 경험이 많이 필요하다. JSP 페이지의 마크업이 페이지 레이아웃과 그래픽 디자인에 익숙한 디자이너에 의해 개발 및 관리되는데 반해 그와 같은 페이지 내의 스크립팅 엘리먼트는 문제가 생길 때 프로그래머가 개입해야한다. 하나의 파일안에 있는 코드에 대한 책임을 공유하는 것은 JSP 페이지의 개발, 디버깅, 향상을 성가신일로 만든다. JSTL은 일반적인 기능을 커스텀 태그 라이브러리의 표준 세트로 패키징했기 때문에 JSP 작성자들이 스크립팅 엘리먼트에 대한 필요를 줄이고 관련된 관리 비용을 피할 수 있도록 한다.

이 기사의 다른 시리즈

Part 2, "core 분석" (2003년 3월)
Part 3, "보여지는 것도 중요하다!" (2007년 3월)
Part 4, "SQL과 XML 콘텐트에 액세스 하기" (2007년 4월)


JSTL 1.0

02년 6월에 릴리스된 JSTL 1.0은 네 개의 커스텀 태그 라이브러리(core, format, xml, sql)와 두 개의 범용 태그 라이브러리 밸리데이터(ScriptFreeTLV & PermittedTaglibsTLV)로 구성되어 있다. core 태그 라이브러리는 커스텀 액션을 제공하여 범위 변수를 통해 데이터를 관리할 수 있도록하며 페이지 콘텐트의 반복과 조건화를 수행할 수 있도록 한다. 또한 URL에서 생성 및 작동할 수 있는 태그도 제공한다. format 태그 라이브러리는 이름이 시사하는 바와 같이 데이터 특히 숫자와 날짜를 포맷하는 액션을 정의한다. 국지화 된 리소스 번들을 사용하여 JSP 페이지의 국제화도 지원한다. xml 라이브러리에는 XML을 통해 표현된 데이터를 조작할 수 있는 테그가 포함되어 있다. sql 라이브러리는 관계형 데이터베이스를 쿼리하는 액션을 정의한다.

두 개의 JSTL 태그 라이브러리 밸리데이터는 개발자들이 JSP 애플리케이션 내에서 표준을 코딩하도록 한다. ScriptFreeTLV 밸리데이터를 설정하여 JSP 페이지 내에 있는 다양한 스크립팅 엘리먼트(스크립틀릿, 표현, 선언)의 다양한 유형을 사용하는 것을 막는다. 이와 비슷하게 PermittedTaglibsTLV 밸리데이터는 애플리케이션의 JSP 페이지들에 의해 액세스된 커스텀 태그 라이브러리(JSTL 태그 라이브러리 포함)을 제한한다.

JSTL은 J2EE 플랫폼에 필요한 컴포넌트가 될 것이지만 적은 수의 애플리케이션 서버들만이 이를 포함하고 있는 것이 현실이다. JSTL 1.0의 레퍼런스 구현은 Apache Software Foundation의 Jakarta Taglibs 프로젝트의 일부로서 사용할 수 있다. (참고자료). 레퍼런스 구현에 있는 커스텀 태그 라이브러리는 JSTL 지원을 추가하기 위해 JSP 1.2와 Servlet 2.3 이상 스팩을 지원하는 모든 애플리케이션 서버에 통합될 수 있다.


Expression language

JSP 1.2에서 JSP 액션의 애트리뷰트는 정적 캐릭터 스트링이나 익스프레션을 사용하여 지정된다. 예를 들어 Listing 2의 경우 정적 값들은 <jsp:setProperty> 액션의 nameproperty 애트리뷰트를 위해 지정된다. 반면 익스프레션은 이것의 값 애트리뷰트를 지정하는데 사용된다. 이 액션은 요청 매개변수의 현재 값을 이름이 붙여진 빈 속성으로 할당하는 효과를 갖고 있다. 이러한 방식으로 사용된 익스프레션은 request-time attribute values이라 일컬어지며 애트리뷰트 값을 동적으로 지정하기위한 JSP 스팩에 내장된 유일한 메커니즘이다.


Listing 2. request-time attribute value을 결합하는 JSP 액션
<jsp:setProperty name="user" property="timezonePref"          value='<%= request.getParameter("timezone") %>'/>

request-time attribute values가 익스프레션을 사용하여 지정되기 때문에 다른 스크립팅 엘리먼트와 같은 소프트웨어 관리 문제가 일어날 수 있다. 이런 이유로 인해 JSTL 커스텀 태그는 동적 애트리뷰트 값을 지정하기 위한 대안 메커니즘을 지원한다. JSP 익스프레션을 사용하는 것 보다 JSTL 액션용 애트리뷰트 값이 단순화 된 expression language (EL)를 사용하여 지정될 수 있다. EL은 JSP 컨테이너에 있는 데이터를 검색 및 조작할 식별자, 접근자, 연산자를 제공한다. EL은 EcmaScript(참고자료)와 XML Path Language (XPath)에 약간 의존하기 때문에 신택스는 페이지 디자이너와 프로그래머 모두 에게 익숙하다. EL은 객체와 속성들을 검색하면서 간단한 작동을 수행한다. 이것은 프로그래밍 언어도 스크립팅 언어도 아니다. JSTL 태그와 결합하면 간단하고 편리한 표기를 사용하여 복잡한 작동이 표현될 수 있다. EL 익스프레션은 달러 표시($)와 중괄호 ({})를 앞에 붙여 사용하여 범위를 정한다.(Listing 3)


Listing 3. JSTL 액션: EL 익스프레션 범위 지정
<c:out value="${user.firstName}"/>

여러개의 익스프레션들과 정적 텍스트를 결합하여 스트링 연결을 통해 동적 애트리뷰트 값을 만들 수 있다.(Listing 4). 개별 익스프레션들은 식별자, 접근자, 리터럴, 연산자로 구성되어 있다. 식별자는 데이터 센터에 저장된 데이터 객체를 참조하는데 사용된다. EL은 11 개의 식별자를 보유하고 있다. 11 개의 EL 내장 객체에 상응하는 것들이다. 다른 모든 식별자들은 범위 변수를 참조하는 것으로 간주된다. 접근자는 객체의 속성 또는 컬렉션의 엘리먼트를 검색하는데 사용된다. 리터럴은 고정된 값들(숫자, 문자, 스트링, 부울, null)을 나타낸다. 연산자는 데이터와 리터럴이 결합 및 비교될 수 있도록 한다.


Listing 4. 정적 텍스트와 여러 EL 익스프레션을 결합하여 동적 애트리뷰트 값 지정하기
<c:out value="Hello ${user.firstName} ${user.lastName}"/>


범위 변수(Scoped variables)

<jsp:useBean> 액션을 통한 JSP 에이피아이는 데이터가 저장될 수 있도록 하며 JSP 컨테이너 내에 네 개의 다른 범위에서 데이터가 검색될 수 있도록 한다. JSTL은 이러한 범위 내에 객체를 할당하고 제거할 추가 액션을 제공한다. 더욱이, EL은 범위 변수 같은 객체들을 검색하는 빌트인 지원을 제공한다. 특히 EL의 내장 객체 중 하나라도 상응하지 않는 EL 익스프레션에 있는 식별자는 네 개의 JSP 스콥 중 하나에 저장된 객체를 참조하는 것으로 자동 간주된다:

  • 페이지 범위
  • 요청 범위
  • 세션 범위
  • 애플리케이션 범위

페이지 범위에 저장된 객체들은 특정 요청에 대한 페이지가 프로세스 되는 동안 검색될 수 있다. 요청 범위 내에 저장된 객체들은 요청 프로세스에 참여한 모든 페이지들이 프로세스 하는 동안 검색될 수 있다. 객체가 세션 범위에 저장되어있다면 웹 애플리케이션과의 단일 인터랙트브 세션 동안 사용자가 액세스 한 페이지로 검색될 수 있다. 웹 애플리케이션이 언로드(unload) 될 때 까지 애플리케이션 범위에 저장된 객체는 모든 페이지에서 접근가능하며 모든 사용자들이 접근할 수 있다.

캐릭터 스트링을 희망하는 범위에 있는 객체로 매핑하여 범위안에 객체를 저장할 수 있다. 이러한 경우에는 같은 캐릭터 스트링을 제공하여 범위에서 객체를 검색할 수도 있다. 스트링은 범위 매핑 중 검색되고 매핑된 객체는 리턴된다. Servlet API 내에서 그와 같은 객체들은 상응하는 범위의 애트리뷰트로서 언급된다. EL의 경우 애트리뷰트와 관련된 캐릭터 스트링은 변수 이름으로 간주될 수도 있다.

EL에서 내장 객체들과 관련이 없는 식별자들은 JSP 범위에 저장된 객체들을 명명하는 것으로 간주된다. 그와 같은 식별자는 페이지 범위를 검사하고 그 다음에는 요청 범위, 세션 범위, 애플리케이션 범위 순으로 검사한다. 식별자의 이름이 그 범위에 저장된 객체 이름과 매칭되는지의 여부가 테스트된다. 첫 번째 매치는 EL 식별자의 값으로 리턴된다. EL 식별자는 범위 변수를 참조하는 것으로 간주될 수 있다.

기술적인 관점에서 보면 내장 객체로 매핑하지 않는 식별자는 PageContext 인스턴스의 findAttribute() 메소드를 사용하여 평가되면서 현재 핸들되는 요청에 대해 익스프레션이 발생하는 페이지의 프로세싱을 나타낸다. 식별자의 이름은 이 메소드에 대한 인자로서 전달된다. 이것은 같은 이름을 가진 애트리뷰트에 대한 네 개의 범위를 검색한다. 발견된 첫 번째 매치는 findAttribute() 메소드 값으로 리턴된다. 그와 같은 애트리뷰트가 네 개의 범위 중에 없으면 null이 리턴된다.

궁극적으로 범위 변수는 네 개의 EL 식별자로서 사용될 수 있는 이름을 가진 JSP 범위의 에트리뷰트라고 할 수 있다. 영숫자 이름으로 할당되는 한 범위 변수는 JSP 에 존재하는 모든 메커니즘으로 만들어져 애트리뷰트를 설정할 수 있다. 여기에는 빌트인 <jsp:useBean> 액션은 물론 setAttribute() 메소드가 포함된다. 게다가 네 개의 JSTL 라이브러리에서 정의된 많은 커스텀 태그들은 스스로 범위 변수로서 애트리뷰트 값을 설정할 수 있다.


내장 객체(Implicit objects)

11 개의 EL 내장 객체용 식별자는 표 1과 같다. JSP 내장 객체와 혼동하지 말것!

표 1. EL 내장 객체

Category식별자설명
JSPpageContext현재 페이지의 프로세싱과 상응하는 PageContext 인스턴스
범위pageScope페이지 범위 애트리뷰트 이름과 값과 관련된 Map
requestScope요청 범위 애트리뷰트 이름과 값과 관련된 Map
sessionScope세션 범위 애트리뷰트 이름과 값과 관련된 Map
applicationScope애플리케이션 범위 애트리뷰트 이름과 값과 관련된 Map
요청 매개변수param요청 매개변수의 기본 값을 이름으로 저장하는 Map
paramValues요청 매개변수의 모든 값을 String 어레이로서 저장하는 Map
요청 헤더header요청 헤더의 기본 값을 이름으로 저장하는 Map
headerValues요청 헤더의 모든 값을 String 어레이로서 저장하는 Map
쿠키cook!e요청에 수반되는 쿠키들을 이름으로 저장하는 Map
초기화 매개변수initParam웹 애플리케이션의 콘텍스트 초기화 매개변수를 이릉으로 저장하는 Map

JSP와 EL 내장 객체가 일반적인 하나의 객체를 갖는 반면(pageContext) 다른 JSP 내장 객체는 EL에서 접근 가능하다. 페이지콘텍스트가 다른 8 개의 JSP 내장 객체 모두에 액세스 할 수 있는 속성을 갖고 있기 때문이다.

남아있는 모든 EL 내장 객체들은 맵(map)이다. 이름에 상응하는 객체들을 탐색한다. 첫 번째 네 개의 맵은 이전에 언급한 다양한 애트리뷰트 범위를 나타낸다. 특정 범위 내의 식별자들을 검색하는데 사용될 수 있다. EL이 기본적으로 사용하는 순차적인 탐색 프로세스에 의존하지 않는다.

다음 네 개의 맵은 요청 매개변수와 헤더의 값을 반입하는 용도이다. HPPT 프로토콜이 요청 매개변수와 헤더가 다중 값을 가질 수 있도록 하기 때문에 각각 한 쌍의 맵이 있다. 각 쌍 중에서 첫 번째 맵은 요청 매개변수 또는 헤더에 대한 기본 값을 리턴한다. 실제 요청 시 첫 번째로 지정된 값이 무엇이든 상관없다. 두 번째 맵은 매개변수나 헤더의 값 모두 검색될 수 있도록 한다. 이 맵의 핵심은 매개변수 또는 헤더의 이름이다. 값들은 String 객체의 어레이이다.

쿠키 내장 객체는 요청으로 설정된 쿠키에 대한 접근을 제공한다. 이 객체는 요청과 관련된 모든 쿠키들의 이름을 Cookie 객체들로 매핑하면서 쿠키들의 속성을 나타낸다.

마지막 EL 내장 객체인 initParam은 웹 애플리케이션과 관련된 모든 콘텍스트 초기와 매개변수의 이름과 값을 저장하는 맵이다. 초기화 매개변수들은애플리케이션의 WEB-INF 디렉토리에 있는 web.xml 전개 디스크립터 파일을 통해 정의된다.


접근자(Accessors)

EL 식별자는 내장 객체 또는 범위 변수로서 설명될 수 있기 때문에 자바 객체로 평가해야한다. EL은 상응하는 자바 클래스에서 프리머티브를 래핑/언래핑한다. 하지만 대부분의 경우 식별자들은 자바 객체에 대한 포인터가 된다.

결과적으로 이러한 객체들의 속성이나, 어레이와 컬렉션의 경우 그들의 엘리먼트에 액세스하는 것이 바람직하다. 이를 위해 EL은 두 개의 다른 접근자를 제공한다. 닷(dot) 오퍼레이터(.)와 브래킷 오퍼레이터([])이다. 이들은 속성과 엘리먼트들이 EL을 통해 연산될 수 있도록 한다.

닷 연산자는 객체의 프로퍼티에 접근하는데 사용된다. ${user.firstName} 익스프레션에서 닷 연산자는 user 식별자에 의해 참조된 객체 중 firstName이라는 이름을 가진 속성에 액세스 한다. EL은 자바 빈 규정을 사용하여 객체 속성에 접근하기 때문에 이 속성에 대한 게터(일반적으로 getFirstName())는 이 익스프레션이 정확히 계산하기 위해서 반드시 정의되어야 한다. 액세스되는 속성이 객체일 때 닷 연산자는 재귀적으로 적용될 수 있다. 예를 들어 가상의 user 객체가 자바 객체로서 구현된 address 속성을 갖고 있다면 닷 연산자는 이 객체의 속성에 액세스 하기 위해 사용될 수도 있다. ${user.address.city} 익스프레션은 이 address 객체 중 중첩된 city 속성을 리턴한다.

브래킷 연산자는 어레이와 컬렉션의 엘리먼트를 검색하는데 사용된다. 어레이와 컬렉션(java.util.List를 구현하는 컬렉션)의 경우 검색될 엘리먼트 인덱스는 브래킷 안에 나타난다. 예를 들어 ${urls[3]} 익스프레션은 이 urls 식별자에 의해 참조된 어레이 또는 컬렉션의 네 번째 엘리먼트를 리턴한다.

java.util.Map 인터페이스를 구현하는 컬렉션의 경우 브래킷 연산자는 관련 키를 사용하여 맵에 저장된 값을 찾는다. 이 키는 브래킷 내에서 지정되고 상응하는 값은 익스프레션 값으로 리턴된다. 예를 들어 ${commands["dir"]} 익스프레션은 commands 식별자에 의해 참조된 Map"dir" 키와 관련된 값을 리턴한다.

익스프레션이 브래킷안에 나타날 수 있다. 중첩된 익스프레션의 계산 결과는 컬렉션이나 어레이의 적절한 엘리먼트를 검색하는 인덱스 또는 키로 작용한다. 닷 연산자가 true라면, 브래킷 연산자도 재귀적으로 적용될 수 있다. 이는 EL이 다차원 어레이, 중첩 컬렉션, 또는 둘의 결합에서 엘리먼트를 검색 할 수 있도록 한다. 더욱이 닷 연산자와 브래킷 연산자는 상호운용성이 있다. 예를들어 한 어레이의 엘리먼트가 객체라면 브래킷 연산자는 그 어레이의 엘리먼트를 검색하는데 사용될 수 있고 닷 연산자와 결합하여 엘리먼트 속성 중 하나를 검색할 수 있다. (예를 들어 ${urls[3].protocol}).

EL이 동적 애트리뷰트 값을 정의하는 간한한 언어로서 작용한다고 볼 때, 자바 접근자와는 다른 EL 접근자의 재미있는 특성 중 하나는 null에 적용될 때 예외를 던지지 않는다는 점이다. EL 접근자가 적용되는 객체(예를 들어 ${foo.bar}${foo["bar"]}foo 식별자)가 null이면 접근자 적용 결과 역시 null이다. 이는 대부분의 경우, 도움이 되는 일이다.

마지막으로 닷 연산자와 브래킷 연산자는 상호 교환될 수 있다. 예를 들어 ${user["firstName"]}user 객체의 firstName 속성을 검색하는데 사용될 수 있다. ${commands.dir}commands 맵에서 "dir" 키와 관련된 값을 반입하는데 사용될 수 있는것과 같은 이치이다.


연산자(Operators)

식별자와 접근자를 사용하여 EL은 애플리케이션 데이터(범위 변수를 통해 노출) 또는 환경 관련 정보(EL 내장 객체를 통해 노출)를 포함하고 있는 객체 계층을 트래버스 할 수 있다. 그와 같은 데이터에 간단히 접근하는 것은 많은 JSP 애플리케이션에 필요한 표현 로직을 구현하는데 종종 부적합하다.

EL에는 EL 익스프레션으로 접근된 데이터를 조작 및 비교할 여러 연산자를 포함하고 있다. 이러한 연산자들을 표 2에 요약했다.

표 2. EL 연산자

Category연산자
산술+, -, *, / (or div), % (or mod)
관계형== (or eq), != (or ne), < (or lt), > (or gt), <= (or le), >= (or ge)
논리&& (or and), || (or or), ! (or not)
타당성검사empty

산술 연산자는 더하기, 빼기, 나누기를 지원한다. 다른 연산자들도 제공된다. 나누기와 나머지 연산자들은 비 상징 이름들이라는 대안을 갖고 있다. 산술 연산자의 사용법을 설명하는 예제 익스프레션은 Listing 5에 설명되어 있다. 산술 연산자를 한 쌍의 EL 익스프레션에 적용한 결과는 그러한 익스프레션에 의해 리턴된 숫자 값에 대한 연산자에 적용한 결과이다.


Listing 5. 산술 연산자를 사용하는 EL 익스프레션
${item.price * (1 + taxRate[user.address.zipcode])}

관계형 연산자는 숫자 또는 텍스트 데이터를 비교할 수 있도록 한다. 비교 결과는 부울 값으로서 리턴된다. 논리적 연산자는 부울 값이 결합될 수 있도록 하며 새로운 부울 값을 리턴한다. EL 논리적 연산자는 중첩된 관계형 연산자 또는 논리적 연산자의 결과에 적용될 수 있다. (Listing 6).


Listing 6. 관계형 연산자 및 논리적 연산자를 사용하는 EL 익스프레션
${(x >= min) && (x <= max)}

EL 연산자는 empty 이다. 데이터의 타당성 검사에 특히 유용하다. empty 연산자는 하나의 익스프레션을 인자로 취한다.(${empty input}). 그리고 익스프레션이 empty 값으로 계산했는지의 여부를 나타내는 부울 값을 리턴한다. null로 계산한 익스프레션은 empty로 간주된다. 어떤 엘리먼트도 없는 컬렉션이나 어레이와 같다. empty 연산자는 인자가 길이가 0인 String으로 계산했다면 true로 리턴한다.

EL 연산자의 우선순위는 표 3에 정리되어 있다. Listing 5와 6에 제안된 것 처럼 괄호는 그룹 익스프레션에 사용되고 일반적인 우선순위를 따른다.

표 3. EL 연산자 우선순위 (위->아래, 왼쪽->오른쪽)

[], .
()
unary -, not, !, empty
*, /, div, %, mod
+, binary -
() <, >, <=, >=, lt, gt, le, ge
==, !=, eq, ne
&&, and
||, or


리터럴(Literals)

숫자, 캐릭터 스트링, 부울, null은 EL 익스프레션에서 리터럴 값으로 지정될 수 있다. 캐릭터 스트링은 싱글 쿼트 또는 더블 쿼트로 범위가 지정된다. 부울 값은 truefalse로 계산된다.


Taglib 지시문

앞서 언급했지만 JSTL 1.0에는 네 개의 커스텀 태그 라이브러리가 포함되어 있다. 익스프레션 언어로 JSTL 태그의 인터랙션을 설명하기 위해 JSTL core 라이브러리에서 여러 태그들을 검토할 것이다. 모든 JSP 커스텀 태그 라이브러리로 true가 된다면 taglib 지시문은 이 라이브러리 태그를 사용할 수 있는 페이지에 포함되어야한다. 이 특정 라이브러리에 대한 지시문은 Listing 7에 나타나있다.


Listing 7. JSTL core 라이브러리의 EL 버전용 테그립 지시문
<%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %>

실제로 JSTL core 라이브러리에 상응하는 두 개의 Taglib 지시문이 있다. JSTL텐에서 EL은 옵션이기 때문이다. JSTL 1.0 의 네 개의 커스텀 태그 라이브러리들은 동적 애트리뷰트 값을 지정할 때 EL 대신 JSP 익스프레션을 사용하는 대안 버전을 갖고있다. 이러한 대안 라이브러리는 JSP의 전통적인 요청시간 애트리뷰트 값에 의존하기 때문에 RT 라이브러리로 일컬어진다. 반면 익스프레션 언어를 사용하는 것은 EL 라이브러리라고 한다. 개발자들은 대안 Taglib 지시문을 사용하는 각각의 라이브러리의 버전들을 구별한다. RT 버전의 코어 라이브러리를 사용하기 위한 지시문은 Listing 8에 나와있다. 하지만 지금은 EL에 집중해야 하기 때문에 지금 필요한 것은 이 지시문들 중 첫 번째 것이다.


Listing 8. RT 버전의 JSTL core 라이브러리용 태그립 지시문
<%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c_rt" %>


변수 태그

첫 번째 JSTL 커스텀 태그는 <c:set> 액션이다. 이미 언급했듯이 범위 변수는 JSTL에서 핵심적인 역할을 하고 <c:set> 액션은 태그 기반의 매커니즘을 제공하여 범위 변수의 생성 및 설정에 쓰인다. 이 액션의 신택스는 Listing 9와 같다. var 애트리뷰트는 범위 변수 이름을 정하고 scope 애트리뷰트는 변수가 머물게 될 범위를 나타내고, value 애트리뷰트는 변수가 될 값을 지정한다. 지정된 변수가 이미 존재하면 지시된 값으로 할당된다. 그렇지 않다면 새로운 범위 변수가 만들어지고 그 값으로 초기화된다.


Listing 9. <c:set> 액션 신택스
<c:set var="name" scope="scope" value="expression"/>

scope 애트리뷰트는 선택적이며 page로 기본 설정되어 있다.

<c:set>의 두 예제는 Lisitng 10에 설명되어 있다. 첫 번째 예제에서 세션 범위 변수는 String 값으로 설정된다. 두 번째에서는 익스프레션은 숫자 값을 설정하는데 사용된다. square라는 페이지 범위 변수는 x 라는 요청 매개변수 값을 배가시킨 결과로 할당된다.


Listing 10. <c:set> 액션 예제
<c:set var="timezone" scope="session" value="CST"/><c:set var="square" value="${param['x'] * param['x']}"/>

애트리뷰트를 사용하는 대신 범위 변수용 값을 <c:set> 액션의 바디 콘텐트로 설정할 수 있다. 이러한 접근방식을 사용하여 Listing 10의 첫 번째 예제를 Listing 11과 같이 재작성할 수 있다. 더욱이 <c:set> 태그의 바디 콘텐트가 커스텀 태그를 적용하는 것도 가능하다. <c:set>의 바디 안에서 만들어진 모든 콘텐트는 String 값 같이 지정된 변수에 할당된다..


Listing 11. 바디 콘텐트를 통해 <c:set> 액션용 값 지정하기
<c:set var="timezone" scope="session">CST</c:set>

JSTL core 라이브러리에는 범위 변수를 관리하는 두 번째 태그(<c:remove>)가 포함되어 있다. 이름에서 시사되는 바와 같이 <c:remove> 액션은 범위 변수를 지우는데 사용되고 두 개의 애트리뷰트를 취한다. var 애트리뷰트는 제거될 변수를 명명하고 선택적인 scope 애트리뷰트는 제거되어야 할 범위를 나타낸다. (Listing 12).


Listing 12. <c:remove> 액션 예제
<c:remove var="timezone" scope="session"/>


아웃풋

<c:set> 액션은 익스프레션의 결과가 범위 변수로 할당될 수 있도록 하는 반면 개발자들은 익스프레션 값을 저장하는 대신 간단히 디스플레이하기를 원한다. 이는 JSTL의 <c:out> 커스텀 태그의 몫이다. (Listing 13). 이 태그는 value 애트리뷰트에서 지정된 익스프레션을 계산한다. 그런다음 결과를 프린트한다. 선택적 default 애트리뷰트가 지정되면 value 애트리뷰트의 익스프레션이 null 또는 비어있는 String으로 계산될 때 <c:out> 액션은 값을 프린트한다.


Listing 13. <c:out> 액션 신택스
<c:out value="expression" default="expression" escapeXml="boolean"/>

escapeXml 애트리뷰트 또한 선택사항이다. "<", ">", "&" 같은 캐릭터가 <c:out> 태그에 의해 아웃풋 될 때 종료되는지의 여부를 제어한다. escapeXml이 true로 설정되어 있다면 이 캐릭터들은 상응하는 XML 인터티(<, >, &)로 바뀐다.

예를 들어, user라는 세션 범위 변수가 있다고 가정해보자. 이것은 사용자에 대한 usernamecompany라는 두 개의 속성들을 정의하는 클래스의 인스턴스이다. 이 객체는 사용자가 사이트에 접근할 때마다 세션에 할당된다. 하지만 이 두 개의 속성들은 사용자가 실제로 로그인하기 전까지 설정되지 않는다. (Listing 14). 일단 사용자가 로그인하면 "Hello"가 디스플레이 되고 뒤따라서 사용자 이름과 감탄부호가 나온다. 사용자가 로그인하기 전에 여기에서 생긴 콘텐트는 "Hello Guest!" 라는 구(phrase)가 된다. 이 경우 username 속성이 초기화되지 않았기 때문에 <c:out> 태그는 default 애트리뷰트 값을 프린트한다.


Listing 14. <c:out> 액션 예제 (디폴트 콘텐트)
Hello <c:out value="${user.username}" default=="Guest"/>!

<c:out> 태그의 escapeXml 애트리뷰트를 사용하는 Listing 15를 보자. company 속성이 자바 String 값인 "Flynn & Sons"으로 설정되었다면 이 액션에서 생긴 콘텐트는 Flynn & Sons이 된다. 이 액션이 HTML 또는 XML 콘텐트를 만드는 JSP 페이지의 일부라면 이 캐릭터의 스트링 중간에 있는 앰퍼샌트 부호는 HTML 또는 XML이 문자를 제어하고 이 콘텐트의 렌더링 또는 파싱을 방해하는것으로 해석하고 끝난다. escapeXml 애트리뷰트의 값이 true로 설정되면 생성된 콘텐트는 Flynn & Sons이 된다. 이 콘텐트를 만나는 브라우저 또는 파서는 인터프리테이션에 아무 문제가 없다. HTML과 XML이 JSP 애플리케이션에서 가장 일반적인 콘텐트 유형이라면 escapeXml 애트리뷰트의 디폴트 값이 true라는 것은 놀라운 일이 아니다.


Listing 15. <c:out> 액션 예제)
<c:out value="${user.company}" escapeXml=="false"/>


디폴트 값으로 변수 설정하기

동적 데이터를 단순하게 하는 것 외에도 디폴트 값을 지정하는 <c:out>의 기능은 <c:set>을 통해 변수 값을 설정할 때에도 유용하다. 범위 변수에 할당된 값이 <c:set> 태그의 바디 콘텐트로 지정될수 있고 value 애트리뷰트로서도 가능하다. <c:out> 액션을 <c:set> 태그의 바디 콘텐트에 중첩하여 변수 할당은 이것의 디폴트 값을 이용할 수 있다. (Listing 11).

이러한 접근 방식은 Listing 16에도 설명되어 있다. 외부 <c:set> 태그의 작동은 단순하다.


Listing 16. <c:set>과 <c:out> 결합: 디폴트 변수 값 제공
<c:set var="timezone" scope=="session">   <c:out value="${cook!e['tzPref'].value}" default=="CST"/></c:set>

요청에 제공된 tzPref 라는 이름의 쿠키가 없다. 내장 객체를 사용한 검색은 null이 된다는 것을 의미한다. 익스프레션은 전체적으로 null을 리턴한다. value 애트리뷰트를 계산한 값이 null 이기 때문에 <c:out> 태그는 default 애트리뷰트를 계산한 결과를 아웃풋한다.


참고자료

필자소개

Mark Kolb는 소프트웨어 엔지니어이며 Web Development with JavaServer Pages, 2nd Edition의 공동저자이다.

'Computer Science' 카테고리의 다른 글

W3C의 오픈소스 HTML/CSS 에디터  (0) 2012.03.24
JSTL 기초, Part 2: core 분석 (한글)  (0) 2012.03.20
[정리] EL :: Function  (0) 2012.03.20
[정리] EL :: Basic  (0) 2012.03.20
ubuntu에서 GTK 개발환경 꾸미기  (0) 2012.03.19

[정리] EL :: Function

EL :: Function (함수)

EL도 파라미터를 사용할 수 있고.. 따라서 함수 사용도 가능합니다. (Java Bean과 다른 점이죠.) EL 문법으로 함수를 만들지는 않고 만들어 둔 자바 함수를 가져다가 사용할 수 있습니다. JSP 라는게 어짜피 MVC 패턴으로 놓고 보면 "VIEW"에 해당하는 것이기 때문에 원래 복잡한 로직은 Model, Controller에서 처리를 하는게 맞습니다. 그래서 스크립틀릿 같은게 지양되고 EL이 권장되는 거겠죠. 그럼에도 불구하고 함수를 사용할 수 있다는건 좀 아이러니 하기도 하네요.

자바에서 왜 Function(함수)라는 이름을 사용할까요? 함수와 메소드의 차이점이 뭔지 기억하신다면, 아래 메소드를 왜 클래스 멤버로 선언하는지 딱 감이 오실겁니다.


EL의 함수는 이렇게 동작합니다.

1) 자바 클래스에서 사용하고자 하는 메소드를 클래스 멤버(static)로 선언합니다.

2) WEB-INF 밑에 TLD(Tag Library Descriptor;XML 타입) 파일을 가져다 놓습니다. (이때 WEB-INF 서브 폴더면 어디든 상관 없습니다.)

3) JSP에서 <%@ taglib %> 지시자로 해당 함수를 지정한 후, EL에서 사용합니다.


그럼 순서대로 소스를 보죠..


DicePlay.java

  1. package foo;
    public class DicePlay {

    public static int rollDice() {
    int result = (int)(Math.random() * 6) + 1;
    return result;
    }

    }


DiceFunctions.tld

  1. <?xml version="1.0" encoding="UTF-8"?>
    <taglib xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
    version="2.0">
    <tlib-version>1.2</tlib-version>
    <short-name>DiceFunctions</short-name>
  2. <uri>DiceFunctions</uri>
    <function>
  3. <name>rollIt</name>
  4. <function-class>foo.DicePlay</function-class>
  5. <function-signature>int rollDice()</function-signature>
  6. </function>
    </taglib>


TldTest.jsp

  1. <%@ taglib prefix="mine" uri="DiceFunctions" %>
    <p>${mine:
    rollIt() }</p>

현재(8.15) 이클립스에서 버그가 있는데, 함수 호출하는 부분에서 EL Syntax Error 라고 나옵니다. https://bugs.eclipse.org/bugs/show_bug.cgi?id=280621 원인도 나와있긴 한데.. 아직 패치는 나오지 않은 것 같습니다. 아니면 저 부분을 찾아서 디버깅 후 컴파일 하는 방법도 있겠네요 (...)

2010.2.6 현재 버그 수정되었습니다.


이것도 돌아가는게 DD와 비슷한데요. 특이한 점은 TLD 파일을 WEB-INF 어디에 놓던 간에 따로 경로 지정을 안해줘도 된다는 겁니다. <@taglib> uri 속성TLD 파일의 <uri>의 내용만 맞으면 어떤 형식으로 들어가도 상관은 없습니다. 그리고 prefix 속성(맘대로 지정할 수 있습니다.)이 있기 때문에 함수를 종류나 쓰임새 별로 묶을 때 그냥 한 곳에 다 때려밖고 prefix만 잘 줘도 충분해 보입니다. XML Namespace의 장점을 잘 살려넣은듯 보이네요.

만약 함수에 파라미터를 넘겨야 하는 경우엔, tld 파일을 아래와 같이 수정합니다.

  1. <function-signature>String toUpperCase(java.lang.String)</function-signature>

[정리] EL :: Basic

[출처] http://ethdemor.springnote.com/pages/4015477

선에서 "JSP는 자바 언어를 몰라도 된다" 라고 했던 말은 EL과 Java Bean을 염두해 두고 한 말이지 스크립틀릿을 두고 한 말은 아닙니다.

Jaba Bean과 Enterprise Java Bean은 또 다른 거라고 하더군요. EJB는 뭘까요?

Expression Language (표현식 언어)

아직까지는 html 안에 java 코딩을 하는 JSP가 많이 남아있지만, 점차 없어지는 추세라고 하는군요. 왜 그럴까요? 그건 다음과 같은 이유랍니다.

1) 웹 페이지 디자이너가 자바를 알 필요가 있을까?

2) JSP 안에 있는 자바 코드는 유지/보수 하기가 어렵다.

EL은 JSP를 사용할 경우 발생하는 위의 두 가지 문제점에 대한 해답입니다.

EL은 JSP 2.0 스펙에 공식적으로 포함되었으며, 스크립틀릿이나 표현식으로 했던 작업을 간편하게 할 수 있습니다. EL의 핵심은 자바 코딩을 다른 곳에 해놓고, EL을 사용해 호출하자는 거죠. 요즘 추세인 비지니스 로직(자바 프로그래머)과 프레젠테이션(디자이너)을 분리해서 각자 할 일을 확실하게 나누자는 거겠죠. 코드가 같은 파일에 들어 있다면 많은 애로사항이 있을 것도 같습니다. EL은 비 자바 개발자를 위해 만들어진 문법이지만 위의 두 가지 문제 중 두 번째 문제를 해결할 수 있다는 점에서 자바 개발자에게도 어필할 수 있죠.


문법이 '직관적'이다 라는게 어디서 부터 시작한 건지는 모르겠지만 어쩌면 그 시작이 XML 일지도 모르겠습니다. XML의 Extensible한 메타 언어이기 때문에 가지는 장점 중 하나는 바로 자기기술적(self-descriptive)이라는 겁니다. 한국말로 적당히 번역해 보자면 특별한 문법을 알지 못해도 직관적으로 눈에 읽히는 코드 라고 할 수 있습니다. (XSD 쪽으로 들어가면 이 의미도 많이 퇴색하긴 하지만 그래도 좀 낫죠.)


샘플 코드 #1 XML

  1. <note>

  2. <to>Tove</to>

    <from>Jani</from>

    <heading>Reminder</heading>

    <body>Don`t forget me this weekend!</body>

    </note>


샘플 코드 #2 HTML

  1. <html>
    <head>

    <title>Reminder</title>

    </head>

  2. <body>

    <p>to - Tove</p>

  3. <p>from - Jani</p>

  4. <p>tit.........젠장!

샘플 코드 #1 을 보고 각 태그가 무슨 의미를 담고 있는지 모르는 사람이 있을까요?

자바 코딩을 하지 못한다라.. 그냥 '하지맙시다' 라고 눈빛을 보내는 것 보다 항상 더 우아한 방법이 있죠.


web.xml

  1. <web-app ...>
  2. <jsp-config>

  3. <jsp-property-group>

  4. <url-pattern>*.jsp</url-pattern>

  5. <scripting-invalid>true</scripting-invalid>

  6. <el-ignored>true</el-ignored>

  7. </jsp-property-group>

  8. </jsp-config>

  9. </web-app>

이것이 <scripting-invalid> 관련 설정의
유일한 방법입니다. JSP 2.0 초안엔 <@ page> 속성으로 뭐가 있었다는데 하여간 최종안에선 사라졌습니다.

아래는 EL을 사용하지 않는다는 설정입니다. 디폴트는 false죠. (기존에 EL 문법처럼 어딘가 코딩을 해놨다면 그게 문제를 일으킬 수도 있지 않겠어요?)


  1. <%@ page isElIgnored="true" %>

이렇게 페이지 별로 따로 설정도 가능합니다. 페이지 지시자는 DD의 설정을 오버라이드 합니다.


<url-pattern> 값 지정으로 전체가 아닌 개별적인 jsp 파일도 지정 가능하다.


Action 태그의 단점은 빈의 프로퍼티가 기본 자료형(+문자열)이 아닐 때 금새 드러나 버립니다. 만약 Foo 객체가 프로퍼티로 Bar 클래스 객체를 가지고 있을 경우, getProperty를 한다면 객체기 때문에 toString()이 호출되겠죠. Bar의 속성에 접근할 수 있는 방법이 없습니다. 그럼 다시 스크립팅의 세계로 가야하느냐.. 답은 EL 입니다.


간단한 한 줄 비교 코드 # 1

  1. Scriptlet: <%= foo.getBar().getName() %>
  2. ActionTag: <jsp:getProperty id="foo" property="bar" /> <!-- 이게 한계. bar.name 하면 에러! -->
  3. EL: ${foo.bar.name}


이렇게 보면 Scriptlet이나 EL이나 별 다른 점이 없어 보이지만, 만약 저 foo 객체가 바인딩되서 넘아온다면 코드가 어떻게 바뀔까요?


  1. Scriptlet: <%= ((foobar.Foo)request.getAttribute("Foo")).getBar().getName() %>
  2. EL: ${foo.bar.name}


Action 태그의 두 번째 단점은 JSP 내장 객체에 접근할 수 없다는 겁니다. 반면에 EL은 쉽게 접근 가능하죠.


간단한 한 줄 비교 코드 # 2

  1. Scriptlet: <%= application.getAttribute("name") %>

  2. ActionTag: <jsp:getProperty id="application" property="name" /> ERROR!

  3. EL: ${applicationScope.name}


사실 첫 번째 한 줄 비교 코드는 예가 좀 잘못되있는데, 그 이유는 EL 첫 자리에 오는 변수는 내장 객체 아니면 Scope에 바인딩 된 속성이기 때문입니다. 따라서 첫 번째 예제는 foo 객체가 어느 Scope이던 바인딩 되어 있다는 걸 전제로 해야합니다.

sudo apt-get build-dep inkscape

sudo apt-get install build-essential libgtk2.0-dev
Oracle11G 우분투에 설치하기

\*설치 환경*
OS : Ubuntu 10.10 amd64 server (64비트환경)
Database : Oracle 11g
  • 우분투 설치 후 다른 서버환경 설치보다 우선적으로 작업하는 것을 권장함.

예) 아파치, 톰캣, SVN 등등보다 우선해서 할 것.

  • 서버 환경에선 설치UI가 안떠서 ubuntu-desktop 을 다시 설치했음.

    $sudo apt-get install ubuntu-desktop

(데스크탑 환경 설치하고 언어팩까지 설정하면 매우 오랜 시간동안 설치작업이 진행됨.)

추가로 작업한 것: 서버환경을 쓸 것이므로 웬만한 패키지는 다시 삭제함.(Game, 컴피즈환경 등..)

*설치 시작*

  1. 터미널을 열고 설치에 필요한 기본 시탭틱을 설치한다.

** 오라클을 설치하기 위해 필요한 패키지이다.

binutils-2.17.50.0.6-2.el5
compat-libstdc++-33-3.2.3-61
elfutils-libelf-0.125-3.el5
elfutils-libelf-devel-0.125
glibc-2.5-12
glibc-common-2.5-12
glibc-devel-2.5-12
glibc-headers-2.5-12
gcc-4.1.1-52
gcc-c++-4.1.1-52
libaio-0.3.106
libaio-devel-0.3.106
libgcc-4.1.1-52
libstdc++-4.1.1
libstdc++-devel-4.1.1-52.e15
make-3.81-1.1
sysstat-7.0.0



-- 패키지명은 운영체제마다 약간식 틀리다. 알맞은 패키지를 검색하여 설치해주면 되는 데, 대부분 패키지는 우분투에 설치되어있다.
$ sudo apt-get update
$ sudo apt-get upgrade

-- 설치가 올바르게 되지 않을 경우 하나하나 설치해주는 것도 좋은 방법이다.
$ sudo apt-get install binutils elfutils glibc-2.9-1 gcc libaio1 libaio-dev libgcc1 libstdc++6 libstdc++6-4.3-dev make sysstat lesstif2 lesstif2-dev build-essential rpm libc6 original-awk gawk ksh alien

주의!!! 각 패키지는 개별적으로 설치하지 않을 경우 일괄설치 안되는 경우가 있어 오라클 설치도중 에러가 난다. 일일이 각 패키지를 설치되었는지 확인할것.

$ sudo apt-get install gcc binutils libaio1 lesstif2 lesstif2-dev make rpm libc6 libstdc++5 build-essential

$ sudo apt-get install

$ sudo apt-get install



2. 시스템에 그룹과 오라클 유저를 만든다.

$sudo groupadd oinstall
$sudo groupadd dba
$sudo groupadd nobody
$sudo useradd -m oracle -g oinstall -G dba -s /bin/bash
$sudo passwd oracle



3. 오라클에 필요한 memory와 CPU resources를 위해 커널 파라미터를 수정한다.(메모리와 CPU자원이 충분하지 않으면 오라클 인스턴스가 올라가질 않는다. 물론 설치도 안됨.)
우선 파일 수정을 위해 gedit로 sysctl.conf파일을 연다.

$sudo gedit /etc/sysctl.conf

파일의 제일 밑에 부분에 아래 정보를 복사해서 붙여넣기.

kernel.shmall = 2097152
kernel.shmmax = 2147483648
kernel.shmmni = 4096
kernel.sem = 250 32000 100 128
fs.file-max = 65536
net.ipv4.ip_local_port_range = 1024 65000



저장하고 파일을 닫는다. 위에서 수정한 sysctl.conf파일은 Ubuntu시스템이 처음 부팅시 한번 읽어들이는 정보이므로 재부팅을 한다. 귀찮으면 모듈을 강제로 내렸다가 올리면 된다.

$sudo /sbin/sysctl -p



마지막으로 시스템에 security의 limits파일에 몇몇 작업을 해야된다.

$sudo vi /etc/security/limits.conf



파일의 제일 밑에 부분에 아래 정보를 복사해서 붙여넣기.

* soft nproc 2047
* hard nproc 16384
* soft nofile 1024
* hard nofile 65536



4. Ubuntu를 레드햇 계열로 속이기? 위한 작업 및 폴더의 권한 설정과 Oracle user의 환경변수 설정 작업을 해야된다.

$sudo ln -s /usr/bin/awk /bin/awk
$sudo ln -s /usr/bin/rpm /bin/rpm
$sudo ln -s /lib/libgcc_s.so.1 /lib/libgcc_s.so
$sudo ln -s /usr/bin/basename /bin/basename
$sudo mkdir /oracle
$sudo mkdir /oracle/11g
$sudo chown -R oracle:oinstall /oracle
$sudo chmod -R 775 /oracle



환경변수 설정을 위해 etc밑에 profile을 gedit로 연다.

$sudo gedit /etc/profile



파일의 제일 밑에 부분에 아래 정보를 복사해서 붙여넣기.

export ORACLE_BASE=/oracle
export ORACLE_HOME=/oracle/11g
export ORACLE_SID=ora11
export PATH=$PATH:$ORACLE_HOME/bin



환경변수가 제대로 적용 됬는지를 터미널을 열어서 확인해 본다.

$ su - oracle
Password:
$ echo $ORACLE_BASE
/oracle
$ echo $ORACLE_HOME
/oracle/11g


레드햇 엔터프라이즈 릴리즈 파일을 만든다. - 안해도 된다.
$sudo gedit /etc/redhat-release

파일에 아래 정보를 복사해서 붙여넣기.

Red Hat Enterprise Linux AS release 3 (Taroon)



여기까지해서 필요한 커널 및 환경 변수 설정은 끝났다.

5. 오라클 사이트에서 리눅스용 오라클을 다운로드 하고 압축을 푼다.여기서는 예로 다운로드 받은 파일의 이름은 10201_database_linux32.zip, ubuntu의 유저는 mnbusr로 하겠다.
다운받은 파일의 절대경로는 /home/mnbusr/에 위치한다고 가정한 상태에서 권한 설정 및 오라클 유저로 압축을 푼다.

$sudo chown oracle:oinstall /home/mnbusr/linux.x64_11gR2_database_1of2.zip

$sudo chown oracle:oinstall /home/mnbusr/linux.x64_11gR2_database_2of2.zip
$sudo chmod 775 /home/mnbusr/*.zip
$sudo mv /home/mnbusr/*.zip /home/oracle
$su - oracle
$unzip /home/mnbusr/linux.x64_11gR2_database_1of2.zip

$unzip /home/mnbusr/linux.x64_11gR2_database_2of2.zip
$rm /home/mnbusr/linux.x64_11gR2_database_1of2.zip

$rm /home/mnbusr/linux.x64_11gR2_database_2of2.zip


6. 시스템(Ubuntu)를 재부팅한 후 그놈 세션에서 oracle user로 로그인 한다.
터미널을 실행하고 다음을 명령을 실행해 인스톨을 진행한다.

$/home/oracle/database/runInstaller



7. 설치 화면에서 부터는 Windows에서 설치하는방법과 동일하지만 한가지 주의 해야 될 부분은 SID입력 부분이다. 설치 과정 4에서 환경변수 설정에서 입력한 SID인 ora11을 입력하지 않으면 지금까지 한 작업이 뻘짓이 될 수 있다;;;;
설치가 완료되면 시스템을 재부팅

8. 그놈 세션에서 원래 유저(예를 들면 mnbusr)로 로그인후 터미널을 실행해 etc밑에 oratab파일에서 ora11:/oracle/11g:N부분을 ora11:/oracle/ora11g:Y로 수정한다. 덧붙여 설명하면 orcl11은 인스턴스이고 가운데 부분은 ORACLE_HOME 마지막 부분은 오라클 인스턴스의 자동 실행 여부(Y/N)이다.

$sudo gedit /etc/oratab
orcl10:/oracle/10g:N

이부분을 ...

orcl10:/oracle/10g:Y

요렇게..



9. 터미널에서 오라클 유저로 로그인해 오라클 리스너의 절대 패스를 수정함.

$su - oracle
$vi $ORACLE_HOME/bin/dbstart



vi편집기에서 다음 라인을 찾은 다음

# Set this to bring up Oracle Net Listener
ORACLE_HOME_LISTNER=/ade/vikrkuma_new/oracle
if [ ! $ORACLE_HOME_LISTNER ] ; then
echo "ORACLE_HOME_LISTNER is not SET, unable to auto-start Oracle Net Listener"
else
LOG=$ORACLE_HOME_LISTNER/listener.log



오라클 리스너의 패스를 수정하고 저장하고 나온다.

ORACLE_HOME_LISTNER=/oracle/10g



10. 오라클을 사용하면 된다...

설치하고 디비 기동 조차 못하면 대략 낭패....
1. 우선 터미널을 열고 오라클 유저로 접속
2. 리스너 스타트
3. dbconsole 스타트
4. 호스트 인증 받기 예) oracle / pass
5. 인스턴트 시작하기 예) sys / pass

su - oracle
pass:

lsnrctl start

emctl start dbconsole

브라우저 열고 http://localhost:1158/em

출처 : http://kekedie.tistory.com/category/Database?page=4

~
Oracle11G 우분투에설치하기


편집하기

REV.0.1 작업중. 2011.04.20

*설치 환경*
OS : Ubuntu 10.10 amd64 server (64비트환경)
Database : Oracle 11g
우분투 설치 후 다른 서버환경 설치보다 우선적으로 작업하는 것을 권장함.

예) 아파치, 톰캣, SVN 등등보다 우선해서 할 것.

서버 환경에선 설치UI가 안떠서 ubuntu-desktop 을 다시 설치했음.

$sudo apt-get install ubuntu-desktop


(데스크탑 환경 설치하고 언어팩까지 설정하면 매우 오랜 시간동안 설치작업이 진행됨.)

추가로 작업한 것: 서버환경을 쓸 것이므로 웬만한 패키지는 다시 삭제함.(Game, 컴피즈환경 등..)

*설치 시작*
1.터미널을 열고 설치에 필요한 기본 시탭틱을 설치한다.

** 오라클을 설치하기 위해 필요한 패키지이다.

binutils-2.17.50.0.6-2.el5
compat-libstdc++-33-3.2.3-61
elfutils-libelf-0.125-3.el5
elfutils-libelf-devel-0.125
glibc-2.5-12
glibc-common-2.5-12
glibc-devel-2.5-12
glibc-headers-2.5-12
gcc-4.1.1-52
gcc-c++-4.1.1-52
libaio-0.3.106
libaio-devel-0.3.106
libgcc-4.1.1-52
libstdc++-4.1.1
libstdc++-devel-4.1.1-52.e15
make-3.81-1.1
sysstat-7.0.0

-- 패키지명은 운영체제마다 약간식 틀리다. 알맞은 패키지를 검색하여 설치해주면 되는 데, 대부분 패키지는 우분투에 설치되어있다.
$ sudo apt-get update
$ sudo apt-get upgrade

-- 설치가 올바르게 되지 않을 경우 하나하나 설치해주는 것도 좋은 방법이다.
$ sudo apt-get install binutils elfutils glibc-2.9-1 gcc libaio1 libaio-dev libgcc1 libstdc++6 libstdc++6-4.3-dev make sysstat lesstif2 lesstif2-dev build-essential rpm libc6 original-awk gawk ksh alien

주의!!! 각 패키지는 개별적으로 설치하지 않을 경우 일괄설치 안되는 경우가 있어 오라클 설치도중 에러가 난다. 일일이 각 패키지를 설치되었는지 확인할것.


$ sudo apt-get install gcc binutils libaio1 lesstif2 lesstif2-dev make rpm libc6 libstdc++5 build-essential

$ sudo apt-get install

$ sudo apt-get install

2. 시스템에 그룹과 오라클 유저를 만든다.

$sudo groupadd oinstall
$sudo groupadd dba
$sudo groupadd nobody
$sudo useradd -m oracle -g oinstall -G dba -s /bin/bash
$sudo passwd oracle

3. 오라클에 필요한 memory와 CPU resources를 위해 커널 파라미터를 수정한다.(메모리와 CPU자원이 충분하지 않으면 오라클 인스턴스가 올라가질 않는다. 물론 설치도 안됨.)
우선 파일 수정을 위해 gedit로 sysctl.conf파일을 연다.

$sudo gedit /etc/sysctl.conf

파일의 제일 밑에 부분에 아래 정보를 복사해서 붙여넣기.

kernel.shmall = 2097152
kernel.shmmax = 2147483648
kernel.shmmni = 4096
kernel.sem = 250 32000 100 128
fs.file-max = 65536
net.ipv4.ip_local_port_range = 1024 65000


저장하고 파일을 닫는다. 위에서 수정한 sysctl.conf파일은 Ubuntu시스템이 처음 부팅시 한번 읽어들이는 정보이므로 재부팅을 한다. 귀찮으면 모듈을 강제로 내렸다가 올리면 된다.

$sudo /sbin/sysctl -p

마지막으로 시스템에 security의 limits파일에 몇몇 작업을 해야된다.

$sudo vi /etc/security/limits.conf

파일의 제일 밑에 부분에 아래 정보를 복사해서 붙여넣기.

* soft nproc 2047
* hard nproc 16384
* soft nofile 1024
* hard nofile 65536

4. Ubuntu를 레드햇 계열로 속이기? 위한 작업 및 폴더의 권한 설정과 Oracle user의 환경변수 설정 작업을 해야된다.

$sudo ln -s /usr/bin/awk /bin/awk
$sudo ln -s /usr/bin/rpm /bin/rpm
$sudo ln -s /lib/libgcc_s.so.1 /lib/libgcc_s.so
$sudo ln -s /usr/bin/basename /bin/basename
$sudo mkdir /oracle
$sudo mkdir /oracle/11g
$sudo chown -R oracle:oinstall /oracle
$sudo chmod -R 775 /oracle

환경변수 설정을 위해 etc밑에 profile을 gedit로 연다.

$sudo gedit /etc/profile

파일의 제일 밑에 부분에 아래 정보를 복사해서 붙여넣기.

export ORACLE_BASE=/oracle
export ORACLE_HOME=/oracle/11g
export ORACLE_SID=ora11
export PATH=$PATH:$ORACLE_HOME/bin

환경변수가 제대로 적용 됬는지를 터미널을 열어서 확인해 본다.

$ su - oracle
Password:
$ echo $ORACLE_BASE
/oracle
$ echo $ORACLE_HOME
/oracle/11g


레드햇 엔터프라이즈 릴리즈 파일을 만든다. - 안해도 된다.

$sudo gedit /etc/redhat-release

파일에 아래 정보를 복사해서 붙여넣기.

Red Hat Enterprise Linux AS release 3 (Taroon)

여기까지해서 필요한 커널 및 환경 변수 설정은 끝났다.

5. 오라클 사이트에서 리눅스용 오라클을 다운로드 하고 압축을 푼다.여기서는 예로 다운로드 받은 파일의 이름은 10201_database_linux32.zip, ubuntu의 유저는 mnbusr로 하겠다.
다운받은 파일의 절대경로는 /home/mnbusr/에 위치한다고 가정한 상태에서 권한 설정 및 오라클 유저로 압축을 푼다.


$sudo chown oracle:oinstall /home/mnbusr/linux.x64_11gR2_database_1of2.zip

$sudo chown oracle:oinstall /home/mnbusr/linux.x64_11gR2_database_2of2.zip
$sudo chmod 775 /home/mnbusr/*.zip
$sudo mv /home/mnbusr/*.zip /home/oracle
$su - oracle
$unzip /home/mnbusr/linux.x64_11gR2_database_1of2.zip

$unzip /home/mnbusr/linux.x64_11gR2_database_2of2.zip
$rm /home/mnbusr/linux.x64_11gR2_database_1of2.zip

$rm /home/mnbusr/linux.x64_11gR2_database_2of2.zip


6. 시스템(Ubuntu)를 재부팅한 후 그놈 세션에서 oracle user로 로그인 한다.
터미널을 실행하고 다음을 명령을 실행해 인스톨을 진행한다.

$/home/oracle/database/runInstaller

7. 설치 화면에서 부터는 Windows에서 설치하는방법과 동일하지만 한가지 주의 해야 될 부분은 SID입력 부분이다. 설치 과정 4에서 환경변수 설정에서 입력한 SID인 ora11을 입력하지 않으면 지금까지 한 작업이 뻘짓이 될 수 있다;;;;
설치가 완료되면 시스템을 재부팅

8. 그놈 세션에서 원래 유저(예를 들면 mnbusr)로 로그인후 터미널을 실행해 etc밑에 oratab파일에서 ora11:/oracle/11g:N부분을 ora11:/oracle/ora11g:Y로 수정한다. 덧붙여 설명하면 orcl11은 인스턴스이고 가운데 부분은 ORACLE_HOME 마지막 부분은 오라클 인스턴스의 자동 실행 여부(Y/N)이다.


$sudo gedit /etc/oratab
orcl10:/oracle/10g:N

이부분을 ...

orcl10:/oracle/10g:Y

요렇게..

9. 터미널에서 오라클 유저로 로그인해 오라클 리스너의 절대 패스를 수정함.

$su - oracle
$vi $ORACLE_HOME/bin/dbstart

vi편집기에서 다음 라인을 찾은 다음

# Set this to bring up Oracle Net Listener
ORACLE_HOME_LISTNER=/ade/vikrkuma_new/oracle
if [ ! $ORACLE_HOME_LISTNER ] ; then
echo "ORACLE_HOME_LISTNER is not SET, unable to auto-start Oracle Net Listener"
else
LOG=$ORACLE_HOME_LISTNER/listener.log

오라클 리스너의 패스를 수정하고 저장하고 나온다.

ORACLE_HOME_LISTNER=/oracle/10g

10. 오라클을 사용하면 된다...

설치하고 디비 기동 조차 못하면 대략 낭패....
1. 우선 터미널을 열고 오라클 유저로 접속
2. 리스너 스타트
3. dbconsole 스타트
4. 호스트 인증 받기 예) oracle / pass
5. 인스턴트 시작하기 예) sys / pass

su - oracle
pass:

lsnrctl start

emctl start dbconsole

브라우저 열고 http://localhost:1158/em

출처 : http://kekedie.tistory.com/category/Database?page=4

$ sudo add-apt-repository ppa:suapapa/ubuntukofonts
$ sudo apt-get update
$ sudo apt-get install ttf-nanum


패키징 방법이 바뀌어 9.04는 더이상 지원하지 않습니다. 10.10부터는 데비안 패키지가 기본 저장소에 포함되어 있으니 따로 ppa를 사용하실 필요가 없어요.

올리면서 붉은별 리눅스의 글꼴들도 패키지 했습니다.
ttf-redstar

* 이번 프로젝트도 1년가까이 했고, 나의 "구르는돌시스템"(rollingStines System)에 따라

다른 프로젝트 냄새를 찾아 다니던중...

* 한곳에 전화해보니 "고객사가 Naver"라길해 한번 지원하고 방문하였다.

네이버는 그런지 조용하고.. 거대기업 S사에서 나와서 그런지 조용하고 차분한 분위기 였다.

찾아가기까지 "일진"이 없었던 탓인지 경기도 수원과 화성 기흥을 잘못 돌다 분당에 도착하고

분당 정자동에서도 NHN을 찾기 어려웠다는 ....

주변에 왼만한 회사라면 주변에 주섬주섬 삼삼오오 오며서 담배필 듯 한데

끽연도 S대기업의 철저한 관리에 속하는 항목에리 네이버의 분당사옥에서도 엄격히 관리되는 듯했다.

주변의 LG CNS와 비교되는듯....

* 2차 모집공고사는 둘째 치고라도 네이버에서 면접본다길래 전혀 생각 못했던

상황을 복잡하게 만들는 사연(보톤 사기극을 벌인 사람은 운을 "설명하자면 복잡합니다"이런 이야기를 한다.

* 사실은 네어버에서 발주하고 관리하는 프로젝트가 아닌 중소기업 (모 벨류)사에서 네이버에 얻쳐서

사업을 하는데 사람을 모집하는 듯했다. 한순간 네이버사옥을 방문한 그 고생스런 일들이 물거품처럼 사라지는 ..

: 어째 오기도 엇갈린길을 많이 걷게 되더라니.. 마지막까지...

부연 설명을 3차 나 4차 회사인듯 이렇게 듣고 나도 모르게 흥분되어 언성이 높아지려는 찬라...

* 감자기 담당자라고 여성중급정도 개발전담자인듯한 분이 와서 심층면접을 보자구 한다.

- 갑자기 정말로 잘못됐다는 생각이 든다.

이제 한국사회도 IT산업은 성숙기에 도달한다. 처음 IT와 컴퓨터를 접한 세대가 실버세대에 접어든다.

맥아더는 "노병은 죽지 않는다 다만 사라져 갈뿐"이라고 했듯이.

이젠 소프트웨어 기술이 학원 다녀서, 또는 책 몇권 보고 자판 두드려 봐서

나오는 기술이 아니라는걸 느껴야 할 듯한데 역시 이 땅은 아니다.

저렴한 비용으로 개발할 수 있어서 라고 하지만 저렴하게 실패하고야만 하는 이유를 아직도 깨닫지 못하는 듯하다.

소프트웨어는 자체가 논리고 엄청난 노력이 들어간 경험으로 기술이 성숙되는 것이지.

몇권의 지식으로 쌓는 지식기반이 취약해도 되는 기술이 아니다.

나도 곧 사라질 처지이지만....

PM의 프로젝트 경험으로 프로젝트는 성공이 갈릴 경우가 많다고 본다.

역시 PM은 안드로이드의 widget과 activity의 차이를 묻는 등 자신이 알고있는 지업젹인 사실을 묻는다.

결과적으로 나의 얼굴이 붉어지며 내공도 들어나지만 소프트웨어 관리자의 내공도 들어난다.

나는 아까 결심한 이 프로젝트는 성공할 수 없고, 나는 잘못된 Naver 사옥 방문이 되었음을 직감한다.

노련한 프로젝트매니저는 프로젝트의 경험이 많아. 면접시 지엽적인 기술질문으로 상대를 평가할 수 없다는 걸

잘 알기 때문이다.

인간사 프로젝트가 그렇고 특히나 소프트웨어 프로젝트는 쌓여진 법전을 외듯이

기술을 딸딸 외구고 있다고프로그래밍 ART과 산출물이 완성되는 것이 아니라는 걸

담당자 PM은 아직 프로젝트 경험이 없어서 모르고있는 것이다.

기술이야Naver에서 제공하듯이 인터넷 검색만 하면 줄줄 나온다.

레퍼런스도줄줄 나온다. 인터넷자체가 google에서 표현하려고 했듯이 10의 10승 이상바이트의 레퍼런스다.

문제는 차분하고 탄탄하기 기초 계획 및 설계와 문해결능력 그리고 구성원 간의 원활한 커뮤니케이션이다.

나는 위의 사항에 해당없지만서도, 이것이 프로젝트의 성공요인이라고 본다.

위의 문제능력으로 산출물이 나오고 영화처럼 흥행력이 있다면 사업 성공이다.

이렇게 자신을 돌아보게 만드는 네이버 NHN 사옥은 좋은 영감을 얻게된 장소가 되길 바란다.

+ Recent posts