SQLP 19회차 실습 1번 문제 

 

  문제를 참고한 블로그 주소 

   - 꿈을 펼처라 님의 블로그    


  simverse.tistory.com/85 




1) 내가 생각하는 답 


--- query 


select /*+ leading(b a) use_nl(a) index(a 고객_pk)  index(b 계약_x02) */ a.고객번호, a.고객이름, a.주민번호, b.계약일, b.계약번호, b.서비스시작일, b.서비스완료일 

  from 고객 a , 계약 b 

  where a.고객번호 = b.고객번호 

   and b.계약일 between :C and :D 

   and not exists ( select /*+ index(a 고객_x01) */ 'x' 

                     from 고객 c

                    where (a.고객이름, a.주민번호) in (:A, :B)

                   )

  union all 

select /*+ ordered use_nl(b) index(b 계약_x01) */ a.고객번호, a.고객이름, a.주민번호, b.계약일, b.계약번호, b.서비스시작일 b.서비스완료일 

  from 고객 a , 계약 b 

  where a.고객번호 = b.고객번호 

   and b.계약일 between :C and :D 

   and exists ( select /*+ index(a 고객_x01) */ 'x' 

                  from 고객 c 

                 where (a.고객이름, a.주민번호) in (:A, :B)

                   and a.rowid = c.rowid

              )

;


-- index 


고객_pk = 고객번호 

고객_x01 = 고객이름 + 주민번호 --- 가능하면 이 컬럼들에 not null 제약이 있으면 더 좋음 


계약_pk = 고객번호 + 계약번호 

계약_x01 = 고객번호 + 계약일 

계약_x02 = 계약일  



2) 이에 대한 나의 해설 


 - 이 문제를 풀어보면서 가장 고민 했던 것은 where 절에 exists 서브쿼리 작성이었다. 

   위의 쿼리를 보면 exists 일때는 where 절에 a.rowid = b.rowid라고 조건을 명시한다. 

   그러나 not exists 일때는 a.rowid = b.rowid를 넣어주지 않았다. 

   여기서 exists 일때 해당 조건을 넣어 준것은 서브쿼리 캐싱 기능을 사용하기 위해서이다. 

   조건이 exists 인 경우 해당 조건을 인덱스를 통해서 부분 범위 처리를 하게 되면 

   random access 양을 크게 줄일 수 있다. 

   그러나 이때 a.rowid = b.rowid 조건을 사용하지 않으면 서브쿼리 캐싱 기능을 사용할 수 없어서 

   인덱스가 조인 후 fiter 조건으로만 사용 될 것이다. 

   그런 비효율을 줄이기 위해서 a.rowid = b.rowid 조건을 넣어준다. 


   그런데 이때 not exists 에서는 a.rowid = b.rowid 를 넣어준 것은 비효율에 문제와 함께 잘못 된 값을 출력하기 때문이다. 

   not exists 시에는 고객이름과 주민번호가 모두 테이블에 존재할 시  'no rows selected' 가 되어야 하는데 

   a.rowid = b.rowid 조건을 추가하게 되면 조인 시 결과 값이 출력 될 것이다. 

   ( 이 부분은 설명이 끝나고 간단한 예시로 증명해볼 수 있다. )  

   그리하여 not exists 부분에는 a.rowid = b.rowid 조건을 빼버렸다. 

   그리고 추가로 고객 테이블의 where 절에 조건을 저런식을 넣어준 것은 인덱스를 사용할 수 있게 함이다.


   그리고 union all 을 기준으로 exists 부분 쿼리와 not exists 부분 쿼리에 힌트가 달라졌는데 이는 driving table 순서가 바뀜을 의미한다. 

   exists 의 경우 이름과 주민번호의 조건으로 소수의 값만 출력 될 것으로 생각 된다. 

   그렇게 되면 nl 조인 시 계약 테이블에 조인 되는 양을 줄일 수 있다. 

   그러나 해당 조건이 없다면 고객 테이블에서는 부분 범위 처리를 할 수 있는 값이 없다. 

   그래서 해당 순서와 같이 한다면 100만 건을 조인 시도 할 것이다. 

   그래서 not exists 부분에서는 조인 순서를 바꿔주었다. 

   7일 이기 때문에 최대 7000 건이다. 100만 건 보다는 훨씬 줄일 수 있을 것으로 생각하였다. 


   또한 index 부분을 보면 '고객번호 + 계약일' 인덱스가 있음에도 불구하고 따로 '계약일' 인덱스를 따로 생성하여 사용하였다. 

   이 쿼리에서는 계약일을 between 범위 처리를 해야 한다. 

   해당 쿼리에 내용상 in 스타일로 변경할 수 없다. 

   그렇다고 skip scan을 써도 제대로 효과가 없을 것으로 생각했는데 그 이유는 고객번호의 변별력이 좋기 때문에디 고객번호는 pk 이기 때문이다. 

   고객번호는 변별력이 매우 큰 컬럼임이다. 

   이처럼 선두 컬럼이 변별력이 클때는 skip scan은 크 효과를 거둘 수 없다. 

   그래서 계약일 인덱스를 생성하였다. 고객이름과 주민번호가 입력되지 않을 시에는 범위 처리가 가능하게 하는 컬럼은 계약일 밖에 없기 때문이다. 

   그래서 인덱스가 2개 생성됨에도 불구하고 자주 사용되는 쿼리라면 '계약일' 인덱스가 필요하다고 생각하였다. 



3) not exists 에 대한 내용에 대한 참고 쿼리 

    - 해당 emp 테이블은 오라클 샘플 emp table 이다.   



select * 

 from emp a 

 where not exists ( select 'x' 

                      from emp b

                     where ename = 'KING'

                      and a.rowid = b.rowid 

                   )

 ;


--- 14 rows 를 출력 


select * 

 from emp a 

 where not exists ( select 'x' 

                      from emp b

                     where ename = 'KING' 

                   )

 ;

  

--- no rows selected 

   

 * 하지만 이부분에서 a.rowid = b.rowid 조건을 넣었을 때 14 rows 가 출력되는 이유는 잘 모르겠다. 

   ename = 'KING'  값이 있는 ' no rows selected '가 되어야 한다고 생각하는데 말이다.   




  







3) IOT (index organizaion table) 테이블 구성
   - table random 엑세스가 발생하지 않도록 테이블을 아예 인덱스 구조로 생성한 object 

    ⑴ IOT 의 장점 
       - 인위적으로 클러스터링 팩터를 좋게 만드는 방법중에 하나여서 Logical read를 줄일 수 있다. 
       - 기존에는 인덱스 + 테이블이였다면, lOT는 인덱스만 만들면 되므로, 저장 공간이 절약된다. 

    ⑵ IOT의 단점 
        - 데이터 입력할 때, 성능이 느리다. 
   
    ⑶ IOT 생성 및 데스트 
        ① create table emp_iot
           (empno, ename, job, mgr, hiredate, sal, comm, deptno, constraint pk_emp_iot primary key(empno) )
           organization index
            as
                 select * from emp  ;

              * IOT 생성시 pirmary key 가 없으면 테이블이 생성되지 않는다. 
              * 기존 테이블에 primary key가 걸려 있어도 의미가 없다. 새로 생기는 table에 primary key를 만들어 주어야 한다.
                  (ctas 사용시 not null 제약을 제외한 나머지 제약은 따라오지 않는다.) 

        ② select * from emp_iot ;
             -- consistent gets 5 
             -- index fast full scan
 
     ⑷ IOT 저장 영역 
         ① PK 속성 영역 : 자주 조회 조건에서 검색되는 컬럼들이 저장된 저장 영역

         ② 오버 플로우 영역 : 값은 필요해서 저장해두지만 출력이나 조회조건으로 거의 사용되지 않는 컬럼들을 저정하는 저장 영역
         
         * 영역을 나누어서 사용하는 이유는 검색 속도를 좀 더 높이기 위해서 
         * IOT는 table이 아니라 index로 저장 된다. 검색시에는 object_type 이나 segment_type을 index로 하여야 찾을 수 있다. 
         * 그러나 drop 이나 truncate 명령어를 사용할 때에는 table 옵션을 사용한다. 
 

  4) 클러스터 테이블 

     ⑴ 클러스터 테이블이란 
        - 클러스터 키 값이 같은 레코드가 한 블럭에 모이도록 저장하는 구조의 테이블을 말한다. 
        - 클러스터 테이블을 이용하면, 여러테이블의 레코드를 심지어 하나의 물리적 공간에 같이 저장할 수 있다 
        - 여러 테이블들을 서로 조인된 상태로 저장해 두는 것이다. 
       
         * 단점은 DML 속도가 상당히 느려진다. 

     ⑵ 클러스터 테이블의 종류 2가지 
         ① 인덱스 클러스터 테이블 
               - 단일 테이블 인덱스 클러스터 
               - 다중 테이블 인덱스 클러스터

         ② 해쉬 클러스터 테이블 

     ⑶ 단일 클러스터 
         - 클러스터 인덱스는 컬럼 값을 하나만 저장하기 때문에 인덱스의 크기가 크지 않아 검색속도가 높아진다.
         - 클러스터 테이블은 클러스터 키값에 따라서 최대한 같은 블럭안에 같은 키값들을 저장히기 때문에 검색속도가 높아진다. 

       
     ⑷ 단일 클러스터 테이블 생성 테스트 
     
            ① 클러스터를 생성한다. 
     
                 CREATE CLUSTER objs_cluster# (object_type VARCHAR2(20) ) INDEX ;
                                                  ㅣ                           ㅣ
                                           클러스터 이름             클러스터 key 값
                                             
            ② 클러스터 인덱스를 생성한다.
               CREATE INDEX objs_cluster_idx ON CLUSTER objs_cluster# ;
                                               ㅣ                                   ㅣ 
                                       클러스터 인덱스 이름            클러스터 이름

            ③ 클러스터 테이블을 생성한다.
                CREATE TABLE objs_cluster
                  CLUSTER objs_cluster# (object_type)
                            AS SELECT *
                                  FROM all_objects
                                ORDER BY DBMS_RANDOM.VALUE;

               * dbms_random.value를 사용해서 정렬해서 데이터를 저장하면 
                   실제로 논리적으로는 object_type 별로 저장되어있지 않고 데이터가 무분별하게 흩어져서 저장되어져있는 것처럼 보이겠지만
                   실제로 물리적으로는 object_type 별로 같은 블럭안에 데이터가 저장된다.                    

                * 생성시에는 cluster, table 모두 create 하지만 truncate 시에는 cluste만 truncate 하면 자동으로 table의 내용이 삭제 된다. 
                   drop시에는 table, cluster 모두 삭제 해야 한다.

                * 클러스터 테이블을 생성하기 위해서는 클러스터 생성시 클러스터 키값은 기존 테이블의 컬럼의 데이터 타입과 길이의 범위가 같아야 한다. 
                   그렇지 않을 시 클러스터 테이블 생성시 error가 발생한다. 
 
            ④ 일반 테이블과 클러스터 테이블의 성능을 비교한다. 
 
               CREATE TABLE objs_regular
                      AS SELECT *
                             FROM objs_cluster
                          ORDER BY DBMS_RANDOM.VALUE;

               CREATE INDEX objs_regular_idx ON objs_regular(object_type) ;

               ALTER TABLE objs_regular MODIFY object_name NULL;
               ALTER TABLE objs_cluster MODIFY object_name NULL;
  
       결과 )  -- consistent gets , physical read  순 

                   SELECT *
                     FROM objs_regular
                   WHERE object_type='TABLE';
                     -- 429
                     -- 235
                     -- full table access

                    SELECT /*+ index(objs_regular objs_regular_idx) */*
                     FROM objs_regular
                   WHERE object_type='TABLE';
                      -- 549
                      -- 235 
                      -- index range scan 

                  SELECT *
                    FROM objs_cluster
                 WHERE object_type ='TABLE';
                     -- 226
                     -- 199
                     -- index unique scan 
                     -- table access cluster 

                 
    5) 다중 클러스터 테이블 
         - 여러 테이블의 레코드를 물리적으로 같은 블럭안에 저장되게 하는 테이블을 말한다. 
         - 즉, 여러테이블들을 서로 조인된 상태로 저장해두는 것을 말한다.      
        

      ⑴ 다중 클러스터 테이블 구현 
          ① 클러스터를 생성한다. 
              create cluster c_deptno# on ( deptno number(20) ) index  ;
           
          ② 클러스터 인덱스를 생성한다. 
             create index i_deptno# on cluster c_deptno# ;

          ③ 클러스터 테이블을 생성한다. 
              create table emp_cluster
                cluster c_deptno#(deptno)   -----------------------> 같은 클러스터 사용 
                 as select * 
                       from emp ;

           
             create table dept_cluster
                cluster c_deptno#(deptno)   -----------------------> 같은 클러스터 사용 
                 as select * 
                       from dept ;

              * 두개의 테이블이 같은 블럭안에 존재한다는 것을 확인해보시오 ! 

                  break on deptno skip 1 ;

                  select d.deptno, e.empno, e.ename,
                            dbms_rowid.rowid_block_number(d.rowid) deptno_block_no,
                            dbms_rowid.rowid_block_number(e.rowid) emp_block_no     
                   from dept_cluster d , emp_cluster e
                   where e.deptno = d.deptno
                   order by d.deptno ;
    

          ④ 일반 테이블과 클러스터 테이블과 성능을 비교한다. 

             select e.ename, d.loc, e.sal, e.hiredate, d.dname, e.deptno, e.mgr
                 from emp e, dept d
                where e.deptno = d.deptno and d.loc ='DALLAS';
               
                  * 순서대로 1회 실행과 2회 실행 시 

                  -- 11  -> 7
                  -- 4   -> 0
                  -- table acess full
                  -- hash join

            select e.ename, d.loc, e.sal, e.hiredate, d.dname, e.deptno, e.mgr
                 from emp_cluster e, dept_cluster d
                where e.deptno = d.deptno and d.loc ='DALLAS';

                -- 26 -> 10
                -- 6  -> 0
                -- table access cluster
                -- table acess full
                -- nested loop


문제 34. 사원 번호가 7788 번인 사원의 모든 컬럼의 데이터를 가져오는 쿼리의 성능을 emp 와 emp_iot 로 비교하시오 

 SELECT /*+ full(emp) */ *
 from emp
 WHERE empno=7788 ;
 -- consistent gets 4 
 -- full table scan 


 select *
 from emp_iot
 WHERE empno=7788 ;
 -- consistent gets 1 
 -- index unique scan 


문제 35. rn 이라는 컬럼이 있는 sales500 테이블을 rn 컬럼을 primary key 로 해서 iot를 생성하는데  이름을 sales500_iot로 구현하시오 ! 

1) rn 컬럼 추가 

CREATE TABLE SALES500
  AS SELECT ROWNUM rn, b.*
       FROM SALES b ;

2) IOT 구성 

CREATE TABLE sale500_iot
  (RN ,PROD_ID, CUST_ID, TIME_ID, CHANNEL_ID, PROMO_ID, QUANTITY_SOLD, AMOUNT_SOLD,
   CONSTRAINT sale500_iot_pk PRIMARY KEY(rn) )
   organization index
            as
             select * from sales500  ;


문제 36. 아래의 쿼리의 검색 속도를 비교하시오! 

 select *
 from sales500
where rn = 384756 ;
-- consistent gets 5076
-- full table scan 

select *
 from sale500_iot
where rn = 384756 ;
-- consistent gets 3
-- index unique scan 

* rn 컬럼에 primary key가 걸려 있기 때문에 자동으로 unique index가 생성되었다. 


문제 37. sales500 table + sales500_rn index 와 sales500_iot table 의 크기 차이를 비교하시오 ! 

SELECT segment_name, segment_type, bytes/1024/1024 MB
 FROM user_segments
 WHERE segment_name LIKE 'SALE%' ;

- sales500 (table) + sales500_rn_pk(index) = 41 + 16 = 57
- sales500_iot_pk = 39 


문제 38. SALES500 테이블과 SALES500_IOT 테이블에 insert 할 때의 속도 차이를 비교해보시오! 
- 행의 갯수 918843

첫 실험 
- sales500  : 7 초
- sales500_IOT :31 초

* 사람들 마다 차이가 있어서 여러번 실험해보아야 한다. 
* 여러번 실험할 수록 차이는 줄어 든다. 그 이유는 insert 시에 high water mark를 높여 놨기 때문이다.
* 제대로 실험해보기 위해서는 해당 table을 drop 한 후 다시 생성하고 buffer cache를 지우고 실험해보아야 한다.  


문제 39. sales500  테이블을 클러스터 테이블로 구성하는데 클러스터 키컬럼을 amount_sold로 해서 생성하시오 ! 

 CREATE CLUSTER sales500_cluster# (amount_sold VARCHAR2(22) ) INDEX ;

 CREATE INDEX sales500_cluster_idx ON CLUSTER sales500_cluster# ;

  CREATE TABLE sales500_cluster
     CLUSTER sales500_cluster# (amount_sold)
           AS SELECT *
                FROM sales500
              ORDER BY DBMS_RANDOM.VALUE;

  CREATE TABLE sales500_regular
          AS SELECT *
        FROM sales500_cluster
       ORDER BY DBMS_RANDOM.VALUE;

  CREATE INDEX sales500_regular_idx ON sales500_regular(amount_sold) ;


문제 40. sales500 테이블과 sales500_cluster 테이블과 성능을 비교하시오 

* 각각 실험 마다 flush buffer_cache 실행 
* 1차, 2차 실행 

select /*+ full(sales500_regular) */*
 from sales500_regular
where amount_sold=136.64 ;
-- 5079 -> 5079
-- 5070 -> 5069


select /*+ index(sales500 sales500_regular_idx) */ *
 from sales500_regular
where amount_sold=136.64 ;
-- 94 -> 90
-- 88 -> 0

select *
 from sales500_cluster
 where amount_sold=136.64 ;
-- 9   -> 9
-- 3   -> 0


문제 41. 아래의 조인문장의 성능이 좋아지도록 SALES 테이블과 PRODUCTS 테이블을 다중 클러스터 테이블과 구성하시오 ! 

create table sales                                    create table products
 as                                                             as 
     select * from sh.sales ;                           select * from sh.products ;

select  /*+ leading(p s) use_nl(p) */ p.prod_name, sum(s.amount_sold) 
   from sales s, products p 
 where s.prod_id = p.prod_id 
    and p.prod_id = 40
  group by p.prod_name ;

-- 4597  ->  4441
-- 4576   -> 4433
-- table acess full 
-- table acess full 
-- hash join 

 SELECT p.prod_name, sum(s.amount_sold)
   from sales_cluster s, product_cluster p
 where s.prod_id = p.prod_id
    and p.prod_id = 40
  group by p.prod_name ;
  -- 1238 -> 244
  -- 619  -> 0
  -- TABLE ACCESS CLUSTER -> INDEX UNIQUE SCAN
  -- TABLE ACCESS CLUSTER -> INDEX UNIQUE SCAN
  -- NESTED LOOPS


* 점심시간 문제 :

  create table emp_iot2
  (empno, ename, job, mgr, hiredate, sal, comm, deptno,
   CONSTRAINT emp_iot2_pk PRIMARY KEY (empno,ename) )
    organization INDEX
    INCLUDING sal
    OVERFLOW tablespace ts01
     as
    select * from emp  ;

-- 조회를 * 로 함 
-- where 조건 including 밖

   SELECT *
     FROM emp_iot
     WHERE deptno = 10;
   -- 5

   SELECT *
    FROM emp_iot2
     WHERE deptno = 10 ;
   -- 7 

-- 조회를 * 로 함 
-- where 조건 including 안

    SELECT *
     FROM emp_iot
     WHERE job = 'SALESMAN';
   -- 5

   SELECT *
    FROM emp_iot2
     WHERE job = 'SALESMAN';
    -- 7

-- 조회를 * 로 함 
-- where 조건 PK 컬럼
 
    SELECT *
     FROM emp_iot
     WHERE empno = 7788;
     -- 1

   SELECT *
     FROM emp_iot2
     WHERE empno = 7788;
     -- 3

* 아래부터는 할 때마다 alter system flush buffer_cache ; (buffer cache 내용 지우기) 를 실행함 
* 그래서 consistent get, physical read 둘다 기록 

-- where 조건 pk 컬럼
-- including 전 컬럼까지 조회

    SELECT empno, ename, job
     FROM emp_iot
     WHERE empno = 7788;
     -- 1
     -- 8

    SELECT empno, ename, job
     FROM emp_iot2
     WHERE empno = 7788 ;
    -- 7
    -- 6

-- where 조건 pk 컬럼 x
-- including 전 컬럼까지 조회

   SELECT empno, ename, job
     FROM emp_iot
     WHERE deptno = 10;
   -- 10
   -- 6

   SELECT empno, ename, job
    FROM emp_iot2
     WHERE deptno = 10 ;
   -- 13
   -- 14

-- where 조건 pk 컬럼
-- including 이외의 컬럼까지 조회

   SELECT empno, ename, deptno
     FROM emp_iot
     WHERE empno = 7788;
   -- 1
   -- 8 

   SELECT empno, ename, deptno
    FROM emp_iot2
     WHERE empno = 7788 ;
   -- 3
   -- 16   


-- where 조건 pk 컬럼 x
-- including 이외의 컬럼까지 조회

   SELECT empno, ename, deptno
     FROM emp_iot
     WHERE deptno = 10;
   -- 10
   -- 6

   SELECT empno, ename, deptno
    FROM emp_iot2
     WHERE deptno = 10 ;
   -- 13
   -- 14


* 오늘의 마지막 문제 

SALES500_IOT 와 SALES_CLUSTER 와 SALES 테이블 3개의 DML 속도를 비교하시오 !

* 모두 truncate 로 아무 것도 없던 상태에서 작업 
* db도 테스트하기 전에 startup force 실행 
* 각 테스트가 끝나면 flush buffer_cache 후 다시 테스트 진행함 

INSERT INTO SALEs500
  SELECT *
  FROM sales500_backup ;
-- 5초 13

INSERT INTO sales500_iot
  SELECT *
  FROM sales500_backup ;
-- 26초 71

INSERT INTO sales500_cluster
  SELECT *
  FROM sales500_backup ;
-- 45초 59 

* sales500 에도 sales500_iot와 동일한 조건을 주기 위해서 rn 컬럼에 pk 제약을 건 후 다시 테스트 
* 이번에는 좀 더 정확한 결과를 위해서 각 테스트 마다 startup 을 새로함 

INSERT INTO SALEs500
  SELECT *
  FROM sales500_backup ;
-- 5초 07

INSERT INTO sales500_iot
  SELECT *
  FROM sales500_backup ;
-- 22초 71

INSERT INTO sales500_cluster
  SELECT *
  FROM sales500_backup ;
-- 40초 49 

- 조금 씩 위의 결과보다 차이는 나지만 큰 차이는 발생하지 않았다. 


나는 용기 있는 사람이다.

 나이가 먹도록 자신이 하고 싶은 일을 위해서 공부만 하고 있는 것이 미안했고 죄스러웠었다. 그 공부는 또한 석사 박사가 되기 위한 공부는 아니었다. 물론 석사 박사가 되기 위한 공부를 하고 싶었다. 자신도 있었고 교수님들에게 공부를 좀 더 해보기를 권하기도 했으며, 무엇보다도 내가 책을 읽고 생각하고 그러한 내용들 다른 사람들과 이야기 하는 것을 너무 좋아했다. 특히 책을 읽고 생각하고 글을 쓰는 것을 너무 좋아해서 그럴 수만 있다면 매일 그러고 싶었다. 그러나 집안 형편상 그럴 수는 없었다. 그때 이후부터였다. 내가 무언가 배우는 것을 열망하기 시작했던 것은 닥치는 대로 책을 읽었으며, 흥미가 있는 것들을 배우는 것에는 빠지기 시작했다. 아마도 그것이 정신적인 나의 위안이었던 것 같다. 그렇게 무언가 배우고 지식을 쌓은 일이 너무나도 재미있었고 그러한 지식과 지혜들이 사회에 어느 정도 적용될 수 있는지 알고 싶었고 그러한 지혜로 세상을 좀 더 좋은 곳으로 바꾸고 싶었다.

 그러나 돈을 버는 것은 중요했다. 하지만 그렇다고 해서 하기 싫은 일을 억지로 하면서 돈을 버는 것은 너무 힘들었다. 돈은 벌 수 있으나 그 시간에 나는 행복하지 못했고 시간이 아깝게 느껴졌다. 그래서 나는 계속해서 공부할 수 있으며, 끊임없이 생각할 수 있고, 세상에 영향력을 줄 수 있으며, 마지막으로 중요한 그러면서 경제 활동이 되는 것이 무엇인지 찾아보았다.

 그래서 찾은 것이 분석에 관련한 업무였다. 나는 꿈에 부풀었었다. 분석은 잘할 수 있을 것 같았다. 어느 정도 지식이 쌓여서 펼쳐지는 남들과는 다양한 생각들을 검증해 볼 수 있으며 계속해서 공부해야만 했고 내가 만들어낸 정보는 사람들에게 중요한 영향을 줄 수도 있다고 생각했다. 그래서 나는 인문학적인 소양을 향상하려고 했다. 기술이란 결국 손에 익으면 된다고 생각했다. 그보다 더 중요한 것은 분석을 왜 해야 하는지 분석을 해야 하는 포인트가 어디인지 그리고 그러한 생각들이 논리적으로 이루어져 있는지에 대한 인문학적인 소양이라고 생각했기 때문이다. 그러나 나는 잘 못 알고 있었다. 기술적인 측면은 취업시장에서 매우 중요한 부분이었다. 나는 그 부분을 간과하고 있었다.

 인문학적인 소양을 키웠으니 기술적인 측면을 키우면 되었지만 그럴만한 시간이 없다고 생각했다. 내가 가지고 있는 것은 측정이 어려운 만큼 다른 사람들이 가지고 있는 측정 가능한 영역에서 비교되는 내가 보였으며, 분석을 하고 싶다는 열망은 가지지 못한 것에 대한 자괴감으로 인하여 더욱 큰 고통이 되었다. 그리고 가장 원했던 것이 아닌 자꾸 차선을 선택하고자 하였다. 그러나 역시나 내 자존심은 내 생각보다 강했다. 이를 자존심이라고 해야 할지 열망이라고 해야 할지 잘 모르겠다. 그러나 그 무엇이 다시금 나를 도전하게 되었다. 남들보다 불리한 상황으로 시작하게 되었다.

  먼저 나이가 많았다. 다른 시작하는 사람들에 비해 나이가 많았다. 그래서 자는 시간을 줄였다. 남들이 자는 시간, 먹는 시간, 노는 시간에 그만큼 공부를 하였다. 남들이 뭐라고 하여도 나는 그게 좋았다. 물론 나도 쉬는 시간은 있었지만, 같이 배우며 배운 내용에 관해서 토론하고 이야기하는 시간은 그보다 더 좋았다. 또 다른 나의 불리한 상황은 가난이었다. 집안은 나를 지원해줄 수 있는 형편은 아니었고 내가 모아둔 돈도 그리 많지 않았다. 그래서 지출을 줄였다. 옷이나 신발 그리고 그 밖에 기본적인 지출을 최소한으로 두었다. 그리고 책은 도서관을 이용했다. 인터넷을 뒤졌다. 사람들에게 도움을 구했다.

 그렇게 노력했고 지금도 노력하고 있다. 그래서 나는 용기 있는 사람이다. 나는 강한 사람은 아니다. 강한 사람이었다면 어려운 현실에 우울함을 느낄 일도 없고, 포기하고자 하는 생각에 번민이 쌓이지도 않을 것이다. 그러나 나는 노력하는 동안에도 흔들렸고 힘이 들었다. 그래서 나는 강한 사람은 아니었지만, 용기는 있는 사람이었다. 용기가 없었다면 이처럼 조금은 늦은 나이에 다시금 도전이라는 시도를 하지 않았을 것이다. 그것도 불리한 것밖에 없는 상황에서 말이다. 나는 아직도 갈 길이 멀다. 어쩌면 이러한 어려움이 조금 더 지속될지 모르겠다. 그래도 나는 나를 앞으로도 패배자라고 생각하지 않겠다. 나는 용기 있는 사람이다. 패배자와 용기 있는 사람의 차이는 이처럼 종이 한 장의 차이로 구별된다

 

  

''나'의 이야기' 카테고리의 다른 글

나의 이야기 (1)  (0) 2019.12.24
글을 쓴다  (1) 2019.12.15
[신념 1.] 확식이 없다면 가장 기본을 먼저 취하라  (0) 2014.11.10
스스로를 믿자  (0) 2014.10.29
뭐가 '잘 될 거야!'냐..  (0) 2014.10.26

+ Recent posts