https://sequelize.org/
๐ ๊ณต์ ๋ฌธ์๋ฅผ ํตํด ์คํ๋ฆฐํธ๋ฅผ ์งํํ๋ค.
๐ ๊ณต์ ๋ฌธ์ ์ฝ๋ ๋ฒ์ ์ตํ๋ค.
mysql> describe urls;
+-----------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+--------------+------+-----+---------+----------------+
| id | int | NO | PRI | NULL | auto_increment |
| url | varchar(255) | YES | | NULL | |
| title | varchar(255) | YES | | NULL | |
| visits | int | YES | | NULL | |
| createdAt | datetime | NO | | NULL | |
| updatedAt | datetime | NO | | NULL | |
+-----------+--------------+------+-----+---------+----------------+
์ด๋ฒ ์คํ๋ฆฐํธ๋ bitly ์ฒ๋ผ ๊ธด url์ ์งง๊ฒ ์ถ์ฝํด์ฃผ๋ ์ฌ์ดํธ์ ๋ก์ง์ ๋ํด์ ํ์ตํ๋ค.
urls ๋ผ๋ ํ ์ด๋ธ์ ๋ง๋ค์ด, ์๋ณธ URL๊ณผ ๋จ์ถ URL์ ๋ฐฉ๋ฌธ ํ์๋ฅผ ๊ธฐ๋กํ๋ค. SQL๋ฌธ์ด ์๋ ORM์ ์ด์ฉํด ์๋์ ๊ฐ์ ํ ์ด๋ธ์ ๋ง๋ ๋ค.
๐ (1-1) ORM ์ค์
โ cli๋ฅผ ํตํด ํ์ํ ํ์ผ์ด ์๋์ผ๋ก ๋ง๋ค์ด์ก๋์ง ํ์ธํฉ๋๋ค
โ model/index.js ํ์ผ์ด ์ ํจํ์ง ํ์ธํฉ๋๋ค
Executing (default): SELECT 1+1 AS result
โ mysql์ ์ ์ํ ์ ์๋์ง ํ์ธํฉ๋๋ค (140ms)
sequelize๋ promise ๊ธฐ๋ฐ์ Node.js ORM์ผ๋ก์ ์ฐ๋ฆฌ๊ฐ ์ฐ๋ MySQL์ ์ํ ํ๋ก๊ทธ๋จ์ด๋ค. ORM์ Object Relational Mapping์ ์ฝ์๋ก ๊ฐ์ฒด ์งํฅ ์ธ์ด์์ ๊ด๊ณํ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ๊ทผํ ์ ์๋๋ก ๋์์ฃผ๋ ์ค๊ฐ ๋ค๋ฆฌ ์ญํ ๋ก ๋ณด๋ฉด๋๋ค. ์ฝ๊ฒ ๋งํด ๋ฐ์ดํฐ ๋ฒ ์ด์ค์ ์ํตํ ์ ์๊ฒ ํด์ค๋ค.
๋ช
๋ น์ด : npm i sequelize
sequelize-cli๊ฐ ๋ง์ด๊ทธ๋ ์ด์ ์ ํ ์ ์๋๋ก ๋๋ ํด๋ก, CLI์์ ๋ชจ๋ธ์ ์์ฑํด์ฃผ๊ฑฐ๋, ์คํค๋ง ์ ์ฉ์ ํ ์ ์๋๋ก ๋๋๋ค๊ณ ํ๋ค. ๊ณต์๋ฌธ์๋ฅผ ํตํด ์ข ๋ ์ดํด๋ณด์.
๋ง์ด๊ทธ๋ ์ด์ ์ Git๊ณผ ๊ฐ์ ๋ฒ์ ์ปจํธ๋กค ์์คํ ์ฒ๋ผ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ณ๊ฒฝ ์ฌํญ์ ์ถ์ ํ๊ณ ์ ์ ์๊ฒ ํ๋ค. ๋ํ ๋ง์ด๊ทธ๋ ์ด์ ์ ์๋ฐ์คํฌ๋ฆฝํธ ํ์ผ๋ก์ ๋ ๊ฐ์ ํจ์๋ฅผ ๋ฐ์ผ๋ก ๋ด๋ณด๋ด๋๋ฐ ํ๋๋ ๋ง์ด๊ทธ๋ ์ด์ ์ ์ํํ๋ up์ด๊ณ , ๋ค๋ฅธ ํ๋๋ ๋ง์ด๊ทธ๋ ์ด์ ์คํ์ ์ทจ์ํ๋ down์ด๋ค.
๋ช
๋ น์ด : npm i โsave-dev sequelize-cli
cli๋ฅผ ํตํด ORM์ ์ ์ฌ์ฉํ ์ ์๋๋ก bootstraping(ํ๋ก์ ํธ ์ด๊ธฐ ๋จ๊ณ๋ฅผ ์๋์ผ๋ก ์ค์ ํ ์ ์๋๋ก ๋์์ฃผ๋ ์ผ)์ ํด์ค์ผ ํ๋ค.
๋น ํ๋ก์ ํธ๋ฅผ ๋ง๋๋ ๋ช
๋ น์ด : npx sequelize-cli init
์ด ๋ช ๋ น์ด๋ฅผ ์ฌ์ฉํ๋ฉด ๋ค์๊ณผ ๊ฐ์ ํด๋๊ฐ ๋ง๋ค์ด์ง๋ค.
1) config : ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ฐ๊ฒฐํ๋ ๋ฐฉ๋ฒ์ CLI์ ์๋ ค์ฃผ๋ ๊ตฌ์ฑ ํ์ผ์ด ํฌํจ๋์ด ์์ต๋๋ค.
2) models : ํ๋ก์ ํธ์ ๋ชจ๋ ๋ชจ๋ธ์ ํฌํจํฉ๋๋ค.
3) migrations : ๋ชจ๋ ๋ง์ด๊ทธ๋ ์ด์
ํ์ผ ํฌํจ
4) seeders : ๋ชจ๋ ์๋ ํ์ผ์ ํฌํจํฉ๋๋ค.
๋จผ์ cli์ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์ฐ๊ฒฐํ๋ ๋ฐฉ๋ฒ์ ์์์ผ ํ๋ค.
config/config.json ํ์ผ์ ์ด๋ฉด ๋ค์๊ณผ ๊ฐ์ด ๋ณด์ธ๋ค.
{
"development": {
"username": "root",
"password": null,
"database": "database_development",
"host": "127.0.0.1",
"dialect": "mysql"
},
"test": {
"username": "root",
"password": null,
"database": "database_test",
"host": "127.0.0.1",
"dialect": "mysql"
},
"production": {
"username": "root",
"password": null,
"database": "database_production",
"host": "127.0.0.1",
"dialect": "mysql"
}
}
3๊ฐ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค๊ฐ ์กด์ฌํ๋๋ฐ ๊ฐ๊ฐ ๊ฐ๋ฐ, ํ
์คํธ, ๋ฐฐํฌ์ฉ์ด๋ผ๋ ์๋ฏธ๋ฅผ ๊ฐ์ง๊ณ ์๋ค. ์ฐ๋ฆฌ๋ ๊ธฐ๋ณธ์ ์ผ๋ก development ์ฆ, ๊ฐ๋ฐ์ฉ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์ฐ๊ณ ์๋๋ฐ models
ํด๋์ index.js
ํ์ผ์ ๋ณด๋ฉด ์ ์ ์๋ค.
const env = process.env.NODE_ENV || 'development';
๋ณ์ env๋ ๋ค๋ฅธ ๊ฐ์ด ์ฃผ์ด์ง์ง ์์ผ๋ฉด ๊ธฐ๋ณธ ๊ฐ์ผ๋ก 'development'๋ฅผ ๊ฐ์ง๊ณ ์๋ค. ๋ฐ๋ผ์ ์ฐ๋ฆฌ๊ฐ ์ฌ๊ธฐ์ ๋ค๋ฅธ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์ ํํ์ง ์์ผ๋ฉด ๊ธฐ๋ณธ ๊ฐ์ธ 'development'๊ฐ ์ค์ ์ด ๋๋ค.
๋ค์ config.json ํ์ผ๋ก ๋์์์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ช ๊ณผ ๋น๋ฐ๋ฒํธ๋ฅผ ์ ๋ ฅํด ์ฃผ์๋ค. ์ฐธ๊ณ ๋ก ๋ฐ์ดํฐ๋ฒ ์ด์ค๊ฐ ์์ง ์กด์ฌํ์ง ์๋ ๊ฒฝ์ฐ db:create๋ช ๋ น์ ํธ์ถํ๋ฉด ๋๋ค. ์ ์ ํ ์ก์ธ์ค๊ฐ ์์ผ๋ฉด ํด๋น ๋ฐ์ดํฐ๋ฒ ์ด์ค๊ฐ ์์ฑ๋๋ค.
CLI config file์ ์ ๋๋ก ์ค์ ํ์๋ค๋ฉด ์ด์ ์ฒซ ๋ชจ๋ธ๊ณผ ๋ง์ด๊ทธ๋ ์ด์ ์ ์์ฑํด์ผ ํ๋ค.
๋ชจ๋ธ ์์ฑ
โ url ๋ชจ๋ธ์ด ์กด์ฌํด์ผ ํฉ๋๋ค
โ url ๋ชจ๋ธ์ ์๊ตฌํ๋ ํ๋๋ฅผ ๊ฐ๊ณ ์์ด์ผ ํฉ๋๋ค
โ url ๋ชจ๋ธ์ ๊ฐ ํ๋๋ ์ ํด์ง ํ์
์ผ๋ก ์์ฑ๋์ด์ผ ํฉ๋๋ค
โ url ๋ชจ๋ธ์ visits ํ๋๋ ๊ธฐ๋ณธ๊ฐ์ด 0์ด์ด์ผ ํฉ๋๋ค
ํ ์คํธ ์ผ์ด์ค์ ๋ง๊ฒ ์์ฑํด์ค๋ค.
model:generate
๋ช
๋ น์ด๋ฅผ ํตํด ์์ฑํ ์ ์๋ค.
์์ฑ๋ urls ํ ์ด๋ธ์ ์คํค๋ง๋ฅผ ๋ณด๋ฉด ํ์ํ ํ๋ ์ด๋ฆ๊ณผ ์์ฑ์ ์ ์ ์๊ณ , ์ด๋ป๊ฒ ์ ์ด์ผ ํ๋์ง๋ ๊ณต์ ๋ฌธ์๋ฅผ ์ฐธ๊ณ ํ์๋ค.
npx sequelize-cli model:generate --name url --attributes url:string,title:string,visits:integer
migrations์ models ํด๋์๋ ๊ฐ ํ์ผ์ด ์ ์์ฑ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
์ด๋ ์ค์ํ ์ ์ ๋ชจ๋ธ ๋ช ์ ๋จ์๋ก ํ๊ธฐํ๋ค. ๋ง์ด๊ทธ๋ ์ด์ ํ์๋ ์์์ ๋ณต์๋ก ๋ฐ๋๊ธฐ ๋๋ฌธ์ด๋ค.
๋ฐ์ ํ ์คํธ ์ผ์ด์ค๊ฐ ์๋๋ฐ
โ ๋ง์ด๊ทธ๋ ์ด์
์ ํ๋ค๋ฉด, urls ํ
์ด๋ธ์ด ์กด์ฌํด์ผ ํฉ๋๋ค (87ms)
์ค์ ๋ก ๋ชจ๋ธ๋ช ์ url๋ก ์ ํด์ ์์ฑํด์ฃผ๋ฉด, ๋ง์ด๊ทธ๋ ์ด์ ํ์ผ์์ ์๋์ผ๋ก urls๋ก ๋ฐ๋์ด ์์ฑ๋์ด ์๋ ๊ฑธ ํ์ธํ ์ ์๋ค.
ํ ์คํธ ์ผ์ด์ค์ ์กฐ๊ฑด์ url ๋ชจ๋ธ์ visits ํ๋๋ ๊ธฐ๋ณธ๊ฐ์ด 0์ด์ด์ผ ํ๋ค. ์ด๋์ ๊ฐ์ ์ ํด์ค์ผ ํ๋๋ฉด, ๋ง์ด๊ทธ๋ ์ด์ ํด๋๊ฐ ์๋๋ผ ๋ชจ๋ธ์ ์ค์ ๋ฐ์ดํฐ๊ฐ ๋ด๊ฒจ์๋ ๋ชจ๋ธ ํด๋์์ ์ ๋ ฅํด์ค์ผ ํ๋ค. ๊ทธ๋์ models์ url.js ํ์ผ์ ๋ค์ด๊ฐ์ visits ๊ฐ์ defaultValue๋ฅผ ์ถ๊ฐํด์ค๋ค. ๊ทธ๋ฆฌ๊ณ ์ค์ ๋ก๋ ์ ์ดํด๋์ค์ ์จ์ ธ์๋ค.
๊ณต์ ๋ฌธ์๋ฅผ ๋ณด๊ณ ์ด๋ ๊ฒ ์์ ํ๋๋ฐ ๋๋ ์ฒ์์ ๋จ์ํ
url<.init({
... : ...,
visits : DataTypes.INTEGER,
defaultValue:0
}, ...)
๋ผ๊ณ ์ ์๋ค. ๊ทธ๋ฌ๋๋ ํต๊ณผ๊ฐ ๋์ง ์์๋ค.
Q. ์ ์์ฑ๊ฐ์ ๊ฐ์ฒด๋ก ๋ฌถ์ด์ค์ผ ํ ๊น?
A. ๊ฐ์ฒด๋ก ๋ฌถ์ด์ฃผ์ง ์๊ณ ๋ฐ์ ๋นผ๋ฉด ๊ทธ ์น๊ตฌ๊ฐ visit์ ์ํ ์น๊ตฌ์ธ์ง ์ด๋ป๊ฒ ์ ์ ์์๊น? defaultValue๋ผ๋ ์ด๋ฆ์ ์ปฌ๋ผ์ผ๋ก ์ธ์ํ์ง ์์๊น?
Q. ์ ๋ฐฐ์ด์ด ์๋๊ณ ๊ตณ์ด ๊ฐ์ฒด์ธ๊ฐ?
A. []์ ๋ฌธ์์ด ํค๋ฅผ ๊ฐ์ง์ ์๋?
์ํ๋ผ์ด์ฆ๋ก ๋ชจ๋ธ์ ์์ฑํ๋ฉด ๋ง์ด๊ทธ๋ ์ด์ ์ด ์๊ธด๋ค. ๋ง์ด๊ทธ๋ ์ด์ ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ค๊ณ๋(์คํค๋ง ๋์์ธ๊ฐ์)๋ผ๊ณ ์๊ฐํ๋ฉด ๋๋ค. ๊ทธ๋ผ ๋ง์ด๊ทธ๋ ์ด์ ๋ง ํ์ํ์ง ์์๊น? ์ ๋ชจ๋ธ๊น์ง ๋ง๋๋ ๊ฑธ๊น?
์๋ฐ์คํฌ๋ฆฝํธ ๋ฌธ๋ฒ์์ DB์์ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ค๊ฑฐ๋ ์ถ๊ฐํ๊ฑฐ๋ ์ญ์ ํ๋ ๋ก์ง์ ์์ฑํด์ผ ๋๋๋ฐ(DB๋ฅผ ์ปจํธ๋กคํ ์ ์๋ ๋ฌธ๋ฒ) ๊ทธ๊ฑธ ์ฌ์ฉํ ์ ์๊ฒ ๋ชจ๋ธ์ด ๋งค๊ฐ์ฒด๊ฐ ๋๋ ๊ฒ์ด๋ค. ๋ฐ๋ผ์ ๋ชจ๋ธ์ ๋ฐ์ดํฐ ๋ฒ ์ด์ค๋ ๊ด๋ จ๋ ๋ก์ง์ ์๋ฐ์คํฌ๋ฆฝํธ ์ธ์ด๋ก ์ฒ๋ฆฌํ๋ ๊ณณ์ด๋ผ๊ณ ๋ณผ ์ ์๋ค.
npx sequelize-cli db:migrate
๋ง์ด๊ทธ๋ ์ด์
์ ์๋ ํฌ๋ ์ดํธ ํ
์ด๋ธ์ ์คํํ๋ค. ์ด๊ฑธ ํ๊ธฐ ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์๋ ์๋ฌด๊ฒ๋ ์๋ค. ๋ฌ๋ ๋ง์ด๊ทธ๋ ์ด์
์ ํ๊ฒ ๋๋ฉด ๋ฐ์ดํฐ ๋ฒ ์ด์ค์ ํ
์ด๋ธ์ด ์์ฑ๋๋ค. ์ฆ, ๋ง์ด๊ทธ๋ ์ด์
์ ์คํ์์ผ์ผ ์ค์ ๋ก ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ํ
์ด๋ธ ๋ฐ์ดํฐ๊ฐ ๋ค์ด๊ฐ๋ ๊ฒ์ด๋ค. ์ด๋ฏธ ๋ชจ๋ธ ํ์ผ์ด ๋ง๋ค์ด์ ธ์๋ค๋ฉด running migration์ ํด์ ๋ฐ์ดํฐ ๋ฒ ์ด์ค์ ์ฐ๊ฒฐํด์ค๋ค.
๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ค๋น๊ฐ ์๋ฃ๋์์ผ๋, MVC ํจํด์ ์ ์ฉํ์ฌ Short.ly์ ๋ฐฑ์๋ ๋ถ๋ถ์ ์์ฑํฉ์๋ค. ๋ผ๊ณ ํ๋ค.
๐ (2-3) controller ๊ตฌํ
5) POST /links์ url์ ๋ฐ์ ๋จ์ถ url๋ก ๋ง๋ญ๋๋ค
6) GET /links๋ urls ํ
์ด๋ธ์ ๋ชฉ๋ก์ JSON์ ํํ๋ก ๋ฐํํฉ๋๋ค
7) GET /links/:id ์ ์์ฒญํ๋ฉด url ํ๋๊ฐ์ผ๋ก ๋ฆฌ๋๋ ์
ํฉ๋๋ค
8) GET /links/:id ์ ์์ฒญํ๋ฉด, ํด๋น ๋ ์ฝ๋์ visit count๊ฐ 1 ์ฆ๊ฐํด์ผ ํฉ๋๋ค
๋ธ๋ผ์ฐ์ ๋ ๋ทฐ๋ ์ํต์ ํด์ ํ์ฑํ ํ ์ฌ์ฉ์ํํ
๋ทฐ๋ฅผ ๋ณด์ฌ์ค๋ค.
์ด๋ป๊ฒ ๋์ํ๋ ๊ฑธ๊น?
๋จผ์ , ๋ธ๋ผ์ฐ์ ์ ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ๋ฉด ์๋ฒ์ ์์ฒญ์ ํ๋ค. ์ด๋ ๋ผ์ฐํฐ๋ฅผ ๋จผ์ ๊ฑฐ์น๋ค. ๊ทธ ํ ๋ผ์ฐํ ๋ ์์ฒญ๋ค์ด ์ปจํธ๋กค๋ฌ๋ฅผ ํตํด์ ํ๋ฌ๊ฐ๋ค. ์ด ์ปจํธ๋กค๋ฌ๋ ์ด์ ๋ฐ์ดํฐ ๋ฒ ์ด์ค์ ์ฐ๊ฒฐ์ด ๋์ด์ผํ๊ธฐ ๋๋ฌธ์, ๋ฐ์ดํฐ ๋ฒ ์ด์ค๋ ๊ด๋ จ๋ ๋ก์ง์ ์ฒ๋ฆฌํ๋ ๊ณณ์ธ ๋ชจ๋ธ(DB์ ์ํต = sequelize ORM)๊ณผ ์ํต์ ํ๋ค.
๐ (2-1) controller ์์ฑ
1) links controller ํ์ผ์ด ์กด์ฌํด์ผ ํฉ๋๋ค
2) links controller์๋ get, post ๋ฉ์๋๊ฐ ๊ฐ๊ฐ ์กด์ฌํด์ผ ํฉ๋๋ค
================================================================================
๐ (2-2) router ์ฐ๊ฒฐ
3) "before all" hook for "POST /links๋ links controller์ post ๋ฉ์๋๋ฅผ ์คํํฉ๋๋ค"
4) "after all" hook for "GET /links๋ links controller์ get ๋ฉ์๋๋ฅผ ์คํํฉ๋๋ค"
ํ ์คํธ ์ผ์ด์ค ํ์ผ์ ๋ณด๊ณ , controllers ํด๋ ์์ links ํด๋๊ฐ ์์ด์ผ ํ๊ณ ๊ทธ ์์ index.jsํ์ผ์ ์์ฑํด์ผ ๋๋ค๋ ๊ฑธ ํ์ ํ๋ค. ํด๋์ ํ์ผ ์์ฑ ํ get, post ๋ฉ์๋๋ฅผ ์ถ๊ฐํด์คฌ๋ค. router๋ฅผ ์ฐ๊ฒฐํด์ฃผ๊ธฐ ์ํด์ exportํด์ค ํ links ํด๋์์ importํด์ฃผ๊ณ ํ ์คํธ ์ผ์ด์ค์ ๋ง๊ฒ ์ฐ๊ฒฐํด์ฃผ์๋ค.
๐ (2-3) controller ๊ตฌํ
5) POST /links์ url์ ๋ฐ์ ๋จ์ถ url๋ก ๋ง๋ญ๋๋ค
6) GET /links๋ urls ํ
์ด๋ธ์ ๋ชฉ๋ก์ JSON์ ํํ๋ก ๋ฐํํฉ๋๋ค
7) GET /links/:id ์ ์์ฒญํ๋ฉด url ํ๋๊ฐ์ผ๋ก ๋ฆฌ๋๋ ์
ํฉ๋๋ค
8) GET /links/:id ์ ์์ฒญํ๋ฉด, ํด๋น ๋ ์ฝ๋์ visit count๊ฐ 1 ์ฆ๊ฐํด์ผ ํฉ๋๋ค
bitly
๊ธด url์ ์งง๊ฒ ์ถ์ฝํด์ฃผ๋ ์ฌ์ดํธ! ๋ฆฌ๋ค์ด๋ ํธ๋ฅผ ํตํด ์ฐ๊ฒฐ
1. ์ค์ด๊ณ ์ถ์ URL์ ๋ฃ๋๋ค.
2. Shorten์ ๋๋ฅธ๋ค
3. ์ถ์ฝ๋ ๋งํฌ๊ฐ ์์ฑ๋๋ค
4. ํด๋น ๋งํฌ๋ฅผ ๋๋ฅด๋ฉด ์๋ณธ URL๋ก ์ด๋ํ๋ค
POST links ์ปจํธ๋กค๋ฌ ๋ก์ง
1. URL์ ํฌ์คํธ ์์ฒญ์ผ๋ก ๋ฐ๋๋ค { url :codestate.com}
2. ์์ฒญ์ ์๋ ์ฃผ์๋ฅผ db์ ๋ ์ฝ๋๋ก ์์ฑ
3. 2-1) ์ฝ๋์คํ
์ด์ธ url์ด ์ ์ฅ
4. 2-2) url์ id๊ฐ ์ ์ฅ -> get ์์ฒญ์ ์ฐ์
5. 2-3) ํํ์ด์ง์ ํ์ดํ์ด ์ ์ฅ
6. 2-4) ๋ฐฉ๋ฌธ ํ์๊ฐ ์ ์ฅ(visitsํ๋)-> ๋๊ตฐ๊ฐ ๋ฆฌ๋ค์ด๋ ํธ ํ ๋๋ง๋ค ์
๋ฐ์ดํธ๊ฐ ๋๋ค
7. ๋ง์ฝ ์ด๋ฏธ ์กด์ฌํ๋ url์ด๋ผ๋ฉด ๊ทธ๋ฅ ๋์ด๊ฐ๋ค
GET links/:id ์ปจํธ๋กค๋ฌ ๋ก์ง
1. Links/1 get ์์ฒญ์ด ์ค๋ฉด ๋ ์ฝ๋๋ฅผ ์์ฑํ๋ ๊ฒ ์๋๋ผ ๋ ์ฝ๋๋ฅผ ์กฐํํ๋ค (id๋ฅผ ๊ฐ์ง๊ณ )
2. ํด๋น ๋ ์ฝ๋ url์ ์ปจํธ๋กค๋ฌ์์ ๋ฆฌ๋ค์ด๋ ํธ ์ํด
3. ๊ทธ๋ฆฌ๊ณ visits ํ๋ ๊ฐ์ 1 ์ถ๊ฐ