Mybatis 기반 프로필 입문 필수 편

Mybatis 프로필 입문
이 글 에서 부터 우 리 는 핵심 프로필 에 착안 하여 Mybatis 가 지원 하 는 핵심 프로필 에 대해 간단 하고 상세 하 게 설명 할 것 입 니 다.
아래 코드 는 my batis 를 사용 하기 전에 설정 초기 화 과정 입 니 다.
우 리 는 그 소스 코드 를 읽 어서 내부 실현 원 리 를 점차적으로 이해한다.

// Mybatis   SqlSessionFactory  SqlSession,       SqlSession        
 private static SqlSessionFactory getSessionFactory() {
  SqlSessionFactory sessionFactory = null;
  String resource = "configuration.xml";
  try {
   sessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsReader(resource));
  } catch (IOException e) {
   e.printStackTrace();
  }
  return sessionFactory;
 }
Sql Session Factory Builder 클래스 에 들 어 갑 니 다.
원본 코드 보기:

/**
 *    Copyright 2009-2016 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.session; 
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.Properties;
 
import org.apache.ibatis.builder.xml.XMLConfigBuilder;
import org.apache.ibatis.exceptions.ExceptionFactory;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.session.defaults.DefaultSqlSessionFactory; 
/**
 * Builds {@link SqlSession} instances.
 *
 * @author Clinton Begin
 */
public class SqlSessionFactoryBuilder {
 
  public SqlSessionFactory build(Reader reader) {
    return build(reader, null, null);
  }
 
  public SqlSessionFactory build(Reader reader, String environment) {
    return build(reader, environment, null);
  }
 
  public SqlSessionFactory build(Reader reader, Properties properties) {
    return build(reader, null, properties);
  }
 
  public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
    try {
      XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        reader.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }
 
  public SqlSessionFactory build(InputStream inputStream) {
    return build(inputStream, null, null);
  }
 
  public SqlSessionFactory build(InputStream inputStream, String environment) {
    return build(inputStream, environment, null);
  }
 
  public SqlSessionFactory build(InputStream inputStream, Properties properties) {
    return build(inputStream, null, properties);
  }
 
  public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        inputStream.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }
    
  public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }
 
}
이 클래스 에 서 는 다양한 구조 SqlSession Factory 를 지원 합 니 다.mybatis 설정 파일 만 전송 할 수 있 고 mybatis 설정 파일 의요소 탭 대신 properties 설정 파일 을 동시에 전송 할 수 있 으 며 환경 파라미터 envirmont 파라미터 도 지원 합 니 다.
우 리 는 소스 코드 를 따라 계속 아래 를 내 려 다 보 았 다.

 public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
    try {
      XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        reader.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }
XML configBuilder 클래스 인 스 턴 스 를 만 들 었 습 니 다.그 를 통 해 my batis 프로필(xml 프로필)을 분석 합 니 다.
분 석 된 코드 입 구 는 다음 과 같 습 니 다.

public Configuration parse() {
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }
 
  private void parseConfiguration(XNode root) {
    try {
      Properties settings = settingsAsPropertiess(root.evalNode("settings"));
      //issue #117 read properties first
      propertiesElement(root.evalNode("properties"));
      loadCustomVfs(settings);
      typeAliasesElement(root.evalNode("typeAliases"));
      pluginElement(root.evalNode("plugins"));
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
      settingsElement(settings);
      // read it after objectFactory and objectWrapperFactory issue #631
      environmentsElement(root.evalNode("environments"));
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      typeHandlerElement(root.evalNode("typeHandlers"));
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }
여기 서 보 듯 이 설정 파일 은 configuration 을 루트 노드 로 하고 루트 노드 아래 에 여러 개의 키 노드 가 있 습 니 다.각각 settings,properties,type:Aliases,plugins,object Factory,object Wrapper Factory,environment,databaseIdProvider,type:Handlers,mappers 입 니 다.
MyBatis 핵심 프로필 태그 안내
XML 맵 설정 파일MyBatis설정 파일 은 MyBatis 의 행동 에 영향 을 주 는 설정(settings)과 속성(properties)정 보 를 포함 합 니 다.문서 의 맨 위 결 과 는 다음 과 같 습 니 다.
설정
속성
설정
형식 이름
타 입 프로세서
대상 공장
플러그 인
환경
환경 변수
사무 관리자
데이터 원본
데이터베이스 제조 업 체 표지

properties
속성 은 모두 외부 설정 이 가능 하고 동적 으로 바 꿀 수 있 습 니 다.전형 적 인 자바 속성 파일 에서 설정 할 수 있 을 뿐만 아니 라 properties 요소 의 하위 요 소 를 통 해 전달 할 수 있 습 니 다.
예 를 들 면:

<!-- 
  mybatis       
   1.        (   )
  -->
  <properties resource="jdbc.properties"></properties>
이 속성 은 전체 프로필 에서 동적 설정 이 필요 한 속성 값 을 바 꿀 수 있 습 니 다.
예 를 들 면:

  <!--       -->
  <dataSource type="POOLED">
       <property name="driver" value="${driverClass}"/>
       <property name="url" value="${url}"/>
       <property name="username" value="${userid}"/>
       <property name="password" value="${password}"/>
   </dataSource>
configuration속성:데이터베이스 연결 파 라 메 터 를 jdbc.properties 에 따로 설정 하고 my batis.xml 파일 에 jdbc.properties 의 속성 값 을 불 러 오기 만 하면 됩 니 다.my batis.xml 에 서 는 데이터베이스 연결 매개 변수 하 드 인 코딩 이 필요 하지 않 습 니 다.(하 드 인 코딩 은 가 변 변 변 수 를 고정 값 으로 대체 하 는 방법 을 말 합 니 다)properties 요소 에서 정 의 된 속성 을 먼저 읽 습 니 다.그리고 properties 요소 에서 resource 나 url 로 딩 속성 을 읽 습 니 다.읽 은 동명 의 속성 을 덮어 씁 니 다.
메모:properties 탭 에서 정 의 된 속성 이${}에 인용 되면\#{}에 사용 되 지 않 습 니 다.그러면 parameter Type 의 매개 변수 값 을 읽 지 않 습 니 다.예 를 들 어 properties 에서 id 속성 을 정의 합 니 다.값 은 40 입 니 다.맵 파일 에서 이 값 을 참조 합 니 다.${id}그러면 저 는 parameterType 에서 값 을 전달 할 때 기본 형식 이 든 인용 형식 이 든 들 어가 도 이${id}값 을 덮어 쓰 지 않 습 니 다.항상 40 을 읽 습 니 다.
속성 도 SqlSession Builder.build()방법 에 전 달 될 수 있 습 니 다.
예 를 들 면:
소스 코드:

 public SqlSessionFactory build(Reader reader, Properties properties) {
    return build(reader, null, properties);
  }
 
  public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
    try {
      XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        reader.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }
typeAliases
형식 별명 은 자바 형식 에 짧 은 이름 을 설정 하 는 것 입 니 다.이것 은 XML 설정 과 만 관련 이 있 으 며,존재 의 미 는 클래스 가 완전히 한 정 된 이름 의 번 거 로 움 을 줄 이 는 데 만 있 습 니 다.
예 를 들 면:

 <!--        -->
  <typeAliases>
   <typeAlias type="cn.et.lesson02.annotion.Food" alias="food"/>
   <typeAlias alias="Author" type="domain.blog.Author"/>
    <typeAlias alias="Blog" type="domain.blog.Blog"/> 
  </typeAliases>
이렇게 설정 할 때 블 로 그 는 domain.blog.blog.blog 를 사용 하 는 모든 곳 에 사용 할 수 있 습 니 다.
가방 이름 을 지정 할 수도 있 습 니 다.MyBatis 는 가방 이름 아래 에 필요 한 자바 빈 을 검색 합 니 다.
예 를 들 면:

<typeAliases> <package name="domain.blog"/> </typeAliases>
모든 패키지 domain.blog 의 자바 빈 은 주석 이 없 는 상태 에서 빈 의 이니셜 소문 자의 비 한정 클래스 이름 을 별명 으로 사용 합 니 다.예 를 들 어 domain.blog.Author 의 별명 은 author 이다.주해 가 있 으 면,별명 은 주해 값 이다.
아래 의 예 를 보십시오.

@Alias("author") public class Author { ... }
properties태그(맵 설정):맵 파일 불 러 오기

<mappers>
   <!-- 
      resource        
                   
                 :
   -->
   <mapper resource="cn/et/lesson01/FoodMapper.xml"/>
   <!-- 
                  
        <mapper url="cn.domarvel.dao.UserMapper"/>
           :      mapper     mapper        ,         。         :    mapper    
    -->
    <!-- 
                   Mapper      
        <package name="cn.domarvel.dao"/>
                       :      mapper     mapper        ,         。         :    mapper    
     -->
  </mappers>
Mapper XML 파일
MyBatis 의 진정한 강 함 은 그의 매 핑 문구 이자 마력 이다.매우 강하 기 때문에 매 핑 기의 XML 파일 은 상대 적 으로 간단 하 다.같은 기능 을 가 진 JDBC 코드 와 비교 하면 95%에 가 까 운 코드 를 절약 한 것 을 즉시 발견 할 수 있 을 것 이다.MyBatis 는 SQL 을 대상 으로 구축 되 었 고 일반적인 방법 보다 더 잘 만 들 었 습 니 다.
4.567914.태그:파일 의 루트 노드 를 매 핑 하고 루트 노드 에서 9 개의 요 소 를 지원 합 니 다.
namespace 는 Dao 인 터 페 이 스 를 연결 하 는 데 사 용 됩 니 다.namespace 인 터 페 이 스 를 연결 하면 인터페이스 구현 클래스 를 쓰 지 않 아 도 됩 니 다.my batis 는 이 바 인 딩 을 통 해 실행 할 SQL 문 구 를 자동 으로 찾 을 수 있 습 니 다.
메모:인터페이스 에 있 는 방법 은 파일 에 있 는 SQL 문장의 ID 와 일일이 대응 합 니 다.

<mapper namespace="a">
</mapper>
<mapper namespace="cn.et.lesson02.xml.FoodMapper" >
</mapper>
SQL 맵 파일 에는 몇 개의 최상 위 요소 가 있 습 니 다.(정의 되 어야 할 순서에 따라)setting네 임 스페이스 의 캐 시 설정
4.567914.네 임 스페이스 캐 시 설정 의 참조.
4.567914.가장 복잡 하고 가장 강력 한 요소 로 데이터 베이스 결과 에서 대상 을 집중 적 으로 불 러 오 는 방법 을 설명 합 니 다.
4.567914.폐기 되 었 고 구식 스타일 의 매개 변수 맵.내 연 매개 변 수 는 첫 번 째 선택 입 니 다.이 요 소 는 나중에 제거 되 고 기록 되 지 않 습 니 다.
4.567914.다른 문장 에 인 용 될 수 있 는 재사 용 가능 한 문장 블록.
맵 삽입 문
맵 업데이트 문
맵 업데이트 문
맵 조회 문
select
검색 어 는 MyBatis 에서 가장 자주 사용 하 는 요소 중 하나 로 데이터 베 이 스 를 데이터베이스 에 저장 할 수 있 는 가치 가 크 지 않 습 니 다.다시 꺼 낼 수 있어 야 유용 합 니 다.대부분의 응용 도 수정 보다 조회 가 빈번 합 니 다.모든 삽입,업데이트 또는 삭제 작업 에 대해 서 는 보통 여러 개의 조회 작업 에 대응 합 니 다.마 이 바 티 스 의 기본 원칙 중 하나 이자 조회 와 결과 에 초점 과 노력 을 두 는 이유 다.간단하게 조회 하 는 select 요 소 는 매우 간단 합 니 다.
예 를 들 면

<select id="selectPerson" parameterType="int" resultType="hashmap"> SELECT * FROM PERSON WHERE ID = #{id} </select>
이 문 구 는 selectPerson 이 라 고 불 리 며 int(또는 Integer)형식의 인 자 를 받 고 HashMap 형식의 대상 을 되 돌려 줍 니 다.그 중의 키 는 열 이름 이 고 값 은 결과 줄 의 대응 값 입 니 다.
매개 변수 기호 주의:\#{id}
select 요 소 는 모든 문장의 역할 디 테 일 을 결정 하기 위해 설정 할 수 있 는 속성 이 많 습 니 다.

SELECT 다 중 조건 조회
parameterType 은 매개 변 수 를 전달 하 는 데 사용 되 는 다 중 매개 변 수 는 전송 대상 과 map 방식 으로 여러 개의 매개 변 수 를 전달 할 수 있 습 니 다.
\#{}전달 하 는 매개 변수 값 이 jdbc 와 유사 하 다 는 것 을 표시 합 니까?${}매개 변수 값 을'%값%'와 직접 바 꾸 는 것 을 표시 합 니 다.
예 를 들 면:

<select id=“selectPerson” parameterType=“map”    resultType=“person”> 
 SELECT * FROM PERSON WHERE ID = #{id} and name like ‘%${name}%' 
</select>
맵 에 id 와 name 키 쌍 이 있어 야 합 니 다.
SELECT 호출 저장 프로시저
저장 프로시저 만 들 기 prgadd(p1 in number,p2 in number,p3 out number)
Mybatis 맵 파일 에서 select 호출 저장 프로시저 사용

<select id=“prgAdd" statementType="CALLABLE">  <![CDATA[  
{call pro_hello (
 #{p1,mode=IN,jdbcType=NUMBER},
 #{p2,mode=IN,jdbcType=NUMBER},
 #{result,mode=OUT,jdbcType=NUMBER})}  
   ]]>  
 </select>  
테스트 호출 과정

Map<String, String> param = new HashMap<String, String>();  
param.put(“p1”, 1);  param.put(“p2”, 2);  
String returnValue = (String) session.selectOne(" prgAdd ", param);  
System.out.println("result=" + param.get("result"));  
System.out.println("returnValue=" + returnValue);  
insert,update,delete
데이터 변경 문 insert,update,delete 의 실현 은 매우 가깝다.

<insert id="insertAuthor" parameterType="domain.blog.Author" flushCache="true" statementType="PREPARED" keyProperty="" keyColumn="" useGeneratedKeys="" timeout="20">
<update id="updateAuthor" parameterType="domain.blog.Author" flushCache="true" statementType="PREPARED" timeout="20"> 
<delete id="deleteAuthor" parameterType="domain.blog.Author" flushCache="true" statementType="PREPARED" timeout="20">
insert,update,delete 인자

4.567914.입 참 의 전 한정 유형 명 또는 유형 별명
4.567914.데이터 시트 가 자동 으로 생 성 되 는 메 인 키 이름 을 설정 합 니 다.특정 데이터베이스(예:PostgreSQL)에 대해 자동 으로 생 성 되 는 홈 키 가 첫 번 째 필드 가 아니라면 설정 해 야 합 니 다.typeAliases기본 값 unset,getGenerated Keys 방법 이나 selectKey 서브 요소 의 반환 값 을 영역 모델 의 속성 에 할당 하 는 데 사용 합 니 다.typeHandlers:수치 범위 true|false(기본 값)설정 은 JDBC 의 getGenereatedKeys 방법 으로 홈 키 를 가 져 오고 key Property 설정 의 영역 모델 속성 에 할당 할 지 여부 입 니 다.
MySQL 과 SQLServer 는 auto-generated key field 를 실행 하기 때문에 데이터 베 이 스 를 자체 성장 키 로 설정 한 후 JDBC 의 getGenerated Keys 방법 으로 얻 을 수 있 습 니 다.하지만 Oralce 등 auto-generated key field 를 지원 하지 않 는 데이터 베 이 스 는 이런 방법 으로 메 인 키 를 가 져 올 수 없습니다.objectFactory:수치 범위 true(기본 값)|false,이 동작 을 실행 하면 2 급 캐 시 와 로 컬 캐 시 를 비 울 지 설정 합 니 다.plugins:기본 값 은 unset(jdbc 드라이브 에 의존 하 는 설정)입 니 다.이 동작 을 수행 하 는 최대 기한 을 설정 하고 시간 을 초과 하면 이상 을 던 집 니 다.environments:수치 범위 Oacle|my sql 등 은 데이터베이스 공장 을 나타 내 고 요소 내 부 는'if test='databaseId='oracle'>'특정 데이터베이스 에 다른 sql 문 구 를 지정 합 니 다.
selectKey
자동 생 성 형식의 데이터 베 이 스 를 지원 하지 않 거나 메 인 키 JDBC 드라이브 를 자동 으로 생 성 하 는 것 을 지원 하지 않 을 수도 있 습 니 다.MyBatis 는 메 인 키 를 생 성 하 는 다른 방법 이 있 습 니 다.
여기에 간단 한 예시 가 있 는데 이것 은 무 작위 ID 를 생 성 할 수 있다.(이렇게 하지 않 는 것 이 가장 좋 지만 여기 서 MyBatis 가 문 제 를 처리 하 는 유연성 과 관심 의 폭 을 보 여 준다.)

<insert id="saveFood" >
  
	<!-- 
	 selectKey  Mybatis      Insert               ,                。
	 		   SelectKey    order  , Mysql               ,
	 		   order     after        。 Oracle        ,     before,     。
	 	keyProperty:selectKey               。
	 	order:     BEFORE   AFTER,      BEFORE,          ,   keyProperty         。
	 								      AFTER,         ,      selectKey  
		statementType:       ,MyBatis    STATEMENT ,PREPARED  CALLABLE        ,
					            PreparedStatement  CallableStatement   。	
					       
		  :selectKey             insert   parameterType            。    insert    
	 -->
 	<selectKey keyProperty="foodId" order="BEFORE" resultType="int" statementType="STATEMENT">
 		select FOOD_SEC.Nextval from dual
 	</selectKey>
	 insert into food values(#{foodId},#{foodName},#{price})
 </insert>
위의 예제 에서 selectKey 요 소 는 먼저 실 행 됩 니 다.food 의 id 는 설정 되 고 삽입 문 구 는 호출 됩 니 다.이것 은 데이터베이스 에서 자동 으로 생 성 된 메 인 키 와 유사 한 행 위 를 처리 하여 자바 코드 를 복잡 하 게 만 들 지 않도록 합 니 다.
SQL
이 요 소 는 재 활용 가능 한 SQL 코드 세그먼트 를 정의 할 수 있 습 니 다.다른 구문 에 포함 시 킬 수 있 습 니 다.정적(로 딩 매개 변수)으로 매개 변수 화 되 고 서로 다른 속성 값 이 너무 높 은 인 스 턴 스 변 화 를 포함 할 수 있 습 니 다.
예 를 들 면:

<sql id="userColumns"> 
${alias}.id,${alias}.username,${alias}.password 
</sql>
이 SQL 세 션 은 다음 과 같은 다른 문장 에 포 함 될 수 있 습 니 다.

<select id="selectUsers" resultType="map"> select <include refid="userColumns">
<property name="alias" value="t1"/></include>, <include refid="userColumns"><property name="alias" value="t2"/>
</include> from some_table t1 cross join some_table t2 </select>
속성 값 은 refid 속성 이나 포 함 된 자구 의 속성 값 에 사용 할 수 있 습 니 다.
Result Map
resultMap 요 소 는 MyBatis 에서 가장 중요 하고 강력 한 요소 입 니 다.결과 에서 데 이 터 를 집중 적 으로 꺼 내야 하 는 JDBC 코드 의 90%를 멀리 하 게 하 는 것 이 며,어떤 경우 에는 JDBC 가 지원 하지 않 는 일 을 할 수 있 도록 해 준다.사실 복잡 한 문 구 를 결합 하여 같은 코드 를 매 핑 하 는 것 과 비슷 하 게 작 성 했 습 니 다.수천 줄 을 넘 을 수 있 는 코드 를 만 들 수 있 습 니 다.ResultMap 의 디자인 은 간단 한 문 구 는 명확 한 결과 맵 이 필요 하지 않 고 많은 복잡 한 문 구 는 그들의 관 계 를 묘사 해 야 한 다 는 것 이다.

	<!-- 
 	resultMap  
		    :  SQL                 
	 	  
	 		id   ,resultMap     。
	 		type   ,         ,     。
	 	autoMapping    ,   true(   )|false,            ,
	 		                        ,   setter  。    false ,
	 		    resultMap                setter  。
 		
 		   :
 			id   ,                    
			result   ,                    
				property	     JavaBean      。 	
 				
 				column	            。
 				
 				javaType	       ,         。         JavaBean,
 							 MyBatis         。  ,          HashMap,
 							      javaType       。
 						
 				jdbcType	          。      insert,update  delete             。
 							JDBC     , MyBatis    。        JDBC   ,       ,       。
 	 			
 	 			typeHandler	               。             ,          。	
 	 			
 			association  
 						        “   ”   。       Java      ,   javaType(  MyBatis      )。
 						           。             ,    typeHandler。 
						        MyBatis         。MyBatis          :
 	
 							select:          SQL       Java    。   ;
							resultsMap:                 join     ,   Java    。
 	 
 	 
 	 -->
 	<resultMap type="grade" id="gradeMap" autoMapping="true" >
 		<!-- 
 			   autoMapping="false"(   true)                 true                   
 			<result column="gid" property="gid"/>
 			        
 				  gid         
 			
 			             id      result
 			<id column="gid" property="gid"/>
 		-->
 		<!--       gname  gname1 -->
 		<id column="gid" property="gid"/>
 		<result column="gname" property="gname"/>
 	</resultMap>
이상 은 개인 적 인 경험 이 므 로 여러분 에 게 참고 가 되 기 를 바 랍 니 다.여러분 들 도 저 희 를 많이 응원 해 주시 기 바 랍 니 다.

좋은 웹페이지 즐겨찾기