Post
엔티티의 writable
필드로 하여금 임시글 공개글을 구분토록 하였다. 게시글 카드를 모아보는 개인 블로그 첫 페이지에서 게시글(태그 포함) 정보를 가져올때 writable이 true인 경우만 가져오도록 하였으며, writable이 false로 저장된 경우는 따로 임시글 탭에서 이를 확인할 수 있도록 하였다.
임시글을 클릭할 경우 기존의 수정 기능을 통해 이어작성 가능하도록 하였다.
자동저장은 수정 템플릿에서, 첫 작성 템플릿에서 각각 수정 자동저장
, 첫작성 자동저장
으로 구분했다.
첫 작성 템플릿에서 자동저장 기능이 수행되면, writable=false로 하여금 게시글을 저장하고 곧바로 해당 게시글 수정 템플릿으로 리다이렉트 시켰다.
1번 시나리오에 대해 1번의 auto-save 이후 지속적인 auto-edit이 진행된다. 작성 이전까지 해당 글은 계속 임시글로 남게 된다.
2번 시나리오는 1번 시나리오에서 auto-save 과정이 빠지고 auto-edit이 계속 동작한다.
3번 시나리오에서는 자동저장이 수행되지 않는다.
document.addEventListener('DOMContentLoaded', () => {
const autoSaveInterval = 60000; // 1분 (60,000ms)
const editor = window.editor; // Toast UI Editor 인스턴스 가져오기
function autoSave() {
if (document.hidden) {
console.log("브라우저가 비활성화 상태입니다. 자동 저장을 건너뜁니다.");
return;
}
const content = encodeURIComponent(editor.getMarkdown()); // 에디터에서 작성 중인 내용
const title = document.querySelector('input[name="title"]').value; // 제목 가져오기
const tags = document.querySelector('#tags').value; // 태그 가져오기
const tagsList = tags.split(','); // 쉼표를 기준으로 문자열 분리
// 제목이 빈값인 경우 실행 중단
if (!title) {
console.warn("제목이 비어있어 자동 저장을 건너뜁니다.");
return;
}
// Ajax 요청 생성
const xhr = new XMLHttpRequest();
xhr.open("POST", "/api/write/auto-save", true); // 비동기 POST 요청
xhr.setRequestHeader("Content-Type", "application/json");
// 요청 데이터 준비
const data = JSON.stringify({
title: title,
content: content,
tags: tagsList,
});
console.log(data);
// 요청 전송
xhr.send(data);
// 요청 상태 확인
xhr.onreadystatechange = function () {
if (xhr.readyState === XMLHttpRequest.DONE) {
if (xhr.status === 200) {
console.log("자동 저장 성공:", xhr.responseText);
const response = JSON.parse(xhr.responseText);
// postUrl이 존재할 경우 리다이렉트
if (response.postUrl) {
window.location.href = `/edit/${response.postUrl}`;
} else {
console.error("postUrl이 응답에 없습니다.");
}
} else {
console.error("자동 저장 실패:", xhr.statusText);
}
}
};
}
// 1분마다 자동 저장 실행
setInterval(autoSave, autoSaveInterval);
});
function autoEdit() {
if (document.hidden) {
console.log("브라우저가 비활성화 상태입니다. 자동 저장을 건너뜁니다.");
return;
}
const content = encodeURIComponent(editor.getMarkdown()); // 에디터에서 작성 중인 내용
const title = document.querySelector('input[name="title"]').value; // 제목 가져오기
const tags = document.querySelector('#tags').value; // 태그 가져오기
const tagsList = tags.split(','); // 쉼표를 기준으로 문자열 분리
const postUrl = window.location.pathname.split('/').filter(Boolean).pop();
console.log(postUrl);
// 제목이 빈값인 경우 실행 중단
if (!title) {
console.warn("제목이 비어있어 자동 저장을 건너뜁니다.");
return;
}
// Ajax 요청 생성
const xhr = new XMLHttpRequest();
xhr.open("POST", "/api/write/auto-edit", true); // 비동기 POST 요청
xhr.setRequestHeader("Content-Type", "application/json");
// 요청 데이터 준비
const data = JSON.stringify({
title: title,
content: content,
tags: tagsList,
postUrl: postUrl
});
console.log(data);
// 요청 전송
xhr.send(data);
// 요청 상태 확인
xhr.onreadystatechange = function () {
if (xhr.readyState === XMLHttpRequest.DONE) {
if (xhr.status === 200) {
console.log("자동 저장 성공:", xhr.responseText);
// JSON 데이터 파싱
const response = JSON.parse(xhr.responseText);
// postUrl이 존재할 경우 리다이렉트
if (response) {
console.log("자동 업데이트 성공")
new Toastify({
text: "🦄 게시글 자동 저장 성공!",
duration: 5000, // autoClose 5000ms
gravity: "top", // top-right 위치 설정
position: "right",
stopOnFocus: true, // pauseOnHover
close: true, // closeOnClick
style: {
background: "linear-gradient(to right, #00b09b, #96c93d)", // 테마 스타일
color: "#fff",
borderRadius: "8px",
boxShadow: "0 2px 6px rgba(0, 0, 0, 0.15)",
}
}).showToast();
} else {
console.error("postUrl이 응답에 없습니다.");
}
} else {
console.error("자동 저장 실패:", xhr.statusText);
}
}
};
}
// 1분마다 자동 저장 실행
setInterval(autoEdit, autoSaveInterval);
});
XMLHttpRequest
를 사용하여 ajax 비동기 요청을 구현하였다.(정상적인 작동을 확인하기 위해 일단 log를 잔뜩 찍어놓았다.)
후에 리다이렉트 될 때 스크롤 위치를 동일하게 하여 이용자가 화면이 리다이렉트된 지 모르도록 할 계획이다.
제목이 비었을 경우 자동저장을 수행하지않는다. 또한 여러 작성 탭을 띄워뒀을 경우 활성화된 탭만 코드가 작동하도록 함수 초반에 이를 필터링해두었다.
Toastify
를 활용하여 자동 저장 성공시 자동저장을 알려주는 기능을 껴넣었다.