2016년 2월 26일 금요일

AWS에서 JDK, TOMCAT, MYSQL 설치하기


AWS JDK, tomcat 설치방법 / EC2 인스턴스 생성 / SSH 접속 방법 참고사이트
http://lhh3520.tistory.com/289


1.mysql 설치 하기

- MYSQL 설치

#sudo yum install mysql mysql-server mysql-libs mysql-server

- mysql을 실행해봅시다.

# sudo /etc/init.d/mysqld start

- 웹서버 정보 수정
# vi /etc/httpd/conf/httpd.conf

ServerName에 DNS명으로 수정

2. mysql root 비번 설정


- MYSQL root 비밀번호 설정

# mysqladmin -u root password '비밀번호'

또는


1) Login to the MySQL server, type the following command at the shell prompt:
mysql -u root -p

2) Use the mysql database (type commands at the mysql> prompt):

mysql> use mysql;

3) Change password for a user:

mysql> update user set password=PASSWORD("newpass") where User='ENTER-USER-NAME-HERE';

4) Reload privileges:

mysql> flush privileges;
mysql> quit

재부팅시에도 apache와 mysql이 자동으로 실행될수 있도록 설정.

# sudo chkconfig --levels 235 mysqld on

# sudo chkconfig --levels 235 httpd on

3. MySQL User에 외부 접속권한 부여

ROOT 계정으로 외부에서 접속 가능하게 하려면


GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'password' WITH GRANT OPTION

4. Security Group(방화벽) 에 3306Port 오픈

5. mysql workbench 에서

SSH Hostname : Public IP:22
SSH USername : ec2-user
SSH key File : pem 파일
Mysql Hostname : 127.0.0.1
port : 3306
username : root
pw : 비밀번호
Share:

2016년 2월 24일 수요일

스프링 AOP 사용시 효율적인 개발전략과 고려해야될 사항

1) AOP 를 이용한 효율적인 개발 전략

AOP를 적용하지 않았던 지금까지의 프로젝트같은 경우에는 애플리케이션 전체에 영향을 미치는 Exception 처리, Logging 처리, Transaction 처리 방안에 대하여 프로젝트 초기에 확정하고 추후에 변경하지 않는 것이 프로젝트를 성공시키는게 큰 요인이 된다. 이 같은 정책이 프로젝트 초기에 수립되지 않은 상태에서 프로젝트를 진행하거나, 초기에 수립된 정책들이 수시로 바뀐다면 개발자들의 생산성은 저하될 수 밖에 없으며, 프로젝트의 위험요소는 증가하게 된다. 따라서 AOP를 적용하지 않는 프로젝트에서는 프로젝트 개발 초기에 Exception 처리, Logging 처리, 트랜잭션 처리 방안에 대한 정책을 확실하게 수립하는 것이 무엇보다 중요하다.

지금까지 국내에서 실행되고 있는 대부분의 프로젝트를 보면 프로젝트 내부에 일정한 명명규칙(Naming Convention)을 가지고 있다. 그러나 실질적으로 프로젝트를 진행할 때 이 같은 명명규칙은 단지 문서작업에 불과할 뿐 제대로 지켜지는 경우가 많지 않았다. 그러나 지금까지 프로젝트를 진행하는데 큰 실패요인이 되지 않았다. 그 이유는 변경사항이 발생할 때마다 개발자들의 희생이 있었기 때문이다. 무수한 밤을 세워가면서 변경된 정책을 소스코드에 반영하는 작업을 해왔기 때문이다.

그러나 AOP를 적용할 경우 프로젝트 초반에 Exception 처리, Logging 처리, 트랜잭션 처리를 어떻게 구현할지에 대해서는 프로젝트 중, 후반으로 미루어도 된다. 단, 구현하는 애플리케이션에 대한 명명규칙을 확립하고 이에 따라 프로젝트를 진행해야 한다. 이와 같이 일정한 명명규칙에 따라 프로젝트를 진행하지 않을 경우 추후에 AOP를 적용하려고 해도 힘들어 지는 상황이 발생하기 때문이다. 따라서 프로젝트 초반에 명명규칙을 확립하고 개발자들이 명명규칙을 따르도록 교육하는 단계가 필요하다.

물론 프로젝트 초반에 Exception 처리, Logging 처리, 트랜잭션 처리와 같은 기능들을 어떻게 처리할지에 대한 정책을 수립하는 것도 중요하지만 AOP를 적용할 경우 이 과정은 프로젝트 중, 후반에 결정해도 늦지 않게 된다. 또한 이 같은 정책을 변경할 경우에는 모든 클래스가 아닌 Aspect만을 변경하면 가능하기 때문에 프로젝트 중,후반에 정책을 변경하는 것도 힘들지 않다.

AOP를 적용한 프로젝트와 그렇지 않은 프로젝트에서 Infrastructure 정책이 변경되었을 때와 명명 규칙이 변경되었을 때 프로젝트에 미치는 영향을 보면 다음과 같다.



그림1 AOP 사용이 프로젝트에 미치는 영향을 도식화한 그림

이 같은 점이 AOP의 가장 큰 장점이라고 볼 수 있다. 역시 중복 코드를 제거함으로서 얻을 수 있는 가장 큰 장점중의 하나라고 볼 수 있겠다. 국내와 같이 프로젝트 초반에 Infrastructure가 제대로 수립되지 않은 상태에서 진행하거나, 프로젝트 도중에 이 같은 Infrastructure가 흔들리는 경우가 많은 곳에서는 AOP의 적용으로 더 큰 효과를 볼 수 있을 것으로 생각한다.

그러나 하나의 Target 클래스에 너무 많은 Aspect를 적용할 경우 실행속도의 저하를 가져올 수 있다. 또한 이 장에서 개발한 Logging Aspect가 애플리케이션의 모든 클래스에 적용되도록 구현한다면 이 또한 애플리케이션 실행속도 저하의 원인이 될 수 있다. Aspect의 사용이 중복 코드를 방지함으로서 많은 부분에서 중복된 개발을 방지하는 것은 사실이지만 무분별한 사용으로 인해 애플리케이션의 실행속도가 저하될 수 있다는 것을 명심하기 바란다. 따라서 Aspect는 애플리케이션 개발에 있어서 꼭 필요한 부분에 제한된 범위를 두고서 사용하는 것이 바람직하다.

새로운 기술이 등장하면 지금까지의 모든 문제점을 해결할 수 있는 것처럼 과대 광고를 한다. AOP 또한 좋은 기술이고 지금까지 해결하지 못했던 많은 문제들을 해결해주는 것이 사실이다. 그러나 대부분의 기술이 그렇듯이 좋은 점이 있다면 그에 따르는 부작용도 있기 마련이다. 프로젝트에 AOP를 적용할 때 이와 같은 부작용까지 고려하면서 사용할 때 좋은 애플리케이션을 개발할 수 있을 것이다.

*reference
  • http://wiki.javajigi.net/pages/viewpage.action?pageId=28016657
Share:

2016년 2월 21일 일요일

데이터가 순회도 자주하고 삽입삭제도 자주하는경우 사용해야 될 방식과 속도가 제일 정렬 방식은?

HashMap?
Quick Sort?


*reference

  • http://nnoco.tistory.com/73 ( HashMap, ArrayList, LinkedList 속도 비교 )
  • https://ko.wikipedia.org/wiki/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98 ( 알고리즘 위키백과 )
  • http://www.joinc.co.kr/modules/moniwiki/wiki.php/Site/Database/algorithm/Quick_Sort ( 퀵소트 )
  • http://ooz.co.kr/71 ( SynchronizedMap과 ConcurrentHashMap )
Share:

TDD(Test-Driven Development)란?

TDD(Test-Driven Development)는 현재 소프트웨어를 개발하는데 있어서 전세계적으로 가장 즐겨 사용하는 개발 방법론으로 자리잡고 있다. 미국의 한 리서치 기관에서 조사한 결과에 따르면 소프트웨어 회사에서 약 77.5% 이상이 TDD 개발방법론을 선호하고 적용하고 있다고 이야기 하고 있다. 이에 비추어 볼 때 아직 국내에서는 도입이 많이 늦기는 했지만 세계적인 데이터를 통틀어 볼 때 보다 높은 퀄리티의 소프트웨어를 만드는데 있어서 필수적인 과정이 분명하다. 

국내에서는 소프트웨어의 테스트라는 것은 일반적으로 어느 정도 규모 있는 소프트웨어 회사들에 한해서만 QA팀이 존재하고 있고 그 팀을 통하여 테스트를 진행해 왔었기 때문에 TDD를 단순히 QA를 위한 혹은 유닛 테스팅 방법론으로 오해할 수 있다. 하지만 TDD는 개발자들을 위한 개발자들에 의한 개발 방법론이다. 개발자들이 보다 객체지향적인 코드를 생성해내는데 큰 일조를 하고 있을 뿐만 아니라 보다 튼튼한 소프트웨어 구조를 잡을 수 있게 도와준다. 

이번 장에서는 TDD가 무엇인지 현재의 개발방법론과 비교하면서 또 장단점을 통해 살펴봄으로써 보다 높은 퀄리티의 소프트웨어를 개발하는데 있어서 왜 TDD를 도입해야 되는지 설명하도록 하겠다.

1) TDD란 무엇인가?

먼저 TDD는 1999년도 XP(Extreme Programing)라는 애자일 기반의 개발 방법론이 자리 잡을 때 처음 소개가 되었다. 이 그룹에서는 프로그래밍 전에 테스트 코드를 먼저 작성하면 어떨까라고 생각하고 시도를 해봤는데 이 때 테스트 이후에 작성되는 코드의 퀄리티가 꽤 인상적이었기 때문에 TDD가 애자일 방법론에 도입되기 시작했다. 


TDD에 대해서 좀 더 쉽게 이해하기 위해서 먼저 “테스트 주도 개발(Test-Driven Development)” 라는 용어를 “테스트 먼저 개발(Test-First Development)”로 바꾸어 이해할 필요가 있다. 즉, 개발을 하기 전에 테스트 코드를 먼저 작성한다는 것이다. 아래 [그림1] 은 일반적인 개발 프로세스를 설명하고 있다.



[그림1] 기존의 개발 프로세스


프로그램을 개발하는데 있어서 익숙한 스텝들일 것이다. 필자 또한 지금 돌아보면 왜 TDD를 진작 소개되지 못했을까 라는 아쉬운 마음이 들 정도로 위와 같은 개발 프로세스를 무려 10년이상이나 유지해왔었다. 이 고전적인 개발 방법론의 문제는 눈에 보이는 것처럼 명확하다. 테스트를 통해서 원하는 결과가 나오지 않았을 때 처음 소프트웨어 디자인을 다시 수정해야 될 수도 있다는 것이다. 왜냐하면 코드를 작성하기 전에 “디자인”에 충분한 시간을 투자하면서 고려하지 못하기 때문이다. 그럼 TDD를 도입한 개발 프로세스는 어떨까? [그림2]를 살펴보자.





[그림2] Test-Driven Development 프로세스

TDD의 경우 테스트 코드를 작성한 뒤에 코드를 작성한다. 때문에 보다 정확한 프로그래밍 목적을 디자인 단계에서 반드시 미리 정의해야만 하고 또 무엇을 테스트해야 할지 미리 정의해야만 한다. 프로그래밍 경험이 있는 개발자라면 누구나 공감할 수 있는 것이 가끔 코드를 작성한 뒤에 테스트를 하면서 “맞아! 원래 이걸 의도한 것이 아니었는데” 하면서 깨닫고 다시 작성한 코드를 수정하는 경우가 많이 있다. 이것을 방지 할 수 있다는 것이 TDD의 하나의 큰 장점 중에 하나이기도 하다. 


그렇다면 위의 테스트코드와 실제 구현까지 이루어지는 주기는 어떻게 산출될까? 아주 작은 유닛들 하나하나 즉, 메서드를 작성할 때 마다 저렇게 주기를 반복해야 되는 것일까? 일반적으로 애자일 개발 방법론 위에서 TDD를 적용한다면 저 주기는 당연히 하나의 유저 스토리를 기반으로 나누어지게 된다. 즉, 애자일 방법론에서는 업무(Task)가 존재하고 그 업무에서는 여러 개의 유저 스토리가 존재하게 된다. 예를 들면 “사용자가 버튼을 클릭할 경우에 새로운 일정을 추가하는 창을 열수 있다.” 라는 정의가 바로 유저스토리이다. 워터폴(Waterfall) 방법론에서 TDD를 적용한다고 하더라도 업무 단위대로 위의 주기를 적용해도 큰 무리는 없을 것이지만 보다 튼튼한 업무분석이 뒷받침 될수록 TDD의 이점을 가져올 수 있다는 것은 사실이다. 

2) TDD의 장점


그렇다면 TDD의 가장 큰 장점은 아마도 높은 퀄리티의 소프트웨어를 보장한다는 것이다. 그렇다면 높은 퀄리티의 소프트웨어는 어떻게 정의할 수 있을까? 먼저 기획을 떠나서 개발자의 역할만 놓고 본다면 절대 에러나 버그가 없어야 한다는 것이다. 두 번째로는 추가적인 요구사항이 있을 때 손쉽게 그 요구사항을 반영해줄 수 있어야 한다는 것이고 마지막으로 누가 그 코드를 봐도 손쉽게 이해하고 수정할 수 있어야 한다는 것이다. 바로 TDD는 정확하게 이 세가지 사항을 만족시켜 준다. 그럼 한가지씩 살펴보도록 하자.

- 보다 튼튼한 객체지향적인 코드 생산 가능

테스트 코드를 먼저 작성한다는 것은 하나하나의 기능들에 대해서 철저히 구조화 시켜 코드를 작성한다는 것을 뜻한다. 나도 모르게 디자인 패턴들을 하나하나 적용하고 인터페이스들을 이용해서 느슨한 결합을 실현시키는 자신을 발견하게 된다. 그 이유는 TDD가 코드의 재사용성을 보장해야만 가능하기 때문에 그렇다. 즉, 우리가 테스트 코드를 작성할 일이 없을 경우에 한 메서드 안에 여러 가지 기능이 혼합되어 이용해도 무방하다. 아니 좀 더 솔직하게 우리가 코드를 작성하면서 실제로 이 코드는 재사용이 되지 않는다라는 가정으로 작성하는 코드가 많지 않은가? 실제로 어떤 기능을 두세 번 호출하게 될 경우에서야 객체화하거나 메서드화 시키는 경우가 많다. 하지만 TDD는 “모든 코드”가 재사용성 기반으로 작성되어야 하기 때문에 보다 튼튼한 코드 생산이 이루어 지게 되는 것이다.

- 재설계 시간의 단축

앞에서 설명한 것처럼 테스트 코드를 먼저 작성하기 때문에 내가 지금 무엇을 해야 하는지 분명히 정의를 하고 시작하게 된다. 때로는 우리가 코드를 작성하면서 삼천포로 빠지는 경우가 많다. 내가 뭘 하려고 했었지? 하면서 코드 정체성의 혼란을 가끔 마주하면서 디자인을 다시 뜯어 고치고 아까운 코드 지울 수도 없고 그냥 주석으로 달아 두고 괜히 라인수만 차지하게 되는 경우를 필자 또한 수없이 경험했다. 하지만 TDD에 익숙해 지는 순간 이러한 경험은 이제 그저 추억 속으로 묻히게 된다. 테스트 코드를 작성하면서 인터페이스나 클래스의 구조들을 많이 수정하게 된다. 만약 테스트 코드가 아니라 실제 구현 코드를 작성하면서 이 작업을 할 경우에 구현하고 있는 코드들 또한 고쳐야 하는 문제를 유발하게 된다.

개인적인 경험에 비추어 보자면 실제로 초급 개발자와 중고급 개발자의 차이는 얼마만큼 예외 상황을 많이 알고 있느냐의 차이로 나누어 지기도 한다. 여기서 만약 먼저 테스트 시나리오들을 정의하게 되면 그만큼 필요한 예외 상황들을 먼저 선임들과 상의할 수 있다는 것이고, 초급 개발자들도 예외 상황들과 테스트 되어야 하는 가능성들을 먼저 조사하게 되는 것이다.


- 디버깅 시간의 단축

이것은 통합 테스팅이 아닌 유닛 테스팅을 하는 이점이기도 하다. 아래서 보다 정확한 논리를 설명할 것이지만 먼저 간단하게 설명하자면 우리는 각각의 모듈 별로 테스트를 자동화할 수 있는 코드가 없다면 특정 버그를 찾기 위해서 모든 레벨(레이어)의 코드들을 살펴봐야 한다. 예를 들어 사용자의 데이터가 잘못 나온다면 DB의 문제인지, 비즈니스 레이어의 문제인지 UI 단의 문제인지 실제 모든 레이어들을 전부다 디버깅할 필요 없이 자동화 된 유닛테스팅 결과로 우리는 손 쉽게 찾아 낼 수 있다는 것이다.

- 애자일과의 시너지 효과

보다 정확하게 말하자면 BDD(Behavior-Driven Development)를 적용하는데 있어서 큰 시너지 효과가 있다. BDD는 행위(동작) 중심적인 방법으로 개발을 진행하는 방법론인데 개발자 디자이너 그리고 기획자 모두 사용자 입장으로 같은 목표로 가지기 위함이다. 예를 들어 회의를 할 때도 항상 개발자는 기술적인 관점으로 어떤 업무를 해석하는 경우가 것을 어느 정도 완화시켜 줄 수 있기 때문에 도입한다. 즉, TDD를 진행하면 항상 그 테스트 요소들이 사용자 관점으로 정의되고 진행되기 때문에 보다 튼튼한 개발 프로세스를 만들게 된다.

- 테스트 문서의 대체가능

주로 S.I 프로젝트를 진행하다 보면 어떤 요소들이 테스트 되었는지 테스트 정의서를 만들고는 한다. 이것은 단순 통합테스트 문서에 지나지 않는다. 즉, 내부적인 하나하나의 로직들이 어떻게 테스트 되었는지 제공할 수 없다. 하지만 TDD를 하게 될 경우에 테스팅을 자동화 시킴과 동시에 보다 정확한 테스트 근거를 산출할 수 있다.

- 추가 구현의 용이함

개발 뒤에 어떤 기능을 추가할 때 가장 우리를 번거롭게 하는 것이 이 기능이 기존의 코드들에 얼만큼 영향을 미치게 될지 모르기 때문에 다시 모든 코드들을 테스트 해야 되는 것이 큰 곤욕이다. 역시나 바로 뒷 부분에 살펴볼 내용이지만 유닛 테스트로 자동화 될 경우에 우리는 수동으로 모든 테스트를 다시 진행해야 되는 시간적인 낭비를 줄일 수 있다.

3) TDD의 단점


그렇다면 단점들은 무엇일까? 먼저 코드 생산성이 가장 꺼림직한 문제일 수 있다. 코드 퀄리티 보다는 빠른 결과물을 원하는 환경이라면 쉽게 도입이 어려울 수 있다. 왜냐하면 개발자들이 TDD 를 공부해야 하고 추가적으로 테스트 코드를 작성해야 하기 때문이다. 필자의 경험으로 처음에는 익숙해지기 위한 시간 때문에 1-2달은 적응시간이 걸린다. TDD가 최소한의 객체지향 프로그래밍을 요구하는 이상 보통 정도하는 1-2년 차 개발자라도 조금 어렵다는 생각이 들 수 있다. 때문에 객체지향에 올바른 이해를 가지고 있는 리드 개발자가 TDD 프로젝트를 리드해 갈 필요가 있다.

추가적으로 결국 미래를 봤을 때 투자한 시간을 충분히 벌 수 있는 잠재력이 충분하다 하더라도 결국 납기일이라는 몹쓸 데드라인이 잡히면 “글쎄,,” 라는 생각이 들 수 있다. 갑을병정 내려가는 프로젝트일수록 퀄리티 보다는 개발기간과 타이밍이 중요하기 마련이고 또한 소프트웨어에서 주인의식을 가지기 힘들 수 있기 때문이다.

마지막으로 개인적으로 체험한 경험적인 이슈가 있다면 TDD로 프로젝트를 진행하면서 테스트가 어려운 예외가 생길 수 있는데 그것 때문에 고민하는 순간이 찾아오게 된다. 뭐 이런 거다. 원칙을 깰 수는 없고 꼼수가 있기는 한데 그 꼼수를 위해서 구조를 바꾸자니 이건 아무래도 아닌 것 같고, 테스트는 말 그대로 테스트일뿐 실제 코드가 더 중요한 상황인데도 불구하고 테스트 원칙 때문에 쉽게 넘어가지 못하는 그런 경우다.

아무튼 이런 단점들이 있다고 하더라도 개발자의 커리어에 있어서는 반드시 TDD를 도입해야만 한다고 필자는 이야기 하고 싶다. 먼저 자신의 객체지향적인 코딩 능력을 향상시킬 수 있기 때문이다. 더욱이 S.I회사가 아닌 자력 소프트웨어를 만드는 경우에서는 더욱더 그렇다. 회사의 입장에서도 처음 TDD의 도입에 대한 실이 많아 보일 수는 있다 하더라도 TDD 도입을 통하여 보다 튼튼한 소프트웨어를 만들 수 있을뿐더러 향후에 관리에도 더 용이하게 된다.

*reference

  • http://www.hoons.net/Lecture/View/644

Share:

2016년 2월 20일 토요일

Code Highlight 로 코드 이쁘게 블로그에 올리기

- 적용방법 및 다운로드
https://highlightjs.org/download/


- 적용한 데모 보기
https://highlightjs.org/static/demo/

- 사용법

<pre><code class="java">
소스
</code></pre>

소스에 html 태그나 부등호 같은것이 들어갈때는 아래링크에서 escape 시킨다
http://accessify.com/tools-and-wizards/developer-tools/quick-escape/default.php

- 추가적인 스타일링 CSS 추가

구글블로그 에서 템플릿 사용할시 기준
 "템플릿" -> "맞춤설정" -> "너비조정" 에서 아래소스추가후 블로그 적용버튼 클릭.


code {
    font-size: 70%;
}
Share:

데코레이터 패턴(Decorator Pattern)과 프록시 패턴

1) 데코레이터 패턴(Decorator Pattern) 정의

1-1) 데코레이터 패턴(Decorator Pattern) 

데코레이터 패턴에서는 객체의 추가적인 요건을 동적으로 추가한다 데코레이터는 서브클래스를 만드는 것을 통해서 기능을 유연하게 확장할 수 있는 방법을 제공한다.

1-2) 데코레이터 단점 

데코레이터 패턴을 이용해 디자인을 하다 보면 잡다한 클래스들이 많아 질 수 있다. 겹겹이 애워싼 객체의 정체를 알기가 힘들다.

2) 데코레이터 패턴 핵심정리

- 상속을 통해 확장을 할 수도 있지만, 디자인 유연성 면에서는 별로 좋지 않다.
- 기존 코드를 수정하지 않고도 행동을 확장하는 방법이 필요하다.
- 구성과 위임을 통해서 실행중에 새로운 행동을 추가할 수 있다.
- 상속대신 데코레이터 패턴을 통해서 행동을 확장 할 수 있다.
- 데코레이터 패턴에서는 구상 구성요소를 감싸주는 데코레이터들을 사용한다.
- 데코레이터의 수퍼클래스는 자신이 장식하고 있는 객체의 수퍼클래스와 같다.
- 데코레이터 패턴을 사용하면 자질한 객체들이 많이 추가될 수 있고, 데코레이터를 너무 많이 사용하면 코드가 필요 이상으로 복잡해 질 수 있다.

3) OCP (Open-Closed Principle)

- OCP는 가장 중요한 디자인 원칙 가운데 하나다.
- 클래스는 확장에 대해서는 열려 있어야 하지만 코드 변경에 있어서는 닫혀 있어야 한다.
- 즉 기존 코드는 건드리지 않은 채로 확장을 통해서 새로운 행동을 간단하게 추가할 수 있도록 하면,
  새로운 기능을 유연하게 추가할 수 있어, 주변 환경에 잘 적응할 수 있으면서도 강하고 튼튼한 디자인을 만들 수 있다.

4) 데코레이터 패턴(Decorator Pattern) 예제

스타버즈 커피는 엄청난 급속도로 성장한 초대형 커피 전문점으로 유명하다.
스타버즈 커피샵은 워낙 빠르게 생장했기 때문에 다양한 음료를 모두 포괄하는 주문시스템을 이제 갖추려고 한다.

처음 사업을 시작할 무렵에 만들어진 클래스는 아래와 같다.


Beverage는 음료를 나타내는 추상 클래스이며, 커피샵에서 판매되는 모든 음료는 이 클래스의 서브클래스가 된다.
추상메소드인 cost()메소드를 새로 정의하여 가격을 구현한다.
처음에는 하우스블렌드, 다크로스트, 디카페인, 에스프레소 네 가지만 판매 하게 된다..

커피를 주문할 때 스팀 우유나 두유, 모카를 추가하고, 그 위에 휘핑 크림을 얹기도 해야 한다면..



우와 클래스 개수가 말 그대로 폭발적으로 늘어나는 군..
정말 이대로 운영한다면 클래스 관리하는게 만만치가 않겠네.
우유 가격이 인상된다면?, 카라멜 토핑을 새로 추가한다면? 생각만 해도 끔찍하네..

4-1) 첫 번째 리팩토링

이거 정말 황당하네, 왜 이렇게 클래스가 많이 필요한 거죠?
그냥 인스턴스 변수하고 슈퍼클래스 상속을 써서 추가 사항을 관리하면 안될까요?

- Beverage라는 음료 클래스에 우유,두유,모카,휘핑크림을 나타내는 인스턴스 변수를 추가한다.
- 첨가물에 첨부 여부와, 첨가물을 설정하기 위한 has,set메소드를 생성 한다.
- cost()에서 추가된 첨가물의 가격을 계산한다. 서브클래스에서 cost() 메소드를 오버라이드 할 때  그 기능을 확장 하여 특정 음료의 가격을 더한다.
- 이제 클래스가 다섯개 밖에 안되네요. 진작에 이렇게 했어야죠?
- 근데 나중에 어떻게 바뀌어야 할지 생각해 보면 이 접근법에도 문제가 있지 않을까요?



public class Beverage{
public float cost();
float condimentCost = 0.0;

if(hasMilk())
condimentCost += milkCost;

if(hasSoy())
condimentCost += soyCost;

...
return condimentCost;
}



public class DarkRoast extends Beverage{

public DarkRoast(){
description = "최고의 다크로스트";
}

public float cost(){
return 1.99+super.cost();
}
}

어떤 문제가 있을까요?
- 첨가물 가격이 바뀔 때마다 기존 코드를 수정해야 한다.
- 첨가물의 종류가 많아지면 새로운 메소드를 추가해야 한다.
- 새로운 음료가 출시될 수도 있습니다. 그 중에는 특정 첨가물이 들어가면 안 되는 경우도 있을 겁니다.
- 아이스 티를 생각해 보면 Tea서브 클래스에서도 hasWhip()같은 메소드를 여전히 상속 받을 것이다.
- 손님이 더블 모카를 주문하면 어떻게 해야 할까요?



public class Beverage{
public float cost();
float condimentCost = 0.0;

if(hasMilk())
condimentCost += milkCost;

if(hasSoy())
condimentCost += soyCost;

...
return condimentCost;


}
사부와 제자 - 서브클래스를 만드는 방식으로 행동을 상속 받으면 그 행동은 컴파일시에 완전히 결정이 된고, 모든 서브 클래스에서 똑같은 행동을 상속 받아야 한다. - 하지만 구성을 통해 객체의 행동을 확장하면 실행중에 동적으로 행동을 설정 할 수 있다

(참고1) OCP( Open-Closed Principle)
- OCP( Open-Closed Principle)는 가장 중요한 디자인 원칙 가운에 하나다. - 클래스는 확장에 대해서는 열려 있어야 하지만 코드 변경에 대해서는 닫혀 있어야 한다. - 즉 기존 코드는 건드리지 않은 채로 확장을 통해서 새로운 행동을 간단하게 추가할 수 있도록 하면, 새로운 기능을 유연하게 추가할 수 있어, 주변 환경에 잘 적응할 수 있으면서도 강하고 튼튼한 디자인을 만들 수 있다.

4-2) 데코레이터 패턴 적용

이번에 사용 할 방법은 특정 음료에서 시작해서 첨가물로 그 음료를 장식(decorate) 할 것이다. 예를 들어 모카와 휘핑크림을 추가한 다크로스트 커피를 주문 한다면 아래와 같이 할 수 있을 것이다.
① DarkRoast 객체를 가져온다.
 ② Mocha 객체로 장식한다.
③ Whip 객체로 장식한다.
④ cost() 메소드를 호출한다.
이때 첨가물의 가격을 계산하는 일은 해당 객체들에게 위임한다.

① DarkRoast 객체에서 시작 합니다.








② 손님이 Mocha를 주문했으니 Mocha객체를 만들고 그 객체로 DarkRoast를 감쌉니다.

 











③ 손님이 휘핑크림도 같이 주문했기 때문에  Whip 데코레이터를 만들고 그 자체로 Mocha를 감쌉니다.














④ 이제 가격을 계산해 볼까요? 
- 가장 바깥쪽에 있는 데코레이터 Whip의 cost()를 호출하면 된다. - 가장 바깥쪽에 있는 데   코레이터 Whip의 cost()를 호출한다. 
- Whip에서는 Mocha의 cost() 메소드를 호출한다. 
- Mocha에서는 다시 DarkRoast의 cost()를 호출한다. 
- DarkRoast에서는 가격과 이름을 반환한다. 
- Mocha에서는 DarkRoast의 리턴값과 모카값을 더해 반환한다. 
- Whip에서는 Mocha에서 받은 가격에 Whip가격을더해 최종 가격을 반환한다.


4-3) Beverage 클래스를 장식해 봅시다.








- Beverage는 가장 기본이 되는 Component 추상 클래스로 볼 수 있다.
- 커피 종류마다 Beverage에 대한 구상 클래스를 하나씩 만든다. (HouseBlend, DarkRoast, Expresso, Decaf)
  (Beverage클래스를 상속 받아 새로운 행동을 동적으로 추가하게 된다.)
- 각각의 첨가물을 나타내는 데코레이터를 추가합니다. cost() 뿐만 아니라 getDescription() 도 구현해야 한다.  (Mocha, Milk, Soy, Whip)
- 각 데코레이터 안에는 Beverage 클래스가 들어있다.
  즉, 데코레이터에는 구성요소에 대한 레퍼런스가 들어있는 인스턴스 변수가 있지요.

사무실에서 들은 이야기..
- CondimentDecorator에서 Beverage클래스를 확장하는 것은 상속이 맞다 - 데코레이터 패턴에서는 상속을 이용해서 형식을 맞추는 거지, 상속을 통해서 행동을 물려 받는게 목적이 아니다. - 데코레이터 패턴에서는 객체 구성(인스턴스 변수로 다른 객체를 저장하는 방식)을 이용하고 있기 때문에 음료하고 첨가물을 다양하게 섞어도 유연성을 잃지 않을 수 있다.

4-4) 코드를 만들어 봅시다.


//Beverage.java
public abstract class Beverage {

protected String description = "제목없음";

public abstract double cost();
public String getDescription() {
return description;

}
}

//Espresso.java
//에스프레소 커피
public class Espresso extends Beverage {

public Espresso(){
//Beverage로부터 상속받음
description = "에스프레소 커피";
}

@Override
public double cost() {
return 1.99;
}
}

//CondimentDecorator.java
public abstract class CondimentDecorator extends Beverage {

//모든 첨가물 데코레이터에서 getDescription() 메소드를 새로 구현하도록 만들 계획임
public abstract String getDescription();


Mocha.java
//Mocha는 데코레이터기 때문에 CondimentDecorator를 확장 합니다.
public class Mocha extends CondimentDecorator {

//감싸고자 하는 음료(하우스블렌드,다크로스트,디카페인,에스프레소)를 저장하는 인스턴스.
Beverage beverage;

//생성자를 이용해서 감싸고자 하는 음료 객체를 전달한다.
public Mocha(Beverage beverage){
this.beverage = beverage;
}

@Override
public String getDescription() {
//음료 명에 첨가물명을 추가한다.
return beverage.getDescription() + ", 모카";
}

//CondimentDecorator는 Beverage를 확장 하죠
@Override
public double cost() {
//음료 가격에 모카 가격을 추가한다.
return .20 + beverage.cost();
}
}

//StarbuzzCoffee.java
public class StarbuzzCoffee {
public static void main(String[] args) {

//에스프레소 커피
Beverage espresso = new Espresso();
System.out.println(espresso.getDescription()+
" : $"+espresso.cost());

//다크로스트 커피 + 모카+ 모카 + 휘핑크림
Beverage darkRoast = new DarkRoast(); //다크로스트 커피
darkRoast = new Mocha(darkRoast); //모카 추가
darkRoast = new Mocha(darkRoast); //모카 한번 더 추가
darkRoast = new Whip(darkRoast); //휘핑크림 추가
System.out.println(darkRoast.getDescription()+
" : $"+darkRoast.cost());

//하우스 블렌드 커피, 두유, 모카, 휘핑크림
Beverage houseBlend = new HouseBlend(); //하우스 블렌드 커피
houseBlend = new Soy(houseBlend); //두유 추가
houseBlend = new Mocha(houseBlend); //모카 추가
houseBlend = new Whip(houseBlend); //휘핑크림 추가
System.out.println(houseBlend.getDescription()+
" : $"+houseBlend.cost());
}
}
에스프레소 커피 : $1.99 다크 로스트 커피, 모카, 모카, 휘핑크림 : $1.49 하우스 블렌드 커피, 두유, 모카, 휘핑크림 : $1.34


5) 데코레이터가 적용된 예 : 자바 I/O

java.io 패키지에는 어마어마하게 많은 클래스들이 있지만, 많은 부분이 데코레이터 패턴을 바탕으로 만들어져 있다.


스타 버즈 디자인하고 별로 다르지 않죠? 출력 스트림의 디자인도 똑같다.
자바 I/O를 보면 데코레이터의 단점도 발견 할 수 있다.
데코레이터 패턴을 이용해서 디자인을 하다 보면 잡다한 클래스들이 너무 많아 진다.

6) 프록시 패턴(proxy pattern)이란?

어떤 객체에 대한 접근을 제어하기 위한 용도로 대리인이나 대변인에 해당하는 객체를 제공하는 패턴


- 원격프록시 : 원격객체에 대한 접근을 제어
- 가상프록시 : 생성하기 힘든 자원에 대한 접근을 제어
- 보호프록시 : 접근권한이 필요한 자원에 대한 접근을 제어

6-1) 원격프로시 

6-1-1) 원격프록시 개요

로컬 환경에 존재하면서, 원격객체에 대하여 대변자 역할을 하는 객체를 원격 프록시라고 한다.

- 원격객체 : 다른 JVM의 Heap영역에 살고있는 객체

6-1-2) 원격에 있는 왕뽑기 기계를 감시하는 모니터링 프로그램 만들기


클라이언트 객체(GumballMonitor)에서는 원격객체(GumballMachine)의 메소드를 호출하는 것처럼 행동하지만, 실제로는 로컬 힙에 들어있는 원격객체 모양과 비슷한 '프록시'객체의 메소드를 호출하고 있는 것이다.
네트워크 통신과 관련된 저수준 작업은 이 프록시 객체에서 처리해준다.

6-1-3) 원격메소드의 기초


1. 클라이언트 객체에서 클라이언트 보조객체의 메소드를 호출한다.
2. 클라이언트 보조객체에서는 메소드 호출에 대한 정보(인자, 메소드이름 등)를 잘 포장해서 네트워크를 통해 서비스 보조객체한테 전달한다.
3. 서비스 보조객체에서는 클라이언트 보조객체로 부터 받은 정보를 해석하여 어떤 객체의 어떤 메소드를 호출할지 알아낸 다음 진짜 서비스객체의 '진짜 메소드'를 호출한다.
4. 서비스객체의 메소드가 호출되고, 메소드 실행이 끝나면 서비스 보조객체에 결과를 리턴해준다.
5. 호출결과로 리턴된 정보를 포장해서 서비스 보조객체에서 클라이언트 보조객체한테 전달한다.
6. 클라이언트 보조객체에서는 리턴된 값을 해석하여 클라이언트 객체한테 리턴한다.

클라이언트 객체 입장에서는 메소드 호출이 어디로 전달되었었는지, 어디에서 왔는지 전혀 알 수 없다.

6-2) 가상프록시

6-2-1) 가상프록시 개요

생성하는데 많은 비용이 드는 객체를 대신하는 역할을 한다. 실제로 진짜 객체가 필요하게 되기 전까지 객체의 생성을 미루게 해 주는 기능을 제공하거나, 객체 생성 전, 또는 생성도중에 객체를 대신하기도 한다. 객체 생성이 완료되고나면 프록시에서 RealSubject에 요청을 직접 전달한다.






import java.util.*;

interface Image {
public void displayImage();
}

//on System A
class RealImage implements Image {
private String filename;
public RealImage(String filename) {
this.filename = filename;
loadImageFromDisk();
}

private void loadImageFromDisk() {
System.out.println("Loading " + filename);
}

public void displayImage() {
System.out.println("Displaying " + filename);
}
}

//on System B
class ProxyImage implements Image {
private String filename;
private Image image;

public ProxyImage(String filename) {
this.filename = filename;
}
public void displayImage() {
if (image == null)
{
image = new RealImage(filename);
}
image.displayImage();
}
}

class ProxyExample {
public static void main(String[] args) {
Image image1 = new ProxyImage("HiRes_10MB_Photo1");
Image image2 = new ProxyImage("HiRes_10MB_Photo2");

image1.displayImage(); // loading necessary
image2.displayImage(); // loading necessary
}
}



*reference

  • http://wiki.gurubee.net/pages/viewpage.action?pageId=1507398
  • http://wiki.gurubee.net/pages/viewpage.action?pageId=1507415
  • https://ko.wikipedia.org/wiki/%ED%94%84%EB%A1%9D%EC%8B%9C_%ED%8C%A8%ED%84%B4



    Share:

    2016년 2월 19일 금요일

    컬렉션 프레임워크(Collection Framework) 란?

    1) Collection Framework

    - 배열의 단점을 보완한 데이터 군을 저장하는 클래스들을 표준화한 설계이다.
    - 다수의 데이터를 쉽게 처리할 수 있는 방법을 제공하는 클래스들로 구성된다.
    - 컬렉션(collection) : 다수의 데이터, 데이터 그룹을 의미한다.
    - 프레임워크(framework) : 표준화, 정형화된 체게적인 프로그래밍 방식이다.
    - 컬렉션 클래스(collection class) : 다수의 데이터를 저장할 수 있는 클래스이다.

    2) Collection 종류



     인터페이스
     특 징
     List
    순서가 있는 데이터의 집합, 데이터의 중복을 허용한다.
    --> 데이터를 add하면 앞에서 부터 순차적(순서대로)으로 데이터가 들어간다. 
           그래서 각각의 저장되어 있는 공간들은 고유한 index를 갖는다. 
    ex.) 대기자 명단
    구현 클래스: ArrayList, LinkedList, Stack, Vector등
     Set
    순서를 유지하지 않는 데이터의 집합, 데이터의 중복을 허용하지 않는다.
    --> 집합이다. 데이터가 순서와는 상관없이 add된다. 중복되지 않는다.
    ex.) 양의 정수 집합, 소수의 집합
    구현 클래스: HashSet, TreeSet등
     Map
    키와 값의 쌍으로 이루어진 데이터의 집합. 순서는 유지되지 않으며, 키는 중복을 허용하지 않고, 값을 중복을 허용한다.
    ex.) 우편번호, 지역번호
    구현 클래스: HashMap, TreeMap, Hashtable, Properties등


    3) 배열과 비교

    3-1 ) String[]와 ArrayList 

     - String[] arrayObj = new String[2]; // 배열의 크기 지정--> 생성후 변경 불가
     - ArrayList<String> al = new ArrayList<String>();  // 배열과 비슷하나, 객체 생성후 몇개      의 값을 사용할 지 지정할 필요가 없다.

    3-2 ) 중복을 허용하는 List/ 중복 허용하지 않는 Set

     - ArrayList : 중복을 허용하며 데이터가 순서대로 정렬된다.
     - HashSet : 중복을 허용하지 않으며 순서대로 정렬되지 않는다.

    3-3 ) Map

     - Map은 iterator 기능이 없기 때문에 Map의 데이터를 가지고 있는 Set을 만들고 Set에 들    어가 있는 데이터 타입은 Map.Entry이다. (Set에 있는 값은 Map에 있는 값이 대응된다.
       --  > Map에 있는 값을 getKey( ), getValue( )로 알아낸다.)


    Iterator itKey = map.keySet().iterator();
    // key값만 가져오는 경우
    while(itKey.hasNext()) {
    System.out.println(itKey.next());
    }

    Iterator itValue = map.values().iterator();
    int totalSum = 0;
    // value값만 가져오는 경우
    while(itValue.hasNext()) {
    Student s = itValue.next();
    System.out.println(s);
    totalSum += s.total;
    }

    3-4) Vector 와 ArrayList 

    - List 인터페이스를 구현하기 때문에 데이터의 저장순서가 유지되고 중복을 허용한다.
    - ArrayList는 기존의 Vector를 개선한 것으로 구현원리와 기능적으로 동일하다.
    - 데이터의 저장공간으로 배열을 사용한다. (배열기반)
    - Vector는 자체적으로 동기화처리가 되어 있으나 ArrayList는 그렇지 않다.
    - Object배열을 이용해서 데이터를 순차적으로 저장한다.
    - 배열에 더이상 공간이 없으면 보다 큰 새로운 배열을 생성해서 기존의 배열에 저장된 내용을 새로운 배열로 복사한 다음에 저장된다.
    - ArrayList는 removeRange()를 제외한 모든 메서드가 Vector 메서드와 일치한다.

    5. Iterator

    컬렉션 프레임워크에서 컬렉션에 저장된 요소들을 읽어오는 방법을 표준화 하였다. 컬렉션에 저장된 각 요소에 접근하는 기능을 가진 Iterator인터페이스를 정의하고, Collection인터페이스에는 Iterator (Iterator를 구현한 클래스의 인스턴스)를 반환하는 iterator()를 정의하고 있다. iterator()는 Collection인터페이스에 정의된 메서드이므로 Collection인터페이스의 자손인 List와 Set에도포함되어 있다. 그래서 List나 Set인터페이스를 구현하는 컬렉션은 iterator()가 각 컬렉션의 특징에 알맞게 작성되어 있다.

    6. 동기화(Synchronization)

    - 멀티쓰레드(multi-thread) 프로그래밍에서는 하나의 객체를 여러 쓰레드가 접근할 수 있기 때문에 데이터의 일관성(consistency)을 유지하기 위해 동기화가 필요하다.
    - Vector와 같은 구버전 클래스들은 자체적으로 동기화처리가 되어있다.
    - ArrayList와 같은 신버전 클래스들은 별도의 동기화처리가 필요하다.
    - Collections 클래스는 다음과 같은 동기화 처리 메서드를 제공한다.
    - static Collection SynchronizedCollection (Collection c)
    - static List synchronizedList (List list)
    - static Map synchronizedMap (Map m)
    - static Set synchronizedSet (Set s)
    - static SortedMap synchronizedSortedMap (SortedMap m)
    - static SortedSet synchronizedSortedSet (SortedSet s)



    *reference
    • http://gangzzang.tistory.com/entry/Java-%EC%BB%AC%EB%A0%89%EC%85%98-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8DCollection-Framework
    • http://devbox.tistory.com/entry/Java-%EC%BB%AC%EB%A0%89%EC%85%98-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC
    • 자바-컬렉션-프레임워크-인터뷰-질문-40개http://starplatina.tistory.com/entry/%EC%9E%90%EB%B0%94-%EC%BB%AC%EB%A0%89%EC%85%98-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC-%EC%9D%B8%ED%84%B0%EB%B7%B0-%EC%A7%88%EB%AC%B8-40%EA%B0%9C










    Share:

    2016년 2월 17일 수요일

    String, StringBuffer, StringBuilder 차이점

    1) String 클래스와 StringBuffer 클래스의 특징


    대체로 초보자는 String 클래스만을 사용하고 있으며, 실력이 향상되어 StringBuffer 클래스를 알게 되면 성능 향상이라는 이유로 StringBuffer 클래스를 자주 사용하는 경향이 있다. 사실 두개의 클래스는 똑같이 문자열 처리를 위한 클래스이며 메모리상의 처리 방법에서 차이를 보여주고 있을 뿐이다. 이러한 처리 방법의 차이가 또한 성능의 차이를 보여주고 있다. 실제로 많은 경우 String 클래스보다 StringBuffer 클래스의 성능이 훨씬 좋다고 많은 사람들이 생각하고 있으며, 실제로 많은 경우에 성능 차이를 보이고 있다.

    그렇다면, StringBuffer 클래스가 String 클래스보다 항상 더 나은 성능을 가지고 있을까? 그렇다면 왜, 자바를 설계한 사람들은 String 클래스를 기본 문자열처리 클래스로 정했을까? 여기에 대한 답을 알아보기로 하자.

    2) String 클래스는 변경이 불가능한 immutable 클래스이다.


    String 클래스에서 substring(), toLowerCase(), concat(), trim() 등의 메소드를 생각하면 String 클래스는 변경 가능한 클래스처럼 보인다. 그러나, 실제로는 이러한 메소드들은 원래 객체와 다른 새로운 String 객체를 만들어 반환한다. 또 하나의 String 객체가 생성되는 것이다. 따라서 원래 String 객체는 가지고 있는 문자열이 변경되지 않으며 여전히 사용가능한 채로 남는다.

    즉, 기존의 String 객체에 substring()과 같은 문자열에 변경을 가하는 메소드를 실행하면 또 하나의 String 객체가 생성되어 서로 다른 두개의 String 객체가 존재하게 된다.

    이런 이유로 String 클래스의 변경은 객체를 생성하기 위하여 시스템 자원(시간, 메모리 등)을 낭비한다고 생각되는 경향이 있다. 그렇다면 왜 immutable(변경불가) 클래스인가?


    3) 왜 immutable(변경불가) 클래스인가?


    immutable 클래스는 몇 가지 조건과 특징을 가지고 있다.

    첫번째는, 클래스가 가지고 있는 값(즉, String 클래스에서는 문자열)은 오직 생성자에서만 설정될 수 있으며, 그 값을 변경할 수 있는 어떠한 메소드도 가지고 있지 않아야 한다. 만약 변경을 원한다면, 원하는 값을 가진 새로운 객체를 생성한다.

    이런 immutable 클래스의 가장 큰 장점은 안전하게 공유될 수 있다는 점이다. 즉, 변경은 적고, 읽기(즉, 문자열의 참조)만 많은 경우, 또는, 여러 쓰레드나 객체에서 공유하는 경우, synchronization(동기화)와 같은 특별한 안전장치 없이도 안전하게 공유될 수 있다.

    대부분의 문자열이 복잡한 문자열 처리과정보다는 한번 설정된 문자열들을 여러 곳에서 공유하는 경우가 많으므로, 자바에서 기본 문자열을 처리하는 클래스로 String 클래스를 immutable 패턴으로 설정하였다.

    4) StringBuffer 클래스는 변경이 가능한 mutable 클래스이다.


    StringBuffer 클래스는 가지고 있는 문자열의 내용을 변경 가능하도록 만든 클래스이다. 즉, append(), insert(), delete() 등의 메소드를 통하여 StringBuffer 객체가 가지고 있는 문자열을 변경할 수 있으며, 이 때, String 클래스처럼 새로운 객체를 생성하지 않고, 기존의 문자열을 변경한다. 이 경우 객체 생성을 하지 않으므로, String 클래스보다 효율적이라고 생각하기 쉽지만, 동기화(synchronization)를 보장해야 하기 때문에 단순한 참조에서는 상대적으로 String 보다 나쁜 성능을 보인다. 따라서, 단순 참조가 많은 경우 StringBuffer 클래스보다 String 클래스가 유리하다. 물론, StringBuffer 클래스는 동기화되어 있으므로, 멀티 쓰레드에 대하여 안전하다.

    또한, StringBuffer 객체는 문자열을 다루는 다른 메소드에서 사용되기 위하여, toString() 메소드를 통하여 String 객체를 생성하게 된다. 이때, 일반적으로 String 객체의 생성과 함께, 가지고 있는 문자열에 대한 복사가 이루어진다. 물론, 자바 규약은 성능 향상을 위하여 String 객체 생성 후에 문자열을 복사하지 않고, StringBuffer 객체와 문자열을 공유하여 참조하는 프록시 패턴을 적용하는 것을 허용하고 있다. 그러나, 이것은 반드시 그런 것은 아니며, 프록시 패턴의 특성상 StringBuffer 객체에 변경이 가해지면, 프록시는 바로 해제되며, 그 시점에서 문자열의 복사가 이루어진다. (프록시 패턴의 적용은 필수 요건이 아니며, 자바 가상 머신 구현체에 따라 다를 수 있으며, 사용상의 차이는 전혀 없고 성능 상의 차이만을 보일 뿐이다.)

    5) 성능 차이의 실제적인 비교


    다음과 같은 질문을 생각해보자.

    - StringBuffer 객체는 내용을 변경할 때 String 객체보다 효율적인가?
    - String 객체는 가지고 있는 문자열을 변경할 때 어느 정도 StringBuffer 객체에 비해 성능 저하를 보이는가?
    - StringBuffer 및 String 클래스는 모두 문자열 처리에서 가장 많이 쓰이는 substring() 메소드에 대하여 String 객체를 생성한다. 그렇다면 성능상의 차이가 있는가?
    - StringBuffer 객체는 toString() 메소드를 통하여 String 객체를 생성하여야만 다른 객체에 문자열을 전달할 수 있다. toString() 메소드를 통한 String 객체 생성의 자원 소모는 어느 정도 인가?
    - String 객체 및 StringBuffer 객체의 생성은 어느 정도의 자원 소모를 필요로 하는가?

    여기에 대한 답을 얻기 위하여 간단한 테스트 프로그램을 작성하여 결과를 구해보았다. 다음과 같은 8가지 경우에 대하여 각각 64만번의 반복을 통하여 소요된 시간과 자유 메모리의 변화를 그래프로 보면 다음과 같다.

    비교를 위해 테스트에 사용된 8가지 메소드

    - String.concat() - String 클래스에 문자열 추가
    - StringBuffer.append() - StringBuffer 클래스에 문자열 추가
    - Stirng.substring() - String 클래스에서 문자열 일부 추출
    - StirngBuffer.substring() - StringBuffer 클래스에서 문자열 일부 추출
    - Stirng.toString() - String 클래스의 toString() 메소드 호출 (실제로는 자기자신을 돌린다)
    - StirngBuffer.toString() - StringBuffer 클래스의 toString() 메소드 호출 (즉, String 객체로 변환)
    - new String() - String 객체 생성
    - new StringBuffer() - StringBuffer 객체 생성

    그림 1. 64만번 반복 동안 감소되어가는 자유 메모리의 양 (단위: MB)

    그림 2. 64만번 반복 동안 소요된 시간 (단위: 밀리초)

    위의 그래프를 통해 알 수 있는 몇가지 중요한 사실을 정리하면 다음과 같다.

    1. 객체를 생성하지 않는 String.toString() 메소드와 StringBuffer.append() 메소드는 메모리 자원을 거의 소모하지 않는다.
    2. StringBuffer 객체의 생성이 시간과 메모리 자원을 가장 많이 필요로 한다.
    3. StringBuffer의 toString() 메소드 등과 같이 String 객체를 생성하는 메소드들은 일정한 시간과 일정한 메모리 자원을 소모한다.

    6) StringBuffer 클래스와 StringBuilder 클래스


    StringBuilder 클래스는 JDK 5.0 에서 새로 추가된 클래스이다.
    StringBuffer 클래스나 StringBuilder 클래스에서 제공하는 메소드는 동일하다.

    StringBuffer 클래스
    : 스레드에 안전하게 설계 (ThreadSafe)
    여러개의 스레드에서 하나의  StringBuffer 객체를 처리해도 전혀 문제가 되지 않는다.

    StringBuilder 클래스
    : 단일 스레드에서의 안전성망을 보장
    여러개의 스레드에서 하나의 StringBuilder 객체를 처리하면 문제가 발생한다.

    ####### 사용 ########

    StringBuffer sb = new StringBuffer();

    // 이렇게 사용해도 되고
    sb.append(" ABCDEF ");
    sb.append(" GFEFEF ");
    sb.append(" WEFWEFWE ");

    // 이렇게 사용해도 된다.
    sb.append(" ABCDEF ")
        .append( "  GFEFEF ")
        .append( "  WEFWEFWE ");

    // 이렇게 만은 제발 사용하지 말자
     sb.append( "ABCDE" + " = " + "FEFEF ");
    sb.insert( 3, "1234");


    // insert() 메소드는 지정된 위치 이후에 넘어온 값을 덧붙이는 작업을 수행한다.
    //insert() 메소드를 수행할 때 지정한 위치까지 값이 할당되어 있지 않으면 StringIndexOutOfBoundsException 이 발생

    ** append() 메소드를 사용할 때 append() 메소드 내에서 + 를 이용해 문자열을 더하면  StringBuffer 를 사용하는 효과가 
    전혀 없게 된다.

    7) String, StringBuffer, StringBuilder 선택 기준


    1. String 객체는 불변이기 때문에 변하지 않는 문자열은 String을 사용한다.
    2. StringBuilder는 비동기방식이기 때문에 Single Thread 환경하에서, 변화되는 문자열의 사용한다.
    3. StringBuffer 동기방식으로 저장되기 때문에 멀티쓰레드로 접근하거나 문자열이 변경될 경우에 사용한다.


    8) JDK 버전에 따른 차이  


    JDK 5.0  이상의 WAS 를 사용한다면 결과가 약간 달라진다. 

    다음과 같은 소스를  JDK 1.4 일 경우에는 아래와 동일하게 컴파일 되지만, 

    String s = "Here " + "is " + "samples";

    JDK 1.5 일 경우 컴파일인 경우에는 다음과 같이 컴파일 된다.

    String s = (new StringBuilder ("Here is")).append("samples").toString();

     결론적으로...

     String, StringBuffer, StringBuilder  이 세가지 클래스 중에서 가장 메모리를 많이 차지하고
    응답시간에 많은 영향을 주는 것은 String  클래스 이다. 
    만약 여러분의 WAS  나 시스템이  JDK 5.0 이상을 사용한다면,
    컴파일러에서 자동으로 StringBuilder  로 변환하여 준다.
    하지만 반복 루프를 사용해서 문자열을 더할때에는 객체를 계속 추가해야 한다는 사실에는 변함이 없다.
    그러므로  String 클래스를 쓰는대신, 스레드와 관련이 있으면  StringBuffer  를,
    스레드 안전여부와 상관이 없으면  StringBuilder  를 사용하는 것을 권장한다.

    9) 성능 향상에 대한 결론


    문자열을 추가하기 위하여 append()와 같은 메소드를 사용할 때 StringBuffer 클래스는 String 클래스와 비교하여 아주 뛰어난 성능을 보인다. 그러나. StringBuffer 객체의 생성 및 toString() 메소드를 통한 String 객체의 생성을 반드시 필요로 하므로 더 많은 시간 및 메모리 자원의 낭비를 초래한다.

    그에 비하여, String 클래스는 StringBuffer 클래스와 비교하여 인스턴스화를 통하여 객체를 생성할 때 상대적으로 적은 자원을 소모하며, toString() 메소드를 통하여 String 객체로 바꿀 필요가 없다.

    따라서, StringBuffer 클래스는 하나의 문자열에 대하여 다른 문자나 문자열의 추가가 여러 번 이루어지는 경우 유리하며, 단 한번의 문자열 추가에 대하여 StringBuffer 클래스를 사용하는 것은 오히려 시간 및 메모리 자원 낭비를 초래하게 된다.

    *reference




    • http://javacan.tistory.com/entry/39
    • http://egloos.zum.com/top2blue/v/5148222



    Share:

    2016년 2월 16일 화요일

    2016년 2월 15일 월요일

    2016년 2월 11일 목요일

    mvc1, mvc2 패턴의 차이와 spirng mvc 구조

    1) 모델 1 개발 방식

     1-1.장점

    - 개발 속도가 빠르다.
    - 개발자의 스킬이 낮아도 배우기 쉬워 빠르게 적용할 수 있다.

     1-2.단점

    - JSP페이지에서 프리젠테이션 로직과 비즈니스 로직을 모두 포함하기 때문에 JSP페이지가   너무 복잡해 진다.
    - 프리젠테이션 로직과 비즈니스 로직이 혼재되어 있기 때문에 개발자와 디자이너의 분리     된 작업이 어려워진다.
    - JSP페이지의 코드가 복작해 짐으로 인해 유지보수 하기 어려워진다.
    - 정교한 Presentation 레이어를 구현하기 힘들다.(유효성 체크, 에러 처리등)

    2) 모델 2 개발 방식



     2-1. 장점(Spring MVC를 기준으로)

    - Presenation에서 명확한 역할 분담이 된다.
    - UI 레이어를 단순화 시킴으로서 디자이너도 작업하는 것이 가능하게 된다. - 단지 Display   용으로만 사용된다.
    - Presentation 레이어의 정교한 개발이 가능하다. 유효성 체크, 에러 처리와 같은 기능들은   Spring 프레임워크에서 제공한다.
    - Dependency Pull 없이 Dependency Injection만을 이용해서 애플리케이션을 개발하는 것   이 가능하다.
    - UI 레이어가 단순해 짐으로서 유지보수가 쉽다.

     2-2. 단점

    - 새로운 기술을 익혀야하는 부담감이 있다.
    - 프로젝트 초반에 개발속도의 저하를 가져올 수 있다.


    3) SPRING MVC 구조


    각 흐름을 좀더 자세하게 설명하면 다음과 같다.

    클라이언트의 요청이 DispatcherServlet에 전달된다.
    DispatcherServlet은 HandlerMapping을 사용하여 클라이언트의 요청이 전달될 Controller 객체를 구한다.
    DispatcherServlet은 Controller 객체의 handleRequest() 메소드를 호출하여 클라이언트의 요청을 처리한다.
    Controller.handleRequest() 메소드는 처리 결과 정보를 담은 ModelAndView 객체를 리턴한다.
    DispatcherServlet은 ViewResolver로부터 처리 결과를 보여줄 View를 구한다
    View는 클라이언트에 전송할 응답을 생성한다.
    여기서 개발자가 직접 개발해주어야 하는 부분은 클라이언트의 요청을 처리할 Commander 클래스와 클라이언트에 응답 결과 화면을 전송할 JSP나 Velocity 템플릿 등의 View 코드이다. 나머지, DispatcherServlet이나 HandlerMapping, ViewResolver 등은 Spring이 제공하는 기본 구현체를 사용하면 된다.



    *reference
    • http://wiki.javajigi.net/pages/viewpage.action?pageId=1136
    • http://javacan.tistory.com/entry/130



    Share:

    Spring aop, Interceptor, filter 차이점


    InterceptorFilterAOP
    실행 위치서블릿서블릿메소드
    실행 순서213
    설정 위치xml or javaweb.xmlxml or java
    실행 메소드preHandler
    postHandler
    afterCompletion
    init
    dofilter
    destroy
    pointcut으로 @after, @before,
    @around등 위치를 지정하여
    자유롭게 메소드 생성 가능

     표를 설명해보자면, interceptor와 filter는 서블릿 단위에서 실행됩니다. 반면에 AOP는 메소드의 앞에 Proxy 패턴을 이용해서 실행됩니다.

    그래서 실행순서에서도 차이가 생깁니다. filter가 가장 겉에 있고, 그 안에 interceptor 그리고 그 안에 aop가 들어있는 식의 구조입니다.
    request가 filter를 거쳐 interceptor 쪽으로 들어가고 aop를 거쳐 다시 나오면서 interceptor와 filter를 거치는 식 입니다.

    실행되는 메소드를 기준으로 설명하면, 서버를 실행시켜 서블릿이 올라오는 동안에 init이 실행되고, 그 후 dofilter가 실행됩니다.
    그 후 컨트롤러에 들어가기 전에 preHandler가 실행되고, aop가 실행된 후에 컨트롤러에서 나와 postHandler, after Completion, dofilter 순서대로 진행되고, 서블릿 종료시 destory가 실행 될 것입니다.

    그림으로 표현해봅시다.

    AOP의 경우에는 Interceptor나 Filter와 달리 메소드 전후의 지점을 자유롭게 설정가능하고, interceptor와 filter가 주소로 밖에 걸러낼 대상을 구분 할 수 없는 것에 비해서 AOP는 주소, 파라미터, 어노테이션등 다양한 방법으로 대상을 지정할 수 있는 장점이 있습니다.

    *reference

    • http://blog.naver.com/platinasnow/220035316135
    Share:

    JAVA Exception

    1) 예외

    예외 : 잘못된 코드, 부정확한 데이터, 예외적인 상황에 의하여 발생하는 오류이다. 예외가 발생되면 프로그램은 곧바로 종료된다는 점에서 에러와 동일하다.

    그러나 예외는 예외처리를 통해 프로그램을 종료하지 않고 정상 실행 상태가 유지되도록 할 수 있다.

    예외는 두 가지 종류가 있다. 하나는 일반예외이고 다른 하나는 실행예외이다. 일반 예외는 컴파일러 체크 예외라고도 하는데, 자바 소스를 컴파일하는

    과정에서 예외처리코드가 필요한지 검사하기 대문이다. 만약 예외처리 코드가 없다면 컴파일 오류가 발생한다.

    실행 예외는 컴파일하는 과정에서 예외 처리 코드를 검사하지 않는 예외를 말한다. 자바에서는 예외를 클래스로 관리한다.

    JVM은 프로그램을 실행하는 도중에 예외가 발생하면 해당 예외 클래스로 객체를 생성한다.

    그리고 나서 예외처리 코드에서 예외 객체를 이용할 수 있도록 해준다. 일반예외는 Exception을 상속받지만 RuntimeException을 상속받지 않는 클래스들이고,

    실행 예외는 RuntimeException을 상속받은 클래스들이다.


    2) 예외(Exception) 클래스

    RuntimeException클래스들은 주로 프로그래머의 실수에 의해서 발생될 수 있는 예외들로서 자바의 프로그래밍 요소들과 관계가 깊다. 예를 들면, 배열의 범위를 벗어난다던가(IndexOutOfBoundsException), 값이 null인 참조변수의 멤버를 호출하려 했다던가(NullPointerException), 클래스간의 형변환을 잘못했다던가(ClassCastException), 정수를 0으로 나누려 했다던가(Arithmetic- Exception)하는 경우에 발생하는 예외들이다.

    RuntimeException클래스들 중의 하나인 ArithmeticException을 try-catch문으로 처리하는 경우도 있지만, 사실 try-catch문을 사용하기보다는 0으로 나누지 않도록 프로그램을 변경하는 것이 올바른 처리방법이다.
    이처럼 RuntimeException예외들이 발생할 가능성이 있는 코드들은 try-catch문을 사용하기 보다는 프로그래머들이 보다 주의 깊게 작성하여 예외가 발생하지 않도록 해야 할 것이다. 

    그 외의 Exception클래스들은 주로 외부의 영향으로 발생할 수 있는 것들로서, 프로그램의 사용자들의 동작에 의해서 발생하는 경우가 많다. 예를 들면, 존재하지 않는 파일을 처리하려한다던지(FileNotFoundException), 실수로 클래스의 이름을 잘못 적었다던가(ClassNotFoundException), 입력한 데이터의 형식이 잘못되었다던가(DataFormatException) 하는 경우에 발생하는 예외들이다.

    이런 종류의 예외들은 반드시 처리를 해주어야 한다. 

    RuntimeException클래스들 - 프로그래머의 실수로 발생하는 예외
    그 외의 클래스들 - 사용자의 실수와 같은 외적인 요인에 의해 발생하는 예외

    RuntimeException클래스들 그룹에 속하는 예외가 발생할 가능성이 있는 코드에는 예외처리를 해주지 않아도 컴파일 시에 문제가 되지 않지만, 그 외의 Exception클래스들 그룹에 속하는 예외가 발생할 가능성이 있는 예외는 반드시 처리를 해주어야 하며, 그렇지 않으면 컴파일시에 에러가 발생한다. 

    *reference

    • http://itmessi.tistory.com/6
    • http://finewoo.tistory.com/22
    • http://hyeonstorage.tistory.com/199
    Share:

    DB Index 란?

    1) INDEX의 의미

    RDBMS에서 검색속도를 높이기 사용하는 하나의 기술입니다.
    INDEX는 색인입니다. 해당 TABLE의 컬럼을 색인화(따로 파일로 저장)하여 검색시 해당 TABLE의 레코드를 full scan 하는게 아니라 색인화 되어있는 INDEX 파일을 검색하여 검색속도를 빠르게 합니다.
    이런 INDEX는 TREE구조로 색인화합니다. RDBMS 에서 사용하는 INDEX는 Balance Search Tree 를 사용합니다.
    실제로는 RDBMS 에서 사용되는 B-Tree 는 B-Tree 에서 파생된 B+ Tree 를 사용한다고 합니다.
     
    참고로 ORACLE이나 MSSQL에서는 여러종류의 TREE를 선택하여 사용가능하다.


    2) INDEX의 원리

     
    INDEX를 해당 컬럼에 주게 되면 초기 TABLE생성시 만들어진 MYD,MYI,FRM 3개의 파일중에서
    MYI에 해당 컬럼을 색인화 하여 저장합니다. 물론 INDEX를 사용안할시에는 MYI파일은 비어 있습니다. 그래서 INDEX를 해당컬럼에 만들게 되면 해당컬럼을 따로 인덱싱하여 MYI 파일에 입력합니다. 그래서 사용자가 SELECT쿼리로 INDEX가 사용하는 쿼리를 사용시 해당 TABLE을 검색하는것이 아니라 빠른 TREE로 정리해둔 MYI파일의 내용을 검색합니다.
    만약 INDEX를 사용하지 않은 SEELCT쿼리라면 해당 TABLE full scan하여 모두 검색합니다.
    이는 책의 뒷부분에 찾아보기와 같은 의미로 정리해둔 단어중에서 원하는 단어를 찾아서 페이지수를 보고 쉽게 찾을수 있는 개념과 같습니다. 만약 이 찾아보기 없다면 처음부터 끝까지 모든 페이지를 보고 찾아야 할것입니다.

    3) INDEX의 장점

    - 키 값을 기초로 하여 테이블에서 검색과 정렬 속도를 향상시킵니다.
    - 질의나 보고서에서 그룹화 작업의 속도를 향상시킵니다.
    - 인덱스를 사용하면 테이블 행의 고유성을 강화시킬 수 있습니다.
    - 테이블의 기본 키는 자동으로 인덱스 됩니다.
    - 필드 중에는 데이터 형식 때문에 인덱스 될 수 없는 필드도 있습니다.
    - 여러 필드로 이루어진(다중 필드) 인덱스를 사용하면 첫 필드 값이 같은 레코드도 구분할   수 있습니다.
      참고로 액세스에서 다중 필드 인덱스는 최대 10개의 필드를 포함할 수 있습니다.
     

    4) INDEX의 단점

    - 인덱스를 만들면 .mdb 파일 크기가 늘어난다.
    - 여러 사용자 응용 프로그램에서의 여러 사용자가 한 페이지를 동시에 수정할 수 있는 병행성이 줄어든다.
    - 인덱스 된 필드에서 데이터를 업데이트하거나, 레코드를 추가 또는 삭제할 때 성능이 떨어집니다.
    - 인덱스가 데이터베이스 공간을 차지해 추가적인 공간이 필요해진다. (DB의 10퍼센트 내외의 공간이 추가로 필요)
    - 인덱스를 생성하는데 시간이 많이 소요될 수 있다.
    - 데이터 변경 작업이 자주 일어날 경우에 인덱스를 재작성해야 할 필요가 있기에 성능에 영향을 끼칠 수 있다.

    따라서 어느 필드를 인덱스 해야 하는지 미리 시험해 보고 결정하는 것이 좋습니다. 인덱스를 추가하면 쿼리 속도가 1초 정도 빨라지지만, 데이터 행을 추가하는 속도는 2초 정도 느려지게 되어 여러 사용자가 사용하는 경우 레코드 잠금 문제가 발생할 수 있습니다.

    또, 다른 필드에 대한 인덱스를 만들게 되면 성능이 별로 향상되지 않을 수도 있습니다. 예를 들어, 테이블에 회사 이름 필드와 성 필드가 이미 인덱스 된 경우에 우편 번호 필드를 추가로 인덱스에 포함해도 성능이 거의 향상되지 않습니다. 만드는 쿼리의 종류와 관계 없이 가장 고유한 값을 갖는 필드만 인덱스 해야 합니다.


    5) INDEX의 목적

     
    RDBMS에는 INDEX가 있습니다. 인덱스의 목적은 해당 RDBMS의 검색 속도를 높이는데 있습니다.
    SELECT 쿼리의 WHERE절이나 JOIN 예약어를 사용했을때만 인덱스를 사용되며 SELECT 쿼리의 검색 속도를 빠르게 하는데 목적을 두고 있습니다.
    ※ DELETE,INSERT,UPDATE쿼리에는 해당 사항없으며 INDEX사용시 좀 느려집니다.
     

    6) 인덱스를 생성해야 하는 경우와 그렇지 않은 경우

    - 인덱스는 열 단위로 생성된다.
    - WHERE절에서 사용되는 컬럼을 인덱스로 만든다.
    - 데이터의 중복도가 높은 열은 인덱스로 만들어도 효용이 없다. (예 : 성별, 타입이 별로 없는 경우, 적은경우)
    - 외래키가 사용되는 열에는 인덱스를 되도록 생성해주는 것이 좋다.
    - JOIN에 자주 사용되는 열에는 인덱스를 생성해주는 것이 좋다.
    - INSERT / UPDATE / DELETE가 얼마나 자주 일어나는지를 고려한다.
    - 사용하지 않는 인덱스는 제거하자
     

    7) 인덱스 주의사항

     * SELECT하는 경우에도 데이터 블록 수와 DB_FILE_MULTIBLOCK_READ_COUNT 값과 분포도 등에 따라 인덱스가 빠를 경우도 있고 full table scan보다 늦어지는 경우도 있음.

     7-1. DML에 취약함 

    ⓐ INSERT
    : index split 현상이 발생할 수 있음.
    index split - 인덱스의 Block들이 하나에서 두 개로 나누어지는 현상 
    -> 인덱스는 데이터가 순서대로 정렬 되어야 함. 기존 블록에 여유 공간이 없는 상황에서  그 블록에 새로운 데이터가 입력되어야 할 경우
    오라클이 기존 블록의 내용 중 일부를 새 블록에다가 기록한 후 기존 블록에 빈 공간을 만들어서 새로운 데이터를 추가하게 됨.
    *성능면에서 매우 불리 
    ① index split은 새로운 블록을 할당 받고 key를 옮기는 복잡한 작업을 수행. 모든 수행 과정이 Redo에 기록.. 많은 양의 Redo를 유발함
    ② index split이 이루어지는 동안 해당 블록에 대해 키 값이 변경되면 안되므로 DML이 블로킹됨.
    enq:TX-index contention 대기 이벤트 발생(RAC- gc current split)

    ⓑ DELETE
    테이블에서 데이터가 delete 될 경우 - 지워지고 다른 데이터가 그 공간을 사용 가능
    index에서 데이터가 delete 될 경우 - 데이터가 지워지지 않고, 사용 안 됨 표시만 해 둔다.
    ->즉, 테이블에 데이터가 1만건 있는 경우, 인덱스에는 2만건이 있을 수 있다는 뜻
    => 인덱스를 사용해도 수행속도를 기대하기 힘들다.

    ⓒ UPDATE : 인덱스에는 update 개념이 없음!!!
    테이블에 update가 발생할 경우 인덱스에서는 delete가 먼저 발생한 후 새로운 작업의 insert 작업이 발생한다. delete와 insert 두 개의 작업이 인덱스에 동시에 일어나 다른 DML보다 더 큰 부하를 주게 됨.

     7-2. 타 SQL 실행에 악영향을 줄 수 있음

    갑자기 인덱스를 추가하면 잘 돌아가고 있던 쿼리에 옵티마이저가 실행계획을 바꾸는 경우가 생겨 갑자기 아주 느려지는 경우가 생김
    -> 기존의 테이블에 인덱스를 추가할 경우 기존에 있던 SQL 문장들까지 전부 고려한 후 인덱스를 생성해야 함.

     

    8) 생성방법

     - 자동생성 : PK나 Unique제약 조건을 정의할 경우 Unique Index가 자동으로 생성됨

    9) 인덱스 종류

    9-1. B-TREE

     B: binary, balance

       Root
       block : branch block에 대한 정보
          l
      Branch
       block : leaf blcok에 대한 정보
          l
        Leaf
       block : 실제 데이터들의 주소


    ⓐ  UNIQUE INDEX : 인덱스 안에 있는 key 값에 중복되는 데이터가 없음 (성능 good)
     - 생성 문법
     SQL > CREATE UNIQUE INDEX 인덱스명
         2  ON 테이블이름(컬럼명 1 ASC|DESC, 컬럼명,....) ;

    ASC : 오름차순 정렬 (기본값)
    DESC  : 내림차순 정렬

    SQL> create table dept2(no number, dname varchar2(10));

    Table created.

    SQL> create unique index idx_dept2_dname
      2  on dept2(dname);

    Index created.


    SQL> insert into dept2
      2  values(9100,'임시매장');

    1 row created.

    SQL> insert into dept2
      2  values(1006,'서울지사');

    1 row created.

    SQL> insert into dept2
      2  values(9101,'임시매장');
    insert into dept2
    *
    ERROR at line 1:
    ORA-00001: unique constraint (SCOTT.IDX_DEPT2_DNAME) violated

    // 이미 들어간 dname이라서 에러 발생

    Non UNIQUE INDEX : 중복되는 데이터가 들어가야 하는 경우
     - 생성 문법
     SQL > CREATE INDEX 인덱스명
         2  ON 테이블이름(컬럼명 1 ASC|DESC, 컬럼명,....) ;

    예 - professor table의 position 컬럼에 Non UNIQUE INDEX를 생성
    SQL > create index idx_prof_position
        2   on professor(position);

    Function Based INDEX ( FBI - 함수기반 인덱스 )

    * 인덱스는 where절에 오는 조건 컬럼이나 조인에 쓰이는 컬럼으로 만들어야 함
    ** 인덱스를 사용하려면 where 절의 조건을 절대로 다른 형태로 가공해서 사용하면 안된다.

    꼭 써야 할 때
    SAL + 100 = 200 이라는 형태로 써야할 때
    -> 인덱스도 SAL+100 형태의 인덱스를 생성= 함수기반 인덱스 

    SQL> create index idx_prof_pay_fbi
      2  on professor(pay+100);

    Index created.

    professor table에 pay+100이라는 컬럼이 없지만 인덱스를 만들 때 저 연산을 수행해서 인덱스를 만들어줌.

    -> 임시적인 해결책은 될 수 있어도 근본적 처방은 아님
    -> pay + 100을 생성했는데 쿼리의 조건이 변경되면 인덱스를 다시 생성해야 함
    -> FBI는 기존 인덱스를 활용할 수 없다.(단점)

    DESCENDING INDEX : 내림차순으로 인덱스를 생성함
    큰 값을 많이 조회하는 SQL에 생성하는 것이 좋음!
    ex ) 계좌내역 최근 날짜부터 조회, 회사 매출

    SQL> create index idx_prof_pay
      2  on professor(pay desc);

    Index created.


    하나의 메뉴에 오름차순과 내림차순을 한번에 조회 할 경우
    : 오름차순, 내림차순 두 개의 인덱스를 만들면 DML의 성능에 악영향을 미침
    -> 힌트를 사용 ( 아래나 위에서부터 읽도록 할 수 있음)

    결합 인덱스 (Composite INDEX) : 인덱스 생성시 두 개 이상의 컬럼을 합쳐서 인덱스 생성
    주로 where 절의 조건 컬럼이 2개 이상이 and로 연결되어 사용되는 경우 많이 사용
    -> 잘못 생성하게 되면 성능에 나쁜 영향을 미침!!

    예) 사원 테이블에 총 50명이 있고, 남자 25명, 여자 25명
    여자 중 이름이 '유관순'인 사람이 2명

    사원 테이블에서 성별이 '여자' 이고 이름이 '유관순'인 사람을 찾을 때 :
    SQL > SELECT 이름, 성별
    FROM 사원
    WHERE 성별 = '여자'
    AND 이름 = '유관순';

    * 결합 인덱스 생성 구문 예 :

    SQL > CREATE INDEX idx_사원_성별_이름
       2  ON 사원(성별,이름);

    **** 결합 인덱스 생성시 컬럼의 배치 순서 

    case 1 : ON 사원(성별, 이름) 

    50 명 -> 여자 -> 25명 -> 유관순 -> 2명
            50                      => 25회 검사

    case 2 : ON 사원(이름, 성별)

    50명 -> 유관순 -> 2명 -> 여자 -> 2명
            50                      => 2회 검사


    => 같은 테이블에 같은 SQL이지만 결합 인덱스를 어떻게 생성하는가에 따라 속도나 검사 횟수가 완전히 달라지게 된다. (신중히 생성...)

    9-2. BITMAP INDEX 

    : 데이터 값의 종류가 적고 동일한 데이터가 많을 경우에 많이 사용
    성별 컬럼 : 남. 여 
     
    Bitmap index를 생성하려면 데이터의 변경량이 적어야 하고, 값의 종류도 적은 곳이 좋다.
    일반적으로 OLAP환경에서 많이 생성하게 됨. 
    무조건 적으로 생성해야 하는 것이 아니고, 테이블 성격이나 데이터를 종합적으로 분석해서 적절한 인덱스를 생성한다. 

    BITMAP INDEX는 어떤 데이터가 어디있다는 지도정보(map)를 Bit로 표시한다. 
    데이터가 존재하는 곳은 1로, 데이터가 없는 곳은 0으로 표시  정보를 찾을 때 1인 값만 찾음 !!! 

    SQL > create BITMAP index dex_사원_성별_bit   2  on 사원(성별);
    bitmap index를 생성하면 성별 컬럼 값의 종류대로 map이 생성됨.
    남자 : 1   0   1   0   0
    여자 : 0   1   0   1   1

    bitmap index를 사용하고 있는데 만약 컬럼 값이 새로 하나 더 생길 경우? 
    기존의 BITMAP INDEX를 전부 수정해야 함. 
    -> B-TREE INDEX는 관련 블록만 변경되면 되지만 BITMAP INDEX는 모든 맵을 다 수정해야 한다는 큰 문제점
    -> BITMAP INDEX는 블록 단위로 lock을 설정해서 같은 블록에 들어있는 다른 데이터도 수정작업이 안 되는 경우가 종종 생김 

    9-3. 데이터 처리 방법  

    - OLTP ( Online Transaction Processing - 실시간 트랜잭션 처리) : 실시간으로 데이터 입력과 수정이 일어나는 환경 - B -TREE 인덱스 많이 사용
    - OLAP ( Online Analytical Processing - 온라인 분석 처리) : 대량의 데이터를 한꺼번에 입력한 후 주로 분석이나 통계 정보를 출력할 때 사용하는 환경 - BITMAP 인덱스 많이 사용

    10) 인덱스의 종류

      10-1. 고유 인덱스(Unique Index)

    - 고유 인덱스는 유일한 값을 갖는 컬럼에 대해서 생성하는 인덱스로 고유 인덱스를 지정하려면 UNIQUE 옵션을 지정해야 합니다.
       SQL> CREATE UNIQUE INDEX  idx_ukempno_emp ON emp(empno);

     10-2. 비고유 인덱스(NonUnique Index)

     10-3. 단일 인덱스(Single Index)

    - 단일 인덱스는 한 개의 컬럼으로 구성한 인덱스를 말합니다.
      SQL> CREATE INDEX  idx_ukempno_emp ON emp(empno);

     10-4. 결합 인덱스(Composite Index)

    - 결합 인덱스는 두 개 이상의 컬럼으로 인덱스를 구성하는 것을 말합니다. 부서 번호와 부서명을 결합하여 인덱스를 설정 해 보도록 하겠습  니다.
      SQL> CREATE INDEX  idx_dept_com ON index_dept(deptno, dname);

     10-5. 함수 기반 인덱스(Function Based Index)

    - 함수 기반 인덱스는 SAL*12와 같이 컬럼에 어떠한 산술식을 수행했을때를 말합니다.
      SAL컬럼에 INDEX가 걸려있다해도 SAL*12은 INDEX를 타지 못합니다. 이럴때 함수 기반 인덱스를 생성합니다.
      SQL> CREATE INDEX  idx_annsal_emp ON emp(sal*12);

    11) 인덱스 구조와 작동 원리 (B-TREE 기준)

    테이블과 인덱스 비교
    - 테이블은 컬럼이 여러 개, 데이터가 정렬되지 않고 입력된 순서대로 들어감
    -> 인덱스는 컬럼이 key 컬럼과 ROWID 컬럼 두 개로 이루어져 있음 ( 오름차순, 내림차순 정렬가능)

    Key : 인덱스를 생성하라고 지정한 컬럼의 값


    select *
    from emp
    where empno=7902;

    데이터 파일의 블록이 10만개 일 때, SQL을 수행시
    1. 서버 프로세스가 파싱 과정을 마친 후 DB buffer cache에 empno 가 7902인 정보가 있는지 확인
    2. 정보가 없으면 하드 디스크 파일에서 7902정보를 가진 블록을 복사해서 DB buffer cache로 가져온 후 7900 정보만 골라내서 사용자에게 보여줌
    이 때
    index 없는 경우 -  7902정보가 어떤 블록에 들어 있는지 모르므로 10만개 전 부 db buffer cache로 복사한 후 하나하나 찾음
    index 있는 경우 - where 절의 컬럼이 index가 만들어져 있는지 확인 후, 인덱스에 먼저 가서 7902정보가 어떤 ROWID를 가지고 있는지 확인한 후 해당 ROWID에 있는 블록만 찾아가서 db buffer cache에 복사함.

    12) 단일 컬럼 인덱스


     12-1. 인덱스를 고려 해야 하는 경우

    - WHERE 조건 절에 자주 사용되는 컬럼
    - 자주 바뀌는 않는 컬럼

     12-2. 변경 작업에 따른 인덱스에 부하 감소

    - 동일 값이 작은 컬럼(1~15% 이하, 5%이하)

     12-3. 인덱스를 피해야 하는 경우

    - 함수 및 연산자에 의해 변경되는 컬럼
    - 낮은 선택도
    - 균일 분포가 아닌 컬럼

     12-4. 조합 인덱스가 단일 인덱스 보다 많은 장점이
    있지만 2개의 단일 인덱스 사용이 성능 상 좋다.


    13) 조합 컬럼 인덱스


     13-1. 조합 인덱스의 큰 장점

    - 선택도가 좋지 않은 두 개 이상의 컬럼을 조합하여,선택도가 좋은 조합 인덱스가 된다.
    - 질의 컬럼이 모두 조합 인덱스에 있는 경우, 물리적인 데이터 블록을 읽을 필요가 없다.

     13-2. 조합 인덱스를 고려해야 하는 경우

    - AND 연산자에 의해 자주 같이 질의 되는 컬럼들
    - 다수의 여러 개의 질의가 특정 컬럼들을 질의

     13-3. 인덱스 가이드라인

    - 자주 사용되는 컬럼을 왼쪽
    - 동일한 사용 빈도이면 선택도가 좋은 것을 왼쪽

    14) 새로운 인덱스 추가 시 문제점


     14-1. 기존 인덱스에 의해 잘 운용되던 환경 가정


     14-2. 새로운 SQL 문장을 위해 새 인덱스 필요성 발생

    - 새로운 인덱스에 따른 테이블 관리 비용 상승
    - 기존 SQL 문장이 혼동(Upset)될 가능성 커짐

     14-3. 잘 돌던 SQL이 새로운 인덱스를 타는 경우


     14-4. 새로운 인덱스의 추가는 신중해야 한다.

    - 기존의 SQL에 미치는 영향 최소화

    15) 효율적인 인덱스 및 SQL 활용 방안


     15-1. 각 테이블에 적당한 수의 인덱스 사용

     15-2. 가능한 Unique 인덱스 생성

     15-3. 전체 테이블의 5%이상 되는 SQL 질의 회피

    - 인덱스 사용 비용이 더 높음

     15-4. 인덱스가 타지 않는 SQL 문장 실수 주의

     15-5. 정렬 작업의 최소화

    - DISTINCT, ORDER BY, GROUP BY
    - 정렬이 최소화되는 인덱스 활용


     15-6. IN 보다는 EXISTS(정렬최소화)


     15-7. 선택도가 좋은 선행 테이블을 FROM 절에 맨마지막에

    16) INDEX가 동작하지 않는 경우

    - 인덱스 컬럼 절의 변형
    - 내부적인 데이터 변환
    - NULL 조건의 사용
    - 부정형 조건의 사용
    - LIKE 연산자 사용
    - 최적기가 판단


    * reference

    • http://2factor.tistory.com/30
    • http://javapia.blogspot.kr/2010/07/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4db%EC%9D%98-%EC%9D%B8%EB%8D%B1%EC%8A%A4index%EB%9E%80.html
    • http://yysvip.tistory.com/183
    • http://ynebula38.tistory.com/75
    • http://choko11.tistory.com/entry/%EC%9D%B8%EB%8D%B1%EC%8A%A4-1-%EA%B0%9C%EB%85%90%EC%A2%85%EB%A5%98%EC%A3%BC%EC%9D%98%EC%82%AC%ED%95%AD
    • http://gyh214.tistory.com/63
    Share: