반응형

소프트웨어 개발 프로젝트뿐만 아니라, 지금 하고 있는 일의 의미와 목적에 대해 생각해 본적이 있는가? 모든 일에는 그 목적을 위해 제때 적절한 사람이 해야 할 일이 있게 마련이다.

시점의 변화

일을 하다 보면 본래 목적과 의미를 잊어버린 채 전혀 엉뚱한 이슈에 많은 시간을 낭비하는 경우가 많다(원래 목적과는 상관없는 이슈로 몇 시간씩 회의해 본 일이 있다면 쉽게 이해할 수 있지 않을까?).

지금 무엇을 하고 있는가?
소프트웨어 프로젝트의 실패 원인 중 대부분은 ‘무엇을 왜 만들고 있는가’라는 고객의 요구사항을 제대로 파악하지 못하는 데 있다. 오픈을 얼마 남기지 않은 프로젝트 중반에 고객의 수정사항은 늘어가고, 고객들은 기능적인 면보다는 화면 디자인(색이나 폰트 사이즈 등)에 더 민감한 모습을 나타낸다. 또한 대부분의 고객은 자신이 원하는 바가 무엇인지를 제대로 알지 못하지만, 고객 자신이 좋아하고 좋아하지 않는 것에 대해서는 정확하게 알고 있다.

성공적인 프로젝트를 위해서는 가장 먼저 고객의 요구사항을 적절하게 파악하는 것이 중요하다. UML 유스 케이스 다이어그램(Use Case Diagram)이나 스토리보드 등이 매우 효율적으로 사용될 수 있으며 간단한 HTML이나 RIA(FLEX etc), 혹은 4GL(VB)로 만든 기능이 없는 스토리 위주의 샘플 애플리케이션은 고객의 요구사항을 수집하는 데 매우 좋은 도구가 될 수 있다. 복잡한 비즈니스 프로세스가 있는 업무의 경우에는 BPA 등의 도구를 사용해 워크플로우를 시각화시켜 두면 복잡한 흐름을 일목요연하게 정리할 수 있다.

이런 도구들로 수집한 요구사항에 대해 고객으로부터 확인을 받아 놓는다면(회의록에 대한 싸인이나 위의 산출물에 대한 고객 확인을 받아 놓는 것) 그 요구사항에 따라 스케줄된 일정에 새로운 변경이 이뤄졌을 경우에 합당한 개발 비용과 시간을 요구할 수 있을 것이다. 이처럼 소프트웨어 개발 프로젝트는 고객의 요구사항만 제대로 파악하고 있어도 80%는 성공한 것이다.

● TAG : 요구사항 분석, Use Case Diagram, sample software, 복잡한 비즈니스 프로세스, 워크플로우, BPA


닭 잡는데 소 잡는 칼 사용하기
모든 기술 역시 그 태생에 목적과 이유를 가지고 있다. 소프트웨어 개발에서 중요한 점 중에 하나가 현재 소프트웨어 개발 규모와 요구사항에 맞게 적절한 기술을 사용하는 것이다. 일반적으로 개발자들은 기술적인 호기심으로 인해 신기술 도입에 대한 욕심이 많다. 또한 매니저는 매니저 나름대로 신기술을 적용한 시스템이라는 ‘업적’을 원한다.

그러나 그런 기술들은 그만큼의 습득 시간과 비용을 요구한다. 예를 들어 몇 년 전 EJB 붐이 일었을 때, 많은 사이트들이 값비싼 WAS를 도입해 개발한 적이 있다. EJB는 분산 컴포넌트와 트랜잭션의 보장이 가장 큰 장점이다. 하지만 정작 이런 사이트들에서 그런 고차원적인 트랜잭션 처리가 필요한 경우는 실제 얼마나 있었을까? 그 당시 많은 사이트들이 고가의 WAS를 도입했음에도 불구하고 WAS를 단순한 JSP/Servlet 컨테이너 수준으로 밖에 사용하지 못했거나, EJB 역시 단순한 POJO식의 Business Object 이상으로 사용하지 못한 경우를 많이 봤다.

요즘처럼 JSTL, WebWork, Struts, Spring, Mapper, AOP 등과 같은 각종 오픈소스 프레임워크와 새로운 개념들이 난무하는 세상에서, 자칫하면 신기술에 대한 욕심으로 무리한 선택을 할 수도 있다. 프레임워크와 기술은 도구일 뿐이다. 아무리 좋은 도구라도 사용법을 제대로 알지 못한다면, 이런 도구들은 오히려 소프트웨어 프로젝트에 해가 될 수 있다.

그러나 한 번 더 생각해 보자. 소프트웨어 개발을 위해서는 시스템 기능과 요구사항에 적합한 적절하고 사용하기 쉬운 기술이 선택되어야 하며, 기술을 도입하는 데는 그만한 도입에 대한 비용(구입 비용뿐만 아니라 교육 및 습득에 대한 비용)이 고려되어야 한다.

● TAG : 오픈소스, 개발자와 매니저의 욕심, 도구는 도구일 뿐, 적절한 도구 사용하기

Simple is best! Easy is best!
모든 기계가 그렇듯이 복잡한 기계일수록 고장이 잘 난다. 소프트웨어도 마찬가지다. 복잡한 아키텍처일수록 문제가 생겼을 경우에 디버깅하기가 어렵고, 변경 사항이 생겼을 때 반영하기가 어렵다. 디자인 패턴으로 무장한 컨설턴트가 와서 시스템을 온통 디자인 패턴으로 도배해 놓을 수도 있다. 그렇지만 그것을 디버깅해야 하는 것은 그 컨설턴트가 아니라 개발자 여러분이다(디자인 패턴이 나쁘다는 것은 아니다. 불필요한 패턴 사용으로 시스템의 복잡도를 올리는 것에 대한 일종의 경고인 셈이다).
가능하면 시스템은 간단하게 설계하자. 고수가 설계한 시스템일수록 단순하고 고장이 적다.

좋은 기술이나 개념은 천재의 머릿속에서 나올 수 있다. 그러나 그것을 실제로 사용하는 사람은 천재가 아니라 평범한 사람들이다. 천재들의 수준에 맞춰 개발된 기술들을 과연 범인들이 쉽게 사용할 수 있을까? 인기 있게 널리 오래 사용되는 기술은 대부분 개념을 이해하기 쉽고 사용하기 쉬운 기술이다. 고수들이 만들어내는 각종 이론들로 무장한 시스템들이 아니라 오히려 이해하기 쉬운 시스템인 것이다.
 
● TAG : 간단할수록 튼튼하다. 쉬운 기술이 널리 퍼진다.

깊게 그리고 넓게
기술을 깊게 공부해야 하는 것은 당연한 이야기이지만, 지식의 깊이와 폭에 대한 균형이 필요하다. 예를 들어 소켓(Socket) 프로그래밍을 할 때, 자바의 경우 Multiplexing 성능이 떨어지기 때문에 C 언어로 MultiPlexer를 구현하고 JNI로 연결하면 훨씬 더 좋은 성능을 낼 수 있다.

화면이 많고 권한 처리가 복잡한 엔터프라이즈 솔루션의 경우에는 엔터프라이즈 포털(Enterprise Portal)과 같은 솔루션을 선택하면 시행착오를 줄이면서 양질의 소프트웨어를 만들어 낼 수 있다. SAP와 CRM 등을 통합할 때 모두가 자신이 강점을 지닌 언어로 개발할 수 있지만, 그보다 EAI 솔루션을 사용한다면 쉽고 성능 좋은 시스템 통합을 이끌어 낼 수 있다. 어느 정도 수준의 개발자라면 고객의 요구사항을 구현하는 것은 그다지 어려운 일은 아닐 것이다. 

그러나 이미 구현되어 있는 솔루션이나 오픈소스 아키텍처에 대한 지식이 있다면 처음부터 구현하는 게 아니라 그것들을 활용함으로써 주어진 시간과 비용 내에서 시행착오를 줄이고 양질의 소프트웨어를 만들어 낼 수 있다.

● TAG : 이미 있는 것들 찾아보기, 활용하기, 폭넓은 지식 가지기

마일스톤
마일스톤이란 ‘이정표’를 의미하는 것으로 프로젝트 일정 가운데 중요시점으로 생각하면 된다. 예를 들어 요구사항 분석 완료 시점, 각각 컴포넌트 완성 시점, 릴리즈 시점, 알파, 베타 테스트 시기 등이 이에 속한다.

마일스톤을 설정하는 이유는 마일스톤 전의 작업에 대한 검증을 통해 발생한 문제를 다음 단계까지 전파시키지 않기 위함이며, 요즘의 개발 방법론들이 잦은 Release와 Feed Back 등을 강조하는 것 역시 잦은 마일스톤을 설정함으로써 문제를 가능한 한 빨리 발견하고 풀어내기 위함이다(불확실성의 제거).

마일스톤이 설정된 주기는 그 각각이 전체 프로젝트에 대한 미니 프로젝트가 되며, 각 마일스톤 시 필요한 최소한의 산출물에 대한 정리와 검증(확인을 통한 문제 발견)이 뒤따라야 한다. 정확한 마일스톤을 설정한다면, 소프트웨어 개발 프로젝트에서 발생할 수 있는 위험 요소를 줄이는 데 큰 도움이 될 것이다.

나만의 도구를 갖추자
소프트웨어 개발에 있어서 IDE나 디버거(Debugger)와 같은 툴은 생산성에 지대한 영향을 준다. 개발자라면 적어도 자기가 가지고 다니는 도구 세트 하나는 있어야 하지 않을까? 여기서는 이미 많이 알려진 IDE나 디버깅(Debugging) 툴보다 소프트웨어 프로젝트에 필요한 각종 자동화 도구에 대해 간략하게 소개한다.

소스 관리
일주일 전에는 잘 돌아가던 모듈인데 일주일 동안의 작업 내용이 반영된 후에는 뭔가 문제가 생겼다. 어떻게 해야 할까? 어느 부분을 수정했는지 알 수 있다면 좀 더 빨리 문제의 원인을 밝혀낼 수 있지 않을까?

운영 중인 시스템이 새로운 코드를 반영한 후에 문제가 생겼다. 수정한 부분을 찾는 것보다 이전의 소스 코드로 원상 복귀하는 것이 운영을 정상화하는 데 더 빠르다. 그렇다면 예전에 개발한 소스는 어디에 있을까?

고객별로 릴리즈한 버전이 많은데 A 고객의 이슈를 해결한 버전은 도대체 어느 버전일까? 여러 사람이 협업 작업을 한다면 공통으로 소스 코드는 어떻게 관리해야 할 것인가? 소스를 한 곳에서 중앙 집중적으로 관리하고 협업을 가능하게 해주며 소스에 대한 변경 내역을 관리할 수 있게 해주는 것이 바로 소스 관리 시스템이다. 널리 사용되고 있는 무료 소스 관리 시스템에는 CVS와 SubVersion이 있다.

● TAG : 소스 관리, CVS, SubVersion
빌드 도구
빌드는 소프트웨어 개발에서 항상 수행해야 하는 작업이다(물론 JSP나 ASP 등의 스크립트 언어로만 개발한다면 모르겠지만). 컴파일하고 기다리고 결과가 나오면 검증하고 에러가 없으면 릴리즈(또는 배포)하고 항상 해야 하는 반복적인 작업이다.
에러가 발생하지 않는 상황이라면 특별하게 할 일도 없다. 우리가 빌드를 기다리는 것은 혹시나 있을지 모르는 에러에 대비하기 위한 것일 뿐 빌드와 배포는 커맨드들을 반복해 입력하는 단순 작업에 해당한다.

이런 단순 작업을 내가 아닌 누군가가 해준다면 그 시간에 코드를 최적화하거나 좋은 소프트웨어 구조로 리펙토링하는 등의 생산적인 작업을 할 수 있지 않을까? 그 누군가가 바로, 빌드 자동화이다. 기본적인 도구로는 make, ant, maven과 같이 커맨드들을 조합해 빌드해 주는 도구가 있고, 여기에 Daily Build (Daily Build의 중요성은 여기서 언급하지 않더라도 『조엘 온 소프트웨어』와 같은 유수의 개발 관련 서적에서 이미 충분히 언급되어 있다)나 스케줄에 따른 빌드나 Release 버전 생성을 자동으로 수행해 주는 빌드 자동화 소프트웨어가 있다. 이러한 소프트웨어들은 빌드 배포에 대한 스케줄링뿐만 아니라, 자동화된 릴리즈 버전 생성 등이 가능하고 빌드 중의 에러에 대해 담당자에게 SMS나 이메일 등으로 ALERT해 주는 기능을 가지고 있으므로, 대규모 협업 프로젝트에서 매우 유용하게 사용될 수 있다.

대표적인 공개 소프트웨어로는 Cruise Control, Ant Hill 등이 있다. Cruise Control은 Text 기반의 도구로 매우 강력한 빌드 자동화 기능을 제공하지만 Text 기반이기 때문에 다소 사용하기가 어렵다. Ant hill의 경우에는 Web UI를 제공하기 때문에 상대적으로 사용하기는 쉽지만 대신 Cruise Control과 같은 강력한 기능을 제공하기는 어렵다. 그리고 빌드 과정 중에 빌드된 컴포넌트가 제대로 작동하는지 여부를 자동으로 테스트하기 위해 JUnit과 같은 테스트 프레임워크가 있다. 빌드 자동화는 ant와 같은 빌드 도구, JUnit과 같은 테스트 도구, 그리고 Cruise Control 등과 같은 빌드 자동화 도구로 구성되고, 빌드와 릴리즈(배포)를 수행함으로써 시간 절약은 물론, 품질에 대한 향상까지 기대할 수 있다.

● TAG : ant, maven, cruise control, anthill, JUnit

테스트 도구
테스트와 QA(Quality Assurance)에 대한 중요성은 언급하지 않더라도 이미 잘 인식하고 있으리라 믿는다. 테스트의 종류에는 컴포넌트별 단위 테스트, 시나리오에 따른 기능 테스트, 적절한 용량을 커버하고 성능을 낼 수 있는지를 검증하는 부하 테스트, 그리고 장애에 대한 대처 능력을 시험하는 장애 테스트 등이 있다.
그 중에서 테스트팀이나 QA팀이 아닌 개발자들이 일반적으로 수행할 수 있는 테스트로는 컴포넌트별 단위 테스트와 부하 테스트를 꼽을 수 있다. 단위 테스트는 소프트웨어 개발 주기 중 테스트 단계에서뿐만이 아니라 개발단계 중간에서도 컴포넌트가 제작될 때마다 모듈의 기능을 검증해 버그를 예방할 수 있다.

단위 테스트는 JUnit 등의 단위 테스트를 자동화할 수 있으며, 위에서 설명한 것처럼 빌드 절차에 자동화해 포함시켜 빌드 때마다 개발한 컴포넌트 기능의 이상 여부를 검증할 수 있다. 부하 테스트는 소프트웨어의 비기능적인 요소인 성능이나 용량을 측정하는 데 사용되는데 개발자 단계의 부하 테스트를 통해 알고리즘의 최적화 여부나 과부하시에 장애(Dead Lock, Lock 대기 현상, CPU 과점유)를 검증할 수 있다.
 

● TAG : 단위 테스트, 기능 테스트, 성능 테스트, JUnit, MS Stress, JMeter, Load Runner

커뮤니케이션 도구
팀원 간의 작업 배분, 이슈에 대한 기술적인 토론, 고객의 변경된 요구사항의 반영 내용, 그리고 버그에 대한 이슈 등을 해결해 나가는 것이 개발이라고 할 수 있다. 보통 이런 이슈들은 회의나 이메일, 전화 등을 통해 이뤄지는데, 각각의 이슈에 대해 서로 이야기한 내용과 협의한 내용들에 대해 문서화해 추후에 이슈에 대해 어떻게 대처를 하고 소프트웨어 코드에 반영을 했는지를 추적해야 할 경우가 있다.

이런 이슈에 대한 추적과 커뮤니케이션을 가능하게 해주는 것이 Issue Tracking 시스템이다. 이슈를 오픈하고 관련된 사람을 추가하며 이슈에 대한 진행 상황과 중요도를 기입함으로써 현재 시스템에 대한 이슈에는 어떤 것이 있는지 알려주고, 진행되고 있는 상태를 쉽게 관리할 수 있게 해준다.

해당 이슈가 반영된 버전을 기입해 어느 버전에 문제가 있고 해결되었는지를 판단하게 함으로써 적절한 Release 버전을 판단할 수 있게 한다. 그리고 이슈가 해결되었을 때는 SubVersion과 같은 소스 관리 시스템에서 이슈를 해결한 Revision Number를 Issue Tracking 시스템에 기입해 이슈를 어떤 식으로 소스 코드에 반영했는지를 추적할 수 있게 한다. 주로 버그 관리에 많이 사용되는데, 그 외의 개발 요구사항을 반영하는 데도 응용할 수 있다. 대표적인 소프트웨어로는 BugZilla나 JIRA와 같은 소프트웨어를 들 수 있다.


● TAG : Issue Tracking System, Bugzilla, JIRA

튼튼한 소프트웨어를 만드는 관점 갖추기
마지막으로 실제 개발을 할 때 생각해야 할 것 몇 가지만을 짚고 넘어가도록 하자.

개발자는 메모리로부터 자유로울 수 없다.
자바 언어가 나오면서 개발자들은 malloc과 free(C 언어에서 메모리를 할당하는 함수)로부터 자유로워졌다. Garbage Collector라는 강력한 기능이 자동으로 메모리를 관리해 준다. 그렇다면 진정 우린 메모리에서 자유로울 수 있을까?

32비트 머신을 가정했을 때, 32비트 머신의 프로세스당 메모리 사용량은 2^32=4G이다. 여기서 유닉스의 경우는 이 가운데 2GB가 공유 메모리 영역이고 실제 자바가 사용할 수 있는 것은 2GB이다. 이 중에서 클래스(Class)나 메소드(Method)가 올라가는 Perm 영역이 대략 64~256MB이고, JVM 자체가 로딩되는 메모리 영역을 다 합하면 실제로 자바 애플리케이션의 Heap Size는 최대 1.5~1.7GB 내외이다.

그 영역 안에서도 애플리케이션이 기본적으로 꾸준히 사용되는 영역이 400~500MB라면, 일반적으로 1GB 내에서 프로그래밍을 해야 한다는 이야기다.
물론 64비트 JVM이 출시되고 GC의 성능이 좋아지면서 메모리에 대한 부담이 줄어드는 것은 사실이지만, 소프트웨어를 개발한다면 항상 메모리 사용에 대해 충분히 고려하고 시스템을 설계해야 한다.

● TAG : 메모리 신경 쓰기

성능에 대해 고민하기
성능에 대해 개발자들만큼 민감한 사람들이 또 있을까? 그렇지만 그들이 작성한 코드는 정말 최적의 성능을 낼 수 있도록 구현되어 있을까? 직접 작성한 코드를 한 두명의 유저 기반에서 테스트했을 때는 잘 돌아간다. 그렇지만 실제 환경이 여러 명의 동시 접속자를 지원하는 서비스라면? SQL 문장이나 잘못된 Synchronized 처리 등은 실제 환경이나 동시 사용자가 많지 않으면 성능에 영향을 주지 않는다.

성능에 대한 좋은 가이드와 프로그래밍 방법은 항상 고민해야 하는 문제이고, 여기에 성능에 대한 검증을 어떻게 할 것인지를 고려해 코드를 만들어야 할 것이다.
컴포넌트별로 테스트시에 Profiler나 APM-Application Performance Management Tool 등을 이용하면 도움이 되고 간단한 부하 테스트를 더한다면 성능 상에 문제가 있는 코드가 아닌지 좀 더 쉽게 검증할 수 있다. 물론 어느 정도 경험이 쌓인다면 이런 도구 없이도 코딩 시에 성능에 문제가 되는 코드의 대부분은 걸러낼 수 있을 것이다.


● TAG : 성능 고려하기, 성능 테스트, Profiler, APM
로깅 습관화
결함(Defect)이 없는 소프트웨어는 있을지 몰라도 문제(Bug)가 없는 소프트웨어는 존재하지 않는다. 그러므로 문제가 있을 때 어떻게 빨리 발견해 해결할 것인지가 중요하다. 그런 방법 중의 하나가 디버깅을 위한 로깅(LOGGING) 처리이다. 로깅은 장애나 문제가 없을 때는 그다지 소용 없는 코드이고 시간 낭비처럼 보일 수 있지만 잘 구현된 로그인(LOGGIN)은 문제 해결 시간을 줄이는 데 큰 도움을 준다. 그렇다고 로깅이 범람해서는 안 된다. 로그(LOG) 메시지도 비즈니스 로직처럼 설계가 필요하다. 적절한 로깅 설계와 반영에 대해 습관을 들이도록 하자.

● TAG : Logging, Debugging

반응형
 
출처 Don't Worry~ Be Happy!! | 바간나
원문 http://blog.naver.com/swucs/40003944891

MVC 이해하기

Duncan Mills 지음

Oracle9i JDeveloper가 Model-View-Controller 디자인 패턴 기반 J2EE 애플리케이션의 구축을 돕는 방법

필자는 여러분들이 내년 이후에는 MVC (Model-View-Controller) 패턴이라 불리는 디자인 패턴에 대하여 훨씬 더 많이 접하게 될 것이라고 확신합니다. 이번 기고는 그런 의미에서 MVC의 정확한 정의와 언제, 어디서 그리고 어떻게 Oracle9i JDeveloper를 사용하여 MVC 애플리케이션을 구축할 수 있는 지에 대하여 설명할 수 있는 좋은 기회라고 생각합니다.


이번 기고에서는 우선 디자인 패턴부터 소개를 하고, 그 후에 MVC에 대하여 자세하게 살펴보도록 하겠습니다.

필수 용어

J2EE 애플리케이션 디자인을 다룰 때 극복해야 할 첫번째 과제는 새로운 용어들과 특정 소프트웨어 형식 관련 전문 용어들입니다. 그래서 이번 기고에서는 자주 다루게 될 J2EE 관련 중요 개념들을 먼저 살펴본 후 본 내용을 다루도록 하겠습니다

디자인 패턴

"디자인 패턴"이라는 용어가 요즘 매우 많이 사용되고 있지만 여기에서 다루고자 하는 것은 프로그래밍 문제 해결을 위한 증명된 시도 방법에 대해서 입니다. 불행하게도 그 용어는 그 동안 남용되어 오면서, 그 자신이 목적이 되곤 했습니다. 소프트웨어는 디자인 패턴에 기반을 둘 수 있고, 그러한 경우에는 소프트웨어의 개발이 더 용이해질 수도 있지만, 소프트웨어를 구입하거나 디자인하는 경우에 기능보다 디자인 패턴을 더 중요하게 여긴다면 역효과를 낼 수도 있습니다.

"이것이 과연 디자인 패턴을 통해 나를 도울 수 있습니까?"라고 질문하면서 개발 환경을 지켜보는 것은 그리 생산적인 행동은 아닙니다. 디자인 패턴이라는 것은 목적에 대한 수단이 되어야지 결코 그 자체가 목적이 되어서는 안되기 때문입니다. 개발 툴을 평가할 때 할 수 있는 의미 있는 질문은 아마도 "애플리케이션 구축을 위해 이 환경이 얼마나 많은 것을 제공할 수 있습니까?" 정도가 될 것입니다.

다음은 고려해야 할 중요한 사항들입니다:

  • 디자인 패턴은 비즈니스 문제들을 해결하기 보다는 특정 프로그래밍 과제들을 해결합니다.
  • 디자인 패턴은 실제 구현보다도 가이드라인을 제공합니다.
  • 디자인 패턴은 재사용이 가능합니다
  • 디자인 패턴은 입증된 트랙 기록을 갖고 있습니다.
현재 웹 상에는 디자인 패턴과 관련된 다양한 정보들이 존재하고 있는데 그 중에서도 Sun Java Blueprints site 사이트는 반드시 확인해 보시기 바랍니다.

MVC

MVC는 디자인 패턴 중의 하나로 프로그래밍 세계에서는 전혀 새로울 것이 없습니다. Smalltalk 등과 같은 객체형 프로그래밍의 초기 시대 부터 Java Swing 컴포넌트 집합의 기초를 제공하는 최근까지 디자인 패턴으로서의 MVC 사용과 관련된 많은 레퍼런스들이 존재하고 있습니다. 그런데 MVC가 다시 주목을 받게 된 이유는 그것의 패턴이 웹 기반 애플리케이션 구축 시 발생하는 기본 문제들 중의 대다수를 해결하는데 적합하다는 것을 깨달았기 때문입니다.


일반적인 데이타베이스-중심 애플리케이션들과 특정한 웹 기반의 씬-클라이언트 애플리케이션들을 살펴보면 애플리케이션이 여러 가지 구분되는 작업들을 수행해야 한다는 것을 발견할 수 있습니다:

  • 데이타 액세스
  • 비즈니스 로직 구현
  • 사용자 인터페이스 표시 (데이타 프리젠테이션)
  • 사용자 상호 작용
  • 애플리케이션 (페이지) 플로우
MVC 아키텍처 또는 패턴은 사용자가 사용자 인터페이스를 재작성하지 않고 다른 데이타 소스에서 애플리케이션으로 용이하게 플러그할 수 있도록 데이타 프리젠테이션 등과 같은 작업들을 데이타 액세스로부터 분리시켜야만 한다는 전제 조건을 갖고 이러한 작업들의 구분 방식을 제공하고 있습니다.

지금은 MVC를 여기까지만 살펴보고 나중에 다시 자세하게 살펴보도록 하겠습니다.

JSP Model 1과 Model 2

진행을 하기에 앞서 JSP Model 1와 Model 2 아키텍처에 대해 잠시 살펴보도록 하겠습니다. 현재 많은 씬-클라이언트 웹 애플리케이션들은 사용자 인터페이스의 디스플레이 처리 방법으로 JSP (JavaServer Page) 를 사용하고 있습니다. 특히 복잡한 애플리케이션에서 여러 페이지를 함께 연결할 때 사용자는 하나 이상의 "루트"를 갖는 페이지들 사이에서 플로우를 어떻게 제어해야 할지 결정을 해야 합니다. 이러한 문제의 경우 JSP는 두 개의 시도 방법을 갖고 있습니다: Model 1 아키텍처와 Model 2 아키텍처

Model 1 아키텍처 ( 그림 1) 는 페이지들 사이의 라우팅을 JSP 자체에게 맡깁니다. 그래서 JSP는 하드코딩된 링크를 갖고 있거나 또는 페이지 내에 내장된 이동 (navigation) 로직을 갖고 있어야 합니다. 그러나 이 전략은 하나의 문제를 해결함과 동시에 또 다른 문제를 야기시키는데, 그것은 프리젠테이션과 이동 로직이 섞여있어서 페이지를 쉽게 재사용할 수 없기 때문입니다. 심지어 JSP Model 1를 사용하는 간단한 페이지 플로우들도 쉽게 복잡해질 수 있기 때문에 각각의 페이지는 그것이 링크된 다른 페이지들을 모두 알고 있어야만 합니다.

Model 1 architecture

JSP Model 2 (그림 2) 는 이 문제에 대한 해결 방법입니다. Model 2 아키텍처의 경우에 페이지 플로우는 JSP에 의해 처리되지 않습니다. 오히려 아키텍처는 페이지와 함께 제출된 정보를 기반으로 하여 분리된 서블릿이 라우팅을 결정하도록 해주어서 페이지들은 논리적으로 링크된 다른 페이지들을 인식할 필요가 없습니다. 이 서블릿은 Controller라고 호칭되는데, 이것은 이번 기고의 원래 주제인 MVC 패턴과 밀접한 관계를 맺고 있습니다.

JSP Model 2

MVC의 상세 내용

MVC는 애플리케이션을 Model, View 그리고 Controller라고 호칭 되는 세 가지 레이어 또는 기능 영역으로 논리적 방법을 통해 분리한 것입니다.


MVC와 같은 디자인 패턴은 각기 다른 레벨로 적용될 수 있는데, MVC는 애플리케이션 구축 방법으로서 뿐만 아니라 컴포넌트 레벨에서도 유용한 전략으로 활용될 수 있습니다. 잠시 JSP와 웹으로부터 벗어나서 Java Swing 리스트 박스 (이것은 단순한 컴포넌트이지만 훨씬 더 지역화된 범위에서 세 가지의 MVC 요소들을 모두 보유하고 있습니다) 와 같은 것을 고려해 보는 것도 좋습니다.


그럼 이제부터 세 가지 레벨을 한번 살펴보도록 하겠습니다:

The Model

Model은 애플리케이션 데이타 및 비즈니스 로직의 저장소 (repository) 입니다. 그러나 Model이 데이타베이스를 표시한 것이라고 말하는 것은 너무나 극단적인 표현입니다. 일반적으로 Model 기능의 일부는 데이타베이스 기반 애플리케이션을 통해 데이타를 데이타베이스로부터 읽어 들이거나 또는 데이타를 데이타베이스에 지속시키는 것입니다. View의 데이타 액세스는 데이타를 드러내거나 또는 View를 통해 입력된 데이타를 입증 및 소모하기 위해 비즈니스 로직 레이어를 구현하는 작업도 필요로 하고 있습니다.


애플리케이션 레벨에서 Model은 표시하는 사용자 인터페이스와 표시되는 비즈니스 데이타 사이의 입증 및 추상화 레이어 처럼 동작을 합니다. 데이타베이스 서버 자체는 단순히 Model을 위한 퍼시스턴스 (persistence) 레이어 입니다.

The View

View는 Model 데이타를 렌더링하는 것과 관련이 있습니다. 이제부터는 View를 렌더링하는데 사용되는 다른 기술들에 대하여 살펴볼 예정인데, 보통은 JSP 페이지를 사용하고 있습니다. 여기에서 주목해야 할 것은 View 코드는 사용자 역할에 따라 조건부 데이타 표시와 같은 작업들의 수행 로직을 포함할 수는 있지만 View 코드 자체가 애플리케이션이나 이동 로직을 하드코딩하지는 않는다는 것입니다. 최종 사용자가 View로부터 렌더링 되는 HTML 페이지 내의 동작을 수행할 때에는 이벤트가 Controller에게 제출이 되고 그 다음에 무엇을 할 것인지는 전적으로 Controller에게 달려 있습니다.

The Controller

Controller는 이름이 나타내는 것과 같이 전체 패턴의 연결 고리 입니다. View에서 수행된 모든 사용자 동작은 브라우저의 요청 컨텐트에 기반을 두고 프로그래밍 또는 메타데이타에 결합되어 다음에 무엇을 할지를 결정하는 Controller를 통해 제출됩니다.


Controller들은 여러 가지 다른 방식으로 운영될 수 있습니다. 어떤 것들은 요청들을 정확한 코드로 라우팅하기 위해 URL 인수들을 사용할 것이고 다른 것들은 요청이 제출되어 지는 페이지를 참작할 것입니다. 또한 그 외의 것들은 페이지 제출 시에 무엇을 할지를 알아내기 위해 숨겨진 필드들을 사용할 수도 있습니다. 그런데 MVC 디자인 패턴 자체는 Controller가 어떻게 동작하는지 그리고 단순히 그것의 기능이 무엇인지에 대해 규정을 하고 있지는 않습니다.


Controller는 여러 가지 구분되는 프로세스들 또는 서블릿들로 만들어져서 전체 Controller 기능의 다른 측면들을 처리할 수 있습니다. 예를 들면, 사용자는 하나의 서블릿이 제어하는 페이지 플로우 컨트롤과 또 다른 서블릿이 제어하는 사용자 인터페이스 이벤트 컨트롤 등과 함께 정확한 하위 Controller에게 디스패치하는 마스터 Controller를 가질 수 있습니다. 이 개념 자체는 다른 디자인 패턴의 주제이기도 합니다: Front-Controller 패턴

웹 애플리케이션에서의 작동 방식

MVC 패턴은 요약이 가능하여 그림3 에서와 같이 다이어그램을 사용하는 전통적인 씬-클라이언트 웹 애플리케이션에 적용 시킬 수 있습니다.

MVC pattern

이와 같은 웹 애플리케이션에 있어서 명심해야 할 것은 모든 것들은 페이지를 제출하는 사용자에 대한 응답을 통해 발생한다는 것입니다. "다음 10 개의 레코드를 표시하십시오" 등과 같은 간단한 UI 작업은 Controller에 대한 클라이언트 브라우저의 요청에 의해 구현되어 더 많은 데이타가 필요하다는 것을 Model에게 가르쳐 준 후 원래 페이지를 다시 표시할 것입니다. 그런 후에 View가 Model로부터 데이타를 요청할 때에는 다음 10 개의 행들이 표시 가능하게 될 것입니다.

지금까지는 모든 것이 매우 간단하게 보였고, 언급된 세 개의 부분 모두 명확하게 설명되었습니다:

  1. View를 이루는 개별 페이지들은 다른 페이지들에 대한 관계를 알지 못합니다. 그것들은 단지 "이벤트"를 발생시키고 Controller는 정확한 일이 발생하였다는 것을 보증해 줍니다.
  2. View와 Controller는 기본이 되는 데이타 구조 또는 비즈니스 규칙에 대해서 알지도 못 하고 구현도 하지 않습니다. 그것들은 단순히 Model의 공용 API들을 사용 (consume) 할 뿐입니다

  3. Model은 View나 Controller Model에 대해서 아무 것도 알지 못 합니다.
MVC 패턴의 본질은 장소에 구애 받음이 없이 애플리케이션의 View와 Model 부분의 재사용을 간편하게 만드는 것이고, 애플리케이션 자체는 본래 데이타 소스와 UI를 의미있는 방식으로 결합하는 Controller 부분에 의해 표현이 됩니다.

마찬가지로, 이러한 논리적 분리는 사용자가 나머지 애플리케이션을 다시 작성하지 않고 선택하는 기술을 특정 레이어를 위해 변경할 수 있게 해줍니다. 예를 들면, View의 구현을 위해 JSP를 사용하여 애플리케이션을 프로토타입할 수 있지만, XML 메타데이타-방식 서블릿을 통해 이후에 View를 바꾸어 놓는 것도 가능합니다. 이 작업은 Model을 변경함이 없이 Controller을 최소한만 변경하여 이행할 수 있습니다. (Controller에 있어서 이러한 경향은 나중에 다시 한번 자세히 살펴보겠지만 Jakarta Struts와 같은 메타데이타-방식 Controller 프레임워크를 사용하기 위함입니다. 그러므로 View를 교환할 때는 Controller 로직 자체가 아니라 그 메타데이타만 변경하면 됩니다.)

한번 MVC-기반 애플리케이션의 구현을 시작하게 되면 모든 것들이 보다 복잡해 지고, 프로그래머들은 레이어들 사이에서 논리적인 분리를 계속 유지하기 위해서는 교육을 받아야 합니다. 예를 들어 링크를 하드코딩해서 네비게이션 로직을 JSP로 코딩하는 것이 좋게 보일 수는 있지만, 만약 이것이 구현되면 페이지는 더 이상 사용할 수 없게 될 것입니다.

레이어들 사이의 라인들이 뚜렷하지 않다는 것도 반드시 인식하고 있어야 합니다. 예를 들면 데이타가 페이지로부터 제출될 때 Struts와 같은 Controller 프레임워크는 JavaBean을 데이타로 채우고 계속 진행하기 위해 빈을 Controller로 전달할 수도 있습니다. 그러면 지금의 그 빈은 View의 일부입니까 아니면 Controller의 일부입니까? 솔직히 말씀드리면 어느 버킷 (bucket) 을 선택해서 그것을 위치시키는가 하는 문제는 그리 중요한 것이 아닙니다. 중요 컴포넌트들 사이에서는 항상 인터페이스 레이어들이 존재할 것입니다. 여기에서 중요하게 여겨야 할 것은 올바른 장소에 정확한 종류의 로직을 유지해야 하는 것입니다.

실제 상황에서의 MVC

앞에서는 MVC의 이론을 살펴보았고, 지금부터는 J2EE가 포함하고 있는 기술들과 MVC 기반 애플리케이션 구축 과정을 도울 수 있는 Oracle9i JDeveloper에 대하여 살펴보도록 하겠습니다.
MVC의 각 부분에 대하여 역할 완수를 위해 사용자가 채택할 수 있는 선택 가능한 구현 방법들이 있는데, 당연히 앞으로 언급할 옵션들은 소모적인 것이 아니라 가능성의 일부를 설명해 주는 것들입니다. 뿐만 아니라 일부 솔루션들은 하나 이상의 MVC 부분도 포함할 것입니다.

실제 상황에서의 Model

Model의 역할은 애플리케이션 기능과 관련된 애플리케이션 데이타와 비즈니스 로직을 처리하는 것이라고 알려져 있는데, Model은 단지 여러 레이어로 이루어져서 다음과 같은 작업을 처리하는 것이라고 생각하면 됩니다:

  • 비즈니스 객체 지속
  • 데이타 액세스
  • 비즈니스 서비스 제공
Enterprise JavaBeans (EJB), JDBC, Web Services, 그리고 Oracle9iAS TopLink 등과 같은 기술들은 모두 Oracle9i JDeveloper Business Components for Java (BC4J)와 같은 완벽한 Model 프레임워크를 통해 가장 생산적인 솔루션을 제공하면서 이러한 작업들의 전부 또는 일부를 이행할 수 있는데, 이 프레임워크는 Model 레이어들의 생산을 위해 선언적인 메타데이타-방식의 시도 방법을 제공하면서 프로그래머들이 로우-레벨 보다는 그들의 코드를 통해 비즈니스 요구 사항들의 해결에 집중할 수 있도록 해줍니다. 그리고 BC4J는 지속성을 위해 EJB를 사용하는 것과 같이 다양한 Model 레이어 작업들을 위해 각각 다른 구현 방법을 사용할 수 있는 유연성을 프로그래머에게 제공하고 있습니다. BC4J와 같은 프레임워크를 사용하게 되면 Model의 사용자들은 (View 개발자들) 데이타 액세스 및 비즈니스 로직 구현에 대한 세부 사항들을 정확하게 알 수가 없고 그들은 단지 추상적 레이어만을 제공 받게 됩니다.

이번 기고에서는 Model 레이어 구현의 다양한 시도 방법에 대한 상대적 장점에 대해서 많은 부분을 할애하지는 않을 것입니다. 그런데 여기에서 알아야 할 것은 JDeveloper는 Model 내에서 사용자들이 비즈니스 프로세스 프로그래밍 로직에 집중할 수 있게 해주면서 이러한 Model 레이어들을 사용하는 기본 작업을 보다 쉽게 만들 수 있는 툴들을 제공한다는 것입니다.

예를 들어 사용자는 JDeveloper를 통해 데이타베이스를 찾고 Model의 기본으로 사용할 데이타베이스 테이블들을 선택한 다음 그 테이블들을 직접 UML 클래스 다이어그램으로 끌어놓을 수 있습니다.

JDeveloper는 사용자가 기본 위저드와 등록 정보 편집기 등을 통해 Model 레이어를 생성할 수 있게 해줍니다. 그리고 동일한 방식으로 기본적인 EJB와 BC4J 클래스들, 메타데이타 그리고 전개 기술자 (deployment descriptor) 등의 정의 작업을 수행할 수도 있습니다. BC4J의 경우에는 사용자가 등록 정보 편집기를 통해 검증과 재사용 가능한 비즈니스 규칙들을 그들 자신의 모델로 적용할 수 있는 능력을 얻게 됩니다.

실제 상황에서의 View

일반적으로 사용자 인터페이스를 생성할 때 어떠한 기술을 선택해야할 지 무척 당황해 할 수 있습니다. 다음은 선택 가능한 세 가지 시도 방법입니다:

  • HTML 페이지 생성 Java 코드를 작성하는 Java 서블릿
  • 기본적으로는 HTML 페이지를 생성하지만 Model로부터 데이타를 가져오는 것과 같은 기능들을 수행할 때에는 특별한 태그 또는 스크립틀릿 (scriptlet) 들을 내장하는 JSP
  • 메타데이타-방식 또는 템플릿-방식 프레임워크
Java 서블릿 방식은 Java 프로그래머들이 많이 선호하는 방식으로서 View-구축에 필요한 요구 사항들을 해결합니다. 모든 것은 프로그램 문자열 내에 내장된 HTML 요소들을 통해 코드 내에서 즉시 처리되어야만 하는데, 이와 같은 방식은 복잡하고 오류가 많이 발생합니다.

현재 View 생성에 있어서 가장 유명한 기술은 JSP 방식인데, 이것은 페이지를 위한 기본적인 사용자 인터페이스 디자인 툴인 Macromedia Dreamweaver 등과 같은 표준 HTML 편집 툴들의 사용 능력과 그 페이지의 작업 방식과 데이타 바인딩을 처리하기 위한 Java 코드의 내장 능력을 함께 결합한 것입니다. JSP가 최초로 실행이 될 때에는 서블릿으로 컴파일이 되는데, HTML 내에 Java 스니핏 (snippet) 들을 내장하는 방식은 Java 내에 HTML을 내장하는 반대 프로세스보다 훨씬 더 생산적이라고 판명되었습니다

JSP들은 그것들 내부로 직접 내장되는 Java 코드의 세그먼트들을 갖거나 또는 커스텀JSP 태그들을 사용할 수 있습니다. 태그들은 컴파일 시에 즉시 배치되는 Java 코드를 위한 위치 표시자 (placeholder) 인데 그것들의 재사용이 가능하도록 해주는 API와 함께 디자인되어 사용되고 있습니다. 그래서 프로그래머는 기본적 구현에 대한 지식을 보유하지 않고도 애플리케이션 내에서 태그 라이브러리를 재사용이 가능한 컴포넌트들의 라이브러리처럼 사용할 수 있습니다.

JSP와 JSP 태그 라이브러리들을 결합하는 것은 View 개발자들에게 막대한 능력을 제공함과 동시에 많은 문제점을 발생시킬 수도 있습니다. 그러한 태그 라이브러리들은 BC4J-기반 모델에 대한 간편한 액세스 기능을 제공하는 Oracle BC4J 태그 등과 같은 특별 라이브러리들부터 Struts 태그 라이브러리와 Java Standard Tag Library (JSTL) 등과 같은 보다 일반적인 "개방형 소스" 라이브러리들 까지 존재합니다.

프로젝트를 위해 정확한 태그 라이브러리를 선택하는 것은 어려운 일이지만 사용을 위해 선택한 다른 기술들이 그 선택 과정을 이끄는 경우도 가끔 발생할 것입니다. 예를 들어, Model 프레임워크로 BC4J를 사용하고 Controller로 Struts를 사용하기로 결정한 경우 JDeveloper는 사용자에게 이러한 조합을 위해 맞추어진 표준 태그 라이브러리들의 집합을 제공할 것입니다

View 개발의 마지막 방식은 템플릿-방식 또는 메타데이타-방식 프레임워크를 사용하는 것입니다. 이것의 관련 예는 XML을 선언적으로 사용하여 페이지 정의 방식을 제공하는 JDeveloper 내의 UIX 프레임워크입니다.

Jakarta Velocity 프로젝트는 또 다른 템플릿-기반 방식을 제공하는데 Velocity는 XML-방식 보다는 Velocity가 대신하는 템플릿 HTML 페이지 내의 특별 마크업을 사용하여 오히려 JSP 페이지처럼 동작합니다.

이와 같은 메타데이타-방식의 시도 방법은 서블릿 또는 JSP를 사용하는 것보다 많은 이점을 제공할 수 있습니다. 우선, 프레임워크는 UI가 무슨 일을 하고 그것이 어떻게 보이는지를 정의하지만 그것이 어떻게 구현되는지는 정의하지 않기 때문에 다른 장비로부터 출력을 얻어낼 때 동일한 메타데이타 정의를 사용할 수 있습니다 (예를 들면 브라우저 클라이언트로부터 HTML 출력을 얻어내거나 또는 전화로부터 WML 출력을 얻어낼 때). 두 번째는 메타데이타의 선언적 성격이 그것 자신에게 정의 뿐만 아니라 시각 편집기의 사용도 추가해 주고 있습니다. 사용자 인터페이스의 포인트-앤드-클릭 편집은 J2EE 개발자의 생산성을 증가시키기 위한 필수 사항 중 하나입니다.

실제 상황에서의 Controller

웹 기반 애플리케이션에 적용되었을 때 Controller 기술이 모든 MVC 컴포넌트들 사이에서 가장 완성된 것은 아닙니다. 이것은 Controller와 View가 혼합된 JSP Model 1 방식이 지금까지 광범위하게 사용되어 왔기 때문입니다. Controller들이 계속 사용되어 온 경우 Controller들은 자체 내에서 성장하면서 Jakarta Struts Controller 프레임워크에 기반을 두는 경향도 갖고 있습니다.

Struts Controller는 매우 유명한 JSP 애플리케이션 구축용 Controller 프레임워크로서 핵심 Controller 기능을 포함하면서 View 레이어를 위해 많은 태그들을 제공하고 있습니다. Struts는 개별 페이지들을 위해 위치 추상화 (abstraction) 를 정의하는 XML 메타데이타와 페이지들 사이의 실제 흐름을 제어하기 위한 프로그래밍 코드의 혼합된 형태를 사용하고 있습니다. 보다 자세한 정보는 Apache 웹 사이트에서 확인해 보시기 바랍니다.

JDeveloper는 Struts 구성 파일 편집기 그리고 단순 Struts 또는 Struts, BC4J, JSP UIX 등의 조합을 사용하는 애플리케이션들의 구축을 보다 간편하게 만들어 주는 위저드와 JSP 태그들의 집합을 제공하여 Controller로서의 Struts 사용을 완벽하게 지원하고 있습니다.

MVC의 미래

이번 기고의 시작 부분에서 잠시 언급했던 것처럼, MVC는 점차 발전하면서 J2EE 애플리케이션들을 위해 선택 가능한 개발 방법론이 되어가고 있습니다. 우리는 현재 매우 흥미로운 개발들이 (특히 View 및 Controller 분야에서) 진행 중인 것을 보고 있습니다.


Controller 측면에서 Struts 버전 1.1은 베타 상태에 있는데, 이 새로운 버전은 Struts 1.0 보다 훨씬 더 유연하고 또한 많은 일을 처리할 수 있습니다. 또한 페이지 플로우 정의 작업을 더욱 선언적으로 만들면서 단순 페이지 플로우 이상으로 Controller의 범위를 확대하는 Controller를 통해 보다 많은 일이 수행되고 있습니다.


페이지 플로우의 시각 모델링이 첫번째 단계인데, 많은 벤더들은 현재 이 기술의 작업을 계속 진행하고 있습니다. 그러나 개발자들이 웹 배치를 위해 보다 복잡한 시스템을 구축하기 시작하면서 단순 페이지 플로우 보다는 전체 프로세스들을 모델링하는 능력에 대한 요구가 점차 증가하고 있습니다. 이와 같은 프로세스들 중에서 페이지를 최종 사용자에게 보여주는 것은 플로우를 위해 가능한 여러 작업들 중의 하나일 뿐입니다. Oracle은 매우 가까운 미래에 이와 같은 종류의 Controller 기술을 통해 흥미로운 개발들을 제공할 예정입니다!

View 측면에서 커다란 변화는 JavaServer Faces (JSF) 입니다. 이것의 목적은 HTML을 위해 J2EE를 사용하여 그래픽 인터페이스의 새로운 표준을 개발하는 것이고, 개념은 표준 UI 요소들과 이벤트 핸들링을 통해 Swing GUI Toolkit과 같은 것을 HTML을 위해 제공하는 것입니다.

Oracle은 Java Community Process (JSR 127) 의 일부로 정의되고 있는 이 과제의 정의 프로세스에 참여하고 있고, 표준 작업이 종료되자 마자 JSF를 구현할 예정입니다. JSF에 대한 보다 자세한 사항은 http://www.jcp.org/en/jsr/detail?id=127을 확인해 보시기 바랍니다.

JavaServer Faces는 MVC의 Model에 영향을 미치고 있습니다. 표준화된 GUI 툴킷이라는 것은 View와 Model 사이의 상호 작용이 Model 데이타를 특정 필드 또는 컨트롤로 바인딩을 해서 표준화가 될 수 있다는 것을 의미합니다. 그래서 필자는 J2EE 개발을 위한 패턴으로서 MVC를 사용하는 흥미로운 단계에 접어들었다고 믿고 있습니다. 이 기술은 이제 막 성숙한 단계에 접어들었고 또한 표준화 작업도 함께 이루어지고 있습니다.


비즈니스 프로그래머들의 작업을 간편하게 해줄 수 있는 시각적 모델러와 UI 편집기를 통해 코드-중심의 애플리케이션 구축으로부터 선언적 컴포넌트 어셈블리로 현재 이동하고 있고, 보다 많은 프레임워크들은 J2EE-관련 표준 및 디자인 패턴의 구현을 위해 통합되어서, 프로그래머들은 코드 작성보다는 비즈니스 문제 해결에 좀 더 집중할 수 있게 되었습니다.

Duncan Mills는 Oracle Application Development Tools 그룹의 제품 관리자로서 Oracle9i JDeveloper와 Oracle9i Forms의 전문가인데 1988년 이후로 DBA 또는 개발자로서 Oracle 제품을 담당해 왔습니다. 

반응형
쉘 프로그래밍 강좌

  • 참고서적: 초보자용 리눅스 프로그래밍 (대림출판사, 한동훈,이만용역, NEIL MATTHEW, RICHARD STONES 저)
  • 넷츠고 리눅스 동호회 7월 제 5회 정기 공개강좌 자료
  • 글쓴이: 위경섭 <powerhack@netsgo.com>
  • 위키문서 변환: 윤현호 <hhyoon@kldp.org> 2005년 2월 23일. 원본: http://wiki.kldp.org/KoreanDoc/Shell_Programming-KLDP


1 변수

  • 쉘변수는 처음 사용될 때 만들어진다. 즉 미리 선언할 필요가 없다.
  • 쉘변수는 유닉스 명령과 마찬가지로 대소문자에 구별이 있다.
  • 쉘변수는 기본적으로 데이터를 문자열로 저장한다. 수치를 대입해도 실제 수치가 아닌 문자열이 저장된다. 계산이 필요할 경우는 자동으로 수치로 변환하여 계산후 다시 문자열로 저장된다.
  • 쉘변수의 값을 사용할 때는 변수명앞에 "$" 를 붙여서 사용한다.
  • 쉘변수에 값을 대입할때는 "$"를 사용하지 않는다.
  • 쉘변수는 타입이 없다. 즉 아무 값이나 다 넣을 수 있다.

1.1 환경변수

쉘을 기동하고나면 기본적으로 셋팅되어있는 변수들이다. 유닉스/리눅스에는 많은 환경변수들이 있고 필요한경우 이 변수들을 마치 일반변수처럼 값을 얻어오거나 셋팅할 수 있다. 여기서는 쉘과 직접적인 관련이 있는것만 설명한다.
  • $0 - 실행된 쉘 스크립트 이름
  • $# - 스크립트에 넘겨진 인자의 갯수
  • $$ - 쉘 스크립트의 프로세스 ID

1.2 인자 변수

쉘스크립트에 인자를 넘겨줄때 그 인자들에 대한 정보를 가지고 있는 변수들.
  • $1~ $nnn - 넘겨진 인자들
  • $* - 스크립트에 전달된 인자들을 모아놓은 문자열. 하나의 변수에 저장되며 IFS 환경변수의 첫번째 문자로 구분된다.
  • $@ - $*과 같다. 다만 구분자가 IFS변수의 영향을 받지 않는다.

2 일반변수

일반변수에 특별한 제약은 없다. 단 대소문자 구분만 정확하게 해주면 된다.

예제
#!/bin/sh
echo "This Script Executable File : $0"
echo "Argument Count : $#"
echo "Process ID : $$"
echo "Argument List \$* : $*"
echo "Argument List \$@ : $@"
echo "Argument 1 : $1"
echo "Argument 2 : $2"
echo "Argument 3 : $3"
echo "Argument 4 : $4"  
실행
$chmod 755 test1
$./test1 a1 a2 a3 a4
This Script Executable File : ./test1
Argument Count : 4
Process ID : 905
Argument List $* : a1 a2 a3 a4
Argument List $@ : a1 a2 a3 a4
Argument 1 : a1
Argument 2 : a2
Argument 3 : a3
Argument 4 : a4

2.1 연산

sh
변수의 산술 연산은 생각하는 것처럼 쉽지 않다. 위에서 언급했듯이 변수에는 모든 것이 문자열로 저장되기 때문에 연산이 불가능하다. 연산을 위해서는 좀 복잡한 절차를 거쳐야 한다.
변수 = $((산술식))
이것이 가장 단순한 연산 규칙이다. 산술식내에는 변수($1, $a 와 같은) 도 들어갈 수 있다. 산술식 내에 숫자가 아닌 문자열, 또는 문자열이 담겨있는 변수가 들어가면 그것들은 계산에서 제외된다. (정확히 말하면 0 으로 간주되어 연산이 이루어 지지 않는다.)

2.2 매개변수 확장

매개변수 확장이란 변수의 값을 문자열 등으로 대체하는 것을 말한다. 단순한 대체뿐 아니라 변수내의 문자열을 조작하여 원하는 문자열만을 추출할 수도 있다.

형식:
  • ${parm:-default} - parm이 존재하지 않으면 default로 대체된다.
  • ${#parm} - parm의 길이를 참조한다.(가져온다)
  • ${parm%word} - 끝에서부터 word와 일치하는 parm의 최소부분(첫번째 일치)을 제거하고 나머지를 반환한다.
  • ${parm%%word} - 끝에서부터 word와 일치하는 parm의 최대부분(마지막 일치)을 제거하고 나머지를 반환한다.
  • ${parm#word} - 처음부터 word와 맞는 parm의 최소부분(첫번째 일치)을 제거하고 나머지 부분을 반환한다.
  • ${parm##word} - 처음부터 word와 맞는 parm의 최대부분(마지막 일치)을 제거하고 나머지를 반환한다.
word에는 와일드 카드를 사용할 수 있다.예를 보자.
1 #!/bin/sh
2
3 p="/usr/X11R6/bin/startx"
4
5 unset p
6 a=${p:-"Variable p Not found"}
7 echo $a
8
9 p="/usr/X11R6/bin/startx"
10 a=${p:-"Variable parm Not found"}
11 echo $a
12
13 a=${#p}
14 echo $a
15
16 a=${p%/*}
17 echo $a
18
19 a=${p%%/*}
20 echo $a
21
22 a=${p#*/}
23 echo $a
24
25 a=${p##*/}
26 echo $a
27                    
위 스크립트의 결과는 다음과 같다.
Variable p Not found
/usr/X11R6/bin/startx
21
/usr/X11R6/bin

usr/X11R6/bin/startx
startx
  • 6행 : 변수 p 가 제거 되었으므로 "Variable p Not found" 가 a에 들어간다.
  • 10행 : 변수 p 가 있으므로 그대로 a에 들어간다.
  • 13행 : a에는 변수 p의 길이가 들어간다.
  • 16행 : p 에서 가장 오른쪽의 "/"부터 끝까지 지우고 나머지를 a에 넣는다.
  • 19행 : p 에서 가장 왼쪽의 "/" 부터 끝까지 지우고 나머지를 a에 넣는다. (아무것도 없다)
  • 22행 : p 의 처음부터 가장왼쪽의 "/" 까지 지우고 나머지를 a에 넣는다.
  • 25행 : p 의 처음부터 가장 오른쪽의 "/"까지 지우고 나머지를 a에 넣는다.

3 조건 판단

쉘 스크립트에서 조건판단은 if 와 test 명령을 혼합하여 사용한다. 일반적인 예는 다음과 같다.
if test -f test1
then
...
fi

-f 는 주어진 인자가 일반 파일일 때 참이 된다.

test 명령은 [] 로 대체될 수 있다.
if [ -f test1 ]
then
...
fi

if [ -f test1 ]; then
...
fi

3.1 test 명령

sh
test 명령의 조건은 다음과 같이 세 부류로 나누어진다.

3.1.1 문자열 비교

  • [ string ] - string이 빈 문자열이 아니라면 참
  • [ string1 = string2 ] - 두 문자열이 같다면 참
  • [ string1 != string2 ] - 두 문자열이 다르면 참
  • [ -n string ] - 문자열이 null(빈 문자열) 이 아니라면 참
  • [ -z string ] - 문자열이 null(빈 문자열) 이라면 참

3.1.2 산술 비교

  • [ expr1 -eq expr2 ] - 두 표현식 값이 같다면 참 ('EQual')
  • [ expr1 -ne expr2 ] - 두 표현식 값이 같지 않다면 참 ('Not Equal')
  • [ expr1 -gt expr2 ] - expr1 > expr2 이면 참 ('Greater Than')
  • [ expr1 -ge expr2 ] - expr1 >= expr2 이면 참 ('Greater Equal')
  • [ expr1 -lt expr2 ] - expr1 < expr2 이면 참 ('Less Than')
  • [ expr1 -le expr2 ] - expr1 <= expr2 이면 참 ('Less Equal')
  • [ ! expr ] - expr 이 참이면 거짓, 거짓이면 참
  • [ expr1 -a expr2 ] - expr1 AND expr2 의 결과 (둘다 참이면 참, 'And')
  • [ expr1 -o expr2 ] - expr1 OR expr2 의 결과 (둘중 하나만 참이면 참, 'Or')

3.1.3 파일 조건

  • [ -b FILE ] - FILE 이 블럭 디바이스 이면 참
  • [ -c FILE ] - FILE 이 문자 디바이스 이면 참.
  • [ -d FILE ] - FILE 이 디렉토리이면 참
  • [ -e FILE ] - FILE 이 존재하면 참
  • [ -f FILE ] - FILE 이 존재하고 정규파일이면 참
  • [ -g FILE ] - FILE 이 set-group-id 파일이면 참
  • [ -h FILE ] - FILE 이 심볼릭 링크이면 참
  • [ -L FILE ] - FILE 이 심볼릭 링크이면 참
  • [ -k FILE ] - FILE 이 Sticky bit 가 셋팅되어 있으면 참
  • [ -p FILE ] - True if file is a named pipe.
  • [ -r FILE ] - 현재 사용자가 읽을 수 있는 파일이면 참
  • [ -s FILE ] - 파일이 비어있지 않으면 참
  • [ -S FILE ] - 소켓 디바이스이면 참
  • [ -t FD ] - FD 가 열려진 터미널이면 참
  • [ -u FILE ] - FILE 이 set-user-id 파일이면 참
  • [ -w FILE ] - 현재 사용자가 쓸 수 있는 파일(writable file) 이면 참
  • [ -x FILE ] - 현재사용자가 실행할 수 있는 파일(Executable file) 이면 참
  • [ -O FILE ] - FILE 의 소유자가 현재 사용자이면 참
  • [ -G FILE ] - FILE 의 그룹이 현재 사용자의 그룹과 같으면 참
  • [ FILE1 -nt FILE2 ] - : FILE1이 FILE2 보다 새로운 파일이면 ( 최근파일이면 ) 참
  • [ FILE1 -ot FILE2 ] - : FILE1이 FILE2 보다 오래된 파일이면 참
  • [ FILE1 -ef FILE2 ] - : FILE1 이 FILE2의 하드링크 파일이면 참

3.2 if 구문

if 문은 조건을 판단하여 주어진 문장을 수행한다.

3.2.1 형식 1 (단일 if 문)

형식:
if [ 조건 ]
then
    문장1
    문장2
fi

3.2.2 형식 2 (if-else 문)

형식:
if [ 조건 ]
then
    문장3
    문장4
fi

3.2.3 형식 3 (if-elif 문)

형식:
if [ 조건 ]
then
    문장1
    문장2
elif
    문장3
    문장4
else
    문장5
    문장6
fi

3.3 case 구문

'패턴'에는 * 문자, 즉 와일드카드를 사용할 수 있다.

형식:
case 변수 in
패턴 [ | 패턴 ] ... ) 문장 ;;
패턴 [ | 패턴 ] ... ) 문장 ;;
....
* ) 문장 ;;
esac

3.4 목록

여려 명령을 실행할때 앞의 명령의 결과에 의해서 다음행동이 결정되어야 할 경우가 있다. 이런경우에 AND나 OR조건을 사용해서 한번에 처리할 수 있다. 이것은 쉘 스크립트 뿐 아니라 명령행에서도 사용 가능하다. 물론 if 문을 이용해서 반환값을 검사하여 처리할 수 있지만 문장이 길어지고 복잡해진다.

3.4.1 AND 목록

statment1 && statment2 && statmentN && .....

위의 명령들은 각 명령이 거짓이 될 때 까지 명령을 수행해 나간다. 수행 도중 결과가 거짓이 되면 그이후의 명령은 수행되지 않는다.

3.4.2 OR 목록

statment1 || statment2 || statmentN || .....

위의 명령들은 각 명령이 거짓이 나오는 동안 계속된다. 즉 참이 나오면 실행을 멈춘다.

3.4.3 AND와 OR목록은 혼용이 가능하다.

[ 조건 ] && 문장1 || 문장2

위의 예는 조건이 참이면 문장1을 수행하고 거짓이면 문장2를 수행한다.

또한 위의 문장1이나 문장2에서 여러개의 문장을 수행하고 싶을 때는 {}를 사용하면 된다.
[조건] && {
    문장1
    문장2
    문장3
} || {
    문장4
    문장5
    문장6
}

4 제어문

4.1 for

for 문은 지정된 범위안에서 루프를 수행한다. 범위는 어떤 집합도 가능하다.

형식:
for 변수 in 값1, 값2, ...
do
    문장
done

매 루프를 돌때마다 변수의 값은 in 이후의 값으로 대체된다.

예제:
for str in "test1", "test2", "test3", "test4"
do
    echo @str
done

출력:
test1
test2
test3
test4

값에는 와일드 카드 확장을 사용할 수 있다.
for file in $(ls -a | grep "^\.")
do
    echo "$file is Hidden File"
done

위 예의 출력 결과는 현재 디렉토리에서 처음이 "." 으로시작하는 파일(히든파일)만을 출력한다.

for file in $(ls chap[345].txt); do
    echo "--- $file ---" >> Books.txt
    cat $file >> Books.txt
done

위의 예는 chap3.txt, chap4.txt, chap5.txt 파일을 Books.txt 라는 파일에 붙여 넣는다.

다음의 예를 보고 결과를 예측해보자.
echo "\$* output"

for fvar in $*
do
    echo $fvar
done

echo "\$@ output"
for fvar in $@
do
    echo $fvar
done

4.2 while

for 명령의 경우는 횟수를 지정해서 루프를 수행하는 데는 문제가 있다. while 문은 실행 횟수가 지정되지 않았을 때 편리하다.

형식:
while 조건문
do
    문장
done

예제를 보자. 패스워드를 입력받고 맞는지 확인하는 프로그램이다.
echo "Enter Password : "
read password1

echo "Retype Password : "
read password2

while [ "$password1" != "$password2" ]
do
    echo "Password mismatch Try again "

    echo "Retype Password : "
    read password2
done

echo "OK Password Match complete"

어떻게 동작하는가 ?

4.3 until

until은 while문과 동일한 효과를 내지만 조건이 반대이다. 즉, while문은 조건이 참일동안 루프를 수행하지만 until은 조건이 거짓일 동안 루프를 수행한다.

형식:
until 조건문
do
    문장
done

다음 예를 보자. 이 예는 지정한 유저가 로그인하면 알려준다.
#!/bin/sh

until who | grep "$1" > /dev/null
do
    sleep 10
done

echo "User $1 just logged in ^_^"

4.4 select

select문은 원하는 리스트를 출력하고 그 중 선택된 것을 돌려주는 구문이다. 주의할 점은 select의 루프 내에서는 자동적으로 루프를 벗어날 수 없다. 반드시 break문을 사용해서 루프를 벗어나야 한다.

예: 간단한 퀴즈
#!/bin/sh

echo "다음중 스크립트언어 프로그래밍에 속하는 것은 ?"
select var in "쉘 프로그래밍" "C 프로그래밍" "자바 프로그래밍" "Exit"
do
    if [ "$var" = "쉘 프로그래밍" ]
    then
        echo "정답입니다."
        exit 0
    elif [ "$var" = "Exit" ]
    then
        echo "종료합니다."
        exit 1
    else
        echo "$var 을 선택하셨습니다. 오답입니다."
        echo "다음중 스크립트언어 프로그래밍에 속하는 것은 ?"
    fi
done

5 함수

쉘 스크립트 내부에 또는 다른 스크립트파일에 함수를 정의해 놓고 사용할 수 있다. 함수를 사용하면 코드를 최적화 할 수 있고, 코딩이 간결해지며,재사용이 가능하다. 그러나 다른 스크립트 파일을 호출해서 함수를 실행할 경우, 가능은 하지만 스크립트의 실행시간이 길어지고, 함수의 결과를 전달하는 것이 까다롭기 때문에 가급적이면 외부파일의 함수는 안쓰는 것이 좋다.

형식:
함수명 ()
{
	문장
	return 값
}

사용
함수명 인자1, 인자2, ...

함수는 독립적으로 $#, $*, $0 등의 인자 변수를 사용한다. 즉 함수내의 $#과 본체의 $#은 다를 수 있다는 것이다.

다음의 예를 보자
#!/bin/sh
		
func()
{
    echo ------ this is func --------
    echo "This Script Executable File : $0"
    echo "Argument Count : $#"
    echo "Process ID : $$"
    echo "Argument List \$* : $*"
    echo "Argument List \$@ : $@"
    echo "Argument 1 : $1"
    echo "Argument 2 : $2"
    echo "Argument 3 : $3"
}

echo ------ this is main --------
echo "This Script Executable File : $0"
echo "Argument Count : $#"
echo "Process ID : $$"
echo "Argument List \$* : $*"
echo "Argument List \$@ : $@"
echo "Argument 1 : $1"
echo "Argument 2 : $2"
echo "Argument 3 : $3"
echo "Argument 4 : $4"
func aa bb cc 

본체와 함수에서 동일한 변수를 보여주지만 값은 틀린다는 것을 알 수 있다.

함수에서 값을 반환하기 - 함수에서 반환값은 반드시 정수값만을 반환할 수 있다. 이 값을 if 등으로 조건을 판단해서 사용할 수 있다. 반환값 중 0은 참으로 나머지 숫자는 거짓으로 판별된다.

6 명령어

쉘에서 쓸 수 있는 명령어는 두가지로 나누어진다. 명령 프롬프트 상에서 실행 시킬 수 있는 외부 명령어와 쉘 내부 명령이다. 내부 명령은 보통 쉘 내부나 쉘 구문상에서 쓰인다. 외부명령은 쉘에 관계없이 사용이 가능하다.

6.1 break

제어문이나 조건문의 루프를 빠져나갈때 사용한다.

예제
while [ $a -eq 10 ]
do
    if [ $a -eq 5 ]; then
        break
    fi
done

6.2 continue

제어문이나 조건문의 처음으로 돌아가서 다시수행한다.

예제
while [ $a -eq 10 ]
do
    if [ $a -eq 5 ]; then
        continue
    fi
done

6.3 : 명령

의미없는 명령. 논리값 true를 대신해 쓰기도 한다.

6.4 . 명령

. 명령을 사용하면 현재 쉘에서 명령을 실행시킨다 그러므로 실행된 명령의 결과를 본 프로그램에서 사용할 수 있다.

예를 들면 A 라는 스크립트에서 B라는 스크립트를 그냥 실행할 경우 B에서의 변화(환경변수 등)는 A에게 아무런 영향도 미치지 않는다. 그러나 . 명령을 사용해서 실행하면 B에서의 변화가 A에도 영향을 미친다.

6.5 echo

문장을 출력한다. 자동으로 개행문자가 삽입된다. (다음 줄로 넘어간다)

6.6 eval

인자의 실제 값을 구하는데 사용한다.
foo=10
x=foo
y='$'$x
echo $y

이 예를 실행해 보면 $foo가 출력된다
foo=10
x=foo
eval y='$'$x
echo $y

이 예에서는 $foo의 값 즉 10 이 출력된다. eval명령은 원하는 문자열들을 조합해서 변수를 액세스 할 수 있다.

6.7 exec

현재 쉘을 다른 프로그램으로 대체한다.

예제
exec csh

6.8 exit n

현재 쉘을 종료한다. 종료시 n 값을 리턴한다.

6.9 export

해당 쉘에서 파생된 자식 프로세스에서 export한 환경변수는 본래 쉘에서 관리한다.

6.10 expr

표현식의 값을 구한다.
x=`expr 1 + 2`
요즘은 expr보다는 $((계산식)) 구문을 많이 사용한다.

6.11 printf

C 언어의 printf명령과 흡사하다.

형식:
printf "Format String" arg1 arg2 arg3 ...

6.12 return

쉘 함수에서 값을 반환 할 때 쓰인다. 0은 성공을 1~125까지는 쉘 에러코드를 나타낸다.

6.13 set

쉘 내부에서 매개 인자를 설정한다. set의 인자로 쓰인 문자열은 공백에 의해 $1 부터 차례대로 대입된다.

예제
#!/bin/sh
echo $#
set $(ls)
echo $# 

결과는
0
22

이다. (22는 필자의 ls 결과의 갯수이다.) 첫번째 0는 이 스크립트에 인수가 없으므로 0이고 set $(ls) 에 의해서 인수의 갯수가 22개로 늘었다.

6.14 shift

쉘의 인자를 한자리씩 아래로(n -> 1 로) 이동시킨다.

예제
#!/bin/sh

echo $1
shift
echo $1
shift 5
echo $1

실행
#./myscript 1 2 3 4 5 6 7 8 9 0
1
2
7

6.15 trap

쉘의 실행도중 시그널을 처리하는 시그널 처리기를 만드는 역할을 한다.

형식:
trap command signal

쉘 스크립트는 위에서 아래로 실행되므로 보호하려는 부분 이전에 trap 명령을 사용해야 한다. trap 조건을 기본으로 사용하려면 명령에 - 를 넣으면 된다. 신호를 무시하려면 '' 빈 문자열을 준다.

6.16 unset

변수나 함수를 제거한다.

7 명령 실행

외부 명령의 실행 결과를 변수에 집어넣어 변수의 값으로 사용할 수 있다.

형식:
x = $(명령)

이렇게 변수에 결과를 넣은 후에는 이 변수를 일반문자열로 생각하고 원하는 가공을 해서 결과를 얻어낼 수 있다. 위에서 보았던 매개변수 확장이나 set명령을 이용해서 원하는 부분을 추출해 내면 그만이다.

8 쉘 스크립트 내부에서 명령에 입력 전달하기 (Here Documents)

이 기능은 쉘 내부에서 명령어에 입력을 전달하는 방법이다. 전달된 입력은 마치 키보드에서 눌려진 것처럼 반응한다.

형식:
명령 << 종료문자열
입력값.....
종료문자열

예제: 자동으로 메일을 보내는 스크립트
#!/bin/sh

mail $1 << myscript
This is Header
This is Body
.

myscript

9 디버깅 하기

쉘 프로그래밍 시 간단하게 디버깅하는 방법을 소개합니다.

9.1 쉘 옵션

  • sh -n 스크립트 : 문법 에러만을 검사, 명령을 실행하지 않음
  • sh -v 스크립트 : 명령을 실행하기 전에 에코
  • sh -x 스크립트 : 명령줄에서 처리한 다음 에코

9.2 set 옵션

위의 쉘 옵션은 아래와 같이 set 옵션으로도 설정할 수 있다.
  • set -o noexec 또는 set -n : 문법 에러만을 검사, 명령을 실행하지 않음
  • set -o verbose 또는 set -v : 명령을 실행하기 전에 에코
  • set -o xtrace 또는 set -x : 명령줄에서 처리한 다음 에코
  • set -o nounset 또는 set -u : 정의되지 않은 변수가 사용되면 에러 메시지를 제공한다.

아래와 같이 set -x를 이용하여 손쉽게 실행과정을 추적할 수 있다. (참고로 set 옵션을 취소하려면 set +x를 입력하면 된다. 다른 옵션도 마찬가지)
set -x
for str in "test1" "test2" "test3" "test4"
do
    echo $str
done

결과
+ for str in '"test1"' '"test2"' '"test3"' '"test4"'
+ echo @str
@str
+ for str in '"test1"' '"test2"' '"test3"' '"test4"'
+ echo @str
@str
+ for str in '"test1"' '"test2"' '"test3"' '"test4"'
+ echo @str
@str
+ for str in '"test1"' '"test2"' '"test3"' '"test4"'
+ echo @str
@str
반응형
◆ 비즈니스를 위한 디지털 마인드맵의 활용

기획서나 제안서 등의 문서는 논리적으로 기술되어야 한다. 이 문서를 왜 작성하고 있으며 어떤 목적을 달성할 것인지를 점검하고 그에 맞는 내용들이 논리적으로 서술되어야 한다. 그러므로 문서 작성 전에 기술하고자 하는 내용들에 대해서 모두 끄집어내고 이들을 논리적으로 정렬할 필요가 있다. 즉 문서에 담고자 하는 내용들을 대표하는 주요 키워드를 끄집어내어 논리적으로 배치함으로써 기획서의 초안을 먼저 구성해야 한다. 초안을 바탕으로 구체적인 제안서를 만들어가는 것이 좋다.


그렇다면 머리 속에 떠오르는 막연한 아이디어와 생각들을 정리해서 어떻게 초안을 만들어야 할까? 이때 사용할 수 있는 것이 마인드맵이다. 마인드맵은 머리 속에 떠오르는 생각을 핵심단어, 기호, 이미지 등을 사용해 구조적이고 체계적으로 정리하는 기법을 말한다. 이러한 정리 기법은 막연하고 추상적인 아이디어를 체계화하는데 큰 도움을 준다. 기획서를 작성하기에 앞서 이러한 방법을 활용해 체계적으로 초안을 정리하면 실제 문서 작성에 큰 도움이 된다. 노트에 무작정 생각을 필기하는 것보다는 마인드맵을 활용하는 것이 좋다.

 

▪ 마인드맵 프로그램을 이용해 디지털 마인드맵 작성법
마인드맵은 1970년대 유럽의 Tony Buzan에 의해 시작된 필기법이다. 생각과 아이디어를 핵심 아이디어를 기준으로 바깥으로 뻗어 나가는 가지를 사용함으로써 구조화하는 것이다. 이렇게 비주얼한 기록을 통해 기억, 회상, 연상을 보다 쉽게 하고 이것이 창조적인 생각을 가능하게 할 뿐 아니라 생각을 구조적으로 정리할 수 있도록 해준다. 마인드맵을 활용하면 창의성 계발과 사고력을 증진시킬 수 있다.

일반적으로 우리는 생각이나 아이디어를 정리하고 메모할 때 직선식의 필기법을 활용한다. 떠오르는 생각들을 노트에 그어진 줄에 맞춰서 순차적으로 정리한다. 이러한 방법은 단조롭고 지루하기 때문에 창의적인 사고를 방해한다. 또한 집중력이 떨어지고 필기한 내용을 한 눈에 알아보기 어렵다. 반면 마인드맵으로 작성된 문서는 필요한 단어만을 사용해 시각적으로 배치하기 때문에 창의적인 생각을 가능하게 해준다.

마인드맵을 PC에서 손쉽게 구현할 수 있도록 도와주는 프로그램이 있다. 심테크시스템의 씽크와이즈라는 프로그램을 이용하면 마인드맵을 쉽게 PC에서 사용할 수 있다. 이 소프트웨어는 ‘http://www.thinkwise.co.kr’에서 30일 동안 무료로 사용할 수 있는 프로그램을 다운로드받을 수 있다. 이 프로그램을 이용하면 머릿속에 떠오르는 여러 아이디어를 비주얼한 이미지와 기호, 색상 등을 이용해 쉽게 정리할 수 있다.

마인드맵은 방사사고의 표현으로 중심을 기준으로 주요 키워드를 사방으로 뻗어나가며 기술한다. 이때 단어를 단색으로 정리하는 것이 아니라 다양한 색상 그리고 이미지와 부호를 사용한다. 그러므로써 좌우 뇌의 기능을 유기적으로 연결하여 사고력 증대를 가져다준다. 즉 마음 속의 지도를 글자는 물론 기호, 그림을 이용해 표현함으로써 창의성과 기억력을 증진시켜준다. 기존의 노트 필기법과는 달리 이미지와 언어의 연상작용을 통해 시각적인 노트 작성법이라 할 수 있다. 마인드맵은 백지와 색펜을 이용해서도 작업이 가능하다. 하지만 씽크와이즈와 같은 PC용 프로그램을 이용하면 쉽고 빠르게 마인드맵을 할 수 있을 뿐 아니라 저장이 용이하다. 씽크와이즈를 실행하면 중앙에 핵심 주제를 입력할 수 있는 메뉴가 나타난다. 이것을 중심어라 한다. 이곳에 다루고자 하는 핵심적인 단어를 기입한다. 입력이 끝나면 엔터를 누른다.

스페이스바를 누르면 중심어의 옆에 주가지가 나타난다. 이곳에 주제를 기입하고 엔터를 누른다. 이렇게 중앙의 핵심 이미지 주위로 가지를 쳐내려가며 주제어를 기입해나가며 생각의 나래를 지도로 정리하는 것이 마인드맵의 핵심이다.

주제어 입력을 끝내면 주제어를 마우스로 클릭하거나 키보드의 방향키를 이용해 주제어를 선택한다. 그리고 스페이스바를 누른다. 주제어 옆에 부가지가 나타난다. 주제어에 대해 세부적인 내용을 담은 핵심어로 부주제를 기입한다. 기입이 끝나면 엔터를 누르고 다시 스페이스바를 누르면 부가지가 나타난다. 이러한 방법으로 부주제를 계속 기록해나간다.

부주제는 주제에 대한 세부적인 내용을 담고 있어야 한다. 그리고 가급적 핵심적인 단어만을 이용해 구성하는 것이 좋다. 또한 주제, 부주제 등에는 심볼 등을 적절히 배치함으로써 시각적으로 강렬한 인상을 주도록 하는 것이 좋다. 심볼은 우측의 기호창에서 선택할 수 있다. 기호를 넣을 주제 또는 부주제를 클릭하고 기호창에서 넣고자 하는 심볼을 클릭하면 기호가 삽입된다. 기호창은 ‘보기 > 도구모음 > 기호’를 클릭해서 화면에 표시할 수 있다.

이러한 방법으로 중앙어를 중심으로 주제와 부주제를 주가지와 부가지를 이용해서 정리해가도록 한다. 부주제는 좀 더 세부적이고 자세한 내용을 기록해 넣도록 한다. 너무 추상적인 단어만으로 구성하면 창의적 아이디어나 다양한 정보를 생각해낼 수 없다. 전체적으로 중앙에 위치한 중앙어 중심으로 가지를 통해서 다양한 정보들이 연계됨으로써 생각을 체계적으로 정리할 수 있다.


[Tip : ] 마인드맵은 PC없이도 할 수 있다.


마인드맵을 꼭 PC에서만 할 수 있는 것은 아니다. 흰 백지와 색펜을 이용해 작업할 수 있다. 오히려 PC에서 작업하는 것보다 더 편하고 자유로울 수 있다. 백지는 가급적 줄이 없는 종이가 좋다. 줄이 쳐진 종이는 생각을 하는데 제한할 수 있기 때문이다. 색펜의 경우 주가지의 수만큼 준비하는 것이 좋다. 대개 5색 정도를 준비하고 중앙에 중심어를 넣으면서 색펜을 이용해 각각의 주가지를 다른 색상을 색칠하면서 이미지와 단어를 이용해서 생각을 정리해나가면 된다.

 


▪ 마인드맵으로 제안서의 초안을 구성하기


마인드맵을 이용해 제안서의 초안을 작성하면 정리하면서 새롭고 창조적인 아이디어를 떠올릴 수 있다. 마케팅 기획안이나 각종 제안서를 작성하는데 처음부터 문서를 만들려 하면 막막하기만 하다. 이때 마인드맵으로 생각을 정리하다보면 쉽게 생각하기 어렵던 기발한 발상도 가능하다. 또한 다양한 경우를 점검해볼 수 있어 빈틈없는 기획안을 작성하는데 밑거름이 되기도 한다. 마인드맵 프로그램은 사용이 그리 어렵지 않으며 생각을 빠르게 정리하는데 큰 도움이 된다.

마케팅은 고정관념에서 벗어나 독특한 발상의 전환이 필요하다. 자유로운 사고를 통해서 기발한 아이디어로 마케팅이 전개되어야 하는데 마인드맵이 이러한 아이디어를 생각하게 하는데 큰 도움이 된다. 우선 중앙에 마케팅의 제목을 기입하고 그 주변으로 생각나는 모든 주제어를 기입한다. 마케팅의 목표, 방안, 컨셉, 일정, 예산 등의 생각나는 중요한 항목들을 모두 주가지로 구성한다.

기입한 주제어 중에서 부주제로 정리해야 할 것들을 정리한다. 예를 들어 컨셉, 지표, 포지셔닝 등을 ‘목표’의 부가지로 위치를 변경하려면 ‘컨셉’을 클릭한채 드래그해서 ‘목표’에 놓는다. ‘지표’와 ‘포지셔닝’을 Shift를 이용해 연속으로 선택해서 한꺼번에 옮길 수도 있다. 이러한 방법으로 부주제와 주제를 정리한다.

가지의 색상을 변경하려면 변경할 가지의 주제를 마우스 오른쪽 버튼으로 클릭한 후에 ‘서식’을 선택한다. ‘가지’에서 가지의 색상과 굵기를 변경할 수 있다. 각각의 가지별로 다른 색상을 지정해서 사용하면 다양한 색상으로 인해 마인드맵의 목적을 달성하는데 큰 도움이 된다.

‘외곽선’을 선택한 후에 적절한 도형을 클릭하면 선택한 주제에 테두리가 표시된다. 중요한 주제어를 강조해서 표현하고자 할 때 유용하다.

가지의 색상과 외곽선이 적용된 모습을 볼 수 있다. 이러한 방법으로 각각의 가지 색생과 주제, 부주제 등을 꾸밀 수 있다.

마케팅 마인드맵에는 가급적 다양한 기호와 이미지를 사용하는 것이 좋다. 이러한 이미지들이 보다 창의적이고 유연한 생각을 떠오르게 하는데 도움을 준다.

마케팅 전략에는 기본적으로 목표와 지표, 컨셉 등이 표시되어야 하며 그 외에 구체적인 마케팅 방안, 예산, 일정 등이 표기되어야 한다. 주제어를 이러한 항목으로 구성한 후에는 부주제에 부가지를 쳐서 세부적인 내용을 기입해 넣는다. 각 항목에 최대한 자세한 내용을 기록해 넣음으로써 한 장의 마인드맵만으로 마케팅 전반적인 계획에 대한 내용을 쉽게 파악할 수 있도록 구성하는 것이 좋다.

철저하고 빈틈없이 작성해야 하는 사업계획서도 마인드맵으로 정리하면 큰 도움이 된다. 최종 문서는 워드프로세서나 프리젠테이션 파일의 형태로 저장하는 것이 좋지만 처음 생각을 정리할 때는 마인드맵을 활용하는 것이 좋다. 특히 원고의 목차나 강의 커리큘럼 등을 정리할 때도 유용하게 사용할 수 있다. 실제 많은 테크라이터와 강사들이 마인드맵을 이용해 책이나 강의를 준비하곤 한다. 물론 사업계획서를 마인드맵으로 처음부터 끝까지 작성할 수는 없다. 마인드맵은 사업계획서의 목차를 잡고 초안을 구성하는데 도움이 된다. 우선 사업계획서에서 다루고자 하는 중심 주제들을 주가지로 정리한다. 각각의 항목별로 색상을 다르게 지정함으로써 다소 지루할 수 있는 계획서의 내용에 신선함을 부여하도록 한다.

주제를 기입해 넣은 후에는 각각의 항목별로 세부적인 사항을 채워 넣는다. 기본적으로 사업계획서의 1차 주제어로는 회사개요, 사업개요, 시장분석, 인력구성, 재무계획, 마케팅 계획, 생산 계획 등이다. 이러한 각 항목 아래에 부가지를 치면서 세부적인 내용을 기록해 넣는다. 사업계획서마다 조금씩 내용 구성이나 형태는 달라질 수 있다. 전체적인 내용을 보면서 각 세부 항목에 어떠한 것을 포함시켜야 할지 비교하면서 중복되지 않도록 채워 넣는다.

이제 가장 중요한 부분이다. 부주제 아래에 보다 상세한 내용을 기입해 넣는다. 앞서 작성한 내용들은 목차 정도의 수준이다. 하지만 3단계의 부주제에 기록해 넣을 사항은 해당 항목에서 실제 다룰 내용들을 기록한다. 내용은 길제 작성하지 않도록 한다. 핵심어를 이용해서 본 사업계획서의 중요한 특징이자 반드시 다루어야 할 사항들을 핵심 단어를 이용해 채워 넣는다.

이렇게 상세한 내용을 채워 넣다보면 사업 계획서의 전체적인 그림이 구성된다. 이렇게 작성된 사업계획서는 초안일 뿐 완성된 문서는 아니다. 이렇게 그려진 마인드맵을 통해서 전체적인 사업계획서의 밑그림을 확인할 수 있다. 이제 전체적인 내용을 살펴보면서 미흡한 부분과 강조할 사항, 특징 등을 기입하며 보다 세부적으로 완성시켜 나간다. 이 작업은 하루, 이틀에 할 수 있는 것이 아니다. 수일, 수십일에 걸쳐 다양한 사람들의 의견을 들으며 정리해나간다. 이후에 이 마인드맵을 기본으로 세부적인 사업계획서를 작성하면 된다.


[Tip : ] 마인드맵 파일 PPT로 변환하기
대개 사업계획서는 MS워드나 파워포인트로 정리한다. 마인드맵으로 구성한 사업계획서는 초안이기 때문에 실제 계획서를 작성하려면 MS워드나 파워포인트를 이용해야 한다. 이때 씽크와이즈에서 제공하는 변환 기능을 활용하면 MS오피스에서 쉽게 계획서를 작성할 수 있는 기본 문서 파일을 생성할 수 있다.
사업계획서의 초안을 만든 마인드맵에서 ‘파일 > 보내기 > Microsoft PowerPoint'를 선택하여 PPT 파일로 변환한다. 이렇게 되면 앞서 마인드맵에서 정리한 내용들이 파워포인트에 그대로 표시되어 나타난다. 이제 마인드맵에서 구성한 목차대로 파워포인트에서 세부적인 내용을 채워나가면 된다.


글/김지현(oojoo@oojoo.co.kr)




원문링크:

http://hdbbs.hanafos.com/Board.aspx?act=View&tbl=QBIC_WEBZINE&pg=1&bid=1&seq=245&thread=49

반응형
2008년 3월호
 
[Java SE]  BoxLayout 알아보기
[Java EE]  상태 유지 세션 빈에서의 확장 지속성 컨텍스트
[Java EE]  SIP 서블릿을 사용하여 자바 EE에 음성 추가하기
[Java ME]  자바 ME 장치 매트릭스로 필터 사용
[Java ME]  자바 ME 옵션 패키지 검색


> 2008 자바원 참석 티켓 이벤트


2008 년 5월 5일부터 9일까지 미국 샌프란시스코에서 진행되는 2008 자바원 컨퍼런스 한장
의 무료 티켓! 지금까지의 활동하신 자바 프로젝트 활동과 자바원 행사 이후의 발표,기고 기획을 알기쉬운 자유형식으로 보내주세요. 한분을 선정하여 자바원 참가 티켓을 보내드립니다.

>>지금 신청하기



> 썬 교육센터 추천 과정


>> J2EE기반의 CBD 개발방법론 5/19 ~ 5/23
  >> AJAX 기반의 JSF Web Application 개발4/14 ~ 4/18  
  >> 실무자를 위한 파워 웹 컴포넌트 개발4/21 ~ 4/25

반응형

실무에서 SQL문을 작성하다 보면 동적인 쿼리문 작성을 작성해야 할 때가 많이 있다.

이때 지겹게 if~else if 문을 통해 아주 지저분한 소스 코드를 생성할 때가 왕왕 있게 마련이다.

이때 ibatis에서는 아주 깔금하게 구현할 수 있는 방법을 제공해 준다.

 

<statement id="dynamicGetAccountList" resultMap="account-result">

  select * from account

  <dynamic prepend="WHERE">

    <isNotNull prepend="AND" property="firstName">

      (acc_first_name = #firstName#

    <isNotNull prepend="OR" property="lastName">

       acc_last_name = #lastName#

    </isNotNull>

    )

    </isNotNull>

    <isNotNull prepend="AND" property="emailAddress">

      acc_email like #emailAddress#

    </isNotNull>

    <isGreaterThan prepend="AND" property="id" campareValue="0">

      acc_id = #id#

    </isGreaterThan>

  </dynamic>

  order by acc_last_name

</statement>

 

상황에 의존적인 위 동적 statement로 부터 각각 다른 16가지의 SQL문이 생성될 수 있다. if-else구조와 문자열 연결을 코딩하는 경우 수백라인이 필요할 수도 있다.

동적 statement를 사용하는 것은 몇몇 조건적인 태그를 추가하는 것처럼 간단하게 작성할 수 있다.

 

이러한 조건들에 대해 간단히 정리하면 아래와 같다.

 

바이너리 조건 요소-바이너리 조건 요소는 정적값 또는 다른 프로퍼티값을 위한 프로퍼티값과 비교한다. 만약 결과가 true라면 몸체부분의 SQL쿼리가 포함된다.

 

바이너리 조건 속성

prepend

Statement에 붙을 오버라이딩 가능한 SQL부분(옵션)

property

비교되는 property(필수)

compareProperty

비교되는 다른 property (필수 또는 compareValue)

compareValue

비교되는 값(필수 또는 compareProperty)

 

<isEqual>

프로퍼티가 값 또는 다른 프로퍼티가 같은지 체크

<isNotEqual>

프로퍼티가 값 또는 다른 프로퍼티가 같지 않은지 체크

<isGreaterThan>

프로퍼티가 값 또는 다른 프로퍼티 보다 큰지 체크

<isGreaterEqual>

프로퍼티가 값 또는 다른 프로퍼티 보다 크거나 같은지 체크

<isLessThan>

프로퍼티가 값 또는 다른 프로퍼티 보다 작은지 체크

<isLessEqual>

프로퍼티가 값 또는 다른 프로퍼티 보다 작거나 같은지 체크

 

사용법 예제)

<isLessEqual prepend="AND" property="age" compareValue="18">

  ADOLESCENT = 'TRUE'

</isLessEqual>

 

단일 조건 요소-단일 조건 요소는 특수한 조건을 위해 프로퍼티의 상태를 체크한다.

prepend

statement에 붙을 오버라이딩 가능한 SQL부분(옵션)

property

체크하기 위한 프로퍼티(필수)

 

<isPropertyAvailable>

프로퍼티가 유효한지 체크

(이를 테면 파라미터의 프로퍼티이다.)

<isNotPropertyAvailable>

프로퍼티가 유효하지 않은지 체크

(이를 테면 파라미터의 프로퍼티가 아니다.)

<isNull>

프로퍼티가 null인지 체크

<isNotNull>

프로퍼티가 null이 아닌지 체크

<isEmpty>

Collection, 문자열 또는 String.valueOf() 프로퍼티가 null이거나 empty(“” or size() < 1)인지 체크

<isNotEmpty>

Collection, 문자열 또는 String.valueOf() 프로퍼티가 null 이아니거나 empty(“” or size() < 1)가 아닌지 체크

 

사용법 예제)

<isNotEmpty prepend="AND" property="firstName">

  FIRST_NAME = #firstName#

</isNotEmpty>


다른 요소들

Parameter Present : 파라미터 객체가 존재하는지 체크

Parameter Present Attributes : prepend - the statement에 붙을 오버라이딩 가능한 SQL부분

<isParameterPresent>

파라미터 객체가 존재(not null)하는지 체크

<isNotParameterPresent>

파라미터 객체가 존재하지(null) 않는지 체크

 

사용법 예제)

<isNotParameterPresent prepend="AND">

EMPLOYEE_TYPE = 'DEFAULT'

</isNotParameterPresent>


Iterate : 이 태그는 Collection을 반복하거나 리스트내 각각을 위해 몸체 부분을 반복한다.

Iterate Attributes :

  prepend - the statement에 붙을 오버라이딩 가능한 SQL부분 (옵션)

  property - 반복되기 위한 java.util.List타입의 프로퍼티 (필수)

  open - 반복의 전체를 열기 위한 문자열, 괄호를 위해 유용하다. (옵션)

  close - 반복의 전체를 닫기 위한 문자열, 괄호를 위해 유용하다. (옵션)

  conjunction - 각각의 반복 사이에 적용되기 위한 문자열, AND 그리고 OR을 위해 유용하다. (옵션)

<iterate>

java.util.List 타입의 프로퍼티 반복


사용법 예제)

<iterate prepend="AND" property="userNameList" open="(" close=")" conjunction="OR">

username = #userNameList[]#

</iterate>


주의:iterator요소를 사용할 때 리스트 프로퍼티의 끝에 중괄호[]를 포함하는 것은 중요하다. 중괄호는 문자열처럼 리스트를 간단하게 출력함으로부터 파서를 유지하기 위해 리스트처럼 객체를 구별한다.

반응형

자바 SE 6의 성능 모니터링 및 진단 (한글)

자바 최신 버전에서 향상된 성능과 모니터링의 장점을 활용하기

 

 

JavaScript가 필요한 문서 옵션은 디스플레이되지 않습니다.

 




난이도 : 중급

Cathy Kegley, 소프트웨어 엔지니어, IBM
Greg Roberts, 스태프 소프트웨어 엔지니어, IBM

2008 년 3 월 11 일

자바 SE 6(Java™ Platform, Standard Edition 6)은 성능에 초점을 맞춰 응용 프로그램을 모니터링, 감시하고 공통적인 문제를 진단하기 위해 확장된 도구들을 제공합니다. 이 기사에서는 자바 SE 플랫폼의 모니터링과 관리에 대한 기본적인 사항들을 소개하고, 자바 SE 6에서 보강된 사항들에 대한 상세한 정보를 제공합니다.

자바 SE 6은 응용 프로그램을 관리, 모니터링하고 일반적인 문제점을 진단하는 확장된 도구들을
제공한다. 개선된 사항은 다음과 같다.

  • 모니터링 및 관리(Monitoring and management) API 향상
  • 개선된 그래픽 모니터링 도구인 JConsole에 대한 공식 지원
  • 강화된 자바 가상 머신(JVM) 도구

이 기사는 자바 SE 플랫폼의 모니터링과 관리에 대한 기본적인 사항들을 소개하고, 최신 버전에서
보강된 사항들에 대해 상세한 정보를 제공한다. 또한 자바 SE 6 플랫폼에서 사용할 수 있는 진단
및 문제 해결 도구들에 대해 설명한다.

이 기사를 통해 이전 버전의 자바 SE에서 소개된 모니터링 및 관리 기능을 확실히 이해하게
 될 것이다.  참고자료를 통해 자세한 배경 정보를 볼 수 있다.

모니터링 및 관리 API

자바 SE 5는 java.lang.management 패키지에 플랫폼 MBean 또는 MXBean이라는 9개의
MBean을 정의한다(참고자료). 각 MXBean은 JVM의 단일 기능 영역을 감싸고 있다.
자바 SE 5부터 JVM은 플랫폼 MBean 서버라고 부르는 MBean 서버를 내장하고 있다.
MBean은 이 저장소 안에 존재하고 관리된다. 표 1에 자바 플랫폼에 포함된 9개의
MXBean을 요약했다.


표 1. 플랫폼 MBeans
관리 인터페이스 관리되는 자원
ClassLoadingMXBean 클래스 로더
CompilationMXBean 컴파일러
MemoryMXBean 메모리
ThreadMXBean 스레드
RuntimeMXBean 런타임
OperatingSystemMXBean 운영체제
GarbageCollectorMXBean 가비지 컬렉터
MemoryManagerMXBean 메모리 관리자
MemoryPoolMXBean 메모리 풀

어떤 응용 프로그램이라도 원하는 빈의 인스턴스를 얻고 절적한 메서드를 호출해 JVM이 제공하는
 플랫폼 MBean을 사용할 수 있다. 로컬 또는 원격 JVM의 동작을 모니터링하고 정보를 얻기 위해
MXBean을 사용할 수 있다.

플랫폼 MBean은 적재된 클래스 개수, JVM 가동시간, 메모리 소모량, 실행 중인 스레드 개수,
스레드 경쟁에 대한 통계 등의 정보에 대해 접근을 제공한다.

JVM 리소스를 모니터링하고 관리하는 두 가지 방법이 있다.

  • MXBean 인터페이스를 통한 직접 접근
  • MBeanServer 인터페이스를 통한 간접 접근

MXBean 인터페이스를 통한 직접 접근

정적인 팩토리 메서드를 통해 MXBean 인스턴스를 얻어 로컬에서 실행 중인 JVM의
MXBean 인터페이스에 직접 접근할 수 있다. ManagementFactory 클래스는 MXBean을 얻을 수
있는 정적인 팩토리 메서드를 제공한다. Listing 1은 이 팩토리를 통해 RuntimeMXBean을 얻어
표준 속성 중 하나인 VmVendor의 값을 얻는 방법을 보여준다.


Listing 1. MXBean에 직접 접근하기
                
RuntimeMXBean mxbean = ManagementFactory.getRuntimeMXBean();

// Get the standard attribute "VmVendor"
String vendor = mxbean.getVmVendor();

MBeanServer 인터페이스를 통한 간접 접근

플랫폼 MBeanServer 인터페이스는 원격 JVM에 연결하고 그 플랫폼에서 실행 중인 MXBean에
접근하기 위해 MXBeanServerConnection을 사용한다. ManagementFactory 클래스의 getPlatformMBeanServer 메서드를 사용해 플랫폼 MBean 서버에 접근할 수 있다. Listing 2는
원격에서 실행 중인 JVM에서 RuntimeMXBean을 얻고, VmVendor 속성의 값을 얻는 방법을
보여준다.


Listing 2. MXBean에 간접 접근하기
                
MBeanServerConnection serverConn;

try {
   //connect to a remote VM using JMX RMI
JMXServiceURL url = new JMXServiceURL( "service:jmx:rmi:///jndi/rmi://<addr>");

   JMXConnector jmxConnector = JMXConnectorFactory.connect(url);

   serverConn = jmxConnector.getMBeanServerConnection();

   ObjectName objName = new 
   ObjectName(ManagementFactory.RUNTIME_MXBEAN_NAME);

   // Get standard attribute "VmVendor"
   String vendor = 
   (String) serverConn.getAttribute(objName, "VmVendor");

} catch (...) { }

참고자료에서 MXBean과 the java.lang.management API에 대한 더 자세한 정보를 볼 수 있다.

자바 SE 6에서 보강된 API

자바 SE 5는 락(lock)과 대기 조건(wait codition)에 대한 프레임워크를 제공하는 java.util.concurrent.locks 패키지를 도입했다. 이 프레임워크는 자바의 내장된 동기화
지원과는 별도로 더욱 융통성 있는 락 사용을 가능케 해 주었다.

자바 SE 6은 java.lang.management 패키지에 java.util.concurrent.locks에 대한 지원을
추가했다. 여기에는 락에 대한 정보를 제공하는 새로운 클래스와 ThreadInfo, ThreadMXBean, OperatingSystemMXBean에 대한 보강이 포함되었다.

자바 SE 6은 새로운 클래스 두 개를 도입했다.

  • LockInfo는 락에 대한 정보를 갖고 있다.
  • MonitorInfoLockInfo를 상속해 객체 모니터 락에 대한 정보를 갖고 있다.

ThreadInfo 클래스는 새로운 객체들을 사용하기 위해 새 메서드 세 가지를 도입했다.

  • getLockInfo()는 주어진 스레드가 차단 대기중인 LockInfo 객체를 반환한다.

  • getLockedMonitors()는 주어진 스레드에 의해 현재 락된 MonitorInfo 객체를 반환한다.

  • getLockedSynchronizers()는 주어진 스레드에 의해 현재 락된 소유 가능한 동기화

객체(ownable synchronizer)를 표현하는 LockInfo 객체를 반환한다.

ThreadMXBean.getThreadInfo 메서드는 자바 SE 5에서는 스레드가 획득 대기중이거나
진입 차단된 차단된 객체 모니터에 대해서만 보고했지만, 자바 SE 6부터는 획득 대기중인 스레드의 AbstractOwnableSynchronizer를 보고하도록 보강되었다.

ThreadMXBean 인터페이스도 메서드 네 개를 추가했다.

  • isObjectMonitorUsageSupported()는 가상 머신이 객체 모니터에 대한 모니터링을

지원하는지 여부를 검사한다.

  • isSynchronizerUsageSupported()는 소유 가능한 동기화 객체의 사용 모니터링을

지원하는지 여부를 검사한다.

  • findDeadlockedThreads()는 데드락된 스레드 ID의 배열을 반환한다. 데드락된 스레드는 다른 스레드가 객체 모니터나 동기화 객체에 진입하는 것을 차단하고 있는 스레드다.

  • dumpAllThreads()는 모든 살아있는 스레드에 대한 스택 트레이스와 동기화 정보를 반환한다.

마지막으로, OperatingSystemMXBean 인터페이스에 최근 수 분 동안 시스템의 평균 부하를 반환하는 getSystemLoadAverage()가 추가되었다.

프로그래밍 수준 지원과 더불어, 자바 SE 6은 문제를 감지하고 JVM의 자원 사용량을 모니터링하기
위한 몇 가지 진단 및 문제 해결 도구를 제공한다. 다음 두 절에서는 사용할 수 있는 진단 도구들에
설명하고 사용 예를 보여준다.






자바 모니터링 및 관리 콘솔(JConsole)

자바 SE 6은 자바 SE 5부터 도입된 모니터링 및 관리 콘솔인 JConsole에 대한 공식 지원을 포함한다. JConsole을 통해 실행 중에 다양한 JVM 통계를 모티터링할 수 있다. 이 도구는 데드락, 락 경합,
메모리 누수, 순환 스레드 등의 증상을 감지하는 데 유용하다. 이 도구는 로컬이나 원격 JVM에
연결하여 다음과 같은 정보를 모니터링할 수 있다.

  • 스레드 상태(연관된 락을 포함한)
  • 메모리 사용량
  • 가비지 컬렉션
  • 런타임 정보
  • JVM 정보

다음 절에서 자바 SE 6에서 JConsole의 보강된 부분들을 설명한다. 참고자료에서 JConsole을
시작하고 사용하는 방법에 대한 추가 정보를 볼 수 있다.

Attach API 지원

자바 SE 6부터 JConsole은 새로운 Attach API를 구현하고 있다. 이 com.sun.tools.attachcom.sun.tools.attach.spi 두 개의 패키지로 구성되어 있으며, 대상 가상 머신에 동적으로
부착되어 대상 JVM에서 에이전트를 실행하는 응용 프로그램을 구현할 수 있다.

예전에는 JConsole로 모니터링하려면 응용 프로그램을 -Dcom.sun.management.jmxremote
옵션을 사용하여 시작해야 했지만, 더 이상 그 옵션을 사용할 필요가 없다. 동적인 부착을 통해 JConsole은 Attach API를 지원하는 어떤 응용 프로그램이라도 지원할 수 있게 되었다. JConsole은
 시작하면서 호환 응용 프로그램을 자동으로 감지한다.

보강된 UI와 MBean 표현

자바 SE 6에서는 JConsole이 Windows® 운영체제나 GNOME 데스크톱 등의 실행 중인 플랫폼과
 비슷한 룩앤필을 갖도록 수정했다. 이 기사에서 이후부터 볼 스크린샷은 윈도우 XP에서 찍은
것으로 이전 버전과 달라진 UI 기능들을 보여준다.

일단 시작해 응용 프로그램과 연결되면, JConsole은 각각 다른 JVM 자원을 표현하는 6개의
 탭으로 구성된 화면을 제공한다.

  • Overview
  • Memory
  • Threads
  • Classes
  • VM Summary
  • MBeans

Overview 탭은 상호 연관된 메모리 사용량, 스레드, 클래스, CPU 사용량에 대한 정보를 그래프
 형식으로 표시한다. Overview 탭은 예전에는 여러 개의 탭을 전환해야 했던 관련된 정보들을
한 페이지에 표시한다. 그림 1은 예제 응용 프로그램의 Overview 탭을 보여준다.


그림 1. JConsole의 Overview 탭
JConsole의 Overview 탭

Overview 탭은 VM 자원 사용 정보에 대한 4개의 그래프와 결과를 보고 싶은 시간 범위를 바꾸기
위한 선택 목록을 표시한다. 첫 번째 힙 메모리 사용량 그래프는 시간대별로 사용된 힙 메모리의
양을 메가바이트 단위로 표시한다. 이 그래프는 메모리 누수를 감지하는 데 유용하다.
응용 프로그램에 메모리 누수가 있다면 힙 메모리 사용량은 시간이 지남에 따라 꾸준히 늘어난다.

스레드 그래프는 시간대 별로 살아있는 스레드 개수를, 클래스 그래프는 적재된 클래스 개수를
표시한다. CPU 사용량 차트는 응용 프로그램 생명 주기의 다양한 지점에서의 CPU 사용량을
표시한다.

그림 2의 VM Summary 탭은 자바 SE 6에 새로 추가된 것이다. 여기에는 총 가동시간, 스레드 정보,
적재된 클래스 개수, 메모리 통계, 가비지 컬렉션, 운영체제 정보를 포함한 JVM에 대한 자세한
정보를 제공한다.


그림 2. JConsole의 VM Summary 탭
JConsole의 VM Summary 탭

MBeans 탭은 MBean들의 동작과 속성에 더 쉽게 접근할 수 있도록 개선되었다. 플랫폼에 등록된
모든 MBean의 정보를 표시한다. 모든 플랫폼 MBean은 이 탭을 통해 접근할 수 있다. 왼쪽에 현재
실행중인 모든 MBean을 트리 구조로 표시한다. MBean을 선택하면 MBeanInfo와 설명자가
오른쪽에 표시된다(그림 3).


그림 3. JConsole의 MBean 탭
JConsole의 MBean 탭

Attributes 노드를 선택하면 MBean의 모든 속성을 표시한다(그림 4).


그림 4. MBean Attributes
MBean 속성

오른쪽에 표시된 속성과 값은 앞에서 설명한 java.lang.management 패키지의 ThreadMXBean
API를 통해 접근할 수 있다는 점을 주목하자. 나열된 속성 값을 더블클릭하면 추가 정보를 얻을 수
있다. 굵게 표시된 속성값은 확장할 수 있다. 예를 들어, AllThreadIds 값을 더블클릭하면 22개
스레드의 스레드 ID를 모두 표시한다(그림 5).


그림 5. 확장된 속성 값
확장된 속성 값

쓰기 가능한 속성은 파란색으로 표시되는데, 클릭한 다음 새 값을 입력해 편집할 수 있다. 예를 들어,
그림 5의 JConsole 화면에서 ThreadContentionMonitoringAvailable 속성을 이 방식으로
편집할 수 있다.

왼쪽 트리 구조에서 Operations 노드를 선택하면 MBean과 연관된 동작들을 표시한다. MBean
동작은 오른쪽에 버튼으로 표시되는데, 클릭하면 지정한 메서드를 호출한다. 그림 6은
ThreadMXBean의 가용한 동작들을 보여준다.


그림 6. MBean Operations
MBean 동작

핫스팟 진단 MBean

자바 SE 6부터 JConsole은 핫스팟 진단(HotSpot Diagnostic) MBean을 지원한다. 이 MBean은
즉석(on-the-spot) 동작을 수행하기 위해 이번 버전에 도입되었다. 이 API를 통해 사용자는 실행
중에 힙을 덤프하거나 다른 VM 옵션을 설정할 수 있다. MBean 탭에서 com.sun.management
노드를 확장하고 HotSpotDiagnostic을 선택하면 핫스팟 진단 MBean에 접근할 수 있다.
그림 7에서 핫스팟 진단 MBean에서 가용한 메서드들을 볼 수 있다.


Figure 7. 핫스팟 진단 MBean
핫스팟 진단 MBean

JConsole 플러그인 지원

자바 SE 6부터 JConsole은 플러그인을 지원한다. JConsole과 함께 실행할 수 있는 고유한
플러그인을 만들 수 있다. 예를 들어, 응용 프로그램에 특화된 MBean에 접근해 고유의 모니터링
 동작을 수행하는 탭을 JConsole의 메인 화면에 추가할 수 있다.

커스텀 JConsole 플러그인을 만들려면 com.sun.tools.jconsole.JConsolePlugin 추상
클래스를 상속해야 한다. JConsole 화면에 제대로 나타나게 하려면 메서드 두 개를 구현해야 한다.

  • newSwingWorker()는 플러그인의 GUI를 갱신하는 SwingWorker 객체를 반환한다.
  • getTabs()는 JConsole 창에 추가될 탭의 맵을 반환한다.

JConsole은 서비스-공급자 메커니즘을 사용해 모든 플러그인 클래스를 감지하고 적재한다.
 이 때문에, 플러그인 클래스는 META-INF/services/com.sun.tools.jconsole.JConsolePlugin
파일을 포함하는 JAR 파일로 제공해야 한다. 이 파일은 한 줄에 하나씩 나열된 플러그인 클래스의
완전한 이름 목록을 포함해야 한다. 아래 명령으로 JConsole을 실행하면 새로운 플러그인
클래스를 JConsole 화면에 불러온다.

jconsole -pluginpath plugin_path
            

이 명령에서, plugin_path는 JConsole 플러그인들이 들어있는 디렉터리나 JAR 파일의 위치다.
여러 개의 경로를 지정할 수 있다.

자바 SE 6에는 JTop이라는 JConsole 플러그인이 예제로 포함되어 있다. JTop은 현재
 응용 프로그램 내에서 실행중인 스레드의 CPU 사용량을 보여준다. JConsole을 JTop과
 함께 실행하려면 아래 명령을 실행하면 된다.

jconsole -pluginpath JAVA_HOME/demo/management/JTop/JTop.jar

그림 8은 JTop 탭이 선택된 JConsole을 보여준다. 왼쪽 컬럼은 실행 중인 스레드의 이름을
표시한다. 각 스레드에 대해 CPU 사용량과 스레드 상태가 표시된다. 통계가 변하면 화면이
자동으로 갱신된다. JTop 플러그인은 CPU 점유율이 높은 스레드를 식별하는 데 유용하다.


그림 8. JConsole의 JTop 플러그인
JConsole의 JTop 플러그인





모니터링 및 문제 해결 도구

자바 SE 6은 JConsole 외에도 몇 가지 다른 명령행 도구를 포함한다. 이 진단 도구들은 응용
프로그램을 특수한 모드로 실행할 필요 없이 아무 응용 프로그램에나 부착할 수 있다.
응용 프로그램이 의도한 대로 동작하는지를 확인하기 위한 정보를 얻을 수 있다. 이 도구들은
실험적이며 이후에 나올 자바 SE 버전에서는 지원되지 않을 수 있음을 주의하자.

모니터링 도구

자바 SE 6은 JVM의 성능 통계를 모니터링하는 데 유용한 명령행 유틸리티들을 포함하고 있다(표 2).


표 2. 모니터링 도구
도구 설명
jps JVM 프로세스 상태 도구
jstat JVM 통계 모니터링 도구
jstatd JVM jstat 데몬

jps 유틸리티는 대상 시스템의 현재 사용자에 대한 가상 머신을 나열한다. 이 유틸리티는
일반적인 자바 실행기 대신, JNI Invocation API를 통해 시작한 VM 환경에 유용하다.
이러한 환경에서는 프로세스 목록에서 자바 프로세스를 식별하는 것이 쉽지만은 않다.
jps 도구는 이 문제를 완화한다.

다음 예제는 jps 사용 예를 보여준다. 단순히 명령행에서 jps를 입력하면, 사용자가 접근
권한을 가진 가상 머신 목록과 프로세스 ID를 나열한다(Listing 3).


Listing 3. jps 유틸리티 사용하기
                
$ jps
16217 MyApplication
16342 jps

jstat 유틸리티는 JVM에 내장된 도구를 사용해 실행 중인 응용 프로그램의 성능과 자원 소모에
 대한 정보를 제공한다. 이 툴은 힙 크기 및 가비지 컬렉션과 연관된 성능 이슈를 진단하는 데
유용하다.

jstatd 데몬은 JVM 생성과 종료를 모니터링하고 원격 모니터링 도구가 로컬 호스트에 실행 중인
 JVM에 부착할 수 있도록 해주는 원격 메서드 호출(Remote Method Invocation, RMI) 서버
애플리케이션이다. 예를 들어, 이 데몬을 통해 jps 유틸리티는 원격 시스템의 프로세스 목록도
나열할 수 있다.

참고자료에서 이 도구들 각각에 대한 추가 문서와 사용 예를 볼 수 있다.

문제 해결 도구

자바 SE 6은 응용 프로그램이 예상치 못한 동작을 할 때 정밀한 처방을 내릴 수 있도록 도와주는
몇 가지 문제 해결 도구들도 포함하고 있다(표 3).


표 3. 문제 해결 도구
도구 설명
jinfo 설정 정보
jhat 힙 덤프 열람기
jmap 메모리 맵
jsadebugd 서비스 가능 여부 진단 에이전트 디버그 데몬
jstack 스택 트레이스

jinfo 명령행 유틸리티는 실행 중인 자바 프로세스의 설정 정보나 충돌 덤프를 추출하고, 가상
머신을 시작할 때 사용한 시스템 프로퍼티나 명령행 플래그를 출력한다.

jhat 도구는 힙의 스냅샷에서 객체 위상을 열람할 수 있는 편리한 수단을 제공한다. 힙 분석 도구
(Heap Analysis Tool, HAT)를 대체하기 위해 자바 SE 6에서 도입된 이 도구는 메모리 누수를
감지하는 데 유용하다.

jmap 명령행 유틸리는 실행 중인 VM이나 코어(core) 파일에 대한 메모리 관련 통계를 출력한다. jsadebugd 데몬을 사용하면 원격 기계의 프로세스나 코어 파일을 조회할 수 있다. jmap 도구는 OutOfMemoryError를 야기할 수 있는 finalizer의 과도한 사용을 진단하는 데 유용하다.

jsadebugd(Serviceability Agent Debug Daemon)은 자바 프로세스나 코어 파일에 부착되어
디버그 서버처럼 동작한다. 이 유틸리티는 현재 솔라리스 OS와 Linux®에서만 사용할 수 있다.
자바 RMI를 사용해 jstack, jmap, jinfo 같은 원격 클라이언트를 이 서버에 부착할 수 있다.

jstack 명령행 유틸리티는 지정한 프로세스나 코어 파일에 부착되어 VM의 내부 스레드를
포함한 가상 머신에 부착된 모든 스레드의 스택 트레이스를 출력하며, 선택적으로 네이티브
스택 프레임도 출력할 수 있다. 이 도구는 jsadebugd 데몬을 통해 원격 기계의 프로세스나
코어 파일을 조회할 수 있다. jstack 도구는 데드락을 진단하는 데 유용하다.

참고자료에서 이 도구들 각각에 대한 추가 문서와 사용 예를 볼 수 있다.






결론

자바 6 플랫폼은 자바 응용 프로그램의 성능과 메모리 문제를 효과적으로 식별하고 진단하는 것을
 돕기 위해 몇 가지 VM 기구, 관리 API, 그리고 JDK 도구들을 강화했다. 이 기사는 자바 SE의
모니터링 및 관리 프레임워크에서 향상된 부분들을 설명하고, 개발자들이 사용할 수 있는 명령행
 진단 유틸리티에 대해 다루었다.

자바 응용 프로그램의 평균 속도는 시간이 지남에 따라 꾸준히 증가하고 있다. 자바 SE 6이
나오면서 자바의 성능은 C나 C++에 비견할 정도가 되었다. 많은 경우에 자바 코드는 눈에
띌 정도로 빨라졌다. 그리고 여기에서 소개한 도구들을 사용해 더 나은 성능 최적화를 이룰 수
있을 것이다. 시도해보자. 예전에는 결코 알 수 없었던, 응용 프로그램의 성능을 향상시킬 수
있는 지점을 발견하게 될 것이라고 장담한다.



참고자료

교육

Caroline Gough, developerWorks, 2006년 4월): MXBean에 대한 더 자세한 정보

썬 개발자 네트워크, 2006년 8월): 자바 SE에서 공통적인 성능 문제 진단하기

이 API를 먼저 살펴보라.

소개한 모니터링 및 문제 해결 도구들에 대한 문서와 예제


토론


필자소개

Cathy Kegley는 IBM의 Lotus Expeditor 클라이언트 팀의 소프트웨어 엔지니어다.


Greg Roberts는 IBM의 Lotus Expeditor 클라이언트 개발 팀의 스태프 소프트웨어 엔지니어다.

반응형

필자 서문


프로그래머들은 솔라리스 제품을 사용하기 시작할 때 프로그래밍 스크립트를 즉시 시작하기를 원합니다. 처음에는 효율성과 간결성에 대해서는 관심들이 없고 오로지 그 결과에만 관심을 가지고 있습니다. 본 기술 자료에서는 신속한 시작을 위해 검증된 쉘 프로그래밍 기법에 대해 설명합니다. 경험이 쌓일수록 자신만의 프로그래밍 스타일을 개발하고 스크립트의 효율성과 간결성을 향상시킬 수 있을 것입니다.

배경


command shell 은 사용자와 상호 작용하고 운영 시스템과 통신하는 레이어입니다 MS-DOS를 사용할 때 대부분의 사람들은 command.com 쉘을 사용하지만, COMSPEC 환경 변수를 통해 다른 쉘이 지정될 수도 있습니다.

이와 유사하게, 각 UNIX 사용자는 UNIX로 전달하기 위해 사용할 명령 쉘을 선택해야 합니다. UNIX 계정이 설정될 때 시스템 관리자는 사용자의 기본 쉘을 선택합니다. 표준 옵션은 Bourne Shell(/bin/sh), C-Shell(/bin/csh), Korn Shell(/bin/ksh), Bourne-Again Shell(/bin/bash)입니다. 많은 개발자들이 C와 유사한 구문 때문에 C-쉘을 사용하지만 이는 주관적인 선택이며, 여기에서는 Korn 쉘만을로 사용합니다. 다른 쉘에서는 구문이 세대로 실행되지 않을 수도 있습니다.

명령행에서 쉘 스크립트를 실행하면 기본 쉘이 사용됩니다. 기본 쉘이 Korn이라면 기술 자료에 있는 스크립트는 구문 오류 없이 실행됩니다. 하지만 스크립트를 실행하기 위해 다른 쉘을 원한다면 어떻게 해야 할까요? 스크립트가 항상 Korn 쉘을 사용해 실행되도록 하려면 사용자의 기본 쉘에만 의존할 수 없습니다. 해결책은 스크립트의 첫 행에 스크립트가 어느 쉘 아래에서 실행될지 표시되는 UNIX 기능을 사용하는 것입니다. 코드 예제 1의 구문은 현재 사용자가 어느 쉘을 사용하든 상관없이 스크립트가 Korn 쉘을 사용해 실행되도록 지시합니다.

#!/bin/ksh

# your script goes here. All lines starting with # 
# are treated as comments
코드 예제 1 - 스크립트가 Korn 쉘에 의해 실행되도록 지정합니다.

일부 문서에서는 표 1에 나타난 것처럼 현재 쉘을 표시하기 위해 다른 명령 프롬프트 기호를 사용합니다. (필자가 가장 선호하는 쉘이 Korn 쉘이기 때문에 본 기술 자료에 나오는 모든 예제는 $-prompt를 사용합니다.) 스크립트가 항상 Korn 쉘을 사용하여 실행되도록 할 수 없기 때문에 각 스크립트의 첫 행으로 #!/bin/ksh를 입력합니다. (본 기술 자료의 $-prompt는 단지 명령이 명령행에 입력되고 있음을 나타냅니다.)

프롬프트 쉘l
$ Bourne 또는 Korn 쉘
% C-shell
# Root login

표 1 - UNIX 프롬프트 기호

스크립트 작성 - 기초


UNIX 스크립트 파일은 DOS BAT 파일과 유사합니다. DOS에서의 프로그래밍 권장 사항과 비권장 사항은 모두 UNIX에도 적용됩니다.

스트립트 작성과 관련된 단계는 다음과 같습니다:

  1. 쉘 프롬프트에서 UNIX 명령을 대화식으로 실행합니다.
  2. UNIX 명령을 포함하는 쉘 스크립트를 작성합니다.
  3. 쉘 스크립트를 실행 가능하도록 만듭니다.
  4. 스크립트를 테스트합니다.
  5. 스크립트를 실행합니다.
    1. 대화식으로
    2. 미래의 어느 일자 및 시간에 단 한 번
    3. 고정된 일정에 따라 반복적으로
    4. HTML 형식 사용

간단한 스크립트 작성


vmstat 정보를 확보하기 위한 스크립트를 작성하고자 한다고 가정해 보십시오. 1분 동안 2초 간격으로 vmstat를 실행하고자 합니다. 위에 설명된 5단계를 사용해 목표를 달성할 수 있습니다.

먼저, man vmstat를 사용해 vmstat에 대한 문서를 조회합니다. 그 다음, 명령을 대화식으로 실행해 구문과 예상되는 출력을 파악합니다. 코드 예제 2는 vmstat를 2초 간격으로 30회 실행하는 구문을 보여줍니다.

$  vmstat  2  30
코드 예제 2 - vmstat 명령을 2초 간격으로 30회 대화식으로 실행

쉘 스크립트 작성


다음으로, 명령을 포함하는 스크립트 파일을 작성합니다. 스크립트 위치와 스크립트 이름을 설명하는 표준을 확립해야 합니다. 예를 들어 회사와 같은 특정 카테고리의 모든 사항을 /usr/local 아래의 하위 디렉토리에 저장합니다. 이 예제에서는 회사를 Acme Products로 가정하므로 디렉토리는 /usr/local/acme입니다. 이 디렉토리 내에 scripts라는 하위 디렉토리와 logs라는 하위 디렉토리를 만드십시오. 목적에 따라 다른 하위 디렉토리가 필요할 수도 있습니다.

다음으로, vi와 같은 텍스트 편집기를 사용해 capture_vmstat.sh라는 스크립트 파일을 작성합니다. EXE, COM, BAT가 실행 가능한 파일을 나타내는 DOS와 달리, UNIX에서는 파일 확장자의 의미가 없습니다. .sh를 확장자로 사용해 쉘 스크립트 파일을 표시할 수 있지만 스크립트를 실행 가능하도록 만들지는 못합니다. 파일에 대한 이 명명 규칙을 통해 보다 쉽고 신속하게 파일을 식별할 수 있습니다. 또한, 파일 이름이 표준을 준수한다면 find 명령을 사용해 특정 유형의 모든 파일을 찾을 수 있습니다.

스크립트 파일은 두 개의 행을 보유합니다. 첫 번째 행은 일말의 여지를 두지 않고 Korn 쉘이 이 스크립트에 있는 명령을 실행하도록 지정합니다. 두 번째 행은 UNIX 명령 그 자체입니다. 코드 예제 3은 capture_vmstat.sh 스크립트의 전체 목록입니다.

#!/bin/ksh

vmstat  2  30
코드 예제 3 - vmstat를 2초 간격으로 30회 실행하는 capture_vmstat.sh 스크립트

쉘 스크립트를 실행 가능하도록 만들기


파일 확장자를 이용해 파일의 실행 가능성 여부를 판단하는 DOS와 달리, UNIX는 파일 접근 권한(file permission)에 의존합니다. chmod 명령은 파일을 실행 가능으로 표시하는 데 사용됩니다.

execute-bit를 설정하는 가장 간단한 방법은 chmod +x capture_vmstat.sh를 사용하는 것입니다. 현업 환경에서는 공개된 서버에서 스크립트에 대한 전체 액세스를 제어하기 위해 소유자, 그룹 및 세계 권한(world permission)을 고려해야 합니다. (파일 접근 권한 주제는 본 문서의 범위를 벗어납니다.) 보다 자세한 정보는 man chmod를 참조하십시오.

쉘 스크립트 테스트


이제 스크립트를 테스트할 준비가 되었습니다. DOS와 달리, UNIX는 실행할 파일을 현재 디렉토리에서 자동으로 찾지 않습니다. UNIX는 PATH 환경 변수를 제공하며 단지 PATH 변수에서 확인된 디렉토리에서 실행 파일을 검색합니다. 대부분의 사람들이 PATH에 현재 디렉토리를 포함시키지 않아(점이 현재 디렉토리를 나타냄) /usr/local/acme/scripts가 PATH에 존재하지 않기 때문에 코드 예제 4의 명령을 입력하는 것만으로는 실행되지 않습니다.

$  cd /usr/local/acme/scripts

$  capture_vmstat.sh
코드 예제 4 - "."이 PATH에 없으면 스크립트가 실행되지 않습니다

경로를 포함하여 스크립트의 전체 파일명을 명시적으로 지정해야 합니다. 나중에 변경되거나 둘 중 하나가 잘못될 가능성이 있으므로 PATH 변수에 의존하지 마십시오. 우선, 스크립트가 있던 디렉토리가 실수로 PATH에서 제거될 수 있으며 이 경우 UNIX는 더 이상 스크립트를 찾지 못할 것입니다. 심지어 UNIX는 새로운 PATH에 있는 다른 디렉토리에서 동일한 이름을 가진 스크립트를 찾아 실행할 수도 있습니다. 그러므로 안전을 위해서는 코드 예제 5에서처럼 전체 파일명을 지정하여 스크립트를 실행해야 합니다.

$  /usr/local/acme/scripts/capture_vmstat.sh
코드 예제 5 - UNIX가 올바른 스크립트를 찾을 수 있도록 전체 파일명 지정

일일이 입력하기가 번거로우므로 "."(점)이 현재 디렉토리를 나타낸다고 가정합니다. 우선, 스크립트 디렉토리로 변경한 다음 코드 예제 6에서처럼 스크립트명에 "./"(점-슬래시)를 덧붙여 스크립트를 실행합니다. 스크립트 하나만 실행하는 경우에는 별 차이가 없지만 스크립트 디렉토리에서 여러 개의 스크립트를 실행하는 경우에는 디렉토리명을 한 번만 입력하면 되는 장점이 있습니다.

$  cd /usr/local/acme/scripts

$  ./capture_vmstat.sh
코드 예제 6 - 점-슬래시(./) 표기를 사용해 스크립트 실행

capture_vmstat.sh 스크립트를 어떤 방법으로 호출하든 간에, vmstat를 대화식으로 실행할 때 얻은 결과와 출력이 동일해야 합니다.

스크립트 실행


이제 스크립트가 작성되었고 그 작동 방식을 알고 있습니다. 스크립트를 다음 네 가지 방법으로 실행할 수 있습니다:

1.대화식


스크립트를 문서화하여 다른 사람(헬프 데스크 직원 등)이 스크립트 파일을 실행할 수 있도록 합니다. DOS 사용자가 그들을 위해 만들어진 BAT 파일을 사용하기 위해 DOS 명령이나 구문을 이해할 필요가 없는 것과 매한가지로, 스크립트를 실행하는 사람들이 UNIX 명령이나 구문을 알아야 할 필요는 없습니다.

2.at 명령 사용


at 명령을 사용해 향후 언젠가 한번 스크립트를 실행합니다. 자세한 내용은 man at을 확인하십시오. 일부 UNIX 시스템은 사용자가 로그아웃하면 at-jobs 실행을 취소합니다. 시스템 문서를 신중하게 확인합니다.

3.cron 유틸리티 사용


crontab 파일을 사용해 고정된 일정에 따라 스크립트를 반복적으로 실행합니다. 자세한 내용은 man crontab을 확인합니다. 코드 예제 7은 월/수/금요일마다 오전 8시-오후 5시까지 한 시간에 한번 X시 10분에 스크립트를 실행하는 간단한 crontab 입력을 보여줍니다:

10   8-17   *   *  1,3,5  /usr/local/acme/scripts/capture_vmstat.sh
코드 예제 7 - capture_vmstat.sh를 실행하는 crontab 입력 script

스크립트를 실행하기 위한 네 번째 방법으로 넘어가기 전에, crontab을 통해 스크립트를 실행할 경우 생기는 두 가지 문제를 이해해야 합니다. 첫째, 스크립트가 실행될 때 로그인하지 않았기 때문에 기본 쉘이 되는 Korn 쉘에 의존할 수 없습니다. 그러므로, 코드 예제 1에 설명된 것처럼 #!/bin/ksh를 스크립트의 첫 행으로 사용하도록 해야 합니다. 둘째, 현재 버전의 스크립트는 터미널로 출력을 전송합니다. cron이 스크립트를 실행할 때 터미널이 없으므로, cron은 stdout의 경로를 다른 곳으로 재지정해야 합니다. 정상적인 위치는 crontab으로 스크립트를 실행한 사용자의 e-메일 수신함(inbasket)입니다. 이 방법도 괜찮지만, 기본 스크립트를 확장할 때 아래에 설명된 다른(더 나은) 해결책을 사용할 수 있습니다.

4.HTML 형식 사용


HTML 형식을 사용해 스크립트를 실행하고 CGI(common gateway interface)를 통해 스크립트를 게시합니다. 명령의 출력은 브라우저로 다시 전송되므로 <PRE>와 </PRE> HTML 태그는 서식을 보존하는 데 사용되어야 합니다.

이 HTML 형식 메소드는 여기서 설명한 것보다 많으며, FORM과 CGI를 사용할 경우 수많은 보안 위험이 존재합니다. 그러나, 이 메소드는 사내 헬프 데스크 직원이나 기타 레벨1 지원 인력이 사용하기에 매우 성공적인 것으로 입증되었습니다.

간단한 스크립트 확장


이전 스크립트는 새로운 프로그래밍 언어를 배울 때 작성되는 최초의 표준 프로그램인 "hello, world"의 쉘 스크립트 버전이었습니다. 이제 몇 가지 기본 기능을 여기에 추가할 수 있습니다.

stdout 경로 재지정


먼저, 스크립트는 출력을 stdout으로 전송합니다(정상적으로는 터미널임). 스크립트를 확장해 코드 예제 8에서처럼 출력 경로를 로그 파일로 재지정할 수 있습니다.

#!/bin/ksh

vmstat  2  30  >  /usr/local/acme/logs/vmstat.log
코드 예제 8 - stdout 경로를 파일로 재지정

그러나 이로 인해 몇 가지 새로운 문제가 발생합니다. 우선, 스크립트를 실행할 때마다 마지막 로그 파일의 내용을 덮어쓰게 됩니다.+ 이 문제를 해결하려면 기존 로그 파일의 끝에 새로운 출력을 덧붙입니다. 파일에 있는 date-time stamp는 마지막 로그가 작성된 시점만을 표시하므로 이제 로그에 있는 각 출력이 작성된 시점을 알아야 합니다.

스크립트 내 하위 명령 실행

스크립트의 각 실행보다 앞서는 파일에 현재의 일자와 시간을 기록합니다. 기존 파일을 덮어쓰는 대신 >>를 사용해 파일 끝에 출력을 덧붙입니다. 코드 예제 9에서, find 및 find-next를 사용해 파일을 검사하기 쉽도록 독립적으로 파악할 수 있는 문자를 열 1에 놓습니다. 또한 로그 파일에 현재의 일자와 시간을 기록할 수도 있습니다. 코드 예제 9 $(date)는 Korn 쉘이 date 명령을 실행하고 출력을 echo 명령행으로 배치하도록 지시합니다. UNIX 명령을 실행하고 출력을 사용하려고 할 때마다 $를 입력하고 괄호 안에 명령을 넣으십시오.

#!/bin/ksh

echo "#--- $(date)"  >> /usr/local/acme/logs/vmstat.log

vmstat  2  30	>> /usr/local/acme/logs/vmstat.log
코드 예제 9 - 로그 파일에 stdout 추가

코드 예제 10에서 Korn 쉘은 netstat 명령, grep for ESTABLISH를 실행하고, 이들 명령을 $(xxx) 안에 넣어 행 수를 세는 데 wc를 사용하도록 지정될 수 있습니다. 나아가 Korn 쉘은 이러한 명령의 출력을 환경 변수 CTR_ESTAB에 저장하도록 지시됩니다. 그 다음, echo 명령에서 Korn 쉘은 해당 환경 변수에 저장된 값을 사용하도록 지시됩니다. 환경 변수에 저장된 값을 사용하려면 변수 이름 앞에 $를 넣습니다(예: $CTR_ESTAB). 가독성을 높이고 모호성을 방지하려면 중괄호 안에 변수 이름을 넣는 Korn 쉘 옵션을 사용합니다(예: ${CTR_ESTAB}).

# store current date as YYYYMMDD in variable DATE for later 
# use

export DATE=$(date +m%d)

# count number of established socket connections and write. 
# to log

export CTR_ESTAB=$(netstat -na -P tcp | grep ESTABLISH | wc 
  -l)

export CTR_CLOSE_WAIT=$(netstat -na -P tcp | grep CLOSE WAIT 
  | wc -l)

echo "${DATE} ${CTR_ESTAB} ${CTR_CLOSE_WAIT} >> ${LOG_FILE}
코드 예제 10 - $(xxx)를 사용해 Korn 쉘 스크립트 내 명령 실행

고유 파일명 생성


여러 사용자가 스크립트를 동시에 실행하면 어떤 일이 발생할까요? 스크립트의 각 인스턴스는 동일한 출력 파일에 기록할 것이므로, 각 스크립트의 출력은 출력 파일에 인터리브됩니다. 코드 예제 11에서처럼 파일 이름에 PID 번호($$로 표현)를 넣어 고유한 출력 파일명을 작성할 수 있습니다.

#!/bin/ksh

echo "#--- $(date)" 
  >> /usr/local/acme/logs/vmstat.$$.log

vmstat  2  30	>> /usr/local/acme/logs/vmstat.$$.log
코드 예제 11 - $$를 사용해 현재 PID를 사용하는 고유한 파일명 생성

다음 사용자가 스크립트를 실행할 때 다른 PID가 스크립트 실행에 할당되기 때문에 기존 로그 파일에 추가되는 것이 아니라 매번 별도의 로그 파일을 작성하게 됩니다 잘못된 것은 아니지만 이런 결과를 원하지도 않았습니다.

스크립트가 실행될 때마다 값이 변경되는 환경 변수를 사용하는 대신 스크립트 실행 이전에 스크립트 외부에서 한 번 설정된 환경 변수를 사용하는 방법도 생각해볼 수 있습니다. UNIX는 사용자가 로그인할 때마다 LOGNAME 환경 변수를 설정합니다. 코드 예제 12에서, 이 값은 각 사용자가 로그 파일을 가질 수 있도록 로그 파일명에 들어갑니다:

#!/bin/ksh

echo "#--- $(date)"	
  >> /usr/local/acme/logs/vmstat.${LOGNAME}.log

vmstat  2  30		
  >> /usr/local/acme/logs/vmstat.${LOGNAME}.log
코드 예제 12 - 값이 외부에서 설정된 환경 변수를 사용해 파일명 생성

구조적 프로그래밍 기법


두 번의 최종 마무리 점검을 거쳐 기본 Korn 쉘 스크립트를 완료합니다. 첫째, vmstat 명령의 빈도 또는 기간을 변경하고자 하는 경우 어떻게 될까요? vmstat 명령의 간격과 기간을 하드 코딩(hard-coding)하는 대신 명령행 인수를 사용해 해당 값을 받아들일 수 있습니다. 이러한 인수는 vmstat 명령이 인수를 액세스할 수 있는 환경 변수에 저장될 수 있습니다. 물론, 사용자가 명령행을 사용해 값을 제공하지 않는 경우 스크립트는 기본값을 제공해야 합니다.

둘째, 로그 파일 명명 규칙에 대한 생각을 변경하면 어떻게 됩니까? 명명 규칙은 사용자에게 명령행 인수를 사용할 때마다 제공할 것을 요구하는 사항이 아닙니다. 그러나, 스크립트의 여러 행에 하드 코딩된 로그 파일명이 있을 경우 다른 명명 규칙을 사용하기로 결정하면 이름이 지정된 위치를 확인하기 위해 스크립트의 모든 행을 검색해야 합니다.

그 대신, 환경 변수에 로그 파일명을 저장하고 변수에 포함된 파일명에 출력을 덧붙이도록 각 명령을 수정하십시오. 그러면 로그 파일 명명 규칙을 변경할 때 환경 변수가 설정된 한 행을 수정하기만 하면 됩니다.

#!/bin/ksh
# ----------------------------------------------------
# capture_vmstat.sh	<INTERVAL> <COUNT>
#	<INTERVAL> vmstat interval
#	<COUNT>	vmstat count
# run vmstat and capture output to a log file
#-----------------------------------------------------

# indicate defaults for how often and for how long 
# to run vmstat
export INTERVAL=2		# every 2 seconds
export COUNT=30		# do it 30 times

# obtain command line arguments, if present
if [ "${1}" != "" ]
then
	INTERVAL=${1}
	# if there is one command line argument, 
	# maybe there's two
	if [ "${2}" != "" ]
	then
	COUNT=${2}
	fi
fi

# directories where scripts and logs are stored
export PROGDIR=/usr/local/acme/scripts
export LOGDIR=/usr/local/acme/logs

# define logfile name and location
export LOG_FILE=${LOGDIR}/capture_vmstat.${LOGNAME}.log

# write current date/time to log file
echo "#--- $(date)"		>> ${LOG_FILE}
vmstat  ${INTERVAL}  ${COUNT}	>> ${LOG_FILE}

# say goodnight, Gracie
exit 0
코드 예제 13 - capture_vmstat.sh 스크립트의 더욱 강력한 버전

for-loop 스크립트 작성


객체 목록에 대해 단일 명령을 실행하고자 하는 경우가 있습니다. 예를 들어, rsh 명령을 사용해 여러 서버에 대해 동일한 명령을 원격으로 실행하려 할 수도 있습니다(자세한 내용과 r-명령을 사용할 때의 보안 위험에 대해서는 man rsh를 참조합니다). .

한 가지 기법은 환경 변수에 LIST라고 하는 객체 목록을 저장하는 것입니다. 그러면 for loop를 사용해 rsh 명령을 반복적으로 실행할 수 있으며, 각 루프는 LIST에 다음 값을 보유하게 됩니다. 코드 예제 14는 for-loop 스크립트 샘플을 보여줍니다.

#!/bin/ksh

export LIST="bvapp1 bvapp2 bvapp3"

export LOG=/usr/local/acme/logs/throw_away.log

for SERVER in ${LIST}
do
  # each loop has a different value for ${SERVER}
  echo "#------- values from ${SERVER}" >> ${LOG}
  rsh  ${SERVER} 
  "ps -f -u bv -o pid,pmem,pcpu,rss,vsz" >> ${LOG}
done

# say goodnight, Gracie
exit 0
코드 예제 14 - 간단한 for-loop 스크립트

while-loop 스크립트 작성


경우에 따라 단일 명령을 실행하고 잠시 기다린 다음 명령을 다시 실행하기를 원할 수도 있습니다. 때로는 이 루프가 무제한 계속되기를 원할 수도 있고 때로는 루프가 한정된 횟수 만큼 실행된 다음 종료되기를 원합니다.

사용자 bv 아래에서 실행되는 프로세스를 모니터링하려고 하며 2시간 동안 10분마다 bv를 모니터링하기를 원한다고 가정해 보겠습니다. 우선, 코드 예제 15에 나오는 코드를 사용해 명령을 대화식으로 테스트합니다(자세한 내용은 man ps를 참조합니다):

ps  -f -u bv  -o  pid,pcpu,pmem,rss,vsz,comm
코드 예제 15 - -o 인수를 사용하는 대화식 ps 명령

이제 이 명령을 루프에서 실행하는 스크립트 파일을 작성해야 합니다. 루프는 ps 명령 실행 간의 10초 동안 일시 정지하고 720회 실행해야 합니다[2시간 동안 10초마다 한번, 즉 분당 6회 또는 시간당 360회(60분/시 * 6/분)]. 코드 예제 16은 간단한 while-loop 스크립트를 보여줍니다.

#!/bin/ksh

export INTERVAL=10
export COUNT=720

export LOG=/usr/local/acme/logs/while_loop_test.log

export CTR=0
while [ true ]
do
	if [ ${CTR} -ge ${COUNT} ]
	then
		exit
	fi
	echo "#------- $(date +m%d-03/24/03M%S)"	
          >> ${LOG}
	ps  -f  -u  bv  -o  pid,pcpu,pmem,rss,vsz,comm	
          >> ${LOG}
	CTR=$(expr ${CTR} + 1)
	sleep ${INTERVAL}
done
코드 예제 16 - 간단한 while-loop 스크립트

코드 예제 17은 출력 로그 파일의 일부분을 보여줍니다.

#------- 19991203-123237
  PID %CPU %MEM  RSS  VSZ COMMAND
12007  0.2  0.8 13640 24280 cmsdb
11938  0.0  0.7 11536 20496 sched_poll_d
<snip>
#------- 19991203-123240
  PID %CPU %MEM  RSS  VSZ COMMAND
12007  0.2  0.8 13640 24280 cmsdb
11938  0.0  0.7 11536 20496 sched_poll_d
<snip>
#------- 19991203-123243
  PID %CPU %MEM  RSS  VSZ COMMAND
12007  0.3  0.8 13640 24280 cmsdb
11938  0.0  0.7 11536 20496 sched_poll_d
<snip>
#------- 19991203-123246
<and-so-on>
코드 예제 17 - while-loop 스크립트의 출력

퀵 레퍼런스 카드(Quick Reference Card)


아래 프로그래밍 팁과 기법은 본 기술 자료에 제시된 프로그래밍 스타일과 방법론에 대한 퀵 레퍼런스로서 여기에서 다룬 주제에 대한 퀵 레퍼런스 버전을 확인할 수 있을 것입니다.

1. 항상 다음과 같은 행으로 스크립트를 시작합니다


#!/bin/ksh

2. 항상 변수를 정의할 때 대문자를 사용합니다. 소문자를 사용해 단어를 분리합니다.


BIN_DIR=/opt/bv1to1/bin

3. 하위 프로세스가 값을 자동으로 액세스하도록 항상 환경 변수를 익스포트합니다:


export SUPPORT_IDS="userA@domain.com,userB@domain.com

4. UNIX 명령을 실행하고 Korn 쉘 스크립트의 어딘가에 출력을 사용하려면 $를 입력하고 괄호 안에 명령을 넣은 다음 출력을 환경 변수에 저장합니다.


export CTR_ESTAB=$(netstat -na | grep ESTABLISH | wc -l)

5. 환경 변수에 저장된 값을 사용하려면 변수명 앞에 $를 넣습니다. 가독성을 높이고 모호성을 방지하려면 중괄호 안에 변수 이름을 넣습니다.


echo "The number of ESTABLISHED connections is ${CTR_ESTAB}"

6. 고유한 파일명을 갖게 하려면 $$를 사용해 파일명에 PID 번호를 포함시킵니다. 파일 확장자 바로 앞의 파일명에 PID 번호를 삽입합니다:


export LOG_FILE=/tmp/capture_vmstat.$$.log

7. chmod +x 파일명을 사용해 스크립트 파일을 실행 가능하도록 만듭니다.


chmod  +x  capture_vmstat.sh

8. 스크립트가 현재 디렉토리에 있다는 것을 UNIX가 알 수 있도록 스크립트 이름 앞에 점-슬래시(./)를 넣습니다.


./capture_vmstat.sh

9. stdout ( > )의 경로를 로그 파일로 재지정하거나 stdout ( >> )을 로그 파일에 덧붙입니다.


./capture_vmstat.sh >> ${LOG_FILE}

10. stderr의 경로를 stdout과 동일한 대상 또는 고유한 파일로 재지정합니다.


./capture_vmstat.sh  >> ${LOG_FILE}  2>&1

- or -

./capture_vmstat.sh  >> ${LOG_FILE}  2>>${ERR_LOG}

11. for-loop를 사용해 목록을 처리합니다.


export LIST=$(ls *sh)
for FILE in ${LIST}
do
	echo "Processing ${FILE}"
	cat ${FILE} | mailx -s "Here is ${FILE}" 
          userA@domain.com
done

Use the while-loop to process the same command 
  repeatedly.
export INTERVAL=20
export COUNT=180

export CTR=0
while [ true ]
do
if [ ${CTR} -ge ${COUNT} ]
	then
		exit
	fi
	# --- do some command here ---
	sleep ${INTERVAL}
	      CTR=$(expr ${CTR} + 1) 
done

참조 자료


UNIX Shell Programming (Hayden Books UNIX System Library)


필자: Stephen G. Kochan, Patrick H. Wood
페이퍼백 - 490페이지
2번째 개정판(1990년 1월)
Hayden Books; ISBN: 067248448X

필자 약력


켄 코트리(Ken Gottry)는 현재 NerveWire, Inc.의 수석 인프라 아키텍트로 근무하고 있으며, 메인프레임에서 데스크탑에 이르는 시스템 부문에서 30년 간 경력을 쌓았습니다. 지난 10년 동안 그는 분산, 다계층 및 웹 기반 시스템의 설계, 구현, 튜닝 업무에 전념해 왔습니다. 또한, 수많은 G2K 업체에 자문을 제공하는 성능 엔지니어로서 웹 서버, 애플리케이션 서버, Solaris상에서 실행되는 JVM을 평가하고 튜닝했습니다.

켄의 기술 자료는 썬의 개발자 웹 사이트에 게재되었던 내용이며, 최근 SysAdmin 매거진에서 Solaris 성능 튜닝에 대한 기술 자료도 발표됐습니다.

반응형

Bash를 이용한 쉘 스크립팅 완전 가이드

Mendel Cooper

Brindlesoft

thegrendel (at) theriver.com



 

차례

Part 1. 소개
1. 왜 쉘 프로그래밍을 해야 하죠?
2. #! 으로 시작하기
2.1. 스크립트 실행하기
2.2. 몸풀기 연습문제(Preliminary Exercises)
Part 2. 기초 단계
3. 종료와 종료 상태(Exit and Exit Status)
4. 특수 문자
5. 변수와 매개변수 소개
5.1. 변수 치환(Variable Substitution)
5.2. 변수 할당(Variable Assignment)
5.3. Bash 변수는 타입이 없다(untyped)
5.4. 특수한 변수 타입
6. 쿼우팅(quoting)
7. 테스트
7.1. 테스트(Test Constructs)
7.2. 파일 테스트 연산자
7.3. 비교 연산자(이진)
7.4. 중첩된 if/then 조건 테스트
7.5. 여러분이 테스트문을 얼마나 이해했는지 테스트 해보기
8. 연산자 이야기(Operations and Related Topics)
8.1. 연산자(Operators)
8.2. 숫자 상수(Numerical Constants)
Part 3. 중급 단계(Beyond the Basics)
9. 변수 재검토(Variables Revisited)
9.1. 내부 변수(Internal Variables)
9.2. 문자열 조작
9.3. 매개변수 치환(Parameter Substitution)
9.4. 변수 타입 지정: declaretypeset
9.5. 변수 간접 참조
9.6. $RANDOM: 랜덤한 정수 만들기
9.7. 이중소괄호(The Double Parentheses Construct)
10. 루프와 분기(Loops and Branches)
10.1. 루프
10.2. 중첩된 루프
10.3. 루프 제어
10.4. 테스트와 분기(Testing and Branching)
11. 내부 명령어(Internal Commands and Builtins)
11.1. 작업 제어 명령어
12. 외부 필터, 프로그램, 명령어
12.1. 기본 명령어
12.2. 복잡한 명령어
12.3. 시간/날짜 명령어
12.4. 텍스트 처리 명령어
12.5. 파일, 아카이브(archive) 명령어
12.6. 통신 명령어
12.7. 터미널 제어 명령어
12.8. 수학용 명령어
12.9. 기타 명령어
13. 시스템과 관리자용 명령어
14. 명령어 치환(Command Substitution)
15. 산술 확장(Arithmetic Expansion)
16. I/O 재지향
16.1. exec 쓰기
16.2. 코드 블럭 재지향
16.3. 응용
17. Here Documents
18. 쉬어가기
Part 4. 고급 주제들(Advanced Topics)
19. 정규 표현식(Regular Expressions)
19.1. 정규 표현식의 간략한 소개
19.2. Globbing
20. 서브쉘(Subshells)
21. 제한된 쉘(Restricted Shells)
22. 프로세스 치환(Process Substitution)
23. 함수
23.1. 복잡 함수와 함수의 복잡성(Complex Functions and Function Complexities)
23.2. 지역 변수와 재귀 함수(Local Variables and Recursion)
24. 별칭(Aliases)
25. 리스트(List Constructs)
26. 배열
27. 파일들
28. /dev 와 /proc
28.1. /dev
28.2. /proc
29. 제로와 널(Of Zeros and Nulls)
30. 디버깅
31. 옵션
32. 몇 가지 지저분한 것들(Gotchas)
33. 스타일 있게 스크립트 짜기
33.1. 비공식 쉘 스크립팅 스타일시트
34. 자질구레한 것들
34.1. 대화(interactive)형 모드와 비대화(non-interactive)형 모드 쉘과 스크립트
34.2. 쉘 래퍼(Shell Wrappers)
34.3. 테스트와 비교: 다른 방법
34.4. 최적화
34.5. 팁 모음(Assorted Tips)
34.6. 괴상한 것(Oddities)
34.7. 이식성 문제(Portability Issues)
34.8. 윈도우즈에서의 쉘 스크립팅
35. Bash, 버전 2
36. 후기(Endnotes)
36.1. 저자 후기(Author's Note)
36.2. 저자에 대해서
36.3. 이 책을 만드는데 쓴 도구들
36.3.1. 하드웨어
36.3.2. 소프트웨어와 프린트웨어
36.4. 크레딧
서지사항
A. 여러분들이 보내준 스크립트들(Contributed Scripts)
B. Sed 와 Awk 에 대한 간단한 입문서
B.1. Sed
B.2. Awk
C. 특별한 의미를 갖는 종료 코드
D. I/O와 I/O 재지향에 대한 자세한 소개
E. 지역화(Localization)
F. 샘플 .bashrc 파일
G. 도스(DOS) 배치 파일을 쉘 스크립트로 변환
H. 연습문제
I. Copyright
예 목록
2-1. cleanup: /var/log 에 있는 로그 파일들을 청소하는 스크립트
2-2. cleanup: 위 스크립트의 향상되고 일반화된 버전.
3-1. 종료/종료 상태
3-2. !으로 조건을 부정하기
4-1. 코드 블럭과 I/O 재지향
4-2. 코드 블럭의 결과를 파일로 저장하기
4-3. 최근 하루동안 변경된 파일들을 백업하기
5-1. 변수 할당과 치환
5-2. 평범한 변수 할당
5-3. 평범하고 재미있는 변수 할당
5-4. 정수? 문자열?
5-5. 위치 매개변수
5-6. wh, whois 도메인 네임 룩업
5-7. shift 쓰기
6-1. 이상한 변수를 에코하기
6-2. 이스케이프된 문자들
7-1. 무엇이 참인가?
7-2. [ ]test 의 동일함
7-3. (( ))로 산술식 테스트 하기
7-4. 산술 비교와 문자열 비교
7-5. 문자열이 인지 테스트 하기
7-6. zmost
8-1. 산술 연산자 쓰기
8-2. && 와 || 를 쓴 복합 조건 테스트
8-3. 숫자 상수 표기법:
9-1. $IFS 와 빈 칸
9-2. 타임 아웃 처리 입력
9-3. 타임 아웃 처리 입력, 한 번 더
9-4. 내가 루트인가?
9-5. arglist: $* 과 $@ 로 인자를 나열하기
9-6. 일관성 없는 $*$@의 동작
9-7. $IFS 가 비어 있을 때 $*$@
9-8. 밑줄 변수(underscore variable)
9-9. 그래픽 파일을 다른 포맷 확장자로 이름을 바꾸면서 변환
9-10. 매개변수 치환과 : 쓰기
9-11. 변수의 길이
9-12. 매개변수 치환에서의 패턴 매칭
9-13. 파일 확장자 바꾸기:
9-14. 임의의 문자열을 파싱하기 위해 패턴 매칭 사용하기
9-15. 문자열의 접두, 접미어에서 일치하는 패턴 찾기
9-16. declare를 써서 변수 타입 지정하기
9-17. 간접 참조
9-18. awk에게 간접 참조를 넘기기
9-19. 랜덤한 숫자 만들기
9-20. RANDOM 으로 주사위를 던지기
9-21. RANDOM 에 seed를 다시 지정해 주기
9-22. C 형태의 변수 조작
10-1. 간단한 for 루프
10-2. 각 [list] 항목이 인자를 두 개씩 갖는 for
10-3. Fileinfo: 변수에 들어 있는 파일 목록에 대해 동작
10-4. for 문에서 파일 조작하기
10-5. in [list]가 빠진 for
10-6. for 문의 [list]에 명령어 치환 쓰기
10-7. 이진 파일에 grep 걸기
10-8. 특정 디렉토리의 모든 바이너리 파일에 대해 원저작자(authorship)를 확인 하기
10-9. 디렉토리에 들어 있는 심볼릭 링크들을 나열하기
10-10. 디렉토리에 들어 있는 심볼릭 링크들을 파일로 저장하기
10-11. C 형태의 for 루프
10-12. 배치 모드로 efax 사용하기
10-13. 간단한 while 루프
10-14. 다른 while 루프
10-15. 다중 조건 while 루프
10-16. C 형태의 문법을 쓰는 while 루프
10-17. until 루프
10-18. 중첩된 루프
10-19. 루프에서 breakcontinue의 영향
10-20. 여러 단계의 루프에서 탈출하기
10-21. 더 상위 루프 레벨에서 계속하기(continue)
10-22. case 쓰기
10-23. case로 메뉴 만들기
10-24. case용 변수를 만들기 위해서 명령어 치환 쓰기
10-25. 간단한 문자열 매칭
10-26. 입력이 알파벳인지 확인하기
10-27. select로 메뉴 만들기
10-28. 함수에서 select를 써서 메뉴 만들기
11-1. printf가 실제로 쓰이는 예제
11-2. read로 변수 할당하기
11-3. read로 여러줄의 입력 넣기
11-4. read파일 재지향과 같이 쓰기
11-5. 현재 작업 디렉토리 변경하기
11-6. let으로 몇 가지 산술 연산을 하기.
11-7. eval의 효과 보여주기
11-8. 강제로 로그 아웃 시키기
11-9. "rot13" 버전
11-10. 위치 매개변수와 set 쓰기
11-11. 변수를 "언셋"(unset) 하기
11-12. export를 써서, 내장된 awk 스크립트에 변수를 전달하기
11-13. getopts로 스크립트로 넘어온 옵션과 인자 읽기
11-14. 데이타 파일 "포함하기"
11-15. exec 효과
11-16. 작업을 계속 해 나가기 전에 프로세스가 끝나길 기다리기
12-1. CDR 디스크를 구울 때 ls로 목차 만들기
12-2. Badname, 파일 이름에 일반적이지 않은 문자나 공백 문자를 포함하는 파일을 지우기.
12-3. inode 로 파일을 지우기
12-4. 시스템 로그 모니터링용 xargs 로그 파일
12-5. copydir. xargs로 현재 디렉토리를 다른 곳으로 복사하기
12-6. expr 쓰기
12-7. date 쓰기
12-8. 스크립트에서 두 파일을 비교하기 위해 cmp 쓰기.
12-9. 낱말 빈도수 분석
12-10. 10자리 랜덤한 숫자 만들기
12-11. tail로 시스템 로그를 모니터하기
12-12. 스크립트에서 "grep"을 에뮬레이트 하기
12-13. 목록에 들어 있는 낱말들의 유효성 확인하기
12-14. toupper: 파일 내용을 모두 대문자로 바꿈.
12-15. lowercase: 현재 디렉토리의 모든 파일명을 소문자로 바꿈.
12-16. du: 도스용 텍스트 파일을 UNIX용으로 변환.
12-17. rot13: 초허접(ultra-weak) 암호화, rot13.
12-18. "Crypto-Quote" 퍼즐 만들기
12-19. 파일 목록 형식화.
12-20. column 으로 디렉토리 목록을 형식화 하기
12-21. nl: 자기 자신에게 번호를 붙이는 스크립트.
12-22. cpio로 디렉토리 트리 옮기기
12-23. rpm 아카이브 풀기
12-24. C 소스에서 주석을 제거하기
12-25. /usr/X11R6/bin 둘러보기
12-26. basenamedirname
12-27. 인코드된 파일을 uudecode하기
12-28. 저당에 대한 월 상환액(Monthly Payment on a Mortgage)
12-29. 진법 변환(Base Conversion)
12-30. 다른 방법으로 bc 실행
12-31. seq로 루프에 인자를 만들어 넣기
12-32. 키보드 입력을 갈무리하기
12-33. 파일을 안전하게 지우기
12-34. m4 쓰기
13-1. 지움 글자(erase character) 세팅하기
13-2. 비밀스런 비밀번호: 터미널 에코 끄기
13-3. 키누름 알아내기
13-4. pidof 로 프로세스를 죽이기
13-5. CD 이미지 확인하기
13-6. 한 파일에서 한번에 파일 시스템 만들기
13-7. 새 하드 드라이브 추가하기
13-8. killall, /etc/rc .d/init.d 에서 인용
16-1. exec으로 표준입력을 재지향 하기
16-2. 재지향된 while 루프
16-3. 다른 형태의 재지향된 while 루프
16-4. 재지향된 until 루프
16-5. 재지향된 for 루프
16-6. 재지향된 for 루프(표준입력, 표준출력 모두 재지향됨)
16-7. 재지향된 if/then 테스트
16-8. 이벤트 로깅하기
17-1. dummyfile: 두 줄짜리 더미 파일 만들기
17-2. broadcast: 로그인 해 있는 모든 사람들에게 메세지 보내기
17-3. cat으로 여러 줄의 메세지 만들기
17-4. 탭이 지워진 여러 줄의 메세지
17-5. Here document에서 매개변수 치환하기
17-6. 매개변수 치환 끄기
17-7. upload: "Sunsite" incoming 디렉토리에 파일 한 쌍을 업로드
17-8. "아무개"(anonymous) Here Document
20-1. 서브쉘에서 변수의 통용 범위(variable scope)
20-2. 사용자 프로파일 보기
20-3. 프로세스를 서브쉘에서 병렬로 돌리기
21-1. 제한된 모드로 스크립트 돌리기
23-1. 간단한 함수
23-2. 매개변수를 받는 함수
23-3. 두 숫자중 큰 수 찾기
23-4. 숫자를 로마 숫자로 바꾸기
23-5. 함수에서 큰 값을 리턴하는지 테스트하기
23-6. 큰 두 정수 비교하기
23-7. 사용자 계정 이름에서 실제 이름을 알아내기
23-8. 지역 변수의 영역(Local variable visibility)
23-9. 지역 변수를 쓴 재귀 함수
24-1. 스크립트에서 쓰이는 별칭(alias)
24-2. unalias: 별칭을 설정, 해제하기
25-1. "and list"를 써서 명령어줄 인자 확인하기
25-2. "and list"를 써서 명령어줄 인자를 확인하는 다른 방법
25-3. "or lists""and list"를 같이 쓰기
26-1. 간단한 배열 사용법
26-2. 배열의 특별한 특성 몇 가지
26-3. 빈 배열과 빈 원소
26-4. 아주 오래된 친구: 버블 정렬(Bubble Sort)
26-5. 복잡한 배열 어플리케이션: 에라토스테네스의 체(Sieve of Erastosthenes)
26-6. 복잡한 배열 어플리케이션: 기묘한 수학 급수 탐색(Exploring a weird mathematical series)
26-7. 2차원 배열을 흉내낸 다음, 기울이기(tilting it)
28-1. 특정 PID와 관련있는 프로세스 찾기
28-2. 온라인 연결 상태
29-1. 쿠키 항아리를 숨기기
29-2. /dev/zero로 스왑 파일 세팅하기
29-3. 램디스크 만들기
30-1. 버그 있는 스크립트
30-2. test24, 버그가 있는 다른 스크립트
30-3. "assert"로 조건을 테스트하기
30-4. exit 잡아채기(Trapping at exit)
30-5. Control-C 가 눌렸을 때 깨끗이 청소하기
30-6. 변수 추적하기
32-1. 서브쉘 함정(Subshell Pitfalls)
34-1. 쉘 래퍼(shell wrapper)
34-2. 조금 복잡한 쉘 래퍼(shell wapper)
34-3. awk 스크립트 쉘 래퍼(shell wrapper)
34-4. Bash 스크립트에 내장된 펄
34-5. 하나로 묶인 Bash 스크립트와 펄 스크립트
34-6. 자신을 재귀적으로 부르는 스크립트
35-1. 문자열 확장
35-2. 간접 변수 참조 - 새로운 방법
35-3. 배열과 약간의 트릭을 써서 한 벌의 카드를 4명에게 랜덤하게 돌리기
A-1. manview: 포맷된 맨 페이지를 보는 스크립트
A-2. mailformat: 이메일 메세지를 포맷해서 보기
A-3. rn: 간단한 파일이름 변경 유틸리티
A-4. encryptedpw: 로컬에 암호화 되어 있는 비밀번호로 ftp 사이트에 파일을 업로드하는 스크립트
A-5. copy-cd: 데이타 CD를 복사하는 스크립트
A-6. days-between: 두 날짜 사이의 차이를 계산해 주는 스크립트
A-7. behead: 메일과 뉴스 메세지 헤더를 제거해 주는 스크립트
A-8. ftpget: ftp에서 파일을 다운로드 해 주는 스크립트
A-9. password: 8 글자짜리 랜덤한 비밀번호 생성 스크립트
A-10. fifo: 네임드 파이프를 써서 매일 백업해 주는 스크립트
A-11. 나머지 연산자로 소수 생성하기
A-12. tree: 디렉토리 구조를 트리 형태로 보여주는 스크립트
A-13. 문자열 함수들: C 형태의 문자열 함수
A-14. 객체 지향 데이타 베이스
F-1. 샘플 .bashrc 파일
G-1. VIEWDATA.BAT: 도스용 배치 파일
G-2. viewdata.sh: VIEWDATA.BAT 의 스크립트 버전

출처 : http://wiki.kldp.org/HOWTO//html/Adv-Bash-Scr-HOWTO/index.html

반응형
<?xml version="1.0"?>

<project name="HelloWorld" default="compile" basedir="." >
<property name="src.dir" value="${basedir}" />
<property name="classes.dir" value="${basedir}" />
<property name="jar.dir" value="${basedir}" />
<property name="main-class"  value="HelloWorld"/>

<target name="compile" >
<javac srcdir="${src.dir}" destdir="${classes.dir}" />
</target>

<target name="jar" depends="compile">
<jar jarfile="${jar.dir}/hello.jar"
    basedir="${classes.dir}"
    includes="**/*.class">
    <manifest>
        <attribute name="Main-Class" value="${main-class}"/>
    </manifest>
</jar>
</target>

</project>


manifest 사용설명
http://ant.apache.org/manual/tutorial-HelloWorldWithAnt.html

ANT (상): Ant 무엇에 쓰는 물건인고?
http://www.javastudy.co.kr/docs/lec_oop/ant/ant1.htm

Ant 사용에 대한 설명
http://www.oracle.com/global/kr/magazine/webcolumns/2002/o62odev_ant.html 

[출처] Java Ant Tutorial|작성자 redcore75

+ Recent posts