[DBMS_Oracle] 뷰 3

뷰 3


1. 뷰를 사용하는 이유

1 복잡하고 긴 쿼리문을 뷰로 정의하면 접근을 단순화시킬 수 있다. 

2 보안에 유리하다.

사용자마다 특정 객체만 조회할 수 있도록 권한을 부여를 할 수 있기에 동일한 테이블을 접근하는 사용자마다에 따라 서로 다르게 보도록 여러 개의 뷰를 정의해 놓고 특정 사용자만이 해당 뷰에 접근할 수 있도록 합니다. 



예를 들어 사원 테이블에 개인 적인 정보인 급여와 커미션은 부서에 따라 접근을 제한해야 합니다. 

급여나 커미션 모두에 대해서 인사과에서는 조회할 수 없도록 하고 경리과에서는 이 모두가 조회될 수 있도록 하지만 영업부서에서는 경쟁심을 유발하기 위해서 다른 사원의 커미션을 조회할 수 있도록 해야 합니다. 


2. 뷰의 종류

뷰는 뷰를 정의하기 위해서 사용되는 기본 테이블의 수에 따라 단순 뷰(Simple View)와 복합 뷰(Complex View)로 나뉩니다.

(1) 실습 [단순 뷰에 대한 데이터 조작]

1) 단순 뷰에 대해서 DML 즉,INSERT/UPDATE/DELETE 문을 사용할 수 있음을 확인합시다. 

EMP_VIEW30 뷰에 데이터를 추가해 봅시다.

INSERT INTO EMP_VIEW30

VALUES(8000, 'ANGEL', 30);

SELECT * FROM EMP_VIEW30;


2) 단순 뷰를 대상으로 실행한 DML 명령문의 치리 결과는 뷰를 정의할 때 사용한 기본 테이블에 적용됩니다. 

SELECT * FROM EMP_COPY;


(2) 실습 [단순 뷰의 컬럼에 별칭 부여하기]

기본 테이블(EMP_COPY)의 컬럼 명을 상속받지 않고 한글화 하여 컬럼 명이 사원번호, 사원명, 급여, 부서번호로 구성되도록 합시다.

1) EMP_VIEW30 뷰에 데이터를 추가해 봅시다.

CREATE OR REPLACE

VIEW EMP_VIEW(사원번호, 사원명, 급여, 부서번호)

AS 

SELECT EMPNO, ENAME, SAL, DEPTNO 

FROM EMP_COPY;


2) EMP_VIEW 는 전체 사원에 대해서 특정 컬럼만 보여주도록 작성하였습니다. 다음과 같이 EMP_VIEW 를 SELECT 하면서 WHERE 절을 추가하여 30번 부서 소속 사원들의 정보만 볼 수 있습니다. 

SELECT * FROM EMP_VIEW

WHERE 부서번호=30;

EMP_VIEW 뷰는 칼럼에 별칭을 주게 되면 기본 테이블의 칼럼 명을 더 이상 상속받지 못하므로 30번 부서를 검색하기 위해서는 뷰를 생성할 때 준 칼럼 별칭(부서번호)을 사용해야 합니다. 


(3) 실습 [그룹 함수를 사용한 단순 뷰]

그룹 함수 SUM과 AVG를 사용해서 각 부서별 급여 총액과 평균을 구하는 뷰를 작성해 봅시다. 뷰를 작성하기 위해서 SELECT 절 다음에 SUM이란 그룹 함수를 사용하면 결과를 뷰의 특정 컬럼처럼 사용하는 것입니다. 

따라서 물리적인 칼럼이 존재하지 않는 가상 칼럼이기에 칼럼 명도 상속 받을 수 없습니다. 뷰를 생성할 때 가상 칼럼을 사용하려면 사용자가 반드시 이름을 따로 설정해야 한다는 것을 명심하기 바랍니다. 


1) 부서별 급여 총액과 평균을 구하기 위한 뷰를 생성해보도록 합시다. 

CREATE VIEW VIEW_SAL

AS

SELECT DEPTNO, SUM(SAL) AS "SalSum", AVG(SAL) AS "SalAvg"

FROM EMP_COPY

GROUP BY DEPTNO;


3. 단순 뷰

단순 뷰에 대해서 DML 명령어를 사용하여 조작이 가능하다고 하였습니다. 

하지만, 다음과 같은 몇 가지의 경우에는 조작이 불가능합니다.


1 뷰 정의에 포함되지 않은 컬럼 중에 기본 테이블의 칼럼이 NOT NULL 제약 조건이 지정되어 있는 경우 INSERT 문이 사용 불가능합니다. 왜냐하면 뷰에 대한 INSERT 문은 기본 테이블에 NULL 값을 입력하는 형태가 되기 때문입니다. 

2 SAL*12와 같이 산술 표현식으로 정의된 가상 칼럼이 뷰에 정의되면 INSERT나 UPDATE가 불가능합니다. 

3 DISTINCT을 포함한 경우에도 DML 명령을 사용할 수 없습니다.

4 그룹 함수나 GROUP BY 절을 포함한 경우에도 DML 명령을 사용할 수 없습니다.


4. 복합 뷰
- 뷰를 사용하는 이유 중의 하나가 복잡하고 자주 사용하는 질의를 보다 쉽고 간단하게 사용하기 위해서라고 했습니다. 이를 살펴보기 위해서 사원 테이블과 부서 테이블을 자주 조인한다고 합시다.
- 사원 테이블과 부서 테이블을 조인하기 위해서는 다음과 같이 복잡한 SELECT 문을 매번 작성해야 합니다. 
SELECT E.EMPNO, E.ENAME, E.SAL, E.DEPTNO, D.DNAME, D.LOC 
FROM EMP E, DEPT D
WHERE E.DEPTNO = D.DEPTNO
ORDER BY EMPNO DESC;
- 뷰를 사용하는 이유 중의 하나가 복잡하고 자주 사용하는 질의를 보다 쉽고 간단하게 사용하기 위해서라고 했습니다. 위에 작성한 조인문에 "CREATE VIEW EMP_VIEW_DEPT AS" 만 추가해서 뷰로 작성해 놓으면 “SELECT * FROM EMP_VIEW_DEPT; " 와 같이 간단하게 질의 결과를 얻을 수 있습니다. 

(1) 실습 [복합 뷰 만들기]
사원 테이블과 부서 테이블을 조인하기 위해서 복합 뷰를 생성해 봅시다. 
1) 다음은 사번, 이름, 급여, 부서번호, 부서명, 지역명을 출력하기 위한 복합 뷰입니다. 
CREATE VIEW EMP_VIEW_DEPT
AS
SELECT E.EMPNO, E.ENAME, E.SAL, E.DEPTNO, D.DNAME, D.LOC 
FROM EMP E, DEPT D
WHERE E.DEPTNO = D.DEPTNO
ORDER BY EMPNO DESC;

2) 뷰를 생성한 후, 이를 활용하면 복잡한 질의를 쉽게 처리할 수 있습니다.
SELECT * FROM EMP_VIEW_DEPT;

5. 뷰 삭제
- 뷰는 실체가 없는 가상 테이블이기 때문에 뷰를 삭제한다는 것은 USER_VIEWS 데이터 딕셔너리에 저장되어 있는 뷰의 정의를 삭제하는 것을 의미합니다. 
- 따라서 뷰를 삭제해도 뷰를 정의한 기본 테이블의 구조나 데이터에는 전혀 영향을 주지 않습니다.

지금까지 생성한 뷰 중에서 VIEW_SAL을 삭제합시다.
DROP VIEW VIEW_SAL;

6. 인라인 뷰

- 사원 중에서 입사일이 빠른 사람 5명(TOP-5)만을 얻어 오는 질의문을 작성해 봅시다. 

- TOP-N을 구하기 위해서는 ROWNUM인라인 뷰가 사용됩니다. 인라인 뷰는 조금 후에 다루어 보도록 하고, 우선 ROWNUM 칼럼에 대해서 살펴보도록 합시다. 

- ROWNUM 칼럼은 DDL을 학습하면서 살펴보았지만 보다 자세히 살펴보도록 합시다. 

    

(1) 실습 [ROWNUM 컬럼 성격 파악하기]

1) 다음은 ROWNUM 칼럼 값을 출력하기 위한 쿼리문입니다. 

SELECT ROWNUM, EMPNO, ENAME, HIREDATE

FROM EMP;


2) 입사일이 빠른 사람 5명만(TOP-N)을 얻어오기 위해서는 일련의 출력 데이터를 일단 임의의 순서로 정렬한 후에 그 중 일부의 데이터만 출력할 수 있도록 해야 하므로 ORDER BY 절을 사용하여 입사일을 기준으로 오름차순 정렬해 봅시다. 

SELECT EMPNO, ENAME, HIREDATE

FROM EMP

ORDER BY HIREDATE;


3) 이번에는 입사일을 기준으로 오름차순 정렬을 하는 쿼리문에 ROWNUM 칼럼을 출력해 봅시다.

SELECT ROWNUM, EMPNO, ENAME, HIREDATE

FROM EMP

ORDER BY HIREDATE;


위 결과를 보면 입사일을 기준으로 오름차순 정렬을 하였기에 출력되는 행의 순서는 바뀌더라도 해당 행의 ROWNUM 칼럼 값은 바뀌지 않는다는 것을 알 수 있습니다. 

ROWNUM 칼럼은 오라클의 내부적으로 부여되는데 INSERT 문을 이용하여 입력하면 입력한 순서에 따라 1씩 증가되면서 값이 지정되어 바뀌지 않습니다. 

정렬된 순서대로 ROWNUM 칼럼 값이 매겨지도록 하려면 새로운 테이블이나 뷰로 새롭게 데이터를 저장해야만 합니다. 


(2) 실습 [뷰와 ROWNUM 칼럼으로 TON-N 구하기]

ROWNUM 칼럼의 성격은 파악했으므로 이제 뷰와 함께 사용하여 TOP-N을 구해봅시다. TOP-N은 일련의 출력 데이터를 일단 임의의 순서로 정렬한 후에 그 중 일부의 데이터만 출력할 수 있도록 하여 구합니다. 

1) 입사일을 기준으로 오름차순 정렬한 쿼리문으로 새로운 뷰를 생성해 봅시다

CREATE OR REPLACE VIEW VIEW_HIRE

AS

SELECT EMPNO, ENAME, HIREDATE

FROM EMP

ORDER BY HIREDATE;

 

2) 입사일을 기준으로 오름차순 정렬을 하는 뷰에 ROWNUM 칼럼을 함께 출력해 봅시다.

SELECT ROWNUM, EMPNO, ENAME, HIREDATE

FROM VIEW_HIRE;


3) 자, 이제 입사일이 빠른 사람 5명만을 얻어와 봅시다.

SELECT ROWNUM, EMPNO, ENAME, HIREDATE

FROM VIEW_HIRE

WHERE ROWNUM<=5;


(3) 인라인 뷰로 TOP-N 구하기

- 인라인 뷰는 SQL 문장에서 사용하는 서브 쿼리의 일종으로 보통 FROM 절에 위치해서 테이블처럼 사용하는 것입니다. 형식은 다음과 같습니다.

- 인라인 뷰란 메인 쿼리의 SELECT 문의 FROM 절 내부에 사용된 서브 쿼리문을 말합니다. 

- 우리가 지금까지 생성한 뷰는 CREATE 명령어로 뷰를 생성했지만, 인라인 뷰는 SQL 문 내부에 뷰를 정의하고 이를 테이블처럼 사용합니다. 


1) 실습 [인라인 뷰로 TOP-N 구하기]

인라인 뷰를 사용해서 입사일이 빠른 사람 5명만을 얻어오기로 합시다. 아래 문장을 보면 FROM 절 다음인 VIEW_HIRE 위치에 VIEW_HIRE를 정의할 때 사용한 서브 쿼리문을 기술한 것뿐입니다. 

SELECT ROWNUM, EMPNO, ENAME, HIREDATE

FROM ( SELECT EMPNO, ENAME, HIREDATE

FROM EMP

ORDER BY HIREDATE)

WHERE ROWNUM<=5;



'Server > DBMS' 카테고리의 다른 글

[DBMS_Oracle] CASE..WHEN...THEN  (0) 2014.12.16
[DBMS_Oracle] 뷰 2  (0) 2014.12.15
[DBMS_Oracle] 뷰 1  (0) 2014.12.15
[DBMS_MySQL] MySQL 설치 및 사용자 추가,권한  (0) 2014.12.13
[DBMS_Oracle] 인덱스  (0) 2014.12.10

[DBMS_Oracle] 뷰 2

뷰 2


1. 뷰의 내부구조와 USER_VIEWS 데이터 딕셔너리

- 뷰는 물리적으로 데이터를 저장하고 있지 않다고 하였습니다. 그런데도 다음과 같은 질의 문을 수행할 수 있는 이유가 무엇일까요?

SELECT * FROM EMP_VIEW30;


- EMP_VIEW30라는 뷰는 데이터를 물리적으로 저장하고 있지 않습니다. 

- CREATE VIEW 명령어로 뷰를 정의할 때 AS 절 다음에 기술한 쿼리 문장 자체를 저장하고 있습니다. 

- 뷰 정의할 때 기술한 쿼리문이 궁금하다면 데이터 딕셔너리 USER_VIEWS 테이블의 TEXT 컬럼 값을 살펴보면 됩니다. 

- USER_VIEWS에서 테이블 이름과 텍스트만 출력해 보겠습니다.

SELECT VIEW_NAME, TEXT

FROM USER_VIEWS;


- 기본 테이블은 디스크 공간을 할당 받아서 실질적으로 데이터를 저장하고 있지만, 뷰는 데이터 딕셔너리USER_VIEWS 에 사용자가 뷰를 정의할 때 기술한 서브 쿼리문(SELECT 문)만을 문자열 형태로 저장하고 있습니다. 


뷰의 동작 원리를 이해하기 위해서 뷰에 대한 질의가 어떻게 내부적으로 처리되는지 자세히 살펴보도록 합시다.

1) 사용자가 뷰에 대해서 질의를 하면 USER_VIEWS에서 뷰에 대한 정의를 조회합니다.

2) 기본 테이블에 대한 뷰의 접근 권한을 살핀다.

3) 뷰에 대한 질의를 기본 테이블에 대한 질의로 변환합니다.

4) 기본 테이블에 대한 질의를 통해 데이터를 검색합니다.

5) 검색된 결과를 출력합니다.


- 우리가 앞에서 생성한 뷰인 EMP_VIEW30를 SELECT문의 FROM절 다음에 기술하여 질의를 하면 오라클 서버는 USER_VIEWS에서 EMP_VIEW30를 찾아 이를 정의할 때 기술한 서브 쿼리문이 저장된 TEXT 값을 EMP_VIEW30 위치로 가져갑니다. 


- 질의는 기본 테이블인 EMP_COPY를 통해 일어납니다. 즉, 기본 테이블인 EMP_COPY 에 대해서 서브 쿼리문을 수행하게 됩니다. 이러한 동작 원리 덕분에 뷰가 실질적으로 데이터를 저장하고 있지 않더라도 데이터를 검색할 수 있는 것입니다. 


(1) 실습 [뷰와 기본 테이블 관계 파악하기]

기본 테이블을 가져다가 쿼리문을 수행한다는 것을 증명하기 위해서 간단한 예를 살펴보도록 하겠습니다. 

1) 다음은 EMP_VIEW30 뷰에 행을 하나 추가하는 문장입니다. 

INSERT INTO EMP_VIEW30

VALUES(1111, 'AAAA', 30);


2) INSERT 문으로 뷰에 새로운 행을 추가하였습니다. 뷰의 내용을 출력해 보면 추가된 행이 뷰에 존재하고 있음을 확인할 수 있습니다. 

SELECT * FROM EMP_VIEW30;


3) 뷰 뿐만 아니라 기본 테이블의 내용을 출력해 보면 INSERT 문에 의해서 뷰에 추가한 행이 테이블에도 존재함을 확인할 수 있습니다. 

SELECT * FROM EMP_COPY;


- INSERT 문에 뷰(EMP_VIEW30)를 사용하였지만, 뷰는 쿼리문에 대한 이름일 뿐이기 때문에 새로운 행은 기본 테이블(EMP_COPY)에 실질적으로 추가되는 것임을 알 수 있습니다. 뷰(EMP_VIEW30)의 내용을 확인하기 위해 SELECT문을 수행하면 변경된 기본 테이블(EMP_COPY)의 내용에서 일부를 서브 쿼리한 결과를 보여줍니다. 

- 뷰는 실질적인 데이터를 저장한 기본 테이블을 볼 수 있도록 한 투명한 창입니다, 기본 테이블의 모양이 바뀐 것이고 그 바뀐 내용을 뷰라는 창을 통해서 볼 뿐입니다. 뷰에 INSERT 뿐만 아니라 UPDATE, DELETE 모두 사용할 수 있는데, UPDATE, DELETE 쿼리문 역시 뷰의 텍스트에 저장되어 있는 기본 테이블이 변경하는 것입니다. 

- 이 정도면 뷰가 물리적인 테이블을 근거로 한 논리적인 가상 테이블이란 말의 의미를 이해할 수 있으리라고 생각됩니다. 


'Server > DBMS' 카테고리의 다른 글

[DBMS_Oracle] CASE..WHEN...THEN  (0) 2014.12.16
[DBMS_Oracle] 뷰 3  (0) 2014.12.15
[DBMS_Oracle] 뷰 1  (0) 2014.12.15
[DBMS_MySQL] MySQL 설치 및 사용자 추가,권한  (0) 2014.12.13
[DBMS_Oracle] 인덱스  (0) 2014.12.10

[DBMS_Oracle] 뷰 1

뷰 1


1. 뷰의 개념

뷰(View)는 한마디로 물리적인 테이블을 근거한 논리적인 가상 테이블이라고 정의할 수 있습니다. 

뷰를 가상 테이블이라고 하는 이유를 살펴보겠습니다. 

가상이란 단어는 실질적으로 데이터를 저장하고 있지 않기 때문에 붙인 것이고, 테이블이란 단어는 실질적으로 데이터를 저장하고 있지 않더라도 사용자는 마치 테이블을 사용하는 것과 동일하게 뷰를 사용할 수 있기 때문에 붙인 것입니다. 

뷰는 기본 테이블에서 파생된 객체로서 기본 테이블에 대한 하나의 쿼리문입니다. 

뷰(View)란 ‘보다’란 의미를 갖고 있는 점을 감안해 보면 알 수 있듯이 실제 테이블에 저장된 데이터를 뷰를 통해서 볼 수 있도록 합니다. 

사용자에게 주어진 뷰를 통해서 기본 테이블을 제한적으로 사용하게 됩니다. 


2. 뷰의 기본 테이블

뷰는 이미 존재하고 있는 테이블에 제한적으로 접근하도록 합니다. 

뷰를 생성하기 위해서는 실질적으로 데이터를 저장하고 있는 물리적인 테이블이 존재해야 하는데 이 테이블을 기본 테이블이라고 합니다. 

우선 시스템에서 제공하는 dept 테이블과 emp 테이블의 내용이 변경되는 것을 막기 위해서 테이블의 내용을 복사한 새로운 테이블을 생성한 후에 이를 기본 테이블로 사용합시다. 

뷰의 기본 테이블을 생성합시다.

1) DEPT_COPY를 DETP 테이블의 복사본으로 생성합시다.

CREATE TABLE DEPT_COPY

AS

SELECT * FROM DEPT;


2) EMP 테이블의 복사본으로 EMP_COPY를 생성합시다.

CREATE TABLE EMP_COPY

AS

SELECT * FROM EMP;


3. 뷰 정의하기

뷰를 생성하여 자주 사용되는 SELECT 문을 간단하게 접근하는 방법을 학습해 봅시다. 다음은 뷰를 생성하기 위한 기본 형식입니다. 

CREATE [OR REPLACE] [FORCE | NOFORCE] VIEW view_name

[(alias, alias, alias, ...)]

AS subquery

[WITH CHECK OPTION]

[WITH READ ONLY];


테이블을 생성하기 위해서 CREATE TABLE 로 시작하지만, 뷰를 생성하기 위해서는 CREATE VIEW로 시작합니다. AS 다음은 마치 서브 쿼리문과 유사합니다. 

subquery에는 우리가 지금까지 사용하였던 SELECT 문을 기술하면 됩니다.


(1) 문장 해설

1) CREATE OR RELPACE VIEW

뷰를 만들 때 CREATE OR RELPACE VIEW 대신 그냥 CREATE VIEW만 사용해도 됩니다. 

그러나 그냥 CREATE VIEW를 통해 만들어진 뷰의 구조를 바꾸려면 뷰를 삭제하고 다시 만들어야 되는 반면, CREATE OR REPLACE VIEW는 새로운 뷰를 만들 수 있을 뿐만 아니라 기존에 뷰가 존재하더라도 삭제하지 않고 새로운 구조의 뷰로 변경(REPLACE)할 수 있습니다. 

그래서 대부분 뷰를 만들 때는 CREATE VIEW 대신 CREATE OR REPLACE VIEW를 사용하는 편입니다.


2) FORCE

FORCE를 사용하면, 기본 테이블의 존재 여부에 상관없이 뷰를 생성합니다. 


3) WITH CHECK OPTION

WITH CHECK OPTION을 사용하면, 해당 뷰를 통해서 볼 수 있는 범위 내에서만 UPDATE 또는 INSERT가 가능합니다.


4) WITH READ ONLY

WITH READ ONLY를 사용하면 해당 뷰를 통해서는 SELECT만 가능하며 INSERT/UPDATE/DELETE를 할 수 없게 됩니다. 

만약 이것을 생략한다면, 뷰를 사용하여 추가, 수정, 삭제(INSERT/UPDATE/DELETE)가 모두 가능합니다.


(2) 뷰 생성 예제

뷰를 만들기 전에 어떤 경우에 뷰를 사용하게 되는지 다음 예를 통해서 뷰가 필요한 이유를 설명해 보도록 하겠습니다. 

만일, 30번 부서에 소속된 사원들의 사번과 이름과 부서번호를 자주 검색한다고 한다면 다음과 같은 SELECT 문을 여러 번 입력해야 합니다. 

SELECT EMPNO, ENAME, DEPTNO

FROM EMP_COPY

WHERE DEPTNO=30;

위와 같은 결과를 출력하기위해서 매번 SELECT 문을 입력하기란 번거로운 일입니다. 

뷰는 이와 같이 번거로운 SELECT 문을 매번 입력하는 대신 보다 쉽게 원하는 결과를 얻고자 하는 바람에서 출발한 개념입니다


자주 사용되는 30번 부서에 소속된 사원들의 사번과 이름과 부서번호를 출력하기 위한 SELECT문을 하나의 뷰로 정의해 봅시다. 

CREATE VIEW EMP_VIEW30

AS 

SELECT EMPNO, ENAME, DEPTNO

FROM EMP_COPY

WHERE DEPTNO=30;


뷰는 테이블에 접근(SELECT)한 것과 동일한 방법으로 결과를 얻을 수 있습니다. 

SELECT * FROM EMP_VIEW30;


(3) 실습 [뷰를 생성할 권한이 불충분한 경우]

30번 부서에 소속된 사원들의 사번과 이름과 부서번호를 출력하기 위한 SELECT문을 하나의 뷰로 정의해 봅시다. 


1) 뷰를 생성하려는데 다음과 같이 권한이 불충분하다고 오류가 발생할 경우가 있습니다.


2) 이럴 경우에는 DBA인 SYSTEM 계정으로 로그인하여 뷰를 생성할 권한을 부여합니다. 

특정 사용자에 대해서 아무 문제없이 뷰가 생성된다면 괜찮지만, 그렇지 않을 경우 GRANT 명령어로 특정 사용자에게 권한을 부여해야 합니다. 

아래 문장은 SCOTT 사용자에게 테이블을 생성할 CREATE VIEW 권한을 부여하는 명령어입니다. 

이 명령어는 DBA 권한이 있는 사용자만이 부여할 수 있으므로 SYSTEM 계정으로 접속합니다. 

CONN system/manager

GRANT CREATE VIEW TO scott;


(4) 실습 [뷰 정의 하기]

뷰를 생성하려는데 다음과 같이 권한을 설정하였다면 30번 부서에 소속된 사원들의 사번과 이름과 부서번호를 출력하기 위한 SELECT문을 하나의 뷰로 다시 정의해 봅시다. 


1) 뷰를 정의합시다.

CREATE VIEW EMP_VIEW30

AS 

SELECT EMPNO, ENAME, DEPTNO

FROM EMP_COPY

WHERE DEPTNO=30;


2) 뷰를 생성할 때 컬럼 이름을 명시하지 않으면 뷰(EMP_VIEW30)를 정의하는 기본 테이블(EMP_COPY)의 컬럼 명을 상속받아 사용합니다. 다음은 생성된 뷰의 구조를 살펴봅시다. 

DESC EMP_VIEW30


3) 뷰의 내용을 출력해 봅시다. 테이블의 내용을 출력하는 것과 동일한 방식으로 수행하면 됩니다.  SELECT * FROM 다음에 테이블 명 대신에 뷰 이름을 기술하면 됩니다. 

SELECT * FROM EMP_VIEW30;


'Server > DBMS' 카테고리의 다른 글

[DBMS_Oracle] 뷰 3  (0) 2014.12.15
[DBMS_Oracle] 뷰 2  (0) 2014.12.15
[DBMS_MySQL] MySQL 설치 및 사용자 추가,권한  (0) 2014.12.13
[DBMS_Oracle] 인덱스  (0) 2014.12.10
[DBMS_Oracle] 시퀀스  (0) 2014.12.08

[Java] 제네릭

제네릭


제네릭은 다양한 타입의 객체들을 다루는 메서드나 컬렉션 클래스에 컴파일 시의 타입 체크를 해주는 기능이다. 

즉, 클래스 내부에서 사용할 데이터 타입을 나중에 인스턴스를 생성할 때 확정하는 것을 제네릭이라 한다.

객체의 타입을 컴파일 시에 체크하기 때문에 객체의 타입 안정성을 높이고 형변환의 번거로움이 줄어든다.

ArrayList와 같은 컬렉션 클래스는 다양한 종류의 객체를 담을 수 있긴 하지만 보통 한 종류의 객체를 담는 경우가 더 많다. 그런데도 꺼낼 때 마다 타입체크를 하고 형변환을 하는 것은 아무래도 불편할 수 밖에 없다.


위의 그림은 아래의 코드를 간략화한 것이다. 

class Person<T>{

    public T info;// p1 일시 데이터 타입은 String이된다.(인스턴스 생성시 String 제네릭 생성)

// p2 일시 데이터 타입은 StringBuilder이 된다.

}

 

public class GenericDemo {

 

    public static void main(String[] args) {

        Person<String> p1 = new Person<String>();

        Person<StringBuilder> p2 = new Person<StringBuilder>();

    }

}

p1.info와 p2.info의 데이터 타입은 결과적으로 아래와 같다.

- p1.info : String

- p2.info : StringBuilder

그것은 각각의 인스턴스를 생성할 때 사용한 <> 사이에 어떤 데이터 타입을 사용했느냐에 달려있다. 


클래스 선언부를 보자.

public T info;


클래스 Person의 필드 info의 데이터 타입은 T로 되어 있다. 그런데 T라는 데이터 타입은 존재하지 않는다. 이 값은 아래 코드의 T에서 정해진다.

class Person<T>{

위 코드의 T는 아래 코드의 <> 안에 지정된 데이터 타입에 의해서 결정된다. 


Person<String> p1 = new Person<String>();

위의 코드를 나눠보자. 아래 코드는 변수 p1의 데이터 타입을 정의하고 있다.


Person<String> p1

아래 코드는 인스턴스를 생성하고 있다. 


new Person<String>();

즉 클래스를 정의 할 때는 info의 데이터 타입을 확정하지 않고 인스턴스를 생성할 때 데이터 타입을 지정하는 기능의 제네릭이다. 


1. 제네릭의 장점

1) 타입 안정성을 제공한다.(타입 안정성을 높인다는 것은 의도하지 않은 타입의 객체를 저장하는 것을 막고, 저장된 객체를 꺼내올 때 원래의 타입과 다른 타입으로 형변환되어 발생할 수 있는 오류를 줄여준다는 뜻이다.)

2) 타입체크와 형변환을 생략할 수 있으므로 코드가 간결해 진다. 

간단히 얘기하면, 다룰 객체의 타입을 미리 명시해줌으로써 형변환을 하지 않아도 되게 하는 것이다.

기존에는 다양한 종류의 타입을 다루는 메서드의 매개변수나 리턴타입으로 Object 타입의 참조변수를 많이 사용했고, 그로 인해 형변환이 불가피했지만, 이젠 Object타입 대신 원하는 타입을 지정하기만 하면 되는 것이다.

(타입을 지정하지 않으면 Object 타입으로 간주된다.)


2. 컬렉션 클래스 이름 바로 뒤에 저장할 객체의 타입을 적어주면, 컬렉션에 저장할 수 있는 객체는 지정한 타입의 객체 뿐이다.

컬렉션클래스<저장할 객체의 타입> 변수명 = new 컬렉션클래스<저장할 객체의 타입>();

ArrayList<Tv> tvList = new ArrayList<Tv>();

// Tv객체만 저장할 수 있는 ArrayList를 생성

tvList.add(new Tv());

tvList.add(new Radio());// 컴파일 에러


3. 다형성을 사용해야 하는 경우에는 부모타입을 지정함으로써 여러 종류의 객체를 저장할 수 있다.

class Product{ }

class Tv extends Product{ }

class Audio extends Product{ }


//Product 클래스의 자손객체들을 저장할 수 있는 ArrayList를 생성

ArrayList<Product> list = new ArrayList<Product>();

list.add(new Product());

list.add(new Tv());

list.add(new Audio());


Product p = list.get(0);// 형변환이 필요없다.

Tv t = (Tv)list.get(i);// 형변환을 필요로 한다.

ArrayList가 Product타입의 객체를 저장하도록 지정하면, 이들의 자손인 Tv와 Audio타입의 객체도 저장할 수 있다. 다만 꺼내올 때 원래의 타입으로 형변환해야 한다.


4. Product 클래스가 Tv클래스의 조상이라 할지라도 아래와 같이는 할 수는 없다.

ArrayList<Product> list = new ArrayList<Tv>();//허용안함


List<Tv> tvList = new ArrayList<Tv>();// But, 허용된다.


5. 와일드카드

보통 제네릭에서는 단 하나의 타입을 지정하지만, 와일드 카드'?'를 사용하면 된다. 보통 제네릭에서는 단 하나의 타입을 지정하지만. 와일드 카드는 하나 이상의 타입을 지정하는 것을 가능하게 해준다.

아래와 같이 어떤 타입('?')이 있고 그 타입이 Product의 자손이라고 선언하면, Tv객체를 저장하는 'ArrayList<Tv>' 또는 Audio객체를 저장하는 'ArrayList<Audio>'를 매개변수로 넘겨줄 수 있다.

Tv와 Audio 모두 Product의 자손이기 때문이다.

public static void printAll(ArrayList<? extends Product> list){//Product 또는 그 자손들이 담긴 ArrayList를 매개변수로 받는 메서드

for(Unit u : list){

System.out.println(u);

}

}


6. 복수의 제네릭

클래스 내에서 여러개의 제네릭을 필요로 하는 경우가 있을 것이다. 예제를 보자.

class EmployeeInfo{

    public int rank;

    EmployeeInfo(int rank){ this.rank = rank; }

}

class Person<T, S>{//복수의 제네릭을 사용할 시에는 ','를 사용한다.

    public T info;

    public S id;

    Person(T info, S id){ 

        this.info = info; 

        this.id = id;

    }

}

public class GenericDemo {

    public static void main(String[] args) {

        Person<EmployeeInfo, int> p1 = new Person<EmployeeInfo, int>(new EmployeeInfo(1), 1);

    }

}

위의 코드는 예외를 발생시키지만 문제는 다음 예제에서 처리하고 형식만 보자. 

즉, 복수의 제네릭을 사용할 때는 <T, S>와 같은 형식을 사용한다. 여기서 T와 S 대신 어떠한 문자를 사용해도 된다. 하지만 묵시적인 약속(convention)이 있기는 하다. 그럼 예제의 오류를 해결하자.


7. 기본 데이터 타입과 제네릭

제네릭은 참조 데이터 타입에 대해서만 사용할 수 있다. 기본 데이터 타입에서는 사용할 수 없다.(wrapper 클래스로 사용할 수 있다.--> 기본타입을 객체타입으로 만드는 것) 따라서 아래와 같이 코드를 변경한다.

class EmployeeInfo{

    public int rank;

    EmployeeInfo(int rank){ this.rank = rank; }

}

class Person<T, S>{

    public T info;

    public S id;

    Person(T info, S id){ 

        this.info = info;

        this.id = id;

    }

}

public class GenericDemo {

    public static void main(String[] args) {

        EmployeeInfo e = new EmployeeInfo(1);

        Integer i = new Integer(10);

        Person<EmployeeInfo, Integer> p1 = new Person<EmployeeInfo, Integer>(e, i);

        System.out.println(p1.id.intValue());

    }

}

new Integer는 기본 데이터 타입인 int를 참조 데이터 타입으로 변환해주는 역할을 한다. 이러한 클래스를 래퍼(wrapper) 클래스라고 한다. 덕분에 기본 데이터 타입을 사용할 수 없는 제네릭에서 int를 사용할 수 있다.


8. 제네릭의 생략

제네릭은 생략 가능하다. 아래 두 개의 코드가 있다. 이 코드들은 정확히 동일하게 동작한다. e와 i의 데이터 타입을 알고 있기 때문이다.

EmployeeInfo e = new EmployeeInfo(1);

Integer i = new Integer(10);

Person<EmployeeInfo, Integer> p1 = new Person<EmployeeInfo, Integer>(e, i);

Person p2 = new Person(e, i);// 제네릭 생략함


9. 메소드에 적용

제네릭은 메소드에 적용할 수도 있다. 

class EmployeeInfo{

    public int rank;

    EmployeeInfo(int rank){ this.rank = rank; }

}

class Person<T, S>{

    public T info;

    public S id;

    Person(T info, S id){ 

        this.info = info;

        this.id = id;

    }

    public <U> void printInfo(U info){// U 데이터 타입은 info라는 매개변수의 데이터타입(EmployeeInfo)이 된다.

        System.out.println(info);

    }

}

public class GenericDemo {

    public static void main(String[] args) {

        EmployeeInfo e = new EmployeeInfo(1);

        Integer i = new Integer(10);

        Person<EmployeeInfo, Integer> p1 = new Person<EmployeeInfo, Integer>(e, i);

        p1.<EmployeeInfo>printInfo(e);//

        p1.printInfo(e);// 제네릭 생략이 가능함

    }

}


10. 제네릭의 제한

(1) extends

제네릭으로 올 수 있는 데이터 타입을 특정 클래스의 자식으로 제한할 수 있다.

abstract class Info{//부모 클래스가 반드시 추상클래스일 필요가 없다.

    public abstract int getLevel();

}

class EmployeeInfo extends Info{

    public int rank;

    EmployeeInfo(int rank){ this.rank = rank; }

    public int getLevel(){

        return this.rank;

    }

}

class Person<T extends Info>{// info 클래스나 info의 자식클래스만이 제네릭으로 올 수 있는 데이터 타입이 된다.(info의 자식이면 OK, 자식이 아니면 컴파일 에러)

    public T info;

    Person(T info){ this.info = info; }

}

public class GenericDemo {

    public static void main(String[] args) {

        Person p1 = new Person(new EmployeeInfo(1));

        Person<String> p2 = new Person<String>("부장");

    }

}

위의 코드에서 중요한 부분은 다음과 같다.


class Person<T extends Info>{

즉 Person의 T는 Info 클래스나 그 자식 외에는 올 수 없다.


extends는 상속(extends)뿐 아니라 구현(implements)의 관계에서도 사용할 수 있다.

interface Info{

    int getLevel();

}

class EmployeeInfo implements Info{

    public int rank;

    EmployeeInfo(int rank){ this.rank = rank; }

    public int getLevel(){

        return this.rank;

    }

}

class Person<T extends Info>{// extends는 상속이 무엇인가가 아니라, 부모가 누군가를 확인하는 코드이다.(implement가 아니다.)

    public T info;

    Person(T info){ this.info = info; }

}

public class GenericDemo {

    public static void main(String[] args) {

        Person p1 = new Person(new EmployeeInfo(1));

        Person<String> p2 = new Person<String>("부장");

    }

}


'Programing > Java' 카테고리의 다른 글

[Java] 바이트 기반의 스트림  (0) 2014.12.16
[Java] 파일I/O 개요  (0) 2014.12.16
[Java] 컬렉션 프레임워크  (0) 2014.12.14
[Java] static  (0) 2014.12.14
[Java] 클래스 메서드와 인스턴스 메서드  (0) 2014.12.14

[Java] 컬렉션 프레임워크


컬렉션 프레임워크



 인터페이스

 특 징

 List

순서가 있는 데이터의 집합, 데이터의 중복을 허용한다.

--> 데이터를 add하면 앞에서 부터 순차적(순서대로)으로 데이터가 들어간다. 

       그래서 각각의 저장되어 있는 공간들은 고유한 index를 갖는다. 

ex.) 대기자 명단

구현 클래스: ArrayList, LinkedList, Stack, Vector등

 

 Set

순서를 유지하지 않는 데이터의 집합, 데이터의 중복을 허용하지 않는다.

--> 집합이다. 데이터가 순서와는 상관없이 add된다. 중복되지 않는다.

ex.) 양의 정수 집합, 소수의 집합

구현 클래스: HashSet, TreeSet등

 Map

키와 값의 쌍으로 이루어진 데이터의 집합. 순서는 유지되지 않으며, 키는 중복을 허용하지 않고, 값을 중복을 허용한다.

ex.) 우편번호, 지역번호

구현 클래스: HashMap, TreeMap, Hashtable, Properties등


1. 컬렉션 프레임워크란

컬렉션즈 프래임워크라는 것은 다른 말로는 컨테이너라고도 부른다. 즉 값을 담는 그릇이라는 의미이다. 그런데 그 값의 성격에 따라서 컨테이너의 성격이 조금씩 달라진다. 자바에서는 다양한 상황에서 사용할 수 있는 다양한 컨테이너를 제공하는데 이것을 컬렉션즈 프래임워크라고 부른다. ArrayList는 그중의 하나다.


2. 배열과 비교

 배열은 연관된 데이터를 관리하기 위한 수단이었다. 그런데 배열에는 몇가지 불편한 점이 있었는데 그 중의 하나가 한번 정해진 배열의 크기를 변경할 수 없다는 점이다.(배열을 만들고 나서 추후에 배열의 크기를 바꿀수 없다.) 이러한 불편함을 컬렉션즈 프래임워크를 사용하면 줄어든다.


import java.util.ArrayList;

public class ArrayListDemo {

 

    public static void main(String[] args) {

        String[] arrayObj = new String[2];

        // 배열의 크기 지정--> 생성후 변경 불가

        arrayObj[0] = "one";

        arrayObj[1] = "two";

        // arrayObj[2] = "three"; 오류가 발생한다.

        // --> 배열의 크기 변경 불가 (배열은 배열의 크기를 알 때만 사용할 수 있다.)

        for(int i=0; i<arrayObj.length; i++){

            System.out.println(arrayObj[i]);

        }

         

        ArrayList<String> al = new ArrayList<String>(); // <Strinf>: al 참조변수에 추가되는 값이 String 데이터 타입이라는 것을 지정한다.

        // 배열과 비슷하나, 객체 생성후 몇개의 값을 사용할 지 지정할 필요가 없다.

        // --> 여기서 al 참조변수는 컨테이너라고 한다.(자료를 담는 그릇이 된다.) --> 아래의 코드는 add()를 이용하여 al에 one, two, Three를 넣은 모습이다.

        al.add("one");

        al.add("two");

        al.add("three");

        // 값을 추가해도 상관없다. outofbound 에러 없음

        for(int i=0; i<al.size(); i++){// 배열은 length

        // String value = al.get(i);// value 변수에 리턴되는 데이터타입은 ArrayList이므로 String으로 데이터타입 지정시 에러가 발생한다. 

        // 그래서 (String)al.get(i) 또는 ArrayList<String>을 객체 선언시 지정해야 한다. 

            System.out.println(al.get(i));//i=1이면 one, i=2이면 two, 제네릭을 통해서 (String)al.get(i)을 통해 형변환할 필요가 없다.

        }

    }

}

실행결과)

one

two

one

two

three

- ArrayList는 배열과는 사용방법이 조금 다르다. 배열의 경우 값의 개수를 구할 때 .length를 사용했지만 ArrayList는 메소드 size를 사용한다. 

또한, 특정한 값을 가져올 때 배열은 [인덱스 번호]를 사용했지만 컬렉션은 .get(인덱스 번호)를 사용한다.


3. 중복을 허용하는 List/ 중복 허용하지 않는 Set

자료를 꺼낼때 순서가 유지되는지 아닌지 주의있는 보자

import java.util.ArrayList;

import java.util.HashSet; 

import java.util.Iterator;

 

public class ListSetDemo {

 

    public static void main(String[] args) {

        ArrayList<String> al = new ArrayList<String>();

        al.add("one");

        al.add("two");

        al.add("two");

        al.add("three");

        al.add("three");

        al.add("five");

        System.out.println("array");

        Iterator<String> ai = al.iterator(); //<String>에서 제네릭을 선언하지 않았다면 (String)로 형변환을 해야 한다.

        while(ai.hasNext()){

            System.out.println(ai.next());

        }

         

        HashSet<String> hs = new HashSet<String>();

        hs.add("one");

        hs.add("two");

        hs.add("two");

        hs.add("three");

        hs.add("three");

        hs.add("five");

        Iterator<String> hi = hs.iterator();

        System.out.println("\nhashset");

        while(hi.hasNext()){

            System.out.println(hi.next());

        }

    }

}

실행결과)

array

one

two

two

three

three

five

 

hashset

two

five

one

three


4. Map

(1) 예제 1

import java.util.*;


public class MapDemo {

 

    public static void main(String[] args) {

        HashMap<String, Integer> a = new HashMap<String, Integer>();

        a.put("one", 1);// key, value

        a.put("two", 2);

        a.put("three", 3);

        a.put("four", 4);

        System.out.println(a.get("one"));

        System.out.println(a.get("two"));

        System.out.println(a.get("three"));

         

        iteratorUsingForEach(a);

        iteratorUsingIterator(a);

    }

    

    // Map 데이터 iterator 기능 없음 --> Map 데이터 가져오는 방법 1

    static void iteratorUsingForEach(HashMap<String, Integer> map){

        Set<Map.Entry<String, Integer>> entries = map.entrySet();

        for (Map.Entry<String, Integer> entry : entries) {

            System.out.println(entry.getKey() + " : " + entry.getValue());

            //Map 데이터를 가져오는 방법 --> getKey()=key값 ,getValue()=value값

        }

    }

    // Map 데이터 iterator 기능 없음 --> Map 데이터 가져오는 방법 2

    static void iteratorUsingIterator(HashMap<String, Integer> map){

        Set<Map.Entry<String, Integer>> entries = map.entrySet();

        Iterator<Map.Entry<String, Integer>> i = entries.iterator();

        while(i.hasNext()){

            Map.Entry<String, Integer> entry = i.next();

            System.out.println(entry.getKey()+" : "+entry.getValue());

        }

    }

}

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


(2) 예제2

import java.util.*;


class GenericsEx5

{

public static void main(String[] args) 

{

HashMap<String,Student> map = new HashMap<String,Student>();

map.put("1-1", new Student("자바왕",1,1,100,100,100));

map.put("1-2", new Student("자바짱",1,2,90,80,70));

map.put("2-1", new Student("홍길동",2,1,70,70,70));

map.put("2-2", new Student("전우치",2,2,90,90,90));

Student s1 = map.get("1-1");

System.out.println("1-1 :" + s1.name);

Iterator<String> itKey = map.keySet().iterator();

// key값만 가져오는 경우

while(itKey.hasNext()) {

System.out.println(itKey.next());

}


Iterator<Student> itValue = map.values().iterator();

int totalSum = 0;

// value값만 가져오는 경우

while(itValue.hasNext()) {

Student s = itValue.next();

System.out.println(s);

totalSum += s.total;

}


System.out.println("전체 총점:"+totalSum);

} // main

}


class Student extends Person implements Comparable<Person> { 

String name = ""; 

int ban = 0; 

int no = 0; 

int koreanScore = 0; 

int mathScore = 0; 

int englishScore = 0; 


int total = 0; 


Student(String name, int ban, int no, int koreanScore, int mathScore, int englishScore) { 

super(ban+"-"+no, name);

this.name = name; 

this.ban = ban; 

this.no = no; 

this.koreanScore = koreanScore; 

this.mathScore = mathScore; 

this.englishScore = englishScore; 


total = koreanScore + mathScore + englishScore; 


public String toString() { 

return name + "\t" 

+ ban + "\t" 

+ no + "\t" 

+ koreanScore + "\t" 

+ mathScore + "\t" 

+ englishScore + "\t" 

+ total + "\t"; 


   // Comparable<Person>이므로 Person타입의 매개변수를 선언.

public int compareTo(Person o) { 

  return id.compareTo(o.id);    // String클래스의 compareTo()를 호출

}


} // end of class Student 


class Person  {    

     String id;

     String name;


     Person(String id, String name) { 

         this.id = id;

         this.name = name; 

     }

}

실행결과)
1-1 :자바왕
2-2
1-1
2-1
1-2
전우치 2 2 90 90 90 270
자바왕 1 1 100 100 100 300
홍길동 2 1 70 70 70 210
자바짱 1 2 90 80 70 240
전체 총점:1020

5. Iterator

컬렉션 프레임워크에서 컬렉션에 저장된 요소들을 읽어오는 방법을 표준화 하였다. 컬렉션에 저장된 각 요소에 접근하는 기능을 가진 Iterator인터페이스를 정의하고, Collection인터페이스에는 Iterator

(Iterator를 구현한 클래스의 인스턴스)를 반환하는 iterator()를 정의하고 있다.

public interface Iterator{

boolean hasNext();

Object next();

void remove();

}


public interface Collection{

...

public Iterator iterator();

...

}

iterator()는 Collection인터페이스에 정의된 메서드이므로 Collection인터페이스의 자손인 List와 Set에도 포함되어 있다.

그래서 List나 Set인터페이스를 구현하는 컬렉션은 iterator()가 각 컬렉션의 특징에 알맞게 작성되어 있다. 


List list = new ArrayList();


Iterator it = list.iterator();

while(it.hasNext()){

System.out.println(it.next());

}

ArrayList에 저장된 요소들은 출력하기 위한 코드이다.

ArrayList대신 List인터페이스를 구현한 다른 컬렉션 클래스에 대해서도 이와 동일한 코드를 사용할 수 있다. 첫 줄에서 ArrayList 대신 List인터페이스를 구현한 다른 컬렉션 클래스의 객체를 생성하도록 변경하기만 하면 된다.


Map map = new HashMap();

...

Iterator it = map.keySet().iterator();

Map 인터페이스를 구현한 컬렉션 클래스는 키와 값을 쌍을 저장하고 있기 때문에 iterator()를 직접 호출할 수 없고, 그 대신 ketSet()이나 entrySet()과 같은 메서드를 통해서 키와 값을 각각 따로 Set의 형태로 얻어 온 후에 다시 iterator()를 호출해야 Iterator를 얻을 수 있다.


(1) 예제1

import java.util.*;


class IteratorEx1{

public static void main(String[] args) {

ArrayList list = new ArrayList();

list.add("1");

list.add("2");

list.add("3");

list.add("4");

list.add("5");


Iterator it = list.iterator();

while(it.hasNext()) {

Object obj = it.next();

System.out.println(obj);

}

} // main

} // class

실행 결과)

1

2

3

4

5

List 클래스들은 저장순서를 유지하기 때문에 Iterator를 이용해서 읽어 온 결과 역시 저장 순서와 동일하지만 Set클래스들은 각 요소간의 순서가 유지 되지 않기 때문에 Iterator를 이용해서 저장된 요소들은 읽어와도 처음에 저장된 순서와 같지 않다.


6. 정렬

import java.util.*;


class ArrayListEx1

{

    public static void main(String[] args) 

    {

        ArrayList<Integer> list1 = new ArrayList<Integer>(10);

        list1.add(new Integer(5));

        list1.add(new Integer(4));

        list1.add(new Integer(2));

        list1.add(new Integer(0));

        list1.add(new Integer(1));

        list1.add(new Integer(3));

 

        ArrayList list2 = new ArrayList(list1.subList(1,4)); 

        print(list1, list2);

 

        Collections.sort(list1);    // list1과 list2를 정렬한다.

        Collections.sort(list2);    // Collections.sort(List l)

        print(list1, list2);

 

        System.out.println("list1.containsAll(list2):"+ list1.containsAll(list2));

 

        list2.add("B");

        list2.add("C");

        list2.add(3, "A");

        print(list1, list2);

 

        list2.set(3, "AA");

        print(list1, list2);

         

        // list1에서 list2와 겹치는 부분만 남기고 나머지는 삭제한다.

        System.out.println("list1.retainAll(list2):" + list1.retainAll(list2)); 

        print(list1, list2);

         

        //  list2에서 list1에 포함된 객체들을 삭제한다.

        for(int i= list2.size()-1; i >= 0; i--) {

        //--> for문의 카운터를 0부터 증가시킨 것이 아니라, list2.size()-1 부터 감소시키면서 거꾸로 반복시켰다.

       //만약 카운터를 증가시켜가면서 삭제하면 한 요소가 삭제될 때마다 빈 공간을 채우기 위해 나머지 요소들이 자리이동을 하기 때문에 올바른 결과를 얻을 수 없다. 그래서 카운터를 감소시켜가면서

       //삭제를 해야 자리이동이 발생해도 영향을 받지 않고 작업이 가능하다.

            if(list1.contains(list2.get(i)))

                list2.remove(i);

        }

        print(list1, list2);

    } // main

 

    static void print(ArrayList list1, ArrayList list2) {

        System.out.println("list1:"+list1);

        System.out.println("list2:"+list2);

        System.out.println();       

    }

} // class

실행 결과)

list1:[5, 4, 2, 0, 1, 3] 

list2:[4, 2, 0]


list1:[0, 1, 2, 3, 4, 5] // Collections.sort(List1)을 이용해서 정렬하였다.(Collections는 클래스이다.)

list2:[0, 2, 4]


list1.containsAll(list2):true // list1이 list2의 모든 요소를 포함하고 있는 경우에만 true를 얻는다.

list1:[0, 1, 2, 3, 4, 5]

list2:[0, 2, 4, A, B, C]// add(Object obj)를 이용해서 새로운 객체를 저장하였다.


list1:[0, 1, 2, 3, 4, 5]

list2:[0, 2, 4, AA, B, C]// set(int index, Object obj)를 이용해서 저장된 객체를 변경하였다.


list1.retainAll(list2):true// retainAll의 작업결과로 list1에 변화가 있었으므로 true를 반환한다.

list1:[0, 2, 4]// list2와의 공통 요소 이외에는 모두 삭제되었다.(변화가 있었다.)

list2:[0, 2, 4, AA, B, C]


list1:[0, 2, 4]

list2:[AA, B, C]



'Programing > Java' 카테고리의 다른 글

[Java] 파일I/O 개요  (0) 2014.12.16
[Java] 제네릭  (3) 2014.12.14
[Java] static  (0) 2014.12.14
[Java] 클래스 메서드와 인스턴스 메서드  (0) 2014.12.14
[Java] 클래스멤버와 인스턴스멤버간의 참조와 호출  (0) 2014.12.14

[Java] static


static

static이라는 의미는 ‘정적인, 움직이지 않는다.’는 뜻이다. 메모리에서 고정되기 때문에 붙은 이름이지만, 실제 소스에서 static을 사용한다는 의미는 모든 객체가 ‘공유’한다는 의미이다.

cf.) 객체지향이라는 패러다임이란 데이터와 기능(로직, 메소드)을 가진 객체들의 커뮤니케이션으로 어떤 작업을 완료하는 것을 의미한다. 


cf.) 객체마다 데이터를 가져도 불편한 때도 있다

예를 들어 여러분이 어떤 쇼핑몰을 운영한다고 가정해보자. 여러분의 시스템에서 발생하는 매출 현황은 비단 여러분뿐 아니라 여러분의 회사의 모든 직원이 알아야 한다.

이 비유는 객체지향 프로그래밍에서도 마찬가지로 적용할 수 있다.

1 모든 객체가 동일한 데이터를 참고해야 할 필요가 있다.

2 모든 객체는 데이터에 영향을 줄 수 있다.


1. static 중요 법칙

1 static이 붙은 변수들은 객체들이 다 같이 공유하는 데이터를 의미한다.

2 static이 붙은 메소드들은 객체들의 데이터와 관계없는 완벽하게 공통적인 로직을 정의할 때 사용한다.

3 따라서 static 메소드에서는 인스턴스 변수나 객체의 메소드를 사용할 수 없다.


2. static을 사용하는 기준

(1) 객체가 데이터를 보관할 필요가 없다면 static 메소드로 만들어도 된다.

ex.) 

    Random random = new Random( ); ①

    double a = Math.ramdom( ); ②

둘다 마찬가지로 임의의 소수를 발생시킨다.

①은 객체가 데이터를 가지기 때문에 동일한 메소드를 호출해도 다른 결과를 만들어 낼 수 있다는 것을 의미한다.(Random 클래스는 객체를 생성할 때 데이터를 가지게 할 수 있다.)

②Math.random( )은 특정한 데이터를 기준으로 하는 것이 아니라, 완전히 매번 동일한 방법으로 동작하게 된다. 결론적으로 어떤 메소드가 완전히 객체의 데이터와 독립적인 결과를 만들어내야 하는 상황에서는 static 메소드로 만드는 것이 더 나은 선택이다.


(2) 객체들이 공유하는 데이터를 사용할 때에도 static 메소드를 이용한다.

static이라는 키워드가 붙은 존재는 객체와 무관해지기 위해서 사용한다. 그런데 static한 메소드에서 어떤 특정한 객체의 데이터를 활용하게 되면 몇 가지 문제가 생긴다.

만일 static한 공통된 기능의 메소드에서 어떤 하나의 객체의 데이터를 사용하게 된다면 대체 어떤 객체의 데이터를 활용해야 할까?

static이 붙으면 객체와는 무관해진다. 따라서 그 안에서 특정한 하나의 객체의 데이터를 활용하게 되면 문제가 되는데, 가장 근본적인 것은 만들어진 수많은 객체 중에서 어떤 객체의 데이터를 활용해야 할지 알 수 없게 된다는 점이다. 이런 이유 때문에 static이 붙은 메소드 안에서는 인스턴스 변수를 활용할 수 없도록 컴파일러가 확인하게 된다. 


3. static의 의미 

(1) 공유

어떤 변수에 static을 붙이게 되면 이 변수는 모든 객체가 다 같이 공유하는 변수가 된다. (static이 붙은 변수를 사용하는 모든 객체는 공유된 데이터를 사용한다.)

위와 같은 문제에서 어떤 데이터를 모든 객체가 같이 사용하게 하고 싶다면 static이라는 키워드가 바로 방법이다. 

ex.) 은행에 있는 여러개의 번호표 → 각 기계(객체)마다 각각의 번호표를 사용한다면 1번 번호표를 가진 고객들은 서로 자신이 1번이라고 주장하는 웃지 못할 상황이 발생한다.

이 문제의 근본적인 문제는 각 객체가 자신만의 데이터를 갖는다는 것에 있다. 이 문제를 해결하기 위한 가장 손쉬운 해결책 역시 모든 객체가 같은 데이터를 공유할 수 있게 하면 된다.

→ static은 어떤 변수를 모든 객체가 공유하는 값으로 변경해버리기 때문에 아무리 많은 객체가 있더라도 같은 데이터를 사용하게 하는 공유의 개념을 완성할 수 있다.


(2) 객체와 무관한 메소드와 함수라는 개념

static이 붙은 메소드는 모든 객체가 공유하는 메소드이다.(static이 붙은 변수도 모든 객체가 공유)

모든 객체가 자신만의 데이터를 가지고 있으니 발생되는 문제처럼 메소드 역시 객체의 데이터의 의해서 좌우되는 상황을 벗어나기 위해서 Java에서의 static은 메소드에서도 적용할 수 있게 된다.

1) static 메소드는 결과적으로 모든 객체가 완벽하게 똑같이 동작하는 메소드를 위해서 사용

클래스에서 나온 객체의 데이터에 영향받지 않고 완벽히 동일하게 동작하는 메소드를 만들고 싶다면 static을 이용해서 객체의 메소드를 처리하면 된다. 메소드 앞에 static이 붙으면 모든 객체가 클래스에 정의된 방법대로만 사용하게 된다. 이 문제는 좀 더 현실적인 해결책으로 생각해보면 객체가 가진 데이터의 의해서 좌우되고 싶지 않다는 뜻이다.

객체가 어떤 데이터를 가지든 객체의 데이터의 영향 없이 동작하게 만든다면 언제나 완전하게 동일하게 동작하는 것을 보장해줄 수 있다.

2) Integer.parseInt( )는 static 메소드이다.

이 메소드는 언제 어디서나 동일하게 동작한다는 것이다. 이 메소드 자체가 어는 상황에서든 객체의 데이터에 의해서 영향받지 않고 같은 기능만을 제공한다.

→ static 메소드는 마치 C언어의 함수와 유사한다. 완벽하게 언제나 동일하게 동작하는 것이 보장되기 때문에 사용자는 객체의 데이터를 신경 쓰지 않아도 된다. 이런 의미에서 보면 static메소드라는 것은 객체가 가지는 메소드라기 보다는 클래스 자체의 메소드라는 의미로 생각해볼 수 있다.


(3) 단 한번만 동작하게 하는 static

static이 실행되는 시점은 클래스가 메모리상에 올라갈 때이다. 즉 우리가 프로그램을 실행하면 필요한 클래스가 메소리상에 로딩되는 과정을 거치게 된다. 그리고 한번 로딩된 클래스는 특별한 일이 발생하지 않는 이상 메모리상에서 객체를 생성할 수 있도록 메모리에 상주하게 된다. 

static 은 바로 이 시점에 동작한다. 클래스가 메모리상에 올라가자마자 실행되면서 필요한 처리를 하게 된다. 따라서 static 은 객체의 생성과는 관계없이 클래스가 로딩되는 시점에 단 한 번만 실행하고 싶은 처리를 하기 위해서 사용한다.


4. static이 붙은 변수가 공유 되는 원리

cf.) static이 붙은 변수는 객체의 레퍼런스를 이용해서 사용하는 일반적인 객체지향 프로그래밍과는 달리 클래스의 변수이기 때문에 그냥 클래스 ‘이름, 클래스’변수라는 방식으로 사용하게 된다.(new해서 객체생성 불요)

(1) static이 붙은 변수를 클래스 변수라고 부른다.

모든 객체는 자신이 어디에서 생산되었는지 해당 클래스를 알 수 있다. 모든 객체가 자신이 속한 클래스를 알 수 있다는 것을 다른 말로 하면 모든 객체는 자신이 속한 클래스의 정보를 공유한다는 뜻이다.

사실 static의 비밀은 바로 이곳에 있다. static의 가장 중요한 내용은 객체와 묶이는 데이터가 아니라 클래스와 묶이는 데이터라는 것이다.

static을 사용하는 변수는 객체가 아닌 클래스의 변수이기 때문에 별도의 사용할 필요 없이 그냥 사용할 수 있다.

(2) JVM의 구조(java의 메모리 구조)

1) Heap 영역(객체 영역)

객체의 영역이란 객체들이 만들어지고 살다가 죽는 영역이다. 이 영역에서 가장 중요한 존재는 다름 아닌 가비지 컬렉터이다. 

2) Method Area(비객체 영역)

비객체 영역인 Method Area는 클래스가 메모리상에 올라가는 영역이다. 이 영역은 가비지 컬렉터의 영향을 받지 않고 메모리에 상주한다. 간단히 생각해보면 모든 객체는 클래스에서 나오는데 이 클래스가 나도 모르게 메소리상에서 지워지면 난감한 일이 발생할 수 있다. 비객체 영역은 가비지 컬렉터의 영향으로부터 자유롭고, 메모리에 상주하게 되어 있는데 이런 상주의 의미를 ‘static'이라는 뜻으로 사용된다.

(3) static이 붙은 부분은 클래스가 메모리상에 로딩되면서 같이 올라간다.

static 키워드가 붙으면 메소리상에서 다르게 처리된다는 것이다. static이 붙은 변수를 클래스 변수라고 하는 것은 변수가 존재하는 영역이 클래스가 존재하는 영역과 같기 때문이다.

Java에서 모든 객체는 결과적으로는 동일한 클래스에서부터 생성되기 때문에 동일한 클래스에서 나온 모든 객체는 자신이 속한 클래스에 대해서 접근할 수 있다.

static 변수는 이런 방식을 통해서 모든 객체가 동일한 데이터를 사용할 수 있는 공유의 개념을 완성시킨다.


cp.) Tip

- 객체의 영향을 받지 않기 때문에 static은 굳이 객체를 통해서 사용할 이유가 없다.

어떤 변수나 메소드 앞에 static이 나온다는 의미는 결과적으로 객체의 데이터나 메소드의 영향을 받지 않고 완벽하게 동일한 공유의 개념을 완성하는 것이다.

따라서 굳이 객페의 레퍼런스를 이용해야 하는 필요성이 떨어진다. 어차피 객체보다는 클래스와 더 가깝기 때문이다.


→ static 클래스 이름. 함수(메소드)

static은 객체마다 다른 데이터를 가지고 동작하는 것을 막고 완벽하게 동일하게 동작하는 것을 보장하기 위해서 사용하기 때문에 굳이 객체를 사용해야 하는 이유가 없으므로 바로 ‘클래스 이름. 변수’나 ‘클래스 이름. 메소드’를 사용할 수 있다.

굳이 객체를 사용하지 않아도 된다는 의미는 객체를 사용하는 기존의 방식도 가능하기는 하다는 뜻이다.


- static이 붙은 메소드를 보면 무조건 ‘객체와 무관하다.’혹은 ‘객체의 영향 받지 않는 메소드’이다.라고 생각하자.

모든 경우에 완벽하게 동일하게 동작해주어야만 한다. 이런 상황에서는 static을 이용해서 경우에 따라서 다르게 동작할 수 없게 하는 것이 정상적인 static의 사용법이다.

→ 실무에서 static을 이용할 것인지 아닌지를 판단하는 데 있어서 가장 중요한 것은 객체마다 데이터를 활용해야 하는 경우인가. 그렇지 않은가 이다.

Integer.parseInt(),Math.random()을 보면 객체가 가진 데이터와는 완전히 무관하다. 그냥 단순히 로직을 묶어둔 존재에 가깝다.


- static은 속도는 빠르지만, 회수되지 않기 때문에 주의 요한다.

static이라는 키워드가 붙은 변수는 가비지 컬렉션의 대상이 아니라는 데에서 시작한다.

static을 클래스 변수라고 말하듯이 static이 붙은 변수는 클래스와 같은 영역에 생기고, 클래스와 동일하게 메모리에서 항상 상주하게 된다. 이 때문에 static으로 어떤 데이터나 객체를 연결해서 사용하게 되면 메모리 회수가 안된다. 실제로 시스템이 가동되면서 점점 느려진다.


5. 예제

class Exam_02_Sub {

private String name;

private double don = 1000.0;

private static float iyul = 0.05f;

// static 초기화 구문(static 초기화: 객체 발생 이전)

static {

iyul = 0.05f;

 // 보통 생성자를 선언하여 초기화 하는 것과 같은 구문이다.

// iyul이라는 공간을 0.05f로 초기화 및 선언한다.

// 이렇게 static 선언하면 객체 생성을 하지 않고도 iyul을 사용할 수 있다.

}

// static 메서드: static 필드 컨트롤 목적 즉, static 필드 초기화 및 수정하게 할 목적인 것이다.

public static void setIyul(float iyul) {

Exam_02_Sub.iyul = iyul;

// this.iyul=iyul; this를 넣을 수 없다. static에는 0번째 매개변수가 없다. 

// 왜냐하면 static메서드는 모든 객체가 공유하는 메서드이므로 자기자신이라는 것은 있을 수 없다.

// static 메서드는 this.disp();를 할 수 없다. -->this들어가는 것은 static에서 무조건 쓸 수 없다.

// static 메서드 안에서 일반 메서드를 호출하려면 객체 생성를 해야 한다. 바로 다이렉트로 일반 메서드를 사용할 수 없다.

Exam_02_Sub es = new Exam_02_Sub("C", 1000.0, 0.05f);

es.disp();

}

public Exam_02_Sub(String name, double don, float iyul) {

this.name = name;

this.don = don;

Exam_02_Sub.iyul = iyul;

// 같은 클래스 내부에서는 일반 메서드끼리는 호출이 가능하다.

// this.disp();

}

public void disp() {

System.out.println("name : " + this.name);

System.out.println("don : " + this.don);

// 일반 메서드에서 static을 다이렉트로 호출이 가능하다.

System.out.println("iyul : " + Exam_02_Sub.iyul);

}

public class Exam_02 {

// 일반 메서드를 main 메서드 안에서 호출하려면 static 메서드로 만들어야 한다. 그렇지 않으면 객체생성후 메서드 호출 요한다.

// 일반 메서드로 만들면 this 때문에 static 메서드에서 호출할 수 없다.

public static void main(String[] ar) {

Exam_02_Sub es = new Exam_02_Sub("A", 1000.0, 0.07f);

es.disp();

System.out.println();

Exam_02_Sub es1 = new Exam_02_Sub("B", 1000.0, 0.03f);

es.disp();

es1.disp();

System.out.println();

Exam_02_Sub es2 = new Exam_02_Sub("C", 1000.0, 0.05f);

es.disp();

es1.disp();

es2.disp();

}

}


class Exam_02_Sub_01 {

private String name;

private double don = 1000.0;

private static float iyul = 0.05f;

//private float iyul = 0.05f;

public Exam_02_Sub_01(String name, double don, float iyul) {

this.name = name;

this.don = don;

Exam_02_Sub_01.iyul = iyul;

}

public void disp() {

System.out.println("name : " + this.name);

System.out.println("don : " + this.don);

System.out.println("iyul : " + Exam_02_Sub_01.iyul);

}

}

public class Exam_02_01 {

public static void main(String[] ar) {

Exam_02_Sub_01 es = new Exam_02_Sub_01("A", 1000.0, 0.07f);

es.disp();

System.out.println("-------------");

Exam_02_Sub_01 es1 = new Exam_02_Sub_01("B", 1000.0, 0.03f);

es.disp();

es1.disp();

System.out.println("-------------");

Exam_02_Sub_01 es2 = new Exam_02_Sub_01("C", 1000.0, 0.05f);

es.disp();

es1.disp();

es2.disp();

}

}



[Java] 클래스 메서드와 인스턴스 메서드


클래스 메서드와 인스턴스 메서드


클래스메서드도 클래스변수처럼, 객체를 생성하지 않고도 '클래스이름.메서드이름(매개변수)'와 같은 식으로 호출이 가능하다.

클래스는 데이터(변수)와 데이터에 관련된 메서드의 집합이라고 할 수 있다. 

같은 클래스 내에 있는 메서드와 멤버변수는 아주 밀접한 관계가 있다.

인스턴스메서드는 인스턴스변수와 관련된 작업을 하는, 즉, 메서드의 작업을 수행하는데 인스턴스변수를 필요로 하는 메서드이다. 그래서 인스턴스변수는 인스턴스(객체)를 생성해야만 만들어지므로 인스턴스변수를 사용하는 인스턴스메서드 역시 인스턴스를 생성해야만 호출할 수 있는 것이다. 반면에 메서드 중에서 인스턴스와 관계없는(인스턴스변수나 인스턴스메서드를 사용하지 않는) 메서드를 클래스메서드로 정의한다.

cf.) 멤버변수는 인스턴스변수와 static변수를 모두 통칭하는 말이다. -->정확히 인스턴스변수라고 표현하자.


1) 클래스를 설계할 때, 멤버변수 중 모든 인스턴스에 공통적으로 사용해야하는 것에 static을 붙인다.

생성된 각 인스턴스는 서로 독립적이기 때문에 각 인스턴스의 변수는 서로 다른 값을 유지한다. 그러나 모든 인스턴스에서 같은 값이 유지되어야 하는 변수는 static을 붙여서 클래스변수로 정의해야 한다.


2) 클래스변수(static 변수)는 인스턴스를 생성하지 않아도 사용할 수 있다.

static이 붙은 변수는 클래스가 메모리에 올라갈 때 이미 자동적으로 생성되기 때문이다.


3) 클래스메서드는 인스턴스변수를 사용할 수 없다.

인스턴스변수는 인스턴스가 반드시 존재해야만 사용할 수 있는데,  클래스메서드는 인스턴스 생성 없이 호출가능하므로 클래스메서드가 호출되었을 때 인스턴스가 존재할 수도 있고, 존재하지 않을 수도 있다.

그래서 클래스메서드에서 인스턴스변수의 사용을 금지한다. 그러나, 인스턴스변수나 인스턴스메서드에서는 static이 붙은 멤버들을 사용하는 것이 언제나 가능하다. 인스턴스변수가 존재한다는 것은 static이 붙은 변수가 이미 메모리에 존재한다는 것을 의미하기 때문이다.


4) 메서드 내에서 인스턴스변수를 사용하지 않는다면, static을 붙이는 것을 고려한다.

메서드의 작업내용 중에서 인스턴스변수를 필요로 한다면, static을 붙일 수 없다. 반대로 인스턴스변수를 필요로 하지 않는다면 static을 붙이자. 메서드 호출시간이 짧아지기 때문에 효율이 높아진다.

인스턴스 메서드는 실행 시 호출되어야할 메서드를 찾는 과정이 추가적으로 필요하기 때문에 시간이 더 걸린다.


cf.) random()과 같은 Math클래스의 모든 메서드는 클래스메서드이다. Math 클래스에는 인스턴스변수가 하나도 없거니와 작업을 수행하는데 필요한 값들을 모두 매개변수로 받아서 처리하기 때문이다.


class MyMath2 {

long a, b;

// 인스턴스변수 a, b만을 이용해서 작업하므로 매개변수가 필요없다.

long add() { return a + b; } // a, b는 인스턴스변수

long subtract() { return a - b; }

long multiply() { return a * b; }

double divide() { return a / b; }


// 인스턴스변수와 관계없이 매개변수만으로 작업이 가능하다.

static long add(long a, long b) { return a + b; }     // a, b는 지역변수

static long subtract(long a, long b) { return a - b; }

static long multiply(long a, long b) { return a * b; }

static double divide(double a, double b) { return a / b; }

}


class MyMathTest2 {

public static void main(String args[]) {

// 클래스메서드 호출(객체생성없이 매개변수만으로 작업 수행)

System.out.println(MyMath2.add(200L, 100L));

System.out.println(MyMath2.subtract(200L, 100L));

System.out.println(MyMath2.multiply(200L, 100L));

System.out.println(MyMath2.divide(200.0, 100.0));


MyMath2 mm = new MyMath2();

mm.a = 200L;

mm.b = 100L;

// 인스턴스메서드는 객체생성 후에만 호출이 가능함.

System.out.println(mm.add());

System.out.println(mm.subtract());

System.out.println(mm.multiply());

System.out.println(mm.divide());

}

}

실행결과)

300

100

20000

2.0

30

100

20000

2.0




'Programing > Java' 카테고리의 다른 글

[Java] 컬렉션 프레임워크  (0) 2014.12.14
[Java] static  (0) 2014.12.14
[Java] 클래스멤버와 인스턴스멤버간의 참조와 호출  (0) 2014.12.14
[Java] 변수의 종류  (0) 2014.12.14
[Java] abstract  (0) 2014.12.11

[Java] 클래스멤버와 인스턴스멤버간의 참조와 호출


클래스멤버와 인스턴스멤버간의 참조와 호출


같은 클래스에 속한 멤버들 간에는 별도의 인스턴스를 생성하지 않고도 서로 참조 또는 호출이 가능하다. 단, 클래스멤버가 인스턴스멤버를 참조 또는 호출하고자 하는 경우에는 인스턴스를 생성해야 한다.

그 이유는 인스턴스멤버가 존재하는 시점에 클래스멤버는 항상 존재하지만, 클래스멤버가 존재하는 시점에 인스턴스멤버가 존재할 수도 있고 존재하지 않을 수도 있기 때문이다.


class TestClass{

void instanceMethod(){} //인스턴스 메서드

static void staticMethod(){} // static 메서드

void instanceMethod2(){ //인스턴스 메서드

instanceMethod();//다른 인스턴스메서드를 호출한다.

staticMethod();// static 메서드를 호출한다.

}

static void staticMethod2(){//static 메서드

instanceMethod();// 에러!! 인스턴스메서드를 호출할 수 없다. static 메서드는 같은 클래스 내의 인스턴스 메서드를 호출할 수 없다.

staticMethod();// static 메서드는 호출할 수 있다.

}

}// end of class;


같은 클래스 내의 메서드는 서로 객체의 생성이나 참조변수 없이 직접 호출이 가능하지만 static 메서드는 인스턴스 메서드를 호출할 수 없다.


class TestClass2{

int iv;// 인스턴스변수

static int cv;// 클래스 변수


void instanceMethod(){// 인스턴스메서드

System.out.println(iv);// 인스턴스 변수를 사용할 수 있다.

System.out.println(cv);// 클래스변수를 사용할 수 있다.

}


static void staticMethod(){// static 메서드

System.out.println(iv);// 에러! 인스턴스 변수를 사용할 수 없다.

System.out.println(cv);//  클래스 변수를 사용할 수 있다.

}

}// end of class

메서드간의 호출과 마찬가지로 인스턴스메서드는 인스턴스변수를 사용할 수 있지만, static 메서드는 인스턴스변수를 사용할 수 없다.


class MemberCall {

int iv = 10;

static int cv = 20;


int iv2 = cv;

        // static int cv2 = iv;// 에러. 클래스변수는 인스턴스 변수를 사용할 수 없음.

static int cv2 = new MemberCall().iv;// 이처럼 객체를 생성해야 사용가능.


static void staticMethod1() {

System.out.println(cv);

    // System.out.println(iv); // 에러. 클래스메서드에서 인스턴스변수를 사용불가.

MemberCall c = new MemberCall();

System.out.println(c.iv);  // 객체를 생성한 후에야 인스턴스변수의 참조가능.

}


void instanceMethod1() {

System.out.println(cv);

System.out.println(iv); // 인스턴스메서드에서는 인스턴스변수를 바로 사용가능.

}


static void staticMethod2() {

staticMethod1();

    // instanceMethod1(); // 에러. 클래스메서드에서는 인스턴스메서드를 호출할 수 없음.

MemberCall c = new MemberCall();

c.instanceMethod1(); // 인스턴스를 생성한 후에야 호출할 수 있음.

  }

void instanceMethod2() {// 인스턴스메서드에서는 인스턴스메서드와 클래스메서드

staticMethod1();//  모두 인스턴스 생성없이 바로 호출이 가능하다.

instanceMethod1();

}

}

클래스 멤버(클래스변수와 클래스메서드)는 언제나 참조 또는 호출이 가능하기 때문에 인스턴스멤버가 클래스멤버를 사용하는 것은 아무런 문제가 안된다.

클래스멤버간의 참조 또는 호출 역시 아무런 문제가 없다.

그러나, 인스턴스멤버(인스턴스변수와 인스턴스메서드)는 반드시 객체를 생성한 후에만 참조 또는 호출이 가능하기 때문에 클래스멤버가 인스턴스멤버를 참조, 호출하기 위해서는 객체를 생성하여야 한다.

하지만, 인스턴스멤버간의 호출에는 아무런 문제가 없다. 하나의 인스턴스멤버가 존재한다는 것은 인스턴스가 이미 생성되어 있다는 것을 의미하며, 즉 다른 인스턴스멤버들도 모두 존재하기 때문이다.


cf.) MemerCall c = new MemberCall();

      int result = c. instanceMethod(); 


      int result = new MemberCall().instanceMethod(); 

      --> 위의 두 줄을 다음과 같이 한 줄로 할 수 있다.



1) 인스턴스 메소드는 클래스 맴버에 접근 할 수 있다.


2) 클래스 메소드는 인스턴스 맴버에 접근 할 수 없다. 


인스턴스 변수는 인스턴스가 만들어지면서 생성되는데, 클래스 메소드는 인스턴스가 생성되기 전에 만들어지기 때문에 클래스 메소드가 인스턴스 맴버에 접근하는 것은 존재하지 않는 인스턴스 변수에 접근하는 것과 같다.

class C1{

    static int static_variable = 1;

    int instance_variable = 2;

    static void static_static(){

        System.out.println(static_variable);

    }

    static void static_instance(){

        // 클래스 메소드에서는 인스턴스 변수에 접근 할 수 없다. 

        //System.out.println(instance_variable);

    }

    void instance_static(){

        // 인스턴스 메소드에서는 클래스 변수에 접근 할 수 있다.

        System.out.println(static_variable);

    }

    void instance_instance(){        

        System.out.println(instance_variable);

    }

}

public class ClassMemberDemo {  

    public static void main(String[] args) {

        C1 c = new C1();

        // 인스턴스를 이용해서 정적 메소드에 접근 -> 성공

        // 인스턴스 메소드가 정적 변수에 접근 -> 성공

        c.static_static();

        // 인스턴스를 이용해서 정적 메소드에 접근 -> 성공

        // 정적 메소드가 인스턴스 변수에 접근 -> 실패

        c.static_instance();

        // 인스턴스를 이용해서 인스턴스 메소드에 접근 -> 성공

        // 인스턴스 메소드가 클래스 변수에 접근 -> 성공

        c.instance_static();

        // 인스턴스를 이용해서 인스턴스 메소드에 접근 -> 성공 

        // 인스턴스 메소드가 인스턴스 변수에 접근 -> 성공

        c.instance_instance();

        // 클래스를 이용해서 클래스 메소드에 접근 -> 성공

        // 클래스 메소드가 클래스 변수에 접근 -> 성공

        C1.static_static();

        // 클래스를 이용해서 클래스 메소드에 접근 -> 성공

        // 클래스 메소드가 인스턴스 변수에 접근 -> 실패

        C1.static_instance();

        // 클래스를 이용해서 인스턴스 메소드에 접근 -> 실패

        //C1.instance_static();

        // 클래스를 이용해서 인스턴스 메소드에 접근 -> 실패

        //C1.instance_instance();

    } 

}

실행결과)

1

1

2

1



'Programing > Java' 카테고리의 다른 글

[Java] static  (0) 2014.12.14
[Java] 클래스 메서드와 인스턴스 메서드  (0) 2014.12.14
[Java] 변수의 종류  (0) 2014.12.14
[Java] abstract  (0) 2014.12.11
[Java] 인터페이스의 이해  (0) 2014.12.06

[Java] 변수의 종류


변수의 종류


변수의 종류 

선언위치 

생성시기 

클래스 변수 

클래스 영역 

클래스가 메모리에 올라갈 때 

인스턴스 변수 

클래스 영역 

인스턴스가 생성되었을 때 

 지역 변수

클래스 영역 이외의 블럭(메서드, 생성자, 초기화 블럭 내부) 

변수 선언문이 수행되었을 때 


1. 인스턴스 변수

클래스 영역에 선언되며, 클래스의 인스턴스를 생성할 때 만들어진다. 그렇기 때문에 인스턴스 변수의 값을 읽어 오거나 저장하기 위해서는 먼저 인스턴스를 생성해야 한다.

인스턴스는 독립적인 저장공간을 가지므로 서로 다른 값을 가질 수 있다. 인스턴스마다 고유한 상태를 유지해야하는 속성의 경우, 인스턴스변수로 선언한다.


2. 클래스 변수

클래스 변수를 선언하는 방법은 인스턴스 변수 앞에 static을 붙이기만 하면 된다. 인스턴스마다 독립적인 저장공간을 갖는 인스턴스변수와는 달리, 클래스변수는 모든 인스턴스가 공통된 저장공간(변수)을 공유하게 된다. 그래서 클래스 변수는 모든 인스턴스가 공통된 저장공간(변수)을 공유하게 된다. 그래서 클래스 변수를 공유 변수라고도 한다. 한 클래스의 모든 인스턴스들이 공통적인 값을 유지해야하는 속성의 경우, 클래스변수로 선언해야 한다. 인스턴스 변수는 인스턴스를 생성한 후에야 사용가능하지만, 클래스 변수를 인스턴스를 생성하지 않고도 언제라고 바로 사용할 수 있다.

'클래스이름.클래스변수'와 같은 형식으로 사용한다.

클래스가 로딩될 때 생성되어  프로그램이 종료될 때 까지 유지되며, public을 앞에 붙이면 같은 프로그램 내에서 어디서나 접근할 수 있는 전역변수의 성격을 갖는다.


3. 지역변수

메서드 내에 선언되어 메서드 내에서만 사용 가능하며, 메서드가 종료되면 소멸되어 사용할 수 없게 된다.

for문 또는 while문의 블러 내에 선언된 지역변수는, 지역변수가 선언된 블럭{} 내에서만 사용 가능하며, 블럭{}을 벗어나면 소멸되어 사용할 수 없게 된다.


4. 예제

클래스 변수와 인스턴스 변수


class Card{

String kind;// 카드의 무늬(인스턴스 변수)

int number;// 카드의 숫자(인스턴스 변수)

static int width;// 카드의 폭(클래스 변수)

static int weight;// 카드의 높이(클래스 변수)

}


각 Card인스턴스의 자신만의 무늬와 숫자를 유지하고 있어야 하므로 이들을 인스턴스 변수로 선언

각 카드의 폭과 높이는 모든 인스턴스가 공통적으로 같은 값을 유지해야하므로 클래스 변수로 선언

카드의 폭을 변경할 필요가 있을 경우, 모든 카드의 width값을 변경하지 않고 한 카드의 width의 값만 변경해도 모든 카드의 width의 값이 변경되는 셈이다.

class CardTest{

public static void main(String argd[]){

System.out.println("Card.width="+Card.width);

System.out.println("Card.height="+Card.height);

// 클래스 변수는 객체 생성 없이 ‘클래스이름.클래스변수’로 직접 사용 가능하다.

Card c1 = new Card();

c1.kind = "Heart";

c1.number = 7;

Card.c2 = new Card();

c2.kind = "Spade";

c2.number = 4;// 인스턴스변수의 값을 변경한다.

System.out.println("c1은“+c1.kind+","+c1.number+"이며, 크기는 ("c1.width+","+c1.height+")");

System.out.println("c2은“+c2.kind+","+c2.number+"이며, 크기는  ("c2.width+","+c2.height+")");

System.out.println("c1의 width와 height를 각각 50,80으로 변경합니다.“);


c1.width = 50;

c1.height = 80;// 클래스 변수의 값을 변경한다.

System.out.println("c1은“+c1.kind+","+c1.number+"이며, 크기는 ("c1.width+","+c1.height+")");

System.out.println("c2은“+c2.kind+","+c2.number+"이며, 크기는  ("c2.width+","+c2.height+")");

}

}


class Card{

String kind;

int number;

static int width =100;

static int height =250;

}

실행결과)

Card.width=100

Card.height=250

c1은Heart,7이며, 크기는 (100,250)

c2은Spade,4이며, 크기는  (100,250)

c1의 width와 height를 각각 50,80으로 변경합니다.

c1은Heart,7이며, 크기는 (50,80)

c2은Spade,4이며, 크기는  (50,80)


Card 클래스의 클래스변수인 width, height를 공유하기 때문에, c1의 width와 height를 변경하면 c2의 width와 height값도 바뀐 것과 같은 결과를 얻는다.

Card.width, c1.width, c2.width는 모두 같은 저장 공간을 참조하므로 항상 같은 값을 갖게 된다.

--> 인스턴스 변수는 인스턴스가 생성될 때 마다 생성되므로 인스턴스마다 각기 다른 값을 유지할 수 있지만, 클래스 변수는 모든 인스턴스가 하나의 저장공간을 공유하므로, 항상 공통된 값을 갖는다.



[Spring] web.xml 기본 설정


web.xml 기본 설정


1. 특정 xml 환경 파일 바라보기

DispatherServlet은 클라이언트의 요청을 전달받는 서블릿으로서, 컨트롤러나 뷰와 같은 스프링 MVC의 구성 요소를 이용하여 클라이언트에게 서비스를 제공하게 된다.

DispatherServlet의 설정은 웹 어플리케이션의 /WEB-INF/web.xml 파일에 추가하며, 다음과 같이 서블릿과 매핑 정보를 추가하면 DispatherServlet 설정이 완료된다.


web.xml 파일은 J2EE 웹 어플리케이션의 기본이 되는 설정 파일이다. DispatcherServlet 클래스를 서블릿으로 정의하며 context root 아래에 확장자 .html 파일로 요청을 하면 모두

DispatcherServlet 클래스로 랩핑하도록 정의하고 있다. 이 정의에 의해 .html의 확장자가 붙은 파일로의 액세스는 모두 DispatcherServlet로 송신된다.

DispatcherServlet 클래스에는 <servlet-name> 태그에 의해 ‘shopping-1’ 이라고 서블릿 이름을 지정하여 정의하고 있다.

이 서블릿 이름 ‘shopping-1’ 에 ‘-servlet.XML’ 라는 문자열을 부가함으로써 컨테이너 상에 로드된 스프링 설정 파일명이 결정된다. 예제의 경우 shopping-1-servlet.xml ([서블릿이름]-servlet.xml) 파일이 로드되게 된다.

<servlet>

<servlet-name>shopping-1</servlet-name>

<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

<load-on-startup>1</load-on-startup>

</servlet>

<servlet-mapping>

<servlet-name>shopping-1</servlet-name>

<url-pattern>*.html</url-pattern>

</servlet-mapping>


2. ContextLoaderListener 로드 원리

DispatcherServlet 클래스를 정의하고 context root 이하로의 액세스에 대해서 DispatcherServlet을 맵핑하고 있다. 

다만, 앞 장의 예제와 달리 비즈니스 로직용의 스프링 설정 파일 (applicationContext.xml)을 작성했기 때문에 listener로 ContextLoaderListener 클래스를 정의하고 있다.

ContextLoaderListener 클래스는 스프링 설정 파일(디폴트에서 파일명 applicationContext.xml)을 로드하면 ServletContextListener 인터페이스를 구현하고 있기 때문에 ServletContext 인스턴스 생성 시(톰켓으로 어플리케이션이 로드된 때)에 호출된다. 즉, ContextLoaderListener 클래스는 DispatcherServlet 클래스의 로드보다 먼저 동작하여 비즈니스 로직층을 정의한 스프링 설정 파일을 로드한다.

<listener>

<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

</listener>


<servlet>

<servlet-name>shopping-1</servlet-name>

<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

<load-on-startup>1</load-on-startup>

</servlet>

<servlet-mapping>

<servlet-name>shopping-1</servlet-name>

<url-pattern>*.html</url-pattern>

</servlet-mapping>


※ 설정 파일의 로드 순서

여러 개의 설정 파일을 이용할 경우 설정 파일의 참조 관계가 걸리기 때문에 따일의 로드 순서가 중요하다. 

위의 예제에서 사용하는 스프링 MVC의 설정 파일(shopping-1-servlet.xml)에서도 applicationContext.xml 파일로 정의된 인스턴스를 참조하고 있다.

그 때문에 shopping-1-servlet.xml 파일이 로드되기 전에 applicationContext.xml 파일이 로드되어 있어야한다.


cp.) ContextLoaderListener를 이용한 설정

일반적으로 빈 설정 파일은 하나의 파일만 사용되기 보다는 persistance, service, web등 layer 단위로 나뉘게 된다.

또한, 같은 persistance, service layer의 빈을 2개 이상의 DispatcherServlet이 공통으로 사용할 경우도 있다.

이럴때는 공통빈(persistance, service)설정 정보는 ApplicationContext에, web layer의 빈들은 WebApplicationContext에 저장하는 아래와 같은 방법을 추천한다.

공통빈 설정 파일은 서블릿 리스너로 등록된 org.springframework.web.context.ContextLoaderListener로 로딩해서 ApplicationContext을 만들고, web layer의 빈설정 파일은 DispatcherServlet이 로딩해서 WebApplicationContext을 만든다.

<!-- ApplicationContext 빈 설정 파일-->

<context-param>

<param-name>contextConfigLocation</param-name>

<param-value>

              <!--빈 설정 파일들간에 구분은 줄바꿈(\n),컴마(,),세미콜론(;)등으로 한다.-->

                            /WEB-INF/config/easycompany-service.xml,/WEB-INF/config/easycompany-dao.xml 

            </param-value>

</context-param>

<!-- 웹 어플리케이션이 시작되는 시점에 ApplicationContext을 로딩하며, 로딩된 빈정보는 모든 WebApplicationContext들이 참조할 수 있다.-->

<listener>

<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

</listener>


<servlet>

<servlet-name>employee</servlet-name>

<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

<init-param>

<param-name>contextConfigLocation</param-name>

<param-value>/WEB-INF/config/easycompany-service.xml</param-value>

</init-param>

</servlet>

        

<servlet>

<servlet-name>webservice</servlet-name>

<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

<init-param>

<param-name>contextConfigLocation</param-name>

<param-value>/WEB-INF/config/easycompany-webservice.xml</param-value>

</init-param>

</servlet>

이 ApplicationContext의 빈 정보는 모든 WebApplicationContext들이 참조할 수 있다. 

예를 들어, DispatcherServlet은 2개 사용하지만 같은 Service, DAO를 사용하는 web.xml을 아래와 같이 작성했다면, 

easycompany-servlet.xml에 정의된 빈정보는 easycompany-webservice.xml가 참조할 수 없지만, 

easycompany-service.xml, easycompany-dao.xml에 설정된 빈 정보는 easycompany-servlet.xml, easycompany-webservice.xml 둘 다 참조한다. ApplicationContext과 WebApplicationContext과의 관계를 그림으로 나타내면 아래와 같다.



3. 설정 파일 순서 정하기

<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"

version="2.5">


<listener>

<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

</listener>


<servlet>

<servlet-name>shopping3-2</servlet-name>

<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

<load-on-startup>1</load-on-startup>

</servlet>


<servlet-mapping>

<servlet-name>shopping3-2</servlet-name>

<url-pattern>*.html</url-pattern>

</servlet-mapping>

</web-app>

web.xml은 DispatherServlet 클래스를 정의하고, 컨텍스트 루트 이하로 들어오는 접근에 대해 DispatherServlet을 맵핑한다. 그리고 리스너로 ContextLoaderLister 클래스를 정의한다.

설정 파일을 복수로 사용할 때에는, 설정 파일을 읽는 순서가 중요하다. 설정 파일 A에서 참조하는 bean을 다른 설정 파일 B에 정의하고 있는 경우, A보다 먼저 B를 읽지 않으면 오류가 일어난다.

샘플에서 사용하는 설정 파일은 shopping3-2-server.xml과 applicationContext.xml 2가지인데, shopping3-2-server.xml에서 applicationContext.xml에 정의된 bean을 이용한다.

이런 이유로 applicationContext.xml 다음에 shopping3-1-server.xml 순서로 설정 파일을 읽는다.


web.xml에서 사용하는 org.springframework.web.context.ContextLoaderListener 클래스는 javax.servlet.ServletContextListener 인터페이스를 구현하는 클래스이다.

ContextLoaderListener 클래스의 기본은 applicationContext.xml이지만 변경도 가능하다. 샘플에서는 비즈니스 로직 계층용으로 설정 파일 'applicationContext.xml'을 만들고, ContextLoaderListner 클래스를 이용해서 읽는다. ContextLoaderListner 클래스는 ServletContext 인스턴스 생성 시(Tomcat 이 애플리케이션을 읽을 때) 호출되는 것으로, DispatherServlet 클래스가 읽기 전 ContextLoaderListener 클래스가 비즈니스 로직 계층용 설정 파일 'applicationContext.xml'을 읽는다.


4. 한 개 이상의 설정파일 설정하기

경우에 따라 한 개 이상의 설정 파일을 사용해야 하는 경우가 있다. 또는 기본 설정 파일 이름이 아닌 다른 이름의 설정 파일을 사용하고 싶은 경우도 있을 것이다.

(빈 설정 파일을 하나 이상을 사용하거나, 파일 이름과 경로를 직접 지정해주고 싶다면 contextConfigLocation 라는 초기화 파라미터 값에 빈 설정 파일 경로를 설정해준다.)

이런 경우, 다음과 같이 DispatherServlet을 설정할 때 contextConfigLocation 초기화 파라미터에 설정 파일 목록을 지정하면 된다.

 <servlet>

        <servlet-name>dispather</servlet-name>

        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

<init-param>

<param-name>contextConfiguration</param-value>

<param-value>

/WEB-INF/main.xml

/WEB-INF/dds.xml

</param-value>

</init-param>

</servlet>

contextConfigLocation 초기화 파라미터는 설정 파일 목록을 값으로 갖는데, 이때 각 설정 파일은 콤마(","), 공백 문자(""), 탭(\t), 줄 바꿈(\n), 세미클론(":")을 이용하여 구분한다.

각 설정 파일의 경로는 웹 어플리케이션 루트 디렉터리를 기준으로 한다.


cp.) 또다른 방법

<context-param>

<param-name>contextConfigLocation</param-name>

<param-value>

/WEB-INF/security.xml 

/WEB-INF/applicationContext.xml

</param-value>

</context-param>


<servlet>

<servlet-name>shopping3-7</servlet-name>

<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

<load-on-startup>1</load-on-startup>

</servlet>


<servlet-mapping>

<servlet-name>shopping3-7</servlet-name>

<url-pattern>*.html</url-pattern>

</servlet-mapping>

설정 파일이 applicationContext.xml 뿐만 아니라 security.xml도 있다. 복수의 설정 파일을 스프링에 읽게 할 때는 'contextConfigLocation'이라는 파라미터 이름으로, 값에 설정 파일 경로를 지정한다.

복수의 파일을 스페이스로 구획한다.(콤마로 구획 가능)


5. 인코딩 필터 배치하기

<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"

version="2.5">

<filter>

<filter-name>CharacterEncodingFilter</filter-name>

<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>

<init-param>

<param-name>encoding</param-name>

<param-value>UTF-8</param-value>

</init-param>

<init-param>

<param-name>forceEncoding</param-name>

<param-value>true</param-value>

</init-param>

</filter>


<filter-mapping>

<filter-name>CharacterEncodingFilter</filter-name>

<url-pattern>/*</url-pattern>

</filter-mapping>


<listener>

<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

</listener>


<servlet>

<servlet-name>shopping3-3</servlet-name>

<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

<load-on-startup>1</load-on-startup>

</servlet>


<servlet-mapping>

<servlet-name>shopping3-3</servlet-name>

<url-pattern>*.html</url-pattern>

</servlet-mapping>

</web-app>


6. 전체적인 나열 보기

<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"

version="2.5">

<!-- 필터 설정 -->

<filter>

<filter-name>CharacterEncodingFilter</filter-name>

<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>

<init-param>

<param-name>encoding</param-name>

<param-value>UTF-8</param-value>

</init-param>

<init-param>

<param-name>forceEncoding</param-name>

<param-value>true</param-value>

</init-param>

</filter>

<filter-mapping>

<filter-name>CharacterEncodingFilter</filter-name>

<url-pattern>/*</url-pattern>

</filter-mapping>

<!-- 리스너 설정 -->

<listener>

<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

</listener>


<context-param>

<param-name>contextConfigLocation</param-name>

<param-value>/WEB-INF/security.xml /WEB-INF/applicationContext.xml</param-value>

</context-param>


<!-- DispathcherSerlvet 설정 -->

<servlet>

<servlet-name>shopping3-7</servlet-name>

<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

<load-on-startup>1</load-on-startup>

</servlet>

<servlet-mapping>

<servlet-name>shopping3-7</servlet-name>

<url-pattern>*.html</url-pattern>

</servlet-mapping>


<!-- <filter-mapping> 요소에서의 'springSecurityFilterChain'은 'org.springframework.web.filter.DelegatingFilterProxy'에 붙인 필터 이름이다. 

DelegatingFilterProxy 클래스는 스프링이 제공하는 필터이고, 실제 처리를 필터 이름에 일치하는 이름의 Bean에 위임한다. 

스프링 시큐리티는 'springSecurityFilterChain'이라는 이름으로 스프링에 Bean을 등록하기 때문에 이렇게 필터를 정의하면 스프링 시큐리티 기능이 유효해 진다. 

또한 애플리케이션의 모든 URL에 대해 이 필터 체인을 유효하게 하고, 스프링 시큐리티를 작동시킨다.-->

<filter>

<filter-name>springSecurityFilterChain</filter-name>

<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>

</filter>

<filter-mapping>

<filter-name>springSecurityFilterChain</filter-name>

<url-pattern>/*</url-pattern>

</filter-mapping>


<!-- 이중 로그인을 막기 위한 리스너 클래스이다.-->

<listener>

<listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>

</listener>


<!-- error 페이지 설정 -->

<error-page>

<error-code>403</error-code>

<location>/WEB-INF/jsp/noAuthority.jsp</location>

</error-page>

</web-app>



'Programing > Spring' 카테고리의 다른 글

[Spring] iBatis 2  (0) 2014.12.16
[Spring] iBatis 1  (0) 2014.12.16
[spring] 스프링 MVC 인터페이스 구현 클래스  (0) 2014.12.13
[Spring] 스프링 MVC 패턴 개요  (0) 2014.12.13
[Spring] AOP 용어 설명  (0) 2014.12.13