JSON 서버 실습
JSON 서버 실습
JSON 요청 실습
- JSON을 요청하는 서버 실습을 진행하자.
- 실습을 위한
projecet,api폴더를 생성한다. projecet에서 json-server를 설치한다.api에서db.json을 생성하여, 다음 코드를 삽입한다.1 2 3 4 5 6
{ "posts": [ { "id": "1", "title": "a title", "views": 100 }, { "id": "2", "title": "another title", "views": 200 } ] }
이후, 해당
db.json파일을 JSON Server CLI에 넘겨준다. (port: 3000)다시
projecet에서index.html을 생성하여, 다음 문서를 작성한다.1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
<!DOCTYPE html> <html lang="ko"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>index HTML</title> <style> [contenteditable="true"]:empty::before { content: attr(placeholder); color: #999; } </style> </head> <body> <div> <div> <h1>Notion</h1> <button type="button" id="pageCreateBtn">새페이지 만들기</button> <ul id="notionList"></ul> </div> </div> <div> <button type="button" id="pageSaveBtn">저장</button> <button type="button" id="historyBackBtn">내용 history 저장</button> <button type="button" id="historyForwardBtn">내용 history forward</button> <div>pageID:<span id="pageId"></span></div> <div id="contentTitle" contenteditable="true" placeholder="새페이지 제목"></div> <div id="contentBody" contenteditable="true" placeholder="새페이지 본문"></div> </div> </body> </html>
이후, 해당 파일 실행 후, 작성된 문서 확인! (port: 5500)
JSONPlaceholder의 guide에 들어가서, 해당 가이드라인을 따른다.index.html에서 생성했던 버튼의 기능을 생각하여,
이벤트 리스너를 작성한다.예) 페이지 만들기 버튼 → Creating a resource의 fetch APi 사용
원하는 구성을 위해, 해당 코드를 복사하여 수정한다.
(+ fetch 주소를 JSON Server CLI에 명시한다.)
코드 (이벤트 리스너 등록 후)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
<!DOCTYPE html> <html lang="ko"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>index HTML</title> <style> [contenteditable="true"]:empty::before { content: attr(placeholder); color: #999; } </style> <script> document.addEventListener("DOMContentLoaded", () => { const pageCreateBtn = document.getElementById("pageCreateBtn"); pageCreateBtn.addEventListener("click", () => { fetch(**"http://localhost:3000/posts"**, { method: "POST", body: JSON.stringify({ title: "", body: "", userId: 1, }), headers: { "Content-type": "application/json; charset=UTF-8", }, }) .then((response) => response.json()) .then((json) => makePageTitle(json)); }); const notionList = document.getElementById("notionList"); const makePageTitle = (x) => { const li = document.createElement("li"); const a = document.createElement("a"); a.href = "#"; a.id = x["id"]; a.textContent = x["title"] == "" ? "새 페이지" : x["title"]; li.appendChild(a); notionList.appendChild(li); }; }); </script> </head> <body> <div> <div> <h1>Notion</h1> <button type="button" id="pageCreateBtn">새페이지 만들기</button> <ul id="notionList"></ul> </div> </div> <div> <button type="button" id="pageSaveBtn">저장</button> <button type="button" id="historyBackBtn">내용 history 저장</button> <button type="button" id="historyForwardBtn">내용 history forward</button> <div>pageID:<span id="pageId"></span></div> <div id="contentTitle" contenteditable="true" placeholder="새페이지 제목"></div> <div id="contentBody" contenteditable="true" placeholder="새페이지 본문"></div> </div> </body> </html>
- 예제처럼, 새페이지를 만들면,
db.json가 다음과 같이 동적으로 코드가 추가된다.
Notion 따라하기
- Notion 페이지 생성과 유사한 동작을 만들어보자.
- [새 페이지 만들기] 버튼 클릭 시, 목록에 새로운 페이지가 생성된다
- 페이지 제목은 빈 문자열인 경우, “새 페이지”로 표현
- 목록에서 페이지를 선택했을 때, 해당 페이지(들)의 내용을 불러온다.
- 추가로, 해당 페이지의 ID를 메인에 표현한다.
- 기본적으로, 페이지 목록이 생성되는 UI가 동작한다.
- 제목은 개행되서는 안되기 떄문에, 엔터키를 막는 동작을 넣는다.
- [저장] 버튼을 클릭 시, 해당 페이지를 저장한다.
- 본문은 개행될 때 마다, 히스토리에 저장되어야 한다.
- 히스토리에 저장되어, 개행된 행마다의 뒤로가기와 앞으로가기의 기능을 구현한다.
- 이 때, 더이상 뒤로 갈 곳이 없거나 앞으로 갈 곳이 없으면 이벤트는 더이상 동작하지 않는다.
index.html
코드 (전체 코드)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
<!DOCTYPE html> <html lang="ko"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>index HTML</title> <style> [contenteditable="true"]:empty::before { content: attr(placeholder); color: #999; } div[contenteditable="true"] { border: 1px dashed #aaa; padding: 5px; } </style> <script> document.addEventListener("DOMContentLoaded", () => { const pageCreateBtn = document.getElementById("pageCreateBtn"); pageCreateBtn.addEventListener("click", () => { fetch("http://localhost:3000/posts", { method: "POST", body: JSON.stringify({ title: "", body: "", }), headers: { "Content-type": "application/json; charset=UTF-8", }, }) .then((response) => response.json()) .then((json) => makePageTitle(json)); // .then((json) => console.log(json)); }); const notionList = document.getElementById("notionList"); const makePageTitle = (x) => { const li = document.createElement("li"); const a = document.createElement("a"); a.href = "#"; a.id = x["id"]; // 페이지 제목이 빈 문자열인 경우, 새 페이지로 표현 a.textContent = x["title"] == "" ? "새 페이지" : x["title"]; // 리스트 목록을 클릭했을 때, 내용이 연결되도록 a.addEventListener("click", (e) => { e.preventDefault(); fetch("http://localhost:3000/posts/" + e.currentTarget.id) .then((response) => response.json()) .then((json) => { setContents(json); }); }); li.appendChild(a); notionList.appendChild(li); }; // 페이지 목록 생성 const getPageTitleList = (x) => { fetch("http://localhost:3000/posts") .then((response) => response.json()) .then((json) => { json.forEach((x) => { makePageTitle(x); }); // 목록 중, 첫번째 페이지 내용을 보여주도록 setContents(json[0]); }); }; getPageTitleList(); // 목록에서 페이지를 선택했을 때 내용 불러오기 const pageId = document.getElementById("pageId"); const contentTitle = document.getElementById("contentTitle"); const contentBody = document.getElementById("contentBody"); const setContents = (x) => { pageId.textContent = x["id"]; contentTitle.textContent = x["title"]; contentBody.textContent = x["body"]; history.back = []; history.forward = []; }; // 제목 엔터키 막기 contentTitle.addEventListener("keydown", (e) => { if (e.keyCode == 13) { e.preventDefault(); } }); // 페이지 저장 const pageSaveBtn = document.getElementById("pageSaveBtn"); pageSaveBtn.addEventListener("click", (e) => { if (confirm("저장하시겠습니까?")) { fetch("http://localhost:3000/posts/" + pageId.textContent, { method: "PUT", body: JSON.stringify({ title: contentTitle.innerHTML, body: contentBody.innerHTML, }), headers: { "Content-type": "application/json; charset=UTF-8", }, }) .then((response) => response.json()) .then((json) => { notionList.querySelector("a[id='" + pageId.textContent + "']").textContent = contentTitle.innerHTML; alert("저장되었습니다!"); }); } }); // 본문에서 엔터키를 누를 때 마다, 히스토리에 저장됨 LIFO const history = { back: [], forward: [], }; contentBody.addEventListener("keydown", (e) => { if (e.keyCode == 13) { // 엔터키를 누를 때, forward가 남아 있으면 클리어 if (history.forward.length > 0) { history.forward = []; } history.back.push(e.currentTarget.innerHTML); } }); const historyBackBtn = document.getElementById("historyBackBtn"); historyBackBtn.addEventListener("click", (e) => { if (history.back.length == 0) { return; } history.forward.push(contentBody.innerHTML); contentBody.innerHTML = history.back.pop(); }); const historyForwardBtn = document.getElementById("historyForwardBtn"); historyForwardBtn.addEventListener("click", (e) => { if (history.forward.length == 0) { return; } history.back.push(contentBody.innerHTML); contentBody.innerHTML = history.forward.pop(); }); }); </script> </head> <body> <div> <div> <h1>Notion</h1> <button type="button" id="pageCreateBtn">새페이지 만들기</button> <ul id="notionList"></ul> </div> </div> <div> <button type="button" id="pageSaveBtn">저장</button> <button type="button" id="historyBackBtn">내용 history back</button> <button type="button" id="historyForwardBtn">내용 history forward</button> <div>pageID:<span id="pageId"></span></div> <div id="contentTitle" contenteditable="true" placeholder="새페이지 제목"></div> <div id="contentBody" contenteditable="true" placeholder="새페이지 본문"></div> </div> </body> </html>
db.json
코드
1 2 3 4 5 6 7 8 9
{ "posts": [ { "id": "1111", "title": "", "body": "" } ] }
추가 개념
confirm(): 사용자가 확인(OK) 또는 취소(Cancel)를 선택할 수 있는 팝업 창을 띄움
1
let result = confirm("메시지 문구");
- 결과 값으로
trueorfalse를 반환true: 사용자가 확인(OK) 버튼을 눌렀을 때false: 사용자가 취소(Cancel) 버튼을 눌렀을 때
This post is licensed under CC BY 4.0 by the author.