POMOTODO(10) 중간정리
2021. 12. 14. 19:29ㆍ프로그래밍/개인프로젝트
진행상황은 깃허브에 매일매일 기록하고 있습니다.
서버를 공부하면서 포스팅은 멈추고 공부에 더 신경썼는데 어느정도 개발이 진행이 되서 포스팅한다
1. NodeJS 웹서버를 구축하고 Express프레임워크를 사용함
* npm으로 설치한 외부모듈
"body-parser": "^1.19.0",
"connect-flash": "^0.1.1",
"ejs": "^3.1.6",
"express": "^4.17.1",
"express-session": "^1.17.2",
"mongodb": "^4.1.4",
"passport": "^0.5.0",
"passport-local": "^1.0.0",
"pbkdf2-password": "^1.2.1"
2. 데이터베이스는 몽고DB를 사용함
디렉토리 구조
server.js
const express = require('express');
const app = express();
app.use(express.static('public'));
const MongoClient = require('mongodb').MongoClient;
let db;
let navId;
navId = 'log in';
const bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({extended : true}));
app.set('view engine', 'ejs');
let flash = require('connect-flash');
let bkfd2Password = require('pbkdf2-password')
let hasher = bkfd2Password();
MongoClient.connect('mongodb+srv://POMOTODO:Aorqnr30335@cluster0.l9rep.mongodb.net/pomotodo?retryWrites=true&w=majority', function(err, client){
//db지정하는코드
db = client.db('pomotodo');
if (err) return console.log(err);
// 서버를 띄우기 위한 코드(데이터베이스 내부에 배치해서 디비와 연결되면 서버를 띄우도록)
app.listen('8080', function(){
console.log('8080포트 접속성공')
});
function yyyymmdd(){
let dateObject = new Date();
let year = dateObject.getFullYear();
let month = dateObject.getMonth()+1;
let date = dateObject.getDate();
return year +"."+ month+"."+date;
}
app.post('/signupResult',function(req, res){
db.collection('users').findOne({id: req.body.loginId}, function(err,result){
// console.log(result)
if(result == null){
console.log('아이디가 없음')
//서버에 아이디가 없으면 if, 있으면 else 출력 -> 없으면 가입을 진행해주고 있으면 얼럿창띄운다음에 회원가입페이지로 보내기
hasher({password: req.body.password}, function(err, pass, salt, hash){
// console.log(err, pass, salt, hash);
// err = undefined, pass:입력한비밀번호값, salt: 랜덤번호생성, hash: 입력비밀번호+salt값에 대한 해쉬값 을 출력해준다
//서버에 자료 저장하기
//db의 컬렉션 지정하기
db.collection('users').insertOne({ id : req.body.loginId, hashPassword : hash, saltPassword : salt, email : req.body.email, number : req.body.number, gender : req.body.gender,birthday : req.body.birthday, }, function(err, result){
console.log('db user create')
})
db.collection('pomodoro').insertOne({ id : req.body.loginId, content: '', contentHTML:''}, function(err, result){
console.log('db pomodoro create')
})
db.collection('todolist').insertOne({ id : req.body.loginId, todoList: '', todoListHTML:''}, function(err, result){
console.log('db todolist create')
})
db.collection('not-todolist').insertOne({ id : req.body.loginId, notTodoList: '', notTodoListHTML:''}, function(err, result){
console.log('db not-todolist create')
})
res.send("<script>alert('회원가입하셨습니다.');location.href='/login';</script>");
})
// console.log('회원가입정보');
// console.log(req.body) : bodyParser를 통해 요청값을 분석한 정보(객체형식으로 반환하는 값)
// console.log("id = "+ req.body.loginId);
// console.log("password = "+ req.body.password);
// console.log("email = "+ req.body.email);
// console.log("number = "+ req.body.num₩ber);
// console.log("gender = "+ req.body.gender);
// console.log("birthday = "+ req.body.birthday);
}else{
console.log('아이디가 있음')
res.send("<script>alert('이미 사용중인 아이디입니다');location.href='/signup';</script>");
}
});
})
let pomoResult;
let todoResult;
let notTodoResult;
app.get('/',function(req, res){
console.log(navId);
// pomodoro 기록 출력하는 코드
if(navId !== 'log in'){ // 아이디가 있을경우 서버에 저장된 결과
db.collection('pomodoro').findOne({id:navId}, function(err, pomodoroResult){
pomoResult = pomodoroResult.contentHTML;
db.collection('todolist').findOne({id : navId}, function(err, todolistResult){
todoResult = todolistResult.todoListHTML;
db.collection('not-todolist').findOne({id : navId}, function(err, nottodolistResult){
notTodoResult = nottodolistResult.notTodoListHTML;
db.collection('pomodoro-record').findOne({ id: navId, 'yyyymmdd' : yyyymmdd() }, function (err, pomoRecCheck) {
if(pomoRecCheck==null){
db.collection('pomodoro-record').insertOne({ 'id' : navId, 'yyyymmdd' : yyyymmdd() ,'pomoRecord' : '' }, function(err, result){
console.log('db pomodoro-record create')
})
}
})
db.collection('todolist-record').findOne({ id: navId, 'yyyymmdd' : yyyymmdd() }, function (err, todoRecCheck) {
if(todoRecCheck==null){
db.collection('todolist-record').insertOne({ 'id' : navId, 'yyyymmdd' : yyyymmdd() ,'todoRecord' : '' }, function(err, result){
console.log('db todo-record create')
})
}
})
db.collection('not-todolist-record').findOne({ id: navId, 'yyyymmdd' : yyyymmdd() }, function (err, notTodoRecCheck) {
if(notTodoRecCheck==null){
db.collection('not-todolist-record').insertOne({ 'id' : navId, 'yyyymmdd' : yyyymmdd() ,'notTodoRecord' : '' }, function(err, result){
console.log('db not-todo-record create')
})
}
})
res.render('POMOTODO.ejs', { posts : `${navId}`, pomodoroRecord : pomoResult, todoListRecord : todoResult, notTodoListRecord : notTodoResult});
//todoList <%- todolist.todoListHTML %>
})
})
})
}else{ // 아이디가 없을경우는 빈공백
res.render('POMOTODO.ejs', { posts : `${navId}`, pomodoroRecord : ' ', todoListRecord : ' ', notTodoListRecord : ' ' }); // 아이디없는경우 꼭 추가해줘야함
}
});
// 누군가가 /signup으로 방문을 하면..signup관련 안내문을 띄워주자.
let idCheckResult = '';
let idCheck;
app.get('/signup',function(req, res){
res.render('signup.ejs', { idCheckResult : idCheck});
});
// 로그인 페이지(세션,쿠키)
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
const session = require('express-session');
// 미들웨어 설정
// 비밀코드는 세션을 만들때 사용할 비밀번호
app.use(session({secret : 'sessionCreatePOMOTODO', resave : true, saveUninitialized: false}));
//미들웨어 : app.use = request - response 중간에 뭔가 실행되는 코드
app.use(passport.initialize());
app.use(passport.session());
app.use(flash());
// 누군가가 /login으로 방문을 하면..login관련 안내문을 띄워주자.
app.get('/login',function(req, res){
let fmsg = req.flash(); // 로그인 실패시 출려되는 플래시메세지
// console.log(fmsg); // 아이디가 없는지 , 비밀번호가 틀렸는지 검사해서 작성해놓은 message를 출력해준다 125번, 132번줄
let feedback= '';
if(fmsg.error){
feedback = fmsg.error[0]
}
res.render('login.ejs', {loginFeedback : feedback})
});
//passport라이브러리 사용
app.post('/login', passport.authenticate('local', { // 로컬방식으로 인증
failureRedirect : '/fail' ,
failureFlash : true
//실패시 /fail페이지로 이동시켜주세요
}), function(req, res){
res.redirect('/') // 성공시 redirect해서 홈페이지로 보내주기
// res.send("<script>location.href='/';</script>");
});
app.get('/fail', function(req, res){ // /fail로 접속시 처리할 코드 (alert창을 띄우고 로그인으로 리다이렉트)
res.redirect('/login')
})
//new LocalStrategy인증방식
//LocalStrategy( { 설정 }, function(){ 아이디비번 검사하는 코드 } )
passport.use(new LocalStrategy({
usernameField: 'loginId', //사용자가 제출한 id가 어디 적혔는지(input의 name 속성값)
passwordField: 'loginPassword', //사용자가 제출한 pw가 어디 적혔는지(input의 name 속성값)
session: true, //로그인 후 세션을 저장할지 ?
passReqToCallback: false, //사용자가 입력한 id,pw외에 다른정보도 검증해보고 싶으면
}, function (inputId, inputPw, done) {
// console.log(입력한아이디, 입력한비번); //-> 사용자가 입력한 id/pw가 콘솔로그로 출력됨
db.collection('users').findOne({ id: inputId }, function (err, user) {
//입력한 id에 대한 정보를 결과에 담아옴
if (err) return done(err) //에러처리문법
//done은 3개의 파라미터를 가질수 있음, 1: 서버에러, 2: 성공시 사용자db, 3:에러메시지
if (!user) {
console.log('아이디가 없습니다')
return done(null, false, { message: '존재하지 않는 ID입니다' })
}
if(user){
console.log('아이디가 존재합니다')
hasher({password:inputPw, salt: user.saltPassword}, function(err, pass, salt, hash){
// console.log(err, pass, salt, hash);
// err = undefined, pass:입력한비밀번호값, salt: 랜덤번호생성, hash: 입력비밀번호+salt값에 대한 해쉬값 을 출력해준다
// 입력비밀번호와 유저의 salt를 가져와서 hash로 만들고 그 해쉬값이 서버의 해쉬값과 일치하다면 로그인성공
if (hash == user.hashPassword) {
return done(null, user)
} else {
console.log('비밀번호 틀렸어요');
return done(null, false, { message: '잘못된 비밀번호입니다' })
}
})
}
})
}));
// 유저의 정보를 암호화해서 user.id라는 세션으로 만든다
//user 파라미터로 아이디/비밀번호 검증 결과가 들어간다
passport.serializeUser(function (user, done) {
done(null, user.id); // 세션데이터안에 passport의 user값으로 사용자의 아이디가 들어간다
navId = user.id; // navId라는 변수에 입력한 id를 담아서 화면에 출력해주도록 하기위한 코드
});
// 세션데이터를 가진 사람을 db에서 찾아주는 코드
passport.deserializeUser(function (아이디, done) { //로그인하면 페이지에 방문할 때 마다 콜백함수가 호출, 사용자의 실제 데이터를 조회해서 가져옴
done(null, {})
});
app.get('/logout', function(req, res){
req.session.destroy(() => {
res.clearCookie('connect.sid');
navId = 'log in';
res.redirect('/');
});
})
// Pomodoro 기록 업데이트
app.post('/insertPomodoro', function(req, res){
if(navId !== 'log in'){ //로그인 했을때만 db에 저장하도록 하는 코드
db.collection('pomodoro').updateOne({id : navId}, { $set : req.body }, function(err, result){
console.log('뽀모도로 업데이트')
res.status(200).send({ message : '뽀모 업데이트 성공했습니다'});
})
}else{
console.log('로그인을 해주세요');
}
})
//투두리스트 업데이트
app.post('/insertTodoList', function(req, res){
if(navId !== 'log in'){
db.collection('todolist').updateOne({id : navId}, { $set : req.body }, function(err, result){
console.log('투두리스트 업데이트')
res.status(200).send({ message : '투두 업데이트 성공했습니다'});
})
}else{
console.log('로그인을 해주세요');
}
})
//낫투두리스트 업데이트
app.post('/insertNotTodoList', function(req, res){
if(navId !== 'log in'){
db.collection('not-todolist').updateOne({id : navId}, { $set : req.body }, function(err, result){
console.log('낫투두리스트 업데이트')
res.status(200).send({ message : '낫투두 업데이트 성공했습니다'});
})
}else{
console.log('로그인을 해주세요');
}
})
// 회원탈퇴
app.get('/deleteUser', function(req, res) {
if(navId !== 'log in'){
db.collection('users').deleteOne({ id: navId }, function (err, result) {
console.log('유저 삭제')
})
db.collection('pomodoro').deleteOne({ id: navId }, function (err, result) {
console.log('포모도로 삭제')
})
db.collection('todolist').deleteOne({ id: navId }, function (err, result) {
console.log('투두리스트 삭제')
})
db.collection('not-todolist').deleteOne({ id: navId }, function (err, result) {
console.log('낫투두리스트 삭제')
})
}
navId = 'log in';
res.redirect('/');
})
// 서버에서 id중복체크하는 ajax요청
app.post('/signup-id-check', function(req, res){
db.collection('users').findOne({id: req.body.id}, function(err,result){
if(result == null){
// 서버에 id가 없는경우
idCheck = '';
}else{
idCheck = '가입할 수 없는 ID입니다';
}
res.status(200).send({ message : idCheck});
})
})
function yyyymmddYesterday(){
let dateObject = new Date();
let year = dateObject.getFullYear();
let month = dateObject.getMonth()+1;
let date = dateObject.getDate()-1;
return year +"."+ month+"."+date;
}
app.get('/record',function(req, res){
console.log(navId);
let dateObject = new Date();
let years = dateObject.getFullYear();
let months = dateObject.getMonth()+1;
let dates = dateObject.getDate()-1;
// 만약 1을 뺏을때 0이라면 전달 말일로 바꿔주는데 1월1일에 1을뺀다면 1년-,11월+,해당달의 말일 을 데이터로 보내준다
if(dates == 0){
if(months == 1){
years = years-1;
months = months+11;
dates = new Date(years, months, 0).getDate(); //month의 말일을 구하는 코드
}else if(months !== 1){
months = months-1;
dates = new Date(years, months, 0).getDate();
}
}
years = String(years);
months = String(months);
dates = String(dates);
if(navId !== 'log in'){
db.collection('pomodoro-record').findOne({'id':navId, 'yyyymmdd':yyyymmddYesterday()}, function(err, pomodoroRecordResult){
if(pomodoroRecordResult ==null){
console.log('pomodoro-record-error');
pomoRecordRes = '';
}else{
pomoRecordRes = pomodoroRecordResult.pomoRecord;
// console.log("pomoRecordRes"+pomoRecordRes);
}
db.collection('todolist-record').findOne({'id' : navId, 'yyyymmdd':yyyymmddYesterday()}, function(err, todolistRecordResult){
if(todolistRecordResult==null){
console.log('todolist-record-error');
todoRecordRes = '';
}else{
todoRecordRes = todolistRecordResult.todoRecord;
// console.log("todoRecordRes"+todoRecordRes);
}
db.collection('not-todolist-record').findOne({'id' : navId, 'yyyymmdd':yyyymmddYesterday()}, function(err, nottodolistRecordResult){
if(nottodolistRecordResult==null){
console.log('not-todolist-record-error');
notTodoRecordRes = '';
}else{
notTodoRecordRes = nottodolistRecordResult.notTodoRecord;
// console.log("notTodoRecordRes"+notTodoRecordRes);
}
res.render('record.ejs', { 'posts' : `${navId}`, 'pomos' : pomoRecordRes, 'todos' : todoRecordRes, 'notTodos' : notTodoRecordRes});
})
})
})
}else{
res.render('record.ejs', { 'posts' : `${navId}`, 'pomos' : '', 'todos' : '', 'notTodos' : ''});
}
});
//---------------------------------서버에 기록 생성 , 수정 코드--------------------------
// function yyyymmdd(){
// let dateObject = new Date();
// let year = dateObject.getFullYear();
// let month = dateObject.getMonth()+1;
// let date = dateObject.getDate();
// return year +"."+ month+"."+date;
// }
app.post('/saveData', function(req, res){
db.collection('pomodoro').find().toArray(function(err,result){
// console.log(result[0].id);
// console.log(result[i].contentHTML.length>0);
for(let i = 0; i < result.length; i++){
if(result[i].contentHTML.length>0){
//contentHTML이 존재할때 값을넘길코드
// result[i].id
// yyyymmdd
// result[i].contentHTML
// db.collection('pomodoro-record').insertOne({ 'id' : result[i].id, 'yyyymmdd' : yyyymmdd() ,'pomoRecord' : result[i].contentHTML }, function(err, result){
// console.log('db pomodoro-record create')
// })
db.collection('pomodoro-record').updateOne({'id' : result[i].id, 'yyyymmdd' : yyyymmdd()},{$set: {'pomoRecord' : result[i].contentHTML}},function(){
console.log('뽀모레코드 업데이트')
// res.status(200).send({ message : '뽀모레코드 업데이트 성공했습니다'});
})
}
}
});
// 포모도로컬렉션의 내용을 찾아서 반복문으로 출력한다
// (포모도로리스트에 id와 날짜데이터가 없으면 생성해주고 있으면 업데이트해준다 초기화메서드는 따로관리한다 00시00분이되면)
// contentHTML이 0자 이상인애들은 인서트시키고 초기화시켜준다(업데이트)
db.collection('todolist').find().toArray(function(err,result){
for(let i = 0; i < result.length; i++){
if(result[i].todoListHTML.length>0){
db.collection('todolist-record').updateOne({'id' : result[i].id, 'yyyymmdd' : yyyymmdd()},{$set: {'todoRecord' : result[i].todoListHTML}},function(){
console.log('투두레코드 업데이트')
})
}
}
});
db.collection('not-todolist').find().toArray(function(err,result){
for(let i = 0; i < result.length; i++){
if(result[i].notTodoListHTML.length>0){
db.collection('not-todolist-record').updateOne({'id' : result[i].id, 'yyyymmdd' : yyyymmdd()},{$set: {'notTodoRecord' : result[i].notTodoListHTML}},function(){
console.log('낫투두레코드 업데이트')
})
}
}
});
res.status(200).send({ message : '저장 성공했습니다'});
})
app.post('/initialization', function(req, res){
db.collection('pomodoro').find().toArray(function(err,result){
for(let i = 0; i < result.length; i++){
if(result[i].contentHTML.length>0){
db.collection('pomodoro').update({'id' : result[i].id},{$set: {'contentHTML' : ''}})
}
}
});
db.collection('todolist').find().toArray(function(err,result){
for(let i = 0; i < result.length; i++){
if(result[i].todoListHTML.length>0){
db.collection('todolist').update({'id' : result[i].id},{$set: {'todoListHTML' : ''}})
}
}
});
db.collection('not-todolist').find().toArray(function(err,result){
for(let i = 0; i < result.length; i++){
if(result[i].notTodoListHTML.length>0){
db.collection('not-todolist').update({'id' : result[i].id},{$set: {'notTodoListHTML' : ''}})
}
}
});
if(navId !== 'log in'){
db.collection('pomodoro-record').findOne({ id: navId, 'yyyymmdd' : yyyymmdd() }, function (err, pomoRecCheck) {
if(pomoRecCheck==null){
db.collection('pomodoro-record').insertOne({ 'id' : navId, 'yyyymmdd' : yyyymmdd() ,'pomoRecord' : '' }, function(err, result){
console.log('db pomodoro-record create')
})
}
})
db.collection('todolist-record').findOne({ id: navId, 'yyyymmdd' : yyyymmdd() }, function (err, todoRecCheck) {
if(todoRecCheck==null){
db.collection('todolist-record').insertOne({ 'id' : navId, 'yyyymmdd' : yyyymmdd() ,'todoRecord' : '' }, function(err, result){
console.log('db todo-record create')
})
}
})
db.collection('not-todolist-record').findOne({ id: navId, 'yyyymmdd' : yyyymmdd() }, function (err, notTodoRecCheck) {
if(notTodoRecCheck==null){
db.collection('not-todolist-record').insertOne({ 'id' : navId, 'yyyymmdd' : yyyymmdd() ,'notTodoRecord' : '' }, function(err, result){
console.log('db not-todo-record create')
})
}
})
}
res.status(200).send({ message : 'record 초기화 / 빈값생성 했습니다'});
})
app.post('/dayButton', function(req, res){
console.log(req.body.clickedButton)
if(navId !== 'log in'){
db.collection('pomodoro-record').findOne({'id':navId, 'yyyymmdd':req.body.clickedButton}, function(err, pomodoroRecordResult){
if(pomodoroRecordResult ==null){
console.log('pomodoro-record-error');
pomoRecordRes = '';
}else{
pomoRecordRes = pomodoroRecordResult.pomoRecord;
// console.log("pomoRecordRes"+pomoRecordRes);
}
// res.status(200).send({ pomoMessage : pomoRecordRes});
db.collection('todolist-record').findOne({'id' : navId, 'yyyymmdd':req.body.clickedButton}, function(err, todolistRecordResult){
if(todolistRecordResult==null){
console.log('todolist-record-error');
todoRecordRes = '';
}else{
todoRecordRes = todolistRecordResult.todoRecord;
// console.log("todoRecordRes"+todoRecordRes);
}
// res.status(200).send({ todoMessage : todoRecordRes});
db.collection('not-todolist-record').findOne({'id' : navId, 'yyyymmdd':req.body.clickedButton}, function(err, nottodolistRecordResult){
if(nottodolistRecordResult==null){
console.log('not-todolist-record-error');
notTodoRecordRes = '';
}else{
notTodoRecordRes = nottodolistRecordResult.notTodoRecord;
// console.log("notTodoRecordRes"+notTodoRecordRes);
}
res.status(200).send({pomoMessage : pomoRecordRes, todoMessage : todoRecordRes , notTodoMessage : notTodoRecordRes});
// res.render('record.ejs', { 'posts' : `${navId}`, 'pomos' : pomoRecordRes, 'todos' : todoRecordRes, 'notTodos' : notTodoRecordRes});
})
})
})
}else{
res.render('record.ejs', { 'posts' : `${navId}`, 'pomos' : '', 'todos' : '', 'notTodos' : ''});
}
})
app.post('/buttonColor', function(req, res){
db.collection('pomodoro-record').findOne({'id':navId, 'yyyymmdd':req.body.count},function(err,result){
// res.status(200).send({ message : result});
if(result !== null){
res.status(200).send({ message : result.pomoRecord.length});
}else{
res.status(200).send({ message : ''});
}
})
})
})
POMOTODO.ejs (홈)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="shortcut icon" href="/POMOTODO icon/mountain-solid.svg" />
<title>POMOTODO</title>
<link rel="stylesheet" href="style.css" />
<!--웹폰트-->
<script src="https://kit.fontawesome.com/65f55f4509.js" crossorigin="anonymous"></script>
<!--제이쿼리-->
<!-- <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script> -->
<script src="http://code.jquery.com/jquery-latest.min.js"></script>
<link href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="https://code.jquery.com/jquery-1.12.4.min.js" ></script>
<script type="text/javascript" src="https://code.jquery.com/ui/1.12.1/jquery-ui.js" ></script>
<!--자바스크립트-->
<script defer src="main.js"></script>
</head>
<body class="body">
<!--네비게이션바 외부 ejs파일로 뺴서 include-->
<%- include ('header.ejs') %>
<div id="mainLayout">
<section class="Pomodoro">
<!-- 뽀모도로타이머 -->
<article class="Pomodoro-timer">
<h2 class="section-title">Pomodoro</h2>
<div>
<!--타이머, 기록 구현-->
<!-- 타이머 -->
<div class="Pomodoro-timer-cover"></div>
<canvas class="Pomodoro-timer-guage" id="guage" width="200px" height="200px" ></canvas>
<div class="pie-chart pie-chart1" id="pie-chart1">
<span class="center"></span>
</div>
<div class="Pomodoro-timer-container">
<button class="clock__updown" id="minute1__plus"><i class="fas fa-caret-up"></i></button>
<button class="clock__updown" id="minute2__plus"><i class="fas fa-caret-up"></i></button>
<button class="clock__updown" id="second1__plus"><i class="fas fa-caret-up"></i></button>
<button class="clock__updown" id="second2__plus"><i class="fas fa-caret-up"></i></button>
<h2 id="pomo-timer"><span id="minute">25</span>:<span id="second">00</span></h2>
<button class="clock__updown" id="minute1__minus"><i class="fas fa-caret-down"></i></button>
<button class="clock__updown" id="minute2__minus"><i class="fas fa-caret-down"></i></button>
<button class="clock__updown" id="second1__minus"><i class="fas fa-caret-down"></i></button>
<button class="clock__updown" id="second2__minus"><i class="fas fa-caret-down"></i></button>
<br />
<button class="clock__btn" id="btn__start" style="display: inline" ><i class="fas fa-play"></i></button>
<button class="clock__btn" id="btn__stop"> <i class="fas fa-stop"></i> </button>
</div>
<div class="record" overflow:auto;>
<ul class="record-list" id="record-list"> <%- pomodoroRecord %> </ul>
</div>
</div>
<!-- 오디오 -->
<div class="audio">
<div name="audio" id="select-audio" size="1"></div>
<div id="modal-audio" style="display: none" size="1">
<button type="button" class="audio-list" id="audio1" value="Farm Morning with Sheep"> Farm Morning with Sheep </button>
<button type="button" class="audio-list" id="audio2" value="Fire"> Fire </button>
<button type="button" class="audio-list" id="audio3" value="Outdoor Summer Ambience"> Outdoor Summer Ambience </button>
<button type="button" class="audio-list" id="audio4" value="Rain Heavy Loud"> Rain Heavy Loud </button>
<button type="button" class="audio-list" id="audio5" value="Rain On Rooftop"> Rain On Rooftop </button>
<button type="button" class="audio-list" id="audio6" value="Valley Night"> Valley Night </button>
<button type="button" class="audio-list" id="audio7" value="Waves Crashing on Rock Beach"> Waves Crashing on Rock Beach </button>
</div>
<br />
<button type="button" class="start-audio" id="start-button" style="display: flex"></button>
<button type="button" class="pause-audio" id="pause-button" style="display: none"></button>
<!-- <button class="createBtn">생성버튼</button> -->
<button class="saveBtn">저장버튼</button>
<!-- <button class="ymdBtn">날짜버튼</button> -->
</div>
</article>
<!--포모도로 레이아웃-->
<!-- <ul id="sortable">
<li class="ui-state-default"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span>11111</li>
<li class="ui-state-default"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span>22222</li>
<li class="ui-state-default"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span>33333</li>
<li class="ui-state-default"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span>44444</li>
<li class="ui-state-default"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span>55555</li>
<li class="ui-state-default"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span>66666</li>
<li class="ui-state-default"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span>77777</li>
<li class="ui-state-default"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span>88888</li>
<li class="ui-state-default"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span>99999</li>
<li class="ui-state-default"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span>10101</li>
</ul> -->
</section>
<section class="todonottodo">
<!-- todolist-->
<article class="todolist">
<div class="section-title"><h2>ToDoList</h2></div>
<div id="todo-write">
<!--투두리스트 입력값을 위한 인풋태그, 버튼태그-->
<form onsubmit="return false;">
<!-- index.html 임시로 지정, onsubmit을 막아서 엔터키를 내가원하는데로 지정-->
<input type="text" class="input-text"id="todo-input-text" maxlength="45" onkeydown="if(window.event.keyCode==13){addItem()}" autocomplete="off"/>
<!--onkeydown속성으로 버튼눌렀을때 키코드확인, 메서드 실행 211009-->
<button type="button" class="input-button" id="todo-input-button"><i class="fas fa-greater-than"></i> </button>
</form>
</div>
<ol class="item-list" id="sortable"> <%- todoListRecord %> </ol>
<!-- 컬러지정을 위한 모달창 -->
<div id="todo-modal" class="todo-modal-overlay">
<span class="todo-modal-content" id="todo-modal-content">ᅠ
<!-- <button type="button" class="color-button" id="close" value="close"> <i class="fas fa-angle-left"></i> </button> -->
<button type="button" class="color-button" id="close" value="close"> <b> < </b> </button>
<button type="button" class="color-button" id="check" value="check"> ✔︎ </button>
<!-- <button type="button" class="color-button" id="check" value="check"><i class="fas fa-check"></i> </button> -->
<button type="button" class="color-button" id="red" value="red"></button>
<button type="button" class="color-button" id="light-orange" value="light-orange"></button>
<button type="button" class="color-button" id="orange" value="orange"></button>
<button type="button" class="color-button" id="light-pink" value="light-pink"></button>
<button type="button" class="color-button" id="pink" value="pink"></button>
<button type="button" class="color-button" id="light-puple" value="light-puple"></button>
<button type="button" class="color-button" id="purple" value="purple"></button>
<button type="button" class="color-button" id="light-green" value="light-green"></button>
<button type="button" class="color-button" id="green" value="green"></button>
<button type="button" class="color-button" id="light-blue-green" value="light-blue-green"></button>
<button type="button" class="color-button" id="blue-green" value="blue-green"></button>
<button type="button" class="color-button" id="light-blue" value="light-blue" ></button>
<button type="button" class="color-button" id="blue" value="blue"></button>
<button type="button" class="color-button" id="sand" value="sand"></button>
<button type="button" class="color-button" id="olive" value="olive"></button>
</span>
</div>
</article>
<!--투두리스트-->
<!--Not-ToDoList-->
<article class="nottodolist">
<div class="section-title"><h2>NotToDoList</h2></div>
<div id="nottodo-write">
<!--낫투두리스트 입력값을 위한 인풋태그, 버튼태그-->
<form onsubmit="return false;">
<input type="text" class="ntd-input-text" maxlength="45" onkeydown="if(window.event.keyCode==13){ntdAddItem()}"/>
<button type="button" class="ntd-input-button"> <i class="fas fa-greater-than"></i> </button>
</form>
</div>
<ol class="ntd-item-list" id="ntd-sortable"> <%- notTodoListRecord %> </ol>
<div id="not-todo-modal" class="not-todo-modal-overlay">
<div class="not-todo-modal-content" id="not-todo-modal-content">
ᅠ
<!-- <button type="button" class="ntd-color-button" id="ntdClose" value="ntd-close"> <i class="fas fa-angle-left"></i> </button>
<button type="button" class="ntd-color-button" id="ntdCheck" value="ntd-check"> <i class="fas fa-check"></i> </button> -->
<button type="button" class="ntd-color-button" id="ntdClose" value="ntd-close"> <b> < </b> </button>
<button type="button" class="ntd-color-button" id="ntdCheck" value="ntd-check"> ✔︎ </button>
<button type="button" class="ntd-color-button" id="ntdRed" value="ntd-red"></button>
</div>
</div>
</article>
<!--낫투두리스트-->
</section>
</div>
<footer>
<!-- -->
</footer>
</body>
</html>
style.css(홈)
@import url("https://fonts.googleapis.com/css2?family=Nanum+Gothic:wght@400;700;800&display=swap");
/* font-family: 'Nanum Gothic', sans-serif; */
@import url("https://fonts.googleapis.com/css2?family=Ubuntu:ital,wght@0,300;0,400;0,500;0,700;1,300;1,400;1,500;1,700&display=swap");
/* font-family: 'Ubuntu', sans-serif; */
/* 본문 */
body {
font-family: "Ubuntu", sans-serif;
display: flex;
justify-content: center;
height: 870px;
margin: 0px;
background-color: #e9f0fa;
}
h2 {
font-weight: 700;
font-size: 35px;
text-align: center;
margin: 0px 0px 20px;
padding: 7px;
opacity: 0.7;
}
.section-title {
/*뽀모도로, 투두리스트, 낫투두리스트 타이틀의 폰트스타일*/
font-style: italic;
/* opacity: 1;
color: #221974; */
}
#mainLayout {
zoom: 100%;
display: flex;
position: absolute;
top: 49%;
left: 50%;
transform: translate(-50%, -53%);
/* justify-content: space-between; */
align-items: center;
/* height: 1200px; */
}
section.Pomodoro {
position: relative;
right: 20px;
top: 50px;
width: 650px;
height: 830px;
padding: 10px;
/* margin-right: 15px; */
border-radius: 30px;
background: #e9f0fa;
box-shadow: 11px 11px 9px #c8ced7, -5px -5px 20px #ffffff;
}
section.todonottodo {
position: relative;
top: 50px;
left: 10px;
width: 650px;
/* margin-left: 15px; */
}
article.todolist {
height: 500px;
padding: 10px;
margin-bottom: 20px;
border-radius: 30px;
background: #e9f0fa;
box-shadow: 11px 11px 9px #c8ced7, -5px -5px 22px #ffffff;
overflow: auto;
}
article.nottodolist {
height: 280px;
padding: 10px;
border-radius: 30px;
background: #e9f0fa;
box-shadow: 11px 11px 9px #c8ced7, -5px -5px 22px #ffffff;
overflow: auto;
}
/* 투두, 낫투두 스크롤 바 */
article.todolist::-webkit-scrollbar,
article.nottodolist::-webkit-scrollbar {
width: 7px;
background-color: transparent;
}
article.todolist::-webkit-scrollbar-thumb,
article.nottodolist::-webkit-scrollbar-thumb {
background-clip: padding-box;
border-top: 30px solid transparent;
border-bottom: 30px solid transparent;
background-color: lightgray;
}
/* 뽀모도로 타이머 */
.Pomodoro-timer-cover {
position: absolute;
top: 75px;
left: 208px;
width: 215px;
height: 215px;
border: 1px solid black;
z-index: ;
border-radius: 50%;
border: 20px solid #ebf3fb;
box-shadow: 3px 3px 6px #949da6, -3px -3px 6px #ffffff;
background-color: white;
}
/* 게이지 구현 */
.Pomodoro-timer-guage {
position: absolute;
/* padding: 0px;
margin: 0px; */
}
/* 시간은 컨트롤하는 타이머 커버영역 */
.Pomodoro-timer-container {
position: relative;
left: 239px;
top: 31px;
width: 175px;
height: 175px;
text-align: center;
border-radius: 50%;
background: #ebf3fb;
}
.Pomodoro-timer-container > h2 {
/*시간, :, 버튼 모두 포함하는 부분*/
position: relative;
opacity: 0.6;
font-size: 44px;
margin: 0px 1px;
/* margin-top: 0px;
margin-left: 1px;
margin-right: 1px;
margin-bottom: 0px; */
padding: 0px;
}
.Pomodoro-timer-container > h2 > span {
/*오직 시간만 표현하는 부분*/
display: inline-block;
position: relative;
top: 35px;
width: 75px;
}
.Pomodoro-timer-container > .clock__updown {
position: relative;
top: 38px;
opacity: 0.1;
border: 0;
padding: 2px 6px 4px;
border-radius: 40%;
background-color: transparent;
}
#pomo-timer {
position: relative;
top: 35px;
opacity: 0.5;
}
#minute,
#second {
position: relative;
top: 2px;
}
/* 버튼간격조절 */
#minute2__minus,
#minute2__plus {
margin-top: 0px;
margin-right: 18px;
}
#second1__minus,
#second1__plus {
margin-top: 0px;
margin-left: 18px;
}
.Pomodoro-timer-container > .clock__btn {
position: relative;
top: 35px;
opacity: 0.5;
font-size: 15px;
padding: 7px 9px 7px;
border: none;
border-radius: 5px;
background: transparent;
}
#btn__start {
display: inline;
color: #ff6f71;
}
#btn__stop {
display: none;
padding: 7px;
}
.clock__btn:hover,
.clock__updown:hover {
opacity: 0.9;
cursor: pointer;
}
.clock__btn:active,
.clock__updown:active {
box-shadow: inset 1px 1px 2px #949da6, -1px -1px 3px #ffffff;
}
button:hover {
opacity: 0.6;
cursor: pointer;
}
button:active {
box-shadow: inset 1px 1px 2px #949da6, -1px -1px 3px #ffffff;
}
/* record , 스크롤 구현 */
div.record {
position: relative;
top: 100px;
left: 75px;
height: 275px;
width: 480px;
padding: 10px;
margin-right: 0px;
border-radius: 25px;
background: #e9f0fa;
box-shadow: 3px 3px 6px #c1c7d0, -3px -3px 6px #ffffff;
overflow: auto;
}
div.record::-webkit-scrollbar {
width: 7px;
background-color: transparent;
}
div.record::-webkit-scrollbar-thumb {
background-clip: padding-box;
border-top: 30px solid transparent;
border-bottom: 30px solid transparent;
background-color: lightgray;
}
/* record 자동입력 부분 */
.record-list {
/*레이아웃*/
font-size: 16px;
font-weight: 500;
color: #5e5e5e;
position: relative;
left: -5px;
width: 300px;
display: inline;
border-radius: 5px;
}
#record-time {
/*시간 자동입력*/
border: 0px;
position: relative;
left: 10px;
font-weight: 400;
font-size: 15px;
color: gray;
background-color: transparent;
padding: 5px;
border-radius: 8px;
}
.record-text {
/*텍스트 자동입력*/
position: relative;
width: 250px;
background-color: #e9f0fa;
border: 1px solid #e9f0fa;
border-bottom: 1px solid #dbe2eb;
}
#record-time:hover {
opacity: 1;
color: black;
}
#record-time:active {
opacity: 0.3;
color: black;
box-shadow: inset 0px 0px 0px #949da6, 0px 0px 0px #ffffff; /*버튼클릭애니메이션을 없애기 위해 작성한 코드*/
}
/* 뽀모도로 기록 타입확인용 아이콘 (빨강/초록)*/
span > i {
position: relative;
padding: 0px;
margin: 0px;
left: 10px;
bottom: 2px;
font-size: 2px;
}
/* 오디오 */
div.audio {
position: relative;
top: 120px;
left: 75px;
height: 150px;
width: 500px;
border-radius: 25px;
background: #e9f0fa;
box-shadow: 3px 3px 6px #c1c7d0, -3px -3px 6px #ffffff;
}
/* 오디오 선택 버튼 */
#select-audio {
font-style: italic;
font-weight: 400;
font-size: 15px;
position: relative;
left: 125px;
top: 30px;
width: 250px;
height: 25px;
text-align: center;
padding-top: 10px;
border: 0;
border-radius: 8px;
background: #e9f0fa;
box-shadow: 2px 2px 2px #949da6, -2px -2px 10px #ffffff;
}
#select-audio:hover {
background: #e9f0fa;
box-shadow: 1px 1px 1px #949da6, -1px -1px 10px #ffffff;
cursor: pointer;
}
#select-audio:active {
background: #e9f0fa;
box-shadow: inset 1px 1px 2px #949da6, -1px -1px 2px #ffffff;
cursor: pointer;
}
/* 오디오 선택시 나오는 모달창 */
#modal-audio {
flex-direction: column;
position: absolute;
top: 20px;
left: 115px;
align-items: center;
background: rgba(255, 255, 255);
width: 260px;
height: 95px;
padding: 5px 5px 10px 5px;
border-radius: 10px;
overflow: auto;
box-shadow: 2px 2px 2px #949da6, -2px -2px 2px #ffffff;
z-index: 99;
}
/* 오디오 선택 모달창 스크롤 */
#modal-audio::-webkit-scrollbar {
width: 5px;
background-color: transparent;
}
#modal-audio::-webkit-scrollbar-thumb {
background-clip: padding-box;
border-top: 10px solid transparent;
border-bottom: 10px solid transparent;
background-color: lightgray;
}
.audio-list {
font-family: "Ubuntu", sans-serif;
font-style: italic;
font-weight: 400;
font-size: 15px;
color: #808080;
width: 250px;
height: 30px;
margin: 5px 0px 0px 0px;
border: 0;
border-radius: 5px;
background: #e9f0fa;
box-shadow: 1px 1px 1px lavender, -1px -1px 4px #ffffff;
}
.audio-list:hover {
color: black;
opacity: 1;
}
.start-audio {
display: flex;
background-image: url("POMOTODO icon/play-solid.svg");
background-size: 25px;
background-repeat: no-repeat;
background-position: center center;
position: relative;
left: 230px;
top: 35px;
width: 45px;
height: 45px;
opacity: 0.5;
border: 0px;
background-color: transparent;
border-radius: 10px;
}
.pause-audio {
display: none;
background-image: url("POMOTODO icon/pause-solid.svg");
background-size: 25px;
background-repeat: no-repeat;
background-position: center center;
position: relative;
left: 229px;
top: 45px;
width: 45px;
height: 45px;
opacity: 0.5;
border: 0px;
background-color: transparent;
border-radius: 10px;
}
/* 투두, 낫투두 */
#todo-write,
#nottodo-write {
position: relative;
left: 20px;
width: 590px;
/* margin: 0px 18px 5px; */
border-radius: 11px;
height: 40px;
background: #e9f0fa;
box-shadow: 3px 3px 6px #949da6, -2px -2px 6px #ffffff;
}
.todo-modal-content > #check {
background-color: lightgray;
color: white;
}
.todo-modal-content > #red {
background-color: #f36164;
}
.todo-modal-content > #light-orange {
background-color: #e6b74b;
}
.todo-modal-content > #orange {
background-color: #f88313;
}
.todo-modal-content > #light-pink {
background-color: #e69dde;
}
.todo-modal-content > #pink {
background-color: #e509cc;
}
.todo-modal-content > #light-puple {
background-color: #9288fd;
}
.todo-modal-content > #purple {
background-color: #ab57f7;
}
.todo-modal-content > #light-green {
background-color: #91c349;
}
.todo-modal-content > #green {
background-color: #5c8a3d;
}
.todo-modal-content > #light-blue-green {
background-color: #91eadd;
}
.todo-modal-content > #blue-green {
background-color: #06d2e0;
}
.todo-modal-content > #light-blue {
background-color: #289bff;
}
.todo-modal-content > #blue {
background-color: #1c00ff;
}
.todo-modal-content > #sand {
background-color: #c39b7a;
}
.todo-modal-content > #olive {
background-color: #b7be54;
}
.not-todo-modal-content > #ntdCheck {
background-color: #5c8a3d;
color: white;
}
.not-todo-modal-content > #ntdRed {
background-color: #f36164;
}
.input-text,
.ntd-input-text {
width: 540px;
height: 33px;
position: relative;
left: 3px;
top: 3px;
border: 0;
outline: 0;
border-radius: 9px;
/* padding: 0px 16px 0px; */
/* margin-right: 2px; */
}
.input-button,
.ntd-input-button {
width: 40px;
height: 34px;
position: relative;
right: px;
top: 3px;
color: #5e5e5e;
border: 0;
outline: 0;
background-color: transparent;
margin-left: 1px;
border-radius: 9px;
padding: 0px;
margin: 0px;
}
/* 투두 모달창 */
#todo-modal.todo-modal-overlay {
width: 85px;
height: 300px;
position: absolute;
right: 20px;
top: 140px;
display: none;
flex-direction: column;
align-items: center;
justify-content: center;
background: rgba(255, 255, 255, 0.5);
box-shadow: 3px 3px 5px #c8ced7, -3px -3px 5px #ffffff;
border-radius: 10px;
overflow: auto;
}
#todo-modal.todo-modal-overlay::-webkit-scrollbar {
width: 7px;
background-color: transparent;
}
#todo-modal.todo-modal-overlay::-webkit-scrollbar-thumb {
background-clip: padding-box;
border-top: 30px solid transparent;
border-bottom: 30px solid transparent;
background-color: lightgray;
}
.color-button {
position: relative;
top: 140px;
display: flex;
justify-content: space-around;
/* padding-top: 100px; */
background-color: #e9f0fa;
padding: 8px;
margin: 5px;
width: 65px;
height: 30px;
border: 0px;
border-radius: 7px;
box-shadow: 1px 1px 2px #c8ced7;
}
/* #todo-modal.todo-modal-close {
display: inline;
float: right;
padding-right: 3px;
cursor: pointer;
color: gray;
} */
/* 낫투두모달창 */
#not-todo-modal.not-todo-modal-overlay {
width: 75px;
height: 110px;
position: absolute;
right: 20px;
top: 680px;
display: none;
flex-direction: column;
align-items: center;
background: rgba(255, 255, 255, 0.8);
box-shadow: 3px 3px 5px #c8ced7, -3px -3px 5px #ffffff;
border-radius: 10px;
}
.ntd-color-button {
position: relative;
top: -20px;
display: flex;
justify-content: space-around;
padding: 8px;
margin: 5px;
width: 65px;
height: 30px;
border: 0px;
border-radius: 7px;
box-shadow: 1px 1px 2px #c8ced7;
}
/* #not-todo-modal.not-todo-modal-close {
display: inline;
float: right;
padding-right: 3px;
cursor: pointer;
color: gray;
} */
/* 투두리스트, 낫투두 입력시 생겨나는 리스트들 */
ol {
margin: 0px;
padding: 20px;
/* width: 100px; */
}
ol li {
position: relative;
left: 0px;
display: flex;
justify-content: space-between;
align-items: center;
width: 585px;
list-style: none;
padding-left: 5px;
margin-bottom: 10px;
border-radius: 11px;
background: #e9f0fa;
box-shadow: 3px 3px 6px #949da6, -2px -2px 6px #ffffff;
color: lightgray;
}
ol li > i {
position: relative;
top: 2px;
}
button > i {
position: relative;
top: 1px;
}
ol li > i:hover {
color: gray;
}
ol li > i:active {
color: black;
}
ol li:hover {
cursor: pointer;
}
/* ol li:active,
ol li.checked:active {
border: 1px solid lightgray;
box-shadow: inset 0px 0px 0px #949da6, -0px -0px 0px #ffffff;
} */
/* 리스트 내용부분 버튼태그 (클릭시 색깔지정 모달창 뜨는 버튼) */
.todo-list-content,
.ntd-todo-list-content {
padding: 1px;
font-family: "Ubuntu", sans-serif;
font-weight: 500;
border: 0px;
border-radius: 10px;
background-color: transparent;
width: 530px;
height: 40px;
/* color: #5e5e5e; */
}
/* .ntd-todo-list-content {
color: #f36164;
} */
.todo-list-content:hover,
.ntd-todo-list-content:hover {
opacity: 0.6;
}
.todo-list-content:active,
.ntd-todo-list-content:active {
box-shadow: inset 0px 0px 0px #949da6, -0px -0px 0px #ffffff;
}
/* 리스트 삭제 버튼 */
.todo-list-delete,
.ntd-todo-list-delete {
position: relative;
right: 5px;
top: -1px;
padding-top: 3px;
margin-left: 10px;
border: 0px;
background-color: transparent;
font-size: 15px;
color: lightgray;
/* background-color: black; */
}
.todo-list-delete:hover,
.ntd-todo-list-delete:hover {
color: #ff8585;
cursor: pointer;
}
.todo-list-delete:active,
.ntd-todo-list-delete:active {
color: red;
cursor: pointer;
box-shadow: inset 0px 0px 0px #949da6, -0px -0px 0px #ffffff;
}
/* 뽀모도로 애니메이션 게이지 */
#pie-chart1 {
position: absolute;
top: 95px;
left: 228px;
display: iline-block;
width: 215px;
height: 215px;
border-radius: 50%;
transition: 0.1s;
}
/* 반응형 */
/* min은 ~이상, max는 ~이하 */
/* @media screen and (max-width: 655px) {
#modal-button-id-check {
right: 155px;
}
} */
@media screen and (max-width: 1350px) {
#mainLayout {
top: 450px;
}
section.Pomodoro {
position: relative;
top: 60px;
right: 1px;
}
section.todonottodo {
position: absolute;
top: 940px;
left: 0px;
width: 670px;
}
article.nottodolist {
position: relative;
top: 0px;
left: 0px;
width: 650px;
margin-bottom: 30px;
}
#todo-write,
#nottodo-write {
left: 31px;
}
ol li {
left: 11px;
}
}
@media screen and (min-height: 1150px) {
#mainLayout,
.top-bar {
zoom: 120%;
}
}
@media screen and (max-width: 1800px) {
#mainLayout,
.top-bar {
zoom: 100%;
}
}
@media screen and (max-width: 730px) {
#mainLayout,
.top-bar {
zoom: 38%;
}
.body {
height: 730px;
}
}
/* @media screen and (min-width: 900px) {
#mainLayout {
zoom: 150%;
}
} */
main.js(홈)
//네비 날짜
function setClock(){
let dateObject = new Date();
let year = dateObject.getFullYear();
let month = dateObject.getMonth()+1;
let date = dateObject.getDate();
let hour = addStringZero(dateObject.getHours());
let min = addStringZero(dateObject.getMinutes());
document.getElementById("POMOTODO__clock").innerHTML = year + ". " + month + ". " + date + "ᅠ " + hour+ " : " + min ;
}
function addStringZero(time){
if(parseInt(time)<10)
return "0"+time;
else
return time;
}
window.onload = function(){
setClock();
setInterval(setClock,1000);
}
//뽀모도로타이머
let redMinutes = 25;
let greenMinutes = 5;
let minutes = redMinutes;
let redSeconds = 0;
let greenSeconds = 0;
let seconds = redSeconds;
const appendMinutes = document.getElementById("minute");
const appendSeconds = document.getElementById("second");
const buttonStart = document.getElementById("btn__start");
const buttonStop = document.getElementById("btn__stop");
const buttonMinute1Plus = document.getElementById("minute1__plus");
const buttonMinute2Plus = document.getElementById("minute2__plus");
const buttonSecond1Plus = document.getElementById("second1__plus");
const buttonSecond2Plus = document.getElementById("second2__plus");
const buttonMinute1Minus = document.getElementById("minute1__minus");
const buttonMinute2Minus = document.getElementById("minute2__minus");
const buttonSecond1Minus = document.getElementById("second1__minus");
const buttonSecond2Minus = document.getElementById("second2__minus");
//뽀모도로 타이머 게이지 채우는 코드
let intervalID;
let func1;
let pomodoroDelayRed=1500; //25분은 1500초
let pomodoroDelayGreen=300;
let pomodoroDelay = pomodoroDelayRed;
let pieChart = document.getElementById("pie-chart1")
let PomodoroGuage
let pomodoroColor;
pomodoroColor = '#FF6F71';
let pomodoroGuageColor = 1; // pomodoroGuageColor 가 1이면 pomodoroColor = ff6f71, 2면 초록색 출력 - 토글버튼을 위한 변수
//뽀모도로 게이지 구현부분 draw함수를 호출해서 애니메이션을 시각화
function draw(classname){
PomodoroGuage = 100; //100%여서 고정
func1 = setInterval(function(){
if(PomodoroGuage >= 0){ //0%일때까지 반복
color1(PomodoroGuage ,classname);
PomodoroGuage -= 0.1;
} else
clearInterval(func1);
},pomodoroDelay); // 5가 최소값이라서 뽀모도로 초 변경부분 5초씩 늘리도록 구현했음
}
function color1(PomodoroGuage, classname){
$(classname).css({
"background":"conic-gradient("+pomodoroColor+" 0% "+PomodoroGuage+"%, #ffffff "+PomodoroGuage+"% 0%)"
});
}
//Pomo Start버튼
buttonStart.onclick = function(){
if(minutes >0 || seconds>0){
if(buttonStart.className === 'clock__btn'){
startRecodList();
timeAudio = new Audio('POMOTODO audio/Beep Short .mp3');
timeAudio.volume = 0.1;
// timeAudio.play();
pieChart.style.background = pomodoroColor;
}
buttonStart.classList.add('active');
buttonStop.classList.add('active');
buttonStart.style.display = "none";
buttonStop.style.display = "inline";
clearInterval(intervalID);
}else if (minutes == 0 && seconds == 0)
alert('시간은 0 이상이여야함 JS line 105')
intervalID = setInterval(operateTimer, 1000);
console.log(pomodoroGuageColor+'타입 '+minutes+'분 '+seconds+'초 '+'애니메이션 시간 = '+pomodoroDelay);
// 콘솔 출력내용 : 현재타입, 분, 초, 애니메이션시간
}
//Pomo stop버튼
buttonStop.onclick = function(){
if(minutes >0 || seconds>0){
if(buttonStop.className === 'clock__btn active')
stopRecordList();
}
clearInterval(intervalID);
}
function operateTimer(){ //1초씩 감소시키기
seconds--;
appendSeconds.textContent = seconds;
if(minutes<10)
appendMinutes.textContent="0"+minutes;
if(seconds<10)
appendSeconds.textContent="0"+seconds;
if(seconds<0){
minutes--;
appendMinutes.textContent = minutes;
seconds = 59;
appendSeconds.textContent = seconds;
if(minutes<10)
appendMinutes.textContent="0"+minutes;
}
if(minutes === 00 && seconds === 00){
clearInterval(intervalID);
stopRecordList();
}
if(minutes < 00){
clearInterval(intervalID);
minutes = 0; seconds = 0;
appendMinutes.textContent = "00";
appendSeconds.textContent = "00";
}
}
//record 시간기록
let recordList = document.getElementById("record-list");
let now;
let startHours;
let startMins;
let stopHours;
let stopMins;
function startRecodList(){ // 스타트버튼 누를때
now = new Date();
startHours = addStringZero(now.getHours());
startMins = addStringZero(now.getMinutes());
draw(pieChart); // 타이머 애니메이션 적용,
};
function stopRecordList(){ // 00분00초돌때, 정지버튼 누를때,
// 재생, 일시정지, 정지버튼을 원래대로 되돌리는 코드
buttonStart.style.display = "inline";
buttonStop.style.display = "none";
buttonStart.classList.remove('active');
buttonStop.classList.remove('active');
PomodoroGuage = 0; // 애니메이션 종료시키기
// pomodoroDelay = 1500; // 초기값으로 초기화;
now = new Date();
stopHours = addStringZero(now.getHours());
stopMins = addStringZero(now.getMinutes());
// 스탑버튼 누르면 기록하는 코드
recordList.innerHTML += '<br><button type="button" class="record-content" id="record-time" onclick="appearInputText(this)" >'
+'<span style="color:'+pomodoroColor+'"><i class="fas fa-circle"></i>ㅤㅤ</span>'
+ startHours +' : '+ startMins
+' ~ '+ stopHours +' : '+ stopMins +'ㅤㅤㅤ'
+'<input type="text" class="record-text" id="record-text" style ="display: inline" value=""'
+'onkeydown=" if(window.event.keyCode==13){changeText(this)}" maxlength="26" autocomplete=off></input>'
+' </button>'
//스탑버튼 누르면 출력하는 오디오
timeAudio = new Audio('POMOTODO audio/Beep Short .mp3');
timeAudio.volume = 0.1;
// timeAudio.play();
// 스탑버튼 누르면 색깔, 분 변경하는 코드
if(pomodoroGuageColor == 1){ // 스탑버튼을 눌렀을 때 빨강이면 초록으로+05분으로+게이지도 초록으로 + 토글변수인 포모도로게이지컬러도 2로변경
buttonStart.style.color = '#56D69C'; // 타이머실행버튼 컬러 조작함수
pomodoroDelay = pomodoroDelayGreen;
minutes = parseInt(pomodoroDelayGreen/60);
seconds = pomodoroDelayGreen%60;
if(minutes<10)
appendMinutes.textContent = "0"+minutes;
else
appendMinutes.textContent = minutes;
if(seconds>9)
appendSeconds.textContent = seconds;
else if(seconds<10)
appendSeconds.textContent = "0"+seconds;
pomodoroColor = '#56D69C'; // 게이지컬러 조작함수
pomodoroGuageColor = 2;
}else if(pomodoroGuageColor == 2){
buttonStart.style.color = '#FF6F71'
pomodoroDelay = pomodoroDelayRed;
minutes = parseInt(pomodoroDelayRed/60);
seconds = pomodoroDelayRed%60;
if(minutes<10)
appendMinutes.textContent = "0"+minutes;
else
appendMinutes.textContent = minutes;
if(seconds < 10)
appendSeconds.textContent = "0"+seconds;
else if(seconds > 9)
appendSeconds.textContent = seconds;
pomodoroColor = '#FF6F71';
pomodoroGuageColor = 1;
}
ajaxPomo();
};
//기록에 입력시 값 대입해주는 코드
function changeText(txt){
txt.style.display ='none';
let recordTextParent = txt.parentNode;
recordTextParent.innerHTML += "<span>"+txt.value+"</span>";
ajaxPomo();
}
function appearInputText(txt){ // 기록내용 수정 클릭 시
let recordTextInputTextTag = txt.childNodes[2]; // inputText창의 display를 inline으로 변경해서 보이게 한다
recordTextInputTextTag.style.display = 'inline';
let recordTextContent = txt.childNodes[4]; // 입력을 통해 생성된 span태그를 변수에 담아준다
if(recordTextContent !== undefined)
recordTextInputTextTag.value = recordTextContent.innerText; // 텍스트입력창에 기존의 텍스트값을 넣어준다
if(recordTextContent !== undefined) // 생성된 span태그가 있을경우에 지워주고 없다면 실행하지않는다
txt.removeChild(recordTextContent);
recordTextInputTextTag.focus();
}
function addZeroMinutes(){ // 10분 미만은 0을 붙여주는 메서드
if(minutes<10) // 타이머 10분 증가, 출력
appendMinutes.textContent="0"+minutes
else
appendMinutes.textContent=minutes;
}
function addZeroSeconds(){// 10초 미만은 0을 붙여주는 메서드
if(seconds<10) // 타이머 1분 증가, 출력
appendSeconds.textContent="0"+seconds
else
appendSeconds.textContent = seconds;
}
//minutes증감 버튼
buttonMinute1Plus.onclick = function(){ //10분 증가
if(minutes<90 && buttonStart.style.display !== "none"){
if(pomodoroGuageColor === 1){
redMinutes+=10;
minutes = redMinutes;
addZeroMinutes();
pomodoroDelayRed += 600; // 게이지 10분 증가
pomodoroDelay = pomodoroDelayRed; //pomodoeoDelay = pomodoroDelayRed 때문에 한번 더 거쳐야 해서 재할당 해줘야 값이 저장됨 안하면 2번째바퀴부터 값이 저장됨
}else if(pomodoroGuageColor === 2){
greenMinutes+=10;
minutes = greenMinutes;
addZeroMinutes();
pomodoroDelayGreen += 600;
pomodoroDelay = pomodoroDelayGreen;
}
}
}
buttonMinute2Plus.onclick = function(){ //1분 증가
if(minutes<99 && buttonStart.style.display !== "none"){
if(pomodoroGuageColor === 1){
redMinutes++;
minutes = redMinutes;
addZeroMinutes();
pomodoroDelayRed += 60;
pomodoroDelay = pomodoroDelayRed;
}else{
greenMinutes++;
minutes = greenMinutes;
addZeroMinutes();
pomodoroDelayGreen += 60;
pomodoroDelay = pomodoroDelayGreen;
}
}
}
buttonMinute1Minus.onclick = function(){ //10분 감소
if(minutes>9 && buttonStart.style.display !== "none"){
if(pomodoroGuageColor === 1){
redMinutes-=10;
minutes = redMinutes;
addZeroMinutes();
pomodoroDelayRed -= 600;
pomodoroDelay = pomodoroDelayRed;
}else{
greenMinutes-=10;
minutes = greenMinutes;
addZeroMinutes();
pomodoroDelayGreen -= 600;
pomodoroDelay = pomodoroDelayGreen;
}
}
}
buttonMinute2Minus.onclick = function(){ //1분 감소
if(minutes>0 && buttonStart.style.display !== "none"){
if(pomodoroGuageColor === 1){
redMinutes--;
minutes = redMinutes;
addZeroMinutes();
pomodoroDelayRed -= 60;
pomodoroDelay = pomodoroDelayRed;
}else{
greenMinutes--;
minutes = greenMinutes;
addZeroMinutes();
pomodoroDelayGreen -= 60;
pomodoroDelay = pomodoroDelayGreen;
}
}
}
//
//seconds 증감 버튼
buttonSecond1Plus.onclick = function(){ //10초 증가
if(seconds<50 && buttonStart.style.display !== "none"){
if(pomodoroGuageColor === 1){
redSeconds+=10;
seconds = redSeconds;
appendSeconds.textContent = seconds
pomodoroDelayRed += 10;
pomodoroDelay = pomodoroDelayRed;
}else if(pomodoroGuageColor === 2){
greenSeconds+=10;
seconds = greenSeconds;
appendSeconds.textContent = seconds;
pomodoroDelayGreen += 10;
pomodoroDelay = pomodoroDelayGreen;
}
}else if (seconds>=50 && buttonStart.style.display !== "none" && minutes < 99){ //초의 범위가 넘어가면 분단위 올리는 코드
if(pomodoroGuageColor === 1){
redMinutes++;
minutes = redMinutes;
addZeroMinutes();
redSeconds -= 50;
seconds = redSeconds;
appendSeconds.textContent="0"+seconds;
pomodoroDelayRed += 10;
pomodoroDelay = pomodoroDelayRed;
}else if(pomodoroGuageColor === 2){
greenMinutes++;
minutes = greenMinutes;
addZeroMinutes();
greenSeconds -= 50;
seconds = greenSeconds;
appendSeconds.textContent="0"+seconds;
pomodoroDelayGreen += 10;
pomodoroDelay = pomodoroDelayGreen;
}
}
}
buttonSecond2Plus.onclick = function(){ // 5초 증가
if(seconds<=54 && buttonStart.style.display !== "none" && minutes < 99){
if(pomodoroGuageColor === 1){
redSeconds += 5;
seconds = redSeconds;
appendSeconds.textContent=seconds;
addZeroSeconds();
pomodoroDelayRed += 5;
pomodoroDelay = pomodoroDelayRed;
}else if(pomodoroGuageColor === 2){
greenSeconds += 5;
seconds = greenSeconds;
appendSeconds.textContent=seconds;
addZeroSeconds();
pomodoroDelayGreen += 5;
pomodoroDelay = pomodoroDelayGreen;
}
}else if(seconds >= 55 && buttonStart.style.display !== "none"){
if(pomodoroGuageColor === 1){
redMinutes++;
minutes = redMinutes;
addZeroMinutes();
redSeconds -= 55;
seconds = redSeconds;
appendSeconds.textContent="0"+seconds;
pomodoroDelayRed += 5;
pomodoroDelay = pomodoroDelayRed;
}else if(pomodoroGuageColor === 2){
greenMinutes++;
minutes = greenMinutes;
addZeroMinutes();
greenSeconds -= 55;
seconds = greenSeconds;
appendSeconds.textContent="0"+seconds;
pomodoroDelayGreen += 5;
pomodoroDelay = pomodoroDelayGreen;
}
}
}
buttonSecond1Minus.onclick = function(){ //10초감소
if(seconds>=10 && buttonStart.style.display !== "none"){
if(pomodoroGuageColor === 1){
redSeconds -= 10;
seconds = redSeconds;
addZeroSeconds();
pomodoroDelayRed -= 10;
pomodoroDelay = pomodoroDelayRed;
}else{
greenSeconds -= 10;
seconds = greenSeconds;
addZeroSeconds();
pomodoroDelayGreen -= 10;
pomodoroDelay = pomodoroDelayGreen;
}
}else if (seconds<10 && buttonStart.style.display !== "none" && minutes > 0){ //초단위가 0보다 아래로 내려갈 때 분단위 감소시키는 코드
if(pomodoroGuageColor === 1){
redMinutes--;
minutes = redMinutes;
addZeroMinutes();
redSeconds += 50;
seconds = redSeconds;
if(seconds<10)
appendSeconds.textContent="0"+seconds;
else
appendSeconds.textContent=seconds;
pomodoroDelayRed -= 10;
pomodoroDelay = pomodoroDelayRed;
}else if(pomodoroGuageColor === 2){
greenMinutes--;
minutes = greenMinutes;
addZeroMinutes();
greenSeconds += 50;
seconds = greenSeconds;
if(seconds<10)
appendSeconds.textContent="0"+seconds;
else
appendSeconds.textContent=seconds;
pomodoroDelayGreen -= 10;
pomodoroDelay = pomodoroDelayGreen;
}
}
}
buttonSecond2Minus.onclick = function(){ //5초감소
if(seconds>=5 && buttonStart.style.display !== "none"){
if(pomodoroGuageColor === 1){
redSeconds -= 5;
seconds = redSeconds;
appendSeconds.textContent=seconds;
addZeroSeconds();
pomodoroDelayRed -= 5;
pomodoroDelay = pomodoroDelayRed;
}else if(pomodoroGuageColor === 2){
greenSeconds -= 5;
seconds = greenSeconds;
appendSeconds.textContent=seconds;
addZeroSeconds();
pomodoroDelayGreen -= 5;
pomodoroDelay = pomodoroDelayGreen;
}
}else if (seconds < 5 && buttonStart.style.display !== "none" && minutes > 0){
if(pomodoroGuageColor === 1){
redMinutes--;
minutes = redMinutes;
addZeroMinutes();
redSeconds += 55;
seconds = redSeconds;
addZeroSeconds();
pomodoroDelayRed -= 5;
pomodoroDelay = pomodoroDelayRed;
}else if(pomodoroGuageColor === 2){
greenMinutes--;
minutes = greenMinutes;
addZeroMinutes();
greenSeconds += 55;
seconds = greenSeconds;
addZeroSeconds();
pomodoroDelayGreen -= 5;
pomodoroDelay = pomodoroDelayGreen;
}
}
}
//모달창 구현
let modal = document.getElementById("modal");
let btnModal = document.getElementById("mail-btn");
btnModal.addEventListener("click", e => { // 누르면 모달창 생성, 한번 더 누르면 모달창 종료
if (modal.style.display == "flex")
modal.style.display = "none";
else
modal.style.display = "flex";
})
let closeBtn = modal.querySelector(".close-area") //X버튼 눌러서 모달창 종료
closeBtn.addEventListener("click", e => {
modal.style.display = "none";
})
window.addEventListener("keyup", e => { //esc키 눌렀을 때 모달창 종료
if(modal.style.display === "flex" && e.key === "Escape")
modal.style.display = "none";
})
//투두
let itemList = document.querySelector(".item-list")
let inputButton = document.querySelector(".input-button");
inputButton.addEventListener("click", addItem);
function addItem() {
let item = document.querySelector(".input-text").value;
if(item.length !== 0 ){ //빈값이면 출력안되도록 구현
itemList.innerHTML += "<li class = 'draggable' draggable = 'true' >"
+" <button type= ='button' class='todo-list-content' style='color: #5e5e5e;'>"+ item +"</button>"
+"<i class='fas fa-arrows-alt-v'></i>"
+"<button type ='button' class='todo-list-delete' onclick='deleteItem(this)'><i class='fas fa-times'></i></button></li>"
document.querySelector(".input-text").value = "";
document.querySelector(".input-text").focus();
ajaxTodo();
}
}
$(function(){
$("#sortable").sortable({
start:function(event, ui){
console.log('투두드래그시작');
},
stop:function(event, ui){
ajaxTodo();
}
});
$("#sortable").disableSelection();
})
function deleteItem(txt) {
txt.parentNode.remove();
ajaxTodo();
}
//투두 모달창 구현
let todoModal = document.getElementById("todo-modal");
let setColorList;
itemList.addEventListener("click", e => { // 누르면 모달창 생성, 한번 더 누르면 모달창 종료
if(e.target.tagName !== "SPAN" && e.target.className=='todo-list-content'){
if (todoModal.style.display == "flex")
todoModal.style.display = "none";
else{
setColorList = e.target;
todoModal.style.display = "flex";
}
}
})
window.addEventListener("keyup", e => { //esc키 눌렀을 때 모달창 종료 //투두 낫투두 공통사항
if(todoModal.style.display === "flex" && e.key === "Escape")
todoModal.style.display = "none";
else if(notTodoModal.style.display === "flex" && e.key === "Escape")
notTodoModal.style.display = "none";
})
let modalTodoColor = document.getElementById("todo-modal-content");
modalTodoColor.addEventListener("click", e =>{
if(e.target.value == "close"){
}else if(e.target.value == "check")
setColorList.style.color="lightgray";
else if(e.target.value == "red")
setColorList.style.color="#f36164";
else if(e.target.value == "light-orange")
setColorList.style.color="#E6B74B";
else if(e.target.value == "orange")
setColorList.style.color="#F88313";
else if(e.target.value == "light-pink")
setColorList.style.color="#E69DDE";
else if(e.target.value == "pink")
setColorList.style.color="#E509CC";
else if(e.target.value == "light-puple")
setColorList.style.color="#9288FD";
else if(e.target.value == "purple")
setColorList.style.color="#AB57F7";
else if(e.target.value == "light-green")
setColorList.style.color="#91C349";
else if(e.target.value == "green")
setColorList.style.color="#5C8A3D";
else if(e.target.value == "light-blue-green")
setColorList.style.color="#91EADD";
else if(e.target.value == "blue-green")
setColorList.style.color="#06D2E0";
else if(e.target.value == "light-blue")
setColorList.style.color="#289BFF";
else if(e.target.value == "blue")
setColorList.style.color="#1C00FF";
else if(e.target.value == "sand")
setColorList.style.color="#C39B7A";
else if(e.target.value == "olive")
setColorList.style.color="#B7BE54";
todoModal.style.display = "none"; //공통사항
ajaxTodo();
})
// 낫투두
let ntdItemList = document.querySelector(".ntd-item-list")
let ntdInputButton = document.querySelector(".ntd-input-button");
ntdInputButton.addEventListener("click", ntdAddItem);
function ntdAddItem() {
let ntdItem = document.querySelector(".ntd-input-text").value;
if(ntdItem.length !== 0 ){ //빈값이면 출력안되도록 구현
ntdItemList.innerHTML += "<li class = 'draggable' draggable = 'true' >"
+" <button type= ='button' class='ntd-todo-list-content' style='color: #f36164;' >"+ ntdItem +"</button>"
+"<i class='fas fa-arrows-alt-v'></i>"
+"<button type ='button' class='ntd-todo-list-delete' onclick='ntdDeleteItem(this)'><i class='fas fa-times'></i></button></li>"
document.querySelector(".ntd-input-text").value = "";
document.querySelector(".ntd-input-text").focus();
ajaxNotTodo();
}
}
function ntdDeleteItem(txt) {
txt.parentNode.remove();
ajaxNotTodo();
}
$(function(){
// $("#sortable").sortable(
$("#ntd-sortable").sortable({
start:function(event, ui){
console.log('낫투두드래그시작');
},
stop:function(event, ui){
ajaxNotTodo();
}
});
$("#sortable").disableSelection();
})
//낫투두 모달창 구현
let notTodoModal = document.getElementById("not-todo-modal");
let ntdSetColorList;
ntdItemList.addEventListener("click", e => { // 누르면 모달창 생성, 한번 더 누르면 모달창 종료
if(e.target.tagName !== "SPAN" && e.target.className=='ntd-todo-list-content'){
if (notTodoModal.style.display == "flex")
notTodoModal.style.display = "none";
else{
ntdSetColorList = e.target;
notTodoModal.style.display = "flex";
}
}
})
let ModalNotTodoColor = document.getElementById("not-todo-modal-content");
ModalNotTodoColor.addEventListener("click", e =>{
// console.log(e.target.value) // 클릭한 요소의 태그를 콘솔에 찍어줌다 10월 28일 2시 여기까지완료
if(e.target.value == "ntd-close"){
}else if(e.target.value == "ntd-check")
ntdSetColorList.style.color="#5C8A3D";
else if(e.target.value == "ntd-red")
ntdSetColorList.style.color="#f36164";
notTodoModal.style.display = "none"; //공통사항
ajaxNotTodo();
})
//오디오 플레이어
let audio = new Audio('POMOTODO audio/Farm Morning with Sheep.mp3');
audio.volume = 0.3;
let selectAudio = document.getElementById("select-audio");
let modalAudio = document.getElementById("modal-audio");
let start = document.getElementById("start-button");
let stop = document.getElementById("pause-button")
// selectAudio.innerText = audio1.innerText;
selectAudio.innerText = 'Farm Morning with Sheep';
start.addEventListener("click", function(){
audio.play();
start.style.display = "none";
stop.style.display = "flex";
});
stop.addEventListener("click", function(){
audio.pause();
start.style.display = "flex";
stop.style.display = "none";
});
// 클릭시 모달창 나오는 코드
selectAudio.addEventListener("click", modalClick);
function modalClick(){
if(modalAudio.style.display == "none")
modalAudio.style.display = "flex";
else if(modalAudio.style.display == "flex")
modalAudio.style.display = "none"
// 오디오 선택 버튼 상호작용 구현
modalAudio.addEventListener("click", e =>{
selectAudio.innerText = '';
console.log(e.target.className);
audio.pause();
if(e.target.value == 'Farm Morning with Sheep'){
audio = new Audio('POMOTODO audio/Farm Morning with Sheep.mp3');
audio.volume = 0.5;
}
else if(e.target.value == 'Fire'){
audio = new Audio('POMOTODO audio/Fire.mp3');
audio.volume = 0.5;
}
else if(e.target.value == 'Outdoor Summer Ambience'){
audio = new Audio('POMOTODO audio/Outdoor Summer Ambience.mp3');
audio.volume = 1;
}
else if(e.target.value == 'Rain Heavy Loud'){
audio = new Audio('POMOTODO audio/Rain Heavy Loud.mp3');
audio.volume = 0.1;
}
else if(e.target.value == 'Rain On Rooftop'){
audio = new Audio('POMOTODO audio/Rain On Rooftop.mp3');
audio.volume = 0.6;
}
else if(e.target.value == 'Valley Night'){
audio = new Audio('POMOTODO audio/Valley Night.mp3');
audio.volume = 1;
}
else if(e.target.value == 'Waves Crashing on Rock Beach'){
audio = new Audio('POMOTODO audio/Waves Crashing on Rock Beach.mp3');
audio.volume = 0.5;
}
selectAudio.innerText = e.target.value;
audio.loop = true;
setTimeout(function(){ //stop과 동시에 play를 해서 생기는 문제 해결
audio.play();
},150)
start.style.display = "none";
stop.style.display = "flex";
modalAudio.style.display = "none"
})
}
// --------------------------------------------------ajax--------------------------------------------------------
function ajaxPomo(){
$.ajax({
method : 'POST',
url : '/insertPomodoro',
data : {id : modalButton.innerHTML, content: recordList.innerText, contentHTML : recordList.innerHTML},
success : function() {
console.log('포모도로 ajax 성공')
},
error : function(xhr, status, error) {
console.log('포모도로 ajax 실패');
}
})
}
function ajaxTodo(){
$.ajax({
type : 'post',
url : '/insertTodoList',
data : {id : modalButton.innerHTML, todoList: itemList.innerText, todoListHTML : itemList.innerHTML},
success : function() {
console.log('투두 ajax 성공')
},
error : function(xhr, status, error) {
console.log('투두 ajax 실패');
}
})
}
function ajaxNotTodo(){
$.ajax({
method : 'POST',
url : '/insertNotTodoList',
data : {id : modalButton.innerHTML, notTodoList: ntdItemList.innerText, notTodoListHTML : ntdItemList.innerHTML},
success : function() {
console.log('낫투두 ajax 성공')
},
error : function(xhr, status, error) {
console.log('낫투두 ajax 실패');
}
})
}
// 조건에 맞으면 서버저장/ 초기화하도록
let saveBtn = document.querySelector('.saveBtn');
saveBtn.onclick = function(){
// let dateObject = new Date();
// let year = dateObject.getFullYear();
// let month = dateObject.getMonth()+1;
// let date = dateObject.getDate();
// let hour = addStringZero(dateObject.getHours());
// let min = addStringZero(dateObject.getMinutes());
// console.log(hour);
// console.log(min);
// $.ajax({
// method : 'POST',
// url : '/saveData',
// // data : {empty : ''},
// success : function(data) {
// console.log('empty ')
// },
// error : function(xhr, status, error) {
// console.log('empty 실패');
// }
// })
// $.ajax({
// method : 'POST',
// url : '/initialization',
// // data : {empty : ''},
// success : function(data) {
// console.log(data.message)
// },
// error : function(xhr, status, error) {
// console.log('empty 실패');
// }
// })
};
// 시간체크 ( 10초마다 서버에 저장
function checkTimeSave(){
// console.log(year);
// console.log(month);
// console.log(hour);
// console.log(min);
$.ajax({
method : 'POST',
url : '/saveData',
// data : {empty : ''},
success : function(data) {
console.log('empty ')
},
error : function(xhr, status, error) {
console.log('empty 실패');
}
})
//적용은 됬는데 브라우저가 안열려있으면 저장이 안됨
}
// checkTime();
setInterval(checkTimeSave,10100);
//화면초기화하고 record필드만들어주는 코드작성(컬렉션에 존재하면 만들지않고 존재하지 않으면 만들도록)
function checkTimeInitialization(){
let dateObject = new Date();
let year = dateObject.getFullYear();
let month = dateObject.getMonth()+1;
let date = dateObject.getDate();
let hour = addStringZero(dateObject.getHours());
let min = addStringZero(dateObject.getMinutes());
if(hour == 00 && min == 00){
$.ajax({
method : 'POST',
url : '/initialization',
// data : {empty : ''},
success : function(data) {
console.log(data.message)
},
error : function(xhr, status, error) {
console.log('empty 실패');
}
})
}
}
setInterval(checkTimeSave,55100);
header.ejs(여러화면에 공통으로 출력되는 코드)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Header</title>
<script src="https://kit.fontawesome.com/65f55f4509.js" crossorigin="anonymous"></script>
<!-- <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script> -->
<script src="http://code.jquery.com/jquery-latest.min.js"></script>
<link href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="https://code.jquery.com/jquery-1.12.4.min.js" ></script>
<script type="text/javascript" src="https://code.jquery.com/ui/1.12.1/jquery-ui.js" ></script>
<link rel="stylesheet" href="header.css" />
<script defer src="header.js"></script>
</head>
<body>
<header>
<nav class="top-bar">
<div class="POMOTODO__logo">
<a href="/"><i class="fas fa-mountain"></i>POMOTODO</a>
</div>
<!-- <span id="testReplace">aaa</span> -->
<div id="id-check">
<!-- 아이디출력 -->
<span id="modal-button-id-check"><%= posts %></span>
<!-- 모달창 출력 -->
<span id="modal-window-id-check">
<!-- 로그인/로그아웃 버튼 -->
<!-- <span>1</span> -->
<span id="hello">Hello!</span>
<span id="hello-user">Hello ! <%= posts %></span>
<button id="loginButton">Log in </button>
<button id="logoutButton">Log out</button>
<button id="signup-login-modal">Sign up</button>
<button id="unregister">Delete ID</button>
<div id="unregister-check">
회원탈퇴하시겠습니까?<br>
<button id="unregister-ok">네</button>
<button id="unregister-no">아니요</button>
</div>
<div id="contact-me-title">
contact-developer
<ul class="POMOTODO__contact-me">
<li>
<a href="https://coqoa.tistory.com/" title="tistory blog" target="blank" ><i class="fas fa-bold"></i></a>
</li>
<li>
<a href="https://github.com/coqoa" title="github" target="blank"><i class="fab fa-github"></i></a>
</li>
<li id="mail-btn"><i class="far fa-envelope" title="E-mail"></i></li>
</ul>
</div>
<div id="modal" class="modal-overlay">
<div class="mail-modal-window">
<div class="close-area"><i class="fas fa-times"></i></div>
<div class="content"><i class="fas fa-paper-plane">ᅠcoqoa28@gmail.com</i></div>
</div>
</div>
</span>
</div>
<ul class="POMOTODO__menu">
<li>
<a href="/" title="Desktop-ver" ><i class="fas fa-desktop"></i></a>
</li>
<li>
<a href="/record" title="Record" ><i class="far fa-calendar-alt"></i><a>
</li>
<li>
<a href="PoMoToDo-side.html" title="Mobile-ver" ><i class="fas fa-mobile-alt"></i><a>
</li>
</ul>
<div id="POMOTODO__clock">시계</div>
<!--이메일 모달창-->
</nav>
</header>
</body>
</html>
header.css
button:hover {
opacity: 0.6;
cursor: pointer;
}
button:active {
box-shadow: inset 1px 1px 2px #949da6, -1px -1px 3px #ffffff;
}
body {
font-family: "Ubuntu", sans-serif;
display: flex;
justify-content: center;
height: 870px;
margin: 0px;
background-color: #e9f0fa;
}
header nav {
font-family: "Ubuntu", sans-serif;
display: flex;
/* justify-content: space-between; */
justify-content: center;
align-items: center;
padding: 8px 12px;
position: fixed;
left: 0;
width: 100%;
height: 10px;
background-color: #f4f8fb;
color: #5e5e5e;
z-index: 999;
}
a {
/*네비게이션바의 a태그*/
text-decoration: none;
color: #5e5e5e;
}
.POMOTODO__logo a {
position: absolute;
bottom: 4px;
left: 10px;
padding: 0px;
font-family: "Ubuntu", sans-serif;
font-size: 17px;
font-weight: 500;
font-style: italic;
}
.POMOTODO__logo a:hover {
background-color: transparent;
color: black;
/* font-weight: bold; */
}
.POMOTODO__logo i {
/* 로고 아이콘 */
position: relative;
bottom: 1px;
padding-right: 5px;
color: #ff8585;
}
#id-check {
font-family: "Ubuntu", sans-serif;
font-size: 17px;
font-weight: 500;
font-style: italic;
text-align: center;
color: gray;
}
#modal-button-id-check {
position: absolute;
right: 155px;
bottom: 0px;
height: 20px;
width: 120px;
padding-bottom: 3px;
border-radius: 7px;
color: gray;
}
#modal-button-id-check:hover {
cursor: pointer;
color: black;
}
#modal-button-id-check:active {
cursor: pointer;
box-shadow: inset 0px 0px 0px #949da6, -0px -0px 0px #ffffff;
}
/* 생성된 모달창 */
#modal-window-id-check {
display: none;
flex-direction: column;
position: absolute;
top: 45px;
right: 120px;
height: 220px;
width: 150px;
font-family: "Ubuntu", sans-serif;
background: rgba(255, 255, 255, 0.6);
box-shadow: 6px 6px 9px #c8ced7;
backdrop-filter: blur(1.5px);
-webkit-backdrop-filter: blur(1.5px);
border-radius: 10px;
padding: 5px 5px 10px 5px;
z-index: 99;
}
/* 로그인버튼, 로그아웃버튼, 회원탈퇴버튼 */
#hello,
#hello-user,
#loginButton,
#logoutButton,
#unregister,
#signup-login-modal {
font-size: 18px;
font-weight: 600;
font-style: italic;
color: gray;
display: none;
justify-content: center;
margin: 13px 0px;
border: 0;
background-color: transparent;
}
#loginButton:hover {
opacity: 1;
color: #8ab36f;
}
#logoutButton:hover {
opacity: 1;
color: #e24747;
}
#signup-login-modal:hover {
opacity: 1;
color: #5140eb;
}
#loginButton:active,
#logoutButton:active,
#signup-login-modal {
box-shadow: inset 0px 0px 0px #949da6, -0px -0px 0px #ffffff;
}
#unregister:hover {
opacity: 1;
color: #f0942c;
}
#unregister:active {
cursor: pointer;
box-shadow: inset 0px 0px 0px #949da6, -0px -0px 0px #ffffff;
}
#unregister-check {
position: relative;
top: 100px;
left: -25px;
display: none;
padding-top: 15px;
border: 1px solid white;
border-radius: 10px;
width: 200px;
height: 150px;
background: rgba(255, 255, 255, 0.6);
box-shadow: 6px 6px 9px #c8ced7;
backdrop-filter: blur(1.5px);
-webkit-backdrop-filter: blur(1.5px);
}
#unregister-ok,
#unregister-no {
font-size: 18px;
font-weight: 500;
color: gray;
font-style: italic;
margin: 10px;
padding: 10px;
border: 0px;
border-radius: 10px;
background-color: transparent;
}
#unregister-ok:hover,
#unregister-no:hover {
color: black;
}
#unregister-ok:active,
#unregister-no:active {
box-shadow: inset 0px 0px 0px #949da6, -0px -0px 0px #ffffff;
}
#contact-me-title {
position: absolute;
top: 150px;
left: 12px;
font-weight: 400;
margin-top: 10px;
padding-top: 10px;
}
.POMOTODO__contact-me {
position: absolute;
top: 40px;
left: 10px;
display: flex;
justify-content: center;
list-style: none;
margin: 0px;
padding: 0px;
}
.POMOTODO__contact-me li {
margin-top: 2px;
padding: 2px 12px;
}
.POMOTODO__contact-me li:hover {
color: black;
}
#mail-btn {
cursor: pointer;
}
#modal.modal-overlay {
position: relative;
top: 100px;
left: -10px;
width: 210px;
height: 65px;
display: none;
flex-direction: column;
align-items: center;
justify-content: center;
background: rgba(255, 255, 255, 0.6);
box-shadow: 6px 6px 9px #c8ced7;
backdrop-filter: blur(1.5px);
-webkit-backdrop-filter: blur(1.5px);
border-radius: 10px;
}
#modal .content {
padding-bottom: 10px;
margin-bottom: 5px;
margin-left: 10px;
}
#modal .close-area {
display: inline;
float: right;
padding-right: 3px;
cursor: pointer;
color: gray;
}
/* 네비게이션아이콘 */
.POMOTODO__menu {
position: relative;
right: 0.5%;
display: flex;
list-style: none;
padding: 0px;
margin: 0px;
}
.POMOTODO__menu li {
padding: 2px 12px;
margin: 0px;
}
.POMOTODO__menu li:hover {
opacity: 0.5;
}
/* 네비게이션 시계 */
#POMOTODO__clock {
display: inline;
font-family: "Ubuntu", sans-serif;
position: absolute;
right: 30px;
top: 4px;
margin-right: 10px;
font-size: 14px;
color: #5e5e5e;
}
header.js
//네비 날짜
function setClock(){
let dateObject = new Date();
let year = dateObject.getFullYear();
let month = dateObject.getMonth()+1;
let date = dateObject.getDate();
let hour = addStringZero(dateObject.getHours());
let min = addStringZero(dateObject.getMinutes());
document.getElementById("POMOTODO__clock").innerHTML = year + ". " + month + ". " + date + "ᅠ " + hour+ " : " + min ;
}
function addStringZero(time){
if(parseInt(time)<10)
return "0"+time;
else
return time;
}
window.onload = function(){
setClock();
setInterval(setClock,1000);
}
let modalButton = document.getElementById('modal-button-id-check');
let modalWindow = document.getElementById('modal-window-id-check');
let loginButton = document.getElementById('loginButton');
let logoutButton = document.getElementById('logoutButton');
let unregister = document.getElementById('unregister');
let unregisterCheck = document.getElementById('unregister-check');
let unregisterOk = document.getElementById('unregister-ok');
let unregisterNo = document.getElementById('unregister-no');
let signupLoginModal = document.getElementById('signup-login-modal');
let hello = document.getElementById('hello');
let helloUser = document.getElementById('hello-user');
modalButton.addEventListener("click", e=>{
if(modalWindow.style.display == 'flex')
modalWindow.style.display = "none"
else
modalWindow.style.display = "flex"
if(modalButton.innerText == "log in"){
loginButton.style.display = "flex"
signupLoginModal.style.display = "flex"
hello.style.display="inline-block"
// logoutButton.style.display = "none"
}
else{
logoutButton.style.display = "flex"
unregister.style.display = "flex"
helloUser.style.display="inline-block"
// loginButton.style.display = "none"
}
})
loginButton.addEventListener("click", e=>{
location.href='/login';
// testReplace.style.display = "none"
})
logoutButton.addEventListener("click", e=>{
location.href='/logout';
})
unregister.addEventListener("click", e=>{
unregisterCheck.style.display = "inline-block"
})
unregisterOk.addEventListener("click", e=>{
location.href='/deleteUser';
})
unregisterNo.addEventListener("click", e=>{
unregisterCheck.style.display = "none"
})
signupLoginModal.addEventListener("click", e=>{
location.href='/signup';
})
로그잇/ 로그아웃/ 회원가입 관련
login.ejs
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>login</title>
<link rel="stylesheet" href="login-signup.css" />
</head>
<body>
<div class="container">
<h1>log in</h1>
<div class="login-title-underline"></div>
<form method="post" id="authForm" action="/login">
<div class="form-list">
<!-- <label for="loginId" class="login-label">username</label><br> -->
<input type="text" class="form-list" id="loginId" name="loginId" placeholder="username" minlength="5" maxlength="10" autocomplete='off' autofocus>
</div>
<div class="form-list">
<!-- <label for="loginPw" class="login-label">password</label><br> -->
<input type="password" class="form-list" id="loginPw" name="loginPassword" placeholder="password" minlength="5" maxlength="20" autocomplete='off'>
</div>
<div class="id-check"><%= loginFeedback %></div>
<button type="submit" class="form-list" id="form-login" onkeyup='printName()''>login</button>
<!-- <button type="button" onclick="location.href='signup.html'">회원가입</button> -->
<button type="button" class="form-list" id="login-toHome" onclick="location.href='/'">home</button>
<button type="button" class="form-list" id="login-toSignup" onclick="location.href='/signup'">sign up</button>
<button type="button" class="form-list" id="form-pomotodo" onclick="window.open('https://github.com/coqoa')">what is POMOTODO?</button>
</form>
</div>
</body>
</html>
signup.ejs
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>signup</title>
<link rel="stylesheet" href="login-signup.css" />
<script defer src="signup.js"></script>
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
</head>
<body>
<div class="container">
<h1 class="signup-title">sign up</h1>
<div class="signup-title-underline"></div>
<form id="authForm" name="signupForm" action="/signupResult" method="post">
<div class="form-list">
<label for="id">username</label><br>
<input type="text" class="form-list" id="signupId" name="loginId" placeholder="id (5~10)" minlength="5" maxlength="10" autocomplete='off' onfocus="validate()">
<div class="validate-id"> <%- idCheckResult %> </div>
</div>
<div class="form-list">
<label for="password">password</label><br>
<input type="password" class="form-list" id="signupPassword" name="password" placeholder="password (5~20)" minlength="5" maxlength="20" autocomplete='off'>
<input type="password" class="form-list" id="signupPasswordCheck" name="passwordCheck" placeholder="password reapeat" minlength="5" maxlength="20" autocomplete='off'>
<div class="message" id="password-message-match">ᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠ✔︎ᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠ</div>
<div class="message" id="password-message-unmatch">ᅠ비밀번호가 일치하지 않습니다ᅠ</div>
<div class="message" id="password-message-length">비밀번호는 5~20자로 입력해주세요</div>
</div>
<div class="form-list">
<label for="email" class="label-email">email</label><br>
<input type="email" class="form-list" id="signupEmail" name="email" placeholder="email" autocomplete="off" onblur="mailCheck()">
<div class="message" id="email-check-match">ᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠ✔︎ᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠ</div>
<div class="message" id="email-check-unmatch">이메일 형식으로 입력해주세요</div>
</div>
<div class="form-list">
<label for="number" class="label-number">number</label><br>
<input type="text" class="form-list" id="signupNumber" name="number" placeholder="( - ) 제외 11자" autocomplete='off' maxlength="11" onkeyup="onlyNumber1(this)" onchange="numberBlur(this)">
<div class="message" id="number-message-count">ᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠ✔︎ᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠ</div>
<div class="message" id="number-message">숫자 11자를 입력해주세요</div>
</div>
<div class="form-list">
<label for="birthday" class="label-birthday">birthday</label><br>
<input type="text" class="form-list" id="signupBirthday" name="birthday" placeholder="ex.910208" maxlength='6' autocomplete='off' onkeyup="onlyNumber2(this)" onblur="numberBlur2(this)">
<div class="message" id="birthday-message-count">ᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠ✔︎ᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠ</div>
<div class="message" id="birthday-message">숫자 6자를 입력해주세요</div>
</div>
<div class="form-list">
<div class="gender">
<label class="gender-label">
<input type="radio" name="gender" value="male" checked="checked">
<span>male</span>
</label>
<label class="gender-label">
<input type="radio" id="signupGender" name="gender" value="female">
<span>female</span>
</label>
</div>
</div>
<button type="button" onclick="signup()" class="form-list" id="form-signup">sign up</button>
<button type="button" class="form-list" id="signup-toHome" onclick="location.href='/'">home</button>
<button type="button" class="form-list" id="signup-toLogin" onclick="location.href='/login'">login</button>
</form>
</div>
</body>
</html>
login-signup.css
@import url("https://fonts.googleapis.com/css2?family=Ubuntu:ital,wght@0,300;0,400;0,500;0,700;1,300;1,400;1,500;1,700&display=swap");
/* font-family: 'Ubuntu', sans-serif; */
:root {
--bluetext: #221974;
--redtext: #ff8585;
--hoverredtext: #f75858;
--graytext: #c7c8ca;
--beigetext: #f5f5dc;
--greentext: #76c576;
}
body {
/* 같은배경컬러 */
background-color: #e9f0fa;
/* 다른배경컬러 */
/* background-color: #dce7f7; */
}
.container {
/* 수직/수평 가운데정렬 */
background-color: #e9f0fa;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 350px;
height: 600px;
box-shadow: 8px 8px 25px #c1c7d0, -5px -5px 25px #ffffff;
/* box-shadow: 8px 8px 25px #c1c7d0, -5px -5px 25px #edf4fc; */
border-radius: 15px;
}
/* #signupForm, */
/* 로그인폼 레이아웃 */
#authForm {
position: relative;
top: 20%;
left: 50%;
transform: translate(-50%, -50%);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
/* 로그인 폼 내 각 요소에 관한 코드 */
.form-list {
font-family: "Ubuntu", sans-serif;
font-size: 15px;
font-weight: 400;
font-style: italic;
color: var(--bluetext);
padding: 5px;
margin: 3px;
border: 0;
border-radius: 15px;
text-align: center;
background-color: transparent;
}
h1 {
font-family: "Ubuntu", sans-serif;
font-size: 40px;
font-weight: 500;
font-style: italic;
color: var(--bluetext);
position: relative;
top: 30px;
right: 85px;
text-align: center;
margin-bottom: 150px;
}
label {
position: relative;
top: 0px;
right: 100px;
}
/*------------------------------------------------ ⬆︎ login, signup공통적용 ------------------------------------------------ */
/* ⬇︎ 개별적용 */
/*------------------------------------------------ ⬇︎ login ------------------------------------------------ */
.login-title-underline {
position: relative;
top: -125px;
left: 45px;
height: 5px;
width: 95px;
border-bottom: 3px solid var(--bluetext);
}
#loginPw,
#loginId {
position: relative;
top: 20px;
width: 250px;
height: 40px;
border: 0;
margin-bottom: 30px;
background-color: white;
border: 1px solid white;
}
#loginPw {
top: 26px;
}
#form-login {
font-size: 20px;
font-weight: 500;
position: relative;
top: 25px;
margin-top: 20px;
width: 260px;
height: 50px;
color: var(--bluetext);
background-color: transparent;
box-shadow: 3px 3px 6px #c1c7d0, -3px -3px 6px #ffffff;
border-radius: 15px;
}
#form-login:hover {
cursor: pointer;
color: var(--hoverredtext);
}
#form-login:active {
box-shadow: inset 2px 2px 3px #949da6, inset -2px -2px 2px #ffffff;
}
/* 플래시 메세지 */
.id-check {
position: absolute;
top: 210px;
font-family: "Ubuntu", sans-serif;
font-size: 12px;
font-weight: 500;
font-style: italic;
color: var(--redtext);
}
#login-toHome,
#login-toSignup,
#form-pomotodo {
font-size: 15px;
position: relative;
color: var(--graytext);
top: 40px;
}
#login-toHome:hover,
#login-toSignup:hover,
#form-pomotodo:hover {
cursor: pointer;
color: black;
}
/*------------------------------------------------ ⬇︎ signup ------------------------------------------------ */
/* 라벨위치 미세조정 */
.label-email {
right: 115px;
}
.label-number {
right: 106px;
}
.label-birthday {
right: 104px;
}
.signup-title {
top: 0px;
right: 70px;
}
.signup-title-underline {
position: relative;
top: -150px;
left: 42px;
height: 5px;
width: 130px;
border-bottom: 3px solid var(--bluetext);
margin-bottom: 30px;
}
/* .gender {
position: relative;
top: -10px;
padding: 0px;
margin: 0px;
}
.gender-label {
position: relative;
top: -2px;
right: 0px;
} */
.gender-label input[type="radio"] {
display: none;
}
.gender-label input[type="radio"] + span {
position: relative;
width: 50px;
left: 101px;
display: inline-block;
background: none;
border: 1px solid lightgray;
border-radius: 10px;
padding: 0px 10px;
margin-bottom: 10px;
text-align: center;
height: 35px;
line-height: 33px;
font-weight: 500;
cursor: pointer;
color: lightgray;
z-index: 100;
}
.gender-label input[type="radio"]:checked + span {
border: 1px solid var(--bluetext);
background: none;
color: var(--bluetext);
}
/* #signupGender {
background-color: black;
} */
#signupId,
#signupPassword,
#signupPasswordCheck,
#signupEmail,
#signupNumber,
#signupBirthday {
font-size: 11px;
position: relative;
width: 250px;
height: 15px;
border: 0;
background-color: white;
border: 1px solid white;
border-radius: 7px;
/* padding: 0px; */
margin: 3px;
}
#form-signup {
font-size: 18px;
font-weight: 500;
position: relative;
top: -25px;
margin-top: 20px;
width: 260px;
height: 35px;
color: var(--bluetext);
background-color: transparent;
box-shadow: 3px 3px 6px #c1c7d0, -3px -3px 6px #ffffff;
border-radius: 8px;
cursor: pointer;
}
#form-signup:active {
box-shadow: inset 2px 2px 3px #949da6, inset -2px -2px 2px #ffffff;
}
#signup-toHome,
#signup-toLogin {
font-size: 15px;
position: relative;
color: var(--graytext);
}
#signup-toHome {
top: -26px;
left: 60px;
}
#signup-toLogin {
top: -58px;
left: 110px;
}
#signup-toHome:hover,
#signup-toLogin:hover {
cursor: pointer;
color: black;
}
/* 회원가입 입력값체크*/
.validate-id {
display: block;
font-size: 12px;
position: absolute;
top: 60px;
left: 115px;
color: var(--redtext);
}
.message {
display: none;
font-size: 12px;
}
#password-message-match,
#password-message-unmatch,
#password-message-length,
#number-message,
#number-message-count,
#birthday-message,
#birthday-message-count,
#email-check-match,
#email-check-unmatch {
position: absolute;
top: 160px;
left: 28%;
}
#password-message-unmatch {
left: 29%;
}
#password-message-match,
#number-message-count,
#birthday-message-count,
#email-check-match {
color: var(--greentext);
}
#password-message-unmatch,
#password-message-length,
#number-message,
#birthday-message,
#email-check-unmatch {
color: var(--redtext);
}
#email-check-match,
#email-check-unmatch {
top: 225px;
left: 31%;
}
#email-check-match {
left: 28%;
}
#number-message,
#number-message-count {
top: 290px;
left: 34%;
}
#number-message-count {
left: 28%;
}
#birthday-message,
#birthday-message-count {
top: 352px;
left: 35%;
}
#birthday-message-count {
left: 28%;
}
/* @media screen and (max-width: 760px) {
.container {
zoom: 1%;
}
} */
@media screen and (min-height: 1150px) {
.container {
zoom: 130%;
}
}
@media screen and (max-width: 1800px) {
.container {
zoom: 100%;
}
}
@media screen and (max-width: 730px) {
.container {
zoom: 70%;
}
}
signup.js
// const e = require("connect-flash");
function signup(){
var signupForm = document.signupForm;
var loginId = signupForm.loginId.value;
var password = signupForm.password.value;
var passwordCheck = signupForm.passwordCheck.value;
var email = signupForm.email.value;
var number = signupForm.number.value;
var birthday = signupForm.birthday.value;
var gender = signupForm.gender.value;
// console.log(loginId);
// console.log(password);
// console.log(passwordCheck);
// console.log(email);
// console.log(number);
// console.log(birthday);
// console.log(gender);
// console.log(isPasswordCheck);
// console.log(isMailCheck);
// console.log(isNumberCheck);
// console.log(isBirthdayCheck);
if(loginId == '' || password == '' || passwordCheck == '' || email == '' || number == '' || birthday == '' || gender == ''){
alert("항목을 모두 입력해주세요.");
}else{
if(isPasswordCheck && isMailCheck && isNumberCheck && isBirthdayCheck){
signupForm.submit();
}else{
if(!isPasswordCheck)
alert('비밀번호를 확인해주세요')
if(!isMailCheck)
alert('이메일을 확인해주세요')
if(!isNumberCheck)
alert('전화번호를 확인해주세요')
if(!isBirthdayCheck)
alert('생년월일을 확인해주세요')
}
}
}
let isPasswordCheck;
let passwordBlur = document.querySelector("#signupPasswordCheck");
let passwordMessageMatch = document.getElementById('password-message-match');
let passwordMessageUnmatch = document.getElementById('password-message-unmatch');
let passwordMessageLength = document.getElementById('password-message-length');
passwordBlur.onblur = function (e) {
var signupForm = document.signupForm;
var password = signupForm.password.value;
var passwordCheck = signupForm.passwordCheck.value;
if(password !=='' && passwordCheck !== ''){
if(password.length > 4){
if(password === passwordCheck){
passwordMessageMatch.style.display = "inline";
passwordMessageUnmatch.style.display = "none";
passwordMessageLength.style.display = "none";
isPasswordCheck = true;
}else{
passwordMessageMatch.style.display = "none";
passwordMessageUnmatch.style.display = "inline";
passwordMessageLength.style.display = "none";
isPasswordCheck = false;
}
}else if(password.length < 5){
passwordMessageMatch.style.display = "none";
passwordMessageUnmatch.style.display = "none";
passwordMessageLength.style.display = "inline";
isPasswordCheck = false
}
}
}
let isMailCheck;
function mailCheck() {
var email = document.getElementById('signupEmail').value;
let emailCehckMatch = document.getElementById('email-check-match');
let emailCheckUnmatch = document.getElementById('email-check-unmatch');
console.log(email);
var regEmail = /^[0-9a-zA-Z]([-_\.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_\.]?[0-9a-zA-Z])*\.[a-zA-Z]{2,3}$/;
if (regEmail.test(email) === true) {
emailCehckMatch.style.display = "inline";
emailCheckUnmatch.style.display = "none";
isMailCheck = true;
}else{
emailCehckMatch.style.display = "none";
emailCheckUnmatch.style.display = "inline";
isMailCheck = false;
}
}
let isNumberCheck;
let numberMessage = document.getElementById('number-message');
let numberMessageCount = document.getElementById('number-message-count');
function onlyNumber1(loc) {
numberMessageCount.style.display = "none";
if(/[^0123456789]/g.test(loc.value)) {
loc.value = "";
loc.focus();
numberMessage.style.display = "inline";
isNumberCheck = false;
}
}
function numberBlur(e) {
if(e.value.length>10){
numberMessageCount.style.display = "inline";
numberMessage.style.display = "none";
isNumberCheck = true;
}else{
numberMessageCount.style.display = "none";
numberMessage.style.display = "inline";
isNumberCheck = false;
}
}
let isBirthdayCheck;
let birthdayMessage = document.getElementById('birthday-message');
let birthdayMessageCount = document.getElementById('birthday-message-count');
function onlyNumber2(loc) {
birthdayMessageCount.style.display = "none";
if(/[^0123456789]/g.test(loc.value)) {
loc.value = "";
loc.focus();
birthdayMessage.style.display = "inline";
isBirthdayCheck = false;
}
}
function numberBlur2(e) {
if(e.value.length>5){
birthdayMessageCount.style.display = "inline";
birthdayMessage.style.display = "none";
isBirthdayCheck = true;
}else{
birthdayMessageCount.style.display = "none";
birthdayMessage.style.display = "inline";
isBirthdayCheck = false;
}
}
// id중복체크 ajax요청
let idBlur = document.querySelector("#signupId");
let validatdId = document.querySelector(".validate-id");
idBlur.onblur = function (e) {
let signupForm = document.signupForm;
let idValue= signupForm.loginId.value;
$.ajax({
method : 'POST',
url : '/signup-id-check',
data : {id : idValue},
success : function(data) {
if(validatdId.style.display == 'none'){
validatdId.style.display = 'block';
}
$(validatdId).html (data.message)
},
error : function(xhr, status, error) {
console.log('아이디체크 실패');
}
})
}
let inputId = document.querySelector('#signupId');
inputId.addEventListener(onfocus, validate);
function validate(){
validatdId.style.display = 'none';
}
record페이지 관련
record.ejs
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PoMoToDo-record</title>
<link rel="stylesheet" href="record.css" />
<script src="https://kit.fontawesome.com/65f55f4509.js" crossorigin="anonymous"></script>
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
<script defer src="/record.js"></script>
</head>
<body class="body">
<%- include ('header.ejs') %>
<!--------------⬆︎ 네비게이션공통 ------------------>
<section class="big-layout" id="mainLayout">
<div class="small-layout" id="calendar-layout">
<div class="record-calendar-content" id="calendar-content">
<table class="calendar-layout" id="calendar" border="3" align="center">
<tr><!-- label은 마우스로 클릭을 편하게 해줌 -->
<td class="prev"><label onclick="prevCalendar()"><i class="fas fa-angle-left"></i></label></td>
<td class="year-month" align="center" id="tbCalendarYM" colspan="5">yyyy- m</td>
<td class="next"><label onclick="nextCalendar()"><i class="fas fa-angle-right"></i></label></td>
</tr>
<tr>
<td class="day" align="center"><font color ="#F79DC2">Sun</td>
<td class="day" align="center">Mon</td>
<td class="day" align="center">Tue</td>
<td class="day" align="center">Wed</td>
<td class="day" align="center">Thur</td>
<td class="day" align="center">Fri</td>
<td class="day" align="center"><font color ="skyblue">Sat</td>
</tr>
</table>
</div>
</div>
<!-- 달력 -->
<div class="small-layout" id="pomo-record">
<h2>Pomodoro</h2>
<div class="record-pomo-content" id="pomo-record-content">
<%- pomos %>
</div>
</div>
</div>
<div class="small-layout" id="todo-record">
<h2>ToDoList</h2>
<div class="record-todo-content" id="todo-record-content">
<%- todos %>
</div>
</div>
<div class="small-layout" id="not-todo-record">
<h2>Not-ToDoList</h2>
<div class="record-nottodo-content" id="not-todo-record-content">
<%- notTodos %>
</div>
</div>
</section>
</body>
</html>
record.css
@import url("https://fonts.googleapis.com/css2?family=Nanum+Gothic:wght@400;700;800&display=swap");
/* font-family: 'Nanum Gothic', sans-serif; */
@import url("https://fonts.googleapis.com/css2?family=Ubuntu:ital,wght@0,300;0,400;0,500;0,700;1,300;1,400;1,500;1,700&display=swap");
#mainLayout {
zoom: 100%;
display: row;
position: absolute;
top: 49%;
left: 50%;
transform: translate(-50%, -48%);
width: 550px;
height: 870px;
align-items: center;
border-radius: 10px;
box-shadow: 7px 7px 7px #c8ced7, -5px -5px 20px #ffffff;
overflow: auto;
}
#mainLayout::-webkit-scrollbar {
width: 10px;
background-color: transparent;
border-radius: 10px;
}
#mainLayout::-webkit-scrollbar-thumb {
background-clip: padding-box;
border-top: 0px solid lightgray;
border-bottom: 0px solid lightgray;
border-radius: 30px;
background-color: rgba(206, 204, 204, 0.945);
}
h2 {
position: relative;
top: 13px;
margin-top: 0px;
margin-bottom: 0px;
}
.small-layout {
position: relative;
left: 25px;
width: 500px;
margin-top: 25px;
text-align: center;
border-radius: 10px;
box-shadow: 4px 4px 7px #c8ced7, -2px -2px 3px #ffffff;
}
#calendar-layout {
height: 360px;
}
#pomo-record {
height: 435px;
margin-bottom: 20px;
}
#todo-record {
margin-top: 25px;
height: 500px;
}
#not-todo-record {
height: 295px;
margin-top: 30px;
margin-bottom: 20px;
}
.record-calendar-content,
.record-pomo-content,
.record-todo-content,
.record-nottodo-content {
position: relative;
/* border: 1px solid lightgray; */
overflow: auto;
margin: 15px;
}
#calendar-content {
top: 5px;
height: 350px;
}
#pomo-record-content {
height: 390px;
text-align: left;
}
#todo-record-content {
height: 450px;
left: 4px;
}
#not-todo-record-content {
height: 240px;
left: 4px;
}
.record-calendar-content::-webkit-scrollbar,
.record-pomo-content::-webkit-scrollbar,
.record-todo-content::-webkit-scrollbar,
.record-nottodo-content::-webkit-scrollbar {
width: 7px;
background-color: transparent;
}
.record-calendar-content::-webkit-scrollbar-thumb,
.record-pomo-content::-webkit-scrollbar-thumb,
.record-todo-content::-webkit-scrollbar-thumb,
.record-nottodo-content::-webkit-scrollbar-thumb {
background-clip: padding-box;
border-top: 0px solid lightgray;
border-bottom: 0px solid lightgray;
border-radius: 30px;
background-color: lightgray;
}
/* ---------------달력------------------ */
table {
position: relative;
top: 10px;
}
#tbCalendarYM {
top: 0px;
}
.fa-angle-left,
.fa-angle-right {
position: relative;
top: -2px;
}
td {
padding: 0px;
box-sizing: border-box;
width: 50px;
height: 25px;
text-align: center;
font-size: 10px;
/* font-family: 굴림; */
border: 1px solid transparent;
border-radius: 10px; /*모서리 둥글게*/
}
td.day {
position: relative;
top: 3px;
}
/* 일 버튼(요일버튼제외) */
.day-button {
font-family: "Ubuntu", sans-serif;
font-style: italic;
margin: 0px;
padding: 0px;
width: 50px;
height: 40px;
font-size: 13px;
/* font-family: 굴림; */
color: gray;
border: 1px solid rgb(231, 229, 229);
/* background-color: transparent; */
border-radius: 8px;
cursor: pointer;
}
.day-button:active {
box-shadow: 0px 0px 0px #c8ced7, -0px -0px 0px #ffffff;
}
.day {
border: 0;
font-size: 15px;
}
.prev,
.next {
font-size: 18px;
border: 0;
}
i:hover {
cursor: pointer;
opacity: 0.7;
}
.year-month {
font-family: "Ubuntu", sans-serif;
font-weight: 700;
font-style: italic;
font-size: 28px;
border: 0;
}
.calendar-layout {
border: 0;
}
/* ------------------------------------------------------------------ */
/* pomo부분 */
.record-content {
font-family: "Ubuntu", sans-serif;
font-style: italic;
font-weight: 400;
font-size: 18px;
margin: 8px;
border: 0;
background-color: transparent;
/* 마우스클릭금지 */
pointer-events: none;
color: rgb(78, 78, 78);
}
.record-text {
border: 0;
background-color: transparent;
}
/* 투두, 낫투두 화면에 표시되면 안되는 부분 처리*/
.draggable {
list-style-type: none;
opacity: 0.8;
}
.fa-circle {
position: relative;
top: -3px;
font-size: 12px;
opacity: 0.7;
}
.fa-arrows-alt-v,
.todo-list-delete,
.ntd-todo-list-delete {
color: transparent;
background-color: transparent;
border: 0;
pointer-events: none;
}
/* 투두, 낫투두 화면에 표시될 부분 처리 */
/* .todo-list-content,
.ntd-todo-list-content { */
.draggable {
pointer-events: none;
}
.todo-list-content,
.ntd-todo-list-content {
font-family: "Ubuntu", sans-serif;
font-style: italic;
/* font-family: "Nanum Gothic", sans-serif; */
font-weight: 500;
font-size: 15px;
position: relative;
top: 10px;
left: 8px;
border: 0px;
background-color: transparent;
margin: 10px;
}
/* -----------------------------------반응형------------------------------------ */
@media screen and (min-height: 1150px) {
#mainLayout,
.top-bar {
zoom: 120%;
}
}
@media screen and (max-width: 1800px) {
#mainLayout,
.top-bar {
zoom: 100%;
}
}
@media screen and (min-width: 1200px) {
#mainLayout {
width: 1070px;
}
#todo-record {
position: absolute;
top: 0px;
left: 550px;
}
#not-todo-record {
position: absolute;
top: 520px;
left: 550px;
}
}
@media screen and (max-width: 730px) {
#mainLayout,
.top-bar {
zoom: 45%;
}
#mainLayout {
left: 365px;
top: 607px;
}
.body {
height: 500px;
}
}
record.js
// 달력
var today = new Date();//오늘 날짜//내 컴퓨터 로컬을 기준으로 today에 Date 객체를 넣어줌
var date = new Date();//today의 Date를 세어주는 역할
function prevCalendar() {//이전 달
today = new Date(today.getFullYear(), today.getMonth() - 1, today.getDate());
buildCalendar();
// today라는 변수에다가 인자값으로 년, 월, 일을 넣어서 빌드캘린더함수실행
// getFullyear ? : 년도를 출력하는 함수
// getMonth ? : 월 출력 함수 저번달이므로 -1해준다 getMonth는 0~11월로 표시되지만 빌드캘린더 함수에서 +1해주므로 -1을 넣어줘야 전달값이 나옴
// getDate ? : 일 출력 함수
}
function nextCalendar() {//다음 달
today = new Date(today.getFullYear(), today.getMonth() + 1, today.getDate());
buildCalendar();
}
function buildCalendar(){//입력받은 값을 기준으로 달력 만들기
var doMonth = new Date(today.getFullYear(),today.getMonth(),1);
//이번 달의 첫째 날,
//new를 쓰는 이유 : new를 쓰면 이번달의 로컬 월을 정확하게 받아온다.
//new를 쓰지 않았을때 이번달을 받아오려면 +1을 해줘야한다.
//왜냐면 getMonth()는 0~11을 반환하기 때문
var lastDate = new Date(today.getFullYear(),today.getMonth()+1,0);
//이번 달의 마지막 날
//new를 써주면 정확한 월을 가져옴, getMonth()+1을 해주면 다음달로 넘어가는데
//day를 1부터 시작하는게 아니라 0부터 시작하기 때문에
//대로 된 다음달 시작일(1일)은 못가져오고 1 전인 0, 즉 전달 마지막일 을 가져오게 된다
var tbCalendar = document.getElementById("calendar");
//날짜를 찍을 테이블 변수 만듬, 일 까지 다 찍힘
var tbCalendarYM = document.getElementById("tbCalendarYM");
//테이블에 정확한 날짜 찍는 변수
//innerHTML : js 언어를 HTML의 권장 표준 언어로 바꾼다
//new를 찍지 않아서 month는 +1을 더해줘야 한다.
tbCalendarYM.innerHTML = today.getFullYear() + "." + (today.getMonth() + 1) + "";
/*while은 이번달이 끝나면 다음달로 넘겨주는 역할*/
while (tbCalendar.rows.length > 2) {
//열을 지워줌
//기본 열 크기는 body 부분에서 2로 고정되어 있다.
tbCalendar.deleteRow(tbCalendar.rows.length-1);
//테이블의 tr 갯수 만큼의 열 묶음은 -1칸 해줘야지
//30일 이후로 담을달에 순서대로 열이 계속 이어진다.
}
var row = null;
row = tbCalendar.insertRow();
//테이블에 새로운 열 삽입//즉, 초기화
var cnt = 0;// count, 셀의 갯수를 세어주는 역할
// 1일이 시작되는 칸을 맞추어 줌
for (i=0; i<doMonth.getDay(); i++) {
/*이번달의 day만큼 돌림*/
cell = row.insertCell();//열 한칸한칸 계속 만들어주는 역할
cnt = cnt + 1;//열의 갯수를 계속 다음으로 위치하게 해주는 역할
}
/*달력 출력*/
for (i=1; i<=lastDate.getDate(); i++) {
let year = today.getFullYear();
let month = ((today.getMonth()+1));
let day = (i);
let yyyymmdd = year+"."+month+"."+day;
//1일부터 마지막 일까지 돌림
cell = row.insertCell();//열 한칸한칸 계속 만들어주는 역할
cell.innerHTML = "<button class='day-button' id='"+i+"' onclick='clickButton(this.id)' style='background-color:transparent'>"+i+"</button>";//셀을 1부터 마지막 day까지 HTML 문법에 넣어줌
cnt = cnt + 1;//열의 갯수를 계속 다음으로 위치하게 해주는 역할
// if (cnt % 7 == 1) {/*일요일 계산*/
// //1주일이 7일 이므로 일요일 구하기
// //월화수목금토일을 7로 나눴을때 나머지가 1이면 cnt가 1번째에 위치함을 의미한다
// // cell.innerHTML = "<font color=#F79DC2>" + i
// cell.innerHTML = "<button class='day-button' id='"+i+"' onclick='clickButton(this.id)'>"+i+"</button>";//셀을 1부터 마지막 day까지 HTML 문법에 넣어줌
// //1번째의 cell에만 색칠
// }
if (cnt%7 == 0){/* 1주일이 7일 이므로 토요일 구하기*/
//월화수목금토일을 7로 나눴을때 나머지가 0이면 cnt가 7번째에 위치함을 의미한다
// cell.innerHTML = "<font color=skyblue>" + i
cell.innerHTML = "<button class='day-button' id='"+i+"' onclick='clickButton(this.id)' style='background-color:transparent'>"+i+"</button>";//셀을 1부터 마지막 day까지 HTML 문법에 넣어줌
//7번째의 cell에만 색칠
row = calendar.insertRow();
//토요일 다음에 올 셀을 추가
}
let countI = document.getElementById(i);
$.ajax({
method : 'POST',
url : '/buttonColor',
data : {'count' : yyyymmdd},
success : function(data) {
// $(pomoRecordContent).html (data.pomoMessage);
function pomoCount(){
console.log(data.message/800)
if(data.message/800 < 0.5){
// console.log('0')
// countI.style.backgroundColor="transparent";
}else if (data.message/800 > 0.5 && data.message/800<4){
// console.log('1~2')
countI.style.backgroundColor="#ffa590";
countI.style.color="white";
}else if (data.message/800 > 3.999 && data.message/800<8){
// console.log('3~5')
countI.style.backgroundColor="#ff8164";
countI.style.color="white";
}
else if (data.message/800 > 7.999 && data.message/800<12){
// console.log('5~10')
countI.style.backgroundColor="#ff6242";
countI.style.color="white";
}else if (data.message/800 > 11.999){
// console.log('10이상')
// console.log(data.message)
countI.style.backgroundColor="#ff4122";
countI.style.color="white";
}
}
pomoCount();
},
error : function(xhr, status, error) {
console.log(data.message)
}
})
/*오늘의 날짜에 노란색 칠하기*/
if (today.getFullYear() == date.getFullYear()
&& today.getMonth() == date.getMonth()
&& i+1 == date.getDate()) {//어제날짜에 칠해야하므로
//달력에 있는 년,달과 내 컴퓨터의 로컬 년,달이 같고, 일이 오늘의 일과 같으면
countI.style.border = "3px solid gray";//셀의 배경색을 노랑으로
}
}
}
function clickButton(clicked_id){
let year = today.getFullYear();
let month = ((today.getMonth()+1));
let day = (clicked_id);
let yyyymmdd = year+"."+month+"."+day;
// console.log(yyyymmdd);
let pomoRecordContent = document.querySelector(".record-pomo-content");
let todoRecordContent = document.querySelector(".record-todo-content");
let notTodoRecordContent = document.querySelector(".record-nottodo-content");
// 클릭제외버튼 테두리색 초기화
let buttonBorder = document.querySelectorAll('.day-button');
for (let i = 0; i < buttonBorder.length; i++)
buttonBorder[i].style.border = "1px solid #dadada";
//클릭한 버튼 테두리색
let clickedButton = document.getElementById(clicked_id);
clickedButton.style.border = "3px solid gray";
// 이거랑 navId로 서버에서 자료찾아서 화면에 뿌려주도록 ajax 있으면 뿌려주고 없으면 빈값으로 출력
$.ajax({
method : 'POST',
url : '/dayButton',
data : {'clickedButton' : yyyymmdd},
success : function(data) {
$(pomoRecordContent).html (data.pomoMessage);
$(todoRecordContent).html (data.todoMessage);
$(notTodoRecordContent).html (data.notTodoMessage);
console.log('데이버튼클릭 성공')
},
error : function(xhr, status, error) {
console.log('데이버튼 클릭실패');
}
})
}
buildCalendar();
'프로그래밍 > 개인프로젝트' 카테고리의 다른 글
POMOTODO - 개발순서 중간정리 ~ 21. 12.28 (0) | 2021.12.28 |
---|---|
POMOTODO(11) (0) | 2021.12.28 |
비밀번호 암호화 (pbkdf2, salt, hash, 사용) (0) | 2021.11.18 |
POMOTODO(9) - 서버환경 구축, 작업코드 서버환경에 생성 +@ (0) | 2021.11.02 |
POMOTODO(8) - 메인페이지 거의 구현완료, 서버공부 (0) | 2021.11.01 |