Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
177 changes: 96 additions & 81 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,114 +1,129 @@
# 🚇 지하철 노선도 미션

## URL

### https://yungo1846.github.io/javascript-subway-map-precourse/

## 디렉토리 구조

```bash
│ index.html
│ LICENSE
│ README.md
├─images
└─src
│ index.js
├─common
│ alertMessage.js
│ checkInput.js
│ clearInput.js
│ constant.js
├─event
│ │ menuEvent.js
│ │
│ ├─common
│ │ checkStorageItem.js
│ │
│ ├─line
│ │ addLineEvent.js
│ │ delLineEvent.js
│ │
│ ├─section
│ │ addSectionEvent.js
│ │ chooseLineEvent.js
│ │ delSectionEvent.js
│ │
│ └─station
│ addStationEvent.js
│ delStationEvent.js
└─render
render.js
renderLine.js
renderMapPrint.js
renderSection.js
renderStation.js
```

- common: 자주 사용되는 상수, 함수 등을 모아놓은 디렉토리
- event: eventHandle에 필요한 로직을 모아놓은 디렉토리
- render: 사용자에게 보이는 화면을 구성하는 로직을 모아놓은 디렉토리

## 🚀 기능 요구사항

### 지하철 역 관련 기능
- 지하철 역을 등록하고 삭제할 수 있다. (단, 노선에 등록된 역은 삭제할 수 없다)
- 중복된 지하철 역 이름이 등록될 수 없다.
- 지하철 역은 2글자 이상이어야 한다.
- 지하철 역의 목록을 조회할 수 있다.

- 지하철 역을 등록하고 삭제할 수 있다. (단, 노선에 등록된 역은 삭제할 수 없다) (완료)
- 중복된 지하철 역 이름이 등록될 수 없다. (완료)
- 지하철 역은 2글자 이상이어야 한다. (완료)
- 지하철 역의 목록을 조회할 수 있다. (완료)

### 지하철 노선 관련 기능
- 지하철 노선을 등록하고 삭제할 수 있다.
- 중복된 지하철 노선 이름이 등록될 수 없다.
- 노선 등록 시 상행 종점역과 하행 종점역을 입력받는다.
- 지하철 노선의 목록을 조회할 수 있다.

- 지하철 노선을 등록하고 삭제할 수 있다. (완료)
- 중복된 지하철 노선 이름이 등록될 수 없다. (완료)
- 노선 이름은 2글자 이상이어야 한다. (완료)
- 노선 등록 시 상행 종점역과 하행 종점역을 입력받는다. (완료)
- 지하철 노선의 목록을 조회할 수 있다. (완료)
- 노선에서 갈래길은 생길 수 없다. (상행선 혹은 하행선이 다른 노선과 겹치는 경우도 갈래길로 인정) (완료)

### 지하철 구간 추가 기능
- 지하철 노선에 구간을 추가하는 기능은 노선에 역을 추가하는 기능이라고도 할 수 있다.
- 역과 역사이를 구간이라 하고 이 구간들의 모음이 노선이다.
- 하나의 역은 여러개의 노선에 추가될 수 있다.
- 역과 역 사이에 새로운 역이 추가 될 수 있다.
- 노선에서 갈래길은 생길 수 없다.

- 지하철 노선에 구간을 추가하는 기능은 노선에 역을 추가하는 기능이라고도 할 수 있다. (완료)
- 역과 역사이를 구간이라 하고 이 구간들의 모음이 노선이다.
- 하나의 역은 여러개의 노선에 추가될 수 있다. (완료)
- 역과 역 사이에 새로운 역이 추가 될 수 있다. (완료)
- 노선에서 갈래길은 생길 수 없다. (상행선 혹은 하행선이 다른 노선과 겹치는 경우도 갈래길로 인정) (완료)
- 구간 순서는 어떤 숫자든 입력 받을 수 있다. (완료)
- 0보다 작은 숫자를 입력 받은 경우, 상행선(0번 순서)에 추가한다.
- 해당 노선 하행선 순서 보다 큰 숫자를 입력 받은 경우, 하행선에 추가한다.

<img width="500" src="/images/section1.png">

### 지하철 구간 삭제 기능
- 노선에 등록된 역을 제거할 수 있다.
- 종점을 제거할 경우 다음 역이 종점이 된다.
- 노선에 포함된 역이 두개 이하일 때는 역을 제거할 수 없다.

- 노선에 등록된 역을 제거할 수 있다. (완료)
- 종점을 제거할 경우 다음 역이 종점이 된다. (완료)
- 노선에 포함된 역이 두개 이하일 때는 역을 제거할 수 없다. (완료)

<img width="500" src="/images/section2.png">

### 지하철 노선에 등록된 역 조회 기능
- 노선의 상행 종점부터 하행 종점까지 연결된 순서대로 역 목록을 조회할 수 있다.

- 노선의 상행 종점부터 하행 종점까지 연결된 순서대로 역 목록을 조회할 수 있다. (완료)

## 🤔 고민사항

- 사용자 localStorage에 다른 프로그램에서 사용하고 있는 stations와 lines가 있는 경우, 어떻게 처리해야하는가?

후보1. localStorage의 stations와 lines를 초기화

=> 다른 프로그램에서 문제가 발생할 수 있음. 또한 초기화 시, 매번 페이지를 새로고침 할 때마다 초기화가 발생하기 때문에 localStorage 사용이유가 사라짐.

후보2. 다른 사람들이 절대 사용하지 않을 것 같은 네이밍을 사용한다.

=> 가장 쉬운 방법이지만 코드의 가독성을 위해 여기서는 사용하지 않았다.

<br/>

## 💻 프로그램 실행 결과

### 역관리

<img width="100%" src="/images/station_manager.gif">

### 노선관리

<img width="100%" src="/images/line_manager.gif">

### 구간관리

<img width="100%" src="/images/section_manager.gif">

### 노선도 출력
<img width="100%" src="/images/map_print_manager.gif">


## ✅ 프로그래밍 요구사항

### 메뉴 버튼
- 역 관리 button 태그는 `#station-manager-button` id값을 가진다.
- 노선 관리 button 태그는 `#line-manager-button` id값을 가진다.
- 구간 관리 button 태그는 `#section-manager-button` id값을 가진다.
- 지하철 노선도 출력 관리 button 태그는 `#map-print-manager-button` id값을 가진다.

### 지하철 역 관련 기능
- 지하철 역을 입력하는 input 태그는 `#station-name-input` id값을 가진다.
- 지하철 역을 추가하는 button 태그는 `#station-add-button` id값을 가진다.
- 지하철 역을 삭제하는 button 태그는 `.station-delete-button` class값을 가진다.

### 지하철 노선 관련 기능
- 지하철 노선의 이름을 입력하는 input 태그는 `#line-name-input` id값을 가진다.
- 지하철 노선의 상행 종점을 선택하는 select 태그는 `#line-start-station-selector` id값을 가진다.
- 지하철 노선의 하행 종점을 선택하는 select 태그는 `#line-end-station-selector` id값을 가진다.
- 지하철 노선을 추가하는 button 태그는 `#line-add-button` id값을 가진다.
- 지하철 노선을 삭제하는 button 태그는 `.line-delete-button` class값을 가진다.

### 지하철 구간 추가 기능
- 지하철 노선을 선택하는 button 태그는 `.section-line-menu-button` class값을 가진다.
- 지하철 구간을 설정할 역 select 태그는 `#section-station-selector` id값을 가진다.
- 지하철 구간의 순서를 입력하는 input 태그는 `#section-order-input` id값을 가진다.
- 지하철 구간을 등록하는 button 태그는 `#section-add-button` id값을 가진다.
- 지하철 구간을 제거하는 button 태그는 `.section-delete-button` class값을 가진다.

### 지하철 노선도 출력 기능
- 지하철 노선도 출력 버튼을 누르면 `<div class="map"></div>` 태그를 만들고 해당 태그 내부에 노선도를 출력한다.

### 기존 요구사항

- 사용자가 잘못된 입력 값을 작성한 경우 `alert`을 이용해 메시지를 보여주고, 재입력할 수 있게 한다.
- 외부 라이브러리(jQuery, Lodash 등)를 사용하지 않고, 순수 Vanilla JS로만 구현한다.
- **자바스크립트 코드 컨벤션을 지키면서 프로그래밍** 한다
- [https://google.github.io/styleguide/jsguide.html](https://google.github.io/styleguide/jsguide.html)
- [https://ui.toast.com/fe-guide/ko_CODING-CONVENSION/](https://ui.toast.com/fe-guide/ko_CODING-CONVENTION)
- **indent(인덴트, 들여쓰기) depth를 3이 넘지 않도록 구현한다. 2까지만 허용**한다.
- 예를 들어 while문 안에 if문이 있으면 들여쓰기는 2이다.
- 힌트: indent(인덴트, 들여쓰기) depth를 줄이는 좋은 방법은 함수(또는 메소드)를 분리하면 된다.
- **함수(또는 메소드)의 길이가 15라인을 넘어가지 않도록 구현한다.**
- 함수(또는 메소드)가 한 가지 일만 잘 하도록 구현한다.
- 변수 선언시 `var` 를 사용하지 않는다. `const` 와 `let` 을 사용한다.
- [const](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Statements/const)
- [let](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Statements/let)
- `import` 문을 이용해 스크립트를 모듈화하고 불러올 수 있게 만든다.
- [https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Statements/import](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Statements/import)
- `template literal`을 이용해 데이터와 html string을 가독성 좋게 표현한다.
- [https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Template_literals](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Template_literals)

### 추가된 요구사항
- [data](https://developer.mozilla.org/ko/docs/Learn/HTML/Howto/%EB%8D%B0%EC%9D%B4%ED%84%B0_%EC%86%8D%EC%84%B1_%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0)속성을 활용하여 html 태그에 역, 노선, 구간의 유일한 데이터 값들을 관리한다.
- [localStorage](https://developer.mozilla.org/ko/docs/Web/API/Window/localStorage)를 이용하여, 새로고침하더라도 가장 최근에 작업한 정보들을 불러올 수 있도록 한다.

<br/>

## 📝 미션 저장소 및 진행 요구사항

- 미션은 [https://github.com/woowacourse/javascript-subway-map-precours](https://github.com/woowacourse/javascript-subway-map-precourse) 저장소를 fork/clone해 시작한다.
- **기능을 구현하기 전에 javascript-subway-precourse/docs/README.md 파일에 구현할 기능 목록**을 정리해 추가한다.
- **git의 commit 단위는 앞 단계에서 README.md 파일에 정리한 기능 목록 단위로 추가**한다.
- [프리코스 과제 제출](https://github.com/woowacourse/woowacourse-docs/tree/master/precourse) 문서 절차를 따라 미션을 제출한다.
<img width="100%" src="/images/map_print_manager.gif">
44 changes: 43 additions & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,49 @@
</head>
<body>
<div id="app">
<h1>🚇 지하철 노선도 관리 </h1>
<h1>🚇 지하철 노선도 관리</h1>
<div class="menu-button">
<button id="station-manager-button">1. 역 관리</button>
<button id="line-manager-button">2. 노선 관리</button>
<button id="section-manager-button">3. 구간 관리</button>
<button id="map-print-manager-button">4. 지하철 노선도 출력</button>
</div>
</div>
<div id="main-container">
<div class="page" id="station-page" style="display: none;">
<h4>역 이름</h4>
<input type="text" id="station-name-input" placeholder="역 이름을 입력해주세요."></input>
<button id="station-add-button">역 추가</button>
<h2>🚉 지하철 역 목록</h2>
<div id="station-table-container"></div>
</div>
<div class="page" id="line-page" style="display: none;">
<h4>노선 이름</h4>
<h4><input type="text" id="line-name-input" placeholder="노선 이름을 입력해주세요."></input></h4>
<div>
<span>상행 종점</span>
<select class="line-station-selector" id="line-start-station-selector">
</select>
</div><br>
<div>
<span>하행 종점</span>
<select class="line-station-selector" id="line-end-station-selector">
</select>
</div><br>
<button id="line-add-button">노선 추가</button>
<h2>🚉 지하철 노선 목록</h2>
<div id="line-table-container"></div>
</div>
<div class="page" id="section-page" style="display: none;">
<h3>구간을 수정할 노선을 선택해주세요.</h3>
<div id="line-select-button-container">
</div>
<div id="section-input-container"></div>
<div id="section-table-container"></div>
</div>
<div class="page" id="map-print-page" style="display: none;">
<div id="map-print-container"></div>
</div>
</div>
<script type="module" src="src/index.js"></script>
</body>
Expand Down
14 changes: 14 additions & 0 deletions src/common/alertMessage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { constant } from "./constant.js";

export const alertMessage = {
SHORT_LENGTH_ERROR: `최소 ${constant.minLength} 글자 이상 입력해야 합니다.`,
ORDERING_INPUT_NOTHING_ERROR: `순서를 입력해주세요`,
DELETE_CHECK_MESSAGE: "정말로 삭제하시겠습니까?",
DELETE_STATION_ON_LINE_MESSAGE: "이 역은 현재 노선에 등록되어 있어 삭제가 불가능합니다.",
DELETE_STATIONS_ON_LINE_SHORTAGE_MESSAGE: `선택한 노선의 역이 ${constant.minStations}개 이하인 경우에는 삭제할 수 없습니다.`,
SAME_DESTINATION_ERROR: `상행과 하행이 같습니다. 다시 입력해주세요.`,
SAME_LINE_EXIST_ERROR: "이미 같은 이름을 가진 노선이 있습니다.",
SAME_STATION_EXIST_ERROR: "이미 같은 이름을 가진 역이 있습니다.",
SAME_SECTION_EXIST_ERROR: "이미 같은 구간이 등록되어 있습니다.",
TRANSFER_STATION_MESSAGE: `역은 다른 노선에 등록되어 있어 추가할 수 없습니다.\n "4. 지하철 노선도 출력"을 클릭하여 노선을 확인해주세요.\n📌 교차역이 없도록 하기 위함입니다. 📌`,
};
112 changes: 112 additions & 0 deletions src/common/checkInput.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { constant } from "./constant.js";

export function isSatisfyLength(len) {
let result = true;
if (String(len).length < constant.minLength) {
result = false;
}

return result;
}

export function isSameDestination(start, end) {
let result = true;
if (start !== end) {
result = false;
}

return result;
}

export function isStationAlreadyExist(stations, name) {
let result = false;
for (let i = 0; i < localStorage.stations.length; i++) {
if (name === stations[i]) {
result = true;
break;
}
}

return result;
}

export function isLineAlreadyExist(lines, name) {
let result = false;
for (let i = 0; i < lines.length; i++) {
if (name === lines[i].name) {
result = true;
break;
}
}

return result;
}

export function isSectionAlreadyExist(sections, name) {
let result = false;
for (let i = 0; i < sections.length; i++) {
if (name === sections[i]) {
result = true;
break;
}
}

return result;
}

function _isStationOnLine(sections, name) {
let result = false;
for (let i = 0; i < sections.length; i++) {
if (name === sections[i]) {
result = true;
break;
}
}

return result;
}

export function isStationOnLine(lines, name) {
let result = false;
for (let i = 0; i < lines.length; i++) {
if (_isStationOnLine(lines[i].sections, name)) {
result = true;
break;
}
}

return result;
}

export function isSatisfyMinNumOfStations(line) {
let result = false;
if (line.sections.length > constant.minStations) {
result = true;
}

return result;
}

export function _isTransferStation(sections, station) {
let result = false;
for (let i = 0; i < sections.length; i++) {
if (station === sections[i]) {
result = true;
break;
}
}

return result;
}

export function isTransferStation(lines, station) {
let result = false;
for (let i = 0; i < lines.length; i++) {
if (_isTransferStation(lines[i].sections, station)) {
result = true;
break;
}
}

return result;
}
4 changes: 4 additions & 0 deletions src/common/clearInput.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export function clearInput() {
const $input = document.querySelectorAll("input");
$input.forEach((input) => (input.value = ""));
}
4 changes: 4 additions & 0 deletions src/common/constant.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const constant = {
minLength: 2,
minStations: 2,
};
Loading