본문 바로가기
Data/Hadoop

[하둡 완벽 가이드] Chapter 6 맵리듀스 프로그래밍

by 양진주 2024. 5. 8.

6.1 환경 설정 API

Configuration 클래스 인스턴스: 환경 설정 속성 & 값의 집합

<?xml version="1.0"?>
<configuration>
  <property>
    <name>color</name>
    <value>yellow</value>
    <description>Color</description>
  </property>
  
  <property>
    <name>size</name>
    <value>10</value>
    <description>Size</description>
  </property>
  
  <property>
    <name>weight</name>
    <value>heavy</value>
    <final>true</final>
    <description>Weight</description>
  </property>
  
  <property>
    <name>size-weight</name>
    <value>${size},${weight}</value>
    <description>Size and weight</description>
  </property>
</configuration>

 

6.1.1 리소스 결합하기

환경설정: 하나 이상의 리소스 파일(XML) 사용 가능

- 기본 속성: core-default.xml 파일

- 특정 속성: core-site.xml 파일 

<?xml version="1.0"?>
<configuration>
  <property>
    <name>size</name>
    <value>12</value>
  </property>
  
  <property>
    <name>weight</name>
    <value>light</value>
  </property>
</configuration>

 

나중에 추가된 리소스에 정의된 속성: 이전에 정의된 속성 오버라이드 (size)

final 지정 속성: 오버라이드 ❌, 경고 메시지 로그 (weight)

➡️ 클라이언트의 설정 파일/잡 제출 매개변수 final 지정 ➡️ 사용자 변경 ❌

 

6.1.2 변수 확장

환경 설정 속성: 다른 속성/시스템 속성 정의 가능 

size-weight 속성 ➡️ ${size}, ${weight} 정의 

 

6.2 개발환경 설정하기 

프로젝트 생성 ➡️ 맵리듀스 프로그램 명령행/IDE 환경 빌드 ➡️ 로컬(독립) 모드 실행 

<project>
  <modelVersion>4.0.0</modelVersion>
	<groupId>com.hadoopbook</groupId>
	<artifactId>hadoop-book-mr-dev</artifactId>
	<packaging>jar</packaging>
	<version>4.0</version>

	<properties>
	  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<hadoop.version>3.3.6</hadoop.version>
	</properties>

	<dependencies>
	  <!-- Hadoop main client artifact -->
		<dependency>
		  <groupId>org.apache.hadoop</groupId>
			<artifactId>hadoop-client</artifactId>
			<version>${hadoop.version}</version>
		</dependency>
		<!-- Unit test artifacts -->
		<dependency>
		  <groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.11</version>
			<scope>test</scope>
		</dependency>
		<dependency>
		  <groupId>org.apache.mrunit</groupId>
			<artifactId>mrunit</artifactId>
			<version>1.1.0</version>
			<scope>test</scope>
			<classifier>hadoop2</classifier>
		</dependency>
		<!-- Hadoop test artifact for running mini clusters -->
		<dependency>
		  <groupId>org.apache.hadoop</groupId>
			<artifactId>hadoop-minicluster</artifactId>
			<version>${hadoop.version}</version>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
	  <finalName>hadoop-examples</finalName>
		<plugins>
		  <plugin>
			  <groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.1</version>
        <configuration>
				  <source>1.6</source>
					<target>1.6</target>
				</configuration>
			</plugin>
			<plugin>
			  <groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-jar-plugin</artifactId>
				<version>2.5</version>
				<configuration>
				  <outputDirectory>${basedir}</outputDirectory>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>

 

만약 POM에서 정의한 의존성과 동일) 빌드 도구를 사용하는 것이 직관적

맵리듀스 잡 빌드: hadoop-client(하둡의 클라이언트 클래스 모두 포함) 의존성만 필요 

- 단위 테스트: junit 사용 

- 맵리듀스 테스트: mrunit 사용 

hadoop-minicluster 라이브러리: '미니' 클러스터(단일 JVM으로 실행되는 하둡 클러스터에서 테스트할 때 유용) 포함 

 

6.2.1 환경 설정 파일 관리하기

하둡 애플리케이션: 로컬 모드/클러스터 모드 번갈아가며 테스트

환경 설정 파일(각 클러스터의 연결 정보 가짐) 생성 ➡️ 하둡 애플리케이션/도구 실행 시 하나 지정 

 

로컬 모드(hadoop-local.xml): 기본 파일 시스템 & 로컬 프레임워크(맵리듀스 잡 실행)에 적합 

<?xml version="1.0"?>

<configuration>

  <property>
	  <name>fs.defaultFS</name>
		<value>file:///</value>
	</property>

	<property>
	  <name>mapreduce.framework.name</name>
		<value>local</value>
	</property>

</configuration>

 

의사 분산 모드(hadoop-localhost.xml): 로컬에서 작동하는 네임노드 & YARN 리소스 매니저 위치 설정 

<?xml version="1.0"?>

<configuration>

  <property>
	  <name>fs.defaultFS</name>
		<value>hdfs://localhost:9000/</value>
	</property>

	<property>
	  <name>mapreduce.framework.name</name>
		<value>yarn</value>
	</property>

	<property>
	  <name>yarn.resourcemanager.address</name>
		<value>localhost:8032</value>
	</property>

</configuration>

 

완전분산 모드(hadoop-cluster.xml): 클러스터의 네임노드 & YARN 리소스 매니저의 주소 

<?xml version="1.0"?>

<configuration>

  <property>
	  <name>fs.defaultFS</name>
		<value>hdfs://namenode/</value>
	</property>

	<property>
	  <name>mapreduce.framework.name</name>
		<value>yarn</value>
	</property>

	<property>
	  <name>yarn.resourcemanager.address</name>
		<value>resourcemanager:8032</value>
	</property>

</configuration>

 

 

사용자 인증 설정
하둡: 사용자 HDFS의 접근 권한 확인 
if 하둡의 사용자 이름 != 클라이언트 머신의 사용자 계정) HADOOP_USER_NAME 환경 변수에 사용자 이름 명시적 설정 

 

-conf + 파일 명시적 지정: 원하는 환경 설정 쉽게 이용 

% hadoop fs -conf conf/hadoop-localhost.xml -ls .

 

6.2.2 GenericOptionsParser, Tool, ToolRunner

헬퍼 클래스: 명령행에서 잡을 쉽게 실행 

- GenericOptionsParser 클래스: 명령행 옵션 해석 ➡️ Configuration 객체에 값을 설정 

(Tool 인터페이스 구현 애플리케이션 작성 ➡️ ToolRunner(GenericOptionsParser를 내부에서 호출)로 프로그램 실행하는 것이 편리)

public class ConfigurationPrinter extends Configured implements Tool {
  
  static {
    Configuration.addDefaultResource("hdfs-default.xml");
    Configuration.addDefaultResource("hdfs-site.xml");
    Configuration.addDefaultResource("mapred-default.xml");
    Configuration.addDefaultResource("mapred-site.xml");
  }

  @Override
  public int run(String[] args) throws Exception {
    Configuration conf = getConf();
    for (Entry<String, String> entry: conf) {
      System.out.printf("%s=%s\n", entry.getKey(), entry.getValue());
    }
    return 0;
  }
  
  public static void main(String[] args) throws Exception {
    int exitCode = ToolRunner.run(new ConfigurationPrinter(), args);
    System.exit(exitCode);
  }
}
//Tool(Configuration 객체 내부의 속성 출력) 구현

 

Configured 클래스(Configurable 인터페이스 구현) 상속 ➡️ ConfigurationPrinter 만듦

run 메서드: getConf() 메서드 ➡️ Configuration 객체 반복적으로 얻고 모든 속성 표준 출력으로 출력 

 

정적 블록: 핵심 환경 설정 외에 HDFS, YARN, 맵리듀스 환경 설정 명시적으로 추가 

 

main() 메서드: 자신의 run() 메서드 직접 호출 ❌, Toolruner의 run() 정적 메서드 호출 

Toolrunner: GenericOptionsParser 사용 ➡️ 명령행에 명시된 표준 옵션 추출 ➡️ Configuration 객체 설정 

 

GenericOptionsParser: 개별 속성 지정 기능 제공 

% hadoop ConfigurationPrinter -D color=yellow | grep color
color=yellow

 

우선순위: -D 명시 옵션 > 환경 설정 파일에서 설정한 속성 ➡️ 기본값: 환경 설정 파일에 정의, -D 옵션으로 필요할 때 값 재정의 

 

옵션 설명
-D property=value 지정된 하둡 환경 설정 속성 ➡️ 주어진 값으로 설정

기본/사이트 속성(환경 설정 파일에 존재) + -conf 옵션
➡️ 설정한 모든 속성을 오버라이드
-conf filename 환경 설정의 리소스 목록 + 지정 파일 
-fs uri 지정된 URI로 기본 파일시스템 설정
-jt host:port 지정된 호스트/포트 ➡️ YARN 리소스 매니저 설정
-files file1, file2, ... 지정된 파일: 공유 파일시스템으로 복사
➡️ 맵리듀스 프로그램 사용 가능 
-archives archive1, archive2, ... 지정된 아카이브: 공유 파일시스템으로 복사, 아카이브 해제
-libjars jar1, jar2, ... 지정된 JAR 파일: 공유 파일시스템으로 복사, 맵리듀스 태스크의 클래스패스에 추가 

 

 

6.3 엠알유닛으로 단위 테스트 작성하기

맵 & 리듀스 함수: 개별적으로 테스트 용이(함수형 스타일의 장점)

엠알유닛: 테스트 라이브러리(매퍼, 리듀서 데이터 전달 ➡️ 예상대로 출력되는지 점검)

 

6.3.1 매퍼

//매퍼 테스트 코드

import java.io.IOException;
import org.apache.hadoop.io.*;
import org.apache.hadoop.mrunit.mapreduce.MapDriver;
import org.junit.*;

public class MaxTemperatureMapperTest {

  @Test
  public void processesValidRecord() throws IOException {
    MaxTemperatureMapper mapper = new MaxTemperatureMapper();
    
    Text value = new Text("0043011990999991950051518004+68750+023550FM-12+0382" +
                                  // Year ^^^^
    		"99999V0203201N00261220001CN9999999N9-00111+99999999999");
                              // Temperature ^^^^^
    OutputCollector<Text, IntWritable> output = mock(OutputCollector.class);

    mapper.map(null, value, output, null);
    
    verify(output).collect(new Text("1950"), new IntWritable(-11));
  }
  
  @Test
  public void ignoresMissingTemperatureRecord() throws IOException {
    MaxTemperatureMapper mapper = new MaxTemperatureMapper();
    
    Text value = new Text("0043011990999991950051518004+68750+023550FM-12+0382" +
                                  // Year ^^^^
        "99999V0203201N00261220001CN9999999N9+99991+99999999999");
                              // Temperature ^^^^^
    OutputCollector<Text, IntWritable> output = mock(OutputCollector.class);

    mapper.map(null, value, output, null);
    
    Text outputKey = anyObject();
    IntWritable outputValue = anyObject();
    verify(output, never()).collect(outputKey, outputValue);
  }
}

 

테스트 방법: 입력 - 날씨 레코드 ➡️ 출력값 확인(입력 연도, 기온 일치)

MapDriver 사용 ➡️ 매퍼 테스트 

- MaxTemperatureMapper 인스턴스 설정 ➡️ (입력키 & 값), (예상되는 출력키 & 값) 설정 ➡️ runTest() 함수 호출 

 

//MaxTemperatureMapperTest를 통과한 첫 번째 버전의 매퍼 

public class MaxTemperatureMapper extends Mapper<LongWritable, Text, Text, IntWritable> {

    @Override
    protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
        String string = value.toString();
        String year = line.substring(15, 19);
        int airTemperature = Integer.parseInt(line.substring(87, 92));
        context.write(new Text(year), new IntWritable(airTemperature));
    }
}

 

//원복 데이터에서 +9999로 나타내는 결측값 테스트 코드 추가

@Test
    public void ignoresMissingTemperatureRecord() throws IOException, InterruptedException {
        Text value = new Text("0043011990999991950051518004+68750+023550FM-12+0382" +
                //Year ^^^^
                "99999V0203201N00261220001CN9999999N9+99991+99999999999");  
                //Temperature^^^^^

        new MapDriver<LongWritable, Text, Text, IntWritable>()
                .withMapper(new MaxTemperatureMapper())
                .withInput(new LongWritable(0), value)
                .runTest();
    }

 

MapDriver: withOutput() 함수의 호출 횟수에 따라 0개 이상 출력 레코드 점검

+9999 특별 입력 다루지 ❌ ➡️ 새로운 테스트 실패 

 

매퍼 로직 추가하는 것보다 파서 클래스(파싱 로직 포함)에서 처리하는 것이 좋음 🔽

//Ncdc 날씨 레코드 파싱 클래스

public class NcdcRecordParser {

    private static final int MISSING_TEMPERATURE = 9999;
    private String year;
    private int airTemperature;
    private String quality;

    public void parse(String record) {
        year = record.substring(15, 19);
        String airTemperatureString;
        if (record.charAt(87) == '+')
            airTemperatureString = record.substring(88, 92);
        else
            airTemperatureString = record.substring(87, 92);

        airTemperature = Integer.parseInt(airTemperatureString);
        quality = record.substring(92, 93);
    }

    public void parse(Text record) {
        parse(record.toString());
    }

    public boolean isTemperatureValid() {
        return airTemperature != MISSING_TEMPERATURE && quality.matches("[01459]");
    }

    public String getYear() {
        return year;
    }

    public int getAirTemperature() {
        return airTemperature;
    }
}

 

입력 텍스트 행으로부터 parse() 메서드(입력 텍스트 행으로부터 관심 있는 필드 파싱하는 파서) 호출

➡️ isValidTemperature() 메서드로 기온값이 유효한지 점검 

public class MaxTemperatureMapper extends Mapper<LongWritable, Text, Text, IntWritable> {
    private NCDCRecordParser parser = new NCDCRecordParser();

    @Override
    public void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
        
        parser.parse(value);
        if (parser.isValidTemperature())
        context.write(new Text(parser.getYear()), new IntWritable(parser.getAirTemperature()));
    }
}

6.3.2 리듀서

리듀서: 입력받은 키의 최대 기온값 찾음 

구현 코드 🔽

@Test
    public void returnsMaximumIntegerInValues() throws IOException, InterruptedException {
        new ReduceDriver<Text, IntWritable, Text, IntWritable>()
                .withReducer(new MaxTemperatureReducer())
                .withInput(new Text("1950"), Arrays.asList(new IntWritable(10), new IntWritable(5)))
                .withOutput(new Text("1950"), new IntWritable(10))
                .runTest();
    }
}

 

테스트를 통과한 MaxTemperatureReducer 구현

public class MaxTemperatureReducer extends Reducer<Text, IntWritable, Text, IntWritable> {  
    @Override
    protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
        int maxValue = Integer.MIN_VALUE;
        for (IntWritable value : values) {
            maxValue = Math.max(maxValue, value.get());
        }
        context.write(key, new IntWritable(maxValue));
    }
}

 

6.4 로컬에서 실행하기

6.4.1 로컬 잡 실행하기

public class MaxTemperatureDriver extends Configured implements Tool {
	
    @Override
    public int run(String[] args) throws Exception {
        if (args.length != 2) {
            System.err.printf("Usage: %s [generic options] <input> <output>\n", getClass().getSimpleName());
            ToolRunner.printGenericCommandUsage(System.err);
            return -1;
        }

        Job job = new Job(getConf(), "Max temperature");  
        job.setJarByClass(getClass()); 

        FileInputFormat.addInputPath(job, new Path(args[0]));
        FileOutputFormat.setOutputPath(job, new Path(args[1])); 

        job.setMapperClass(MaxTemperatureMapper.class); 
        job.setCombinerClass(MaxTemperatureReducer.class);
        job.setReducerClass(MaxTemperatureReducer.class); 

        job.setOutputKeyClass(Text.class); 
        job.setOutputValueClass(IntWritable.class); 

        return job.waitForCompletion(true) ? 0 : 1; 
    }

    public static void main(String[] args) throws Exception {
        int exitCode = ToolRunner.run(new MaxTemperatureDriver(), args); 
        System.exit(exitCode);
    }
}

 

MaxTemperatureDriver: Tool 인터페이스의 구현체, GenericOptionsParser가 제공하는 옵션 설정 기능 이용 

run() 메서드: Tool(잡 시작될 때 사용)의 환경 설정 기반 ➡️ Job 객체 생성

잡 환경 설정 中 입출력 파일 경로, 매퍼, 리듀서, 컴바이너 클래스, 출력 타입 설정

- 입력 타입: TextInput Format(기본 입력 포맷) - LongWritable 키 & Text 값

 

하둡: 맵리듀스 실행 엔진의 축소 버전 ➡️ 단일 JVM에서 동작하는 로컬 잡 실행자(테스트 용도) 제공

환경 설정에서 mapreduce.framework.name 속성: local(기본값)로 설정 ➡️ 로컬 잡 실행자(모든 파일 시스템에서 작동) 사용 

% mvn compile
% export HADOOP_CLASSPATH=target/classes/
% hadoop v2.MaxTemperatureDriver -conf conf/hadoop-local.xml \input/ncdc/micro output

+) -fs, -jt 옵션 사용 방법 (GenericOptionsParser 제공)

% hadoop v2.MaxTemperatureDriver -fs file:/// -jt local /input/ncdc/micro output

 

MaxTemperatureDriver(로컬 input/ncdc/micro 디렉터리의 데이터 입력 ➡️ 로컬 output 디렉터리에 결과 저장) 실행 

 

6.4.2 드라이버 테스트하기

Tool 구현 애플리케이션 제장의 장점: 환경 설정 옵션의 유연성, Configuration 객체 직접 입력 ➡️ 테스트 용이

테스트 코드(로컬 잡 실행자에 테스트용 입력 데이터 입력 ➡️ 출력 결과 점검) 작성 시 활용 가능

 

i) 로컬 잡 실행자 사용 ➡️ 테스트 파일(in 로컬 파일시스템)로 실행 

@Test
    public void test() throws Exception {
        Configuration conf = new Configuration(); 
        conf.set("fs.defaultFS", "file:///");
        conf.set("mapreduce.framework.name", "local");
        conf.setInt("mapreduce.task.io.sort.mb", 1);

        Path input = new Path("/mnt/sda6/NCDC.txt");
        Path output = new Path("/mnt/sda6/output");

        FileSystem fs = FileSystem.getLocal(conf);
        fs.delete(output, true);

        MaxTemperatureDriver driver = new MaxTemperatureDriver();
        driver.setConf(conf);

        int exitCode = driver.run(new String[]{
                input.toString(), output.toString()
        });
        assertThat(exitCode, is(0));

        checkOutput(conf,output);
    }
}

fs.defaultFS & mapreduce.framework.name 명시적 설정 ➡️ 로컬 파일시스템 & 로컬 잡 실행자 사용

MaxTemperatureDriver(Tool 인터페이스 구현) 작은 입력 데이터로 실행 

checkOutput() 메서드: 실제 출력 결과 & 예상 결과 한 줄씩 비교 

 

ii) '미니' 클러스터에서 수행

하둡: MiniDFS, Cluster, MiniMRCluster, MiniYARNCluster 테스트 클래스 제공 ➡️ 단일 프로세스 클러스터 생성 가능 + HDFS, 맵리듀스, YARN과 기술적으로 똑같은 환경에서 테스트 가능 

+) 미니 클러스터: 사용자 코드 테스트할 때 사용 가능 (ClusterMaprReduceTestCase: 테스트 코드 작성을 위한 원형)

    - setUp(), tearDown(): 단일 프로세스 HDFS & YARN 클러스터 시작/중단 제어 

    - Configuration 객체 생성 

 

➡️ 회귀 검사, 입력 극한 사례를 위한 테스트 저장소로 유용

 

6.5 클러스터에서 실행하기 

~6.4: 작은 테스트 데이터셋 확인

6.5~: 하둡 클러스터의 전체 데이터셋 

6.5.1 잡 패키징

로컬 잡 실행자: 단일 JVM 사용 ➡️ 잡에 필요한 모든 클래스: 로컬의 클래스 경로에 존재하면 잘 작동함 

분산 환경: 필요한 모든 클래스 잡 JAR 파일에 패키징 ➡️ 클러스터로 전송 

 

잡 JAR 파일: 빌드 도구(앤트, 메이븐)를 사용해 쉽게 생성 

POM 이용하여 메이븐 명령어 실행 ➡️ hadoop-examples.jar 파일(프로젝트 디렉터리에 컴파일된 클래스 모두 포함) 생성

% mvn package -DskipTests

JAR 파일에 잡 한 개 ➡️ JAR 파일의 manifest에 메인 클래스 지정 or 명령행 지정 

 

클라이언트 클래스경로

hadoop jar <jar>로 지정하는 클라이언트 클래스경로

= 잡 JAR 파일 +  잡 JAR 파일의 lib 디렉터리에 있는 모든 JAR 파일과 classes 디렉터리+ HADOOP_CLASSPATH에 정의한 클래스 경로 

 

태스크 클래스경로

클러스터에서 맵 & 리듀스 태스크: 개별 JVM으로 실행 ➡️ 클래스 경로로 지정해도 소용 ❌

태스크 클래스경로

= 잡 JAR 파일 + 잡 JAR 파일의 lib 디렉터리에 있는 모든 JAR 파일과 classes 디렉터리 + -libjars 옵션/DistributedCache/Job의 addFileToClassPath() 메서드를 사용해 분산 캐리세 추가한 모든 파일 

 

의존 라이브러리 패키징

- 라이브러리 풀기 ➡️ JAR에 넣고 다시 패키징

- 잡 JAR의 lib 디렉터리에 라이브러리 패키징

- 잡 JAR와 다른 위치에 라이브러리 지정 ➡️ HADOOP_CLASSPATH & -libjars 이용 ➡️ 클라이언트의 클래스경로 & 태스크의 클래스 경로 각각 추가 

➡️ 의존 라이브러리 잡 JAR에 다시 넣지 않아도 됨 ➡️ 가장 빌드하기 간단, 전송할 JAR 파일 개수 ⬇️

 

태스크 클래스경로의 우선순위

사용자 JAR 파일: 클라이언트 클래스경로 & 태스크 클래스 경로 마지막에 추가 ➡️ 내장 라이브러리와 충돌 가능 ➡️ 태스크 클래스 경로 제어 ➡️ 사용자 클래스 먼저 선택 

 

HADOOP_USER_CLASSPATHFIRST 환경 변수 = true ➡️ 사용자 클래스 경로: 강제로 하둡의 클래스 경로 검색 순서의 맨 앞

mapreduce.job.user.classpath.first 속성 = true

 

* 잡 제출/태스크 실패 가능 유의 

 

6.5.2 잡 구동하기 

잡 구동하기 위해 드라이버 실행: -conf 옵션 ➡️ 실행할 클러스터 지정 

% unset HADOOP_CLASSPATH
% hadoop jar hadoop-examples.jar v2.MaxTemperatureDriver \ -conf conf/hadoop-Cluster.xml input/ncdc/all mzx-temp

 

watForcompletion(): 잡 구동 후 진행 상황 주기적으로 보고, 맵 & 리듀스 처리 과정에 변화 ➡️ 요약하여 출력

실행 결과: ID 출력, (잡 완료 시) 카운터의 통계 정보 출력

 

6.5.3 맵리듀스 웹 UI

 

리소스 매니저 페이지

'클러스터 메트릭스': 클러스터에서 현재 실행 중인 애플리케이션 개수, 클러스터 가용 자원의 용량, 노드 매니저 정보

메인 테이블: 클러스터에서 완료/실행 중인 모든 애플리케이션, 검색 창 

잡 히스토리 페이지: 리소스 매니저에서 삭제된 애플리케이션(영속적)

 

맵리듀스 잡 페이지 

Tracking UI 클릭 ➡️ 애플리케이션 마스터의 웹 UI로 이동 

실행 중인 잡의 진행 상황 확인

테이블: 맵 & 리듀스의 진행 상황 

테이블의 마지막 부분: 맵 / 리듀스 태스크의 실패/죽은 태스크 시도의 전체 개수 (killed)

Configuration 링크 클릭 ➡️ 통합 설정 파일(잡을 실행할 때 영향을 미치는 모든 속성 & 값을 가짐)로 이동