diff --git a/.eslinrc.json b/.eslinrc.json
new file mode 100644
index 000000000..984c2dc7b
--- /dev/null
+++ b/.eslinrc.json
@@ -0,0 +1,16 @@
+{
+ "plugins": ["prettier"],
+ "extends": ["eslint:recommended", "google", "plugin:prettier/recommended"],
+ "rules": {
+ "prettier/prettier": "error"
+ },
+ "env": {
+ "browser": true,
+ "node": true,
+ "es2020": true
+ },
+ "parserOptions": {
+ "ecmaVersion": 11,
+ "sourceType": "module"
+ }
+}
diff --git a/.prettierrc b/.prettierrc
new file mode 100644
index 000000000..598d33228
--- /dev/null
+++ b/.prettierrc
@@ -0,0 +1,7 @@
+{
+ "tabWidth": 2,
+ "useTabs": false,
+ "semi": true,
+ "bracketSpacing": true,
+ "printWidth": 80
+}
diff --git a/README.md b/README.md
index e97a1d649..eafa0409f 100644
--- a/README.md
+++ b/README.md
@@ -3,20 +3,23 @@
## ๐ ๊ธฐ๋ฅ ์๊ตฌ์ฌํญ
### ์งํ์ฒ ์ญ ๊ด๋ จ ๊ธฐ๋ฅ
+
- ์งํ์ฒ ์ญ์ ๋ฑ๋กํ๊ณ ์ญ์ ํ ์ ์๋ค. (๋จ, ๋
ธ์ ์ ๋ฑ๋ก๋ ์ญ์ ์ญ์ ํ ์ ์๋ค)
- ์ค๋ณต๋ ์งํ์ฒ ์ญ ์ด๋ฆ์ด ๋ฑ๋ก๋ ์ ์๋ค.
- ์งํ์ฒ ์ญ์ 2๊ธ์ ์ด์์ด์ด์ผ ํ๋ค.
- ์งํ์ฒ ์ญ์ ๋ชฉ๋ก์ ์กฐํํ ์ ์๋ค.
### ์งํ์ฒ ๋
ธ์ ๊ด๋ จ ๊ธฐ๋ฅ
+
- ์งํ์ฒ ๋
ธ์ ์ ๋ฑ๋กํ๊ณ ์ญ์ ํ ์ ์๋ค.
- ์ค๋ณต๋ ์งํ์ฒ ๋
ธ์ ์ด๋ฆ์ด ๋ฑ๋ก๋ ์ ์๋ค.
- ๋
ธ์ ๋ฑ๋ก ์ ์ํ ์ข
์ ์ญ๊ณผ ํํ ์ข
์ ์ญ์ ์
๋ ฅ๋ฐ๋๋ค.
- ์งํ์ฒ ๋
ธ์ ์ ๋ชฉ๋ก์ ์กฐํํ ์ ์๋ค.
### ์งํ์ฒ ๊ตฌ๊ฐ ์ถ๊ฐ ๊ธฐ๋ฅ
+
- ์งํ์ฒ ๋
ธ์ ์ ๊ตฌ๊ฐ์ ์ถ๊ฐํ๋ ๊ธฐ๋ฅ์ ๋
ธ์ ์ ์ญ์ ์ถ๊ฐํ๋ ๊ธฐ๋ฅ์ด๋ผ๊ณ ๋ ํ ์ ์๋ค.
- - ์ญ๊ณผ ์ญ์ฌ์ด๋ฅผ ๊ตฌ๊ฐ์ด๋ผ ํ๊ณ ์ด ๊ตฌ๊ฐ๋ค์ ๋ชจ์์ด ๋
ธ์ ์ด๋ค.
+ - ์ญ๊ณผ ์ญ์ฌ์ด๋ฅผ ๊ตฌ๊ฐ์ด๋ผ ํ๊ณ ์ด ๊ตฌ๊ฐ๋ค์ ๋ชจ์์ด ๋
ธ์ ์ด๋ค.
- ํ๋์ ์ญ์ ์ฌ๋ฌ๊ฐ์ ๋
ธ์ ์ ์ถ๊ฐ๋ ์ ์๋ค.
- ์ญ๊ณผ ์ญ ์ฌ์ด์ ์๋ก์ด ์ญ์ด ์ถ๊ฐ ๋ ์ ์๋ค.
- ๋
ธ์ ์์ ๊ฐ๋๊ธธ์ ์๊ธธ ์ ์๋ค.
@@ -24,6 +27,7 @@
### ์งํ์ฒ ๊ตฌ๊ฐ ์ญ์ ๊ธฐ๋ฅ
+
- ๋
ธ์ ์ ๋ฑ๋ก๋ ์ญ์ ์ ๊ฑฐํ ์ ์๋ค.
- ์ข
์ ์ ์ ๊ฑฐํ ๊ฒฝ์ฐ ๋ค์ ์ญ์ด ์ข
์ ์ด ๋๋ค.
- ๋
ธ์ ์ ํฌํจ๋ ์ญ์ด ๋๊ฐ ์ดํ์ผ ๋๋ ์ญ์ ์ ๊ฑฐํ ์ ์๋ค.
@@ -31,6 +35,7 @@
### ์งํ์ฒ ๋
ธ์ ์ ๋ฑ๋ก๋ ์ญ ์กฐํ ๊ธฐ๋ฅ
+
- ๋
ธ์ ์ ์ํ ์ข
์ ๋ถํฐ ํํ ์ข
์ ๊น์ง ์ฐ๊ฒฐ๋ ์์๋๋ก ์ญ ๋ชฉ๋ก์ ์กฐํํ ์ ์๋ค.
@@ -38,32 +43,38 @@
## ๐ป ํ๋ก๊ทธ๋จ ์คํ ๊ฒฐ๊ณผ
### ์ญ๊ด๋ฆฌ
+
### ๋
ธ์ ๊ด๋ฆฌ
+
### ๊ตฌ๊ฐ๊ด๋ฆฌ
+
### ๋
ธ์ ๋ ์ถ๋ ฅ
-
+
## โ
ํ๋ก๊ทธ๋๋ฐ ์๊ตฌ์ฌํญ
### ๋ฉ๋ด ๋ฒํผ
+
- ์ญ ๊ด๋ฆฌ 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๊ฐ์ ๊ฐ์ง๋ค.
@@ -71,6 +82,7 @@
- ์งํ์ฒ ๋
ธ์ ์ ์ญ์ ํ๋ button ํ๊ทธ๋ `.line-delete-button` class๊ฐ์ ๊ฐ์ง๋ค.
### ์งํ์ฒ ๊ตฌ๊ฐ ์ถ๊ฐ ๊ธฐ๋ฅ
+
- ์งํ์ฒ ๋
ธ์ ์ ์ ํํ๋ button ํ๊ทธ๋ `.section-line-menu-button` class๊ฐ์ ๊ฐ์ง๋ค.
- ์งํ์ฒ ๊ตฌ๊ฐ์ ์ค์ ํ ์ญ select ํ๊ทธ๋ `#section-station-selector` id๊ฐ์ ๊ฐ์ง๋ค.
- ์งํ์ฒ ๊ตฌ๊ฐ์ ์์๋ฅผ ์
๋ ฅํ๋ input ํ๊ทธ๋ `#section-order-input` id๊ฐ์ ๊ฐ์ง๋ค.
@@ -78,6 +90,7 @@
- ์งํ์ฒ ๊ตฌ๊ฐ์ ์ ๊ฑฐํ๋ button ํ๊ทธ๋ `.section-delete-button` class๊ฐ์ ๊ฐ์ง๋ค.
### ์งํ์ฒ ๋
ธ์ ๋ ์ถ๋ ฅ ๊ธฐ๋ฅ
+
- ์งํ์ฒ ๋
ธ์ ๋ ์ถ๋ ฅ ๋ฒํผ์ ๋๋ฅด๋ฉด `
` ํ๊ทธ๋ฅผ ๋ง๋ค๊ณ ํด๋น ํ๊ทธ ๋ด๋ถ์ ๋
ธ์ ๋๋ฅผ ์ถ๋ ฅํ๋ค.
### ๊ธฐ์กด ์๊ตฌ์ฌํญ
@@ -101,7 +114,8 @@
- [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 ํ๊ทธ์ ์ญ, ๋
ธ์ , ๊ตฌ๊ฐ์ ์ ์ผํ ๋ฐ์ดํฐ ๊ฐ๋ค์ ๊ด๋ฆฌํ๋ค.
+
+- [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)๋ฅผ ์ด์ฉํ์ฌ, ์๋ก๊ณ ์นจํ๋๋ผ๋ ๊ฐ์ฅ ์ต๊ทผ์ ์์
ํ ์ ๋ณด๋ค์ ๋ถ๋ฌ์ฌ ์ ์๋๋ก ํ๋ค.
diff --git a/docs/README.md b/docs/README.md
new file mode 100644
index 000000000..e3527a893
--- /dev/null
+++ b/docs/README.md
@@ -0,0 +1,165 @@
+# ๐ ์งํ์ฒ ๋
ธ์ ๋ ๋ฏธ์
+
+## ๐ ํ๋ก์ ํธ ๊ฐ์
+
+์งํ์ฒ ๋
ธ์ ๋ ๋ฏธ์
์ ์งํ์ฒ ๋
ธ์ ๋๋ฅผ ๊ด๋ฆฌํ ์ ์๋ ํ๋ก์ ํธ์
๋๋ค. ํ๋ก๊ทธ๋จ ์๋จ์๋ ์ด 4๊ฐ์ง์ ๋ฉ๋ด๋ก `1. ์ญ ๊ด๋ฆฌ`, `2. ๋
ธ์ ๊ด๋ฆฌ`, `3. ๊ตฌ๊ฐ ๊ด๋ฆฌ`, `4. ์งํ์ฒ ๋
ธ์ ๋ ์ถ๋ ฅ` ๊ฐ ์์ต๋๋ค. `1. ์ญ ๊ด๋ฆฌ`, `2. ๋
ธ์ ๊ด๋ฆฌ`, `3. ๊ตฌ๊ฐ ๊ด๋ฆฌ` ๋ฉ๋ด๋ ๊ฐ๊ฐ ์งํ์ฒ ์ญ, ์งํ์ฒ ๋
ธ์ , ์งํ์ฒ ๊ตฌ๊ฐ์ ๋ฑ๋ก(์ถ๊ฐ) ํ๊ฑฐ๋ ์ญ์ , ์กฐํํ๋ ๊ธฐ๋ฅ์ ๊ฐ์ง๊ณ ์์ผ๋ฉฐ, `4. ์งํ์ฒ ๋
ธ์ ๋ ์ถ๋ ฅ` ๋ฉ๋ด๋ ์งํ์ฒ ๋
ธ์ ๋๋ฅผ ๋ณด์ฌ์ฃผ๋ ๊ธฐ๋ฅ์ ํฉ๋๋ค.
+
+## ๐ ๊ธฐ๋ฅ ์๊ตฌ ์ฌํญ
+
+### 1.1 ์ฃผ์ด์ง ์๊ตฌ ์ฌํญ
+
+##### ์งํ์ฒ ์ญ ๊ด๋ จ ๊ธฐ๋ฅ
+
+- ์งํ์ฒ ์ญ์ ๋ฑ๋กํ๊ณ ์ญ์ ํ ์ ์๋ค. (๋จ, ๋
ธ์ ์ ๋ฑ๋ก๋ ์ญ์ ์ญ์ ํ ์ ์๋ค)
+- ์ค๋ณต๋ ์งํ์ฒ ์ญ ์ด๋ฆ์ด ๋ฑ๋ก๋ ์ ์๋ค.
+- ์งํ์ฒ ์ญ์ 2๊ธ์ ์ด์์ด์ด์ผ ํ๋ค.
+- ์งํ์ฒ ์ญ์ ๋ชฉ๋ก์ ์กฐํํ ์ ์๋ค.
+
+##### ์งํ์ฒ ๋
ธ์ ๊ด๋ จ ๊ธฐ๋ฅ
+
+- ์งํ์ฒ ๋
ธ์ ์ ๋ฑ๋กํ๊ณ ์ญ์ ํ ์ ์๋ค.
+- ์ค๋ณต๋ ์งํ์ฒ ๋
ธ์ ์ด๋ฆ์ด ๋ฑ๋ก๋ ์ ์๋ค.
+- ๋
ธ์ ๋ฑ๋ก ์ ์ํ ์ข
์ ์ญ๊ณผ ํํ ์ข
์ ์ญ์ ์
๋ ฅ๋ฐ๋๋ค.
+- ์งํ์ฒ ๋
ธ์ ์ ๋ชฉ๋ก์ ์กฐํํ ์ ์๋ค.
+
+##### ์งํ์ฒ ๊ตฌ๊ฐ ์ถ๊ฐ ๊ธฐ๋ฅ
+
+- ์งํ์ฒ ๋
ธ์ ์ ๊ตฌ๊ฐ์ ์ถ๊ฐํ๋ ๊ธฐ๋ฅ์ ๋
ธ์ ์ ์ญ์ ์ถ๊ฐํ๋ ๊ธฐ๋ฅ์ด๋ผ๊ณ ๋ ํ ์ ์๋ค.
+ - ์ญ๊ณผ ์ญ์ฌ์ด๋ฅผ ๊ตฌ๊ฐ์ด๋ผ ํ๊ณ ์ด ๊ตฌ๊ฐ๋ค์ ๋ชจ์์ด ๋
ธ์ ์ด๋ค.
+- ํ๋์ ์ญ์ ์ฌ๋ฌ๊ฐ์ ๋
ธ์ ์ ์ถ๊ฐ๋ ์ ์๋ค.
+- ์ญ๊ณผ ์ญ ์ฌ์ด์ ์๋ก์ด ์ญ์ด ์ถ๊ฐ ๋ ์ ์๋ค.
+- ๋
ธ์ ์์ ๊ฐ๋๊ธธ์ ์๊ธธ ์ ์๋ค.
+
+##### ์งํ์ฒ ๊ตฌ๊ฐ ์ญ์ ๊ธฐ๋ฅ
+
+- ๋
ธ์ ์ ๋ฑ๋ก๋ ์ญ์ ์ ๊ฑฐํ ์ ์๋ค.
+- ์ข
์ ์ ์ ๊ฑฐํ ๊ฒฝ์ฐ ๋ค์ ์ญ์ด ์ข
์ ์ด ๋๋ค.
+- ๋
ธ์ ์ ํฌํจ๋ ์ญ์ด ๋๊ฐ ์ดํ์ผ ๋๋ ์ญ์ ์ ๊ฑฐํ ์ ์๋ค.
+
+##### ์งํ์ฒ ๋
ธ์ ์ ๋ฑ๋ก๋ ์ญ ์กฐํ ๊ธฐ๋ฅ
+
+- ๋
ธ์ ์ ์ํ ์ข
์ ๋ถํฐ ํํ ์ข
์ ๊น์ง ์ฐ๊ฒฐ๋ ์์๋๋ก ์ญ ๋ชฉ๋ก์ ์กฐํํ ์ ์๋ค.
+
+### 1.2 ๊ธฐ๋ฅ ๋ชฉ๋ก
+
+- [x] `1. ์ญ ๊ด๋ฆฌ`, `2. ๋
ธ์ ๊ด๋ฆฌ`, `3. ๊ตฌ๊ฐ ๊ด๋ฆฌ`, `4. ์งํ์ฒ ๋
ธ์ ์ถ๋ ฅ` ๋ฒํผ์ ๋๋ฅด๋ฉด ๊ด๋ จ ํ๋ฉด์ ์ถ๋ ฅํ๋ค.
+- ์ญ ๊ด๋ฆฌ
+ - [x] `localStorage` ๋ฐ `data`์์ฑ์ ๋ฐ์ํ๋ค.
+ - [x] ์งํ์ฒ ์ญ ๋ชฉ๋ก์ ์กฐํํ๋ค.
+ - `์ญ ์ถ๊ฐ`
+ - [x] ์
๋ ฅํ ์ญ ์ด๋ฆ์ด 2๊ธ์ ์ด์์ธ์ง ๊ฒ์ฆํด์ผ ํ๋ค.
+ - [x] ์
๋ ฅํ ์ญ ์ด๋ฆ์ด ์ค๋ณต์ธ์ง ๊ฒ์ฆํด์ผ ํ๋ค.
+ - [x] ์งํ์ฒ ์ญ ๋ชฉ๋ก์ ๋ฐ์ํ๋ค.
+ - `์ญ์ `
+ - [x] `alert`๋ฅผ ํตํด ๋ค์ ๋ฌผ์ด๋ณธ๋ค.
+ - [x] ์งํ์ฒ ์ญ ๋ชฉ๋ก์ ๋ฐ์ํ๋ค.
+ - [x] ๋๋ฅธ ๊ณณ์ด ๋ฒํผ์ธ์ง ๊ฒ์ฆํด์ผ ํ๋ค.
+ - [x] ๋
ธ์ (์ ์ฒด ๊ตฌ๊ฐ)์ ํฌํจ๋์ด ์๋์ง ๊ฒ์ฆํด์ผ ํ๋ค.
+- ๋
ธ์ ๊ด๋ฆฌ
+ - [x] ๋
ธ์ ๋ชฉ๋ก์ ์กฐํํ๋ค.
+ - `๋
ธ์ ์ถ๊ฐ`
+ - [x] ์ํ ์ข
์ ๊ณผ ํํ ์ข
์ ๋ชฉ๋ก์ ์กฐํํ๋ค.
+ - [x] ์
๋ ฅํ ๋
ธ์ ์ด๋ฆ์ด ์ค๋ณต์ธ์ง ๊ฒ์ฆํด์ผ ํ๋ค.
+ - [x] ๋
ธ์ ์ ์ํ ์ข
์ ๊ณผ ํํ ์ข
์ ์ด ๋์ผํ์ง ๊ฒ์ฆํด์ผ ํ๋ค.
+ - [x] ๋
ธ์ ์ ์ด๋ฆ์ด ๋ฌ๋ผ๋ ์ํ ์ข
์ ๋๋ ํํ ์ข
์ ์ด ์ค๋ณต๋ ์ ์๋ค.
+ - [x] ์ํ ์ข
์ ์ด ์ค๋ณต์ธ์ง ๊ฒ์ฆํด์ผ ํ๋ค.
+ - [x] ํํ ์ข
์ ์ด ์ค๋ณต์ธ์ง ๊ฒ์ฆํด์ผ ํ๋ค.
+ - [x] ์ํ ์ข
์ ๊ณผ ํํ ์ข
์ ์ ๋ฐฉํฅ์ด ๋ฐ๋์ธ์ง ๊ฒ์ฆํด์ผ ํ๋ค.
+ - [x] ๋
ธ์ ๋ชฉ๋ก์ ๋ฐ์ํ๋ค.
+ - `์ญ์ `
+ - [x] `alert`๋ฅผ ํตํด ๋ค์ ๋ฌผ์ด๋ณธ๋ค.
+ - [x] ๋
ธ์ ๋ชฉ๋ก์ ๋ฐ์ํ๋ค.
+ - [x] ๋๋ฅธ ๊ณณ์ด ๋ฒํผ์ธ์ง ๊ฒ์ฆํด์ผ ํ๋ค.
+- ๊ตฌ๊ฐ ๊ด๋ฆฌ
+ - [x] ๊ตฌ๊ฐ์ ์์ ํ ๋
ธ์ ์ ๋ชฉ๋ก์ ์กฐํํ๋ค. (๋ฒํผ์ผ๋ก ์ถ๋ ฅ)
+ - [x] ๋
ธ์ ์ ์ ํํ๋ฉด ํด๋น ๋
ธ์ ์ ๊ตฌ๊ฐ ๋ชฉ๋ก์ ์กฐํํ๋ค.
+ - [x] ์ ๋ชฉ์ `(์ ํ๋ ๋
ธ์ ) ๊ด๋ฆฌ`๋ฅผ ์ถ๋ ฅํ๋ค.
+ - [x] ๋๋ฅธ ๊ณณ์ด ๋ฒํผ์ธ์ง ๊ฒ์ฆํด์ผ ํ๋ค.
+ - `๋ฑ๋ก`
+ - [x] ์
๋ ฅํ ์ญ์ ์ด๋ฆ๊ณผ ์์๋ฅผ ๊ฒ์ฆํด์ผ ํ๋ค.
+ - [x] ์
๋ ฅํ ์ญ์ ์ด๋ฆ์ด ์ค๋ณต์ธ์ง ๊ฒ์ฆํด์ผ ํ๋ค.
+ - [x] ์ญ๊ณผ ์ญ ์ฌ์ด(๊ตฌ๊ฐ)์ธ์ง ๊ฒ์ฆํด์ผ ํ๋ค.
+ - [x] ๊ตฌ๊ฐ ๋ชฉ๋ก์ ๋ฐ์ํ๋ค.
+ - `๋
ธ์ ์์ ์ ๊ฑฐ`
+ - [x] ๋
ธ์ ์ ๋ฑ๋ก๋ ์ญ์ด ๋ ๊ฐ ์ดํ์ธ์ง ๊ฒ์ฆํด์ผ ํ๋ค. (๋ ๊ฐ ์ดํ์ธ ๊ฒฝ์ฐ์๋ ์ญ์ ๋ถ๊ฐ)
+ - [x] `alert`๋ฅผ ํตํด ๋ค์ ๋ฌผ์ด๋ณธ๋ค.
+ - [x] ๊ตฌ๊ฐ ๋ชฉ๋ก์ ๋ฐ์ํ๋ค.
+ - [x] ๋๋ฅธ ๊ณณ์ด ๋ฒํผ์ธ์ง ๊ฒ์ฆํด์ผ ํ๋ค.
+- ์งํ์ฒ ๋
ธ์ ๋ ๊ด๋ฆฌ
+ - [x] ๊ฐ ๋
ธ์ ์ ์ญ๋ค์ ์กฐํํ๋ค.
+
+
+
+## โ
๋น๊ธฐ๋ฅ ์๊ตฌ์ฌํญ
+
+### 2.1 ์์ฑ ํ๊ทธ ์๊ตฌ์ฌํญ
+
+##### ๋ฉ๋ด ๋ฒํผ
+
+- [x] ์ญ ๊ด๋ฆฌ button ํ๊ทธ๋ `#station-manager-button` id๊ฐ์ ๊ฐ์ง๋ค.
+- [x] ๋
ธ์ ๊ด๋ฆฌ button ํ๊ทธ๋ `#line-manager-button` id๊ฐ์ ๊ฐ์ง๋ค.
+- [x] ๊ตฌ๊ฐ ๊ด๋ฆฌ button ํ๊ทธ๋ `#section-manager-button` id๊ฐ์ ๊ฐ์ง๋ค.
+- [x] ์งํ์ฒ ๋
ธ์ ๋ ์ถ๋ ฅ ๊ด๋ฆฌ button ํ๊ทธ๋ `#map-print-manager-button` id๊ฐ์ ๊ฐ์ง๋ค.
+
+##### ์งํ์ฒ ์ญ ๊ด๋ จ ๊ธฐ๋ฅ
+
+- [x] ์งํ์ฒ ์ญ์ ์
๋ ฅํ๋ input ํ๊ทธ๋ `#station-name-input` id๊ฐ์ ๊ฐ์ง๋ค.
+- [x] ์งํ์ฒ ์ญ์ ์ถ๊ฐํ๋ button ํ๊ทธ๋ `#station-add-button` id๊ฐ์ ๊ฐ์ง๋ค.
+- [x] ์งํ์ฒ ์ญ์ ์ญ์ ํ๋ button ํ๊ทธ๋ `.station-delete-button` class๊ฐ์ ๊ฐ์ง๋ค.
+
+##### ์งํ์ฒ ๋
ธ์ ๊ด๋ จ ๊ธฐ๋ฅ
+
+- [x] ์งํ์ฒ ๋
ธ์ ์ ์ด๋ฆ์ ์
๋ ฅํ๋ input ํ๊ทธ๋ `#line-name-input` id๊ฐ์ ๊ฐ์ง๋ค.
+- [x] ์งํ์ฒ ๋
ธ์ ์ ์ํ ์ข
์ ์ ์ ํํ๋ select ํ๊ทธ๋ `#line-start-station-selector` id๊ฐ์ ๊ฐ์ง๋ค.
+- [x] ์งํ์ฒ ๋
ธ์ ์ ํํ ์ข
์ ์ ์ ํํ๋ select ํ๊ทธ๋ `#line-end-station-selector` id๊ฐ์ ๊ฐ์ง๋ค.
+- [x] ์งํ์ฒ ๋
ธ์ ์ ์ถ๊ฐํ๋ button ํ๊ทธ๋ `#line-add-button` id๊ฐ์ ๊ฐ์ง๋ค.
+- [x] ์งํ์ฒ ๋
ธ์ ์ ์ญ์ ํ๋ button ํ๊ทธ๋ `.line-delete-button` class๊ฐ์ ๊ฐ์ง๋ค.
+
+##### ์งํ์ฒ ๊ตฌ๊ฐ ์ถ๊ฐ ๊ธฐ๋ฅ
+
+- [x] ์งํ์ฒ ๋
ธ์ ์ ์ ํํ๋ button ํ๊ทธ๋ `.section-line-menu-button` class๊ฐ์ ๊ฐ์ง๋ค.
+- [x] ์งํ์ฒ ๊ตฌ๊ฐ์ ์ค์ ํ ์ญ select ํ๊ทธ๋ `#section-station-selector` id๊ฐ์ ๊ฐ์ง๋ค.
+- [x] ์งํ์ฒ ๊ตฌ๊ฐ์ ์์๋ฅผ ์
๋ ฅํ๋ input ํ๊ทธ๋ `#section-order-input` id๊ฐ์ ๊ฐ์ง๋ค.
+- [x] ์งํ์ฒ ๊ตฌ๊ฐ์ ๋ฑ๋กํ๋ button ํ๊ทธ๋ `#section-add-button` id๊ฐ์ ๊ฐ์ง๋ค.
+- [x] ์งํ์ฒ ๊ตฌ๊ฐ์ ์ ๊ฑฐํ๋ button ํ๊ทธ๋ `.section-delete-button` class๊ฐ์ ๊ฐ์ง๋ค.
+
+##### ์งํ์ฒ ๋
ธ์ ๋ ์ถ๋ ฅ ๊ธฐ๋ฅ
+
+- [x] ์งํ์ฒ ๋
ธ์ ๋ ์ถ๋ ฅ ๋ฒํผ์ ๋๋ฅด๋ฉด `` ํ๊ทธ๋ฅผ ๋ง๋ค๊ณ ํด๋น ํ๊ทธ ๋ด๋ถ์ ๋
ธ์ ๋๋ฅผ ์ถ๋ ฅํ๋ค.
+
+### 2.2 ํ๋ก๊ทธ๋๋ฐ ์๊ตฌ์ฌํญ
+
+##### ๊ธฐ์กด ์๊ตฌ์ฌํญ
+
+- ์ฌ์ฉ์๊ฐ ์๋ชป๋ ์
๋ ฅ ๊ฐ์ ์์ฑํ ๊ฒฝ์ฐ `alert`์ ์ด์ฉํด ๋ฉ์์ง๋ฅผ ๋ณด์ฌ์ฃผ๊ณ , ์ฌ์
๋ ฅํ ์ ์๊ฒ ํ๋ค.
+- ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ(jQuery, Lodash ๋ฑ)๋ฅผ ์ฌ์ฉํ์ง ์๊ณ , ์์ Vanilla JS๋ก๋ง ๊ตฌํํ๋ค.
+- **์๋ฐ์คํฌ๋ฆฝํธ ์ฝ๋ ์ปจ๋ฒค์
์ ์งํค๋ฉด์ ํ๋ก๊ทธ๋๋ฐ** ํ๋ค
+- **indent(์ธ๋ดํธ, ๋ค์ฌ์ฐ๊ธฐ) depth๋ฅผ 3์ด ๋์ง ์๋๋ก ๊ตฌํํ๋ค. 2๊น์ง๋ง ํ์ฉ**ํ๋ค.
+- **ํจ์(๋๋ ๋ฉ์๋)์ ๊ธธ์ด๊ฐ 15๋ผ์ธ์ ๋์ด๊ฐ์ง ์๋๋ก ๊ตฌํํ๋ค.**
+ - ํจ์(๋๋ ๋ฉ์๋)๊ฐ ํ ๊ฐ์ง ์ผ๋ง ์ ํ๋๋ก ๊ตฌํํ๋ค.
+- ๋ณ์ ์ ์ธ์ `var` ๋ฅผ ์ฌ์ฉํ์ง ์๋๋ค. `const` ์ `let` ์ ์ฌ์ฉํ๋ค.
+- `import` ๋ฌธ์ ์ด์ฉํด ์คํฌ๋ฆฝํธ๋ฅผ ๋ชจ๋ํํ๊ณ ๋ถ๋ฌ์ฌ ์ ์๊ฒ ๋ง๋ ๋ค.
+- `template literal`์ ์ด์ฉํด ๋ฐ์ดํฐ์ html string์ ๊ฐ๋
์ฑ ์ข๊ฒ ํํํ๋ค.
+
+#### ์ถ๊ฐ๋ ์๊ตฌ์ฌํญ
+
+- [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)๋ฅผ ์ด์ฉํ์ฌ, ์๋ก๊ณ ์นจํ๋๋ผ๋ ๊ฐ์ฅ ์ต๊ทผ์ ์์
ํ ์ ๋ณด๋ค์ ๋ถ๋ฌ์ฌ ์ ์๋๋ก ํ๋ค.
+
+## ๐ป ํ๋ก๊ทธ๋จ ์คํ ๊ฒฐ๊ณผ
+
+### ์ญ๊ด๋ฆฌ
+
+
+
+### ๋
ธ์ ๊ด๋ฆฌ
+
+
+
+### ๊ตฌ๊ฐ๊ด๋ฆฌ
+
+
+
+### ๋
ธ์ ๋ ์ถ๋ ฅ
+
+
diff --git a/index.html b/index.html
index fc99deac2..d2cda19c3 100644
--- a/index.html
+++ b/index.html
@@ -2,12 +2,101 @@
+
์งํ์ฒ ๋
ธ์ ๋ ๊ด๋ฆฌ
-
๐ ์งํ์ฒ ๋
ธ์ ๋ ๊ด๋ฆฌ
+ ๐ ์งํ์ฒ ๋
ธ์ ๋ ๊ด๋ฆฌ
+
+
+
+
+
+
+
+
+
+
+
+
+
+
๐ ์งํ์ฒ ์ญ ๋ชฉ๋ก
+
+
+
+ | ์ญ ์ด๋ฆ |
+ ์ค์ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
๐ ์งํ์ฒ ๋
ธ์ ๋ชฉ๋ก
+
+
+
+ | ๋
ธ์ ์ด๋ฆ |
+ ์ํ ์ข
์ ์ญ |
+ ํํ ์ข
์ ์ญ |
+ ์ค์ |
+
+
+
+
+
+
+
+
+
๊ตฌ๊ฐ์ ์์ ํ ๋
ธ์ ์ ์ ํํด์ฃผ์ธ์.
+
+
+
+
+
๊ตฌ๊ฐ ๋ฑ๋ก
+
+
+
+
+
+
+ | ์์ |
+ ์ด๋ฆ |
+ ์ค์ |
+
+
+
+
+
+
+
+
+
+
diff --git a/src/controller/line-manager.js b/src/controller/line-manager.js
new file mode 100644
index 000000000..a55410ce7
--- /dev/null
+++ b/src/controller/line-manager.js
@@ -0,0 +1,58 @@
+import { Line, LineObject } from "../model/line.js";
+import { Station } from "../model/station.js";
+import { ConfirmMessage, Constant } from "../util/constant.js";
+import { Storage } from "../util/storage.js";
+import { LineValidation } from "../util/validation.js";
+import { Element, ElementControl } from "../view/element.js";
+import { LineView } from "../view/line-view.js";
+
+export const LineManager = {
+ isVisited: false,
+
+ init() {
+ this.isVisited = true;
+ Station.stations = Storage.load(Station.key)
+ ? Storage.load(Station.key)
+ : [];
+ Line.lines = Storage.load(Line.key) ? Storage.load(Line.key) : [];
+ LineView.render();
+ this.setEventListener();
+ },
+
+ setEventListener() {
+ Element.lineAddButton.addEventListener(Constant.CLICK, () => {
+ this.onClickAddButton();
+ });
+
+ Element.lineContainer
+ .querySelector(Constant.TBODY)
+ .addEventListener(Constant.CLICK, (e) => {
+ this.onClickDeleteButton(e);
+ });
+ },
+
+ onClickAddButton() {
+ const name = Element.lineNameInput.value;
+ const start = Element.lineStartStationSelector.value;
+ const end = Element.lineEndStationSelector.value;
+
+ if (LineValidation.isValidLine(name, start, end)) {
+ Line.add(new LineObject(name, start, end));
+ }
+ LineView.render();
+ ElementControl.clearInput(Element.lineNameInput);
+ },
+
+ onClickDeleteButton(e) {
+ const name = e.target.dataset.name;
+
+ if (e.target.tagName !== Constant.BUTTON) {
+ return;
+ }
+
+ if (confirm(ConfirmMessage.CHECK_DELETION)) {
+ Line.delete(name);
+ LineView.render();
+ }
+ },
+};
diff --git a/src/controller/map-print-manager.js b/src/controller/map-print-manager.js
new file mode 100644
index 000000000..b91ab826c
--- /dev/null
+++ b/src/controller/map-print-manager.js
@@ -0,0 +1,17 @@
+import { Line } from "../model/line.js";
+import { Station } from "../model/station.js";
+import { Storage } from "../util/storage.js";
+import { MapPrintView } from "../view/map-print-view.js";
+
+export const MapPrintManager = {
+ isVisited: false,
+
+ init() {
+ this.isVisited = true;
+ Station.stations = Storage.load(Station.key)
+ ? Storage.load(Station.key)
+ : [];
+ Line.lines = Storage.load(Line.key) ? Storage.load(Line.key) : [];
+ MapPrintView.render();
+ },
+};
diff --git a/src/controller/section-manager.js b/src/controller/section-manager.js
new file mode 100644
index 000000000..3c7884c3f
--- /dev/null
+++ b/src/controller/section-manager.js
@@ -0,0 +1,76 @@
+import { Line } from "../model/line.js";
+import { Section } from "../model/section.js";
+import { Station } from "../model/station.js";
+import { ConfirmMessage, Constant, ErrorMessage } from "../util/constant.js";
+import { Storage } from "../util/storage.js";
+import { SectionValidation } from "../util/validation.js";
+import { Element, ElementControl } from "../view/element.js";
+import { SectionView } from "../view/section-view.js";
+
+export const SectionManager = {
+ isVisited: false,
+ selectedLine: "",
+
+ init() {
+ this.isVisited = true;
+ Station.stations = Storage.load(Station.key)
+ ? Storage.load(Station.key)
+ : [];
+ Line.lines = Storage.load(Line.key) ? Storage.load(Line.key) : [];
+ SectionView.render();
+ this.setEventListener();
+ },
+
+ setEventListener() {
+ Element.sectionLineMenu.addEventListener(Constant.CLICK, (e) => {
+ this.onClickLineButton(e);
+ });
+
+ Element.sectionAddButton.addEventListener(Constant.CLICK, () => {
+ this.onClickAddButton();
+ });
+
+ Element.sectionContainer
+ .querySelector(Constant.TBODY)
+ .addEventListener(Constant.CLICK, (e) => {
+ this.onClickDeleteButton(e);
+ });
+ },
+
+ onClickLineButton(e) {
+ if (e.target.tagName !== Constant.BUTTON) {
+ return false;
+ }
+ this.selectedLine = e.target.dataset.name;
+ SectionView.renderSectionManager(this.selectedLine);
+ },
+
+ onClickAddButton() {
+ const station = Element.sectionStationSelector.value;
+ const order = Element.sectionOrderInupt.value;
+
+ if (SectionValidation.isValidSection(station, order, this.selectedLine)) {
+ Section.add(station, order, this.selectedLine);
+ }
+ SectionView.renderSectionManager(this.selectedLine);
+ ElementControl.clearInput(Element.sectionOrderInupt);
+ },
+
+ onClickDeleteButton(e) {
+ const idx = e.target.dataset.idx;
+
+ if (e.target.tagName !== Constant.BUTTON) {
+ return;
+ }
+
+ if (confirm(ConfirmMessage.CHECK_DELETION_FROM_LINE)) {
+ if (!SectionValidation.hasMinimumStations(this.selectedLine)) {
+ alert(ErrorMessage.MINIMUM_STATIONS);
+
+ return;
+ }
+ Section.delete(idx, this.selectedLine);
+ SectionView.renderSectionManager(this.selectedLine);
+ }
+ },
+};
diff --git a/src/controller/station-manager.js b/src/controller/station-manager.js
new file mode 100644
index 000000000..bef65abd3
--- /dev/null
+++ b/src/controller/station-manager.js
@@ -0,0 +1,61 @@
+import { Line } from "../model/line.js";
+import { Station } from "../model/station.js";
+import { ConfirmMessage, Constant, ErrorMessage } from "../util/constant.js";
+import { Storage } from "../util/storage.js";
+import { StationValidation } from "../util/validation.js";
+import { Element, ElementControl } from "../view/element.js";
+import { StationView } from "../view/station-view.js";
+
+export const StationManager = {
+ isVisited: false,
+
+ init() {
+ this.isVisited = true;
+ Station.stations = Storage.load(Station.key)
+ ? Storage.load(Station.key)
+ : [];
+ Line.lines = Storage.load(Line.key) ? Storage.load(Line.key) : [];
+ StationView.render();
+ this.setEventListener();
+ },
+
+ setEventListener() {
+ Element.stationAddButton.addEventListener(Constant.CLICK, () => {
+ this.onClickAddButton();
+ });
+
+ Element.stationContainer
+ .querySelector(Constant.TBODY)
+ .addEventListener(Constant.CLICK, (e) => {
+ this.onClickDeleteButton(e);
+ });
+ },
+
+ onClickAddButton() {
+ const name = Element.stationNameInput.value;
+
+ if (StationValidation.isValidStation(name)) {
+ Station.add(name);
+ StationView.render();
+ }
+ ElementControl.clearInput(Element.stationNameInput);
+ },
+
+ onClickDeleteButton(e) {
+ const name = e.target.dataset.name;
+
+ if (e.target.tagName !== Constant.BUTTON) {
+ return;
+ }
+
+ if (confirm(ConfirmMessage.CHECK_DELETION)) {
+ if (!StationValidation.isValidStatonDeletion(name)) {
+ alert(ErrorMessage.STATION_RELATED_LINE);
+
+ return;
+ }
+ Station.delete(name);
+ StationView.render();
+ }
+ },
+};
diff --git a/src/index.js b/src/index.js
index e69de29bb..d0b94168c 100644
--- a/src/index.js
+++ b/src/index.js
@@ -0,0 +1,26 @@
+import { Constant } from "./util/constant.js";
+import { Element, ElementControl } from "./view/element.js";
+
+const App = () => {
+ SubwayManager();
+};
+
+const SubwayManager = () => {
+ Element.stationManagerButton.addEventListener(Constant.CLICK, () => {
+ ElementControl.showStationContainer();
+ });
+
+ Element.lineManagerButton.addEventListener(Constant.CLICK, () => {
+ ElementControl.showLineContainer();
+ });
+
+ Element.sectionManagerButton.addEventListener(Constant.CLICK, () => {
+ ElementControl.showSectionContainer();
+ });
+
+ Element.mapPrintManagerButton.addEventListener(Constant.CLICK, () => {
+ ElementControl.showMapPrintContainer();
+ });
+};
+
+App();
diff --git a/src/model/line.js b/src/model/line.js
new file mode 100644
index 000000000..44f0871b3
--- /dev/null
+++ b/src/model/line.js
@@ -0,0 +1,24 @@
+import { Constant } from "../util/constant.js";
+import { Storage } from "../util/storage.js";
+
+export class LineObject {
+ constructor(name, start, end) {
+ this.name = name;
+ this.stations = [start, end];
+ }
+}
+
+export const Line = {
+ key: Constant.STORAGE_KEY_LINE,
+ lines: [],
+
+ add(lineObject) {
+ this.lines.push(lineObject);
+ Storage.save(this.key, this.lines);
+ },
+
+ delete(name) {
+ this.lines = this.lines.filter((el) => el.name !== name);
+ Storage.save(this.key, this.lines);
+ },
+};
diff --git a/src/model/section.js b/src/model/section.js
new file mode 100644
index 000000000..c1c4c9484
--- /dev/null
+++ b/src/model/section.js
@@ -0,0 +1,20 @@
+import { Storage } from "../util/storage.js";
+import { Line } from "./line.js";
+
+export const Section = {
+ add(station, order, line) {
+ const stationArray = Line.lines.filter(({ name }) => name === line)[0]
+ .stations;
+
+ stationArray.splice(order, 0, station);
+ Storage.save(Line.key, Line.lines);
+ },
+
+ delete(idx, line) {
+ const stationArray = Line.lines.filter(({ name }) => name === line)[0]
+ .stations;
+
+ stationArray.splice(idx, 1);
+ Storage.save(Line.key, Line.lines);
+ },
+};
diff --git a/src/model/station.js b/src/model/station.js
new file mode 100644
index 000000000..9b4cc279b
--- /dev/null
+++ b/src/model/station.js
@@ -0,0 +1,17 @@
+import { Constant } from "../util/constant.js";
+import { Storage } from "../util/storage.js";
+
+export const Station = {
+ key: Constant.STORAGE_KEY_STATION,
+ stations: [],
+
+ add(name) {
+ this.stations.push(name);
+ Storage.save(this.key, this.stations);
+ },
+
+ delete(name) {
+ this.stations = this.stations.filter((e) => e !== name);
+ Storage.save(this.key, this.stations);
+ },
+};
diff --git a/src/util/constant.js b/src/util/constant.js
new file mode 100644
index 000000000..d76025eab
--- /dev/null
+++ b/src/util/constant.js
@@ -0,0 +1,71 @@
+export const Constant = {
+ // ์ด๊ธฐ ํ๋ฉด ๋ฒํผ
+ STATION_MANAGER_BTN_ID: "#station-manager-button",
+ LINE_MANAGER_BTN_ID: "#line-manager-button",
+ SECTION_MANAGER_BTN_ID: "#section-manager-button",
+ MAP_PRINT_MANAGER_BTN_ID: "#map-print-manager-button",
+
+ // ์ญ ๊ด๋ฆฌ
+ STATION_CONTAINER_CLASS: ".station-container",
+ STORAGE_KEY_STATION: "stations",
+ STATION_ADD_BUTTON_ID: "#station-add-button",
+ STATION_NAME_INPUT_ID: "#station-name-input",
+ STATION_DELELE_BUTTON_CLASS: ".station-delete-button",
+
+ // ๋
ธ์ ๊ด๋ฆฌ
+ LINE_CONTAINER_CLASS: ".line-container",
+ STORAGE_KEY_LINE: "lines",
+ LINE_START_STATION_SELECTOR_ID: "#line-start-station-selector",
+ LINE_END_STATION_SELECTOR_ID: "#line-end-station-selector",
+ LINE_ADD_BUTTON_ID: "#line-add-button",
+ LINE_NAME_INPUT_ID: "#line-name-input",
+
+ // ๊ตฌ๊ฐ ๊ด๋ฆฌ
+ SECTION_CONTAINER_CLASS: ".section-container",
+ SECTION_LINE_MENU_CLASS: ".section-line-menu",
+ SECTION_MANAGER_CLASS: ".section-manager",
+ SECTION_MANAGER_TITLE_CLASS: ".section-manager-title",
+ SECTION_STATION_SELECTOR_ID: "#section-station-selector",
+ SECTION_ADD_BUTTON_ID: "#section-add-button",
+ SECTION_ORDER_INPUT_ID: "#section-order-input",
+
+ // ์งํ์ฒ ๋
ธ์ ๊ด๋ฆฌ
+ MAP_PRINT_CONTAINER_CLASS: ".map-print-container",
+
+ // validation
+ MINIMUM_LENGTH: 2,
+ REGEX_CATCHING_WHITESPACE: /^\s*$/,
+ REGEX_CATCHING_INTEGER: /^[0-9 ()+-]+$/,
+
+ // tag ๊ด๋ จ
+ CLICK: "click",
+ NONE: "none",
+ BLOCK: "block",
+ TBODY: "tbody",
+ BUTTON: "BUTTON",
+};
+
+export const ErrorMessage = {
+ // ๊ณตํต
+ DUPLICATED_STATION_NAME: "์ค๋ณต๋์ง ์์ ์ญ ์ด๋ฆ์ ์
๋ ฅํด ์ฃผ์ธ์.",
+
+ // ์ญ ๊ด๋ฆฌ
+ MINIMUM_NAME_LENGTH: "๊ณต๋ฐฑ์ด ์๋ 2๊ธ์ ์ด์์ ์ญ ์ด๋ฆ์ ์
๋ ฅํด ์ฃผ์ธ์.",
+ STATION_RELATED_LINE: "๋
ธ์ ์ ํฌํจ๋ ์ญ์ ์ ๊ฑฐํ ์ ์์ต๋๋ค.",
+
+ // ๋
ธ์ ๊ด๋ฆฌ
+ NAME_WHITE_SPACE: "๊ณต๋ฐฑ์ด ์๋ ๋
ธ์ ์ด๋ฆ์ ์
๋ ฅํด ์ฃผ์ธ์.",
+ SAME_START_END_STATION: "์๋ก ๋ค๋ฅธ ์ข
์ ์ ์ ํํด ์ฃผ์ธ์.",
+ DUPLICATED_START_END_STATION: "๊ธฐ์กด ๋
ธ์ ๊ณผ ๋ค๋ฅธ ์ํ, ํํ ์ข
์ ์ ์ ํํด ์ฃผ์ธ์.\n๋ํ, ์ข
์ ๋ฐฉํฅ๋ง ๋ค๋ฅธ ๊ฒฝ์ฐ ๋์ผํ ๋
ธ์ ์ผ๋ก ๊ฐ์ฃผ๋ฉ๋๋ค.",
+ DUPLICATED_LINE_NAME: "์ค๋ณต๋์ง ์์ ๋
ธ์ ์ด๋ฆ์ ์
๋ ฅํด ์ฃผ์ธ์.",
+
+ // ๊ตฌ๊ฐ ๊ด๋ฆฌ
+ NOT_INTEGER_ORDER: "1 ์ด์ ์ ์์ ์์๋ฅผ ์
๋ ฅํด ์ฃผ์ธ์.",
+ MINIMUM_ORDER: "1 ์ด์์ ์์๋ฅผ ์
๋ ฅํด ์ฃผ์ธ์.",
+ MINIMUM_STATIONS: "๋
ธ์ ์ ํฌํจ๋ ์ญ์ด ๋๊ฐ ์ดํ์ผ ๋๋ ์ญ์ ์ ๊ฑฐํ ์ ์์ต๋๋ค.",
+};
+
+export const ConfirmMessage = {
+ CHECK_DELETION: "์ ๋ง๋ก ์ญ์ ํ์๊ฒ ์ต๋๊น?",
+ CHECK_DELETION_FROM_LINE: "์ ๋ง๋ก ๋
ธ์ ์์ ์ญ์ ํ์๊ฒ ์ต๋๊น?",
+};
diff --git a/src/util/storage.js b/src/util/storage.js
new file mode 100644
index 000000000..3df31279b
--- /dev/null
+++ b/src/util/storage.js
@@ -0,0 +1,9 @@
+export const Storage = {
+ save(key, array) {
+ localStorage.setItem(key, JSON.stringify(array));
+ },
+
+ load(key) {
+ return JSON.parse(localStorage.getItem(key));
+ },
+};
diff --git a/src/util/validation.js b/src/util/validation.js
new file mode 100644
index 000000000..dc8e6afbb
--- /dev/null
+++ b/src/util/validation.js
@@ -0,0 +1,186 @@
+import { Line } from "../model/line.js";
+import { Station } from "../model/station.js";
+import { Constant, ErrorMessage } from "./constant.js";
+
+export const StationValidation = {
+ isValidStation(name) {
+ return this.hasValidName(name);
+ },
+
+ hasValidName(name) {
+ if (!this.hasMinimumLength(name)) {
+ alert(ErrorMessage.MINIMUM_NAME_LENGTH);
+
+ return;
+ }
+
+ if (!this.isNotDuplicated(name)) {
+ alert(ErrorMessage.DUPLICATED_STATION_NAME);
+
+ return;
+ }
+
+ return true;
+ },
+
+ hasMinimumLength(name) {
+ return (
+ name.length >= Constant.MINIMUM_LENGTH &&
+ !Constant.REGEX_CATCHING_WHITESPACE.test(name)
+ );
+ },
+
+ isNotDuplicated(name) {
+ return !Station.stations.includes(name);
+ },
+
+ isValidStatonDeletion(station) {
+ let isValid = true;
+
+ Line.lines.forEach(({ stations }) => {
+ if (stations.includes(station)) {
+ isValid = false;
+ }
+ });
+
+ return isValid;
+ },
+};
+
+export const LineValidation = {
+ isValidLine(name, start, end) {
+ return this.hasValidName(name) && this.hasValidStartEndStations(start, end);
+ },
+
+ hasValidName(name) {
+ if (!this.isNotWhiteSpace(name)) {
+ alert(ErrorMessage.NAME_WHITE_SPACE);
+
+ return;
+ }
+
+ if (!this.isNotDuplicated(name)) {
+ alert(ErrorMessage.DUPLICATED_LINE_NAME);
+
+ return;
+ }
+
+ return true;
+ },
+
+ hasValidStartEndStations(start, end) {
+ if (this.isSameStartEndStation(start, end)) {
+ alert(ErrorMessage.SAME_START_END_STATION);
+
+ return;
+ }
+
+ if (this.isDuplicatedStartEndStation(start, end)) {
+ alert(ErrorMessage.DUPLICATED_START_END_STATION);
+
+ return;
+ }
+
+ return true;
+ },
+
+ isNotWhiteSpace(name) {
+ return !Constant.REGEX_CATCHING_WHITESPACE.test(name);
+ },
+
+ isNotDuplicated(name) {
+ const stationNameArray = Line.lines.map(({ name }) => name);
+
+ return !stationNameArray.includes(name);
+ },
+
+ isSameStartEndStation(start, end) {
+ return start === end;
+ },
+
+ isDuplicatedStartEndStation(start, end) {
+ let isValid = false;
+
+ Line.lines.forEach(({ stations }) => {
+ if (stations[0] === start) {
+ isValid = true;
+ }
+ if (stations[stations.length - 1] === end) {
+ isValid = true;
+ }
+ if (stations[0] === end && stations[stations.length - 1] === start) {
+ isValid = true;
+ }
+ });
+
+ return isValid;
+ }
+};
+
+export const SectionValidation = {
+ isValidSection(station, order, selectedLine) {
+ return (
+ this.hasValidName(station, selectedLine) &&
+ this.hasValidOrder(order, selectedLine)
+ );
+ },
+
+ hasValidName(station, selectedLine) {
+ if (!this.isNotDuplicated(station, selectedLine)) {
+ alert(ErrorMessage.DUPLICATED_STATION_NAME);
+
+ return;
+ }
+
+ return true;
+ },
+
+ hasValidOrder(order, selectedLine) {
+ const stationArray = Line.lines.filter(
+ ({ name }) => name === selectedLine
+ )[0].stations;
+
+ if (!Constant.REGEX_CATCHING_INTEGER.test(order)) {
+ alert(ErrorMessage.NOT_INTEGER_ORDER);
+
+ return;
+ }
+
+ if (!this.hasValidNumberRange(order, stationArray)) {
+ return;
+ }
+
+ return true;
+ },
+
+ hasValidNumberRange(order, stationArray) {
+ if (order < 1) {
+ alert(ErrorMessage.MINIMUM_ORDER);
+
+ return;
+ }
+
+ if (order > stationArray.length - 1) {
+ alert(`${stationArray.length - 1} ์ดํ์ ์์๋ฅผ ์
๋ ฅํด ์ฃผ์ธ์.`);
+
+ return;
+ }
+
+ return true;
+ },
+
+ isNotDuplicated(station, selectedLine) {
+ const stationArray = Line.lines.filter(
+ ({ name }) => name === selectedLine
+ )[0].stations;
+
+ return !stationArray.includes(station);
+ },
+
+ hasMinimumStations(selectedLine) {
+ return (
+ Line.lines.filter(({ name }) => name === selectedLine)[0].stations
+ .length > Constant.MINIMUM_LENGTH
+ );
+ },
+};
diff --git a/src/view/element.js b/src/view/element.js
new file mode 100644
index 000000000..4fe778790
--- /dev/null
+++ b/src/view/element.js
@@ -0,0 +1,103 @@
+import { StationManager } from "../controller/station-manager.js";
+import { StationView } from "./station-view.js";
+import { Constant } from "../util/constant.js";
+import { LineManager } from "../controller/line-manager.js";
+import { LineView } from "./line-view.js";
+import { SectionManager } from "../controller/section-manager.js";
+import { SectionView } from "./section-view.js";
+import { MapPrintView } from "./map-print-view.js";
+import { MapPrintManager } from "../controller/map-print-manager.js";
+
+export const Element = {
+ // ์ด๊ธฐ ํ๋ฉด ๋ฒํผ
+ stationManagerButton: document.querySelector(Constant.STATION_MANAGER_BTN_ID),
+ lineManagerButton: document.querySelector(Constant.LINE_MANAGER_BTN_ID),
+ sectionManagerButton: document.querySelector(Constant.SECTION_MANAGER_BTN_ID),
+ mapPrintManagerButton: document.querySelector(
+ Constant.MAP_PRINT_MANAGER_BTN_ID
+ ),
+
+ // ์ญ ๊ด๋ฆฌ
+ stationContainer: document.querySelector(Constant.STATION_CONTAINER_CLASS),
+ stationAddButton: document.querySelector(Constant.STATION_ADD_BUTTON_ID),
+ stationNameInput: document.querySelector(Constant.STATION_NAME_INPUT_ID),
+ stationDeleteButton: document.querySelector(
+ Constant.STATION_DELELE_BUTTON_CLASS
+ ),
+
+ // ๋
ธ์ ๊ด๋ฆฌ
+ lineContainer: document.querySelector(Constant.LINE_CONTAINER_CLASS),
+ lineStartStationSelector: document.querySelector(
+ Constant.LINE_START_STATION_SELECTOR_ID
+ ),
+ lineEndStationSelector: document.querySelector(
+ Constant.LINE_END_STATION_SELECTOR_ID
+ ),
+ lineAddButton: document.querySelector(Constant.LINE_ADD_BUTTON_ID),
+ lineNameInput: document.querySelector(Constant.LINE_NAME_INPUT_ID),
+
+ // ๊ตฌ๊ฐ ๊ด๋ฆฌ
+ sectionContainer: document.querySelector(Constant.SECTION_CONTAINER_CLASS),
+ sectionLineMenu: document.querySelector(Constant.SECTION_LINE_MENU_CLASS),
+ sectionManager: document.querySelector(Constant.SECTION_MANAGER_CLASS),
+ sectionManagerTitle: document.querySelector(
+ Constant.SECTION_MANAGER_TITLE_CLASS
+ ),
+ sectionStationSelector: document.querySelector(
+ Constant.SECTION_STATION_SELECTOR_ID
+ ),
+ sectionAddButton: document.querySelector(Constant.SECTION_ADD_BUTTON_ID),
+ sectionOrderInupt: document.querySelector(Constant.SECTION_ORDER_INPUT_ID),
+
+ // ์งํ์ฒ ๋
ธ์ ๋ ๊ด๋ฆฌ
+ mapPrintContainer: document.querySelector(Constant.MAP_PRINT_CONTAINER_CLASS),
+};
+
+export const ElementControl = {
+ hideAllContainers() {
+ Element.stationContainer.style.display = Constant.NONE;
+ Element.lineContainer.style.display = Constant.NONE;
+ Element.sectionContainer.style.display = Constant.NONE;
+ Element.mapPrintContainer.style.display = Constant.NONE;
+ },
+
+ showStationContainer() {
+ this.hideAllContainers();
+ Element.stationContainer.style.display = Constant.BLOCK;
+
+ StationManager.isVisited ? StationView.render() : StationManager.init();
+ },
+
+ showLineContainer() {
+ this.hideAllContainers();
+ Element.lineContainer.style.display = Constant.BLOCK;
+
+ LineManager.isVisited ? LineView.render() : LineManager.init();
+ },
+
+ showSectionContainer() {
+ this.hideAllContainers();
+ Element.sectionContainer.style.display = Constant.BLOCK;
+
+ SectionManager.isVisited ? SectionView.render() : SectionManager.init();
+ },
+
+ showSectionManager() {
+ Element.sectionManager.style.display = Constant.BLOCK;
+ },
+
+ hideSectionManager() {
+ Element.sectionManager.style.display = Constant.NONE;
+ },
+
+ showMapPrintContainer() {
+ this.hideAllContainers();
+ Element.mapPrintContainer.style.display = Constant.BLOCK;
+
+ MapPrintManager.isVisited ? MapPrintView.render() : MapPrintManager.init();
+ },
+
+ clearInput(element) {
+ element.value = "";
+ },
+};
diff --git a/src/view/line-view.js b/src/view/line-view.js
new file mode 100644
index 000000000..abae4870c
--- /dev/null
+++ b/src/view/line-view.js
@@ -0,0 +1,43 @@
+import { Line } from "../model/line.js";
+import { Station } from "../model/station.js";
+import { Constant } from "../util/constant.js";
+import { Element } from "./element.js";
+
+export const LineView = {
+ render() {
+ this.renderLineStartEndSelector();
+ this.renderLineContainerTable();
+ },
+
+ renderLineStartEndSelector() {
+ let content = "";
+
+ Station.stations.forEach((station) => {
+ content += `
+
+ `;
+ });
+
+ Element.lineStartStationSelector.innerHTML = content;
+ Element.lineEndStationSelector.innerHTML = content;
+ },
+
+ renderLineContainerTable() {
+ let content = "";
+
+ Line.lines.forEach(({ name, stations }) => {
+ content += `
+
+ | ${name} |
+ ${stations[0]} |
+ ${stations[stations.length - 1]} |
+
+
+ |
+
+ `;
+ });
+
+ Element.lineContainer.querySelector(Constant.TBODY).innerHTML = content;
+ },
+};
diff --git a/src/view/map-print-view.js b/src/view/map-print-view.js
new file mode 100644
index 000000000..3a047c2b8
--- /dev/null
+++ b/src/view/map-print-view.js
@@ -0,0 +1,20 @@
+import { Line } from "../model/line.js";
+import { Element } from "./element.js";
+
+export const MapPrintView = {
+ render() {
+ let content = '';
+
+ Line.lines.forEach(({ name, stations }) => {
+ content += `
${name}
`;
+
+ stations.forEach((station) => {
+ content += `- ${station}
`;
+ });
+ content += "
";
+ });
+ content += "
";
+
+ Element.mapPrintContainer.innerHTML = content;
+ },
+};
diff --git a/src/view/section-view.js b/src/view/section-view.js
new file mode 100644
index 000000000..4a2763e51
--- /dev/null
+++ b/src/view/section-view.js
@@ -0,0 +1,74 @@
+import { Line } from "../model/line.js";
+import { Station } from "../model/station.js";
+import { Constant } from "../util/constant.js";
+import { Element, ElementControl } from "./element.js";
+
+export const SectionView = {
+ render() {
+ this.renderSectionLineMenuButton();
+
+ ElementControl.hideSectionManager();
+ },
+
+ renderSectionLineMenuButton() {
+ let content = "";
+
+ Line.lines.forEach(({ name }) => {
+ content += `
+
+ `;
+ });
+
+ Element.sectionLineMenu.innerHTML = content;
+ },
+
+ renderSectionManager(line) {
+ this.renderSectionManagerTitle(line);
+ this.renderSectionStationSelector();
+ this.renderSectionContainerTable(line);
+
+ ElementControl.showSectionManager();
+ },
+
+ renderSectionManagerTitle(line) {
+ const sectionManagerTitleContent = `${line} ๊ด๋ฆฌ`;
+
+ Element.sectionManagerTitle.innerHTML = sectionManagerTitleContent;
+ },
+
+ renderSectionStationSelector() {
+ let content = "";
+
+ Station.stations.forEach((station) => {
+ content += `
+
+ `;
+ });
+
+ Element.sectionStationSelector.innerHTML = content;
+ },
+
+ renderSectionContainerTable(line) {
+ const stationArray = Line.lines.filter(({ name }) => name === line)[0]
+ .stations;
+ let content = "";
+
+ for (let i = 0; i < stationArray.length; i++) {
+ content += `
+
+ | ${i} |
+ ${stationArray[i]} |
+
+
+ |
+
+ `;
+ }
+
+ Element.sectionContainer.querySelector(Constant.TBODY).innerHTML = content;
+ },
+};
diff --git a/src/view/station-view.js b/src/view/station-view.js
new file mode 100644
index 000000000..0a97d72e1
--- /dev/null
+++ b/src/view/station-view.js
@@ -0,0 +1,22 @@
+import { Station } from "../model/station.js";
+import { Constant } from "../util/constant.js";
+import { Element } from "./element.js";
+
+export const StationView = {
+ render() {
+ let content = "";
+
+ Station.stations.forEach((station) => {
+ content += `
+
+ | ${station} |
+
+
+ |
+
+ `;
+ });
+
+ Element.stationContainer.querySelector(Constant.TBODY).innerHTML = content;
+ },
+};
diff --git a/style.css b/style.css
new file mode 100644
index 000000000..5a7579515
--- /dev/null
+++ b/style.css
@@ -0,0 +1,46 @@
+/* table */
+table {
+ border: 1px solid gray;
+}
+
+th,
+td {
+ border: 1px solid gray;
+}
+
+/* ์ญ ๊ด๋ฆฌ */
+.station-container {
+ padding-top: 20px;
+ display: none;
+}
+
+/* ๋
ธ์ ๊ด๋ฆฌ */
+.line-container {
+ padding-top: 20px;
+ display: none;
+}
+
+#line-start-end-station {
+ padding-top: 20px;
+ padding-bottom: 20px;
+}
+
+/* ๊ตฌ๊ฐ ๊ด๋ฆฌ */
+.section-container {
+ padding-top: 20px;
+ display: none;
+}
+
+.section-manager {
+ display: none;
+}
+
+#section-container-table {
+ margin-top: 40px;
+}
+
+/* ์งํ์ฒ ๋
ธ์ ๋ ๊ด๋ฆฌ */
+.map-print-container {
+ padding-top: 20px;
+ display: none;
+}