코드보기 : https://github.com/maplesyrup0423/DevTyper
DB를 이용하는 방법은 기존 다른 프로젝트에서 많이 사용해 봤기에 이번에는 로컬 스토리지를 사용하는 방법으로 진행하기로 했다.
1. 로컬 스토리지 사용
로컬 스토리지는 사용자의 브라우저에 데이터를 저장하는 방식입니다.
장점
- 간단함: 서버 설정이나 데이터베이스 없이 빠르게 구현할 수 있어 간단한 개인 프로젝트에 적합합니다.
- 속도: 로컬 스토리지에 저장된 데이터는 브라우저 내에서 바로 접근 가능하므로 속도가 빠릅니다.
오프라인 가능: 네트워크 연결 없이도 데이터를 로드하고 관리할 수 있습니다.단점
- 브라우저마다 독립적: 로컬 스토리지는 브라우저마다 따로 저장되므로 사용자가 다른 기기나 브라우저를 사용하면 기록을 볼 수 없습니다.
- 용량 제한: 로컬 스토리지에는 보통 5~10MB의 용량 제한이 있어, 데이터를 무제한으로 저장할 수 없습니다.
- 보안 약점: 로컬 스토리지에 저장된 데이터는 브라우저 개발자 도구로 쉽게 접근할 수 있어 민감한 정보는 적합하지 않습니다.
2. 서버와 데이터베이스 사용
서버에 데이터를 저장하는 방식은 데이터베이스를 통해 서버에 정보를 저장하는 방법입니다.
장점
- 기기 간 데이터 동기화: 서버에 저장된 데이터는 사용자가 어느 기기에서나 접근할 수 있어 여러 브라우저나 기기에서 같은 기록을 공유할 수 있습니다.
- 확장성: 데이터를 더 많이 저장하거나 복잡한 쿼리를 사용할 수 있어 대규모 프로젝트나 향후 기능 확장에 유리합니다.
- 보안 강화: 서버는 더 높은 수준의 보안을 적용할 수 있으므로 민감한 정보 저장에도 적합합니다.
단점
- 설정의 복잡성: 서버와 데이터베이스 설정 및 유지 관리는 초기 설정이 더 복잡합니다.
- 네트워크 의존성: 사용자가 데이터를 불러오기 위해 항상 네트워크 연결이 필요합니다.
- 비용: 클라우드 서버와 데이터베이스를 사용할 경우 비용이 발생할 수 있습니다.
추천:
- 간단한 개인 프로젝트나 학습 목적이라면 로컬 스토리지를 활용하는 것이 더 빠르고 쉬운 선택이 될 수 있습니다.
- 다수의 기기에서 접근해야 하거나 향후 확장 가능성을 고려한 서비스라면 서버와 데이터베이스를 사용하는 것이 더 적합합니다.
const saveTypingRecord = (time, accuracy, wpm) => {
const completedAt = new Date().toLocaleString(); // 완료된 시간
const record = { completedAt, time, accuracy, wpm };
// 기존 기록을 가져와 배열로 저장하거나 빈 배열로 초기화
const storedRecords = JSON.parse(localStorage.getItem("typingRecords")) || [];
storedRecords.push(record);
// 로컬 스토리지에 갱신된 기록 저장
localStorage.setItem("typingRecords", JSON.stringify(storedRecords));
};
// 타이핑 완료 및 기록 저장
useEffect(() => {
const cleanedCodeToType = codeToType.replace(/\s+/g, " ").trim();
if (userInput.length >= cleanedCodeToType.length && userInput.length > 0) {
const endTime = new Date().getTime();
setIsFinished(true);
const timeTaken = (endTime - startTime) / 1000; // 초 단위 소요 시간
const correctChars = userInput
.split("")
.filter((char, index) => char === cleanedCodeToType[index]).length;
// 정확도 계산
const accuracy = ((correctChars / cleanedCodeToType.length) * 100).toFixed(2);
setAccuracy(accuracy);
// 총 입력한 글자 수 기준 WPM 계산
const wpm = (userInput.length / 5 / (timeTaken / 60)).toFixed(2); // 5글자 = 1 단어 기준
setWpm(wpm);
// 기록을 로컬 스토리지에 저장
saveTypingRecord(timeTaken.toFixed(1), accuracy, wpm);
}
}, [userInput, codeToType, startTime]);
const [typingRecords, setTypingRecords] = useState([]);
//컴포넌트가 마운트될 때 로컬 스토리지에서 기록을 불러옴
useEffect(() => {
fetchJSFilesFromGithub(); // 컴포넌트가 처음 렌더링될 때 .js 파일을 가져옵니다.
const storedRecords =
JSON.parse(localStorage.getItem("typingRecords")) || [];
setTypingRecords(storedRecords);
}, []);
useEffect(() => {
if (isFinished) {
saveTypingRecord(); // 타자 연습 완료 시 기록 저장
const storedRecords =
JSON.parse(localStorage.getItem("typingRecords")) || [];
setTypingRecords(storedRecords); // 갱신된 기록 설정
}
}, [isFinished]);
시작할때와 타자연습 완료시마다 불러올 수 있게 했다.
{/* 저장된 기록 표시 */}
<div className="typing-records">
<h3>타자 연습 기록</h3>
{typingRecords.length > 0 ? (
<div>
{typingRecords.map((record, index) => (
<div key={index} className="typingRecords">
<p>완료 시간: {record.completedAt}</p>
<span>소요 시간: {record.time} 초</span>
||
<span>정확도: {record.accuracy}%</span>
||
<span>속도: {record.wpm} WPM</span>
</div>
))}
</div>
) : (
<p>기록이 없습니다.</p>
)}
</div>
.typing-records {
width: 600px;
height: 500px;
overflow: auto;
margin: auto;
margin-top: 20px;
border: 2px solid #ffcd19;
border-radius: 10px;
padding: 20px;
}
/* 스크롤바 스타일 */
.typing-records::-webkit-scrollbar {
width: 10px; /* 스크롤바의 너비 */
}
.typing-records::-webkit-scrollbar-track {
background: none; /* 스크롤바 트랙 색상 */
}
.typing-records::-webkit-scrollbar-thumb {
background: #ddb72d; /* 스크롤바 색상 */
border-radius: 10px; /* 모서리 둥글게 */
}
.typing-records::-webkit-scrollbar-thumb:hover {
background: #8d782d; /* 스크롤바 호버 시 색상 */
}
.typing-records h3 {
text-align: center;
}
.typingRecords {
padding: 10px;
border: 2px solid #ffcd19;
margin-top: 5px;
}
typingRecords
라는 키를 찾아 해당 값을 확인시 저장된 타자 연습 기록을 JSON 형식으로 볼 수 있다.
Delete
키로 삭제하거나 localStorage.clear()
로 초기화 가능
특정 값만 지우고싶은경우
Console 탭 에서
// 로컬 스토리지에서 typingRecords 불러오기
const storedRecords = JSON.parse(localStorage.getItem("typingRecords")) || [];
// 3번째 항목 삭제하기 (인덱스는 2)
if (storedRecords.length > 2) {
storedRecords.splice(2, 1); // 3번째 항목 삭제
}
// 수정된 배열을 로컬 스토리지에 다시 저장
localStorage.setItem("typingRecords", JSON.stringify(storedRecords));
특정 항목을 선택해 지울 수 있다.