diff --git a/README.md b/README.md
index c550c4c2a09..e99e567af60 100644
--- a/README.md
+++ b/README.md
@@ -6,4 +6,14 @@
 * 모든 피드백을 완료하면 다음 단계를 도전하고 앞의 과정을 반복한다.
 
 ## 온라인 코드 리뷰 과정
-* [텍스트와 이미지로 살펴보는 온라인 코드 리뷰 과정](https://github.com/next-step/nextstep-docs/tree/master/codereview)
\ No newline at end of file
+* [텍스트와 이미지로 살펴보는 온라인 코드 리뷰 과정](https://github.com/next-step/nextstep-docs/tree/master/codereview)
+
+---
+
+## "3단계 - 자동차 경주" 기능 목록 및 요구사항
+
+### 기능 요구사항
+- [ ] 자동차 대수를 입력받는다.
+- [ ] 이동할 횟수를 입력받는다.
+- [ ] 랜덤 값을 생성하여 전진 (값이 4 이상) 혹은 정지 (값이 3 이하) 할지 결정한다.
+- [ ] 매 이동 시 마다 자동차 상태를 출력한다.
\ No newline at end of file
diff --git a/src/main/java/controller/RacingGame.java b/src/main/java/controller/RacingGame.java
new file mode 100644
index 00000000000..83cbd72a4bd
--- /dev/null
+++ b/src/main/java/controller/RacingGame.java
@@ -0,0 +1,41 @@
+package controller;
+
+import domain.Car;
+import domain.Cars;
+import domain.RandomNumberGenerator;
+import view.InputView;
+import view.ResultView;
+
+public class RacingGame {
+    private final Cars cars;
+    private final int numberOfRounds;
+    private final RandomNumberGenerator randomNumberGenerator = new RandomNumberGenerator();
+
+    public RacingGame(int numberOfCars, int numberOfRounds) {
+        this.numberOfRounds = numberOfRounds;
+        this.cars = new Cars(numberOfCars);
+    }
+
+    private void runOneRound() {
+        for (Car car : cars.getCars()) {
+            car.move(randomNumberGenerator);
+            ResultView.printPosition(car.getCurrentPosition());
+        }
+        ResultView.printMessage("");
+    }
+
+    private void run() {
+        ResultView.printMessage("실행 결과");
+        for (int i = 0; i < this.numberOfRounds; i++) {
+            runOneRound();
+        }
+    }
+
+    public static void main(String[] args) {
+        int numberOfCars = InputView.getNumberOfCars();
+        int numberOfRounds = InputView.getNumberOfRounds();
+
+        RacingGame game = new RacingGame(numberOfCars, numberOfRounds);
+        game.run();
+    }
+}
diff --git a/src/main/java/domain/Car.java b/src/main/java/domain/Car.java
new file mode 100644
index 00000000000..a552f82be6a
--- /dev/null
+++ b/src/main/java/domain/Car.java
@@ -0,0 +1,17 @@
+package domain;
+
+public class Car {
+    private static final int MOVE_THRS = 4;
+    private final Position position = new Position();
+
+    public void move(NumberGenerator numberGenerator) {
+        final int number = numberGenerator.generate();
+        if (number >= MOVE_THRS) {
+            this.position.increase();
+        }
+    }
+
+    public Position getCurrentPosition() {
+        return this.position;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/domain/Cars.java b/src/main/java/domain/Cars.java
new file mode 100644
index 00000000000..e38e6b2b8d5
--- /dev/null
+++ b/src/main/java/domain/Cars.java
@@ -0,0 +1,22 @@
+package domain;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class Cars {
+    private final List<Car> cars = new ArrayList<>();
+
+    public Cars(int numberOfCars) {
+        generateCars(numberOfCars);
+    }
+
+    private void generateCars(int numberOfCars) {
+        for (int i = 0; i < numberOfCars; i++) {
+            this.cars.add(new Car());
+        }
+    }
+
+    public List<Car> getCars() {
+        return this.cars;
+    }
+}
diff --git a/src/main/java/domain/NumberGenerator.java b/src/main/java/domain/NumberGenerator.java
new file mode 100644
index 00000000000..d1e5b53aa44
--- /dev/null
+++ b/src/main/java/domain/NumberGenerator.java
@@ -0,0 +1,6 @@
+package domain;
+
+public interface NumberGenerator {
+    int generate();
+}
+
diff --git a/src/main/java/domain/Position.java b/src/main/java/domain/Position.java
new file mode 100644
index 00000000000..8088d0a9ee4
--- /dev/null
+++ b/src/main/java/domain/Position.java
@@ -0,0 +1,20 @@
+package domain;
+
+public class Position {
+    private int value = 0;
+
+    public void increase() {
+        this.value++;
+    }
+
+    @Override
+    public String toString() {
+        return "-".repeat(this.value);
+    }
+
+    private void validate() {
+        if (this.value < 0) {
+            throw new IllegalArgumentException("Position value must be greater than or equal to 0.");
+        }
+    }
+}
diff --git a/src/main/java/domain/RandomNumberGenerator.java b/src/main/java/domain/RandomNumberGenerator.java
new file mode 100644
index 00000000000..d6d4760497a
--- /dev/null
+++ b/src/main/java/domain/RandomNumberGenerator.java
@@ -0,0 +1,13 @@
+package domain;
+
+import java.util.Random;
+
+public class RandomNumberGenerator implements NumberGenerator {
+    private static final Random random = new Random();
+    private static final int MAX_BOUND = 10;
+
+    @Override
+    public int generate() {
+        return random.nextInt(MAX_BOUND);
+    }
+}
diff --git a/src/main/java/view/InputView.java b/src/main/java/view/InputView.java
new file mode 100644
index 00000000000..0803b2fb1c6
--- /dev/null
+++ b/src/main/java/view/InputView.java
@@ -0,0 +1,20 @@
+package view;
+
+import java.util.Scanner;
+
+public class InputView {
+    private static final Scanner scanner = new Scanner(System.in);
+
+    public static int getNumberOfCars() {
+        return getInput("자동차 대수는 몇 대 인가요?");
+    }
+
+    public static int getNumberOfRounds() {
+        return getInput("시도할 회수는 몇 회 인가요?");
+    }
+
+    private static int getInput(String message) {
+        System.out.println(message);
+        return scanner.nextInt();
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/view/ResultView.java b/src/main/java/view/ResultView.java
new file mode 100644
index 00000000000..90aa96aa077
--- /dev/null
+++ b/src/main/java/view/ResultView.java
@@ -0,0 +1,14 @@
+package view;
+
+import domain.Position;
+
+public class ResultView {
+
+    public static void printPosition(Position position) {
+        printMessage(position.toString());
+    }
+
+    public static void printMessage(String message) {
+        System.out.println(message);
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/domain/CarTest.java b/src/test/java/domain/CarTest.java
new file mode 100644
index 00000000000..bae2f1185d3
--- /dev/null
+++ b/src/test/java/domain/CarTest.java
@@ -0,0 +1,30 @@
+package domain;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class CarTest {
+    @DisplayName("숫자가 4 미만이면 움직이지 않는다.")
+    @Test
+    void moveTestSmallerThanFour() {
+        Car car = new Car();
+        NumberGenerator numberGenerator = new StaticNumberGenerator(3);
+
+        car.move(numberGenerator);
+
+        assertThat(car.getCurrentPosition()).extracting("value").isEqualTo(0);
+    }
+
+    @DisplayName("숫자가 4 이상이면 움직인다.")
+    @Test
+    void moveTestLargerThanOrEqualToFour() {
+        Car car = new Car();
+        NumberGenerator numberGenerator = new StaticNumberGenerator(5);
+
+        car.move(numberGenerator);
+
+        assertThat(car.getCurrentPosition()).extracting("value").isEqualTo(1);
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/domain/CarsTest.java b/src/test/java/domain/CarsTest.java
new file mode 100644
index 00000000000..097a03368a8
--- /dev/null
+++ b/src/test/java/domain/CarsTest.java
@@ -0,0 +1,19 @@
+package domain;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.InstanceOfAssertFactories.list;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class CarsTest {
+    @DisplayName("입력받은 숫자만큼 자동차를 생성한다.")
+    @Test
+    void generateCarsTest() {
+        int numberOfCars = 3;
+
+        Cars cars = new Cars(numberOfCars);
+
+        assertThat(cars.getCars()).asInstanceOf(list(Car.class)).hasSize(numberOfCars);
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/domain/StaticNumberGenerator.java b/src/test/java/domain/StaticNumberGenerator.java
new file mode 100644
index 00000000000..f3c5613a54b
--- /dev/null
+++ b/src/test/java/domain/StaticNumberGenerator.java
@@ -0,0 +1,14 @@
+package domain;
+
+public class StaticNumberGenerator implements NumberGenerator {
+    private static int NUMBER;
+
+    public StaticNumberGenerator(int number) {
+        NUMBER = number;
+    }
+
+    @Override
+    public int generate() {
+        return NUMBER;
+    }
+}
\ No newline at end of file