기획/설계
구현에 앞서 프로그램을 기획하고 어떻게 만들지 설계해야한다. 단, 이때 기획자는 반드시 어떤식으로 구현할지 함께 고려해야 한다.1
연락처 데이터 모델링
웹 어플리케이션을 구성하기 위하여 필요한 사항들을 기획해야하는데 우리는 단순 학습용이니 많은 부분은 생략하고 코드 작성과 밀접한 모델링부터 진행해보도록 하겠다.
CREATE TABLE public.contact (
username varchar(25) NOT NULL,
email varchar(50) NULL,
bio text NULL,
created int8 NULL,
CONSTRAINT contact_pk PRIMARY KEY (username)
);
프로그램이라는 것의 목적은 데이터의 체계적인 관리2에 있다. 그렇기에 프로그래밍이라고 하는 것은 기본적으로 관리에 용이한 데이터 구조를 구성하는 것이 좋다. 웹이라는 것은 이 데이터를 볼 수 있는 하나의 출력 도구일뿐 목적이 될 수 없다. 진정 좋은 프로그램은 컬럼명만 봐도 알 수 있는 법이다.
이 문서에서 바로 데이터베이스를 활용한 연동작업을 진행하지는 않을 예정이지만 데이터 타입과 필드명을 정의할 때 테이블을 구성해보는 것이 가장 자연스럽고 정확한 방법이다. 추후에 데이터 베이스를 연동할 때에도 재활용 할 수 있기 때문에 나쁘지 않은 방법이다.
연락처를 저장하기 위하여 사용자 ID
3, 이메일
, 간단한 소개
, 생성일
4가지 컬럼을 구성하였다. 사용자 ID는 중복 없는 고유키로하여 중복을 회피하면서 데이터의 KEY로 설정하였다. 생성일은 timestamp를 활용해도 좋겠으나 날짜 객체의 경우 사용이 복잡하여 int8을 할당하고 epoch time을 저장하여 활용하기로 결정했다.
라우트 모델링
사용자가 이 프로그램을 어떤 방식으로 사용하게 할지, 어떤식으로 사용하는 것이 편리할지 고민해야한다. 연락처를 관리한다는 목적을 수행하기 위하여 필요한 페이지들을 결정한다.
우리는 이전에 만든 nodejs 웹 서버를 재활용하여 간단하게 구현할 예정이다. 접속 이후에 브라우저가 제공하는 fetch()
함수를 활용하여 서버에 데이터를 추가로 요청하여 저장된 연락처 목록을 화면에 표시하는 방식으로 만들 계획이다.
이미 학습했듯이 정적인변하지 않는 html 파일을 서버로부터 응답받아 표시하는 것이 기본이다. 동적인가변적인 데이터가 존재하여 html 내용도 동적으로 변경되어야 할 때에는 두가지 방법이 있다.
하나는 서버에서 가변 데이터를 포함시킨 HTML을 매번 생성하여 응답jsp 등해주는 것이고, 두번째는 자바스크립트를 활용하여 비동기 요청fetch으로 가변 데이터를 응답받아 화면에 표시하는 것이다. 이 두 방법 모두 장단점이 있고 두번째 방식이 대세였으나 최근에는 두 방식을 적절히 혼합하여 사용하는 방식SSR을 많이 사용하고 있다.
페이지 라우트
연락처 목록: /index.html
연락처 상세: /detail.html
연락처 생성: /create.html
연락처 편집: /update.html
API 라우트
// 연락처 CRUD
(GET) /api/contact
(PUT) /api/contact
(POST) /api/contact
(DELETE) /api/contact
API 개발
const users = [
{
username: "luasenvy",
email: "luas.envy@gmail.com",
bio: "제공한 정보가 유익하길 바랍니다. :)",
created: 1738679688507,
}
];
function getContacts(username) {
return users.find(({ username: _username }) => username === _username);
}
function searchContacts() {
return users;
}
const server = http.createServer((req, res) => {
let { pathname, searchParams } = new URL(req.url,`http://${req.headers.host}`);
// API 라우트
if(/^\/api\/contact\/?$/.test(pathname)) {
switch(req.method) {
case "GET": {
const username = searchParams.get("username");
const list = username ? getContacts(username) : searchContacts(searchParams);
res.writeHead(200, { "Content-Type": "application/json" });
return res.end(Buffer.from(JSON.stringify(list)));
}
default: {
res.writeHead(405);
return res.end("Method Not Allowed");
}
}
}
// 루트로 접속하면 index.html 파일 지정
if ("/" === pathname) pathname = "/index.html";
/** else... */
페이지가 정상적으로 표시되려면 API가 먼저 완성되어야 한다. 앞서 배웠던 라우트 규칙 따라서 GET
에 대한 라우트를 구현한다. 필요하다면 POST
, PUT
등 restful에 맞추어 기능을 분할할 수 있다. CRUD에서 조회기능의 경우는 검색하여 목록을 조회하는 기능과 id로 지정하여 하나만 조회하는 기능으로 구분하기도 한다. 이유는 목록내에서 하나의 행을 선택하여 상세보기로 전환되는 기능을 생각해보면 된다. 준비된 데이터베이스와 연동하는 것은 다음 문서에서 진행해보는 걸로 하고 우선 변수에 할당하여 빠르게 결과를 확인해보자.
클라이언트 개발
index.html
<!-- else... -->
<main>
<h1>연락처</h1>
<ul id="contact"></ul>
</main>
<!-- else... -->
app.js
(function (){
document.addEventListener("DOMContentLoaded", async function() {
const res = await fetch("/api/contact");
const contacts = await res.json();
const list = document.querySelector("#contact");
contacts.forEach(function(contact) {
const li = document.createElement("li");
li.textContent = `${contact.username} (${contact.email})`;
list.appendChild(li);
});
});
})();
앞서 작성한 index.html
의 <main />
부분을 수정하고 app.js
에 프로그램을 작성한다. 이렇게 작성하면 HTML 문서가 브라우저에 준비된 후 fetch()
함수를 통하여 화면 이동 없이 비동기 요청을 서버로 보내 json 데이터를 응답받아 파싱하고 ul 태그 하위에 목록을 추가할 수 있다.