Home dependsOn과 mustRunAfter는 무슨 차이가 있을까?
Post
Cancel

dependsOn과 mustRunAfter는 무슨 차이가 있을까?

글을 작성하게 된 계기


Java로 된 프로젝트를 Kotlin으로 마이그레이션 하면서 build.gradle을 수정해야 할 일이 발생했습니다. 이 과정에서 dependsOn과 mustRunAfter의 차이점 에 대해 알게 되었고, 이를 정리하기 위해 글을 작성하게 되었습니다.





1. dependsOn


dependsOn은 한 작업이 다른 작업에 의존한다는 것을 의미합니다. A.dependsOn(B)를 사용하면, Gradle은 A 작업을 실행하기 전, 반드시 B 작업을 실행합니다. 즉, B 작업이 성공적으로 완료되어야 A 작업이 실행됩니다.

Adds the given dependencies to this task. See here for a description of the types of objects which can be used as task dependencies.





이를 통해 동적 으로 작업을 제어할 수 있습니다. 예를 들어, project.hasProperty(someCondition)와 같이 특정 조건을 활용해 필요한 작업을 추가하는 것입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
tasks.register("taskA") {
    doLast {
        println("Executing task A")
    }
}

tasks.register("specialTask") {
    doLast {
        println("Executing specialTask")
    }
}

tasks.named("taskA") {
    // 특정 속성(`runSpecialTask`)이 있는 경우에만 `mySpecialTask`를 dependsOn으로 추가
    if (project.hasProperty("runSpecialTask")) {
        dependsOn("specialTask")
    }
}





1-1. 여러 작업에 대한 의존성

이는 하나의 작업이 여러 작업에 의존할 수도 있는데, 예를 들어 A.dependsOn(B, C)와 같이 설정하면 A 작업을 실행하기 전, B와 C 작업이 모두 완료됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
tasks.register("taskB") {
    doLast {
        println("Executing task B")
    }
}

tasks.register("taskC") {
    doLast {
        println("Executing task C")
    }
}

tasks.register("taskA") {
    dependsOn("taskB", "taskC")
    doLast {
        println("Executing task A")
    }
}





하지만 dependsOn을 사용하면 taskB와 taskC의 실행 순서는 보장되지 않으며, 두 작업은 병렬로 실행될 수도 있습니다.

1
2
3
4
5
6
7
8
> Task :taskB
Executing task B

> Task :taskC
Executing task C

> Task :taskA
Executing task A





1-2. 후행 작업 관리

finalizedBy를 통해 후행 작업을 관리할 수도 있습니다. 작업이 완료된 후 실행되어야 하는 작업을 지정할 수 있기 때문에 작업 종료 후 후속 작업을 추가할 때 유용합니다.

1
2
3
4
tasks.named("A") {
    dependsOn("B")      // A 작업은 B 작업에 의존합니다
    finalizedBy("C")    // A 작업이 끝나면 C 작업이 실행됩니다
}







2. mustRunAfter


mustRunAfter는 한 작업이 특정 작업 뒤에 실행되어야 한다는 것을 지정합니다. A.mustRunAfter(B)를 사용하면, A 작업은 B 작업이 완료된 후에 실행됩니다. 하지만, 의존 관계는 설정하지 않습니다. B 작업이 필요하지 않다면, Gradle은 B 작업을 생략할 수 있으며 A 작업은 그냥 실행됩니다.

Specifies that this task must run after all of the supplied tasks.





특정 작업이 반드시 순서대로 실행되어야 하지만 서로 의존 관계가 필요 없을 때 유용합니다. 예를 들어, 문서 생성 작업이 test 작업 뒤에 실행되도록 순서를 정할 수 있습니다.

1
2
3
tasks.named("A") {
    mustRunAfter("B")
}





2-1. 순서만 보장

mustRunAfter는 순서만 지정하고, 실제로 작업이 실행될 필요는 없습니다. 따라서 taskA가 taskB 뒤에 실행되도록 A.mustRunAfter(B)로 지정하면, taskB가 실행되지 않더라도 taskA는 독립적으로 실행될 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
tasks.register("taskB") {
    doLast {
        println("Executing task B")
    }
}

tasks.register("taskA") {
    mustRunAfter("taskB")
    doLast {
        println("Executing task A")
    }
}





2-2. 여러 작업에 대해 순서 지정 가능

하나의 작업이 여러 작업의 뒤에 실행되도록 설정할 수 있습니다. 예를 들어 taskA.mustRunAfter(taskB, taskC)와 같이 설정하면, taskA는 taskB와 taskC가 완료된 후 실행됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
tasks.register("taskB") {
    doLast {
        println("Executing task B")
    }
}

tasks.register("taskC") {
    doLast {
        println("Executing task C")
    }
}

tasks.register("taskA") {
    mustRunAfter("taskB", "taskC")
    doLast {
        println("Executing task A")
    }
}





2-3. 병렬 빌드

mustRunAfter는 병렬 빌드 설정에서도 유용하게 사용됩니다. 여러 작업을 동시에 실행하면서도 특정 작업의 순서를 제어할 수 있어, 빌드 속도를 최적화하고 작업 간의 의존성을 줄일 수 있습니다.

1
2
3
gradle.settingsEvaluated {
    it.startParameter.parallelProjectExecutionEnabled = true
}
1
./gradlew build --parallel





이는 shouldRunAfter과 비슷한데, 순서 보장에서 차이점이 있습니다. mustRunAfter는 반드시 순서를 지켜야 하는 경우 사용하지만, shouldRunAfter는 순서를 권장하는 정도로 설정됩니다. 즉, shouldRunAfter는 병렬 실행을 할 경우, 순서가 보장되지 않을 수 있지만, mustRunAfter는 반드시 지정된 순서를 따릅니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
tasks.register("taskA") {
    doLast {
        println("Executing task A")
    }
}

tasks.register("taskB") {
    doLast {
        println("Executing task B")
    }
}

tasks.register("taskC") {
    mustRunAfter("taskA")       // taskC는 taskA가 완료된 후에 실행됩니다
    shouldRunAfter("taskB")     // taskC는 taskB 이후에 실행되는 것이 권장됩니다
    doLast {
        println("Executing task C")
    }
}







3. 차이점


mustRunAfter와 dependsOn의 차이점은 작업 간 의존성 유무실행 순서 보장 여부에서 나뉩니다.

  1. 작업 간 의존성 유무
  2. 실행 순서 보장



3-1. 의존 관계 여부

dependsOn은 특정 작업이 다른 작업에 의존하는 것을 의미합니다. 즉, taskA를 실행하기 전, 반드시 taskB가 실행되어야 하며, taskA가 taskB에 완전히 종속됩니다. taskB가 실행되지 않으면 taskA도 실행되지 않습니다.

1
2
3
4
5
6
7
8
9
10
11
12
tasks.register("taskB") {
    doLast {
        println("Executing task B")
    }
}

tasks.register("taskA") {
    dependsOn("taskB")
    doLast {
        println("Executing task A")
    }
}





여기서 taskA는 taskB에 의존하므로, taskB가 먼저 실행됩니다. 만약 taskB가 실행되지 않으면 taskA도 실행되지 않습니다.

1
2
3
4
5
> Task :taskB
Executing task B

> Task :taskA
Executing task A





반면, mustRunAfter는 작업 간 순서만 지정하는 옵션으로, taskB가 실행된 경우에만 이후에 taskA가 실행되도록 보장합니다. 하지만 taskB가 실행되지 않는다면 taskA는 독립적으로 실행될 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
tasks.register("taskB") {
    doLast {
        println("Executing task B")
    }
}

tasks.register("taskA") {
    mustRunAfter("taskB")
    doLast {
        println("Executing task A")
    }
}





./gradlew taskB taskA를 통해 각 태스크를 모두 실행한 경우, 다음과 같은 결과가 출력됩니다.

1
2
3
4
5
> Task :taskB
Executing task B

> Task :taskA
Executing task A





하지만 ./gradlew taskA를 통해 taskA만 실행한 경우, taskB가 실행되지 않아도 taskA는 독립적으로 실행됩니다.

1
2
> Task :taskA
Executing task A






3-2. 실행 순서 보장

dependsOn은 필수적인 순서 보장과 의존성을 제공합니다. 따라서 taskA에 taskB를 dependsOn으로 설정하면, taskA 작업을 호출할 때 Gradle이 반드시 taskB 작업을 먼저 실행하고 나서 taskA 작업을 수행합니다. 반면, mustRunAfter는 작업 실행 순서만 보장합니다. taskB가 실행된 경우에는 taskA가 반드시 taskB 뒤에 실행되지만, taskB가 필요 없으면 생략될 수 있습니다.

병렬 빌드에서 작업 순서만 보장하고, 작업 간 종속성을 줄이고 싶을 때 mustRunAfter가 유용합니다.







4. 정리


필수적인 의존성(dependsOn)과 순서 지정(mustRunAfter)의 차이점에 대해 살펴보았습니다. 둘의 차이를 활용하면 유연한 빌드 구성을 할 수 있기 때문에 차이점을 명확히 이해하고 사용할 수 있도록 합시다.


This post is licensed under CC BY 4.0 by the author.