소개
이 실습에서는 Hive의 구성과 기본 작업을 다룹니다. SQL 기초 지식이 있는 학습자가 Hive의 아키텍처와 기본 배포 및 사용 방법을 이해하도록 구성했습니다.
문서에 나오는 샘플 코드는 가능한 한 직접 입력하고, 복사하여 붙여넣는 방식은 피하세요. 코드를 직접 입력해야 코드에 더 익숙해질 수 있습니다. 문제가 생기면 문서를 꼼꼼히 다시 확인하거나 포럼에서 도움을 요청하고 의견을 나누세요.
Hive 소개
이 단계에서는 Apache Hive가 Hadoop에 저장된 대규모 데이터셋을 SQL 인터페이스로 조회하고 분석하는 방법을 알아봅니다. 먼저 Hive의 아키텍처와 주요 기능을 살펴봅니다.
Hive 아키텍처
- 구성 요소: Hive는 테이블과 스토리지를 관리하는
HCatalog, MapReduce 작업이나 Pig 쿼리를 실행하고 REST API를 통해 Hive 메타데이터와 상호작용하는WebHCat으로 구성됩니다. - 기능:
- 데이터 웨어하우징 작업인 ETL, 데이터 분석, 보고서 작성에 SQL 쿼리를 사용할 수 있습니다.
- 데이터는 HDFS나 HBase와 같은 스토리지 시스템에 저장하고, 쿼리는 Tez, Spark, MapReduce와 같은 계산 엔진으로 실행합니다.
- 절차형 언어와 HPL-SQL을 지원하며, Hive LLAP, YARN, Slider를 활용한 하위 쿼리 조회도 지원합니다.
Hive의 기본 데이터 단위
- 데이터베이스: 이름 충돌을 방지하고 보안을 강화하는 네임스페이스입니다.
- 테이블: 같은 스키마를 사용하는 동종 데이터 단위입니다. 예를 들어
page_views테이블에는 타임스탬프, userid, page_url 등의 열이 있을 수 있습니다. - 파티션: 키를 기준으로 데이터를 나누어 효율적으로 조회할 수 있게 합니다.
- 버킷: 파티션으로 나눈 데이터를 해시 함수 값에 따라 버킷으로 분할하여 쿼리 효율성을 높입니다.
Hive 데이터 유형
- 원시 데이터 유형과 복합 데이터 유형을 지원합니다. 자세한 내용은 Hive 데이터 유형 문서를 참조하세요.
Hive는 온라인 트랜잭션 처리(OLTP)보다 데이터 웨어하우징 작업에 적합하도록 설계되었습니다. 확장성, 성능, 안정성, 내결함성에 중점을 두며, 다양한 입력 형식을 지원하고 여러 형식의 커넥터로 확장할 수 있습니다. 빅데이터 분석에서 Hive의 기능을 효과적으로 활용하려면 Hive의 아키텍처, 데이터 단위, 데이터 유형을 이해해야 합니다.
Hive 설치 및 배포
이 단계에서는 Hive의 디렉터리 구조, 환경 변수, 메타스토어, 시스템 매개변수를 포함해 Hive 설치와 배포 방법을 살펴봅니다.
먼저 이후 작업을 수행할 hadoop 사용자로 전환합니다. 데스크톱에서 Xfce 터미널을 두 번 클릭해 연 다음, 다음 명령을 입력하세요.
su - hadoop
참고: hadoop 사용자의 비밀번호는 hadoop입니다.
이제 wget 명령으로 Hive의 최신 안정 버전을 다운로드합니다. 이 실습에서는 Hive 2.3.3을 사용합니다.
sudo wget https://labexfile.oss-us-west-1.aliyuncs.com/courses/144/apache-hive-2.3.3-bin.tar.gz
다운로드한 설치 패키지의 압축을 풉니다. 터미널에 다음 명령을 입력하세요.
tar -zxvf apache-hive-2.3.3-bin.tar.gz
압축을 푼 디렉터리를 /opt 디렉터리로 이동합니다. /opt에 쓰기 권한이 없을 수 있으므로 sudo 권한을 사용하세요.
sudo mv /home/hadoop/apache-hive-2.3.3-bin /opt/hive-2.3.3
디렉터리를 이동한 다음에는 Hive 디렉터리의 소유자와 그룹을 hadoop으로 변경해야 합니다. 터미널에 다음 명령을 입력하세요.
sudo chown -R hadoop:hadoop /opt/hive-2.3.3
참고: 다른 터미널을 열어 위 작업을 sudo 권한으로 수행할 수도 있습니다.
labex:~/ $ ls -al /opt/
total 24
drwxrwxr-x 10 hadoop hadoop 4096 Mar 3 12:01 hive-2.3.3
...
환경 변수 설정
이 단계에서는 Hive 환경 변수를 설정해 HIVE_HOME이 Hive 설치 디렉터리를 가리키도록 합니다.
먼저 vim 편집기로 hadoop 사용자의 .bashrc 파일을 엽니다. 터미널에 다음 명령을 입력하세요.
vim /home/hadoop/.bashrc
파일 끝에 다음 내용을 추가합니다. PATH는 현재 실습 환경에 맞게 수정해야 합니다. $PATH는 기존 값을 참조합니다. PATH 환경 변수 끝에 :$HIVE_HOME/bin을 추가하세요.
export HIVE_HOME=/opt/hive-2.3.3
export PATH=$PATH:$HIVE_HOME/bin
편집을 마치면 파일을 저장하고 편집기를 종료합니다. 그런 다음 source 명령으로 환경 변수를 적용합니다.
이 실습 환경에는 Hive가 사전 설치되어 있으므로 환경 변수를 source로 적용할 필요가 없습니다. 실제로 설치할 때는 환경에 따라 이 단계를 수행하면 됩니다.
구성 설정
이 단계에서는 사전 설치된 Hive 구성을 확인하고 점검에 필요한 구성 파일을 만듭니다.
메타스토어 구성
Hive를 사용하기 전에 메타데이터 저장소를 설정해야 합니다. 기본적으로 Hive는 메타정보를 내장 Derby 데이터베이스에 저장합니다. 디스크의 저장 위치는 Hive 구성 파일 conf/hive-default.xml에 있는 javax.jdo.option.ConnectionURL 구성 항목으로 정합니다. 기본 위치는 ./metastore_db입니다.
하지만 이 실습에서는 메타데이터를 저장할 데이터베이스로 MySQL을 사용합니다. 따라서 Hive 구성 파일을 수정해야 합니다.
vim 편집기로 새 구성 파일을 만들고 엽니다.
vim /opt/hive-2.3.3/conf/hive-site.xml
<configuration>
<property>
<name>javax.jdo.option.ConnectionURL</name>
<value>jdbc:mysql://localhost:3306/hive_metastore1?createDatabaseIfNotExist=true</value>
</property>
<property>
<name>javax.jdo.option.ConnectionDriverName</name>
<value>com.mysql.cj.jdbc.Driver</value>
</property>
<property>
<name>javax.jdo.option.ConnectionUserName</name>
<value>hiveuser1</value>
</property>
<property>
<name>javax.jdo.option.ConnectionPassword</name>
<value>123qweQWE...</value>
</property>
</configuration>
위 구성 항목을 입력한 다음 파일을 저장하고 편집기를 종료합니다.
MySQL 데이터베이스 만들기
sudo mysql 명령으로 MySQL 서버를 시작하고 hive_metastore 데이터베이스를 만듭니다.
CREATE DATABASE hive_metastore1;
CREATE USER 'hiveuser1'@'localhost' IDENTIFIED BY '123qweQWE...';
GRANT ALL PRIVILEGES ON hive_metastore1.* TO 'hiveuser1'@'localhost';
FLUSH PRIVILEGES;
exit;
그런 다음 MySQL JDBC 드라이버를 다운로드해 /opt/hive-2.3.3/lib 디렉터리에 복사합니다.
cp /usr/local/hive/lib/mysql-connector-j-8.3.0.jar /opt/hive-2.3.3/lib
Hive 시스템 매개변수 설정
메타스토어를 구성한 다음에는 Hadoop의 위치와 내부 구성 파일 경로 등을 지정하도록 Hive 내부 구성 항목을 설정해야 합니다.
먼저 cp 명령으로 설정 템플릿을 복사해야 설정이 적용됩니다.
터미널에 다음 명령을 입력하세요.
cp /opt/hive-2.3.3/conf/hive-env.sh.template /opt/hive-2.3.3/conf/hive-env.sh
그런 다음 vim 편집기로 hive-env.sh 파일을 엽니다.
vim /opt/hive-2.3.3/conf/hive-env.sh
48번째 줄에서 HADOOP_HOME을 Hadoop 설치 경로로 설정합니다.
HADOOP_HOME=/home/hadoop/hadoop
위 구성 항목을 입력한 다음 파일을 저장하고 편집기를 종료합니다.
메타스토어 초기화
이 단계에서는 Hive 메타스토어를 초기화하고 Hive에 필요한 Hadoop 서비스를 시작합니다.
이후 사용할 데이터는 HDFS에 저장하므로 미리 HDFS를 시작해야 합니다. 터미널에 다음 명령을 입력해 HDFS를 시작하세요.
start-dfs.sh
start-yarn.sh
jps 명령을 입력해 서비스 상태를 확인합니다.
hadoop:~/ $ jps [17:31:43]
8960 Jps
3153 NodeManager
2823 SecondaryNameNode
3017 ResourceManager
2570 DataNode
2428 NameNode
Hadoop을 처음 사용하는 경우 Hadoop 데이터를 삭제하고 hdfs namenode -format으로 초기화해야 합니다.
이 실습에서는 사전 설치된 Hive를 u per 데이터베이스로 초기화했습니다. 직접 다운로드한 Hive를 초기화하려면 다음 명령을 사용하세요.
/opt/hive-2.3.3/bin/schematool -initSchema -dbType mysql
Initialization script completed
schemaTool completed
방금 다운로드한 Hive를 처음 초기화하는 경우에는 ~/.bashrc에서 HIVE_HOME 경로를 새로 설치한 Hive의 경로로 변경한 뒤 source를 실행해야 합니다. 하지만 이후 Hive 테스트는 모두 사전 설치된 Hive를 기준으로 하므로 그렇게 하지 않는 편이 좋습니다.
초기화가 완료되었다는 메시지가 표시되면 hive 명령으로 Hive 명령줄 인터페이스에 들어갈 수 있습니다. 터미널에 다음 명령을 입력하세요.
hive
참고: 사전 설치된 Hive 셸에서 hive를 입력하면 방금 설정한 Hive가 아니라 사전 설치된 Hive가 실행됩니다. 방금 설정한 Hive 셸에 들어가려면 절대 경로를 입력하세요.
Hive Session ID = 3eee2693-175d-4452-82d2-47f1b639d9d5
Logging initialized using configuration in jar:file:/usr/local/hive/lib/hive-common-3.1.3.jar!/hive-log4j2.properties Async: true
Hive-on-MR is deprecated in Hive 2 and may not be available in the future versions. Consider using a different execution engine (i.e. spark, tez) or using Hive 1.X releases.
Hive Session ID = 68d0d345-92c3-4eb1-8393-7ee34f56363f
hive>
Hive 기본 작업
이 단계에서는 Hive 테이블의 기본 작업을 알아봅니다. 모든 Hive 문은 Language Manual에서도 확인할 수 있습니다.
테이블 만들기
먼저 데이터를 준비해야 합니다. 이 실습에서 사용할 데이터는 NginX 서버 로그를 흉내 낸 로그 파일로, 웹사이트 페이지 조회 수를 분석하는 데 사용할 수 있습니다.
터미널을 다시 열고 다음 명령을 입력해 샘플 데이터를 다운로드합니다.
su hadoop
cd ~
wget http://labfile.oss-cn-hangzhou.aliyuncs.com/courses/567/log_example.csv
그런 다음 파일을 HDFS에 업로드합니다.
hdfs dfs -mkdir -p /user/data/staging/page_view
hdfs dfs -put /home/hadoop/log_example.csv /user/data/staging/page_view
데이터를 준비한 다음에는 테이블을 만들어야 합니다. 위 데이터로 page_view 테이블을 만들려면 Hive 명령줄에 다음 문을 입력하세요.
CREATE TABLE page_view(viewTime INT, userid BIGINT,
Page_url STRING, referrer_url STRING,
Ip STRING COMMENT 'IP Address of the User')
COMMENT 'This is the page view table'
PARTITIONED BY(dt STRING, country STRING)
STORED AS SEQUENCEFILE;
이 예제에서는 테이블의 각 열에 적절한 데이터 유형을 지정합니다. 열과 테이블 수준에 주석을 추가할 수도 있습니다. PARTITIONED BY 절은 데이터 열과 별도로 파티션 열을 정의합니다. 파티션 열은 데이터 열과 함께 저장되지 않습니다. 이런 방식으로 파티션 열을 지정하면 각 행은 줄바꿈 문자를 구분자로 사용합니다.
데이터 형식이 위와 다르면 다음 예제처럼 필드 구분자를 매개변수로 지정할 수 있습니다.
다음 문은 예시일 뿐이므로 hive 셸에 입력하지 마세요.
CREATE TABLE page_view1(viewTime INT, userid BIGINT,
Page_url STRING, referrer_url STRING,
Ip STRING COMMENT 'IP Address of the User')
COMMENT 'This is the page view table'
PARTITIONED BY(dt STRING, country STRING)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '1'
STORED AS SEQUENCEFILE;
줄 구분자는 Hive가 아니라 Hadoop 시스템의 구분자 설정으로 정해지므로 직접 변경할 수 없습니다.
일반적으로 열 데이터가 정해진 테이블은 버킷에 저장합니다. 이렇게 하면 데이터셋 쿼리에서 데이터를 효율적으로 샘플링할 수 있습니다. 버킷이 없으면 테이블을 무작위로 샘플링할 수는 있어도 전체 데이터를 스캔하는 동안 효율적으로 샘플링할 수 없습니다. 다음 예제에서는 userid 열을 기준으로 page_view 테이블의 버킷 저장을 설정합니다.
다음 문은 예시일 뿐이므로 hive 셸에 입력하지 마세요.
CREATE TABLE page_view2(viewTime INT, userid BIGINT,
Page_url STRING, referrer_url STRING,
Ip STRING COMMENT 'IP Address of the User')
COMMENT 'This is the page view table'
PARTITIONED BY(dt STRING, country STRING)
CLUSTERED BY(userid) SORTED BY(viewTime) INTO 32 BUCKETS
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '1'
COLLECTION ITEMS TERMINATED BY '2'
MAP KEYS TERMINATED BY '3'
STORED AS SEQUENCEFILE;
위 예제에서는 해시 함수를 사용해 테이블의 userid 열을 32개 버킷으로 나눕니다. 각 버킷 안의 데이터는 viewTime을 기준으로 오름차순 정렬합니다. 이 방식으로 데이터를 구성하면 집계 대상 열(여기서는 userid 열)을 효과적으로 샘플링할 수 있습니다. 정렬 기능을 활용하면 데이터 관리자가 더 나은 데이터 구조를 통해 쿼리를 효율적으로 평가할 수 있습니다.
다음 문은 예시일 뿐이므로 hive 셸에 입력하지 마세요.
CREATE TABLE page_view3(viewTime INT, userid BIGINT,
Page_url STRING, referrer_url STRING,
Friends ARRAY<BIGINT>, properties MAP<STRING, STRING>,
Ip STRING COMMENT 'IP Address of the User')
COMMENT 'This is the page view table'
PARTITIONED BY(dt STRING, country STRING)
CLUSTERED BY(userid) SORTED BY(viewTime) INTO 32 BUCKETS
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '1'
COLLECTION ITEMS TERMINATED BY '2'
MAP KEYS TERMINATED BY '3'
STORED AS SEQUENCEFILE;
위 예제는 다른 프로그래밍 언어의 유형 정의와 유사하게 필드 이름과 유형으로 테이블의 각 행 형식을 정의합니다. 테이블 이름과 열 이름은 대소문자를 구분하지 않습니다. IP Address of the User와 아래의 COMMENT 문은 열과 테이블 수준에 모두 주석을 추가할 수 있음을 보여 줍니다. 또한 PARTITIONED BY 절은 데이터 열과 별도로 파티션 열을 정의합니다. 앞서 설명했듯이 파티션 열에는 실제 데이터가 저장되지 않습니다. CLUSTERED BY 절은 버킷을 나눌 열과 버킷 수를 지정합니다. ROW FORMAT DELIMITED 절은 Hive 테이블에서 행을 저장하는 방식을 지정합니다. 구분자를 사용하는 형식에서는 필드의 종료 기호, 컬렉션(배열 또는 맵) 항목의 종료 기호, 맵 키의 종료 기호를 지정합니다. 숫자는 ASCII 인코딩 값을 나타냅니다. STORED AS SEQUENCEFILE은 데이터가 HDFS에 바이너리 형식, 구체적으로는 Hadoop의 SequenceFile 형식으로 저장됨을 뜻합니다. ROW FORMAT과 STORED AS 절의 설정은 현재 Hive의 기본값입니다. 따라서 처음 테이블을 만들 때는 이 설정을 문에 명시하지 않았습니다.
테이블과 파티션 조회
웨어하우스에 있는 테이블 목록을 보려면 다음 문을 사용합니다.
SHOW TABLES;
테이블이 많으면 위 문에서 많은 결과가 출력됩니다. 접두사를 지정해 검색 범위를 좁힐 수 있습니다. 예를 들어 접두사가 page인 테이블을 조회하려면 다음 문을 사용합니다.
SHOW TABLES 'page.*';
이 문은 정규식과 같은 규칙으로 이름을 찾으며, 마침표(.)는 와일드카드로 사용됩니다.
테이블의 파티션 목록을 보려면 다음 문을 사용합니다. 파티션 테이블이 아니면 아무 정보도 반환되지 않습니다.
SHOW PARTITIONS page_view;
테이블의 열과 각 열의 데이터 유형을 보려면 DESCRIBE 문을 사용합니다.
DESCRIBE page_view;
열과 기타 모든 속성을 확인하려면 EXTENDED 키워드를 추가합니다. 이 명령은 많은 정보를 출력하며, 일반적으로 디버깅에 사용합니다.
DESCRIBE EXTENDED page_view;
테이블 수정
기존 테이블의 이름을 바꾸려면 RENAME TO를 사용하는 ALTER TABLE 문을 실행합니다. 변경할 이름의 테이블이 이미 있으면 오류가 반환됩니다. 다음 문은 예시일 뿐입니다. 다음 단계에서 앞서 만든 page_view 테이블을 사용하므로 실행하지 마세요.
ALTER TABLE page_view RENAME TO new_page_view;
결과를 확인합니다.
hive> ALTER TABLE page_view RENAME TO new_page_view;
OK
Time taken: 0.124 seconds
hive> show tables;
OK
new_page_view
...
Time taken: 0.021 seconds, Fetched: 4 row(s)
기존 테이블의 열 이름도 바꿀 수 있습니다. 이때 각 기존 열에 레코드를 포함해야 하며, 열의 데이터 유형은 그대로 유지해야 합니다.
ALTER TABLE new_page_view REPLACE COLUMNS (viewTime INT, userid BIGINT, page_url STRING, referrer_url STRING, ipaddr STRING COMMENT 'IP Address of the User');
기존 테이블에 새 열을 추가할 수도 있습니다.
ALTER TABLE new_page_view ADD COLUMNS (new_column INT COMMENT 'a new int column');
열 추가와 같은 스키마 변경을 적용해도 테이블의 기존 파티션 스키마는 유지됩니다. 이 동작은 테이블이 파티션 테이블이 되는 것을 방지합니다. 기존 파티션에서 해당 열에 액세스하는 쿼리는 그 열에 대해 암시적으로 null 또는 지정된 기본값을 반환합니다.
테이블과 파티션 삭제
Hive는 데이터 웨어하우스로 설계되었으므로 데이터를 삭제하는 작업은 항상 신중해야 합니다. 따라서 테이블 삭제 작업은 다소 번거롭습니다. 테이블에 DROP 명령을 사용하면 해당 테이블에 생성된 인덱스도 암시적으로 삭제됩니다.
다음 예제는 테이블을 삭제합니다. 이 실습에서는 실행하지 마세요.
DROP TABLE new_page_view;
데이터 로드 및 쿼리
이 단계에서는 준비된 데이터를 파티션이 있는 Hive 테이블에 로드하고 결과를 조회합니다.
데이터 로드
Hive 테이블에 데이터를 로드하는 방법은 여러 가지입니다. HDFS의 특정 위치를 가리키는 외부 테이블을 만들 수 있습니다. 이 방식에서는 HDFS의 put 또는 copy 명령으로 데이터 파일을 지정된 위치에 복사한 다음, 해당 위치를 가리키는 테이블을 만듭니다. 이 테이블에는 관련된 행 형식 정보가 모두 포함됩니다.
테이블을 만든 후에는 데이터를 변환해 다른 Hive 테이블에 삽입할 수 있습니다. 앞서 log_example.csv 파일을 HDFS에 업로드하고 이름을 page_view로 변경했습니다. 이 데이터를 해당 파티션의 page_view 테이블에 로드하려면 다음과 같이 합니다.
먼저 외부 테이블을 만들고 지정된 파일과 연결합니다.
CREATE EXTERNAL TABLE page_view_stg(viewTime INT, userid BIGINT,
Page_url STRING, referrer_url STRING,
Ip STRING COMMENT 'IP Address of the User',
Country STRING COMMENT 'country of origin')
COMMENT 'This is the staging page view table'
ROW FORMAT DELIMITED FIELDS TERMINATED BY '44' LINES TERMINATED BY '\n' STORED AS TEXTFILE
LOCATION '/user/data/staging/page_view';
그런 다음 데이터를 최종 저장할 page_view 테이블을 만듭니다.
CREATE TABLE page_view(viewTime INT, userid BIGINT,
Page_url STRING, referrer_url STRING,
Ip STRING COMMENT 'IP Address of the User')
COMMENT 'This is the page view table'
PARTITIONED BY(dt STRING, country STRING)
STORED AS SEQUENCEFILE;
이 작은 연습용 데이터셋은 분산 YARN 작업이 필요하지 않습니다. 실습 VM에서 삽입 작업이 안정적으로 실행되도록 이 Hive 세션에서 Hadoop의 로컬 작업 실행기를 설정합니다.
SET mapreduce.framework.name=local;
마지막으로 외부 테이블의 데이터를 page_view 테이블에 삽입합니다.
FROM page_view_stg pvs
INSERT OVERWRITE TABLE page_view
PARTITION(dt='2015-05-17', country='us')
SELECT pvs.viewTime, pvs.userid, pvs.page_url, pvs.referrer_url, pvs.ip
WHERE pvs.country = 'us';
로컬 작업은 몇 초 안에 완료되며, 조건에 맞는 행을 page_view 파티션에 로드합니다.
...
Loading data to table default.page_view partition (dt=2015-05-17, country=us)
MapReduce Jobs Launched:
Stage-Stage-1: HDFS Read: ... HDFS Write: ... SUCCESS
Total MapReduce CPU Time Spent: 0 msec
OK
Time taken: ... seconds
간단한 쿼리
데이터를 삽입한 후 간단한 쿼리를 실행할 수 있습니다. 일반적인 SQL 문과 비슷합니다. Hive 명령줄 인터페이스에 다음 문을 입력하세요.
SELECT * FROM page_view WHERE userid = 0 LIMIT 10;
결과에는 쿼리로 찾은 레코드가 표시됩니다.
hive> select * from page_view;
OK
NULL 490 /downloads/product_1 /archives/webroot 217.168.17.5 2015-05-17 us
NULL 490 /downloads/product_2 /archives/webroot 217.168.17.5 2015-05-17 us
NULL 3316 /downloads/product_2 /archives/webroot 217.168.17.5 2015-05-17 us
...
서비스 종료
이 단계에서는 추가 학습 자료를 확인하고 Hive를 종료한 다음 Hadoop 서비스를 중지합니다.
사용을 마치면 quit 명령으로 Hive 명령줄 인터페이스를 종료합니다.
quit;
HDFS 서비스도 종료해야 합니다. 터미널에 다음 명령을 입력하세요.
stop-yarn.sh
stop-dfs.sh
요약
이번 실습에서는 Hive의 아키텍처와 설치 및 배포, 기본 HQL 문을 살펴봤습니다. 샘플 데이터를 사용해 데이터를 가져오는 방법도 알아봤습니다.
주요 학습 내용은 다음과 같습니다.
- Hive 아키텍처
- Hive의 기본 데이터 단위
- Hive 배포 방법
- Hive HQL 언어
Hive는 데이터 웨어하우스 소프트웨어이므로 더 많은 기능을 살펴볼 필요가 있습니다. 기술 자료를 꾸준히 찾아보는 습관을 들이고, 다음 과정도 계속 학습하세요.



