'분류 전체보기'에 해당되는 글 304건

  1. 2008.08.19 RISC 기본원리
  2. 2008.08.18 개발자 볼것
  3. 2008.08.18 [스크랩] 면접
  4. 2008.08.18 RIA,SaaS
  5. 2008.08.18 SaaS
  6. 2008.08.12 Will Cisco Take on Google and Microsoft?
  7. 2008.08.12 Bankrupt Retailers: Pushed to the Brink
  8. 2008.08.12 AMD vs. Intel: The Challenger's New Plan 1
  9. 2008.08.05 ejb
  10. 2008.08.04 jms

RISC 기본원리

Algorithm 2008. 8. 19. 09:27

RISC의 기본 설계 개념 주에서 가장 중요한 사항은 명령어들이 한 클럭 사이클(clock cycle)에 한 개씩 실행되도록 하는 것이다. 그러나 명령어 실행은 오퍼랜드 인출, 연산, 및 결과의 저장을 모두 포함하기 때문에 사실상 한 클럭 사이클내에 실행한다는 것은 불가능하다. 따라서 이것이 가능하도록 하기 위하여 명령어 실행 과정을 파이프라이닝(pipelining)하고, 특정 명령어들 외에는 실행 과정에서 주기억장치를 액세스하지 않도록 하며, 필요한 데이터를 가능한한 프로세서 내부에서 액세스할수 있도록 레지스터의 수를 증가시키는 것과 같은 특별한 고려가 필요하다. RISC 설계에서 공통적으로 사용되고 있는 이와 같은 몇 가지 기본 원리들을 좀더 자세히 살펴보면 다음과 같다.


1) 복잡한 명령어의 제거


한 사이클 내에 명령어가 실행되도록 하기 위하여 실행 과정을 파이프라이닝 하였을 때, 파이프라인의 어느 한 단계가 특별히 긴 시간이 걸린다면 전체 클럭 사이클의 주기가 길어질 수 밖에 없을 것이다. 이러한 문제를 피하기 위하여 복잡하고 긴 시간이 걸리는 연산을 포함하는 명령어는 제거하였다. 예를 들면, 대부분의 RISC 프로세서들의 명령어 세트에는 곱셈 명령어와 나눈셈 명령어가 없다. 이들은 쉬프트(shift)와 덧셈 또는 뺄셈 명령어들을 반복 사용함으로써 대체될 수 있다. 또한 부동-소수점 연산으 산술 보조프로세서(arthmetic coprocessor)를 이용하여 처리할 수 있다.


2) 주기억장치 액세스 명령어의 제한


명령어 실행의 속도를 높이기 위하여 파이프라인 구조가 사용되었더라도 연산을 위한 데이터가 필요한 시점에서 사용 가능하지 않다면, 파이프라인의 효율이 급격히 떨어질 것이다. 그러나 기억장치를 액세스하는 동작은 다른 동작들에 비하여 시간이 오래 걸리기 때문에, 명령어 실행 도중에 데이터를 기억장치로부터 액세스하게 되면 파이프라인 동작의 흐름이 깨어질 것이다. 이러한 문제를 방지하기 위하여 RISC 프로세서에서는 데이터에 대한 연산을 수행하는 명령어의 실행에 필요한 데이터는 항상 (외부 기억장치가 아닌) 내부 레지스터에 저장되어 있도록 하고 있다. 즉, 이러한 명령어들에게는 기억 장치 액세스를 허용하지 않는 것이다.


그렇게 되는 경우에는 데이터에 대한 연산을 수행하는 명령어들은 레지스터 주소지정 방식(register addressing mode)만을 가지면 되며, CISC 프로세서의 명령어에서와 같은 여러 가지 복잡한 주소지정 방식들은 사용할 필요가 없어진다. 이에 따른 이점들은 다음과 같다.


명령어 코드가 기억장치 주소 비트들은 포함하지 않고 레지스터 번호를 나타내는 적은 수의 비트들만 가지면 되므로, 명려어 코드의 비트 수가 줄어든다. 예를 들어, 레지스터의 수가 32개라면 주소필드는 5비트이면 된다.

유효 주소(effective address)를 계산하는 데 걸리는 시간이 절약된다.


연산에 필요한 데이터가 항상 레지스터 내에 있도록 하기 위해서는 데이터가 사용되기 이전에 미리 다른 명령어에 의하여 기억장치로부터 인출되어 있어야 한다. 또한 연산이 완료된 후에 결과값도 일단 레지스터에 저장되며, 기억장치에 저장되어야 하는 시점에서 다른 명령어에 의하여 저장된다. RISC 프로세서에서는 이와 같은 기억장치 액세스 동작은 LOAD와 STORE 명령어에 의해서만 이루어지도록 하고 있다. 또한 이 명령어들은 사용되는 주소지정 방식도 간단한 것들만 사용된다.


일반적인 RISC 프로세서에서 정의된 LOAD 및 STORE 명령어의 종류를 보면 표 6과 같다. 여기서 LOAD 명령어가 부호화 여부(signed 또는 unsigned)에 따라 두 가지로 구분되어 있는 이유는 다음과 같다: 기억장치로부터 읽어오는 데이터가 프로세서의 한 단어(word) 길이인 32비트보다 짧은 byte 또는 halfword라면, 32비트 레지스터로 적재될 때 부호(+,-)를 가진 수의 경우에는 상위 비트들이 모두 부호 비트(sign bit)와 같은 값을 가지도록 한다. 즉, 부호 비트가 확장된다. 부호가 없는 수(unsigned byte 또는 halfword)의 경우에는 부호 비트의 확장이 필요하지 않다. STORE 명령어의 경우에는 명령어 실행 과정에서 부호 비트 확장 동작이 필요하지 않으므로, 명령어도 구분될 필요가 없다.

       [표 6] LOAD 및 STORE 명령어의 종류

LOAD 명령어

STORE 명령어

LOAD signed byte

LOAD unsigned byte

STORE byte

LOAD signed halfword

LOAD unsigned halfword

STORE halfword

LOAD word

STORE word



이와 같이 기억장치 액세스는 LOAD 및 STORE 명령어만에 의하여 이루어지기 때문에, 다른 명령어들은 기억장치 주소를 포함할 필요가 없다. 예를 들어, ADD, MOVE, AND와 같은 명령어들에는 레지스터 번호를 나타내는 적은 수의 비트들만 포함되어 있다.


3) 주소지정 방식의 단순화


명령어의 종류가 많아지면 연산 코드를 해독하는 회로가 복잡해지는 것과 마찬가지로, 주소지정 방식(addressign mode)의 종류가 많으면 유효 주소(effective address)를 계산하는 시간이 그만큼 더 걸리게 되고 회로도 복잡해진다. 이를 방지하기 위해서는 주소지정 방식의 종류도 최소화시킬 필요가 있다. 실제로 RISC 프로세서들은 매우 적은 수의 간단한 주소지정 방식들만 사용하고 있는데, 그 설명을 위한 예로서 다음과 같은 RISC I의 명령어 형식을 분석해 보자.

7

1

5

5

1

13

OP code

C

DEST

SRC

I

OFFSET

이 형식에서 위의 숫자들은 각 필드의 비트 수를 나타내며, OP code 필드는 명령어의 연산코드이고, C 비트는 이 명령어의 실행 결과에 조건 코드(condition code)를 세트 할 것인지의 여부를 나타낸다. 즉, C=1이면, 연산 결과에 따라 조건 코드를 세트하라는 의미이고, C=0이면 명령어 실행 결과가 조건 코드에 영향을 주지 않는다는 것이다. DEST 필드는 이 연산의 결과가 저장될 목적지 레지스터(destination register)를 지정하고, SRC 필드는 연산에 사용될 데이터의 근원지 레지스터(source register)를 지정한다. 이 필드들은 각각 5비트씩이므로, 32개의 레지스터들이 지정될 수 있다.


I 비트는 OFFSET 필드의 내용을 지정해준다. 즉, I=1이면 OFFSET 필드의 내용이 연산에 사용될 실제 데이터라는 것을 가리키고, I=0이면 OFFSET 필드의 하위 5비트가 연산의 두 번째 오퍼랜드를 가지고 있는 레지스터의 번호라는 것을 의미한다. 이에 따라 주소지정 방식은 다음과 같은 종류로 구분될 수 있다.


I=1의 경우: 즉치 주소지정방식(immediate addressing mode)

R(DEST) = R(SRC) op OFFSET

I=0의 경우: 레지스터 주소지정방식(register addressing mode)

R(DEST) = R(SRC) op R(lower 5 bit of OFFSET)


단, R(n)은 n값이 지정하는 번호의 레지스터를 나타내고, op는 OP code에 의하여 지정되는 연산(ADD, SUB, AND 등)을 의미한다. I=1일 때, OFFSET 필드가 13비트이므로 데이터가 2의 보수로 표현되는 경우에 -212 과 (212-1) 사이의 정수값을 가질 수 있다.


여기서 흥미로운 것은 레지스터 0의 내용을 하드웨어적으로 0값에 고정시키는 것이다. 따라서, 연산 오퍼랜드의 근원지가 레지스터 0인 경우에는 읽혀오는 값이 항상 0이 된다. 이것을 이용한 연산의 몇 가지 예를 보면 다음과 같다.


I = 1, SRC = 0, op = ADD : R(DEST)OFFSET

I = 0, SRC = 0, op = AND : R(DEST)0

I = 0, OFFSET = 0, op = ADD : R(DEST)R(SRC)


일반적인 명령어들에세는 위에서 설명한 두 가지 주소지정 방식들만 사용되며, 주기억장치를 액세스하는 LOAD 와 STORE 명령어에서는 이들과는 다른 주소지정 방식들이 사용된다. 먼저 LOAD 와 STORE 명령어에서 유효 주소 effective address : EA)가 계산되는 방법을 보면 다음과 같다.


EA = (RSRC) + OFFSET


여기서 만약 OFFSET = 0 이라면 EA = (RSRC)가 되어 레지스터 간접 주소지정(register indirect addressing) 방식이 되며, OFFSET 0 이면 인덱스 주소지정(index addressing)방식이 된다. 또한 만약 RSRC = R0 라면, EA = OFFSET이 되므로 직접 주소지정(direct addressing) 방식이 된다. 그런데 OFFSET 필드는 13비트이므로, 이 방식을 사용하는 명령어는 전체 주소 공간의 최하위 213 = 8192개의 기억 장소들만 주소 지정할 수 있다.


그 외에도 RISC II에서는 조건분기 명령어를 위하여 PC-relative 주소지정 방식이 추가되어 있다. 이 방식에서는 RISC I의  명령어 형식에서 DEST 필드의 비트들이 분기의 조건을 지정하는데 사용되고, SRC 필드를 포함한 하위 19비트들이 부호를 가진 변위(signed offset) 값으로 사용되어 PC 값에 더해져서 분기의  목적지 주소가 된다.


대부분의 RISC 프로세서들은 지금까지 살펴본 몇 가지 주소지정 방식들만 사용하기 때문에 명령어 형식이 고정되고, 명령어의 비트 수가 줄어들며, 유효 주소를 결정하는데 있어서 복잡한 계산이 필요하지 않도록 하고 있다.


4) 파이프 라이닝


RISC 프로세서에서 명령어들이 비록 간략화 되기는 하였으나, 사실상 명령어를 한 클럭 사이클 내에 실행하는 것은 불가능하다. 그러나 n 사이클 이내에 n 개의 명령어 실행을 완료할 수만 있다면, 평균적으로 한 사이클 당 한 개의 명령어를 실행한다고 말할 수 있을 것이다. 이를 위하여 모든 RISC들은 명령어 실행 과정을 파이프라이닝(pipelining)하고 있는데, 일반적으로 다음과 같은 파이프라인 단계(pipeling stages)들로 나누어진다: 명령어 인출 단계, 명령어 해독 단계, 실행 단계 및 기억장치 액세스 단계. 이들 중에서 명령어 인출 단계는 명령어 캐쉬가 적중(hit)된 경우에 한 사이클 내에 완료될 수 있고, 명령어의 해독과 실행도 각각 한 사이클 내에 처리될 수 있다. 그러나 기억장치를 액세스하는 LOAD와 STORE 명령어에 있어서는 실행 단계(execution stage)가 적어도 두 사이클은 소요된다. 그에 따른 문제점을 분석하기 위하여 다음과 같은 어셈블리 프로그램을 고려해보자.


LOAD R0, X ; 기억장치 X번지의 내용을 레지스터 R0로 적재하라.

ADD R1, R0 ; 레지스터 R0와 R1의 내용을 더하고, 결과를 R1에 저장하라.


이 프로그램의 처리 과정에서 두 명령어들이 순서대로 파이프라인을 통과하기 때문에 LOAD 명령어가 실행된 지 한 사이클 후에는 ADD 명령어의 실행이 시작될 것이다. 그러나 LOAD 명령어는 아직 실행이 완료되지 않았으므로 레지스터 R0에는 X의 내용이 적재되지 않았다. 따라서 ADD 명령어는 X번지의 내용이 아닌 원래 R0의 내용을 R1에 더하게 될 것이므로 잘못된 계산 결과를 산출하게 된다. 이를 방지하기 위하여 RISC 프로세서에서느 다음과 같은 두 가지 방법들이 사용된다.


하드웨어 인터라킹(H/W interlocking): LOAD/STORE 명령어가 실행된 후에 해당 레지스터에 데이터가 적재될 때까지 자동적으로 NO-OP 명령어 코드에 해당하는 지연 슬롯(delay slot)을 삽입하는 방법이다. 프로세서 속도와 기억장치 속도의 차이가 큰 경우에는 여러 개의 지연 슬롯들이 삽입될 수도 있다. 이 방법은 하드웨어로 구현된다.

프로그램 실행 순서의 재조정: 컴파일러가 어셈블리 프로그램 코드들을 조사하여 LOAD/STORE 명령어의 실행 완료 여부와 무관하게 실행될 수 있는 명령어를 LOAD/STORE 명령어의 다음에 위치시키는 방법이다. 만약 그러한 명령어를 찾지 못했을 때는 NO-OP 명령어 코드를 삽입한다.


이와 같은 파이프란인의 실제 처리 과정을 설명하기 위하여 [그림 1]과 같은 3단계 파이프라인을 고려해보자. [그림 1]에서는 10개의 명령어들이 순서대로 처리되고 있는데, 유의할 부분은 세 번째와 일곱 번째에 각각 위치하고 있는 LOAD와 STORE 명령어의 실행이다. 먼저 첫 번째 사이클에서 명령어 1이 인출된다. 두 번째 사이클에서는 명령어 2가 인출된다. 동시에 명령어 1은 실행된다. 세 번째 사이클에서는 명령어 3(LOAD임을 나타내기 위하여 L로 표시)이 인출되고, 동시에 명령어 2가 실행된다. 네 번째 사이클에서 LOAD 명령어의 실행이 시작되는데, 한 사이클 내에 완료되지 못한다. 따라서 다섯 번째 사이클에서 특별한 상황이 일어난다. 즉, LOAD 명령어 실행은 완료되지 못한 상태에서 명령어 4(<4>로 표시)가 실행된다. 만약 명령어 4가 앞의 LOAD 명령어의 목적지 레지스터를 사용하지만 않는다면 아무런 문제가 발생하지 않으며, 명령어 실행은 지연 없이 진행된다.

 

[그림 1] 파이프라인된 RISC에서의 명령어 실행 과정

 

 

                사이클

파이프라인 단계

1

2

3

4

5

6

7

8

9

10

명령어 인출

1

2

L

4

5

6

S

8

9

10

명령어 실행

 

1

2

L

<4>

5

6

S

<8>

9

기억장치 액세스

 

 

 

 

L

 

 

 

S

 

그것이 가능해지도록 하기 위하여 위에서 설명한 방법들 중에서 두 번째 방법을 사용한다면, 컴파일러가 LOAD 명령어의 다음 위치에 LOAD 명령어에 의하여 기억장치로부터 인출되는 데이터를 사용하는 명령어가 오지 않도록 순서를 조정해주면 된다. 만약 컴파일러가 그러한 명령어를 찾을 수 없을 때는 LOAD 명령어 다음에 NO-OP 명령어를 삽입해주어서 아무런 동작이 처리되지 않으면서 한 사이클을 기다리게 한다. STORE 명령어의 경우에도 비슷한 일이 발생하는데, 이때도 명령어 8이 STORE 명령어의 실행과 무관하다면 아무런 문제가 없다.


이와 같이 기억장치를 액세스하는 명령어의 다음 위치에 그와 무관한 명령어를 위치시키는 재구성 방법을 사용하면 H/W 인터라킹 방법보다 프로세서의 내부 하드웨어가 더 간단해진다. 그러나 컴퓨터시스템에 따라 프로세서의 속도와 기억장치 속도간의 차이가 더 커져서 기억장치 액세스에 여러 사이클들이 걸리는 경우에는 컴파일러가 그만큼 더 많은 명령어들을 찾아서 삽입해야 하고, 만약 그러한 명령어들을 찾을 수 없다면 그 수만큼의 NO-OP 명령어들을 삽입해야 한다. 이러한 동작은 컴파일러에게 큰 부담이 될 수 있기 때문에 MIPS에서는 이와 같은 명령어 실행 순서의 재조정을 담당하는 별도의 프로그램인 재구성기(reorganizer)를 사용한다. 재구성기는 어셈블리 프로그램을 조사하여 기억장치의 액세스의 지연에 따른 영향을 최소화시키기 위하여 명령어들의 실행 순서를 전체적으로 재조정해주는 역할을 한다.


5) 마이크로프로그램의 제거


독자들은 CISC 프로세서에서 제어 유니트가 명령어 해석과 제어 신호 발생을 위하여 마이크로프로그램을 사용하였다는 것을 기억할 것이다. 그 주요 이유는 제어 유니트 내부 회로의 복잡성을 줄이기 위한 것이었다. 그에 따라 제어 유니트의 내부에는 마이크로로그램을 저장하기 위한 제어 기억장치(control memory)가 필요하게 되었고, 그것으로부터 마이크로 코드(micro-code)를 인출해야 하기 때문에 실행 과정에 많은 시간이 걸리게 되었다. 즉, 명령어 실행 속도가 저하되는 주요 원인이 된 것이다.


RISC 프로세서에서는 모든 명령어 실행 과정이 하드웨어만에 의하여 이루어진다. 다시 표현하면, 명령어를 해석하고 제어 신호를 발생하는 과정에서 마이크로프로그램이 개입되지 않는다는 것이다. 명령어 인출 유니트(instruction fetch unit)에 의해 인출된 명령어 코드의 비트들이 그 명령어 실행에 필요한 제어 신호들을 발생시키는 데 직접 사용된다. 이것은 실제 RISC 프로세서의 고속화에 큰 기여를 하였다. 

 

'Algorithm' 카테고리의 다른 글

병행프로세스와 상호배제  (0) 2008.08.19
주소지정방식  (0) 2008.08.19
deadlock 4가지  (0) 2008.08.01
B-, B+ Tree  (0) 2008.08.01
CPU scheduling  (0) 2008.08.01
Posted by 으랏차
,

개발자 볼것

IT 2008. 8. 18. 13:42

원문 : http://www.zdnet.co.kr/itbiz/column/anchor/hsryu/0,39030308,39166851,00.htm


류한석 (IT 컬럼니스트) ( ZDNet Korea )   2008/03/17


[지디넷코리아]개발자 K씨를 재회한 것은 8년만의 일이다. 그는 나와 함께 일했던 직장에서 이직한 이후에 4번이나 더 이직을 했는데, 현재는 실직 상태에서 직장을 구하고 있었다.

솔루션을 개발하는 회사에서는 비전이 없어 그만 두었고, 대기업 계열 SI업체를 들어갔으나 개발이 아닌 관리를 시켜서 그만두었고, 포털에 들어갔는데 할 일이 별로 없고 회사 상황이 정치적이어서 그만두었다고 했다. 그리고 마지막 회사는 소위 벤처기업이었는데, 6개월이나 임금을 받지 못한 상태에서 사장이 사실상 야반도주를 해서 회사가 망했다고 했다.

K씨는 자바를 정말 잘 다루던 개발자였는데, 일반적인 기준에서 볼 때 성격이 좋다고 얘기하기는 힘든 사람이었지만 그 정도면 무난하다고 할 수 있었다. 다만 여느 개발자와 마찬가지로, 타인의 욕구에 관심을 가지거나 커뮤니케이션 스킬이 뛰어난 사람은 아니었다. 다음은 그가 한 얘기이다.

“회사 경영은 나하고 상관이 없다고 생각했어요. 제가 경영이나 관리 같은 것은 잘 모르고요. 회사에서 벌어지는 정치 게임은 질색이에요. 저는 그저 개발만 하고 싶었어요. 그런데 개발에 집중할 수 있는 조직이 참 없더라고요. 이제 저는 어떻게 해야 할까요?”

필자는 그날 K씨와 새벽까지 술을 마실 수 밖에 없었다. 개발자가 개발자답게 일하고 성장할 수 없는 것이 바로 한국의 현실이다. 성장을 하는 것이 아니라 사라져 가고 있다.

개발자는 어떤 사람인가? 문제를 발견하고 문제를 해결하고, 스펙에 따라(또는 창조적으로) 무언가를 만들어 내고, 오랜 시간 동안 한 자리에 앉아서 화면만을 째려보며 몰입할 수 있기에 개발자다. 그것이 그들의 특징이며 그렇기 때문에 개발을 할 수 있는 것이다.

개발자에 대해 IT업계의 다른 직종들은 어떻게 생각하고 있을까? 단편적이지만 그들의 생각을 살펴보자. 어떤 영업맨은 “저한테 저렇게 열 시간 동안 앉아 있으라고 하면 절대 그러지 못할 거 같네요. 어떻게 저럴 수 있나요?”라고 필자에게 반문하기도 했다.

어떤 마케터는 “그들은 쿠폰에 항상 도장을 찍더군요. 작은 것에 민감한 거 같아요. 시야가 좁고 자신들의 분야 외에는 거의 관심이 없는 거 같더군요. 게임이나 애니, 미드 같은 것을 좋아하고. 업계나 시장 돌아가는 상황에 대해서는 관심도 없고...”라고 얘기했다. 실제로 마케터들은 개발자와 함께 일하는 경우가 별로 없어서 그들을 잘 모른다. 원거리에서 그들을 바라볼 뿐이다.

반면에 개발자와 함께 협업하는 경우가 많은 요구분석가, 웹기획자들 중 상당수는 다음과 같은 얘기를 했다. “그들은 커뮤니케이션 스킬이 없어요. 중요한 대화에는 제대로 응하지 않다가 자신들과 상관이 있는 이슈가 나오면 발끈해요. 무조건 안 된다고만 하죠. 도무지 협상이라고는 할 줄 모르는 사람들이에요.”

혼자서 일하는 1인 개발자가 아닌 이상, 대부분의 개발자는 조직에서 협업을 해야 한다. 프로젝트 매니저와 대화해야 하고, 기획자/디자이너/동료 개발자와 협업을 해야 한다. 프로젝트에 따라서는 고객과 직접적인 커뮤니케이션을 해야 하는 경우도 있다. 그리고 사내정치를 피해갈 수 있는 개발자는 거의 없다. 직간접적으로 영향을 받을 수 밖에 없다.

그런데 한국에서 사내정치는 중소기업에서 대기업, 인터넷기업까지 만연되어 있다. 많은 개발자들이 정치를 싫어한다. 정확히 표현하면 정치가 미치는 부정적인 영향을 싫어한다고 할 수 있을 것이다. 하지만 조직이라는 것은 그 안에 있는 수많은 조직구성원들이 지위 고하에 따라 자신의 목표와 이익을 추구하는 곳이다. 그리고 그들간의 이해관계는 상충될 수 밖에 없다. 그래서 누군가는 희생자가 된다. 안타깝게도 그 대상은 대부분 개발자이다.

개발자는 현실적인 일정 하에서 보다 나은 기술을 이용하여 높은 품질의 소프트웨어를 만들고 싶어하지만, 어떤 사람들은 기술 자체나 품질은 전혀 상관없이 일자 또는 비용만이 그들의 관심사이다. 그렇다면 그것은 잘못된 것인가? 그럴 수도 있고 아닐 수도 있다. 상황에 따라 답이 다르다. 현실은 단순한 흑백논리로 설명되지는 않는다.

패배하지 않기 위해 이것만은 기억하자

사내정치에서 살아남기 위해서 개발자가 알고 있으면 유용할 세 가지 지침을 제시한다. 다음의 세가지 지침은 서로 연동된다.

1. 나의 목표와 주변의 이해관계를 파악하고 있어야 한다.
자신이 원하는 것이 돈인지 명예인지 지위인지, 아니면 개발을 통한 자아실현인지, 개인생활의 추구인지 명확히 알고 있어야 한다. 또한 나의 목표를 실현하는데 있어 가장 큰 장애물이 무엇인지 알고서 그것을 관리해야 한다. 자신의 목표와 상충되는 목표를 가진 이해관계자의 욕구를 파악하고 그것과의 타협점을 찾아야 한다.

하지만 사실, 대부분의 경우 목표를 실현하는데 있어서 가장 큰 장애물은 자기자신의 성격이다. 그렇지만 성격을 수양하는 개발자가 과연 몇 %나 될까? 아는 것과 실천은 완전히 별개의 단계이다.

2. “너와 나의 진실은 다르다”는 사실을 이해하고 있어야 한다.
자신이 믿는 것만이 정의이고 진실이라는 생각이 들 때, 숨을 세 번 크게 내쉬면서 상대편의 입장에서도 과연 그럴까 다시 한번 생각해 보기 바란다. 내가 알거나 느끼는 것을 쉽게 드러내서는 곤란하다. 대부분의 경우 그것은 설익은 판단이고 타이밍이 적절치 않은 경우가 많다. 하지만 자신의 감정을 주체하지 못하고 ‘욱’한 나머지, 준비도 안된 상태에서 회사를 그만 두어 버리고 경력을 망치는 개발자들이 많다. 퇴사 후 놀고 있는 당신을 사내정치인들은 비웃고 있다.

3. “군자에게는 실수를 해도 소인배에게는 실수를 하지 말라”는 격언을 기억하기 바란다.
이 말은 필자가 회사 생활에서 곤란을 겪는 후배들에게 숱하게 해주었던 말이다. 이 말을 처음 들었을 때의 임팩트는 상당히 크다. 군자(君子)는 점잖고 덕이 있는 사람이다. 그래서 군자는 누가 실수를 해도 그 이유를 스스로 파악하여 너그럽게 이해해준다. 하지만 소인배는 조금만 불이익을 당하거나 무시를 당했다고 느끼면 바로 삐지며, 심할 경우 끝까지 따라다니며 괴롭힌다.

그런데 사람이란 군자에게는 존경심을 갖고서 공손히 대하고 소인배는 무시한 나머지 함부로 대한다. 그것이 인지상정이다. 하지만 만일 그 소인배가 당신의 직장상사라면?

사내정치는 어느 나라에나 존재한다. 한국뿐만 아니라 미국에도 일본에도 있다. 하지만 한국에서 더욱 사내정치가 심할 수 밖에 없는 이유가 있다. 한국은 아직까지 IT업계뿐만 아니라 사회의 여러 분야에서 전문가의 개념이 불분명한 나라이다. 제대로 된 전문가가 출현하고 그 가치를 인정받는 지식사회가 되기까지 앞으로도 꽤 많은 시간이 걸릴 것이다.

한국은 아직은 선진 지식사회가 아니다. 그러므로 고급지식을 가진 사람들이 별로 없을 뿐만 아니라, 설사 있다고 하더라도 그것을 인정하지 못하며, 설사 인정한다고 할 지라도 필요로 하지 않는다. 실력을 인정하는 기준이 없으니, 사내정치가 판을 친다.

전문가를 필요로 하지 않는 사회, 자기계발이 살길
궤변으로 들릴 지 모르지만, 우리 업계에 전문가가 없는 것은 전문가를 필요로 하지 않기 때문이다. 조직 내에 사내정치인이 승진하고 인정받는 것은 조직의 상층부가 몰라서 그런 것이 아니라 그런 사람을 선호하기 때문이다.

성장은 커녕 생존을 이야기해야 하는 현실이 안타깝지만, 일단 생존해야 자기계발을 하고 경력관리를 하면서 기회를 노릴 것이 아닌가? 사내정치를 잘 할 필요는 없지만(그리고 개발자의 특성상 잘 하지도 못 할 것이다), 희생자가 되어서는 곤란하다. 이것이 바로 필자가 개발자 K씨에게 한 말이다.

개발자는 자신의 개발력과 장점을 해치지 않는 선에서, 이해관계자를 파악하고 그들의 욕구를 다루는 능력을 갖추어야 한다. 자신의 목표를 분명히 해야 하며, 감정에 치우쳐서 일을 그르치지 말아야 한다. 그러지 못한다면 결국 희생자가 될 뿐이다.

그러한 희생을 몇 번 당하다 보면, 개발업에 대한 애정이 식어버려 자기계발을 등한시하게 될 뿐만 아니라 성격까지 나빠져서 더욱 더 안 좋은 상태에 처하게 된다. 그렇게 사라져간 개발자들이 참 많다.

이런 조언을 하는 것에 대해 한편으로는 미안하게 생각한다. 언젠가 개발력만으로도 인정받을 수 있는 사회가 오면(너무 낭만적인 표현이다), 사내정치 대신 좀 더 아름다운 세상에 대해 이야기하겠지만 지금은 아니다. 정신을 똑바로 차리고 이 난세에서 생존하기 바란다. 환경을 바꿀 수 없으면 자신을 바꾸어야 하며, 자신을 진화시킨 개발자에게는 반드시 기회가 올 것이다. 세상은 장기적으로 볼 때 스스로 혁신하는 사람의 편이니까 말이다. @

'IT' 카테고리의 다른 글

S/W System 개발  (0) 2008.08.21
Optimize PC  (0) 2008.08.20
[스크랩] 면접  (0) 2008.08.18
RIA,SaaS  (0) 2008.08.18
SaaS  (0) 2008.08.18
Posted by 으랏차
,

[스크랩] 면접

IT 2008. 8. 18. 13:35

원문 : http://kldp.org/node/96950


안녕하세요, kldp 가입한지 얼마 안되었지만 좋은 글 들 읽으며 여러 도움을 얻고 있는 개발자 입니다.

어떤 게시물을 읽다가 정보 교환의 차원에서 제가 최근 받았던 인터뷰 질문들을 정리해 보면 재밌겠다는 생각이 들었습니다.

참고로 이 인터뷰 문제들은 중소 기업들 문제로서, intermediate나 senior 레벨 정도의 수준입니다. 제가 비전산 출신 독학에다가 석사 학위도 없는 근본 없는 사람인 데다가, 나이도 조금 되서 개발자로서는 퇴물에 속해서 여러 면에서 서류심사에서 통과하지 못한 경우가 대부분 이었습니다.

제가 이것 저것 관심이 많아서 경력이 없는 분야에도 지원을 많이 했습니다만, 대부분 운 좋게도 면접관들이 제가 선택한 언어에 대해 질문들을 해 주었습니다. 지원 했던 회사들이 요구하는 언어들 중 제가 주로 사용하는 C++/C# 이 아닌 PHP, ActionScript, JavaScript 등의 언어에 대해서는 제 스스로가 프로젝트 수행을 위해 언어 습득을 해가면서 단기간에 진행 한 것들이라서 랭귀지 자체나 관련 환경에 대한 심화된 문제보다는 주로 portfolio에 대한 질문으로 이어졌고, 그 외에 기억 나는 몇가지를 추려보겠습니다. 수준은 그리 높지 않은 것들이었습니다.

 

문제에는 몇가지 예(코드)들이 주어진 경우가 있는데, 그 실재 코드들은 생략합니다. 실제 정확한 질문내용이 중요하겠지만, 우선은 대충의 질문 내용을 정리해 보는것도 좋지 않을까 생각해 봅니다.

참고로 상당히 많은 면접을 보았지만, 많이 퇴짜를 받았습니다. 전화상 외국어 통화에 문제가 있던 적도 있고 하지만, 뭐 변명은 하지 않겠습니다. 모든 것이 제 실력의 결과니까....... 어흑..

 

1. 프로그래밍 언어에 대한 문제는 대부분 지원자의 언어 사용상의 바른 지식과 실제 습관을 파악하는 수준이었던 것 같습니다.

 

a) C++. 완전 가상함수는 무엇이고 왜 필요한가?
b) 일반. 뮤텍스와 세마포어의 차이는 무엇인가? 이경우 무엇을 사용해야 하는가?
c) 일반. 데드락이 생성되는 조건과 그 해결 방법은 무엇인가?
d) 언어 무관 (주로 c++또는 Java/C#) 이 두 base class가 있다. 두 클래스로부터 파생된 클래스가 있는데, 이 코드들은 드라이버에 사용되는 것들로서 파생된 클래스는 최소한의 사이즈를 가져야 한다. 두 base class의 일부 기능만을 가진 이 자식클래스에서 실제로 사용되지 않는 멤버는 완전히 제거하려면 어떻게 해야 하는가?
e) STL: iterator란 무엇인가? STL의 ???? 라는 것의 역할은 무엇인가?
f) C# : 이 B query 결과에는 가변적(수,타입)인 테이블들이 존재한다. 이 다양한 결과 처리를 하는 부분을 최소한의 코드로 처리해 본다면 --> reflection으로 구현했습니다. 이에 대해 추가 질문 --> 수행속도에 있어 어떻다고 생각하는가> --> 아뿔싸..

 

2. 알고리즘 또는 riddle.


인터넷에 떠도는 google이나 여러 다른 회사의 색다른 질문은 못받아 본 것 같네요..

g) 당신은 가이드고, 6x6 의 이 지역 left top에서 ,right bottom으로 관광객들을 최소한의 비용을 들여 이동시켜야 한다. 각 cell 사이에는 이동 비용이 서로 다르며, 한 번 visit한 cell로는 돌아갈 수 없다. 어떻게 할 것인지 pesudo code로 기술해 보라.
h) 이미 정렬된 배열이 있고, 그 내용은 알 수 없다. 최소한의 스텝으로 원하는 데이터를 찾는 방법은?

 

3. Networking

 

제가 최근 면접 본 회사들의 경우는 네트워크 깊숙한 레벨까지 들어가는 회사는 없었던 것 같습니다.

i) 패킷이 생성되서 전달되기 까지의 과정을 최대한 상세하게 설명하라.
j) 이 경우는 무슨 명령어로 상태를 확인하는가? 이 경우는? 그럼 이 경우는 무엇이 문제일까?
k) OSI 7layer 를 설명 --> 아... hotdog 인가 뭔가 까먹었다.
m) 당신이라면 최소한의 돈으로 어떻게 웹서버의 로드분산을 수행하겠는가?


4. Portfolio 검증

 

n) 당신이 관리하는, 또는 멤버인 open source 프로젝트가 있는가? -> sourceforge 로긴 -> 그럼 이 파일에서 당신의 코드는 무엇인가, 이 함수의 역할은 무엇인가?

 

5. 일반 이력 검증


o) 당신의 이전 회사 A의 경우 B제품에서의 역할과, 실재 수행했던 내용을 설명
p) 당신의 이력 중 가장 힘들었던 일은 무엇이었고 어떻게 해결했는가?

 

6. 협업 및 management skill

 

r) 사용 경험이 있는 config. man. tool 은?
s) 당신이 경험한 개발 방법론은? 실 례 는? 방법론의 차이점은?
t) 이러한 경우 당신은 어떻게 이 실타래를 풀어나갈 것인가?

 

7. 인성확인


u) 당신은 개발자인가 매니저인가?
v) 당신의 5년후 이회사에서의 모습은 무엇인가 ? --> 이게 정말 tricky한 질문입니다.
w) 다른 지원자 대신에 당신을 뽑아야 할 이유는 무엇인가?

참고로 회사마다 role 마다 중점을 두고 물어보는 내용이 서로 다르기 때문에 위 리스트는 여러 회사의 질문 내용이 포함되어 있습니다. 질문 자체를 이해 못한 것도 있고 해서 빠진 내용도 있는 것 같네요..
면접 전에 미리 interview question들을 찾아 읽어 보는것을 최근에야 시작했습니다...

너무 생각 없이 구직을 했던 일인...

 

추가로 Design pattern에 대한 질문도 있었군요. 그리고 몇가지 더..

 

a. 자주 사용하는 design pattern은?
b. 이 패턴을 사용하는 경우는? 이 패턴과의 차이는?
c. C++로 Garbage collector을 구현한다면?
d. Ajax 경험이 있는가?
e. MVC에 대해 설명. ASP.NET와 Flex의 경우 각각 M/V/C에 해당 하는 부분은?
f. 사용 경험이 있는 debugging 툴은?

 

원글에보니 오래전에 제가 interviewer로서 질문한 내용이 끼어있는것 같습니다. 그래서 약간 과장된 것으로 비쳐질 지 모르겠지만, 이런 류의 질문내용이 나올 수 있다는 것 정도로 정보 공유가 되는 계기가 되었다면 그것으로 좋겠습니다.

'IT' 카테고리의 다른 글

Optimize PC  (0) 2008.08.20
개발자 볼것  (0) 2008.08.18
RIA,SaaS  (0) 2008.08.18
SaaS  (0) 2008.08.18
Will Cisco Take on Google and Microsoft?  (0) 2008.08.12
Posted by 으랏차
,

RIA,SaaS

IT 2008. 8. 18. 13:22

원문 : http://www.zdnet.co.kr/itbiz/column/anchor/hsryu/0,39030308,39150831,00.htm


류한석(피플웨어 운영자)   2006/09/05

 

[지디넷코리아]새로운 IT 시대를 여는 분위기 메이커로서 웹 2.0이 중요한 역할을 톡톡히 수행하고 있다. 그에 따라 웹 2.0과 관련된 UCC, 롱테일 등의 개념, 그리고 Ajax와 같은 웹 기술에 대해 여러 곳에서 언급이 되고 있다.

하지만 웹 2.0에 대한 소개가 빈번하게 다루어지는 반면(사실 많은 사람들이 벌써 웹 2.0이라는 말에 피로감을 느끼고 있다), 필자가 블로그에서 밝힌 IT 혁신의 징후라는 관점에서의 전반적인 통찰은 거의 이루어지지 않고 있는 듯 하다.

새로운 IT 시대를 이끌어갈 주요 요소
그간 IT의 역사는 메인 프레임, 클라이언트/서버, 웹(또는 인터넷)의 시대를 거쳐왔다. 그러한 발전의 과정에서 지속적으로 시대의 변화를 이끌어 온 몇 가지 중요하고도 공통된 요소들을 정리해보고자 한다. 아래의 요소들은 새로운 시대를 이끄는 데 있어서도 중요한 요소로 작용할 것이다. 여러 요소들이 통합되어 미래를 만들어낸다.

첫째, 비즈니스 모델의 혁신이다. 소프트웨어 비즈니스 모델은 지금 상당한 변화를 맞이하고 있다. 소비자가 소프트웨어를 직접 소유하고 설치하여 사용하는 기존의 방식에서 SaaS(Software as a Service)로의 변화가 바로 그것이다. SaaS에 대한 내용이 궁금하다면 필자의 SaaS 아티클을 참고하기 바란다.

둘째, 커뮤니케이션 아키텍처의 혁신이다. 네트웍 인프라는 그 동안 다이얼업으로부터 브로드밴드, 와이파이로 발전해왔다. 그리고 최근에는 와이브로, HSDPA 등으로의 혁신을 눈앞에 두고 있다.

과거에 보면, 인프라가 진화할 경우 그 위에서 구동되는 킬러 소프트웨어와 새로운 컨텐츠 비즈니스 모델이 나왔으며, 그것들은 서로서로 협업하며 새로운 시대를 이끌었다. 앞으로도 마찬가지일 것이다.

커뮤니케이션 기술에 있어서 클라이언트/서버 시대에는 TCP/IP가 확산되었고 웹의 시대에 는 HTTP가 확산되었다. 앞으로는 HTTP와 XML을 기반으로 한, 웹 서비스(Web Services)가 주요한 역할을 수행하게 될 것이다. 여러 커뮤니케이션 기술들이 효과적으로 통합되는 것이 추세이다.

‘프로그래밍이 가능한 인터페이스로서의 웹 서비스’는 웹 기반 서비스라는 ‘폭넓은 의미의 웹 서비스’라는 용어와 쉽게 혼동이 되고는 한다. 필자가 의미하는 웹 서비스는 ‘프로그래밍이 가능한 인터페이스로서의 웹 서비스’이다. 웹 서비스는 SOA(Service-Oriented Architecture)를 구현하기 위한 중요한 기술 요소이기도 하다.

웹 서비스는 몇 년 전 처음으로 선보인 이후로 지금까지 보급이 지지부진하였지만 웹 2.0 개념의 등장과 함께 프로그래밍이 가능한 웹, 그리고 매쉬업의 인기가 폭증하면서 새롭게 주목을 받고 있다.

솔직히 그 동안 엔터프라이즈 시장에서 웹 서비스는 폭발적인 힘을 발휘하지 못했다. 과거의 익숙한 방식으로 대체할 수 있다면, 새로운 방식은 주목 받지 못한다. 그러한 이유로 엔터프라이즈 시장에서는 웹 서비스의 확산이 지지부진했던 것이다. 웹 서비스가 확산될 만한 비즈니스 모델과 기술적 당위성이 부족했다.

하지만 웹 2.0은 많은 것을 바꾸어 놓았다. 그리고 그것은 결국 엔터프라이즈 쪽에도 영향을 미치게 될 것으로 예상된다. 앞서 설명한 SaaS와 같은 비즈니스 모델의 혁신, 그리고 다음에 설명한 UI의 혁신과 결합함으로써 웹 서비스는 비로소 제대로 작용하게 될 것이다.

셋째, 사용자 인터페이스(UI, User Interface)의 혁신이다. 마지막으로 살펴 볼 UI의 혁신은 필자가 가장 중요하게 생각하는 요소이다. 사용자의 입장에서 가장 임팩트있게 다가오는 것이 바로 UI의 혁신인 것이다. 이 점은 필자가 지속적으로 강조하고 있다.

이러한 UI의 혁신을 OS의 관점에서 살펴보면 MS의 윈도우 비스타, 애플의 타이거 그리고 차기 버전인 레오파드, 리눅스의 XGL 등 거의 모든 데스크톱 OS에서 UI의 상당한 혁신이 이루어지고 있다.

또한 개발 환경의 관점에서 살펴보면 .NET 프레임워크 3.0의 일부인 WPF(Windows Presentation Foundation), 그리고 WPF의 부분집합으로서 크로스 플랫폼과 크로스 브라우저를 지원하는 WPF/E(Windows Presentation Foundation/Everywhere)가 있다. MS는 애플리케이션의 그래픽/UI 디자인을 위해 새롭게 익스프레션(Expression!) 제품 군을 선보이고 있기도 하다.

그리고 RIA(Rich Internet Application) 개발 도구인 어도비(구 매크로미디어)의 플렉스(Flex)가 있다. 플렉스는 얼마 전 2.0 버전이 출시되었는데 기능과 성능이 많이 개선되었다. 또한 오픈소스 기반인 오픈라즐로(OpenLaszlo)도 RIA 개발 솔루션으로서 상당한 주목을 받고 있다. 아주 매력적인 기술들이다. Ajax는 이러한 RIA 도구들에 비하면 소품이라고 할 수 있다. 물론 Ajax는 나름의 장점을 갖고 있는데 워낙 유명한 기술 세트이므로 여기에서 일일이 언급하지는 않겠다.

RIA는 웹에서 데스크톱과 흡사한 UI 환경을 제공하기 위한 노력의 결과물이며 계속 진화하고 있다. 웹 2.0에서 중요한 UI 기술로 부각되고 있으며 활용이 점차 증가하고 있다. 그간의 IT 역사를 보면 UI가 바뀌면 많은 것들이 바뀌었다. 필자는 이번에도 예외가 아닐 것으로 확신하고 있다.

빅 임팩트를 위하여
위에 소개한 요소들은 개별적으로 다루어지기 보다는, IT 시대의 혁신이라는 하나의 목표를 향해 한 지점으로 모이고 있는 것으로 이해되어야 한다. 이러한 개념들은 한 지점에서 폭발할 것이며, 얼마 지나지 않아 일반적으로 받아들여지고 금방 일상화될 것이다.

그 때의 시작이 바로 내년이다!

새로운 시대를 앞두고 전하고 싶은 내용들이 참 많다. 필자가 커뮤니티 사람들과 함께 개설한 스마트플레이스 블로그를 통해 국내외의 새로운 웹 2.0 사이트들에 대한 리뷰와 신기술에 대한 정보를 지속적으로 공유하고자 한다. 아직은 그리 내용이 많지 않지만 계속 업데이트가 될 것이다.

끝으로 해외에 비해 활력이 많이 떨어지는 국내 소프트웨어 업계의 분발을 바라는 마음으로 에리히 프롬의 명구를 소개하며 글을 마친다. @

“활력은 비전의 산물이다. 위대하고, 아름답고, 중요한 그 어떤 것에 대해 비전이 없다면 활력은 감소되고 인간의 생명력은 약해진다. - 에리히 프롬”

'IT' 카테고리의 다른 글

개발자 볼것  (0) 2008.08.18
[스크랩] 면접  (0) 2008.08.18
SaaS  (0) 2008.08.18
Will Cisco Take on Google and Microsoft?  (0) 2008.08.12
AMD vs. Intel: The Challenger's New Plan  (1) 2008.08.12
Posted by 으랏차
,

SaaS

IT 2008. 8. 18. 13:20
2008/08/12 - [Company_Temp] - ASP의 진화 개념으로서 SaaS(Software as a Service)

ASP모형의 특징
사용자 삽입 이미지
ASP모형의 진화
사용자 삽입 이미지
ASP/SaaS발전 단계
사용자 삽입 이미지
소프트웨어 유통 패러다임 발전 단계
사용자 삽입 이미지

'IT' 카테고리의 다른 글

개발자 볼것  (0) 2008.08.18
[스크랩] 면접  (0) 2008.08.18
RIA,SaaS  (0) 2008.08.18
Will Cisco Take on Google and Microsoft?  (0) 2008.08.12
AMD vs. Intel: The Challenger's New Plan  (1) 2008.08.12
Posted by 으랏차
,

Will Cisco Take on Google and Microsoft?

Cisco needs to redefine itself as a software company. But Google and Microsoft are way ahead in the "collaboration" game

Cisco Systems (CSCO) reported its fiscal fourth-quarter 2008 financials last week, but while the San Jose (Calif.)-based networking giant beat Wall Street estimates, thanks to the hurdle posed by the law of large numbers, it forecast more modest growth going forward. "The market is clearly in transition, and we will use this time as an opportunity to expand our share of customer spend and to aggressively move into market adjacencies," CEO John Chambers said in a statement.

The question is, which adjacent markets is Chambers eyeing? After all, to move the needle, Cisco needs to find untapped markets it can serve. Such a challenge comes at a particularly difficult time: The telecom market has consolidated in the hands of a few carriers, new opportunities are few and far between, and the overall trend is toward hardware becoming a service.

Where the Growth Is

Therein lies Cisco's solution: It needs to start thinking like a software company, one that assumes that "the network is the corporation." If it does that, it will see that one of the biggest potential areas for growth lies with the (seemingly boring) infrastructure found in data centers, since, as a reader points out, the growing popularity of cloud computing means corporate data centers will increasingly start to look like Internet data centers.

Cisco has already recognized that as the "network" continues to become the focal point around which our digital personal and professional lives revolve, the opportunity to make money will be immense. That's why Chambers never misses an opportunity to talk about "collaboration."

For instance, in the press release announcing the company's latest numbers, he said: "We believe we are entering the next phase of the Internet as growth and productivity will center on collaboration enabled by networked Web 2.0 technologies." But Cisco isn't the only one with this vision—Microsoft (MSFT) and Google (GOOG) are thinking along these lines as well and are much further ahead in the game.

In a recent interview (BusinessWeek.com, 8/2/08), Manesh Patel, chief information officer of Sanmina-SC, an electronics manufacturer with $10.7 billion in sales, said: "We have project teams working on a global basis and to help them collaborate effectively, we use Google Apps."

Collaboration Means Competition

Meanwhile, while many have focused their attention on Microsoft's suite of "office apps," the company's online collaboration strategy is actually centered around SharePoint, which offers a way for companies to share information and services and is well on its way to becoming a billion-dollar business. "The spectacular growth of SharePoint is the result of the great combination of collaboration and information management capabilities it delivers," Microsoft co-founder Bill Gates said back in March. "I believe that the success we've seen so far is just the beginning for SharePoint."

We've been saying for a while now that Microsoft and Cisco were going to butt heads. As GigaOM writer Allan Leinwand pondered last fall: "So could a Cisco social-networking platform aimed at the enterprise market enable messaging, interaction, and collaboration and, by extension, be a wedge against Microsoft and their current lock on the enterprise IT messaging market?"

To fulfill Chambers' desire to be a collaboration king, Cisco has to compete with these guys aggressively. The problem is that it doesn't have either the software or the Web DNA of its fellow tech behemoths. It has made some hesitant steps. It bought WebEx to move deeper into Web conferencing, nabbed Tribe.net to get a closer look at social networking, and acquired Five Across to beef up its content management skills.

Ideally, Cisco would develop a suite of applications that pivot around WebEx, which it could do by offering to work with all comers, big and small. Acting as a neutral player that delivers best-of-breed Web services would give Cisco that best shot at effectively competing with Google-only and Microsoft-only solutions. At this stage, however, it just might not be enough.

Provided by GigaOm

'IT' 카테고리의 다른 글

개발자 볼것  (0) 2008.08.18
[스크랩] 면접  (0) 2008.08.18
RIA,SaaS  (0) 2008.08.18
SaaS  (0) 2008.08.18
AMD vs. Intel: The Challenger's New Plan  (1) 2008.08.12
Posted by 으랏차
,

Bankrupt Retailers: Pushed to the Brink

Changes in the law have sharply reduced retailers' ability to reorganize, driving many to liquidate quickly

http://images.businessweek.com/story/08/600/0810_sharper_image.jpg

People walk by a Sharper Image store Feb. 20 in San Francisco. Justin Sullivan/Getty Images

On Feb. 19 the electronic gizmo retailer Sharper Image filed for Chapter 11 bankruptcy protection. As part of its court filing, Sharper Image management committed to its lenders to close 90 of its 184 stores. But within weeks, newly appointed Chief Executive Robert Conway decided to liquidate the rest of the stores. Conway, who is a principal at turnaround specialist Conway, Del Genio, Greiss & Co., reached the conclusion that it would be nearly impossible to secure adequate financing to restock the remaining 94 stores. "We didn't want to delay the process to a point where there would be no value left, and we decided liquidation was the best option. The last few stores closed at the end of July," says Conway.

But the final nail in the coffin for Sharper Image came three years earlier, when U.S. bankruptcy law was revised to add cash payments to utilities and other suppliers, and place a 210-day cap on the amount of time bankrupt companies have to decide whether to keep a lease.

No Time to Reorganize

The rapid dissolution of Sharper Image took many in the bankruptcy industry by surprise. But that chain isn't alone. Several retailers that have filed for Chapter 11 protection (BusinessWeek.com, 7/21/08) since the economy started swooning have unraveled just as quickly: Wickes Furniture closed down its 36 stores. Friedman's is in the process of selling off jewelry and is closing its 377 stores, while Whitehall Jewelers is liquidating its 300 stores. All these companies filed for bankruptcy reorganization in 2008. And in December 2007, Bombay Co. and Levitz closed all their stores.

The new provisions in the bankruptcy law—pushed primarily by mall owners, suppliers, and utility companies, and signed by President George W. Bush in 2005—were intended to shorten the time that a company stays under court supervision. The point was to protect creditors, who sometimes had to wait years for payments while lawyers racked up hefty fees and managers collected big pay packages. "There was a pattern in some bankruptcy courts of granting extensions for as long as the debtor wanted, and that had to be stopped," says Lynn LoPucki, a professor at the University of California at Los Angeles School of Law and author of Courting Failure: How Competition for Big Cases Is Corrupting the Bankruptcy Courts.

Not everyone thinks sick companies should be given a second, or sometimes third, lease on life. Already many retail experts believe that chains overexpanded during the flush consumer spending of the past decade, and that parts of America now have more stores than people to shop at them. Given the track record of some big retailers, even if the latest bunch of troubled companies were to emerge from bankruptcy, they might wind up right back in court a few years later. Bradlees, Tower Records, and FAO Schwarz are among those that have filed multiple bankruptcies in recent years.

Strong Headwinds

All filers are covered by the new bankruptcy law, but the changes were particularly harsh on retailers. For companies that already are short of cash—and, in the current environment, unlikely to find new financing—these new provisions in the law can amount to a death sentence. "Liquidity is sucked out of the debtor in a way that it becomes hard to survive," says Lawrence Gottlieb, chair of the bankruptcy and restructuring practice at New York law firm Cooley Godward Kronish, who has represented creditors' committees in the bankruptcies of Sharper Image and Linens 'n Things.

Retailers already face strong headwinds. Consumers' appetite for discretionary purchases has dwindled sharply, and credit conditions are tight. That has led to shrinking sales month after month at most retailers and a string of store closings. Foot Locker (FL) is closing 140 stores; Wilson's Leather is closing 160; Ann Taylor (ANN), 117; and jeweler Zales (ZLC) has closed 105.

For some chains, times are even more desperate, and the drumbeat of retail bankruptcies grows louder by the day. So far this year, 15 retailers with assets of $100 million or more have filed for Chapter 11 bankruptcy, up from seven for all of 2007, according to Bankruptcydata.com, which tracks such filings. On Aug. 4, Boscov's, a department-store chain with 49 stores in the Northeast, filed for Chapter 11, just a week after the 177-store Mervyn's chain in California filed for protection from creditors.

Retail experts wonder what fate awaits these and other retailers such as Goody's Family Clothing and Linens 'n Things, which are already in Chapter 11. According to research tracked by LoPucki, in the 20 years prior to 2005, 41% of the 94 retailers that filed for bankruptcy went out of business. Those that emerged included such well-known outfits as Kmart (SHLD), Winn-Dixie Stores, and Macy's (M).

This time around, almost all of the retailers that filed in the first three months of 2008 dissolved quickly. The others face tough choices. Executives from Goody's and Boscov's didn't return calls to discuss their options.

Credit conditions are so tight that lenders are not willing to provide any wiggle room to distressed retailers. Talbots (TLB), for instance, isn't among the litany of bankrupt retailers. But earlier this year the upscale clothing chain had to extend its payment terms with its vendors to 45 days from 22 days, after Bank of America (BAC) and HSBC (HBC) refused to renew lines of credit worth $130 million and $135 million, respectively.

Pressure from Landlords, Vendors

File for bankruptcy, and the pressure now intensifies enormously. Prior to 2005, debtors had 60 days from filing for Chapter 11 to assume or reject a lease. Most of the time, bankruptcy courts would grant repeated extensions that lasted two years or more. Bankruptcy experts argue that gift of time was crucial: They say it takes a minimum of two Christmas cycles before a retailer is ready to put its finances in order and see if its reorganization plan is working.

But mall owners don't like to house bankrupt retailers. An extended, court-run reorganization can hurt the landlord's chance of securing positive financing terms. The real estate industry lobbied successfully for the 210-day cap on how long companies have to assume or reject leases. "Macy's got at least two Christmas seasons, but today if a company files in January, they don't even have until Christmas to decide what they will do," says lawyer Gottlieb.

Even as the lease decision looms, companies have to pay a cash deposit within 30 days to the utility companies that provide gas, electricity, and water to their stores. The purpose of that change was to provide assurance of the filer's ability to pay bills in the months going forward. Previously, just an assurance had been enough.

At the same time, vendors who ship goods to a company in the 20 days preceding a filing can get a priority claim that requires they be paid in full. The 2005 law also put an 18-month limit on how long the bankrupt company has to submit a restructuring plan; previously there was no limit. After the 18 months, creditors or other interested parties can offer their own plans.

Retailers may have stores in multiple locations and hundreds of vendors supplying a variety of items. "Stores immediately lose working capital," says Harvey Miller, a partner and bankruptcy specialist at New York law firm Weil Gotschal. He worked with Macy's in the past and has recently worked with several retailers including Goody's Family Clothing, a 355-store chain that operates in 20 states and filed for bankruptcy on June 9. Miller says Macy's reorganization, which took four years, wouldn't have been possible under the new setup. "In stress situations, you have to analyze by circumstances and not make deals under a formula," he says.

Landlords Say Retailers Overstate Law's Burden

Usually under Chapter 11, stores can sublet the leases to bring in much-required cash. However, most of these lease terms are expensive, because they were drawn up in the past decade when retailers were expanding and consumers' appetite for shopping seemed limitless. Today, not too many retailers are opening up or expanding. It's not surprising that Goody's Family Clothing canceled a lease auction that was to be held in the second week of August for 66 of its stores, after it was able to secure bids on just three of them in a previous auction.

"The rule that was meant to protect landlords is now coming back to bite them, because they will be left with neither stores nor payments," says Weil's Miller.

Landlords say that retailers and their lawyers are making the law look much worse than it is. That's because even though the law set a 210-day cap, it did leave some wiggle room and allowed a bankruptcy judge to grant an extension if the landlord consented.

"Now why wouldn't landlords consent to extend leases, when they know it's difficult to find another tenant?" asks Norman Kranzdorf, chairman of the Bankruptcy Task Force of the International Council of Shopping Centers, a trade group that represents mall owners. Besides, he says, landlords are entitled to a smaller claim—so if a retailer decides to close a store and still has a multiple-year lease outstanding, the landlord can claim payments only for a two-year period rather than for the remainder of the lease as they could previously.

"Sure, retailers don't have the luxury of time as they did before, but it's not that much of a detriment because landlords also lose at the bargaining table," argues J. David Forsyth, a partner at law firm Sessions, Fishman & Nathan in New Orleans, who represents landlords.

Ultimately, though, it's all a question of control, says Elizabeth Warren, law professor at Harvard University. She says that the impact of store closings is bound to be felt across the broader U.S. economy in coming months. "This will have a ripple effect through communities with hundreds of job losses, loss of taxes, and suppliers going out of business," says Warren, who in 2005 testified to Congress against changes to the bankruptcy law. "Bankruptcy is countercyclical—it is a tool designed to be a cushion during a downturn so that everybody takes a small hit for the short term and emerges stronger for the long term."

Posted by 으랏차
,

Struggling AMD may look to unload its chipmaking facilities to a partner to concentrate on battling the industry's giant

http://images.businessweek.com/story/08/600/0810_amd.jpg

AMD Module Shift Manager Peter John presents a 200mm wafer in Dresden, Germany. Norbert Millauer/AFP/Getty Images

by Arik Hesseldahl
Chipmaker Advanced Micro Devices says it's about to go through a major change concerning how it makes chips, but it hasn't said exactly what that change will be. Speculating about it has become a great guessing game among Wall Street investors and Silicon Valley's chattering classes.

AMD (AMD) is suffering through one of the toughest stretches in its history, racking up losses of $1.6 billion on sales of $2.8 billion so far this year. It's now struggling with a nagging question: how to continue making chips for personal computers and servers that can compete with those of rival Intel (INTC) without having to bear the heavy expenses required to operate its chip factories known as "fabs."

AMD has said it's pursuing a new strategy it calls "asset smart," aimed at saving money while at the same time preserving its manufacturing muscle. Hector Ruiz, CEO up until he stepped down on July 17 (BusinessWeek.com, 7/17/08), says that in his new position as executive chairman he'll focus his attention on completing the transformation. Many chip industry analysts believe the outcome will either save the company or doom it.

At Least Some Outsourcing

The company has managed to keep a tight lid on its plans, unusual for the gossipy chip industry. The company declined to comment on its plans to BusinessWeek, beyond issuing a brief statement: "AMD continues to look at multiple options that leverage our world-class manufacturing capabilities and relationships to achieve an optimum blend of internal and external operations."

The phrase "optimal blend" is important, because it suggests that AMD is going to outsource at least some of its existing manufacturing operations. The backbone of its manufacturing operations are two fabs in Dresden, Germany, and all the chipmaking equipment in them. It also has two large test and assembly plants in Malaysia and Singapore, and a smaller one in China. One popular theory has AMD turning to a third-party chip foundry company like Chartered Semiconductor Manufacturing (CHRT) of Singapore to step in and operate the fabs under contract. Chartered already makes some of AMD's chips under contract to help AMD keep up with demand surges. Taiwan Semiconductor Manufacturing (TSM) handles manufacturing for AMD's graphics chip unit ATI.

Selling off fabs would have clear financial benefits for AMD, but would also expose some problems. For one thing it would go a long way toward cutting AMD's operational expenses, which were $1.6 billion in the first six months of the fiscal year, eclipsing its $1.3 billion gross margin. Unloading the fabs would put AMD in a class of chip companies who don't own their own factories, and thus hire companies like Chartered, TSM, United Microelectronics (UMC), and even IBM (IBM) to make chips under contract. Going "fabless" has meant success for chip companies as varied as Nvidia (NVDA), Broadcom (BRCM) and Qualcomm (QCOM). Still, such a move would fly in the face of AMD tradition: Its founder, Jerry Sanders, once famously quipped "Real men have fabs."

Maybe, but there are pitfalls to owning fabs. They're expensive to build—$3 billion to $5 billion each—and expensive to maintain. Competing with Intel means upgrading a fab with billions' worth of new manufacturing equipment every two years or so. There are also costs to holding chip inventory, which, given the volatile nature of the PC market (BusinessWeek.com, 5/30/07), has caused AMD to occasionally be left holding a lot of costly chips that are hard to unload.

Intel Deal Could Stand in the Way

Unloading the fabs to a foundry like Chartered might solve some problems, but it would create others. One important one: AMD would lose control of the up-close, day-to-day management of its manufacturing processes, which have over the years been a strategic advantage. "They know how to run a fab, and they do it well, and for the most part that's been a big help," says analyst Nathan Brookwood, head of Insight64, a Silicon Valley consultancy.

Going fabless might also run afoul of an important cross-licensing agreement between AMD and Intel. The deal, which is a leftover from their legal battles in the 1980s and was renewed most recently in 2001, expires on Jan. 1, 2010. Analyst Tim Luke of Lehman Brothers (LEH), in a research note issued July 23, says the deal likely limits the extent to which AMD could pursue a fabless arrangement, and that if AMD were to bring in an outside partner to run its fabs, it would likely have to retain ownership of at least 50%.

So is there a third alternative for AMD, something other than holding on to its fabs or selling them outright? Perhaps. Luke suggests splitting AMD into two companies, one devoted to design, the other to manufacturing. "A separate foundry function might enable it to compete with Intel without the capital required to keep up from a manufacturing standpoint," and manufacturing could be funded by an investment from a partner, probably Chartered.

There's another possible in-between path. Brookwood thinks AMD will sell the fabs and the equipment in them to a third party, and then lease them back. Such a move would allow AMD to retain full control of the fabs and operate them, yet greatly reduce the operational expenses. AMD would essentially be paying rent on the fabs and the equipment in them to a landlord.

Regulatory Approval

So who would that landlord be? Brookwood thinks a leading candidate might be a sovereign wealth fund like Mubadala Development, the Abu Dhabi-based fund that paid $622 million for a stake in AMD last November. At the time, the fund paid $12.70 a share for 49 million shares, an 8% stake. AMD's stock price has since dropped to less than half that, closing Aug. 8 at $5.13. "By taking ownership of the fabs, this fund might go a long way toward seeing its investment pay off," Brookwood says.

Such a deal would likely require the approval of government regulators in Germany, the European Union, and probably the U.S. That might explain why it's taking so long for Ruiz and new CEO Dirk Meyer to announce the plan. But would it pass muster with Intel under their cross-licensing agreement? After all, selling the fabs might arguably be considered the kind of change in control that gives Intel the right to cancel the deal.

But such a move is unlikely. Intel in recent months has been dogged by antitrust investigations in the EU, Japan, and the U.S. An unusual anti-AMD move by Intel may hurt the company's ability to argue that it's not a monopolistic bully.

So it looks like a major strategic shift is ahead for AMD. With lower operational expenses, the company would be able to concentrate on designing and selling chips. The company has proven that at times in the past it has been able to go head to head with the fearsome Intel. Even now it has a number of promising chips coming out in the near future, including one code-named Shanghai for servers and another code-named Puma for notebooks.

Unloading the financial burden of its fabs may let AMD do even more in the future. "There are some real bright spots in their product line," says analyst Dean McCarron of Mercury Research. "They do look like they are starting to turn a corner."

Hesseldahl is a reporter for BusinessWeek.com.

'IT' 카테고리의 다른 글

개발자 볼것  (0) 2008.08.18
[스크랩] 면접  (0) 2008.08.18
RIA,SaaS  (0) 2008.08.18
SaaS  (0) 2008.08.18
Will Cisco Take on Google and Microsoft?  (0) 2008.08.12
Posted by 으랏차
,

ejb

dev 2008. 8. 5. 17:03

출처 : 월간 마이크로 소프트웨어


EJB 아키텍처를 기반으로 웹애플리케이션을 구축하는 프로젝트들이 2000년 이후부터 증가해왔음은 모두가 아는 사실. 더불어 EJB 관련 서적들도 다양하게 출판됐고, EJB를 사용하는 개발자들도 늘어나고 있다.

그러나 실제 EJB를 사용하면서 개발자들이 저지르기 쉬운 실수, 함정 등은 아직 서적에서는 친절히 안내되어 있지 않은 듯 하다. 이번 강좌의 목적은 흔히 개발자들이 스스로 잘 안다고 생각하는 것 때문에 빠질 수 있는 함정들을 미리 짚어봄으로써 그 함정이 초래할 혼란을 최소화하는데 있다.

강좌 중간중간 퍼포먼스를 고려한 팁이 언급될 수도 있고, 클래스 설계와 관련된 첨언도 들어갈 수 있다. 이 강좌의 목적이나 성격이 하나의 개념에 대한 순차적인 설명 형식은 아니므로 기본개념 설명을 건너뛰거나, 동시에 초보 자바 개발자도 알고 있는 사항에 대해 새삼 지적하는 경우도 있을 것이다.

자신에게 닥칠 수도 있는 문제를 미리 파악해 보고, 어떠한 방법으로 예방하고 해결해야하는지 살펴보도록 하자. 이번 강좌에서 언급될 예시에서 쓰는 EJB의 스펙은 1.1버전이고, DBMS는 오라클 8i이다.

DB와의 연결은 누가, 언제, 어떻게 할까?
EJB 애플리케이션이 DB와의 연결자원을 이용하는 방법은 2가지다.

첫째, ConnectionPool 을 이용하는 방법이 있다. EJB 서버가 형성하게 되는 ‘ConnectionPool’이라는 데이터 소스를 이용하는 방법이다. 임의의 비즈니스 프로세스를 담당하는 세션빈의 메소드 내에서 DB의 레코드를 써야할 경우, ConnectionPool에서 java.sql.Connetion 객체를 받아 쓰는 경우이다.

웹로직(weblogic), 웹스피어(websphere), JBOSS 등 다양한 업체들의 EJB 서버마다 기동시 ConnetionPool을 생성시킬 수 있는 기능을 제공한다. 기동시 읽어들이는 프로퍼티 파일에 DB의 URL, SID, user name, 패스워드, ConnectionPool의 name까지 사용자가 지정해야 한다.

아래는 웹로직 서버(버전 5.1)의 weblogic.properties 파일 내의 ConnetionPool 설정정보 부분이다.


# testPool
weblogic.JBDC.connectionPool.testPool=\
url=JBDC:oracle:thin:@123.4.5.67:portNumber:sid명,\
driver=oracle.JBDC.driver.OracleDriver,\
initialCapacity=1,\
maxCapacity=10,\
CapacityIncrement=2,\
allowShrinking=true,\
shrinkPeriodMins=15,\
props=user=user명;password=패스워드


이렇게 EJB 서버 기동시 미리 ConnectionPool을 만들게 되면 애플리케이션 운영중 발생하게 되는 DBMS 와의 빈번한 요청이 있을 때마다 JBDC 드라이버로 DB와 일일이 연결할 필요없이, 만들어진 ConnetionPool에서 Connetion을 꺼내 쓰고 다시 반환하므로 시스템 퍼포먼스 측면에서 월등히 효율적이다.

둘째, 엔터티빈을 활용하는 방법이다. 엔터티빈 중에서도 컨테이너가 퍼시스턴스를 관리해주는 CMP 엔터티빈을 EJB 서버에 배치해 쓰면 개발자 입장에서는 많은 부담을 덜 수 있기 때문에 대부분의 프로젝트에서 BMP 엔터티빈 보다는 CMP 엔터티빈을 사용한다.

엔터티빈 객체 하나는 DB의 하나의 테이블을 대표하며, 엔터티빈 객체가 갖고 있는 필드들은 해당 테이블의 컬럼과 매핑된다. 엔터티빈의 인스턴스는 DB의 임의의 레코드를 객체화한 것이다. 그러므로 엔터티빈의 인스턴스를 호출하여, get, create, set, remove 등의 메소드를 쓴다면, 직접 SQL문을 DB로 날리지 않아도 동일한 결과를 얻을 수 있다. insert 문 대신 create 메소드를 쓰면 되므로 코딩이 간결해진다. 더불어 SQL문에 익숙하지 않은 개발자라도 쉽게 쓸 수 있다는 장점이 있다.

그러나 엔터티빈의 이러한, 소위 장점이라고 하는 것들에 섣불리 현혹되지 않는 것이 좋다. EJB 프로젝트에서 발생하는 문제의 원인은 대부분 엔터티빈에 있다.

DB 핸들링 주체는 세션빈
DB에 어떻게 접근하든 비즈니스 프로세스를 담당하는 주체는 세션빈이다. 그리고 분석된 업무 흐름을 애플리케이션으로 구현할 때의 최소단위는 세션빈의 메소드이다.

예를 들어 급여계산 프로그램이 있다면 세션빈 내의 calcuratorPay(String empNo, String yyyyMm)라는 메소드가 있을 것이고, 복리후생비 신청 프로그램은 submitWelfare(String empNo, String date, ...)라는 메소드를 콜한다.

개발자는 세션빈의 메소드 안에서 적절히 저 두가지 방법을 활용해 DB의 레코드를 핸들링하게 된다. 엔터티빈 인스턴스가 DB의 레코드를 표현한다고 해도, 엄청나게 다양한 조건 및 함수 등이 담겨있는 select 문의 효과를 엔터티빈 인스턴스를 사용해서 자바 코딩으로 얻을 수 있겠는가? 없다.

따라서 그러한 다양한 조회를 할 필요가 있는 경우에는 ConnectionPool 에서 connection 객체를 하나 얻어와서 쓰게 된다. 뿐만 아니라 update, delete, insert 등에서도 pk 값으로만 조회해서 그 값을 변경, 삭제, 추가하는 경우보다 추가적인 제약이 따르는 경우가 많다.

결국 전체 시스템의 모든 세션빈이 connetion 객체를 쓰는 경우가 훨씬 많기 때문에, 모든 세션빈이 콜 할 수 있는 공통 모듈로 connectionPool 에서 connection 객체를 하나 뽑아 리턴해주는 클래스의 개발 필요사항이 발생한다. 이 클래스에는 getConnetion() 및 closeConnection() 메소드가 위치하게 될 것이다.

아래 예시에서 등장하게 되는 ConnManager라는 클래스는 이러한 역할을 하는 클래스다.

예시 소스코드를 보자. 사번을 통해 사원명을 조회하는 매우 간단한 메소드이다.


public 클래스 EmpManagerEJB implements javax.ejb.세션빈{

    public EmpManagerEJB()
    {
    }

    public String getEmpName(String empNo) throws Exception
    {
      Connection con = null;
      String eName = null;

      try {
         con = ConnManager.getConnection();
         EmpDB DB = new EmpDB();
         eName = DB.getEmpName(con,empNo);

      } catch(Exception e) {
         throw new Exception(e.toString());
      } finally {
         try {
            ConnManager.closeConnection(con);
         } catch(Exception e) {
            throw new Exception(e);
         }
      }
      return eName;
    }

    // ---------------------------------------------------------------
    // 세션빈 interface implementation
    public void ejbActivate()
    {
      // to do: code goes here.

    }
.... 이하 생략,

}

위의 예시에서 보듯이 세션빈 메소드 내에서는 Connection 객체를 가지고 와서 마지막에 닫고, 자신을 호출한 클라이언트에게 사원명을 전달하는 일만 하고 있다. 직접적으로 DB에 갖다오는 일은 EmpDB라는 클래스에게 맡긴다. 다양한 업무를 반영하는 세션빈 메소드 내에서는 이와 같은 패턴이 보다 효과적이고 간결하다.

그렇다면 EmpDB라는 클래스의 getEmpName 메소드를 살펴보자.


    public String getEmpName(Connetion con , String empNo)
    throws Exception {

      String eName = null;
      PreparedStatement pstmt = null;
      ResultSet rs = null;

      /*
         SELECT
            EMP_NAME
         FROM EMP
         WHERE EMP_NO = '000001'
      */

      String sql =
         "SELECT " +
         " EMP_NAME " +
         "FROM EMP " +
         "WHERE EMP_NO = ? ";

      try {
         pstmt = con.prepareStatement(sql);
         pstmt.setString(1, empNo);
         rs = pstmt.executeQuery();

         if(rs.next()) {

            eName = rs.getString("EMP_NAME"));

         }
      } finally {
         ConnManager.closeResultSet(rs);
         ConnManager.closeStatement(pstmt);
      }
      return eName;
    }

사번 테이블에서 사원 번호로 사원명을 리턴하는 메소드이다. 이 메소드 중에서 가장 주의깊게 봐야 할 부분은 PreparedStatement를 Statement interface 대신 사용한 부분이다. 조회성 쿼리를 DB에 던지고자 할 경우 아무 생각없이 Statement interface를 사용하는 개발자가 많은데, 이렇게 만들어서는 안된다.
① 세션빈에서의 DB 접근전략 및 엔터티빈 사용시 주의사항
② 세션빈에서의 트랜잭션 관리
③ 비즈니스 프로세스 구현 최적화하기
④ 능률 높여주는 유틸리티를 사용하자

EJB 아키텍처를 기반으로 웹애플리케이션을 구축하는 프로젝트들이 2000년 이후부터 증가해왔음은 모두가 아는 사실. 더불어 EJB 관련 서적들도 다양하게 출판됐고, EJB를 사용하는 개발자들도 늘어나고 있다.

그러나 실제 EJB를 사용하면서 개발자들이 저지르기 쉬운 실수, 함정 등은 아직 서적에서는 친절히 안내되어 있지 않은 듯 하다. 이번 강좌의 목적은 흔히 개발자들이 스스로 잘 안다고 생각하는 것 때문에 빠질 수 있는 함정들을 미리 짚어봄으로써 그 함정이 초래할 혼란을 최소화하는데 있다.

강좌 중간중간 퍼포먼스를 고려한 팁이 언급될 수도 있고, 클래스 설계와 관련된 첨언도 들어갈 수 있다. 이 강좌의 목적이나 성격이 하나의 개념에 대한 순차적인 설명 형식은 아니므로 기본개념 설명을 건너뛰거나, 동시에 초보 자바 개발자도 알고 있는 사항에 대해 새삼 지적하는 경우도 있을 것이다.

자신에게 닥칠 수도 있는 문제를 미리 파악해 보고, 어떠한 방법으로 예방하고 해결해야하는지 살펴보도록 하자. 이번 강좌에서 언급될 예시에서 쓰는 EJB의 스펙은 1.1버전이고, DBMS는 오라클 8i이다.

DB와의 연결은 누가, 언제, 어떻게 할까?
EJB 애플리케이션이 DB와의 연결자원을 이용하는 방법은 2가지다.

첫째, ConnectionPool 을 이용하는 방법이 있다. EJB 서버가 형성하게 되는 ‘ConnectionPool’이라는 데이터 소스를 이용하는 방법이다. 임의의 비즈니스 프로세스를 담당하는 세션빈의 메소드 내에서 DB의 레코드를 써야할 경우, ConnectionPool에서 java.sql.Connetion 객체를 받아 쓰는 경우이다.

웹로직(weblogic), 웹스피어(websphere), JBOSS 등 다양한 업체들의 EJB 서버마다 기동시 ConnetionPool을 생성시킬 수 있는 기능을 제공한다. 기동시 읽어들이는 프로퍼티 파일에 DB의 URL, SID, user name, 패스워드, ConnectionPool의 name까지 사용자가 지정해야 한다.

아래는 웹로직 서버(버전 5.1)의 weblogic.properties 파일 내의 ConnetionPool 설정정보 부분이다.


# testPool
weblogic.JBDC.connectionPool.testPool=\
url=JBDC:oracle:thin:@123.4.5.67:portNumber:sid명,\
driver=oracle.JBDC.driver.OracleDriver,\
initialCapacity=1,\
maxCapacity=10,\
CapacityIncrement=2,\
allowShrinking=true,\
shrinkPeriodMins=15,\
props=user=user명;password=패스워드


이렇게 EJB 서버 기동시 미리 ConnectionPool을 만들게 되면 애플리케이션 운영중 발생하게 되는 DBMS 와의 빈번한 요청이 있을 때마다 JBDC 드라이버로 DB와 일일이 연결할 필요없이, 만들어진 ConnetionPool에서 Connetion을 꺼내 쓰고 다시 반환하므로 시스템 퍼포먼스 측면에서 월등히 효율적이다.

둘째, 엔터티빈을 활용하는 방법이다. 엔터티빈 중에서도 컨테이너가 퍼시스턴스를 관리해주는 CMP 엔터티빈을 EJB 서버에 배치해 쓰면 개발자 입장에서는 많은 부담을 덜 수 있기 때문에 대부분의 프로젝트에서 BMP 엔터티빈 보다는 CMP 엔터티빈을 사용한다.

엔터티빈 객체 하나는 DB의 하나의 테이블을 대표하며, 엔터티빈 객체가 갖고 있는 필드들은 해당 테이블의 컬럼과 매핑된다. 엔터티빈의 인스턴스는 DB의 임의의 레코드를 객체화한 것이다. 그러므로 엔터티빈의 인스턴스를 호출하여, get, create, set, remove 등의 메소드를 쓴다면, 직접 SQL문을 DB로 날리지 않아도 동일한 결과를 얻을 수 있다. insert 문 대신 create 메소드를 쓰면 되므로 코딩이 간결해진다. 더불어 SQL문에 익숙하지 않은 개발자라도 쉽게 쓸 수 있다는 장점이 있다.

그러나 엔터티빈의 이러한, 소위 장점이라고 하는 것들에 섣불리 현혹되지 않는 것이 좋다. EJB 프로젝트에서 발생하는 문제의 원인은 대부분 엔터티빈에 있다.

DB 핸들링 주체는 세션빈
DB에 어떻게 접근하든 비즈니스 프로세스를 담당하는 주체는 세션빈이다. 그리고 분석된 업무 흐름을 애플리케이션으로 구현할 때의 최소단위는 세션빈의 메소드이다.

예를 들어 급여계산 프로그램이 있다면 세션빈 내의 calcuratorPay(String empNo, String yyyyMm)라는 메소드가 있을 것이고, 복리후생비 신청 프로그램은 submitWelfare(String empNo, String date, ...)라는 메소드를 콜한다.

개발자는 세션빈의 메소드 안에서 적절히 저 두가지 방법을 활용해 DB의 레코드를 핸들링하게 된다. 엔터티빈 인스턴스가 DB의 레코드를 표현한다고 해도, 엄청나게 다양한 조건 및 함수 등이 담겨있는 select 문의 효과를 엔터티빈 인스턴스를 사용해서 자바 코딩으로 얻을 수 있겠는가? 없다.

따라서 그러한 다양한 조회를 할 필요가 있는 경우에는 ConnectionPool 에서 connection 객체를 하나 얻어와서 쓰게 된다. 뿐만 아니라 update, delete, insert 등에서도 pk 값으로만 조회해서 그 값을 변경, 삭제, 추가하는 경우보다 추가적인 제약이 따르는 경우가 많다.

결국 전체 시스템의 모든 세션빈이 connetion 객체를 쓰는 경우가 훨씬 많기 때문에, 모든 세션빈이 콜 할 수 있는 공통 모듈로 connectionPool 에서 connection 객체를 하나 뽑아 리턴해주는 클래스의 개발 필요사항이 발생한다. 이 클래스에는 getConnetion() 및 closeConnection() 메소드가 위치하게 될 것이다.

아래 예시에서 등장하게 되는 ConnManager라는 클래스는 이러한 역할을 하는 클래스다.

예시 소스코드를 보자. 사번을 통해 사원명을 조회하는 매우 간단한 메소드이다.


public 클래스 EmpManagerEJB implements javax.ejb.세션빈{

    public EmpManagerEJB()
    {
    }

    public String getEmpName(String empNo) throws Exception
    {
      Connection con = null;
      String eName = null;

      try {
         con = ConnManager.getConnection();
         EmpDB DB = new EmpDB();
         eName = DB.getEmpName(con,empNo);

      } catch(Exception e) {
         throw new Exception(e.toString());
      } finally {
         try {
            ConnManager.closeConnection(con);
         } catch(Exception e) {
            throw new Exception(e);
         }
      }
      return eName;
    }

    // ---------------------------------------------------------------
    // 세션빈 interface implementation
    public void ejbActivate()
    {
      // to do: code goes here.

    }
.... 이하 생략,

}

위의 예시에서 보듯이 세션빈 메소드 내에서는 Connection 객체를 가지고 와서 마지막에 닫고, 자신을 호출한 클라이언트에게 사원명을 전달하는 일만 하고 있다. 직접적으로 DB에 갖다오는 일은 EmpDB라는 클래스에게 맡긴다. 다양한 업무를 반영하는 세션빈 메소드 내에서는 이와 같은 패턴이 보다 효과적이고 간결하다.

그렇다면 EmpDB라는 클래스의 getEmpName 메소드를 살펴보자.


    public String getEmpName(Connetion con , String empNo)
    throws Exception {

      String eName = null;
      PreparedStatement pstmt = null;
      ResultSet rs = null;

      /*
         SELECT
            EMP_NAME
         FROM EMP
         WHERE EMP_NO = '000001'
      */

      String sql =
         "SELECT " +
         " EMP_NAME " +
         "FROM EMP " +
         "WHERE EMP_NO = ? ";

      try {
         pstmt = con.prepareStatement(sql);
         pstmt.setString(1, empNo);
         rs = pstmt.executeQuery();

         if(rs.next()) {

            eName = rs.getString("EMP_NAME"));

         }
      } finally {
         ConnManager.closeResultSet(rs);
         ConnManager.closeStatement(pstmt);
      }
      return eName;
    }

사번 테이블에서 사원 번호로 사원명을 리턴하는 메소드이다. 이 메소드 중에서 가장 주의깊게 봐야 할 부분은 PreparedStatement를 Statement interface 대신 사용한 부분이다. 조회성 쿼리를 DB에 던지고자 할 경우 아무 생각없이 Statement interface를 사용하는 개발자가 많은데, 이렇게 만들어서는 안된다.


꼭 PreparedStatement를 사용하자
Statement 인터페이스 보다는 PreparedStatement가 낫다. 사실 ‘좋다’ 정도가 아니라 ‘Statement를 쓰지 말고 PreparedStatement를 써야한다’고 말해도 될 정도다.

getEmpName의 메소드에서 PreparedStatement 대신 Statement의 executeQuery(String sql) 메소드를 사용해서 개발하면 어떤 일이 생기는지 알아보자.

인사업무 담당자가 사원 한 명씩 상세정보를 조회하고자 한다고 가정하자. 조회버튼을 누르면 사원번호로 getEmpName 메소드안에서 쿼리가 스트링으로 완성돼 Statement의 executeQuery(String sql) 메소드안의 SQL 값으로 들어간다. DB의 옵티마이저가 쿼리를 파싱, 실행하고, resultSet 리턴한다.

여기서 다음 사원을 조회하면? 사원번호만 다른, 그러나 방금전 조회시의 쿼리와 모두 같은 스트링이 던져진다. DB의 옵티마이저가 쿼리를 파싱, 실행한다.

계속 다른 사원을 조회하면? DB 입장에서는 조회조건만 다른 쿼리에 대해 파싱을 반복적으로 하고 있는 셈이고, 매번 파싱을 하고 결과값을 리턴해야 하기에 조회시 결과값 출력도 그만큼 늦어진다.

만일 이 시스템 전체가 Statement 인터페이스로 이뤄져 있고, 향후 시스템 성능 테스트에서도 이 부분이 발견되지 않는다면 어느날 갑자기 원인도 모르게 CPU 사용도가 올라가고 시스템 전체가 느려지는 현상이 발생하게 된다. 서버 관리자가 서버를 내렸다 올리겠지만 원인을 알 수 없으니 속이 편하진 않을 터.

나중에 다시 스트레스 테스트를 하고, 결국 파싱과 실행 수가 비슷하다는 사실이 발견된다. 책임은 개발자들에게 돌아간다.

Statement로 이뤄진 프로그램을 PreparedStatement로 바꾸라는 지시가 떨어지겠지만 이는 간단치 않은 일이다. Statement로 되어있는 프로그램을 그동안 copy & paste 해서 만든 프로그램이 몇 개인지 파악도 안되고, 파악이 되다 해도 바꾸는 작업은 쉽지 않다.

PreparedStatement는 주어진 쿼리를 미리 파싱을 해두기 때문에, 조회조건만 달라진 다른 쿼리가 들어오면 바로 실행만 시키게 된다. 시스템 성능 면에서 훨씬 더 효율적이다.

엔터티빈으로 접근한 row는 또 다른 Connetion 객체로 접근하지 말 것
세션빈에서 한 레코드에 대해 변경을 하는 작업을 엔터티빈으로 한다고 하고, 이때 요청사항으로 추가되는 어떤 컬럼에 대해 변경하는 부분이 늘어난다고 하자. 기존 소스는 아래와 같다. 아래 소스는 세션빈 내에서 사원발령이 났을 경우 발령 시작일자와 발령 마감일자를 변경하는 비즈니스 프로세스를 갖고 있다.


  public boolean setMoveEmp(String empNo,String startDt, String endDt ) throws Exception
  {

    boolean doneYn = false;
    EmpHome empHome = null;
    Emp emp = null;

    try {
      EmpPk pk = new EmpPk();

      /* HomeFactory : JNDI 이름으로 Entitybean의 HOME 객체를 리턴한다 */
      empHome = (EmpHome)HomeFactory.getHome(EmpHome.클래스);

      pk.empNo = empNo;
      emp = empHome.finDByPrimaryKey(pk);
      emp.setStartDt( startDt );
      emp.setEndDt( endDt );

    } catch(Exception e) {
      throw new Exception(e.toString());
    } finally {
          ConnManager.closeConnection(con);
      } catch(Exception e) {
          throw new Exception(e);
      }
    }
    return doneYn;
  }


여기에서 사원발령정보를 변경하기 위해 엔터티빈 리모트 인터페이스의 setStartDt, setEndDt 메소드를 사용했다. 여기서 만일 사원발령정보를 낼 때 변경 일자도 기록하라는 요청사항이 추가될 경우, 이를 기존 소스에 추가하는 작업에서 엔터티빈을 사용하지 않고 Connection 객체를 만들어서 직접 SQL문을 던지는 형식으로 레코드의 한 컬럼 값을 변경시킨다고 가정해보자.


  public boolean setMoveEmp(String empNo,String startDt, String endDt ) throws Exception
  {

    /* Connection 선언 추가한다. */
    Connection con = null;
    boolean doneYn = false;
    EmpHome empHome = null;
    Emp emp = null;

    try {

      /* Connection instance 받아온다. */
      con = ConnManager.getConnection();
      EmpDB DB = new EmpDB(); /* DB 래퍼 선언 */



      EmpPk pk = new EmpPk();

      /* HomeFactory : JNDI 이름으로 Entitybean의 HOME 객체를 리턴한다 */
      empHome = (EmpHome)HomeFactory.getHome(EmpHome.클래스);

      pk.empNo = empNo;
      emp = empHome.finDByPrimaryKey(pk);

      emp.setStartDt( startDt );


      /* 변경 일자를 기록하는 부분이 추가된다 */
      DB.setUpdateDate(con,empNo);


      emp.setEndDt( endDt );



    } catch(Exception e) {
      throw new Exception(e.toString());
    } finally {
      try {
        ConnManager.closeConnection(con);
      } catch(Exception e) {
        throw new Exception(e);
      }
    }
    return doneYn;
  }


위의 소스를 보면 일견 아무런 문제가 없어 보인다. 컴파일도 잘 되고 실행시에 아무런 에러도 나지 않는다. 하지만 결과를 보면,

기존

emp.setStartDt( startDt );
emp.setEndDt( endDt );


를 타서, 레코드에 startDt와 endDt 컬럼 부분은 모두 원하는 데로 변경이 되었으나,


/* 변경 일자를 기록하는 부분이 추가된다 */
DB.setUpdateDate(con,empNo);


부분이 실행이 되었음에도 변경 일자 컬럼에는 아무런 변화가 없음을 알 수 있다.

엔터티빈의 인스턴스를 가지고 온 후부터 DB 레코드와 엔터티빈간의 동기화가 형성되고, 세션빈 메소드가 끝났을 때 DB에 결과를 쓰게 된다. 즉, 최종적으로 기록이 남는 것은 엔터티빈으로 작업한 결과이다. 중간에 새로 생긴 업데이트 세션은 그 작업은 성공한다 해도 DB에는 전혀 반영이 되지 않는 것이다.

이 소스를 보고 ‘그렇다면 모두 엔터티빈 메소드로 실행시키면 되지 왜 굳이 Connection 객체로 접근하는가'라는 의문이 들 수도 있다. 하지만 시스템을 오픈하고 나서 테이블에 컬럼을 추가해야 하는 경우가 업무적으로 반드시 생기게 된다. 그때 추가된 컬럼에 대해서는 엔터티빈안에 추가된 컬럼의 필드와 상응하는 변수를 추가하고 엔터티빈을 서버에 다시 배치해야 한다. 하지만 이 작업이 번거로우므로 추가된 컬럼에 한해서 SQL 쿼리로 간단히 핸들링하고자 하는 계획을 세우게 된다. 그럴때 위와 같은 경우가 생기게 되고, 한 row에 대해서 엔터티빈을 콜 할 때 엔터티빈 인스턴스와 DB가 동기화 된다는 것을 염두에 두지 못한 채 Connetion 객체를 써서 업데이트하는 하는 일도 생기는 것이다.

위의 소스는 모두 Connection 객체를 써서 해당 row의 컬럼을 변경 시키든지, 아니면 모두 엔터티빈 메소드를 쓰도록 변경해야 한다.

엔터티빈 사용을 지양하자
주위에서 진행되는 프로젝트들을 살펴보면 엔터티빈을 몰라서 쓰지않는 것이 아니라, 엔터티빈의 성질을 잘 알기 때문에 안쓰는 경우를 종종 보게된다.

엔터티빈의 이점이 몇가지 있다는 것은 분명하지만 개발자 입장에서는 쿼리로 간단히 대처를 할 수 있다는 측면에서 엔터티빈을 기피하게 된다.

한편 엔터티빈 말고 Connection 객체로 DB를 핸들링한다고 해서 트랜잭션이 언제나 완결되는가 하면 그것은 또 아니다. 분명 엔터티빈보다는 훨씬 더 강력하지만 예외도 있다. 다음 이어지는 강좌에서는 그에 해당하는 예제를 들고, 이를 피하려면 어떤 전략을 취해야 하는지 소개하도록 하겠다. @

'dev' 카테고리의 다른 글

locatin 속성  (0) 2008.09.12
EUC-kr, Unicode, UTF-8  (1) 2008.08.20
jms  (0) 2008.08.04
expression pattern  (0) 2008.08.01
script class  (0) 2008.08.01
Posted by 으랏차
,

jms

dev 2008. 8. 4. 22:20

원문 : http://cafe.naver.com/mythinkbank.cafe?iframe_url=/ArticleRead.nhn%3Farticleid=245

#######################################
# JMS(Java Message Service) API
#######################################
- JMS API는 애플레케이션에서 메시지를 생성하고 전송하고 전송받고 읽는 기능을 구현할 때 사용하는 Java API이다.
- 메세징은 느슨한 연결(Lazy Connection)되어 있는 분산 처리 시스템의 통신방법이다.
- 메시징은 보내는 쪽이 직접 메시지를 받는 쪽에 전달하는 것이 아니라 "목적지"라는 특별한 저장소로 전달하는 것이기 때문에

  받는쪽에 대한 어떠한 정보도 알 필요가 없다. 마찬가지로 받는 쪽에서도 보내는 쪽의 정보를 알 필요 없이 목적지로부터 메시지를

  전달받으면 된다. 따라서 보내는 쪽과 받는 쪽 모두 메시지가 어떤 형태이고 목적지가 어딘지만 알면 되는 것이다.
- JMS API는 느슨하게 연결된 통신 방법뿐 아니라 다음과 같은 특징도 가지고 있다.
   - 비동기식 : JMS제공자는 자신에게 전달된 메시지를 메시지를 전달받은 클라이언트에게 비동기식으로 전달할 수 있다.
   - 신뢰성 : JMS API는 오직 한번만 전달된다. 목적지에 전달된 메시지는 전달받으려는 클라이언트에게 메시지가 전달된 후에는 다시

                  전달되지 않는다. 이는 신뢰성이 낮은 수준의 메시지를 잃어버리거나 중복된 메시지가 잔달되는 애플리케이션에서 많은

                  도움이 될 수 있다.
- RPC(Remote Procedure Call)보다 JMS가 사용되는게 바람직한 경우
   - 다른 컴포넌트의 인터페이스 정보에 의존하지 않고 컴포넌트가 쉽게 변경되는 서비스를 원할 때
   - 동시에 모든 애플리케이션이 작동하고 있는 상황에서 특정 애플리케이션이 작동하기를 원할때
   - 애플리케이션 비즈니스 모델을 사용하는 컴포넌트가 다른 곳으로 정보를 전달하고, 바로 응답을 받지 않은 상태에서도 계속

      작동하는것을 원할때
  
########################################
# JMS API 기본 개념
########################################
1. JMS API 구조(JMS API Architecture)
   - JMS 제공자(JMS Provider) : JMS 인터페이스를 구현하고 관기 기능과 제어 기능을 가지고 있는 메시징 시스템을 말한다.

                                              J2EE1.3 플랫품 이후부터는 애플리케이션 서버에 JMS 제공자가 포함되고 있다.
   - JMS 클라이언트(JMS Client) : 메시지를 생산하고 소비하는 자바 언어로 작성된 프로그램이나 컴포넌트를 말한다. 모든 J2EE

                                                애플리케이션 컴포넌트는 JMS 클라이언트로 사용될 수 있다.
   - 메시지(Message) : JMS클라이언트 간에 통신 정보를 주고받을 수 있는 객체를 말한다.
   - 관리객체(Administered Object) : 클라이언트가 사용하기 위해 미리 설정된 JMS 객체로서 애플리케이션 관리자가 생성한다.

                                                       JMS 관리 객체는 목적지와 커넥션 팩토리 두 가지가 있다.
                                          
2. 메시징 도메인(Messaging Domain)
   - 메시징 도메인인란 JMS API를 이용하여 개발한 애플리케이션으로 JMS API 이전에는 대부분 Point-to-Point 또는

     Publish/Subscribe 방식의 메시징 서비스를 지원하였다.               
   - JMS명세서에서는 각각의 분리된 도메인이나 동일한 도메인에서 메시징 서비스를 할 수 있도록 지원한다.
   - 독립적인 JMS 제공자는 하나 또는 양쪽의 도메인을 구현할 수 있으나 J2EE 제품 제공자는 반드시 양쪽 도메인을 구축해야만 한다.
   - JMS API를 사용하여 개발한 대부분의 애플리케이션은 Point-to-Point와 Publish/Subscribe도메인을 지원할 수 있다.
  
3. Point-to-Point 메시징 도메인
   - Point-to-Point 방식의 메시징 도메인은 Queue, Sender, Receiver의 개념을 기본으로 하여 구성되어 있다.
   - 각각의 메세지는 지정된 Queue의 주소로 전달되고 전달 받으려는 클라이언트는 자신에게 전달된 메시지를 가지고 있는

      Queue로부터 메시지를 받는다.
   - Point-to-Point 메시징 도메인의 특징
      - 각각의 메시지는 오직 하나의 소비자에게만 전달된다.
      - 메시지 Sender와 Receiver는 시간에 비의존적이다. 즉 Receiver는 Sender가 언제 메시지를 보냈는지에 상관없이 자신이

        원할 때 메시지를 받는다.
      - Receiver는 큐에 성공적으로 메시지를 받았음을 알린다.
   - Point-to-Point 메시징 도메일을 사용하여 메시지를 전달하면 반드시 한 소비자에게만 메시지가 전달된다.
  
4. Publish/Subscribe 메시징 도메인
   - Publish/Subscribe 방식의 메시징 도메인은 메시지를 전달하는 클라이언트가 인터넷 게시판과 같은 기능을 갖는 Topic이라는

     목적지로 메시지를 전달한다.
   - Publisher와 Subscriber는 일반적으로 익명의 상대를 대상으로 하며 동적으로 메시지를 전달하거나 전달받을 수 있다.
   - 이 시스템은 다양한 Publisher가 다양한 Subscriber에게 메시지를 전달할 수 있고 토픽은 현재 자신에게 등록한 Subscriber에게

     메시지를 전달한다.
   - Publish/Subscribe 메시징 도메인의 특징은 다음과 같다.
      - 각각의 메시지는 다양한 소비자에게 전달된다.
      - Publisher와 Subcriber는 시간에 의존적이다. 특정 토픽(Topic)에 등록한 Subscriber는 자신이 등록한 토픽에 전달된

        메시지만을 받을 수가 있고, 메시지를 소비하기 위해서 반드시 Publisher에 연결된 상태이어야 한다.
       
5. 메시지의 소비
   - 메시징 서비스 제품들은 기본적으로 비동식이다. 그러나 하나의 메시지는 동기식, 비동기식 모두에서 사용될 수 있다.
      - 동기식(Synchronously) : Subscriber또는 Receiver는 명시적으로 receive()메소드를 호출하여 목적지로부터 메시지를

                                              받는다. 이 메소드의 호출은 메시지가 도착하거나 메시지가 도착하지 않았을때 지정된 시간까지

                                              대기한다.
      - 비동기식(Asynchromously) : 클라이언트는 이벤트 리스너와 비슷한 메시지 리스너(MEssage Listener)를 통해 메시지를

                                                   전달받을 수 있다. 메시지 리스너는 목적지에 메시지가 도착하였을 때 onMessage() 메소드가

                                                   호출되어 메시지가 전달되었음을 알려준다.


##########################################
# JMS API로 구성된 애플리케이션의 구성
##########################################
- JMS애플리케이션은 다음과 같은 구성요소로 되어 있다.
   - 관리객체(Administered Object) : 커넥션 펙토리(Connection Factory)와 목적지(Destination)
   - 커넥션(Connection)
   - 세션(Session)
   - 메시지 생산자(Message Producer)
   - 메시지 소비자(Message Consumer)
   - 메시지(Message)


1. 관리객체 (Administered Object)
   - JMS 애플리케이션에서 프로그래밍적인 요소보다 더 중요하게 관리되는 구성 요소가 커넥션 펙토리와 목적지라는 관리 객체이다.
   - 이들 객체의 생성과 관리는 JMS API에서 직접 구현하는 것이 아니라 JMS제공자에 의해서 관리된다.
   - 관리자는 JNDI 네임스페이스내에 이들 관리 객체를 구성하고 JMS 클라이언트는 JNDI API를 통해 등록된 JNDI네임스페이스를

     검색할 수 있다.


1.1 커넥션 팩토리(Connection Factory)
   - 커넥션 팩토리는 클라이언트가 JMS 서비스에 연결하기 위한 커넥션을 생성할 때 사용하는 객체이다.
   - 커넥션 팩토리는 관리자가 정의한 커넥션에 관련된 설정 매개 변수르르 캡슐화하고 있으며, 각각의 커넥션 팩토리는
     QueueConnectionFactory 또는 TopicConnectionFactory 인터페이스의 인스턴스이다.
   - JSM클라이언트는 프로그램 시작 부분에 JNDI API를 사용하여 커넥션 팩토리를 네이밍서비스에서 검색한다.
   - 검색된 커넥션 팩토리는 ConnectionFactory 객체로 캐스팅하여 사용되기도 하고 QueueConnectionFactory나
     TopicConnectionFactory 객체로 캐스팅하여 사용되기도 한다.
   - EX>
        Context ctx = new InitialContext();
        ConnectionFactory connectionFactory = (ConnectionFactory)ctx.lookup("jms/ConnectionFactory");
       
1.2 목적지(Destination)
   - 목적지는 생성된 메시지가 저장되는 저장소로 그리고 메시지가 소비되는 곳으로 사용되는 객체이다.
   - 목적지는 Point-to-Point 메세징 도메인에서는 Queue, Publish/Subscribe 메시징 도메인에서는 Topic이라 부른다.
   - 애플리케이션 서버를 사용하여 목적지를 생성하려면 다음 두 가지 단계를 거쳐야 한다.
      - JNDI 목적지의 이름을 지정한 JMS 목적지 리소스를 생성한다.
      - JNDI 이름을 참조하는 물리적인 목적지를 사용한다.
   - 클라이언트 프로그램에서는 먼저 커넥션 팩토리를 네이밍 컨텍스트에서 검색하고 그런 다음에 목적지를 검색한다.
   - 목적지 객체는 Destination 타입으로 캐스팅되며, 적절한 다른 타입으로 캐스팅될 수도 있다.
   - Ex> Destination myDest = (Destination)ctx.lookup("jms/MyTopic");
           Topic myTopic = (Topic)myDest;
           or
           Queue myQueue = (Queue)ctx.lookup("jms/MyQueue");
          
2. 커넥션(Connection)
   - 커넥션은 JMS 제공자를 이용한 가상의 커넥션을 캡슐화하고 있다.
   - 커넥션은 클라이언트와 제공자 서비스 데몬 사이를 TCP/IP 소켓을 이용하여 통신하고, 개발자는 커넥션을 사용하여 한 개 또는
     여러개의 세션을 생성한다.
   - 커넥션은 ConnectionFactory 객체의 createConnection() 메소드를 호출하여 Connection인터페이스 타입으로 얻을 수 있다.
        Connection connection = connectionFactory.createConnection();
   - 애플리케이션이 완료되기 전에 생성했던 커넥션은 반드시 닫아야 한다.
        connection.close();
       
3. 세션(Session)
   - 세션은 메시지를 생성하고 소비하는 단일 스레드 단위이다.
   - 세션은 메시지 리스너가 실행될 때 함께 작동하며, 메시지 생산자, 메시지 소비자 그리고 메시지를 생성할 때 세션을
     사용할 수 있다.
   - 애플리케이션에서 메시지를 보내고 받기 위한 최소한의 단위를 트랜잭션이라 하는데 세션은 이런 트랜잭션을 기능을 제공한다.
   - 세션은 Session 인터페이스를 구현하고 Connection 객체의 createSession() 메소드를 호출하여 세션 객체를 생성할 수 있다.
         Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
   - 트랜잭션에 포함되는 세션을 생성하려면 다음과 같이 작성한다.
         Session session = connection.createSession(true, 0);
     
4. 메시지 생산자(Message Producer)
   - 메시지 생산자는 목적지에 메시지를 전달하는 역할을 하는 객체이다.
   - 메시지 생산자는 MessageProducer 인터페이스를 구현한 객체로 MessageProducer를 생성하기 위해서는 Session 객체를
     사용한다.
   - Ex> MessageProducer producer = session.createProducer(myQueue);
            MessageProducer producer = session.createProducer(myTopic);
   - 메시지 생성자를 생성한 다음에 send()메소드를 호출하여 메시지를 전달한다.
         producer.send(message);
   - createProducer()메소드에 null값을 지정하여 특정한 생성자를 할당하지 않을 수도 있다. 이렇게 메시지 목적지를 갖지 않는
     메시지 생산자는 목적지를 지정하기 위해서 send() 메소드의 첫번째 매개 변수에 목적지를 지정해야 한다.
         MessageProducer anon_prod = session.createProducer(null);
         anon_prod.send(myQueue, message);
     
5. 메시지 소비자(Message Consumer)
   - 메시지 소비자는 목적지에 전달된 메시지를 전달받는 역할을 하는 객체이다.
   - 메시지 소비자는 MessageConsumer 인터페이스를 구현한 객체로 메시지 생산자와 마찬가지로 세션에 의해서 생성된다.
   - 메시지 소비자는 JMS클라이언트가 JMS제공자를 이용하여 목적지에 클라이언트 자신을 등록할 수 있도록 해준다.
   - 목적지에 등록된 클라이언트만이 메시지를 전달받을 수 있으며, JMS제공자는 목적지에 등록된 메시지 소비자에게 목적지에 전달된
     메시지를 전달하는 것을 관리하게 된다.
   - Ex> MessageConsumer consumer = session.createConsumer(myQueue);
            MessageConsumer consumer = session.createConsumer(myTopic);
   - 메시지 소비자가 생성된 다음에 이 메시지 소비자를 이용하여 메시지를 잔달받을 수 있으나 메시지는 커넥션이 연결된 다음
     start() 메소드가 호출되기 전까지는 전달되지 않는다. 메시지 소비자가 동기적으로 메시지를 소비하기 위해서는 receive()
     메소드가 사용되는데 start()메소드가 호출된 다음에는 언제든지 receive()메소드를 호출할 수 있다.
         MessageConsumer의 close()메소드를 사용하면 메시지의 소비자의 사용을 중지할 수 있다.
         connection.start();
         Message m = consumer.receive();
         connection.start();
         Message m = consumer.receive(1000); // time out after a second
   - 만약 비동기적으로 메시지를 소비하려면 메시지를 이벤트처럼 관리하는 메시지 리스너가 필요하다.
  
5.1 메시지 리스터(Message Listener)
   - 메시지 리스너는 JMS API에서 사용되는 이벤트 핸들러로 비동기식으로 메시지를 전달받을 수 있게 해준다.
   - 메시지 리스너는 MessageListener 인터페이스를 구현해야 하며 MessageListener에는 onMessage()라는 단 하나의 이벤트
     메소드를 가지고 있다. 메시지가 목적지에 도착했을 때 작업하려는 내용을 onMessage()메소드에 작성하면 된다.
   - 메시지 리스너를 등록하기 위해서는 MessageConsumer의 setMessageListener()메소드를 이용한다.
        Listener myListener = new Listener();
        consumer.setMessageListener(myListener);
   - 메시지 리스너를 등록한 다음에는 Connection의 start()메소드를 호출하여 메시지 전송을 시작한다.
   - 메시지가 전달되기 시작하면 JMS 제공자는 메시지가 전달되었을 때 자동으로 메시지 리스너의 onMessage() 메소들 호출한다.
     onMessage() 메소드는 메시지를 매개 변수로 전달받는데 전달받은 메시지는 6가지(TextMessage, MapMessage,
     BytesMessage, StreamMessage, ObjectMessage, Message)의 메시지 타입 중 하나로 변환하여 사용할 수 있다.
    
5.2 메시지 셀렉터(Message Selector)    
   - 애플리케이션에서 전달받은 메시지를 필터링(Filtering)할 필요가 있다면 JMS API의 메시지 셀렉터를 사용한다.
   - 메시지 셀렉터의 표현식은 문자열로 나타내는데 이 표현식은 SQL92조건절을 기반으로 해서 작성한다.
   - Ex> NewsType = '스포츠' OR NewsType = '독자의견'
   - createConsumer()와 createDurableSubsriber()메소드를 이용하여 메시지 소비자를 생성할 때 매개 변수로 메시지 셀렉터를
     지정할 수 있다. 이렇게 메시지 셀렉터를 가지고 생성된 메시지 소비자는 메시지의 해더(Header)와 프로퍼티(Property)가
     셀렉터와 일치하는 메시지만을 전달받게 된다.
     
6. 메시지(Message)
   - JMS 애플리케이션의 최종 목적은 메시지를 생성한 다음에 다른 소프트웨어 애플리케이션으로 하여금 이 메시지를 소비하도록
     하는데 있다.
   - JMS메시지는 Header, Property 그리고 Body 의 세부분으로 구성되어 있다. 이중 Header는 필수사항이고 나머지는 옵션이다.
      - Message Header(필수)
      - Message Property(옵션)
      - Message Body(옵션)
     
6.1 메시지 헤더(Message Header)
   - JMS메시지 해더는 JMS클라이언트와 JMS제공자가 메시지를 확인하고 전달하기 위해 사용하는 몇가지 정의된 값을 가지고 있다.
   - 모든 메시지는 JMS 헤더 필드에 JMSMessageID라는 유일한 식별자를 가지고 있다.
   - JMSDestination이라는 헤더 필드는 메시지가 보내질 큐나 토픽을 나타낸다.
   - 각각의 헤더 필드는 모두 setter()/getter()메소드를 가지고 있으며 Message 인터페이스를 참조하여 이를 사용할 수 있다.
   - 대부분의 헤더 필드는 send() 또는 publish() 메소드에 의해서 자동으로 사용되는 데 반해서 클라이언트에 의해서 사용되기도
     한다.
   JMS 메시지 헤더 필드
   ===============================================================
   헤더필드                                  사용되는 위치
   ==================      ==========================================
   JMSDestination                       send() 또는 publish()메소드
   JMSDeliveryMode                    send() 또는 publish()메소드
   JMSExpiration                         send() 또는 publish()메소드
   JMSPriority                              send() 또는 publish()메소드
   JMSMessageID                       send() 또는 publish()메소드
   JMSTimestamp                       send() 또는 publish()메소드
   JMSCorrelationID                   Client
   JMSReplyTo                            Client
   JMSType                                 Client
   JMSRedelivered                      JMS제공자
  
6.2 메시지 프로퍼티(Message Property)
   - 메시지에 헤더 필드가 제공한 정보 외에 추가로 정보가 필요하면 메시지 프로퍼티를 생성하여 지정하면 된다.
   - 메시지 프로퍼티는 다른 메시지 시스템과 호환되어 사용될 수 있고 메시지 셀렉터에서 사용될 수 있다.
  
6.3 메시지 바디(Message Body)
   - JMS API에는 6가지의 메소드 바디타입이 있다.
   - 다양한 형태의 데이터로 메시지 바디를 채울수 있도록 JMS API는 여러 메소드를 제공한다.
      Ex> TextMessage message = session.createTextMessage();
            message.setText(msg_text);  // msg_text is a String
            producer.send(message);
   - 소비하는 측에서는 메시지를 일반적인 Message인터페이스 타입으로 받는데 반드시 적절한 메시지 타입으로 캐스팅해야만
     메시지를 사용할 수 있다.
   - 캐스팅된 메시지의 내용을 얻기 위해서는 getText()메소드와 같은 getter()메소드를 이용한다.
      Ex> Message m = consumer.receive();
            if(m instanceof TextMessage){
                TextMessage message = (TextMessage)m;
                System.out.println("Reading message : " + message.getText());
            }else{
                //Handle Error
            }
  JMS 메시지 타입           
   ==============================================================
   메시지 타입                         메시지 내용
   =================   ======================================
   TextMessage                    java.lang.String 객체 타입의 메시지이다.
   MapMessage                    name/value를 쌍으로 갖는 타입의 메시지이다.
   ByteMessage                    바이트스트림의 메시지이다. 이 메시지 타입은 기존의 메시지 포멧과 매칭하기 위해서 메시지의
                                            내용을 인코딩(Encoding)할 때 사용한다.
   StreamMessage                자바에서 사용하는 기본 스트림 타입의 메시지이다.
   ObjectMessage                 자바에서 사용하는 직렬화된 객체 타입의 메시지이다.
   Message                           메시지 바디가 없이 헤더와 프로퍼티만으로 구성되어 있다. 메시지 바디가 필요하지 않을 때 사용한다.
  
7. 예외처리
   - JMS API에서 사용되는 예외중 최상위 예외 클래스는 JMSException이다. JMSException 서브 클래스는 다음과 같다.
      - IllegalStateException
      - InvalidClientIDException
      - InvalidDestinationException
      - InvalidSelectorException
      - JMSSecurityException
      - MessageEOFException
      - MessageFormatException
      - MessageNotReadableException
      - MessageNotWriteableException
      - ResourceAllocationException
      - TransactionInProgressException
      - TransactionRolledBackException  


########################################

# Sun Application 설정

########################################      

- Sun Application Server 를 JMS제공자로 사용하기위해 다음 세가지를 설정한다.

   - 두 개의 커넥션 펙토리 (Queue/Topic)

   - 두 개의 물리적인 목적지 (Queue/Topic)

   - 두 개의 목적지 리소스 (Queue/Topic)


- 커넥션 펙토리 설정

   1. Admin Console을 실행한다

   2. Java Message Service Resource > Connection Factory 노드 선택

   3. JMS Connection Factory 페이지에서 "New" 버튼클릭 -> Create JMS Connection Factory 화면 이동

   4. JNDI Name 필드에서 jms/QueueConnectionFactory 입력, Type 콤보박스에서 javax.jms.QueueConnectionFactory를

       선택, Resource 항목은 Enabled Check한 후 "OK" 버튼으로 커넥션 펙토리를 저장한다.

   5. 위와 같은 방법으로 jms/TopicConnectionFactory, javax.jms.TopicConnectionFactory를 생성한다.


- 물리적 목적지 설정

   1. Configuration > Java Message Service > Physical Destination 항목을 선택한다.

   2. Physical Destination 항목에서 "New" 를 클릭 -> Create Physical Destination 페이지로 이동

   3. Physical Destination Name 필드에 PhysicalQueue라고 입력. Type 콤보박스에서 Queue 선택. "OK" 버튼을 클릭하여

      저장한다.

   4. 위와 같은 방법으로 PhysicalTopic/topic을 생성한다.


- 목적지 리소스 생성

   1. JMS Resource > Destination Resource 으로 이동한다.

   2. "New" 버튼을 클릭 -> Create JMS Destination Resource 페이지로 이동한다.

   3. JNDI Name필드에 jms/Queue라고 입력. Type콤보박스에서 javax.jms.Queue를 선택하고 Resource Enabled항목으로

       선택. Additional Property 항목의 Name 필드의 Value에 PhysicalQueue라고 입력한 후 "OK"버튼을 클릭하여 저장한다.

   4. 위와 같은 방법으로 jms/Topic/javax.jms.Topic/Resource Enabled Check 선택, Name 필드의 Value에 PhysicalTopic

       라고 입력한다.

  
########################################

# 예제!!

########################################    

/*
 * SimpleProducer.java
 *
 * Created on 2007년 3월 26일 (월), 오후 9:25
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package simpleproducer;

import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.naming.*;
import javax.jms.*;

/**
 *
 * @author Administrator
 */
public class SimpleProducer {
   
    /** Creates a new instance of SimpleProducer */
    public SimpleProducer() {
    }
   
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        // TODO code application logic here
        final int NUM_MSGS;
       
        if ((args.length < 1) || (args.length > 2))
        {
            System.out.println("Program take one or two arguments : <dest_name> [<number-of message>]");
            System.exit(1);
        }
       
        String destName = new String(args[0]);
        System.out.println("Destination name is " + destName);
       
        if (args.length == 2)
        {
            NUM_MSGS = (new Integer(args[1])).intValue();
        }
        else
        {
            NUM_MSGS = 1;
        }
       
        Context jndiContext = null;
       
        try
        {
            jndiContext = new InitialContext();
        }
        catch(NamingException e)
        {
            System.out.println("Could not create JNDI API context : " + e.toString());
            System.exit(1);
        }
       
        ConnectionFactory connectionFactory = null;
        Destination dest = null;
       
        try
        {
            connectionFactory = (ConnectionFactory)jndiContext.lookup("jms/QueueConnectionFactory");
            dest = (Destination) jndiContext.lookup(destName);
        }
        catch(Exception e)
        {
            System.out.println("JNDI API lookup failed : " + e.toString());
            e.printStackTrace();
            System.exit(1);
        }
       
        Connection connection = null;
        MessageProducer producer = null;
       
        try
        {
            connection = connectionFactory.createConnection();
           
            Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
           
            producer = session.createProducer(dest);
           
            TextMessage message = session.createTextMessage();
           
            for(int i = 0; i < NUM_MSGS; i++)
            {
                message.setText("This is message " + (i + 1));
                System.out.println("Sending message : " + message.getText());
               
                producer.send(message);
            }
        }
        catch(JMSException e)
        {
            System.out.println("Exception occurred : " + e.toString());
        }
        finally{
            if (connection != null)
            {
                try{
                    connection.close();
                }
                catch(JMSException e)
                {
                   
                }
            }
        }
       
    }
   
}




/*
 * SimpleSynchConsumer.java
 *
 * Created on 2007년 3월 26일 (월), 오후 10:21
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package simplesynchconsumer;

import javax.jms.*;
import javax.naming.*;

/**
 *
 * @author Administrator
 */
public class SimpleSynchConsumer {
   
    /** Creates a new instance of SimpleSynchConsumer */
    public SimpleSynchConsumer() {
    }
   
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        // TODO code application logic here
       
        String destName = null;
        Context jndiContext = null;
        ConnectionFactory connectionFactory = null;
        Connection connection = null;
        Session session = null;
        Destination dest = null;
        MessageConsumer consumer = null;
        TextMessage message = null;
       
        if(args.length != 1)
        {
            System.out.println("Program takes one argument : <dest_name>");
            System.exit(1);
        }
       
        destName = new String(args[0]);
        System.out.println("Destination name is " + destName);
       
        try
        {
            jndiContext = new InitialContext();
        }
        catch(NamingException e)
        {
            System.out.println("Could not create JNDI API context : " + e.toString());
            System.exit(1);
        }
       
        try
        {
            connectionFactory = (ConnectionFactory)jndiContext.lookup("jms/QueueConnectionFactory");
            dest = (Destination)jndiContext.lookup(destName);
        }
        catch(Exception e)
        {
            System.out.println("JNDI API lookup failed : " + e.toString());
            System.exit(1);
        }
       
        try
        {
            connection = connectionFactory.createConnection();
           
            session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
           
            consumer = session.createConsumer(dest);
           
            connection.start();
           
            while(true)
            {
                Message m = consumer.receive(1);
               
                if (m != null)
                {
                    if (m instanceof TextMessage)
                    {
                        message = (TextMessage)m;
                       
                        System.out.println("Reading message : " + message.getText());
                    }
                }
                else
                {
                    break;
                }
            }
        }
        catch(JMSException e)
        {
            System.out.println("Exception occured : " + e.toString());
        }
        finally
        {
            if (connection != null)
            {
                try{
                    connection.close();
                }
                catch(JMSException e)
                {
                   
                }
            }
        }
    }
   
}




/*
 * SimpleAsynchConsumer.java
 *
 * Created on 2007년 3월 26일 (월), 오후 11:02
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package simpleasynchconsumer;

import javax.jms.*;
import javax.naming.*;
import java.io.*;

/**
 *
 * @author Administrator
 */
public class SimpleAsynchConsumer {
   
    /** Creates a new instance of SimpleAsynchConsumer */
    public SimpleAsynchConsumer() {
    }
   
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        // TODO code application logic here
       
        String destName = null;
        Context jndiContext = null;
        ConnectionFactory connectionFactory = null;
        Connection connection = null;
        Session session = null;
        Destination dest = null;
        MessageConsumer consumer = null;
        TextListener listener = null;
        TextMessage message = null;
        InputStreamReader inputStreamReader = null;
        char answer = '\0';
       
        if (args.length != 1)
        {
            System.out.println("Program takes one argument : <dest_name>");
            System.exit(1);
        }
       
        destName = new String(args[0]);
        System.out.println("Destination name is " + destName);
       
        try
        {
            jndiContext = new InitialContext();
        }
        catch(NamingException e)
        {
            System.out.println("Could not create JNDI API context : " + e.toString());
            System.exit(1);
        }
       
        try
        {
            connectionFactory = (ConnectionFactory)jndiContext.lookup("jms/QueueConnectionFactory");
            dest = (Destination)jndiContext.lookup(destName);
        }
        catch(Exception e){
            System.out.println("JNDI API lookup failed : " + e.toString());
            System.exit(1);
        }
       
        try
        {
            connection = connectionFactory.createConnection();
           
            session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
           
            consumer = session.createConsumer(dest);
           
            listener = new TextListener();
           
            consumer.setMessageListener(listener);
           
            connection.start();
           
            System.out.println("To end program, type Q or q, then <return>");
           
            inputStreamReader = new InputStreamReader(System.in);
           
            while(!((answer == 'q') || (answer == 'Q')))
            {
                try
                {
                    answer = (char)inputStreamReader.read();
                }
                catch(IOException e)
                {
                    System.out.println("I/O exception : " + e.toString());
                }
            }
        }
        catch(JMSException e)
        {
            System.out.println("Exception occured : " + e.toString());
        }
        finally
        {
            if (connection != null)
            {
                try
                {
                    connection.close();
                }
                catch(JMSException e)
                {

                }
            }
        }
    }
   
}



/*
 * TextListener.java
 *
 * Created on 2007년 3월 26일 (월), 오후 11:02
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package simpleasynchconsumer;

import java.awt.font.TextMeasurer;
import javax.jms.*;

/**
 *
 * @author Administrator
 */
public class TextListener implements MessageListener {
   
    /** Creates a new instance of TextListener */
    public TextListener() {
    }
   
    public void onMessage(Message message)
    {
        TextMessage msg = null;
       
        try
        {
            if (message instanceof TextMessage)
            {
                msg = (TextMessage)message;
               
                System.out.println("Reading message : " + msg.getText());
            }
            else
            {
                System.out.println("Message is not a TextMessage");
            }
        }
        catch(JMSException e)
        {
            System.out.println("JMSException in onMessage() : " + e.toString());
        }
        catch(Throwable t)
        {
            System.out.println("Exception in onMessage() : " + t.getMessage());
        }
    }
   
}

'dev' 카테고리의 다른 글

EUC-kr, Unicode, UTF-8  (1) 2008.08.20
ejb  (0) 2008.08.05
expression pattern  (0) 2008.08.01
script class  (0) 2008.08.01
정규화  (0) 2008.08.01
Posted by 으랏차
,