diff --git a/doc/README.md b/doc/README.md
new file mode 100644
index 000000000..5fa6c6b79
--- /dev/null
+++ b/doc/README.md
@@ -0,0 +1,152 @@
+### 초기 설정
+- [ ] 프로그램 시작 시 역, 노선, 구간 정보를 초기 설정 해야 한다.
+ - 거리와 소요 시간은 양의 정수이며 단위는 km와 분을 의미한다.
+ - 아래의 사전 등록 정보로 반드시 초기 설정을 한다.
+
+```
+ 1. 지하철역으로 교대역, 강남역, 역삼역, 남부터미널역, 양재역, 양재시민의숲역, 매봉역이 등록되어 있다.
+ 2. 지하철 노선으로 2호선, 3호선, 신분당선이 등록되어 있다.
+ 3. 노선에 역이 아래와 같이 등록되어 있다.(왼쪽 끝이 상행 종점)
+ - 2호선: 교대역 - ( 2km / 3분 ) - 강남역 - ( 2km / 3분 ) - 역삼역
+ - 3호선: 교대역 - ( 3km / 2분 ) - 남부터미널역 - ( 6km / 5분 ) - 양재역 - ( 1km / 1분 ) - 매봉역
+ - 신분당선: 강남역 - ( 2km / 8분 ) - 양재역 - ( 10km / 3분 ) - 양재시민의숲역
+ ```
+
+### 지하철 역 기능
+- [x] 지하철 역을 생성할 수 있다.
+ - 지하철 역 생성 시 이름을 입력받는다.
+ - 예외 처리
+ - [x] 지하철 역 이름은 2글자 이상이어야 한다.
+
+### 지하철 역 관리 기능
+- [x] 지하철 역 저장소에 지하철 역을 등록할 수 있다.
+ - 예외 처리
+ - [x] 중복된 지하철 역 이름이 등록될 수 없다.
+
+### 지하철 노선 기능
+- [x] 지하철 노선을 생성할 수 있다.
+ - [x] 노선 생성 시 이름, 상행 종점역, 하행 종점역을 입력받는다.
+ - 예외 처리
+ - [x] 지하철 노선 이름은 2글자 이상이어야 한다.
+ - [x] 상행 종점역과 하행 종점역이 같을 수는 없다.
+
+### 지하철 노선 관리 기능
+- [x] 지하철 노선 저장소에 지하철 노선을 등록할 수 있다.
+ - 예외 처리
+ - [x] 중복된 지하철 노선 이름이 등록될 수 없다.
+
+### 지하철 구간 기능
+- [x] 지하철 구간을 생성할 수 있다.
+ - LineStation은 JGrapht의 Edge 정보(거리, 시간)를 생성자 인자로 받는다.
+ - 거리, 시간을 수정하면 거리, 시간 관리 그래프에도 반영되어야 한다.
+
+### 지하철 구간 관리 기능
+- [x] 지하철 노선 저장소에서 지하철 노선에 구간을 추가할 수 있다.
+ - [x] 해당 노선에 이미 존재하는 지하철 역은 추가할 수 없다.
+ - 순서
+ 1. 거리 그래프 관리 객체에 추가할 역을 Vertex로 추가한다.
+ 2. 거리 그래프 관리 객체에 해당 노선의 하행 끝 역과 추가할 역의 JGrapht Edge를 만든다.
+ 3. 거리 그래프 관리 객체에 해당 JGrapht Edge에 weight을 설정해서 추가한다.
+ 4. 시간 그래프 관리 객체에 추가할 역을 Vertex로 추가한다.
+ 5. 시간 그래프 관리 객체에 해당 노선의 하행 끝 역과 추가할 역의 JGrapht Edge를 만든다.
+ 6. 시간 그래프 관리 객체에 해당 JGrapht Edge에 weight을 설정해서 추가한다.
+ 7. LineStation의 속성으로 위의 두 JGrapht Edge를 설정하여 생성한 뒤에 노선의 하행 끝 뒤에 추가한다.
+
+### 경로 관리 기능
+- [x] 거리 그래프 객체와 시간 그래프 객체를 가지고 있다.
+- [x] 출발역과 도착역을 입력받아 최단 거리 경로를 조회한다.
+- [x] 출발역과 도착역을 입력받아 최소 시간 경로를 조회한다.
+ - 경로 조회 시 총 거리, 총 소요 시간도 함께 출력한다.
+ - 예외 처리
+ - [x] 경로 조회 시 출발역이나 도착역이 존재하지 않으면 에러를 출력한다.
+ - [x] 경로 조회 시 출발역과 도착역이 같으면 에러를 출력한다.
+ - [x] 경로 조회 시 출발역과 도착역이 연결되어 있지 않으면 에러를 출력한다.
+
+
+### 생각해보기
+#### 구간 정보가 바뀌면 그래프가 자동으로 업데이트 되도록 하자!
+구간 정보에 거리와 시간에 대한 JGrapht의 Edge 정보들을 저장하고 구간 정보가 바뀌어야 할 때 구간 관리 기능을 통해 해당 Edge를 업데이트 해준다.
+
+
+
+## ✍🏻 입출력 요구사항
+- `프로그래밍 실행 결과 예시`와 동일하게 입출력을 구현한다.
+- 기대하는 출력 결과는 `[INFO]`를 붙여서 출력한다. 출력값의 형식은 예시와 동일하게 한다.
+- 에러 발생 시 `[ERROR]`를 붙여서 출력한다. 에러의 문구는 자유롭게 작성한다.
+
+### 💻 프로그래밍 실행 결과 예시
+#### 경로 조회
+```
+## 메인 화면
+1. 경로 조회
+Q. 종료
+
+## 원하는 기능을 선택하세요.
+1
+
+## 경로 기준
+1. 최단 거리
+2. 최소 시간
+B. 돌아가기
+
+## 원하는 기능을 선택하세요.
+1
+
+## 출발역을 입력하세요.
+교대역
+
+## 도착역을 입력하세요.
+양재역
+
+## 조회 결과
+[INFO] ---
+[INFO] 총 거리: 6km
+[INFO] 총 소요 시간: 14분
+[INFO] ---
+[INFO] 교대역
+[INFO] 강남역
+[INFO] 양재역
+
+## 메인 화면
+1. 경로 조회
+Q. 종료
+
+...
+```
+
+#### 에러 출력 예시
+
+```
+## 메인 화면
+1. 경로 조회
+Q. 종료
+
+## 원하는 기능을 선택하세요.
+1
+
+## 경로 기준
+1. 최단 거리
+2. 최소 시간
+B. 돌아가기
+
+## 원하는 기능을 선택하세요.
+1
+
+## 출발역을 입력하세요.
+강남역
+
+## 도착역을 입력하세요.
+강남역
+
+[ERROR] 출발역과 도착역이 동일합니다.
+
+## 경로 기준
+1. 최단 거리
+2. 최소 시간
+B. 돌아가기
+
+## 원하는 기능을 선택하세요.
+
+...
+
+```
\ No newline at end of file
diff --git a/src/main/java/subway/ActionType.java b/src/main/java/subway/ActionType.java
new file mode 100644
index 000000000..7b2834654
--- /dev/null
+++ b/src/main/java/subway/ActionType.java
@@ -0,0 +1,18 @@
+package subway;
+
+public enum ActionType {
+
+ SHORTEST_DISTANCE_PATH("최단 거리"),
+ SHORTEST_TIME_PATH("최소 시간"),
+ BACK("돌아가기");
+
+ private final String name;
+
+ ActionType(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+}
diff --git a/src/main/java/subway/Application.java b/src/main/java/subway/Application.java
index 0bcf786cc..fceb99b6f 100644
--- a/src/main/java/subway/Application.java
+++ b/src/main/java/subway/Application.java
@@ -1,10 +1,28 @@
package subway;
import java.util.Scanner;
+import subway.utils.ErrorUtils;
+import subway.view.InputView;
+import subway.view.screen.MainScreen;
+import subway.view.screen.ScreenManager;
public class Application {
+
public static void main(String[] args) {
final Scanner scanner = new Scanner(System.in);
- // TODO: 프로그램 구현
+ InputView inputView = InputView.of(scanner);
+ DummyData.load();
+ start(inputView);
+ }
+
+ public static void start(InputView inputView) {
+ ScreenManager.push(new MainScreen());
+
+ while (!ScreenManager.isEmpty()) {
+ ErrorUtils.screenGoBackWhenException(() -> {
+ ScreenManager.visualize();
+ ScreenManager.logic(inputView);
+ });
+ }
}
}
diff --git a/src/main/java/subway/CategoryType.java b/src/main/java/subway/CategoryType.java
new file mode 100644
index 000000000..72977fd23
--- /dev/null
+++ b/src/main/java/subway/CategoryType.java
@@ -0,0 +1,31 @@
+package subway;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+public enum CategoryType {
+
+ STATION(
+ "경로 조회",
+ Arrays.asList(ActionType.SHORTEST_DISTANCE_PATH, ActionType.SHORTEST_TIME_PATH)),
+ EXIT(
+ "종료",
+ Collections.emptyList());
+
+ private final String name;
+ private final List actionOrder;
+
+ CategoryType(String name, List actionOrder) {
+ this.name = name;
+ this.actionOrder = actionOrder;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public List getActionOrder() {
+ return actionOrder;
+ }
+}
diff --git a/src/main/java/subway/DummyData.java b/src/main/java/subway/DummyData.java
new file mode 100644
index 000000000..a87dd1adb
--- /dev/null
+++ b/src/main/java/subway/DummyData.java
@@ -0,0 +1,36 @@
+package subway;
+
+import java.util.Arrays;
+import subway.domain.line.domain.Line;
+import subway.domain.line.domain.LineRepository;
+import subway.domain.station.domain.Station;
+import subway.domain.station.domain.StationRepository;
+
+public class DummyData {
+
+ public static void load() {
+ Station station1 = Station.from("교대역");
+ Station station2 = Station.from("강남역");
+ Station station3 = Station.from("역삼역");
+ Station station4 = Station.from("남부터미널역");
+ Station station5 = Station.from("양재역");
+ Station station6 = Station.from("양재시민의숲역");
+ Station station7 = Station.from("매봉역");
+
+ StationRepository.saveAll(
+ Arrays.asList(station1, station2, station3, station4, station5, station6, station7)
+ );
+
+ Line line1 = Line.of("2호선", station1, station2, 2, 3);
+ line1.addSection(station3, 2, 3);
+
+ Line line2 = Line.of("3호선", station1, station4, 3, 2);
+ line2.addSection(station5, 6, 5);
+ line2.addSection(station7, 1, 1);
+
+ Line line3 = Line.of("신분당선", station2, station5, 2, 8);
+ line3.addSection(station6, 10, 3);
+
+ LineRepository.saveAll(Arrays.asList(line1, line2, line3));
+ }
+}
diff --git a/src/main/java/subway/HandlerMapping.java b/src/main/java/subway/HandlerMapping.java
new file mode 100644
index 000000000..020fe47b7
--- /dev/null
+++ b/src/main/java/subway/HandlerMapping.java
@@ -0,0 +1,25 @@
+package subway;
+
+import subway.view.screen.ScreenManager;
+import subway.view.screen.action.path.ShortestDistancePathActionScreen;
+import subway.view.screen.action.path.ShortestTimePathActionScreen;
+
+public class HandlerMapping {
+
+ public static void mapping(CategoryType categoryType, ActionType actionType) {
+ if (categoryType == CategoryType.STATION) {
+ pathMapping(categoryType, actionType);
+ }
+ }
+
+ private static void pathMapping(CategoryType categoryType, ActionType actionType) {
+ if (actionType == ActionType.SHORTEST_DISTANCE_PATH) {
+ ScreenManager.push(new ShortestDistancePathActionScreen(categoryType));
+ return;
+ }
+
+ if (actionType == ActionType.SHORTEST_TIME_PATH) {
+ ScreenManager.push(new ShortestTimePathActionScreen(categoryType));
+ }
+ }
+}
diff --git a/src/main/java/subway/domain/Line.java b/src/main/java/subway/domain/Line.java
deleted file mode 100644
index f4d738d5a..000000000
--- a/src/main/java/subway/domain/Line.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package subway.domain;
-
-public class Line {
- private String name;
-
- public Line(String name) {
- this.name = name;
- }
-
- public String getName() {
- return name;
- }
-
- // 추가 기능 구현
-}
diff --git a/src/main/java/subway/domain/LineRepository.java b/src/main/java/subway/domain/LineRepository.java
deleted file mode 100644
index 2c4a723c9..000000000
--- a/src/main/java/subway/domain/LineRepository.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package subway.domain;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Objects;
-
-public class LineRepository {
- private static final List lines = new ArrayList<>();
-
- public static List lines() {
- return Collections.unmodifiableList(lines);
- }
-
- public static void addLine(Line line) {
- lines.add(line);
- }
-
- public static boolean deleteLineByName(String name) {
- return lines.removeIf(line -> Objects.equals(line.getName(), name));
- }
-
- public static void deleteAll() {
- lines.clear();
- }
-}
diff --git a/src/main/java/subway/domain/Path/domain/PathRepository.java b/src/main/java/subway/domain/Path/domain/PathRepository.java
new file mode 100644
index 000000000..e66049b2c
--- /dev/null
+++ b/src/main/java/subway/domain/Path/domain/PathRepository.java
@@ -0,0 +1,31 @@
+package subway.domain.Path.domain;
+
+import org.jgrapht.graph.DefaultWeightedEdge;
+import subway.domain.station.domain.Station;
+
+public class PathRepository {
+
+ private static final WeightGraph distanceWeightGraph = new WeightGraph();
+ private static final WeightGraph timeWeightGraph = new WeightGraph();
+
+ public static WeightGraph getDistanceWeightGraph() {
+ return distanceWeightGraph;
+ }
+
+ public static WeightGraph getTimeWeightGraph() {
+ return timeWeightGraph;
+ }
+
+ public static DefaultWeightedEdge addDistanceWeightEdge(Station sourceStation, Station targetStation, double weight) {
+ return distanceWeightGraph.addEdge(sourceStation, targetStation, weight);
+ }
+
+ public static DefaultWeightedEdge addTimeWeightEdge(Station sourceStation, Station targetStation, double weight) {
+ return timeWeightGraph.addEdge(sourceStation, targetStation, weight);
+ }
+
+ public static void addVertex(Station station) {
+ distanceWeightGraph.addVertex(station);
+ timeWeightGraph.addVertex(station);
+ }
+}
diff --git a/src/main/java/subway/domain/Path/domain/WeightGraph.java b/src/main/java/subway/domain/Path/domain/WeightGraph.java
new file mode 100644
index 000000000..19222e322
--- /dev/null
+++ b/src/main/java/subway/domain/Path/domain/WeightGraph.java
@@ -0,0 +1,35 @@
+package subway.domain.Path.domain;
+
+import org.jgrapht.alg.shortestpath.DijkstraShortestPath;
+import org.jgrapht.graph.DefaultWeightedEdge;
+import org.jgrapht.graph.WeightedMultigraph;
+import subway.domain.station.domain.Station;
+
+public class WeightGraph {
+
+ private final WeightedMultigraph graph = new WeightedMultigraph(DefaultWeightedEdge.class);
+
+ public DefaultWeightedEdge addEdge(Station sourceStation, Station targetStation, double weight) {
+ graph.addVertex(targetStation);
+ DefaultWeightedEdge edge = graph.addEdge(sourceStation, targetStation);
+ graph.setEdgeWeight(edge, weight);
+
+ return edge;
+ }
+
+ public void addVertex(Station station) {
+ graph.addVertex(station);
+ }
+
+ public double getEdgeWeight(DefaultWeightedEdge edge) {
+ return graph.getEdgeWeight(edge);
+ }
+
+ public void setEdgeWeight(DefaultWeightedEdge edge, double weight) {
+ graph.setEdgeWeight(edge, weight);
+ }
+
+ public DijkstraShortestPath getDijkstraShortestPath() {
+ return new DijkstraShortestPath(graph);
+ }
+}
diff --git a/src/main/java/subway/domain/Path/dto/PathResponseDto.java b/src/main/java/subway/domain/Path/dto/PathResponseDto.java
new file mode 100644
index 000000000..7949e53e0
--- /dev/null
+++ b/src/main/java/subway/domain/Path/dto/PathResponseDto.java
@@ -0,0 +1,23 @@
+package subway.domain.Path.dto;
+
+import java.util.List;
+import subway.domain.station.dto.StationResponseDto;
+
+public class PathResponseDto {
+
+ final List stations;
+ final double totalWeight;
+
+ public PathResponseDto(List stations, double totalWeight) {
+ this.stations = stations;
+ this.totalWeight = totalWeight;
+ }
+
+ public List getStations() {
+ return stations;
+ }
+
+ public double getTotalWeight() {
+ return totalWeight;
+ }
+}
diff --git a/src/main/java/subway/domain/Path/exception/NotConnectedException.java b/src/main/java/subway/domain/Path/exception/NotConnectedException.java
new file mode 100644
index 000000000..36e6af9ef
--- /dev/null
+++ b/src/main/java/subway/domain/Path/exception/NotConnectedException.java
@@ -0,0 +1,10 @@
+package subway.domain.Path.exception;
+
+public class NotConnectedException extends IllegalArgumentException {
+
+ private static final String MESSAGE = "출발역과 도착역이 연결되어 있지 않습니다. (입력 값: '%s, %s')";
+
+ public NotConnectedException(final String start, final String end) {
+ super(String.format(MESSAGE, start, end));
+ }
+}
diff --git a/src/main/java/subway/domain/Path/exception/SameStartAndEndStationException.java b/src/main/java/subway/domain/Path/exception/SameStartAndEndStationException.java
new file mode 100644
index 000000000..c9784bfd4
--- /dev/null
+++ b/src/main/java/subway/domain/Path/exception/SameStartAndEndStationException.java
@@ -0,0 +1,10 @@
+package subway.domain.Path.exception;
+
+public class SameStartAndEndStationException extends IllegalArgumentException {
+
+ private static final String MESSAGE = "출발역과 도착역은 달라야 합니다. (입력 값: '%s, %s')";
+
+ public SameStartAndEndStationException(final String start, final String end) {
+ super(String.format(MESSAGE, start, end));
+ }
+}
diff --git a/src/main/java/subway/domain/Path/service/PathService.java b/src/main/java/subway/domain/Path/service/PathService.java
new file mode 100644
index 000000000..3df3131c7
--- /dev/null
+++ b/src/main/java/subway/domain/Path/service/PathService.java
@@ -0,0 +1,43 @@
+package subway.domain.Path.service;
+
+import org.jgrapht.GraphPath;
+import subway.domain.Path.domain.PathRepository;
+import subway.domain.Path.domain.WeightGraph;
+import subway.domain.Path.dto.PathResponseDto;
+import subway.domain.Path.exception.NotConnectedException;
+import subway.domain.Path.exception.SameStartAndEndStationException;
+import subway.domain.station.domain.Station;
+import subway.domain.station.domain.StationRepository;
+
+public class PathService {
+
+ public static PathResponseDto getShortestDistanceGraphPath(String sourceStationName,
+ String targetStationName) {
+ return getShortestRoute(PathRepository.getTimeWeightGraph(), sourceStationName,
+ targetStationName);
+ }
+
+ public static PathResponseDto getShortestTimeGraphPath(String sourceStationName,
+ String targetStationName) {
+ return getShortestRoute(PathRepository.getDistanceWeightGraph(), sourceStationName,
+ targetStationName);
+ }
+
+ private static PathResponseDto getShortestRoute(WeightGraph weightGraph,
+ String sourceStationName, String targetStationName) {
+ if (sourceStationName.equals(targetStationName)) {
+ throw new SameStartAndEndStationException(sourceStationName, targetStationName);
+ }
+
+ Station sourceStation = StationRepository.findByName(sourceStationName);
+ Station targetStation = StationRepository.findByName(targetStationName);
+
+ try {
+ GraphPath graphPath = weightGraph.getDijkstraShortestPath()
+ .getPath(sourceStation, targetStation);
+ return new PathResponseDto(graphPath.getEdgeList(), graphPath.getWeight());
+ } catch (IllegalArgumentException e) {
+ throw new NotConnectedException(sourceStationName, targetStationName);
+ }
+ }
+}
diff --git a/src/main/java/subway/domain/Station.java b/src/main/java/subway/domain/Station.java
deleted file mode 100644
index bdb142590..000000000
--- a/src/main/java/subway/domain/Station.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package subway.domain;
-
-public class Station {
- private String name;
-
- public Station(String name) {
- this.name = name;
- }
-
- public String getName() {
- return name;
- }
-
- // 추가 기능 구현
-}
diff --git a/src/main/java/subway/domain/StationRepository.java b/src/main/java/subway/domain/StationRepository.java
deleted file mode 100644
index 8ed9d103f..000000000
--- a/src/main/java/subway/domain/StationRepository.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package subway.domain;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Objects;
-
-public class StationRepository {
- private static final List stations = new ArrayList<>();
-
- public static List stations() {
- return Collections.unmodifiableList(stations);
- }
-
- public static void addStation(Station station) {
- stations.add(station);
- }
-
- public static boolean deleteStation(String name) {
- return stations.removeIf(station -> Objects.equals(station.getName(), name));
- }
-
- public static void deleteAll() {
- stations.clear();
- }
-}
diff --git a/src/main/java/subway/domain/line/domain/Line.java b/src/main/java/subway/domain/line/domain/Line.java
new file mode 100644
index 000000000..074919e8a
--- /dev/null
+++ b/src/main/java/subway/domain/line/domain/Line.java
@@ -0,0 +1,82 @@
+package subway.domain.line.domain;
+
+import java.util.List;
+import java.util.Objects;
+import subway.domain.line.exception.DuplicateStationNameInLineException;
+import subway.domain.line.exception.ShorterThanMinLineNameException;
+import subway.domain.line.exception.UpstreamDownstreamStationInputException;
+import subway.domain.station.domain.Station;
+
+public class Line {
+
+ public static final int MIN_NAME_SIZE = 2;
+
+ private final String name;
+ private final LineStations lineStations;
+
+ private Line(String name, LineStations lineStations) {
+ this.name = name;
+ this.lineStations = lineStations;
+ }
+
+ public static Line of(String name, Station upstreamStation, Station downstreamStation,
+ double distance, double time) {
+ checkAddLineValidation(name, upstreamStation, downstreamStation);
+ LineStations lineStations = LineStations.of(upstreamStation, downstreamStation, distance, time);
+
+ return new Line(name, lineStations);
+ }
+
+ private static void checkAddLineValidation(String name, Station upstreamStation,
+ Station downstreamStation) {
+ if (name.length() < MIN_NAME_SIZE) {
+ throw new ShorterThanMinLineNameException(name);
+ }
+
+ if (upstreamStation.equals(downstreamStation)) {
+ throw new UpstreamDownstreamStationInputException(upstreamStation.getName(),
+ downstreamStation.getName());
+ }
+ }
+
+ public boolean contains(Station target) {
+ return lineStations.contains(target);
+ }
+
+ public void addSection(Station station, double distance, double time) {
+ if (lineStations.contains(station)) {
+ throw new DuplicateStationNameInLineException(name, station.getName());
+ }
+
+ lineStations.add(station, distance, time);
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public List getLineStations() {
+ return lineStations.getLineStations();
+ }
+
+ public Station getLastDownstreamStation() {
+ return lineStations.getLastDownstreamStation();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ Line line = (Line) o;
+ return Objects.equals(name, line.name);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(name);
+ }
+}
diff --git a/src/main/java/subway/domain/line/domain/LineRepository.java b/src/main/java/subway/domain/line/domain/LineRepository.java
new file mode 100644
index 000000000..5c63dca49
--- /dev/null
+++ b/src/main/java/subway/domain/line/domain/LineRepository.java
@@ -0,0 +1,45 @@
+package subway.domain.line.domain;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import subway.domain.line.exception.CannotFindLineByNameException;
+import subway.domain.line.exception.DuplicateLineNameException;
+
+public class LineRepository {
+
+ private static final List lines = new ArrayList<>();
+
+ public static List findAll() {
+ return Collections.unmodifiableList(lines);
+ }
+
+ public static Line findByName(String name) {
+ return lines.stream()
+ .filter(line -> line.getName().equals(name))
+ .findAny()
+ .orElseThrow(() -> {
+ throw new CannotFindLineByNameException(name);
+ });
+ }
+
+ public static void save(Line line) {
+ if (lines.contains(line)) {
+ throw new DuplicateLineNameException(line.getName());
+ }
+
+ lines.add(line);
+ }
+
+ public static void saveAll(List lines) {
+ lines.forEach(LineRepository::save);
+ }
+
+ public static void delete(Line line) {
+ lines.remove(line);
+ }
+
+ public static void deleteAll() {
+ lines.clear();
+ }
+}
diff --git a/src/main/java/subway/domain/line/domain/LineStation.java b/src/main/java/subway/domain/line/domain/LineStation.java
new file mode 100644
index 000000000..35f602c79
--- /dev/null
+++ b/src/main/java/subway/domain/line/domain/LineStation.java
@@ -0,0 +1,66 @@
+package subway.domain.line.domain;
+
+import org.jgrapht.graph.DefaultWeightedEdge;
+import subway.domain.Path.domain.PathRepository;
+import subway.domain.station.domain.Station;
+
+public class LineStation {
+
+ private final Station station;
+ private Station prevStation;
+ private DefaultWeightedEdge distanceWeightEdge;
+ private DefaultWeightedEdge timeWeightEdge;
+
+ public LineStation(Station station, Station prevStation,
+ DefaultWeightedEdge distanceWeightEdge, DefaultWeightedEdge timeWeightEdge) {
+ this.station = station;
+ this.prevStation = prevStation;
+ this.distanceWeightEdge = distanceWeightEdge;
+ this.timeWeightEdge = timeWeightEdge;
+ }
+
+ public static LineStation from(Station station) {
+ return new LineStation(station, null, null, null);
+ }
+
+ public static LineStation of(Station station, Station prevStation,
+ DefaultWeightedEdge distanceWeightEdge, DefaultWeightedEdge timeWeightEdge) {
+ return new LineStation(station, prevStation, distanceWeightEdge, timeWeightEdge);
+ }
+
+ public boolean isFirst() {
+ return prevStation == null;
+ }
+
+ public String getName() {
+ return station.getName();
+ }
+
+ public Station getStation() {
+ return station;
+ }
+
+ public Station getPrevStation() {
+ return prevStation;
+ }
+
+ public void setPrevStation(Station station) {
+ prevStation = station;
+ }
+
+ public double getDistance(double distance) {
+ return PathRepository.getDistanceWeightGraph().getEdgeWeight(distanceWeightEdge);
+ }
+
+ public void setDistance(double distance) {
+ PathRepository.getDistanceWeightGraph().setEdgeWeight(distanceWeightEdge, distance);
+ }
+
+ public double getTime(double time) {
+ return PathRepository.getTimeWeightGraph().getEdgeWeight(timeWeightEdge);
+ }
+
+ public void setTime(double time) {
+ PathRepository.getTimeWeightGraph().setEdgeWeight(timeWeightEdge, time);
+ }
+}
diff --git a/src/main/java/subway/domain/line/domain/LineStations.java b/src/main/java/subway/domain/line/domain/LineStations.java
new file mode 100644
index 000000000..2d2373293
--- /dev/null
+++ b/src/main/java/subway/domain/line/domain/LineStations.java
@@ -0,0 +1,71 @@
+package subway.domain.line.domain;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.jgrapht.graph.DefaultWeightedEdge;
+import subway.domain.Path.domain.PathRepository;
+import subway.domain.station.domain.Station;
+
+public class LineStations {
+
+ private final List lineStations;
+
+ private LineStations(List lineStations) {
+ this.lineStations = lineStations;
+ }
+
+ public static LineStations from() {
+ return new LineStations(new ArrayList<>());
+ }
+
+ public static LineStations of(List lineStations) {
+ return new LineStations(lineStations);
+ }
+
+ public static LineStations of(Station upstreamStation, Station downstreamStation,
+ double distance, double time) {
+ LineStations lineStations = from();
+ lineStations.addFirstStation(upstreamStation);
+ lineStations.add(downstreamStation, distance, time);
+
+ return lineStations;
+ }
+
+ public boolean contains(Station targetStation) {
+ for (LineStation lineStation : lineStations) {
+ if (lineStation.getStation().equals(targetStation)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public int size() {
+ return lineStations.size();
+ }
+
+ public void addFirstStation(Station station) {
+ PathRepository.addVertex(station);
+ lineStations.add(LineStation.from(station));
+ }
+
+ public void add(Station station, double distance, double time) {
+ Station lastDownstreamStation = getLastDownstreamStation();
+ DefaultWeightedEdge distanceWeightEdge = PathRepository
+ .addDistanceWeightEdge(lastDownstreamStation, station, time);
+ DefaultWeightedEdge timeWeightEdge = PathRepository
+ .addTimeWeightEdge(lastDownstreamStation, station, distance);
+ LineStation newLineStation = LineStation
+ .of(station, lastDownstreamStation, distanceWeightEdge, timeWeightEdge);
+ lineStations.add(newLineStation);
+ }
+
+ public List getLineStations() {
+ return lineStations;
+ }
+
+ public Station getLastDownstreamStation() {
+ return lineStations.get(lineStations.size() - 1).getStation();
+ }
+}
diff --git a/src/main/java/subway/domain/line/dto/LineRequestDto.java b/src/main/java/subway/domain/line/dto/LineRequestDto.java
new file mode 100644
index 000000000..d5d775703
--- /dev/null
+++ b/src/main/java/subway/domain/line/dto/LineRequestDto.java
@@ -0,0 +1,39 @@
+package subway.domain.line.dto;
+
+public class LineRequestDto {
+
+ private final String name;
+ private final String upstreamStationName;
+ private final String downstreamStationName;
+ private final double distance;
+ private final double time;
+
+ public LineRequestDto(String name, String upstreamStationName,
+ String downstreamStationName, double distance, double time) {
+ this.name = name;
+ this.upstreamStationName = upstreamStationName;
+ this.downstreamStationName = downstreamStationName;
+ this.distance = distance;
+ this.time = time;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getUpstreamStationName() {
+ return upstreamStationName;
+ }
+
+ public String getDownstreamStationName() {
+ return downstreamStationName;
+ }
+
+ public double getDistance() {
+ return distance;
+ }
+
+ public double getTime() {
+ return time;
+ }
+}
diff --git a/src/main/java/subway/domain/line/dto/LineStationResponseDto.java b/src/main/java/subway/domain/line/dto/LineStationResponseDto.java
new file mode 100644
index 000000000..fc3814cbe
--- /dev/null
+++ b/src/main/java/subway/domain/line/dto/LineStationResponseDto.java
@@ -0,0 +1,40 @@
+package subway.domain.line.dto;
+
+import java.util.List;
+import java.util.stream.Collectors;
+import subway.domain.line.domain.LineStation;
+
+public class LineStationResponseDto {
+
+ private final String stationName;
+ private final String prevStationName;
+
+ public LineStationResponseDto(String stationName, String prevStationName) {
+ this.stationName = stationName;
+ this.prevStationName = prevStationName;
+ }
+
+ public static LineStationResponseDto of(LineStation entity) {
+ String stationName = null;
+ if (!entity.isFirst()) {
+ stationName = entity.getPrevStation().getName();
+ }
+
+ return new LineStationResponseDto(
+ entity.getStation().getName(), stationName);
+ }
+
+ public static List of(List entities) {
+ return entities.stream()
+ .map(LineStationResponseDto::of)
+ .collect(Collectors.toList());
+ }
+
+ public String getStationName() {
+ return stationName;
+ }
+
+ public String getPrevStationName() {
+ return prevStationName;
+ }
+}
diff --git a/src/main/java/subway/domain/line/dto/SectionInsertRequestDto.java b/src/main/java/subway/domain/line/dto/SectionInsertRequestDto.java
new file mode 100644
index 000000000..b699f6ea5
--- /dev/null
+++ b/src/main/java/subway/domain/line/dto/SectionInsertRequestDto.java
@@ -0,0 +1,33 @@
+package subway.domain.line.dto;
+
+public class SectionInsertRequestDto {
+
+ private final String lineName;
+ private final String stationName;
+ private final double distance;
+ private final double time;
+
+ public SectionInsertRequestDto(String lineName, String stationName, double distance,
+ double time) {
+ this.lineName = lineName;
+ this.stationName = stationName;
+ this.distance = distance;
+ this.time = time;
+ }
+
+ public String getLineName() {
+ return lineName;
+ }
+
+ public String getStationName() {
+ return stationName;
+ }
+
+ public double getDistance() {
+ return distance;
+ }
+
+ public double getTime() {
+ return time;
+ }
+}
diff --git a/src/main/java/subway/domain/line/exception/CannotFindLineByNameException.java b/src/main/java/subway/domain/line/exception/CannotFindLineByNameException.java
new file mode 100644
index 000000000..9e7c0a2fa
--- /dev/null
+++ b/src/main/java/subway/domain/line/exception/CannotFindLineByNameException.java
@@ -0,0 +1,10 @@
+package subway.domain.line.exception;
+
+public class CannotFindLineByNameException extends RuntimeException {
+
+ private static final String MESSAGE = "등록되지 않은 지하철 노선 입니다. (입력 값: '%s')";
+
+ public CannotFindLineByNameException(final String input) {
+ super(String.format(MESSAGE, input));
+ }
+}
diff --git a/src/main/java/subway/domain/line/exception/DuplicateLineNameException.java b/src/main/java/subway/domain/line/exception/DuplicateLineNameException.java
new file mode 100644
index 000000000..351fc1a6e
--- /dev/null
+++ b/src/main/java/subway/domain/line/exception/DuplicateLineNameException.java
@@ -0,0 +1,10 @@
+package subway.domain.line.exception;
+
+public class DuplicateLineNameException extends IllegalArgumentException {
+
+ private static final String MESSAGE = "이미 존재하는 노선 이름입니다. (입력 값: '%s')";
+
+ public DuplicateLineNameException(final String input) {
+ super(String.format(MESSAGE, input));
+ }
+}
diff --git a/src/main/java/subway/domain/line/exception/DuplicateStationNameInLineException.java b/src/main/java/subway/domain/line/exception/DuplicateStationNameInLineException.java
new file mode 100644
index 000000000..9f984f414
--- /dev/null
+++ b/src/main/java/subway/domain/line/exception/DuplicateStationNameInLineException.java
@@ -0,0 +1,10 @@
+package subway.domain.line.exception;
+
+public class DuplicateStationNameInLineException extends IllegalArgumentException {
+
+ private static final String MESSAGE = "%s 노선에 이미 존재하는 역입니다. (입력 값: '%s')";
+
+ public DuplicateStationNameInLineException(final String lineName, final String stationName) {
+ super(String.format(MESSAGE, lineName, stationName));
+ }
+}
diff --git a/src/main/java/subway/domain/line/exception/ShorterThanMinLineNameException.java b/src/main/java/subway/domain/line/exception/ShorterThanMinLineNameException.java
new file mode 100644
index 000000000..a55e1b3de
--- /dev/null
+++ b/src/main/java/subway/domain/line/exception/ShorterThanMinLineNameException.java
@@ -0,0 +1,13 @@
+package subway.domain.line.exception;
+
+import subway.domain.line.domain.Line;
+
+public class ShorterThanMinLineNameException extends IllegalArgumentException {
+
+ private static final String MESSAGE = "지하철 노선 이름은 " + Line.MIN_NAME_SIZE
+ + "글자 이상이어야 합니다. (입력 값: '%s')";
+
+ public ShorterThanMinLineNameException(final String input) {
+ super(String.format(MESSAGE, input));
+ }
+}
diff --git a/src/main/java/subway/domain/line/exception/UpstreamDownstreamStationInputException.java b/src/main/java/subway/domain/line/exception/UpstreamDownstreamStationInputException.java
new file mode 100644
index 000000000..93a7067fa
--- /dev/null
+++ b/src/main/java/subway/domain/line/exception/UpstreamDownstreamStationInputException.java
@@ -0,0 +1,11 @@
+package subway.domain.line.exception;
+
+public class UpstreamDownstreamStationInputException extends IllegalArgumentException {
+
+ private static final String MESSAGE = "상행 종점역과 하행 종점역이 같을 수는 없습니다. (입력 값: '%s', '%s')";
+
+ public UpstreamDownstreamStationInputException(final String upstreamStation,
+ final String downstreamStation) {
+ super(String.format(MESSAGE, upstreamStation, downstreamStation));
+ }
+}
diff --git a/src/main/java/subway/domain/line/service/LineService.java b/src/main/java/subway/domain/line/service/LineService.java
new file mode 100644
index 000000000..3c6ab2a7d
--- /dev/null
+++ b/src/main/java/subway/domain/line/service/LineService.java
@@ -0,0 +1,24 @@
+package subway.domain.line.service;
+
+import subway.domain.line.domain.Line;
+import subway.domain.line.domain.LineRepository;
+import subway.domain.line.dto.LineRequestDto;
+import subway.domain.station.domain.Station;
+import subway.domain.station.domain.StationRepository;
+
+public class LineService {
+
+ public static void save(LineRequestDto requestDto) {
+ Station upstreamStation = StationRepository.findByName(requestDto.getUpstreamStationName());
+ Station downstreamStation = StationRepository.findByName(requestDto.getDownstreamStationName());
+ Line newLine = Line
+ .of(requestDto.getName(), upstreamStation, downstreamStation,
+ requestDto.getDistance(), requestDto.getTime());
+ LineRepository.save(newLine);
+ }
+
+ public static boolean contains(Station targetStation) {
+ return LineRepository.findAll().stream()
+ .anyMatch(line -> line.contains(targetStation));
+ }
+}
diff --git a/src/main/java/subway/domain/line/service/LineStationService.java b/src/main/java/subway/domain/line/service/LineStationService.java
new file mode 100644
index 000000000..15d422394
--- /dev/null
+++ b/src/main/java/subway/domain/line/service/LineStationService.java
@@ -0,0 +1,16 @@
+package subway.domain.line.service;
+
+import subway.domain.line.domain.Line;
+import subway.domain.line.domain.LineRepository;
+import subway.domain.line.dto.SectionInsertRequestDto;
+import subway.domain.station.domain.Station;
+import subway.domain.station.domain.StationRepository;
+
+public class LineStationService {
+
+ public static void addSection(SectionInsertRequestDto requestDto) {
+ Line line = LineRepository.findByName(requestDto.getLineName());
+ Station station = StationRepository.findByName(requestDto.getStationName());
+ line.addSection(station, requestDto.getDistance(), requestDto.getTime());
+ }
+}
diff --git a/src/main/java/subway/domain/station/domain/Station.java b/src/main/java/subway/domain/station/domain/Station.java
new file mode 100644
index 000000000..5e67e341c
--- /dev/null
+++ b/src/main/java/subway/domain/station/domain/Station.java
@@ -0,0 +1,44 @@
+package subway.domain.station.domain;
+
+import java.util.Objects;
+import subway.domain.station.exception.ShorterThanMinStationNameException;
+
+public class Station {
+
+ public static final int MIN_NAME_SIZE = 2;
+
+ private final String name;
+
+ private Station(String name) {
+ this.name = name;
+ }
+
+ public static Station from(String name) {
+ if (name.length() < MIN_NAME_SIZE) {
+ throw new ShorterThanMinStationNameException(name);
+ }
+
+ return new Station(name);
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ Station station = (Station) o;
+ return Objects.equals(name, station.name);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(name);
+ }
+}
diff --git a/src/main/java/subway/domain/station/domain/StationRepository.java b/src/main/java/subway/domain/station/domain/StationRepository.java
new file mode 100644
index 000000000..8c99c4368
--- /dev/null
+++ b/src/main/java/subway/domain/station/domain/StationRepository.java
@@ -0,0 +1,44 @@
+package subway.domain.station.domain;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import subway.domain.station.exception.CannotFindStationByNameException;
+import subway.domain.station.exception.DuplicateStationNameException;
+
+public class StationRepository {
+ private static final List stations = new ArrayList<>();
+
+ public static List findAll() {
+ return Collections.unmodifiableList(stations);
+ }
+
+ public static Station findByName(String name) {
+ return stations.stream()
+ .filter(station -> station.getName().equals(name))
+ .findAny()
+ .orElseThrow(() -> {
+ throw new CannotFindStationByNameException(name);
+ });
+ }
+
+ public static void save(Station station) {
+ if (stations.contains(station)) {
+ throw new DuplicateStationNameException(station.getName());
+ }
+
+ stations.add(station);
+ }
+
+ public static void saveAll(List stations) {
+ stations.forEach(StationRepository::save);
+ }
+
+ public static void delete(Station station) {
+ stations.remove(station);
+ }
+
+ public static void deleteAll() {
+ stations.clear();
+ }
+}
diff --git a/src/main/java/subway/domain/station/dto/StationRequestDto.java b/src/main/java/subway/domain/station/dto/StationRequestDto.java
new file mode 100644
index 000000000..be191fac2
--- /dev/null
+++ b/src/main/java/subway/domain/station/dto/StationRequestDto.java
@@ -0,0 +1,14 @@
+package subway.domain.station.dto;
+
+public class StationRequestDto {
+
+ private final String name;
+
+ public StationRequestDto(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+}
diff --git a/src/main/java/subway/domain/station/dto/StationResponseDto.java b/src/main/java/subway/domain/station/dto/StationResponseDto.java
new file mode 100644
index 000000000..45dec2ced
--- /dev/null
+++ b/src/main/java/subway/domain/station/dto/StationResponseDto.java
@@ -0,0 +1,28 @@
+package subway.domain.station.dto;
+
+import java.util.List;
+import java.util.stream.Collectors;
+import subway.domain.station.domain.Station;
+
+public class StationResponseDto {
+
+ private final String name;
+
+ public StationResponseDto(String name) {
+ this.name = name;
+ }
+
+ public static StationResponseDto of(Station entity) {
+ return new StationResponseDto(entity.getName());
+ }
+
+ public static List of(List entities) {
+ return entities.stream()
+ .map(StationResponseDto::of)
+ .collect(Collectors.toList());
+ }
+
+ public String getName() {
+ return name;
+ }
+}
diff --git a/src/main/java/subway/domain/station/exception/CannotFindStationByNameException.java b/src/main/java/subway/domain/station/exception/CannotFindStationByNameException.java
new file mode 100644
index 000000000..e31fe5a0e
--- /dev/null
+++ b/src/main/java/subway/domain/station/exception/CannotFindStationByNameException.java
@@ -0,0 +1,10 @@
+package subway.domain.station.exception;
+
+public class CannotFindStationByNameException extends RuntimeException {
+
+ private static final String MESSAGE = "등록되지 않은 지하철 역 입니다. (입력 값: '%s')";
+
+ public CannotFindStationByNameException(final String input) {
+ super(String.format(MESSAGE, input));
+ }
+}
diff --git a/src/main/java/subway/domain/station/exception/DuplicateStationNameException.java b/src/main/java/subway/domain/station/exception/DuplicateStationNameException.java
new file mode 100644
index 000000000..1b8f3bd71
--- /dev/null
+++ b/src/main/java/subway/domain/station/exception/DuplicateStationNameException.java
@@ -0,0 +1,10 @@
+package subway.domain.station.exception;
+
+public class DuplicateStationNameException extends IllegalArgumentException {
+
+ private static final String MESSAGE = "이미 존재하는 역 이름입니다. (입력 값: '%s')";
+
+ public DuplicateStationNameException(final String input) {
+ super(String.format(MESSAGE, input));
+ }
+}
diff --git a/src/main/java/subway/domain/station/exception/ShorterThanMinStationNameException.java b/src/main/java/subway/domain/station/exception/ShorterThanMinStationNameException.java
new file mode 100644
index 000000000..0d17ba6e3
--- /dev/null
+++ b/src/main/java/subway/domain/station/exception/ShorterThanMinStationNameException.java
@@ -0,0 +1,13 @@
+package subway.domain.station.exception;
+
+import subway.domain.station.domain.Station;
+
+public class ShorterThanMinStationNameException extends IllegalArgumentException {
+
+ private static final String MESSAGE = "지하철 역 이름은 " + Station.MIN_NAME_SIZE
+ + "글자 이상이어야 합니다. (입력 값: '%s')";
+
+ public ShorterThanMinStationNameException(final String input) {
+ super(String.format(MESSAGE, input));
+ }
+}
diff --git a/src/main/java/subway/domain/station/service/StationService.java b/src/main/java/subway/domain/station/service/StationService.java
new file mode 100644
index 000000000..3144e64f7
--- /dev/null
+++ b/src/main/java/subway/domain/station/service/StationService.java
@@ -0,0 +1,13 @@
+package subway.domain.station.service;
+
+import subway.domain.station.domain.Station;
+import subway.domain.station.domain.StationRepository;
+import subway.domain.station.dto.StationRequestDto;
+
+public class StationService {
+
+ public static void save(StationRequestDto requestDto) {
+ Station newStation = Station.from(requestDto.getName());
+ StationRepository.save(newStation);
+ }
+}
diff --git a/src/main/java/subway/utils/ErrorUtils.java b/src/main/java/subway/utils/ErrorUtils.java
new file mode 100644
index 000000000..51b4cb017
--- /dev/null
+++ b/src/main/java/subway/utils/ErrorUtils.java
@@ -0,0 +1,30 @@
+package subway.utils;
+
+import java.util.function.Supplier;
+import subway.view.OutputView;
+import subway.view.screen.Screen;
+import subway.view.screen.ScreenManager;
+
+public class ErrorUtils {
+
+ public static Object repeatingUntilNoException(final Supplier